From: Peter Osterlund Get rid of the ioctl interface and use an auxiliary character device instead to control device bindings. The driver creates a misc character device and bind/unbind of the block devices are controlled by ioctl commands on the char device. This patch needs corresponding changes in the pktsetup user space program. I'll post a patch for pktsetup as a separate message. The driver no longer uses reserved device numbers. Instead, pktsetup reads /proc/misc to find the pktcdvd char device. Signed-off-by: Peter Osterlund DESC Subject: Re: 2.6.8-rc2-mm2 EDESC From: Peter Osterlund Andrew Morton writes: > ftp://ftp.kernel.org/pub/linux/kernel/people/akpm/patches/2.6/2.6.8-rc2/2.6.8-rc2-mm2/ > > Changes since 2.6.8-rc2-mm1: > > +packet-door-unlock.patch > +pkt_lock_door-warning-fix.patch The door-unlock patch got mis-merged, which caused the need for the warning fix patch. pkt_lock_door should be called from pkt_release_dev, not from pkt_generic_packet. This patch fixes it. Signed-off-by: Peter Osterlund DESC control-pktcdvd-with-an-auxiliary-character-device-fix EDESC Signed-off-by: Andrew Morton --- 25-akpm/Documentation/cdrom/packet-writing.txt | 18 25-akpm/drivers/block/pktcdvd.c | 591 ++++++++++++------------- 25-akpm/fs/compat_ioctl.c | 1 25-akpm/include/linux/compat_ioctl.h | 2 25-akpm/include/linux/major.h | 2 25-akpm/include/linux/pktcdvd.h | 54 +- 6 files changed, 342 insertions(+), 326 deletions(-) diff -puN Documentation/cdrom/packet-writing.txt~control-pktcdvd-with-an-auxiliary-character-device Documentation/cdrom/packet-writing.txt --- 25/Documentation/cdrom/packet-writing.txt~control-pktcdvd-with-an-auxiliary-character-device Mon Aug 16 16:11:19 2004 +++ 25-akpm/Documentation/cdrom/packet-writing.txt Mon Aug 16 16:11:19 2004 @@ -13,13 +13,11 @@ Getting started quick as appropriate): # cdrwtool -d /dev/hdc -q -- Make sure that /dev/pktcdvd0 exists (mknod /dev/pktcdvd0 b 97 0) - - Setup your writer - # pktsetup /dev/pktcdvd0 /dev/hdc + # pktsetup dev_name /dev/hdc -- Now you can mount /dev/pktcdvd0 and copy files to it. Enjoy! - # mount /dev/pktcdvd0 /cdrom -t udf -o rw,noatime +- Now you can mount /dev/pktcdvd/dev_name and copy files to it. Enjoy! + # mount /dev/pktcdvd/dev_name /cdrom -t udf -o rw,noatime Packet writing for DVD-RW media @@ -33,8 +31,8 @@ overwrite mode, run: You can then use the disc the same way you would use a CD-RW disc: - # pktsetup /dev/pktcdvd0 /dev/hdc - # mount /dev/pktcdvd0 /cdrom -t udf -o rw,noatime + # pktsetup dev_name /dev/hdc + # mount /dev/pktcdvd/dev_name /cdrom -t udf -o rw,noatime Packet writing for DVD+RW media @@ -56,9 +54,9 @@ writes are not 32KB aligned. Both problems can be solved by using the pktcdvd driver, which always generates aligned writes. - # pktsetup /dev/pktcdvd0 /dev/hdc - # mkudffs /dev/pktcdvd0 - # mount /dev/pktcdvd0 /cdrom -t udf -o rw,noatime + # pktsetup dev_name /dev/hdc + # mkudffs /dev/pktcdvd/dev_name + # mount /dev/pktcdvd/dev_name /cdrom -t udf -o rw,noatime Notes @@ -76,7 +74,7 @@ Notes device with a 2KB block size, you can put any filesystem you like on the disc. For example, run: - # /sbin/mke2fs /dev/pktcdvd0 + # /sbin/mke2fs /dev/pktcdvd/dev_name to create an ext2 filesystem on the disc. diff -puN drivers/block/pktcdvd.c~control-pktcdvd-with-an-auxiliary-character-device drivers/block/pktcdvd.c --- 25/drivers/block/pktcdvd.c~control-pktcdvd-with-an-auxiliary-character-device Mon Aug 16 16:11:19 2004 +++ 25-akpm/drivers/block/pktcdvd.c Mon Aug 16 16:11:19 2004 @@ -31,7 +31,7 @@ * *************************************************************************/ -#define VERSION_CODE "v0.1.6a 2004-07-01 Jens Axboe (axboe@suse.de) and petero2@telia.com" +#define VERSION_CODE "v0.2.0a 2004-07-14 Jens Axboe (axboe@suse.de) and petero2@telia.com" #include #include @@ -44,28 +44,42 @@ #include #include #include -#include /* for invalidate_bdev() */ -#include +#include #include #include #include #include +#if PACKET_DEBUG +#define DPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args) +#else +#define DPRINTK(fmt, args...) +#endif + +#if PACKET_DEBUG > 1 +#define VPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args) +#else +#define VPRINTK(fmt, args...) +#endif + #define ZONE(sector, pd) (((sector) + (pd)->offset) & ~((pd)->settings.size - 1)) -static struct pktcdvd_device *pkt_devs; +static struct pktcdvd_device *pkt_devs[MAX_WRITERS]; static struct proc_dir_entry *pkt_proc; +static int pkt_major; +static struct semaphore ctl_mutex; /* Serialize open/close/setup/teardown */ -static int pkt_lock_door(struct pktcdvd_device *pd, int lockflag); static struct pktcdvd_device *pkt_find_dev(request_queue_t *q) { int i; - for (i = 0; i < MAX_WRITERS; i++) - if (pkt_devs[i].bdev && bdev_get_queue(pkt_devs[i].bdev) == q) - return &pkt_devs[i]; + for (i = 0; i < MAX_WRITERS; i++) { + struct pktcdvd_device *pd = pkt_devs[i]; + if (pd && bdev_get_queue(pd->bdev) == q) + return pd; + } return NULL; } @@ -317,13 +331,6 @@ static int pkt_generic_packet(struct pkt DECLARE_COMPLETION(wait); int err = 0; - if (!pd->bdev) { - printk("pkt_generic_packet: no bdev\n"); - return -ENXIO; - } - - pkt_lock_door(pd, 0); - q = bdev_get_queue(pd->bdev); rq = blk_get_request(q, (cgc->data_direction == CGC_DATA_WRITE) ? WRITE : READ, @@ -485,8 +492,6 @@ static void pkt_iosched_process_queue(st return; atomic_set(&pd->iosched.attention, 0); - if (!pd->bdev) - return; q = bdev_get_queue(pd->bdev); for (;;) { @@ -1213,11 +1218,7 @@ static int kcdrwd(void *foobar) min_sleep_time = pkt->sleep_time; } - if (pd->bdev) { - request_queue_t *q; - q = bdev_get_queue(pd->bdev); - generic_unplug_device(q); - } + generic_unplug_device(bdev_get_queue(pd->bdev)); VPRINTK("kcdrwd: sleeping\n"); residue = schedule_timeout(min_sleep_time); @@ -1898,23 +1899,18 @@ static int pkt_open_dev(struct pktcdvd_d int i; char b[BDEVNAME_SIZE]; - if (!pd->dev) - return -ENXIO; - - pd->bdev = bdget(pd->dev); - if (!pd->bdev) { - printk("pktcdvd: can't find cdrom block device\n"); - return -ENXIO; - } - - if ((ret = blkdev_get(pd->bdev, FMODE_READ, 0))) { - pd->bdev = NULL; - return ret; - } + /* + * We need to re-open the cdrom device without O_NONBLOCK to be able + * to read/write from/to it. It is already opened in O_NONBLOCK mode + * so bdget() can't fail. + */ + bdget(pd->bdev->bd_dev); + if ((ret = blkdev_get(pd->bdev, FMODE_READ, O_RDONLY))) + goto out; if ((ret = pkt_get_last_written(pd, &lba))) { printk("pktcdvd: pkt_get_last_written failed\n"); - return ret; + goto out_putdev; } set_capacity(pd->disk, lba << 2); @@ -1929,11 +1925,12 @@ static int pkt_open_dev(struct pktcdvd_d */ q = bdev_get_queue(pd->bdev); for (i = 0; i < MAX_WRITERS; i++) { - if (pd == &pkt_devs[i]) + if (pd == pkt_devs[i]) continue; - if (pkt_devs[i].bdev && bdev_get_queue(pkt_devs[i].bdev) == q) { + if (pkt_devs[i] && bdev_get_queue(pkt_devs[i]->bdev) == q) { printk("pktcdvd: %s request queue busy\n", bdevname(pd->bdev, b)); - return -EBUSY; + ret = -EBUSY; + goto out_putdev; } } spin_lock_irq(q->queue_lock); @@ -1968,6 +1965,9 @@ restore_queue: q->elevator.elevator_completed_req_fn = pd->cdrw.elv_completed_req_fn; q->merge_requests_fn = pd->cdrw.merge_requests_fn; spin_unlock_irq(q->queue_lock); +out_putdev: + blkdev_put(pd->bdev); +out: return ret; } @@ -1977,117 +1977,82 @@ restore_queue: */ static void pkt_release_dev(struct pktcdvd_device *pd, int flush) { + request_queue_t *q; + if (flush && pkt_flush_cache(pd)) DPRINTK("pktcdvd: %s not flushing cache\n", pd->name); - if (pd->bdev) { - request_queue_t *q = bdev_get_queue(pd->bdev); - pkt_set_speed(pd, 0xffff, 0xffff); - spin_lock_irq(q->queue_lock); - q->elevator.elevator_merge_fn = pd->cdrw.elv_merge_fn; - q->elevator.elevator_completed_req_fn = pd->cdrw.elv_completed_req_fn; - q->merge_requests_fn = pd->cdrw.merge_requests_fn; - spin_unlock_irq(q->queue_lock); - blkdev_put(pd->bdev); - pd->bdev = NULL; - } + pkt_lock_door(pd, 0); + + q = bdev_get_queue(pd->bdev); + pkt_set_speed(pd, 0xffff, 0xffff); + spin_lock_irq(q->queue_lock); + q->elevator.elevator_merge_fn = pd->cdrw.elv_merge_fn; + q->elevator.elevator_completed_req_fn = pd->cdrw.elv_completed_req_fn; + q->merge_requests_fn = pd->cdrw.merge_requests_fn; + spin_unlock_irq(q->queue_lock); + blkdev_put(pd->bdev); +} + +static struct pktcdvd_device *pkt_find_dev_from_minor(int dev_minor) +{ + if (dev_minor >= MAX_WRITERS) + return NULL; + return pkt_devs[dev_minor]; } static int pkt_open(struct inode *inode, struct file *file) { struct pktcdvd_device *pd = NULL; int ret; - int special_open, exclusive; VPRINTK("pktcdvd: entering open\n"); - if (iminor(inode) >= MAX_WRITERS) { - printk("pktcdvd: max %d writers supported\n", MAX_WRITERS); + down(&ctl_mutex); + pd = pkt_find_dev_from_minor(iminor(inode)); + if (!pd) { ret = -ENODEV; goto out; } + BUG_ON(pd->refcnt < 0); - special_open = 0; - if (((file->f_flags & O_ACCMODE) == O_RDONLY) && (file->f_flags & O_CREAT)) - special_open = 1; - - exclusive = 0; - if ((file->f_mode & FMODE_WRITE) || special_open) - exclusive = 1; - - /* - * either device is not configured, or pktsetup is old and doesn't - * use O_CREAT to create device - */ - pd = &pkt_devs[iminor(inode)]; - if (!pd->dev && !special_open) { - VPRINTK("pktcdvd: not configured and O_CREAT not set\n"); - ret = -ENXIO; - goto out; - } - - down(&pd->ctl_mutex); pd->refcnt++; - if (pd->refcnt > 1) { - if (exclusive) { - VPRINTK("pktcdvd: busy open\n"); - ret = -EBUSY; - goto out_dec; - } - - /* - * Not first open, everything is already set up - */ - goto done; - } - - if (!special_open) { + if (pd->refcnt == 1) { if (pkt_open_dev(pd, file->f_mode & FMODE_WRITE)) { ret = -EIO; goto out_dec; } + /* + * needed here as well, since ext2 (among others) may change + * the blocksize at mount time + */ + set_blocksize(inode->i_bdev, CD_FRAMESIZE); } - /* - * needed here as well, since ext2 (among others) may change - * the blocksize at mount time - */ - set_blocksize(inode->i_bdev, CD_FRAMESIZE); - -done: - up(&pd->ctl_mutex); + up(&ctl_mutex); return 0; out_dec: pd->refcnt--; - if (pd->refcnt == 0) { - if (pd->bdev) { - blkdev_put(pd->bdev); - pd->bdev = NULL; - } - } - up(&pd->ctl_mutex); out: VPRINTK("pktcdvd: failed open (%d)\n", ret); + up(&ctl_mutex); return ret; } static int pkt_close(struct inode *inode, struct file *file) { - struct pktcdvd_device *pd = &pkt_devs[iminor(inode)]; + struct pktcdvd_device *pd = pkt_find_dev_from_minor(iminor(inode)); int ret = 0; - down(&pd->ctl_mutex); + down(&ctl_mutex); pd->refcnt--; BUG_ON(pd->refcnt < 0); - if (pd->refcnt > 0) - goto out; - if (pd->dev) { + if (pd->refcnt == 0) { int flush = test_bit(PACKET_WRITABLE, &pd->flags); pkt_release_dev(pd, flush); } -out: - up(&pd->ctl_mutex); + up(&ctl_mutex); return ret; } @@ -2105,11 +2070,6 @@ static int pkt_make_request(request_queu goto end_io; } - if (!pd->dev) { - printk("pktcdvd: request received for non-active pd\n"); - goto end_io; - } - /* * quick remap a READ */ @@ -2249,7 +2209,7 @@ end_io: static int pkt_merge_bvec(request_queue_t *q, struct bio *bio, struct bio_vec *bvec) { - struct pktcdvd_device *pd = &pkt_devs[MINOR(bio->bi_bdev->bd_dev)]; + struct pktcdvd_device *pd = q->queuedata; sector_t zone = ZONE(bio->bi_sector, pd); int used = ((bio->bi_sector - zone) << 9) + bio->bi_size; int remaining = (pd->settings.size << 9) - used; @@ -2285,7 +2245,7 @@ static int pkt_seq_show(struct seq_file int states[PACKET_NUM_STATES]; seq_printf(m, "Writer %s mapped to %s:\n", pd->name, - __bdevname(pd->dev, bdev_buf)); + bdevname(pd->bdev, bdev_buf)); seq_printf(m, "\nSettings:\n"); seq_printf(m, "\tpacket size:\t\t%dkB\n", pd->settings.size / 2); @@ -2346,35 +2306,50 @@ static struct file_operations pkt_proc_f .release = single_release }; -static int pkt_new_dev(struct pktcdvd_device *pd, struct block_device *bdev) +static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev) { - dev_t dev = bdev->bd_dev; int i; int ret = 0; char b[BDEVNAME_SIZE]; struct proc_dir_entry *proc; + struct block_device *bdev; + if (pd->pkt_dev == dev) { + printk("pktcdvd: Recursive setup not allowed\n"); + return -EBUSY; + } for (i = 0; i < MAX_WRITERS; i++) { - if (pkt_devs[i].dev == dev) { - printk("pktcdvd: %s already setup\n", bdevname(bdev, b)); + struct pktcdvd_device *pd2 = pkt_devs[i]; + if (!pd2) + continue; + if (pd2->bdev->bd_dev == dev) { + printk("pktcdvd: %s already setup\n", bdevname(pd2->bdev, b)); + return -EBUSY; + } + if (pd2->pkt_dev == dev) { + printk("pktcdvd: Can't chain pktcdvd devices\n"); return -EBUSY; } } + bdev = bdget(dev); + if (!bdev) + return -ENOMEM; + ret = blkdev_get(bdev, FMODE_READ, O_RDONLY | O_NONBLOCK); + if (ret) + return ret; + /* This is safe, since we have a reference from open(). */ __module_get(THIS_MODULE); - memset(&pd->stats, 0, sizeof(struct packet_stats)); - if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) { printk("pktcdvd: not enough memory for buffers\n"); ret = -ENOMEM; goto out_mem; } + pd->bdev = bdev; set_blocksize(bdev, CD_FRAMESIZE); - pd->dev = dev; - BUG_ON(pd->bdev); pkt_init_queue(pd); @@ -2396,60 +2371,43 @@ static int pkt_new_dev(struct pktcdvd_de out_thread: pkt_shrink_pktlist(pd); out_mem: + blkdev_put(bdev); /* This is safe: open() is still holding a reference. */ module_put(THIS_MODULE); return ret; } -/* - * arg contains file descriptor of CD-ROM device. - */ -static int pkt_setup_dev(struct pktcdvd_device *pd, unsigned int arg) +static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct inode *inode; - struct file *file; - int ret; - - if ((file = fget(arg)) == NULL) { - printk("pktcdvd: bad file descriptor passed\n"); - return -EBADF; - } - - inode = file->f_dentry->d_inode; - ret = -ENOTBLK; - if (!S_ISBLK(inode->i_mode)) { - printk("pktcdvd: device is not a block device (duh)\n"); - goto out; - } - ret = -EROFS; - if (IS_RDONLY(inode)) { - printk("pktcdvd: Can't write to read-only dev\n"); - goto out; - } - ret = pkt_new_dev(pd, inode->i_bdev); - -out: - fput(file); - return ret; -} + struct pktcdvd_device *pd = pkt_find_dev_from_minor(iminor(inode)); -static int pkt_remove_dev(struct pktcdvd_device *pd, struct block_device *bdev) -{ - if (!IS_ERR(pd->cdrw.thread)) - kthread_stop(pd->cdrw.thread); + VPRINTK("pkt_ioctl: cmd %x, dev %d:%d\n", cmd, imajor(inode), iminor(inode)); + BUG_ON(!pd); + switch (cmd) { /* - * will also invalidate buffers for CD-ROM + * forward selected CDROM ioctls to CD-ROM, for UDF */ - invalidate_bdev(bdev, 1); + case CDROMMULTISESSION: + case CDROMREADTOCENTRY: + case CDROM_LAST_WRITTEN: + case CDROM_SEND_PACKET: + case SCSI_IOCTL_SEND_COMMAND: + return ioctl_by_bdev(pd->bdev, cmd, arg); - pkt_shrink_pktlist(pd); + case CDROMEJECT: + /* + * The door gets locked when the device is opened, so we + * have to unlock it or else the eject command fails. + */ + pkt_lock_door(pd, 0); + return ioctl_by_bdev(pd->bdev, cmd, arg); + + default: + printk("pktcdvd: Unknown ioctl for %s (%x)\n", pd->name, cmd); + return -ENOTTY; + } - remove_proc_entry(pd->name, pkt_proc); - pd->dev = 0; - DPRINTK("pktcdvd: writer %s unmapped\n", pd->name); - /* This is safe: open() is still holding a reference. */ - module_put(THIS_MODULE); return 0; } @@ -2468,164 +2426,223 @@ static int pkt_media_changed(struct gend return attached_disk->fops->media_changed(attached_disk); } -static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +static struct block_device_operations pktcdvd_ops = { + .owner = THIS_MODULE, + .open = pkt_open, + .release = pkt_close, + .ioctl = pkt_ioctl, + .media_changed = pkt_media_changed, +}; + +/* + * Set up mapping from pktcdvd device to CD-ROM device. + */ +static int pkt_setup_dev(struct pkt_ctrl_command *ctrl_cmd) { - struct pktcdvd_device *pd = &pkt_devs[iminor(inode)]; - int ret; + int idx; + int ret = -ENOMEM; + struct pktcdvd_device *pd; + struct gendisk *disk; + dev_t dev = new_decode_dev(ctrl_cmd->dev); - VPRINTK("pkt_ioctl: cmd %x, dev %d:%d\n", cmd, imajor(inode), iminor(inode)); + for (idx = 0; idx < MAX_WRITERS; idx++) + if (!pkt_devs[idx]) + break; + if (idx == MAX_WRITERS) { + printk("pktcdvd: max %d writers supported\n", MAX_WRITERS); + return -EBUSY; + } + + pd = kmalloc(sizeof(struct pktcdvd_device), GFP_KERNEL); + if (!pd) + return ret; + memset(pd, 0, sizeof(struct pktcdvd_device)); + + disk = alloc_disk(1); + if (!disk) + goto out_mem; + pd->disk = disk; + + spin_lock_init(&pd->lock); + spin_lock_init(&pd->iosched.lock); + sprintf(pd->name, "pktcdvd%d", idx); + init_waitqueue_head(&pd->wqueue); + + disk->major = pkt_major; + disk->first_minor = idx; + disk->fops = &pktcdvd_ops; + disk->flags = GENHD_FL_REMOVABLE; + sprintf(disk->disk_name, "pktcdvd%d", idx); + disk->private_data = pd; + disk->queue = blk_alloc_queue(GFP_KERNEL); + if (!disk->queue) + goto out_mem2; + + pd->pkt_dev = MKDEV(disk->major, disk->first_minor); + ret = pkt_new_dev(pd, dev); + if (ret) + goto out_new_dev; + + add_disk(disk); + pkt_devs[idx] = pd; + ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev); + return 0; + +out_new_dev: + blk_put_queue(disk->queue); +out_mem2: + put_disk(disk); +out_mem: + kfree(pd); + return ret; +} + +/* + * Tear down mapping from pktcdvd device to CD-ROM device. + */ +static int pkt_remove_dev(struct pkt_ctrl_command *ctrl_cmd) +{ + struct pktcdvd_device *pd; + int idx; + dev_t pkt_dev = new_decode_dev(ctrl_cmd->pkt_dev); - if ((cmd != PACKET_SETUP_DEV) && !pd->dev) { + for (idx = 0; idx < MAX_WRITERS; idx++) { + pd = pkt_devs[idx]; + if (pd && (pd->pkt_dev == pkt_dev)) + break; + } + if (idx == MAX_WRITERS) { DPRINTK("pktcdvd: dev not setup\n"); return -ENXIO; } - switch (cmd) { - case PACKET_GET_STATS: - if (copy_to_user(&arg, &pd->stats, sizeof(struct packet_stats))) - return -EFAULT; - break; + if (pd->refcnt > 0) + return -EBUSY; - case PACKET_SETUP_DEV: - if (pd->dev) { - printk("pktcdvd: dev already setup\n"); - return -EBUSY; - } - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - return pkt_setup_dev(pd, arg); + if (!IS_ERR(pd->cdrw.thread)) + kthread_stop(pd->cdrw.thread); - case PACKET_TEARDOWN_DEV: - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - down(&pd->ctl_mutex); - BUG_ON(pd->refcnt < 1); - if (pd->refcnt != 1) - ret = -EBUSY; - else - ret = pkt_remove_dev(pd, inode->i_bdev); - up(&pd->ctl_mutex); - return ret; + blkdev_put(pd->bdev); - /* - * forward selected CDROM ioctls to CD-ROM, for UDF - */ - case CDROMMULTISESSION: - case CDROMREADTOCENTRY: - case CDROM_LAST_WRITTEN: - case CDROM_SEND_PACKET: - case SCSI_IOCTL_SEND_COMMAND: - if (!pd->bdev) - return -ENXIO; - return ioctl_by_bdev(pd->bdev, cmd, arg); + pkt_shrink_pktlist(pd); - case CDROMEJECT: - if (!pd->bdev) - return -ENXIO; - /* - * The door gets locked when the device is opened, so we - * have to unlock it or else the eject command fails. - */ - pkt_lock_door(pd, 0); - return ioctl_by_bdev(pd->bdev, cmd, arg); + remove_proc_entry(pd->name, pkt_proc); + DPRINTK("pktcdvd: writer %s unmapped\n", pd->name); + + del_gendisk(pd->disk); + blk_put_queue(pd->disk->queue); + put_disk(pd->disk); + + pkt_devs[idx] = NULL; + kfree(pd); + + /* This is safe: open() is still holding a reference. */ + module_put(THIS_MODULE); + return 0; +} + +static void pkt_get_status(struct pkt_ctrl_command *ctrl_cmd) +{ + struct pktcdvd_device *pd = pkt_find_dev_from_minor(ctrl_cmd->dev_index); + if (pd) { + ctrl_cmd->dev = new_encode_dev(pd->bdev->bd_dev); + ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev); + } else { + ctrl_cmd->dev = 0; + ctrl_cmd->pkt_dev = 0; + } + ctrl_cmd->num_devices = MAX_WRITERS; +} + +static int pkt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + struct pkt_ctrl_command ctrl_cmd; + int ret = 0; + + if (cmd != PACKET_CTRL_CMD) + return -ENOTTY; + if (copy_from_user(&ctrl_cmd, argp, sizeof(struct pkt_ctrl_command))) + return -EFAULT; + + switch (ctrl_cmd.command) { + case PKT_CTRL_CMD_SETUP: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + down(&ctl_mutex); + ret = pkt_setup_dev(&ctrl_cmd); + up(&ctl_mutex); + break; + case PKT_CTRL_CMD_TEARDOWN: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + down(&ctl_mutex); + ret = pkt_remove_dev(&ctrl_cmd); + up(&ctl_mutex); + break; + case PKT_CTRL_CMD_STATUS: + down(&ctl_mutex); + pkt_get_status(&ctrl_cmd); + up(&ctl_mutex); + break; default: - printk("pktcdvd: Unknown ioctl for %s (%x)\n", pd->name, cmd); return -ENOTTY; } - return 0; + if (copy_to_user(argp, &ctrl_cmd, sizeof(struct pkt_ctrl_command))) + return -EFAULT; + return ret; } -static struct block_device_operations pktcdvd_ops = { - .owner = THIS_MODULE, - .open = pkt_open, - .release = pkt_close, - .ioctl = pkt_ioctl, - .media_changed = pkt_media_changed, + +static struct file_operations pkt_ctl_fops = { + .ioctl = pkt_ctl_ioctl, + .owner = THIS_MODULE, +}; + +static struct miscdevice pkt_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "pktcdvd", + .devfs_name = "pktcdvd/control", + .fops = &pkt_ctl_fops }; int pkt_init(void) { - int i; + int ret; - devfs_mk_dir("pktcdvd"); - if (register_blkdev(PACKET_MAJOR, "pktcdvd")) { - printk("unable to register pktcdvd device\n"); - return -EIO; + ret = register_blkdev(pkt_major, "pktcdvd"); + if (ret < 0) { + printk("pktcdvd: Unable to register block device\n"); + return ret; } + if (!pkt_major) + pkt_major = ret; - pkt_devs = kmalloc(MAX_WRITERS * sizeof(struct pktcdvd_device), GFP_KERNEL); - if (pkt_devs == NULL) - goto out_mem; - memset(pkt_devs, 0, MAX_WRITERS * sizeof(struct pktcdvd_device)); - - for (i = 0; i < MAX_WRITERS; i++) { - struct pktcdvd_device *pd = &pkt_devs[i]; - - pd->disk = alloc_disk(1); - if (!pd->disk) - goto out_mem2; + ret = misc_register(&pkt_misc); + if (ret) { + printk("pktcdvd: Unable to register misc device\n"); + goto out; } - for (i = 0; i < MAX_WRITERS; i++) { - struct pktcdvd_device *pd = &pkt_devs[i]; - struct gendisk *disk = pd->disk; - - spin_lock_init(&pd->lock); - spin_lock_init(&pd->iosched.lock); - sprintf(pd->name, "pktcdvd%d", i); - init_waitqueue_head(&pd->wqueue); - init_MUTEX(&pd->ctl_mutex); - - disk->major = PACKET_MAJOR; - disk->first_minor = i; - disk->fops = &pktcdvd_ops; - disk->flags = GENHD_FL_REMOVABLE; - sprintf(disk->disk_name, "pktcdvd%d", i); - sprintf(disk->devfs_name, "pktcdvd/%d", i); - disk->private_data = pd; - disk->queue = blk_alloc_queue(GFP_KERNEL); - if (!disk->queue) - goto out_mem3; - add_disk(disk); - } + init_MUTEX(&ctl_mutex); pkt_proc = proc_mkdir("pktcdvd", proc_root_driver); DPRINTK("pktcdvd: %s\n", VERSION_CODE); return 0; -out_mem3: - while (i--) - blk_put_queue(pkt_devs[i].disk->queue); - i = MAX_WRITERS; -out_mem2: - while (i--) - put_disk(pkt_devs[i].disk); - kfree(pkt_devs); -out_mem: - printk("pktcdvd: out of memory\n"); - devfs_remove("pktcdvd"); - unregister_blkdev(PACKET_MAJOR, "pktcdvd"); - return -ENOMEM; +out: + unregister_blkdev(pkt_major, "pktcdvd"); + return ret; } void pkt_exit(void) { - int i; - for (i = 0; i < MAX_WRITERS; i++) { - struct gendisk *disk = pkt_devs[i].disk; - del_gendisk(disk); - blk_put_queue(disk->queue); - put_disk(disk); - } - - devfs_remove("pktcdvd"); - unregister_blkdev(PACKET_MAJOR, "pktcdvd"); - remove_proc_entry("pktcdvd", proc_root_driver); - kfree(pkt_devs); + misc_deregister(&pkt_misc); + unregister_blkdev(pkt_major, "pktcdvd"); } MODULE_DESCRIPTION("Packet writing layer for CD/DVD drives"); @@ -2634,5 +2651,3 @@ MODULE_LICENSE("GPL"); module_init(pkt_init); module_exit(pkt_exit); - -MODULE_ALIAS_BLOCKDEV_MAJOR(PACKET_MAJOR); diff -puN include/linux/compat_ioctl.h~control-pktcdvd-with-an-auxiliary-character-device include/linux/compat_ioctl.h --- 25/include/linux/compat_ioctl.h~control-pktcdvd-with-an-auxiliary-character-device Mon Aug 16 16:11:19 2004 +++ 25-akpm/include/linux/compat_ioctl.h Mon Aug 16 16:11:19 2004 @@ -382,6 +382,8 @@ COMPATIBLE_IOCTL(CDROMREADALL) COMPATIBLE_IOCTL(DVD_READ_STRUCT) COMPATIBLE_IOCTL(DVD_WRITE_STRUCT) COMPATIBLE_IOCTL(DVD_AUTH) +/* pktcdvd */ +COMPATIBLE_IOCTL(PACKET_CTRL_CMD) /* Big L */ ULONG_IOCTL(LOOP_SET_FD) COMPATIBLE_IOCTL(LOOP_CLR_FD) diff -puN include/linux/major.h~control-pktcdvd-with-an-auxiliary-character-device include/linux/major.h --- 25/include/linux/major.h~control-pktcdvd-with-an-auxiliary-character-device Mon Aug 16 16:11:19 2004 +++ 25-akpm/include/linux/major.h Mon Aug 16 16:11:19 2004 @@ -111,8 +111,6 @@ #define MDISK_MAJOR 95 -#define PACKET_MAJOR 97 - #define UBD_MAJOR 98 #define JSFD_MAJOR 99 diff -puN include/linux/pktcdvd.h~control-pktcdvd-with-an-auxiliary-character-device include/linux/pktcdvd.h --- 25/include/linux/pktcdvd.h~control-pktcdvd-with-an-auxiliary-character-device Mon Aug 16 16:11:19 2004 +++ 25-akpm/include/linux/pktcdvd.h Mon Aug 16 16:11:19 2004 @@ -12,6 +12,8 @@ #ifndef __PKTCDVD_H #define __PKTCDVD_H +#include + /* * 1 for normal debug messages, 2 is very verbose. 0 to turn it off. */ @@ -39,18 +41,6 @@ * No user-servicable parts beyond this point -> */ -#if PACKET_DEBUG -#define DPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args) -#else -#define DPRINTK(fmt, args...) -#endif - -#if PACKET_DEBUG > 1 -#define VPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args) -#else -#define VPRINTK(fmt, args...) -#endif - /* * device types */ @@ -96,25 +86,24 @@ #undef PACKET_USE_LS -/* - * Very crude stats for now - */ -struct packet_stats -{ - unsigned long pkt_started; - unsigned long pkt_ended; - unsigned long secs_w; - unsigned long secs_rg; - unsigned long secs_r; +#define PKT_CTRL_CMD_SETUP 0 +#define PKT_CTRL_CMD_TEARDOWN 1 +#define PKT_CTRL_CMD_STATUS 2 + +struct pkt_ctrl_command { + __u32 command; /* in: Setup, teardown, status */ + __u32 dev_index; /* in/out: Device index */ + __u32 dev; /* in/out: Device nr for cdrw device */ + __u32 pkt_dev; /* in/out: Device nr for packet device */ + __u32 num_devices; /* out: Largest device index + 1 */ + __u32 padding; /* Not used */ }; /* * packet ioctls */ #define PACKET_IOCTL_MAGIC ('X') -#define PACKET_GET_STATS _IOR(PACKET_IOCTL_MAGIC, 0, struct packet_stats) -#define PACKET_SETUP_DEV _IOW(PACKET_IOCTL_MAGIC, 1, unsigned int) -#define PACKET_TEARDOWN_DEV _IOW(PACKET_IOCTL_MAGIC, 2, unsigned int) +#define PACKET_CTRL_CMD _IOWR(PACKET_IOCTL_MAGIC, 1, struct pkt_ctrl_command) #ifdef __KERNEL__ #include @@ -132,6 +121,18 @@ struct packet_settings __u8 block_mode; }; +/* + * Very crude stats for now + */ +struct packet_stats +{ + unsigned long pkt_started; + unsigned long pkt_ended; + unsigned long secs_w; + unsigned long secs_rg; + unsigned long secs_r; +}; + struct packet_cdrw { struct list_head pkt_free_list; @@ -228,11 +229,10 @@ struct packet_data struct pktcdvd_device { struct block_device *bdev; /* dev attached */ - dev_t dev; /* dev attached */ + dev_t pkt_dev; /* our dev */ char name[20]; struct packet_settings settings; struct packet_stats stats; - struct semaphore ctl_mutex; /* Serialize access to refcnt */ int refcnt; /* Open count */ __u8 write_speed; /* current write speed */ __u8 read_speed; /* current read speed */ diff -puN fs/compat_ioctl.c~control-pktcdvd-with-an-auxiliary-character-device fs/compat_ioctl.c --- 25/fs/compat_ioctl.c~control-pktcdvd-with-an-auxiliary-character-device Mon Aug 16 16:11:26 2004 +++ 25-akpm/fs/compat_ioctl.c Mon Aug 16 16:11:26 2004 @@ -114,6 +114,7 @@ #include #include #include +#include #include _