From: Adrian M. <zx8...@us...> - 2002-11-03 00:35:55
|
Update of /cvsroot/linuxdc/linux-sh-dc/fs/vmufs In directory usw-pr-cvs1:/tmp/cvs-serv32754/fs/vmufs Added Files: Tag: linux-sh-dc-2_4-branch vmufs.h vmufs_inode.c vmufs_sblock.c Log Message: VMU File System support --- NEW FILE: vmufs.h --- /****************************** * VMU File System Headers * *****************************/ /* Licenced under the GPL v2 * Copyright Adrian McMenamin, 2002 * ad...@mc... */ struct inode_operations vmufs_inode_operations; struct inode_operations vmufs_file_inode_operations; struct file_operations vmufs_file_dir_operations; struct file_operations vmufs_file_operations; /* Memory card details */ typedef struct memcard_s { long partitions; long blocklen; long writecnt; long readcnt; long removable; long numblocks; struct mtd_info *mtd; } memcard_t; struct vmufs_file_info { struct vmufs_file_info *prev; struct vmufs_file_info *next; __u8 ftype; __u8 copy_pro; __u16 fblk; char fname[12]; int date[8]; __u16 blk_cnt; __u16 hd_off; }; int int_from_bcd(__u8 bcd); #ifndef MTD_BLOCK_MAJOR #define MTD_BLOCK_MAJOR 31 #endif #define ROOT_BLOCK 255 --- NEW FILE: vmufs_inode.c --- #include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/locks.h> #include <linux/blkdev.h> #include <linux/mtd/mtd.h> #include "vmufs.h" struct dentry *vmufs_inode_lookup(struct inode *in, struct dentry *dent) { struct super_block *sb = in->i_sb; struct buffer_head *bh; bh = bread(sb->s_dev, 253, 512); struct vmufs_file_info *first_one = NULL; struct vmufs_file_info *last_one = NULL; int i, fno = 0; do { struct vmufs_file_info *saved_file = kmalloc(sizeof(struct vmufs_file_info), GFP_KERNEL); saved_file->prev = last_one; saved_file->ftype = ((__u8 *) bh->b_data)[0 + fno * 0x20]; if (saved_file->ftype == 0) { /* no file */ if (last_one) last_one->next = NULL; kfree(saved_file); break; } if (first_one == NULL) first_one = saved_file; if (last_one) last_one->next = saved_file; last_one = saved_file; saved_file->copy_pro = ((__u8 *) bh->b_data)[1 + fno * 0x20]; saved_file->fblk = ((__u16 *) bh->b_data)[1 + fno * 0x10]; memcpy(saved_file->fname, bh->b_data + 4 + fno * 0x20, 12); for (i = 0; i < 8; i++) { saved_file->date[i] = int_from_bcd(((__u8 *) bh->b_data)[i + 16 + fno * 0x20]); } fno++; } while (1); brelse(bh); /*Got the list - does one match? */ int error; struct inode *ino; do { if (first_one == NULL) { error = -ENOENT; return ERR_PTR(error); } if (memcmp(dent->d_name.name, first_one->fname, 12) != 0) { if (first_one->next) { first_one = first_one->next; kfree(first_one->prev); } else { kfree(first_one); error = -ENOENT; return ERR_PTR(error); } } else { ino = iget(sb, le16_to_cpu(first_one->fblk)); break; } } while (1); d_add(dent, ino); do { if (first_one->next) { first_one = first_one->next; kfree(first_one->prev); } else { kfree(first_one); first_one = NULL; } } while (first_one); return NULL; } struct inode_operations vmufs_inode_operations = { /* int (*create) (struct inode *,struct dentry *,int); struct dentry * (*lookup) (struct inode *,struct dentry *); int (*link) (struct dentry *,struct inode *,struct dentry *); int (*unlink) (struct inode *,struct dentry *); int (*symlink) (struct inode *,struct dentry *,const char *); int (*mkdir) (struct inode *,struct dentry *,int); int (*rmdir) (struct inode *,struct dentry *); int (*mknod) (struct inode *,struct dentry *,int,int); int (*rename) (struct inode *, struct dentry *, struct inode *, struct dentry *); int (*readlink) (struct dentry *, char *,int); int (*follow_link) (struct dentry *, struct nameidata *); void (*truncate) (struct inode *); int (*permission) (struct inode *, int); int (*revalidate) (struct dentry *); int (*setattr) (struct dentry *, struct iattr *); int (*getattr) (struct dentry *, struct iattr *); */ lookup:vmufs_inode_lookup, }; int vmufs_readdir(struct file *filp, void *dirent, filldir_t filldir) { /* readdir is given: */ /* VFS inode */ /* VFS file */ /* VFS dirent */ /* readdir must: */ /* wander through the directory */ /* find next after VFS file's position */ /* call dcache_add */ /* call filldir with dirent with as many dir entries as we like?? */ /* read in the contents of the directory - there is only one */ int i; i = filp->f_pos; struct inode *inode = filp->f_dentry->d_inode; struct super_block *sb = inode->i_sb; struct buffer_head *bh; bh = bread(sb->s_dev, 253, 512); /* Special cases first */ if (i == 0) { if (filldir(dirent, ".", 1, i, inode->i_ino, DT_DIR) < 0) goto finish; i++; filp->f_pos++; } if (i == 1) { if (filldir(dirent, "..", 2, 2, inode->i_ino, DT_DIR) < 0) goto finish; i++; filp->f_pos++; } /* wander through the Directory and find files */ if (i > 200) { brelse(bh); return -1; /* Cannot be more than 200 */ } struct vmufs_file_info *saved_file = kmalloc(sizeof(struct vmufs_file_info), GFP_KERNEL); do { saved_file->ftype = ((__u8 *) bh->b_data)[0 + (i - 2) * 0x20]; if (saved_file->ftype == 0) { goto finish; } saved_file->fblk = le16_to_cpu(((__u16 *) bh->b_data)[1 + (i - 2) * 0x10]); memcpy(saved_file->fname, bh->b_data + 4 + (i - 2) * 0x20, 12); if (filldir (dirent, saved_file->fname, 12, i, saved_file->fblk, DT_REG) < 0) { goto finish; } i++; filp->f_pos++; } while (1); finish: i++; filp->f_pos++; kfree(saved_file); brelse(bh); return 0; } struct file_operations vmufs_file_dir_operations = { /* struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char *, size_t, loff_t *); ssize_t (*write) (struct file *, const char *, size_t, loff_t *); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); */ owner:THIS_MODULE, read:generic_read_dir, readdir:vmufs_readdir, fsync:file_fsync, }; ssize_t vmufs_file_read(struct file* file, char* buf, size_t count, loff_t* ppos) { char * readbuf = NULL; struct buffer_head *bh; unsigned long blcks_to_read, x; /* Get the inode */ struct inode *in = file->f_dentry->d_inode; long file_len = in->i_size; if (*ppos > file_len) return -EINVAL; /* Read in the file */ unsigned long blksize = in->i_blksize; unsigned long blkoffset = *ppos >> in->i_sb->s_blocksize_bits; if (*ppos + count > file_len) count = file_len - *ppos; blcks_to_read = count >> in->i_sb->s_blocksize_bits; readbuf = kmalloc(count, GFP_KERNEL); for (x = 0; x < blcks_to_read; x++) { bh = bread(in->i_sb->s_dev, in->i_ino - blkoffset - x, blksize); memcpy(readbuf + x * blksize, bh->b_data, blksize); brelse(bh); } /* Copy to user space */ copy_to_user(buf, readbuf, count); kfree(readbuf); *ppos += count; return count; } struct file_operations vmufs_file_operations = { read: vmufs_file_read, }; struct inode_operations vmufs_file_inode_operations = { }; --- NEW FILE: vmufs_sblock.c --- /******************************************* * * * VMUFS - file system support * * for Sega Dreamcast visual * * memory unit flash memory * * on Linux. * * http://linuxdc.org * * http://sourceforge.net/projects/linuxdc * * * * This software is copyright, 2002, * * Adrian McMenamin * * ad...@mc... * * * * This software is licencsed under the * * terms of the GNU General Public * * Licence version 2. * * http://www.fsf.org * * * * This software draws on various other * * bits of fs code - especially BFS and * * JFFS2. All copyrights acknowledged * ******************************************/ #include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/locks.h> #include <linux/blkdev.h> #include <linux/mtd/mtd.h> #include <linux/maple.h> #include <linux/time.h> #include "vmufs.h" int int_from_bcd(__u8 bcd) { int topnib = (bcd >> 4) & 0x000f; int botnib = bcd & 0x000f; return (topnib * 10) + botnib; } static struct super_block *vmufs_read_super(struct super_block *, void *, int); static DECLARE_FSTYPE(vmufs_fs_type, "vmufs", vmufs_read_super, FS_REQUIRES_DEV | FS_SINGLE); void vmufs_read_inode(struct inode *in) { /* From the HOWTO */ /*read_inode must: fill in the VFS inode: i_mode i_uid i_gid i_nlink i_size i_mtime i_atime i_ctime i_blocks i_blksize = PAGE_SIZE i_version = ++event (scheduler event count) i_op // depends on filetype! set rdev (for block & char filetypes) set any flags initialize any data */ /*Populate the inode */ struct super_block *sb = in->i_sb; if (in->i_ino == 255){ in->i_mode |= S_IFDIR; in->i_atime = CURRENT_TIME; /*Read in creation time */ struct buffer_head *bh; bh = sb_bread(sb, 255); int century = int_from_bcd((bh->b_data)[0x30]); int year = int_from_bcd((bh->b_data)[0x31]); int month = int_from_bcd((bh->b_data)[0x32]); int day = int_from_bcd((bh->b_data)[0x33]); int hour = int_from_bcd((bh->b_data)[0x34]); int minute = int_from_bcd((bh->b_data)[0x35]); int second = int_from_bcd((bh->b_data)[0x36]); in->i_ctime = in->i_mtime = mktime(century * 100 + year, month, day, hour, minute, second); in->i_mode |= S_IRUGO | S_IXUGO; /* This is read only at present */ in->i_uid = in->i_gid = 0; in->i_op = &vmufs_inode_operations; in->i_fop = &vmufs_file_dir_operations; /*Not in FAT table */ in->i_size = 512; /* Times */ in->i_blocks = 1; in->i_blksize = 512; in->i_version = ++event; brelse(bh); /* break; */ /* default: */ /* printk("Some other mode\n"); */ /* } */ } else { if (in->i_ino > 199) { /* Not here */ return; } /* a file, not a directory */ struct buffer_head *bh; in->i_mode |= S_IFREG; /* Scan through the directory to find the matching file */ bh = bread(sb->s_dev, 253, 512); int y; for (y = 0; y < 200; y++) { if (((__u16 *)bh->b_data)[y * 0x10 + 0x01] == in->i_ino) break; } if (y >= 200){ brelse(bh); /* TO DO: error return required */ return; } /* identified the correct directory entry */ int century = int_from_bcd((bh->b_data)[0x10 + y * 0x20]); int year = int_from_bcd((bh->b_data)[0x11 + y * 0x20]); int month = int_from_bcd((bh->b_data)[0x12 + y * 0x20]); int day = int_from_bcd((bh->b_data)[0x13 + y * 0x20]); int hour = int_from_bcd((bh->b_data)[0x14 + y * 0x20]); int minute = int_from_bcd((bh->b_data)[0x15 + y * 0x20]); int second = int_from_bcd((bh->b_data)[0x16 + y * 0x20]); in->i_ctime = in->i_mtime = mktime(century * 100 + year, month, day, hour, minute, second); in->i_mode |= S_IRUGO; /* Mode - is the file copiable? */ if (((__u8 *) bh->b_data)[0x01 + y * 0x20] == 0x00) in->i_mode |= S_IWUGO; /* Is file executible - ie a game */ if (((__u8 *)bh->b_data)[y * 0x20] == 0xcc) in->i_mode |= S_IXUGO; in->i_uid = in->i_gid = 0; in->i_op = &vmufs_file_inode_operations; in->i_fop = &vmufs_file_operations; in->i_blocks = le16_to_cpu(((__u16 *)bh->b_data)[y * 0x10 + 0x0C]); in->i_size = in->i_blocks * 512; in->i_blksize = 512; brelse(bh); } return; } void vmufs_put_super(struct super_block *sb) { /* put_super is given: VFS sb to umount put_super must: lock_super s_dev = 0 free any structures allocated mark super buffer dirty? brelse super buffer unlock_super decrement MOD count */ sb->s_dev = 0; } static int vmufs_statfs(struct super_block *sb, struct statfs *buf) { /* 12 struct statfs { 13 long f_type; 14 long f_bsize; 15 long f_blocks; 16 long f_bfree; 17 long f_bavail; 18 long f_files; 19 long f_ffree; 20 __kernel_fsid_t f_fsid; 21 long f_namelen; 22 long f_spare[6]; 23 }; */ int minor = MINOR(sb->s_dev); struct mtd_info *mtd = get_mtd_device(NULL, minor); struct maple_driver_data *d = (struct maple_driver_data *) (mtd->priv); memcard_t *memcard = (memcard_t *) (d->private_data); buf->f_type = 0x55555555; buf->f_bsize = mtd->erasesize; buf->f_bfree = memcard->numblocks; buf->f_bavail = 0; buf->f_ffree = 0; buf->f_namelen = 12; put_mtd_device(mtd); return 0; } static struct super_operations vmufs_super_operations = { read_inode:vmufs_read_inode, /* delete_inode: jffs2_delete_inode, */ put_super:vmufs_put_super, /* write_super: vmufs_write_super, */ statfs:vmufs_statfs, /* remount_fs: vmufs_remount_fs, clear_inode: vmufs_clear_inode */ }; static struct super_block *vmufs_read_super(struct super_block *sb, void *data, int silent) { int log_2 = 0; int erasesize; struct inode *root_i; kdev_t dev = sb->s_dev; /* Read off the details of this device */ if (MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR) { printk(KERN_ERR "vmufs: attempt to mount non-MTD device %s\n as vmu filesystem", kdevname(sb->s_dev)); return NULL; } int minor = MINOR(dev); struct mtd_info *mtd = get_mtd_device(NULL, minor); /*sb tasks - from draft HOWTO (http://collective.cpoint.net/lfs/notes/howto.txt) read_super must: increment MOD count lock_super fill in the VFS sb: s_blocksize s_blocksize_bits s_magic s_dev s_op s_mounted // the VFS inode for the root dir s_dirt initialize everything it needs for it's data structures (can use s_generic_sbp in VFS sb) unlock_super */ sb->s_blocksize = mtd->erasesize; erasesize = mtd->erasesize; while ((erasesize /= 2) != 0) log_2++; /* thanks to MR Brown */ sb->s_blocksize_bits = log_2; sb->s_magic = 0x55555555; /* Nearest thing vmu has */ sb->s_op = &vmufs_super_operations; struct maple_driver_data *d = (struct maple_driver_data *) (mtd->priv); memcard_t *memcard = (memcard_t *) (d->private_data); put_mtd_device(mtd); struct buffer_head *bh; bh = sb_bread(sb, memcard->numblocks - 1); /* Check a formatted vmu */ if (!((((__u32 *) bh->b_data)[0] == sb->s_magic) && (((__u32 *) bh->b_data)[1] == sb->s_magic) && (((__u32 *) bh->b_data)[2] == sb->s_magic) && (sb->s_magic == ((__u32 *) bh->b_data)[3]))) { printk("Attempted to mount non-vmufs filesystem as vmufs\n"); return NULL; } brelse(bh); root_i = iget(sb, memcard->numblocks - 1); /* Some details for root inode */ /*Where is the inode? */ //__u16 dirloc = le16_to_cpu(((__u16 *)(bh->b_data))[0x25]); //(sb->s_root)->d_inode = iget(sb, dirloc); if (is_bad_inode(root_i)) { printk("VMUFS: get root inode failed\n"); return NULL; } /* Find root dentry */ sb->s_root = d_alloc_root(root_i); return sb; } static int __init init_vmufs_fs(void) { printk("Virtual Memory Unit file system for SEGA Dreamcast Linux\n"); return register_filesystem(&vmufs_fs_type); } static void __exit exit_vmufs_fs(void) { unregister_filesystem(&vmufs_fs_type); } module_init(init_vmufs_fs) module_exit(exit_vmufs_fs) MODULE_DESCRIPTION("Filesystem for Sega Dreamcast VMU"); MODULE_AUTHOR("Adrian McMenamin"); MODULE_LICENSE("GPL"); |