|
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");
|