diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c index 5b06f92..85e1cb0 100644 --- a/drivers/usb/storage/initializers.c +++ b/drivers/usb/storage/initializers.c @@ -45,6 +45,12 @@ #include "debug.h" #include "transport.h" +#define RIO_MSC 0x08 +#define RIOP_INIT "RIOP\x00\x01\x08" +#define RIOP_INIT_LEN 7 +#define RIO_SEND_LEN 40 +#define RIO_RECV_LEN 0x200 + /* This places the Shuttle/SCM USB<->SCSI bridge devices in multi-target * mode */ int usb_stor_euscsi_init(struct us_data *us) @@ -91,3 +97,70 @@ int usb_stor_ucr61s2b_init(struct us_dat return (res ? -1 : 0); } + +/* Place the Rio Karma into mass storage mode. + * + * The initialization begins by sending 40 bytes starting + * RIOP\x00\x01\x08\x00, which the device will ack with a 512-byte + * packet with the high four bits set and everything else null. + * + * Next, we send RIOP\x80\x00\x08\x00. Each time, a 512 byte response + * must be read, but we must loop until byte 5 in the response is 0x08, + * indicating success. */ +int rio_karma_init(struct us_data *us) +{ + int result, partial; + char *recv; + unsigned long timeout; + + memset(us->iobuf, 0, RIO_SEND_LEN); + memcpy(us->iobuf, RIOP_INIT, RIOP_INIT_LEN); + + // us->iobuf is big enough to hold cmd but not receive + if (!(recv = kmalloc(RIO_RECV_LEN, GFP_KERNEL))) + goto die_nomem; + + US_DEBUGP("Initializing Karma...\n"); + + result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + us->iobuf, RIO_SEND_LEN, &partial); + if (result != USB_STOR_XFER_GOOD) + goto die; + + result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + recv, RIO_RECV_LEN, &partial); + if (result != USB_STOR_XFER_GOOD) + goto die; + + us->iobuf[4] = 0x80; + us->iobuf[5] = 0; + timeout = jiffies + msecs_to_jiffies(3000); + for (;;) { + US_DEBUGP("Sending init command\n"); + result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + us->iobuf, RIO_SEND_LEN, &partial); + if (result != USB_STOR_XFER_GOOD) + goto die; + + result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + recv, RIO_RECV_LEN, &partial); + if (result != USB_STOR_XFER_GOOD) + goto die; + + if (recv[5] == RIO_MSC) + break; + if (time_after(jiffies, timeout)) + goto die; + msleep(10); + } + US_DEBUGP("Karma initialized.\n"); + kfree(recv); + return 0; + +die: + kfree(recv); +die_nomem: + US_DEBUGP("Could not initialize karma.\n"); + return USB_STOR_TRANSPORT_FAILED; +} + diff --git a/drivers/usb/storage/initializers.h b/drivers/usb/storage/initializers.h index 7372386..fa9a2ff 100644 --- a/drivers/usb/storage/initializers.h +++ b/drivers/usb/storage/initializers.h @@ -52,3 +52,4 @@ int sddr09_init(struct us_data *us); /* This function is required to activate all four slots on the UCR-61S2B * flash reader */ int usb_stor_ucr61s2b_init(struct us_data *us); +int rio_karma_init(struct us_data *us); diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 0a9858f..0cdfebf 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -145,6 +145,11 @@ UNUSUAL_DEV( 0x0451, 0x5416, 0x0100, 0x US_SC_DEVICE, US_PR_BULK, NULL, US_FL_NEED_OVERRIDE ), +UNUSUAL_DEV( 0x045a, 0x5210, 0x0101, 0x0101, + "Rio", + "Rio Karma", + US_SC_SCSI, US_PR_BULK, rio_karma_init, 0), + /* Patch submitted by Philipp Friedrich */ UNUSUAL_DEV( 0x0482, 0x0100, 0x0100, 0x0100, "Kyocera", diff --git a/fs/Kconfig b/fs/Kconfig index d5255e6..87ac8fd 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -1248,6 +1248,10 @@ config SYSV_FS If you haven't heard about all of this before, it's safe to say N. +config OMFS_FS + tristate "OMFS (Rio Karma)" + help + Say 'Y' to get support for the RIO Karma fs. config UFS_FS tristate "UFS file system support (read only)" diff --git a/fs/Makefile b/fs/Makefile index 4c26557..9efa233 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -100,4 +100,5 @@ obj-$(CONFIG_AFS_FS) += afs/ obj-$(CONFIG_BEFS_FS) += befs/ obj-$(CONFIG_HOSTFS) += hostfs/ obj-$(CONFIG_HPPFS) += hppfs/ +obj-$(CONFIG_OMFS_FS) += omfs/ obj-$(CONFIG_DEBUG_FS) += debugfs/ diff --git a/fs/omfs/Makefile b/fs/omfs/Makefile new file mode 100644 index 0000000..667e918 --- /dev/null +++ b/fs/omfs/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for OMFS filesystem. +# + + +obj-$(CONFIG_OMFS_FS) += omfs.o + +omfs-objs := inode.o dir.o file.o bitmap.o checksum.o diff --git a/fs/omfs/bitmap.c b/fs/omfs/bitmap.c new file mode 100644 index 0000000..cb8e5ad --- /dev/null +++ b/fs/omfs/bitmap.c @@ -0,0 +1,22 @@ +#include "omfs.h" + +static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0}; + +unsigned long omfs_count_free(struct super_block *sb) +{ + unsigned int i, j, count = 0; + long *map; + unsigned long sum = 0; + struct omfs_sb_info *sbi = OMFS_SB(sb); + + for (i = 0; i < sbi->imap_size; i++) + { + map = sbi->imap[i]; + for (j=0; j < sb->s_blocksize && + count + j < sbi->num_blocks; j++) + sum += nibblemap[map[j] & 0xf] + + nibblemap[(map[j] >> 4) & 0xf]; + count += sb->s_blocksize; + } + return (sum); +} diff --git a/fs/omfs/checksum.c b/fs/omfs/checksum.c new file mode 100644 index 0000000..481278c --- /dev/null +++ b/fs/omfs/checksum.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include "omfs.h" + +#define POLY 0x1021 + +/* + * crc-ccitt with MSB first (i.e., backwards), so we can't use the + * kernel table as-is. + */ +static u16 omfs_crc(u16 crc, unsigned char *buf, int count) +{ + int i, j; + printk(KERN_DEBUG "omfs: in crc for count %x\n", crc); + for (i=0; ii_sb; + int block = clus_to_blk(inode->i_ino); + u16 crc=0; + struct buffer_head *bh; + unsigned char *ptr = (unsigned char *) oi; + + count = be32_to_cpu(oi->head.body_size); + ofs = sizeof(struct omfs_header); + + printk(KERN_DEBUG "omfs: body size is %x\n", count); + + for (; count > 0; count -= sb->s_blocksize) { + bh = sb_bread(inode->i_sb, block++); + if (!bh) { + ret = -ENOMEM; + goto out; + } + // count will always fall on a blocksize boundary + crc = omfs_crc(crc, &bh->b_data[ofs], sb->s_blocksize - ofs); + brelse(bh); + ofs = 0; + } + + oi->head.crc = cpu_to_be16(crc); + + xor = ptr[0]; + for (i=1; ihead.check_xor = xor; +out: + printk(KERN_DEBUG "Calculated checksum for %lx as %x\n", + inode->i_ino, crc); + return ret; +#endif +} + + diff --git a/fs/omfs/dir.c b/fs/omfs/dir.c new file mode 100644 index 0000000..37d4f3e --- /dev/null +++ b/fs/omfs/dir.c @@ -0,0 +1,327 @@ +/* + * fs/omfs/dir.c + * OMFS (as used by RIO Karma) directory operations. + * Copyright (C) 2005 Bob Copeland + */ + +#include +#include +#include +#include +#include "omfs.h" + +static int omfs_hash(const char *name, int namelen, int mod) +{ + int i, hash=0; + for (i=0; ii_size - OMFS_DIR_START)/8; + int block = clus_to_blk(dir->i_ino); + int bucket = omfs_hash(name, namelen, nbuckets); + + ptr = OMFS_DIR_START + bucket * 8; + block += ptr >> dir->i_sb->s_blocksize_bits; + *ofs = ptr & (dir->i_sb->s_blocksize - 1); + + return sb_bread(dir->i_sb, block); +} + +static struct buffer_head *omfs_find_entry(struct inode *dir, + const char *name, int namelen) +{ + struct buffer_head *bh; + struct omfs_inode *oi; + int ofs; + u64 cluster; + bh = omfs_get_bucket(dir, name, namelen, &ofs); + if (!bh) + goto out; + + cluster = be64_to_cpu(*((u64 *) & bh->b_data[ofs])); + brelse(bh); + while (cluster != ~0) { + bh = sb_bread(dir->i_sb, clus_to_blk(cluster)); + if (!bh) + goto out; + + oi = (struct omfs_inode *)bh->b_data; + if (strncmp(oi->name, name, namelen) == 0) { + return bh; + } + cluster = be64_to_cpu(oi->sibling); + brelse(bh); + } +out: + return NULL; +} + +#ifdef OMFS_WRITE +int omfs_make_empty(struct inode *inode, struct super_block *sb) +{ + int i; + int block = clus_to_blk(inode->i_ino); + int first = 1; + struct buffer_head *bh; + + for (i=0; i < OMFS_SB(sb)->sys_blocksize; i += sb->s_blocksize) + { + bh = sb_bread(sb, block++); + if (!bh) + return -ENOMEM; + + if (first) { + memset(bh->b_data, 0, OMFS_DIR_START); + memset(&bh->b_data[OMFS_DIR_START], 0xff, + sb->s_blocksize - OMFS_DIR_START); + } else + memset(bh->b_data, 0xff, sb->s_blocksize); + mark_buffer_dirty(bh); + brelse(bh); + first = 0; + } + return 0; +} + +int omfs_add_link(struct dentry *dentry, struct inode *inode) +{ + struct inode *dir = dentry->d_parent->d_inode; + const char *name = dentry->d_name.name; + int namelen = dentry->d_name.len; + struct omfs_inode *oi; + struct buffer_head *bh; + u64 cluster; + u64 *entry; + int ofs; + + // just prepend to head of queue in proper bucket and we are done. + bh = omfs_get_bucket(dir, name, namelen, &ofs); + if (!bh) + goto out; + + entry = (u64 *) &bh->b_data[ofs]; + cluster = be64_to_cpu(*entry); + *entry = cpu_to_be64(inode->i_ino); + mark_buffer_dirty(bh); + brelse(bh); + + // now set the sibling and parent pointers on the new inode + bh = sb_bread(dir->i_sb, clus_to_blk(inode->i_ino)); + if (!bh) + goto out; + + oi = (struct omfs_inode *) bh->b_data; + memcpy (oi->name, name, namelen); + memset (oi->name + namelen, 0, OMFS_NAMELEN - namelen); + oi->sibling = cpu_to_be64(cluster); + oi->parent = cpu_to_be64(dir->i_ino); + mark_buffer_dirty(bh); + brelse(bh); + + dir->i_ctime = CURRENT_TIME_SEC; + + // mark affected inodes dirty to rebuild checksums + mark_inode_dirty(dir); + mark_inode_dirty(inode); + return 0; +out: + return -ENOMEM; +} + +static int omfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + int err = 0; + struct inode *inode; + + mode |= S_IFDIR; + + inode = omfs_new_inode(dir, mode); + if (!inode) + return -ENOSPC; + + if (dir->i_mode & S_ISGID) { + inode->i_gid = dir->i_gid; + if (S_ISDIR(mode)) + inode->i_mode |= S_ISGID; + } + + printk(KERN_DEBUG "I just made an inode at block %lx\n", inode->i_ino); + + err = omfs_make_empty(inode, dir->i_sb); + if (err) + goto out; + + err = omfs_add_link(dentry, inode); + if (err) + goto out; + + d_instantiate(dentry, inode); +out: + return err; +} + +// TODO: omfs_make_empty can handle both dirs and files; then +// omfs_mkdir and omfs_create are the same (use mknod?) + +static int omfs_create(struct inode * dir, struct dentry * dentry, int mode, + struct nameidata *nd) +{ + int err; + struct inode * inode; + + mode |= S_IFREG; + + inode = omfs_new_inode(dir, mode); + if (!inode) + return -ENOSPC; + + err = omfs_make_empty_file(inode, dir->i_sb); + if (err) + goto out; + + err = omfs_add_link(dentry, inode); + if (err) + goto out; + + d_instantiate(dentry, inode); +out: + return err; +} +#endif + +static struct dentry *omfs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ + struct buffer_head *bh; + struct inode *inode = NULL; + + if (dentry->d_name.len > OMFS_NAMELEN) + return ERR_PTR(-ENAMETOOLONG); + + bh = omfs_find_entry(dir, dentry->d_name.name, dentry->d_name.len); + if (bh) { + struct omfs_inode *oi = (struct omfs_inode *)bh->b_data; + ino_t ino = be64_to_cpu(oi->head.self); + brelse(bh); + inode = iget(dir->i_sb, ino); + if (!inode) { + return ERR_PTR(-EINVAL); + } + } + d_add(dentry, inode); + return NULL; +} + +static int omfs_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct inode *dir = filp->f_dentry->d_inode; + struct buffer_head *bh = NULL, *bh2; + struct omfs_inode *oi; + loff_t bl_ofs; + loff_t offset, res; + u64 cluster; + unsigned char d_type; + ino_t self; + unsigned int hchain, hindex; + sector_t block; + int nbuckets; + + printk(KERN_DEBUG "Reading inode %lx\n", dir->i_ino); + + switch (filp->f_pos) { + case 0: + if (filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR) < 0) + goto success; + filp->f_pos++; + /* fall through */ + case 1: + if (filldir(dirent, "..", 2, 1, parent_ino(filp->f_dentry), DT_DIR) < 0) + goto success; + filp->f_pos = 0x01000000; + /* fall through */ + } + + nbuckets = (dir->i_size - OMFS_DIR_START) / 8; + + /* high byte stores bucket + 1 and low 24 bits store hash index */ + hchain = (filp->f_pos >> 24) -1; + hindex = filp->f_pos & 0xffffff; + + for (; hchain < nbuckets; hchain++) { + offset = OMFS_DIR_START + hchain * 8; + + bl_ofs = offset & (dir->i_sb->s_blocksize - 1); + if (!bl_ofs || !bh) { // wrapped a block + brelse(bh); + block = offset >> dir->i_sb->s_blocksize_bits; + bh = sb_bread(dir->i_sb, block + + clus_to_blk(dir->i_ino)); + if (!bh) + goto no_bh; + } + cluster = be64_to_cpu(*((u64 *) & bh->b_data[bl_ofs])); + while (cluster != ~0) { + bh2 = sb_bread(dir->i_sb, clus_to_blk(cluster)); + if (!bh2) + goto no_bh2; + + oi = (struct omfs_inode *)bh2->b_data; + self = cluster; + cluster = be64_to_cpu(oi->sibling); + + if (hindex) { + hindex--; + brelse(bh2); + continue; + } + + if (oi->type == OMFS_DIR) + d_type = DT_DIR; + else + d_type = DT_REG; + + res = filldir(dirent, oi->name, strnlen(oi->name, + OMFS_NAMELEN), filp->f_pos, self, d_type); + brelse(bh2); + if (res < 0) + { + brelse(bh); + goto success; + } + filp->f_pos++; + } + filp->f_pos = (hchain+2) << 24; + } + brelse(bh); + +success: + return 0; +no_bh2: + brelse(bh); +no_bh: + return -EINVAL; +} + +struct inode_operations omfs_dir_inops = { + .lookup = omfs_lookup, +#ifdef OMFS_WRITE + .mkdir = omfs_mkdir, + .rename = simple_rename, + .create = omfs_create, +#endif +}; + +struct file_operations omfs_dir_operations = { + .read = generic_read_dir, + .readdir = omfs_readdir, +}; diff --git a/fs/omfs/file.c b/fs/omfs/file.c new file mode 100644 index 0000000..972792a --- /dev/null +++ b/fs/omfs/file.c @@ -0,0 +1,202 @@ +/* + * fs/omfs/file.c + * OMFS (as used by RIO Karma) file operations. + * Copyright (C) 2005 Bob Copeland + */ + +#include +#include +#include +#include "omfs.h" + +int omfs_make_empty_file(struct inode *inode, struct super_block *sb) +{ + int block = clus_to_blk(inode->i_ino); + struct buffer_head *bh; + + struct omfs_extent oe = { + .next = ~0, + .extent_count = 0, + .fill = cpu_to_be32(0x22), /* FIXME ? */ + }; + + bh = sb_bread(sb, block++); + if (!bh) + return -ENOMEM; + + memset(bh->b_data, 0, OMFS_EXTENT_START); + memcpy(&bh->b_data[OMFS_EXTENT_START], &oe, sizeof(struct omfs_extent)); + mark_buffer_dirty(bh); + brelse(bh); + + return 0; +} + +/* + * Scans across the directory table for a given block number. There are + * several possible exit conditions: + * 1) we find the cluster and return it + * 2) count is zero (for this table); we return 0 and *count is zero. + * 3) no more pointers on this page (but more in table); return 0 and + * *count is non-zero. caller must repeat with ent on next block. + */ +static sector_t find_block(struct inode *inode, struct omfs_extent_entry *ent, + sector_t block, sector_t *searched, + int block_ofs, int *count) +{ + for (; *count > 0 && block_ofs < inode->i_sb->s_blocksize; + (*count)--, block_ofs += sizeof(struct omfs_extent_entry)) + { + int numblocks = clus_to_blk(be64_to_cpu(ent->blocks)); + if (block >= *searched && + block < (*searched + numblocks)) + { + // found it at cluster + (block - searched) + return clus_to_blk(be64_to_cpu(ent->cluster)) + + block - *searched; + } + *searched += numblocks; + ent++; + } + return 0; +} + +static int omfs_get_block(struct inode *inode, sector_t block, + struct buffer_head *bh_result, int create) +{ + struct buffer_head *bh; + sector_t next, block_start, offset, searched=0, new_cluster; + int block_ofs, extent_count; + struct omfs_extent *oe; + struct omfs_extent_entry *entry; + + block_start = clus_to_blk(inode->i_ino); + bh = sb_bread(inode->i_sb, block_start); + if (!bh) + goto err; + + oe = (struct omfs_extent *)(&bh->b_data[OMFS_EXTENT_START]); + + for(;;) { + int count; + + extent_count = be32_to_cpu(oe->extent_count); + next = be64_to_cpu(oe->next); + entry = &oe->entry; + block_ofs = (long) entry - (long)bh->b_data; + + count = extent_count; + while (count > 0) { + offset = find_block(inode, entry, block, &searched, + block_ofs, &count); + if (offset > 0) { + brelse(bh); + map_bh(bh_result, inode->i_sb, offset); + return 0; + } + if (count) { + // spilled over a buffer + brelse(bh); + bh = sb_bread(inode->i_sb, ++block_start); + if (!bh) goto err; + entry = (struct omfs_extent_entry *) bh->b_data; + block_ofs = 0; + } + } + if (next == ~0) + break; + + brelse(bh); + bh = sb_bread(inode->i_sb, clus_to_blk(next)); + if (!bh) goto err; + oe = (struct omfs_extent *) (&bh->b_data[OMFS_EXTENT_CONT]); + } + if (!create) { + brelse(bh); + return 0; + } + + /* reached the end of the extent table with no blocks mapped. + * there are three possibilities for adding: grow last extent, + * add a new extent to the current extent table, and add a + * continuation inode. in last two cases need an allocator for + * sbi->cluster_size + */ + + /* trivially grow current extent, if next block is not taken */ + block_ofs = (long) entry - (long)bh->b_data; + if (extent_count > 1 && block_ofs != 0) + { + entry -= sizeof(struct omfs_extent_entry); + + new_cluster = be64_to_cpu(entry->cluster) + + be64_to_cpu(entry->blocks) + 1; + + if (omfs_reserve_block(inode->i_sb, new_cluster)) + { + entry->blocks = + cpu_to_be64(be64_to_cpu(entry->blocks) + 1); + mark_buffer_dirty(bh); + brelse(bh); + + printk(KERN_DEBUG + "omfs: extended %lx to: %llx %llx (%lx)\n", + inode->i_ino, be64_to_cpu(entry->cluster), + be64_to_cpu(entry->blocks), new_cluster); + + map_bh(bh_result, inode->i_sb, + clus_to_blk(new_cluster)); + return 0; + } + } + + // add a new extent + // if (oe->count * sizeof(entry) + offset > something) + // add a continuation inode + + // oe points to nothing! +err: + return -EIO; +} + +static int omfs_readpage(struct file *file, struct page *page) +{ + return block_read_full_page(page, omfs_get_block); +} + +#ifdef OMFS_WRITE +static int omfs_writepage(struct page *page, struct writeback_control *wbc) +{ + return block_write_full_page(page, omfs_get_block, wbc); +} + +static int omfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) +{ + return block_prepare_write(page,from,to,omfs_get_block); +} + +static sector_t omfs_bmap(struct address_space *mapping, sector_t block) +{ + return generic_block_bmap(mapping,block,omfs_get_block); +} +#endif + +struct file_operations omfs_file_operations = { + .llseek = generic_file_llseek, + .read = generic_file_read, + .write = generic_file_write, + .mmap = generic_file_mmap, + .sendfile = generic_file_sendfile, +}; + +struct address_space_operations omfs_aops = { + .readpage = omfs_readpage, +#ifdef OMFS_WRITE + .writepage = omfs_writepage, + .sync_page = block_sync_page, + .prepare_write = omfs_prepare_write, + .commit_write = generic_commit_write, + .bmap = omfs_bmap, +#endif +}; + diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c new file mode 100644 index 0000000..68d1476 --- /dev/null +++ b/fs/omfs/inode.c @@ -0,0 +1,504 @@ +/* + * Optimized MPEG FS - inode and super operations. + * Copyright (C) 2005 Bob Copeland + */ +#include +#include +#include +#include +#include +#include +#include "omfs.h" + +MODULE_AUTHOR("Bob Copeland "); +MODULE_DESCRIPTION("OMFS (ReplayTV/Karma) Filesystem for Linux"); +MODULE_LICENSE("GPL"); + +#ifdef OMFS_WRITE + +int omfs_reserve_block(struct super_block *sb, sector_t block) +{ + struct buffer_head *bh; + struct omfs_sb_info *sbi = OMFS_SB(sb); + int bits_per_entry = 8 * sb->s_blocksize; + int map = block / bits_per_entry; + int bit = block % bits_per_entry; + + if (!test_and_set_bit(bit, sbi->imap[map])) + return 0; + + if (sbi->bitmap_ino > 0) + { + bh = sb_bread(sb, clus_to_blk(sbi->bitmap_ino + map)); + set_bit(bit, (long*) bh->b_data); + mark_buffer_dirty(bh); + brelse(bh); + } + return 1; +} + +static void omfs_set_bit(struct super_block *sb, int block, int bit) +{ + struct buffer_head *bh; + struct omfs_sb_info *sbi = OMFS_SB(sb); + + set_bit(bit, sbi->imap[block]); + if (sbi->bitmap_ino > 0) + { + bh = sb_bread(sb, clus_to_blk(sbi->bitmap_ino + block)); + set_bit(bit, (long*) bh->b_data); + mark_buffer_dirty(bh); + brelse(bh); + } +} + +static int find_empty_block(struct super_block *sb, int mode, ino_t *ino) +{ + int bit=0, i; + struct omfs_sb_info *sbi = OMFS_SB(sb); + int block_request = 1; + int bits_per_entry = 8 * sb->s_blocksize; + + if (S_ISREG(mode)) + block_request = sbi->clustersize; + + for (i=0; i < sbi->imap_size; i++) + { + bit = find_first_zero_bit(sbi->imap[i], bits_per_entry); + if (bit < bits_per_entry) + break; + } + *ino = i * bits_per_entry + bit; + if (*ino >= sbi->num_blocks) + return -ENOSPC; + omfs_set_bit(sb, i, bit); + return 0; +} +#endif + +static kmem_cache_t *omfs_inode_cachep; + +static struct inode *omfs_alloc_inode(struct super_block *sb) +{ + struct omfs_inode_info *oinf; + oinf = (struct omfs_inode_info *) kmem_cache_alloc(omfs_inode_cachep, + SLAB_KERNEL); + if (!oinf) + return NULL; + return &oinf->vfs_inode; +} + +static void omfs_destroy_inode(struct inode *inode) +{ + kmem_cache_free(omfs_inode_cachep, OMFS_I(inode)); +} + +static void init_once(void *p, kmem_cache_t *cachep, unsigned long flags) +{ + struct omfs_inode_info *oinf = (struct omfs_inode_info *) p; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) + inode_init_once(&oinf->vfs_inode); +} + +static int init_inodecache(void) +{ + omfs_inode_cachep = kmem_cache_create("omfs_inode_cache", + sizeof(struct omfs_inode_info), + 0, SLAB_RECLAIM_ACCOUNT, + init_once, NULL); + if (!omfs_inode_cachep) + return -ENOMEM; + return 0; +} + +static void destroy_inodecache(void) +{ + if (kmem_cache_destroy(omfs_inode_cachep)) + printk(KERN_INFO "omfs_inode_cache: not all structures were freed\n"); +} + +#ifdef OMFS_WRITE +struct inode *omfs_new_inode(struct inode *dir, int mode) +{ + struct inode *inode; + ino_t ino; + int res; + + inode = new_inode(dir->i_sb); + if (!inode) + return ERR_PTR(-ENOMEM); + + res = find_empty_block(dir->i_sb, mode, &ino); + if (res) + return ERR_PTR(res); + + inode->i_ino = ino; + inode->i_mode = mode; + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + inode->i_blksize = PAGE_SIZE; + inode->i_blocks = 0; + inode->i_mapping->a_ops = &omfs_aops; + + OMFS_I(inode)->state = OMFS_STATE_NEW; + + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + switch (mode & S_IFMT) { + case S_IFDIR: + inode->i_op = &omfs_dir_inops; + inode->i_fop = &omfs_dir_operations; + inode->i_size = OMFS_SB(inode->i_sb)->sys_blocksize; + inode->i_nlink++; /* "." entry */ + break; + case S_IFREG: + inode->i_fop = &omfs_file_operations; + inode->i_size = 0; + break; + } + + insert_inode_hash(inode); + printk(KERN_DEBUG "omfs: new marked dirty: %lx\n", inode->i_ino); + mark_inode_dirty(inode); + return inode; +} + +static int omfs_write_inode(struct inode *inode, int wait) +{ + struct omfs_inode *oi; + struct omfs_sb_info *sbi; + struct buffer_head *bh, *bh2; + unsigned int block; + u64 ctime; + int i; + + printk(KERN_DEBUG "omfs: writing inode %lx\n", inode->i_ino); + + sbi = OMFS_SB(inode->i_sb); + + /* get current inode since we may have written sibling ptrs etc. */ + block = clus_to_blk(inode->i_ino); + bh = sb_bread(inode->i_sb, block); + if (!bh) + return -EIO; + + oi = (struct omfs_inode *) bh->b_data; + if (OMFS_I(inode)->state & OMFS_STATE_NEW) + { + memset(oi, 0, sizeof(struct omfs_header)); + OMFS_I(inode)->state &= ~OMFS_STATE_NEW; + } + + oi->head.self = cpu_to_be64(inode->i_ino); + if (S_ISDIR(inode->i_mode)) { + oi->type = OMFS_DIR; + } else if (S_ISREG(inode->i_mode)) { + oi->type = OMFS_FILE; + } else + BUG(); + + oi->head.body_size = cpu_to_be32(sbi->sys_blocksize - + sizeof(struct omfs_header)); + oi->head.version = 1; + oi->head.type = OMFS_INODE_NORMAL; + oi->head.magic = OMFS_IMAGIC; + oi->size = cpu_to_be64(inode->i_size); + + ctime = inode->i_ctime.tv_sec * 1000LL + + ((inode->i_ctime.tv_nsec + 999)/1000); + oi->ctime = cpu_to_be64(ctime); + + if (omfs_update_checksums(oi, inode) != 0) + return -EIO; + + mark_buffer_dirty(bh); + brelse(bh); + + // if mirroring writes, copy to next mirror + for (i = sbi->mirrors; i > 0; i--) + { + int j; + int num_blks = sbi->sys_blocksize / inode->i_sb->s_blocksize; + for (j=0; j < num_blks; j++, block++) + { + bh = sb_bread(inode->i_sb, block); + if (!bh) + return -EIO; + bh2 = sb_bread(inode->i_sb, block + num_blks); + if (!bh2) + { + brelse(bh); + return -EIO; + } + memcpy(bh2->b_data, bh->b_data, bh->b_size); + mark_buffer_dirty(bh2); + brelse(bh); + brelse(bh2); + } + } + + // do_sync? + return 0; +} +#endif + +void omfs_read_inode(struct inode *inode) +{ + struct omfs_inode *oi; + struct omfs_inode_info *oinf = OMFS_I(inode); + struct buffer_head *bh; + unsigned int block; + u64 ctime; + unsigned long nsecs; + ino_t ino = inode->i_ino; + + // check against num_blocks + block = clus_to_blk(ino); + bh = sb_bread(inode->i_sb, block); + if (!bh) { + make_bad_inode(inode); + return; + } + + oi = (struct omfs_inode *)bh->b_data; + + // check self + if (ino != be64_to_cpu(oi->head.self)) { + make_bad_inode(inode); + return; + } + + inode->i_uid = 0; + inode->i_gid = 0; + + ctime = be64_to_cpu(oi->ctime); + nsecs = do_div(ctime, 1000) * 1000L; + + inode->i_atime.tv_sec = ctime; + inode->i_mtime.tv_sec = ctime; + inode->i_ctime.tv_sec = ctime; + inode->i_atime.tv_nsec = nsecs; + inode->i_mtime.tv_nsec = nsecs; + inode->i_ctime.tv_nsec = nsecs; + + oinf->state = 0; + + if (oi->type == OMFS_DIR) { + inode->i_mode = S_IFDIR | S_IRUGO | S_IWUGO | S_IXUGO; + inode->i_op = &omfs_dir_inops; + inode->i_fop = &omfs_dir_operations; + inode->i_size = be32_to_cpu(oi->head.body_size) + + sizeof(struct omfs_header); + } else if (oi->type == OMFS_FILE) { + inode->i_mode = S_IFREG | S_IRUGO; + inode->i_mapping->a_ops = &omfs_aops; + inode->i_fop = &omfs_file_operations; + inode->i_size = be64_to_cpu(oi->size); + } + brelse(bh); +} + + +static void omfs_put_super(struct super_block *sb) +{ + struct omfs_sb_info *sbi = OMFS_SB(sb); + if (sbi) + { + kfree(sbi->imap); + kfree(sbi); + } + sb->s_fs_info = NULL; +} + +static int omfs_statfs(struct super_block *s, struct kstatfs *buf) +{ + struct omfs_sb_info *sbi = OMFS_SB(s); + buf->f_type = OMFS_MAGIC; + buf->f_bsize = sbi->blocksize; + buf->f_blocks = sbi->num_blocks; + buf->f_files = sbi->num_blocks; + buf->f_namelen = OMFS_NAMELEN; + + buf->f_bfree = buf->f_bavail = buf->f_ffree = + omfs_count_free(s); + return 0; +} + +struct super_operations omfs_sops = { + .alloc_inode = omfs_alloc_inode, + .destroy_inode = omfs_destroy_inode, +#ifdef OMFS_WRITE + .write_inode = omfs_write_inode, +#endif + .read_inode = omfs_read_inode, + .put_super = omfs_put_super, + .statfs = omfs_statfs, +}; + +/* + * For Rio Karma, there is an on-disk free bitmap whose location is + * stored in the root block. For ReplayTV, there is no such free bitmap + * so we have to walk the tree. Both inodes and file data are allocated + * from the same map. This array can be big (300k) so we allocate + * in units of the blocksize. + */ +static int omfs_get_imap(struct super_block *sb) +{ + int bitmap_size; + int array_size; + int count; + struct omfs_sb_info *sbi = OMFS_SB(sb); + struct buffer_head *bh; + unsigned long **ptr; + sector_t block; + + bitmap_size = (sbi->num_blocks + 7) / 8; + array_size = (bitmap_size + sb->s_blocksize - 1) / sb->s_blocksize; + + printk(KERN_DEBUG "omfs: trying to alloc %d bytes\n", array_size); + sbi->imap_size = array_size; + sbi->imap = kzalloc(array_size, GFP_KERNEL); + if (!sbi->imap) + return -ENOMEM; + + block = clus_to_blk(sbi->bitmap_ino); + ptr = sbi->imap; + for (count = bitmap_size; count > 0; count -= sb->s_blocksize) { + bh = sb_bread(sb, block++); + if (!bh) + goto nomem; + *ptr = kmalloc(sb->s_blocksize, GFP_KERNEL); + memcpy(*ptr, bh->b_data, sb->s_blocksize); + brelse(bh); + ptr ++; + } + return 0; +nomem: + printk(KERN_DEBUG "died at %ld\n", block); + for (count=0; countimap[count]); + } + printk(KERN_DEBUG "Freeing imap\n"); + kfree(sbi->imap); + sbi->imap = NULL; + return -ENOMEM; +} + +static int omfs_fill_super(struct super_block *sb, void *data, int silent) +{ + struct buffer_head *bh=NULL, *bh2=NULL; + struct omfs_super_block *omfs_sb; + struct omfs_root_block *omfs_rb; + struct omfs_sb_info *sbi; + struct inode *root; + sector_t start; + int ret = 0; + + sbi = kzalloc(sizeof(struct omfs_sb_info), GFP_KERNEL); + if (!sbi) + return -ENOMEM; + sb->s_fs_info = sbi; + +#ifndef OMFS_WRITE + sb->s_flags |= MS_RDONLY; +#endif + sb->s_maxbytes = 0xffffffff; + + // We always operate on single sectors since OMFS partitions + // regularly use blocks larger than PAGE_SIZE + // (blocksize *could* be anything that is a factor of + // sys_blocksize, however, if changing clus_to_blk) + sb_set_blocksize(sb, 0x200); + + if (!(bh = sb_bread(sb, 0))) + goto out; + + omfs_sb = (struct omfs_super_block *)bh->b_data; + + if (be32_to_cpu(omfs_sb->magic) != OMFS_MAGIC) { + if (!silent) + printk(KERN_ERR "omfs: Invalid superblock (%x)\n", + omfs_sb->magic); + goto out; + } + sb->s_magic = OMFS_MAGIC; + + start = clus_to_blk(be64_to_cpu(omfs_sb->root_block)); + if (!(bh2 = sb_bread(sb, start))) + goto out; + + omfs_rb = (struct omfs_root_block *)bh2->b_data; + printk(KERN_DEBUG "omfs: volume: %s\n", omfs_rb->name); + + sbi->num_blocks = be64_to_cpu(omfs_sb->num_blocks); + sbi->blocksize = be32_to_cpu(omfs_sb->blocksize); + sbi->mirrors = be32_to_cpu(omfs_sb->mirrors); + sbi->root_ino = be64_to_cpu(omfs_sb->root_block); + sbi->sys_blocksize = be32_to_cpu(omfs_sb->sys_blocksize); + sbi->bitmap_ino = be64_to_cpu(omfs_rb->bitmap); + sbi->clustersize = be32_to_cpu(omfs_rb->clustersize); + + ret = omfs_get_imap(sb); + if (ret) + goto end; + + sb->s_op = &omfs_sops; + root = iget(sb, be64_to_cpu(omfs_rb->root_dir)); + + sb->s_root = d_alloc_root(root); + if (!sb->s_root) { + iput(root); + goto out; + } + goto end; + +out: + ret = -EINVAL; + +end: + if (bh2) + brelse(bh2); + if (bh) + brelse(bh); + return ret; +} + +static struct super_block *omfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data) +{ + return get_sb_bdev(fs_type, flags, dev_name, data, omfs_fill_super); +} + +static struct file_system_type omfs_fs_type = { + .owner = THIS_MODULE, + .name = "omfs", + .get_sb = omfs_get_sb, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; + +static int __init init_omfs_fs(void) +{ + int err = init_inodecache(); + if (err) + goto out; + + err = register_filesystem(&omfs_fs_type); + if (err) + destroy_inodecache(); +out: + return err; +} + +static void __exit exit_omfs_fs(void) +{ + unregister_filesystem(&omfs_fs_type); + destroy_inodecache(); +} + +module_init(init_omfs_fs); +module_exit(exit_omfs_fs); diff --git a/fs/omfs/omfs.h b/fs/omfs/omfs.h new file mode 100644 index 0000000..344cf6b --- /dev/null +++ b/fs/omfs/omfs.h @@ -0,0 +1,140 @@ +#ifndef _OMFS_H +#define _OMFS_H + +#include +#include +#include + +#define OMFS_MAGIC 0xC2993D87 +#define OMFS_IMAGIC 0xD2 + +#define OMFS_DIR 'D' +#define OMFS_FILE 'F' +#define OMFS_INODE_NORMAL 'e' +#define OMFS_INODE_SYSTEM 's' +#define OMFS_NAMELEN 256 +#define OMFS_DIR_START 0x1b8 +#define OMFS_EXTENT_START 0x1d0 +#define OMFS_EXTENT_CONT 0x40 +#define OMFS_XOR_COUNT 19 + +#define OMFS_WRITE + +#define OMFS_STATE_NEW 1 + +/* In-memory structures */ +struct omfs_sb_info { + u64 num_blocks; + u64 bitmap_ino; + u64 root_ino; + u32 blocksize; + u32 mirrors; + u32 sys_blocksize; + u32 clustersize; + unsigned long **imap; + int imap_size; +}; + +struct omfs_inode_info { + u8 state; + struct inode vfs_inode; +}; + +/* On-disk structures */ +struct omfs_super_block { + char fill1[256]; + __be64 root_block; + __be64 num_blocks; + __be32 magic; + __be32 blocksize; + __be32 mirrors; + __be32 sys_blocksize; +}; + +struct omfs_header { + __be64 self; + __be32 body_size; + __be16 crc; + char fill1[2]; + u8 version; + char type; + u8 magic; + u8 check_xor; + __be32 fill2; +}; + +struct omfs_root_block { + struct omfs_header head; + __be64 fill1; + __be64 num_blocks; + __be64 root_dir; + __be64 bitmap; + __be32 blocksize; + __be32 clustersize; + __be64 mirrors; + char name[OMFS_NAMELEN]; +}; + +struct omfs_inode { + struct omfs_header head; + __be64 parent; + __be64 sibling; + __be64 ctime; + char fill1[35]; + char type; + __be32 fill2; + char fill3[64]; + char name[OMFS_NAMELEN]; + __be64 size; +}; + +struct omfs_extent_entry { + __be64 cluster; + __be64 blocks; +}; + +struct omfs_extent { + __be64 next; + __be32 extent_count; + __be32 fill; + struct omfs_extent_entry entry; +}; + + +/* convert a cluster number to a 512-byte block number */ +static inline sector_t clus_to_blk(sector_t in) +{ + return in << 4; +} + +static inline struct omfs_sb_info *OMFS_SB(struct super_block *sb) +{ + return sb->s_fs_info; +} + +static inline struct omfs_inode_info *OMFS_I(struct inode *inode) +{ + return list_entry(inode, struct omfs_inode_info, vfs_inode); +} + +/* bitmap.c */ +extern unsigned long omfs_count_free(struct super_block *sb); + +/* checksum.c */ +extern int omfs_update_checksums(struct omfs_inode *oi, struct inode *inode); + +/* dir.c */ +extern struct file_operations omfs_dir_operations; +extern struct inode_operations omfs_dir_inops; + +/* file.c */ +extern struct file_operations omfs_file_operations; +extern struct address_space_operations omfs_aops; +extern int omfs_make_empty_file(struct inode *inode, struct super_block *sb); + +/* inode.c */ +extern void omfs_read_inode(struct inode *inode); +extern struct inode *omfs_new_inode(struct inode *dir, int mode); +extern int omfs_reserve_block(struct super_block *sb, sector_t block); + +#endif diff --git a/fs/partitions/Kconfig b/fs/partitions/Kconfig index deb25b6..f916ef1 100644 --- a/fs/partitions/Kconfig +++ b/fs/partitions/Kconfig @@ -225,4 +225,11 @@ config EFI_PARTITION were partitioned using EFI GPT. Presently only useful on the IA-64 platform. +config KARMA_PARTITION + bool "Karma Partition support" + depends on PARTITION_ADVANCED + help + Say Y here if you would like to mount the Rio Karma MP3 player, + as it uses a proprietary partition table. + # define_bool CONFIG_ACORN_PARTITION_CUMANA y diff --git a/fs/partitions/Makefile b/fs/partitions/Makefile index 66d5cc2..42c7d38 100644 --- a/fs/partitions/Makefile +++ b/fs/partitions/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_SUN_PARTITION) += sun.o obj-$(CONFIG_ULTRIX_PARTITION) += ultrix.o obj-$(CONFIG_IBM_PARTITION) += ibm.o obj-$(CONFIG_EFI_PARTITION) += efi.o +obj-$(CONFIG_KARMA_PARTITION) += karma.o diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 8dc1822..ac10938 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -35,6 +35,7 @@ #include "ibm.h" #include "ultrix.h" #include "efi.h" +#include "karma.h" #ifdef CONFIG_BLK_DEV_MD extern void md_autodetect_dev(dev_t dev); @@ -103,6 +104,9 @@ static int (*check_part[])(struct parsed #ifdef CONFIG_IBM_PARTITION ibm_partition, #endif +#ifdef CONFIG_KARMA_PARTITION + karma_partition, +#endif NULL }; diff --git a/fs/partitions/karma.c b/fs/partitions/karma.c new file mode 100644 index 0000000..b4f5fed --- /dev/null +++ b/fs/partitions/karma.c @@ -0,0 +1,57 @@ +/* + * fs/partitions/karma.c + * Rio Karma partition info. + * + * Copyright (C) 2005 Bob Copeland (me@bobcopeland.com) + * based on ofs.c + */ + +#include "check.h" +#include "karma.h" + +int karma_partition(struct parsed_partitions *state, struct block_device *bdev) +{ + int i; + int slot = 1; + Sector sect; + unsigned char *data; + struct disklabel { + u8 reserved[270]; + struct d_partition { + __le32 res; + u8 fstype; + u8 res2[3]; + __le32 offset; + __le32 size; + } partitions[2]; + u8 blank[208]; + __le16 magic; + } __attribute__((packed)) *label; + struct d_partition *p; + + data = read_dev_sector(bdev, 0, §); + if (!data) + return -1; + + label = (struct disklabel *) data; + if (le16_to_cpu(label->magic) != KARMA_LABEL_MAGIC) { + put_dev_sector(sect); + return 0; + } + + p = label->partitions; + for (i = 0 ; i < 2; i++, p++) { + if (slot == state->limit) + break; + + if (p->fstype == 0x4d && le32_to_cpu(p->size)) { + put_partition(state, slot, le32_to_cpu(p->offset), + le32_to_cpu(p->size)); + } + slot++; + } + printk("\n"); + put_dev_sector(sect); + return 1; +} + diff --git a/fs/partitions/karma.h b/fs/partitions/karma.h new file mode 100644 index 0000000..ecf7d3f --- /dev/null +++ b/fs/partitions/karma.h @@ -0,0 +1,8 @@ +/* + * fs/partitions/karma.h + */ + +#define KARMA_LABEL_MAGIC 0xAB56 + +int karma_partition(struct parsed_partitions *state, struct block_device *bdev); +