Hi Greg, On Thu, Jan 24, 2008 at 07:42:20PM +0200, Pekka Enberg wrote: > > Yeah, that would work but why do we want to mount all devices under > > the same mount point? If you move device discovery to ->probe() it's > > simple to have per-device mount points by overriding ->get_sb() to > > check for USB_DEVICE_MAJOR and look up the actual device, no? > > Otherwise you have to deal with device plug/unplug at filesystem > > level... On Thu, 24 Jan 2008, Greg KH wrote: > Yes, you could do that (per device mount), but it might be confusing for > users to have to control things that way.
Well, that's how it works for all other music players (especially ones that support USB storage natively). But anyway, here's a skeleton driver for what I was proposing in case someone is interested. Pekka --- drivers/usb/misc/Kconfig | 6 drivers/usb/misc/Makefile | 1 drivers/usb/misc/iriver.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 319 insertions(+) Index: linux-2.6/drivers/usb/misc/iriver.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6/drivers/usb/misc/iriver.c 2008-01-24 21:15:07.000000000 +0200 @@ -0,0 +1,312 @@ +#include <linux/kref.h> +#include <linux/module.h> +#include <linux/mount.h> +#include <linux/namei.h> +#include <linux/usb.h> + +#define USB_IRIVER_MINOR_BASE 128 /* FIXME: ask for one */ + +#define IRIVER_VENDOR_ID 0x4102 + +static struct usb_device_id iriver_table[] = { + { USB_DEVICE(IRIVER_VENDOR_ID, 0x1001) }, + { USB_DEVICE(IRIVER_VENDOR_ID, 0x1003) }, + { USB_DEVICE(IRIVER_VENDOR_ID, 0x1005) }, + { USB_DEVICE(IRIVER_VENDOR_ID, 0x1007) }, + { USB_DEVICE(IRIVER_VENDOR_ID, 0x1008) }, + { USB_DEVICE(IRIVER_VENDOR_ID, 0x1010) }, + { } +}; +MODULE_DEVICE_TABLE(usb, iriver_table); + +struct iriver_device { + struct usb_device *udev; + struct usb_interface *interface; + unsigned char *bulk_in_buffer; + size_t bulk_in_size; + __u8 bulk_in_endpoint_addr; + __u8 bulk_out_endpoint_addr; + struct mutex mutex; + struct kref kref; +}; +#define to_iriver_dev(d) container_of(d, struct iriver_device, kref) + +static struct usb_class_driver iriver_class = { + .name = "iriver%d", + .minor_base = USB_IRIVER_MINOR_BASE, +}; + +static void iriver_delete(struct kref *kref) +{ + struct iriver_device *dev = to_iriver_dev(kref); + + usb_put_dev(dev->udev); + kfree(dev); +} + +static void iriver_disconnect(struct usb_interface *interface) +{ + struct iriver_device *dev; + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + usb_deregister_dev(interface, &iriver_class); + kref_put(&dev->kref, iriver_delete); +} + +static int iriver_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_host_interface *iface_desc; + struct iriver_device *dev; + int i, err; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + kref_init(&dev->kref); + mutex_init(&dev->mutex); + + dev->udev = usb_get_dev(interface_to_usbdev(interface)); + dev->interface = interface; + + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + struct usb_endpoint_descriptor *endpoint; + + endpoint = &iface_desc->endpoint[i].desc; + + if (!dev->bulk_in_endpoint_addr && + usb_endpoint_is_bulk_in(endpoint)) { + size_t buffer_size; + + buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + dev->bulk_in_size = buffer_size; + dev->bulk_in_endpoint_addr = endpoint->bEndpointAddress; + dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); + if (!dev->bulk_in_buffer) { + err = -ENOMEM; + goto failed; + } + } + + if (!dev->bulk_out_endpoint_addr && + usb_endpoint_is_bulk_out(endpoint)) + dev->bulk_out_endpoint_addr = endpoint->bEndpointAddress; + } + if (!dev->bulk_in_endpoint_addr || !dev->bulk_out_endpoint_addr) { + err = -EINVAL; + goto failed; + } + usb_set_intfdata(interface, dev); + + err = usb_register_dev(interface, &iriver_class); + if (err) { + usb_set_intfdata(interface, NULL); + goto failed; + } + return 0; +failed: + if (dev) + kref_put(&dev->kref, iriver_delete); + return err; +} + +static struct usb_driver iriver_driver = { + .name = "iriver", + .probe = iriver_probe, + .disconnect = iriver_disconnect, + .id_table = iriver_table, +}; + +#define IRIVERFS_MAGIC 0xdeadbeef +#define IRIVERFS_BLOCK_SIZE 512 +#define IRIVERFS_BLOCK_BITS 9 + +static int iriver_set_super(struct super_block *sb, void *data) +{ + sb->s_fs_info = data; + return set_anon_super(sb, data); +} + +static struct iriver_device *iriver_sb_info(struct super_block *sb) +{ + return sb->s_fs_info; +} + +static void iriver_kill_sb(struct super_block *sb) +{ + generic_shutdown_super(sb); +} + +static int iriverfs_readdir(struct file *file, void *dirent, filldir_t filldir) +{ + struct dentry *dentry = file->f_dentry; + struct super_block *sb = dentry->d_sb; + struct iriver_device *dev; + + dev = iriver_sb_info(sb); + mutex_lock(&dev->mutex); + /* + * TODO: Fill in the blank. + */ + mutex_unlock(&dev->mutex); + return dcache_readdir(file, dirent, filldir); +} + +static struct file_operations iriverfs_dir_ops = { + .open = dcache_dir_open, + .release = dcache_dir_close, + .read = generic_read_dir, + .readdir = iriverfs_readdir, +}; + +static struct dentry *iriverfs_lookup(struct inode *dir, + struct dentry *dentry, + struct nameidata *nd) +{ + return NULL; +} + +static struct inode_operations iriverfs_dir_inode_ops = { + .lookup = iriverfs_lookup, +}; + +static struct inode *iriver_get_inode(struct super_block *sb, int mode) +{ + struct inode *inode = new_inode(sb); + if (inode) { + switch (mode & S_IFMT) { + case S_IFDIR: + inode->i_op = &iriverfs_dir_inode_ops; + inode->i_fop = &iriverfs_dir_ops; + break; + default: + BUG(); + } + } + return inode; +} + +static const struct super_operations iriverfs_ops = { + .drop_inode = generic_delete_inode, +}; + +static int iriver_fill_super(struct super_block *sb, void *data, int silent) +{ + struct dentry *root; + struct inode *inode; + int err; + + sb->s_maxbytes = MAX_LFS_FILESIZE; + sb->s_blocksize = IRIVERFS_BLOCK_SIZE; + sb->s_blocksize_bits = IRIVERFS_BLOCK_BITS; + sb->s_magic = IRIVERFS_MAGIC; + sb->s_op = &iriverfs_ops; + + inode = iriver_get_inode(sb, S_IFDIR | 0755); + if (!inode) + return -ENOMEM; + + root = d_alloc_root(inode); + if (!root) { + iput(inode); + err = -ENOMEM; + } + sb->s_root = root; + + return 0; +} + +static int iriver_get_sb(struct file_system_type *fs_type, int flags, + const char *dev_name, void *data, + struct vfsmount *mnt) +{ + struct usb_interface *interface; + struct iriver_device *dev; + struct super_block *sb; + struct nameidata nd; + unsigned minor; + int err; + + if (!dev_name) + return -EINVAL; + + err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd); + if (err) + return err; + + if (nd.mnt->mnt_flags & MNT_NODEV) { + err = -EACCES; + goto error; + } + if (imajor(nd.dentry->d_inode) != USB_DEVICE_MAJOR) { + err = -EINVAL; + goto error; + } + minor = iminor(nd.dentry->d_inode); + path_release(&nd); + interface = usb_find_interface(&iriver_driver, minor); + if (!interface) { + err = -ENODEV; + goto error; + } + dev = usb_get_intfdata(interface); + sb = sget(fs_type, NULL, iriver_set_super, dev); + if (IS_ERR(sb)) { + err = PTR_ERR(sb); + goto error; + } + if (sb->s_root) { + err = -EBUSY; + goto error; + } + sb->s_flags = flags; + err = iriver_fill_super(sb, data, flags & MS_SILENT ? 1 : 0); + if (err) { + up_write(&sb->s_umount); + deactivate_super(sb); + goto error; + } + sb->s_flags |= MS_ACTIVE; + return simple_set_mnt(mnt, sb); +error: + path_release(&nd); + return err; +} + +static struct file_system_type iriverfs_fs_type = { + .owner = THIS_MODULE, + .name = "iriverfs", + .get_sb = iriver_get_sb, + .kill_sb = iriver_kill_sb, + .fs_flags = FS_REQUIRES_DEV, +}; + +static int __init iriver_init(void) +{ + int err; + + err = usb_register(&iriver_driver); + if (err) + return err; + + err = register_filesystem(&iriverfs_fs_type); + if (err) { + usb_deregister(&iriver_driver); + return err; + } + return 0; +} + +static void __exit iriver_exit(void) +{ + unregister_filesystem(&iriverfs_fs_type); + usb_deregister(&iriver_driver); +} + +module_init(iriver_init); +module_exit(iriver_exit); + +MODULE_LICENSE("GPL"); Index: linux-2.6/drivers/usb/misc/Kconfig =================================================================== --- linux-2.6.orig/drivers/usb/misc/Kconfig 2008-01-24 21:15:14.000000000 +0200 +++ linux-2.6/drivers/usb/misc/Kconfig 2008-01-24 21:16:22.000000000 +0200 @@ -269,3 +269,9 @@ See <http://www.linux-usb.org/usbtest/> for more information, including sample test device firmware and "how to use it". +config USB_IRIVER + tristate "iRiver iFP portable music player driver (DEVELOPMENT)" + depends on USB && EXPERIMENTAL + help + This is a driver for the iRiver iFP portable music player. + Index: linux-2.6/drivers/usb/misc/Makefile =================================================================== --- linux-2.6.orig/drivers/usb/misc/Makefile 2008-01-24 21:16:33.000000000 +0200 +++ linux-2.6/drivers/usb/misc/Makefile 2008-01-24 21:16:46.000000000 +0200 @@ -26,6 +26,7 @@ obj-$(CONFIG_USB_TEST) += usbtest.o obj-$(CONFIG_USB_TRANCEVIBRATOR) += trancevibrator.o obj-$(CONFIG_USB_USS720) += uss720.o +obj-$(CONFIG_IRIVER) += iriver.o obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/