+/*
+ * SECTION: ioctl functions.
+ */
+static struct list_head dasd_ioctl_list = LIST_HEAD_INIT(dasd_ioctl_list);
+
+/*
+ * Find the ioctl with number no.
+ */
+static struct dasd_ioctl *
+dasd_find_ioctl(int no)
+{
+ struct dasd_ioctl *ioctl;
+
+ list_for_each_entry (ioctl, &dasd_ioctl_list, list)
+ if (ioctl->no == no)
+ return ioctl;
+ return NULL;
+}
+
+/*
+ * Register ioctl with number no.
+ */
+int
+dasd_ioctl_no_register(struct module *owner, int no, dasd_ioctl_fn_t handler)
+{
+ struct dasd_ioctl *new;
+ if (dasd_find_ioctl(no))
+ return -EBUSY;
+ new = kmalloc(sizeof (struct dasd_ioctl), GFP_KERNEL);
+ if (new == NULL)
+ return -ENOMEM;
+ new->owner = owner;
+ new->no = no;
+ new->handler = handler;
+ list_add(&new->list, &dasd_ioctl_list);
+ return 0;
+}
+
+/*
+ * Deregister ioctl with number no.
+ */
+int
+dasd_ioctl_no_unregister(struct module *owner, int no, dasd_ioctl_fn_t handler)
+{
+ struct dasd_ioctl *old = dasd_find_ioctl(no);
+ if (old == NULL)
+ return -ENOENT;
+ if (old->no != no || old->handler != handler || owner != old->owner)
+ return -EINVAL;
+ list_del(&old->list);
+ kfree(old);
+ return 0;
+}
+
+int
+dasd_ioctl(struct inode *inp, struct file *filp,
+ unsigned int no, unsigned long data)
+{
+ struct block_device *bdev = inp->i_bdev;
+ struct dasd_device *device = bdev->bd_disk->private_data;
+ struct dasd_ioctl *ioctl;
+ const char *dir;
+ int rc;
+
+ if ((_IOC_DIR(no) != _IOC_NONE) && (data == 0)) {
+ PRINT_DEBUG("empty data ptr");
+ return -EINVAL;
+ }
+ dir = _IOC_DIR (no) == _IOC_NONE ? "0" :
+ _IOC_DIR (no) == _IOC_READ ? "r" :
+ _IOC_DIR (no) == _IOC_WRITE ? "w" :
+ _IOC_DIR (no) == (_IOC_READ | _IOC_WRITE) ? "rw" : "u";
+ DBF_DEV_EVENT(DBF_DEBUG, device,
+ "ioctl 0x%08x %s'0x%x'%d(%d) with data %8lx", no,
+ dir, _IOC_TYPE(no), _IOC_NR(no), _IOC_SIZE(no), data);
+ /* Search for ioctl no in the ioctl list. */
+ list_for_each_entry(ioctl, &dasd_ioctl_list, list) {
+ if (ioctl->no == no) {
+ /* Found a matching ioctl. Call it. */
+ if (!try_module_get(ioctl->owner))
+ continue;
+ rc = ioctl->handler(bdev, no, data);
+ module_put(ioctl->owner);
+ return rc;
+ }
+ }
+ /* No ioctl with number no. */
+ DBF_DEV_EVENT(DBF_INFO, device,
+ "unknown ioctl 0x%08x=%s'0x%x'%d(%d) data %8lx", no,
+ dir, _IOC_TYPE(no), _IOC_NR(no), _IOC_SIZE(no), data);
+ return -EINVAL;
+}
+
+long
+dasd_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ int rval;
+
+ lock_kernel();
+ rval = dasd_ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
+ unlock_kernel();
+
+ return (rval == -EINVAL) ? -ENOIOCTLCMD : rval;
+}