Hi!
This enables sg_io stuff to be shared across architectures and makes
setserial support works on all architectures.
Only change *needed* in each architecture is moving A() macros into
compat.h, so that generic code can use it. Please apply,
Pavel
--- linux.clean/arch/x86_64/ia32/ia32_ioctl.c 2003-05-05 15:53:57.000000000 -0700
+++ linux/arch/x86_64/ia32/ia32_ioctl.c 2003-05-06 14:16:12.000000000 -0700
@@ -108,10 +108,6 @@
#include <asm/mtrr.h>
-
-#define A(__x) ((void *)(unsigned long)(__x))
-#define AA(__x) A(__x)
-
/* Aiee. Someone does not find a difference between int and long */
#define EXT2_IOC32_GETFLAGS _IOR('f', 1, int)
#define EXT2_IOC32_SETFLAGS _IOW('f', 2, int)
@@ -1333,235 +1329,6 @@
return err;
}
-
-typedef struct sg_io_hdr32 {
- s32 interface_id; /* [i] 'S' for SCSI generic (required) */
- s32 dxfer_direction; /* [i] data transfer direction */
- u8 cmd_len; /* [i] SCSI command length ( <= 16 bytes) */
- u8 mx_sb_len; /* [i] max length to write to sbp */
- u16 iovec_count; /* [i] 0 implies no scatter gather */
- u32 dxfer_len; /* [i] byte count of data transfer */
- u32 dxferp; /* [i], [*io] points to data transfer memory
- or scatter gather list */
- u32 cmdp; /* [i], [*i] points to command to perform */
- u32 sbp; /* [i], [*o] points to sense_buffer memory */
- u32 timeout; /* [i] MAX_UINT->no timeout (unit: millisec) */
- u32 flags; /* [i] 0 -> default, see SG_FLAG... */
- s32 pack_id; /* [i->o] unused internally (normally) */
- u32 usr_ptr; /* [i->o] unused internally */
- u8 status; /* [o] scsi status */
- u8 masked_status; /* [o] shifted, masked scsi status */
- u8 msg_status; /* [o] messaging level data (optional) */
- u8 sb_len_wr; /* [o] byte count actually written to sbp */
- u16 host_status; /* [o] errors from host adapter */
- u16 driver_status; /* [o] errors from software driver */
- s32 resid; /* [o] dxfer_len - actual_transferred */
- u32 duration; /* [o] time taken by cmd (unit: millisec) */
- u32 info; /* [o] auxiliary information */
-} sg_io_hdr32_t; /* 64 bytes long (on sparc32) */
-
-typedef struct sg_iovec32 {
- u32 iov_base;
- u32 iov_len;
-} sg_iovec32_t;
-
-static int alloc_sg_iovec(sg_io_hdr_t *sgp, u32 uptr32)
-{
- sg_iovec32_t *uiov = (sg_iovec32_t *) A(uptr32);
- sg_iovec_t *kiov;
- int i;
-
- sgp->dxferp = kmalloc(sgp->iovec_count *
- sizeof(sg_iovec_t), GFP_KERNEL);
- if (!sgp->dxferp)
- return -ENOMEM;
- memset(sgp->dxferp, 0,
- sgp->iovec_count * sizeof(sg_iovec_t));
-
- kiov = (sg_iovec_t *) sgp->dxferp;
- for (i = 0; i < sgp->iovec_count; i++) {
- u32 iov_base32;
- if (__get_user(iov_base32, &uiov->iov_base) ||
- __get_user(kiov->iov_len, &uiov->iov_len))
- return -EFAULT;
-
- kiov->iov_base = kmalloc(kiov->iov_len, GFP_KERNEL);
- if (!kiov->iov_base)
- return -ENOMEM;
- if (copy_from_user(kiov->iov_base,
- (void *) A(iov_base32),
- kiov->iov_len))
- return -EFAULT;
-
- uiov++;
- kiov++;
- }
-
- return 0;
-}
-
-static int copy_back_sg_iovec(sg_io_hdr_t *sgp, u32 uptr32)
-{
- sg_iovec32_t *uiov = (sg_iovec32_t *) A(uptr32);
- sg_iovec_t *kiov = (sg_iovec_t *) sgp->dxferp;
- int i;
-
- for (i = 0; i < sgp->iovec_count; i++) {
- u32 iov_base32;
-
- if (__get_user(iov_base32, &uiov->iov_base))
- return -EFAULT;
-
- if (copy_to_user((void *) A(iov_base32),
- kiov->iov_base,
- kiov->iov_len))
- return -EFAULT;
-
- uiov++;
- kiov++;
- }
-
- return 0;
-}
-
-static void free_sg_iovec(sg_io_hdr_t *sgp)
-{
- sg_iovec_t *kiov = (sg_iovec_t *) sgp->dxferp;
- int i;
-
- for (i = 0; i < sgp->iovec_count; i++) {
- if (kiov->iov_base) {
- kfree(kiov->iov_base);
- kiov->iov_base = NULL;
- }
- kiov++;
- }
- kfree(sgp->dxferp);
- sgp->dxferp = NULL;
-}
-
-static int sg_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg)
-{
- sg_io_hdr32_t *sg_io32;
- sg_io_hdr_t sg_io64;
- u32 dxferp32, cmdp32, sbp32;
- mm_segment_t old_fs;
- int err = 0;
-
- sg_io32 = (sg_io_hdr32_t *)arg;
- err = __get_user(sg_io64.interface_id, &sg_io32->interface_id);
- err |= __get_user(sg_io64.dxfer_direction, &sg_io32->dxfer_direction);
- err |= __get_user(sg_io64.cmd_len, &sg_io32->cmd_len);
- err |= __get_user(sg_io64.mx_sb_len, &sg_io32->mx_sb_len);
- err |= __get_user(sg_io64.iovec_count, &sg_io32->iovec_count);
- err |= __get_user(sg_io64.dxfer_len, &sg_io32->dxfer_len);
- err |= __get_user(sg_io64.timeout, &sg_io32->timeout);
- err |= __get_user(sg_io64.flags, &sg_io32->flags);
- err |= __get_user(sg_io64.pack_id, &sg_io32->pack_id);
-
- sg_io64.dxferp = NULL;
- sg_io64.cmdp = NULL;
- sg_io64.sbp = NULL;
-
- err |= __get_user(cmdp32, &sg_io32->cmdp);
- sg_io64.cmdp = kmalloc(sg_io64.cmd_len, GFP_KERNEL);
- if (!sg_io64.cmdp) {
- err = -ENOMEM;
- goto out;
- }
- if (copy_from_user(sg_io64.cmdp,
- (void *) A(cmdp32),
- sg_io64.cmd_len)) {
- err = -EFAULT;
- goto out;
- }
-
- err |= __get_user(sbp32, &sg_io32->sbp);
- sg_io64.sbp = kmalloc(sg_io64.mx_sb_len, GFP_KERNEL);
- if (!sg_io64.sbp) {
- err = -ENOMEM;
- goto out;
- }
- if (copy_from_user(sg_io64.sbp,
- (void *) A(sbp32),
- sg_io64.mx_sb_len)) {
- err = -EFAULT;
- goto out;
- }
-
- err |= __get_user(dxferp32, &sg_io32->dxferp);
- if (sg_io64.iovec_count) {
- int ret;
-
- if ((ret = alloc_sg_iovec(&sg_io64, dxferp32))) {
- err = ret;
- goto out;
- }
- } else {
- sg_io64.dxferp = kmalloc(sg_io64.dxfer_len, GFP_KERNEL);
- if (!sg_io64.dxferp) {
- err = -ENOMEM;
- goto out;
- }
- if (copy_from_user(sg_io64.dxferp,
- (void *) A(dxferp32),
- sg_io64.dxfer_len)) {
- err = -EFAULT;
- goto out;
- }
- }
-
- /* Unused internally, do not even bother to copy it over. */
- sg_io64.usr_ptr = NULL;
-
- if (err)
- return -EFAULT;
-
- old_fs = get_fs();
- set_fs (KERNEL_DS);
- err = sys_ioctl (fd, cmd, (unsigned long) &sg_io64);
- set_fs (old_fs);
-
- if (err < 0)
- goto out;
-
- err = __put_user(sg_io64.pack_id, &sg_io32->pack_id);
- err |= __put_user(sg_io64.status, &sg_io32->status);
- err |= __put_user(sg_io64.masked_status, &sg_io32->masked_status);
- err |= __put_user(sg_io64.msg_status, &sg_io32->msg_status);
- err |= __put_user(sg_io64.sb_len_wr, &sg_io32->sb_len_wr);
- err |= __put_user(sg_io64.host_status, &sg_io32->host_status);
- err |= __put_user(sg_io64.driver_status, &sg_io32->driver_status);
- err |= __put_user(sg_io64.resid, &sg_io32->resid);
- err |= __put_user(sg_io64.duration, &sg_io32->duration);
- err |= __put_user(sg_io64.info, &sg_io32->info);
- err |= copy_to_user((void *)A(sbp32), sg_io64.sbp, sg_io64.mx_sb_len);
- if (sg_io64.dxferp) {
- if (sg_io64.iovec_count)
- err |= copy_back_sg_iovec(&sg_io64, dxferp32);
- else
- err |= copy_to_user((void *)A(dxferp32),
- sg_io64.dxferp,
- sg_io64.dxfer_len);
- }
- if (err)
- err = -EFAULT;
-
-out:
- if (sg_io64.cmdp)
- kfree(sg_io64.cmdp);
- if (sg_io64.sbp)
- kfree(sg_io64.sbp);
- if (sg_io64.dxferp) {
- if (sg_io64.iovec_count) {
- free_sg_iovec(&sg_io64);
- } else {
- kfree(sg_io64.dxferp);
- }
- }
- return err;
-}
-
struct sock_fprog32 {
__u16 len;
__u32 filter;
@@ -3190,8 +2957,6 @@
/* And these ioctls need translation */
HANDLE_IOCTL(TIOCGDEV, tiocgdev)
-HANDLE_IOCTL(TIOCGSERIAL, serial_struct_ioctl)
-HANDLE_IOCTL(TIOCSSERIAL, serial_struct_ioctl)
HANDLE_IOCTL(MEMREADOOB32, mtd_rw_oob)
HANDLE_IOCTL(MEMWRITEOOB32, mtd_rw_oob)
#ifdef CONFIG_NET
@@ -3274,7 +3039,6 @@
HANDLE_IOCTL(FDPOLLDRVSTAT32, fd_ioctl_trans)
HANDLE_IOCTL(FDGETFDCSTAT32, fd_ioctl_trans)
HANDLE_IOCTL(FDWERRORGET32, fd_ioctl_trans)
-HANDLE_IOCTL(SG_IO,sg_ioctl_trans)
HANDLE_IOCTL(PPPIOCGIDLE32, ppp_ioctl_trans)
HANDLE_IOCTL(PPPIOCSCOMPRESS32, ppp_ioctl_trans)
HANDLE_IOCTL(PPPIOCSPASS32, ppp_sock_fprog_ioctl_trans)
--- linux.clean/drivers/block/Makefile 2003-05-05 15:49:42.000000000 -0700
+++ linux/drivers/block/Makefile 2003-05-06 13:53:24.000000000 -0700
@@ -25,7 +25,7 @@
obj-$(CONFIG_BLK_DEV_PS2) += ps2esdi.o
obj-$(CONFIG_BLK_DEV_XD) += xd.o
obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o
-obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o
+obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o
obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o
obj-$(CONFIG_BLK_DEV_UMEM) += umem.o
--- linux.clean/drivers/block/scsi_ioctl.c 2003-05-05 15:49:42.000000000 -0700
+++ linux/drivers/block/scsi_ioctl.c 2003-05-07 04:51:52.000000000 -0700
@@ -15,6 +15,9 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-
*
+ * Changes:
+ * Pavel Machek <[email protected]> 05/2003
+ * - move ioctl32 emulation here [this patch is dedicated to [email protected]]
*/
#include <linux/kernel.h>
#include <linux/errno.h>
@@ -25,12 +28,14 @@
#include <linux/cdrom.h>
#include <linux/slab.h>
#include <linux/bio.h>
+#include <linux/compat.h>
+#include <linux/ioctl32.h>
+#include <linux/init.h>
#include <asm/uaccess.h>
#include <scsi/scsi.h>
#include <scsi/scsi_ioctl.h>
-
/* Command group 3 is reserved and should never be used. */
const unsigned char scsi_command_size[8] =
{
@@ -294,6 +299,213 @@
return 0;
}
+#ifdef CONFIG_COMPAT
+typedef struct sg_io_hdr32 {
+ s32 interface_id; /* [i] 'S' for SCSI generic (required) */
+ s32 dxfer_direction; /* [i] data transfer direction */
+ u8 cmd_len; /* [i] SCSI command length ( <= 16 bytes) */
+ u8 mx_sb_len; /* [i] max length to write to sbp */
+ u16 iovec_count; /* [i] 0 implies no scatter gather */
+ u32 dxfer_len; /* [i] byte count of data transfer */
+ u32 dxferp; /* [i], [*io] points to data transfer memory
+ or scatter gather list */
+ u32 cmdp; /* [i], [*i] points to command to perform */
+ u32 sbp; /* [i], [*o] points to sense_buffer memory */
+ u32 timeout; /* [i] MAX_UINT->no timeout (unit: millisec) */
+ u32 flags; /* [i] 0 -> default, see SG_FLAG... */
+ s32 pack_id; /* [i->o] unused internally (normally) */
+ u32 usr_ptr; /* [i->o] unused internally */
+ u8 status; /* [o] scsi status */
+ u8 masked_status; /* [o] shifted, masked scsi status */
+ u8 msg_status; /* [o] messaging level data (optional) */
+ u8 sb_len_wr; /* [o] byte count actually written to sbp */
+ u16 host_status; /* [o] errors from host adapter */
+ u16 driver_status; /* [o] errors from software driver */
+ s32 resid; /* [o] dxfer_len - actual_transferred */
+ u32 duration; /* [o] time taken by cmd (unit: millisec) */
+ u32 info; /* [o] auxiliary information */
+} sg_io_hdr32_t; /* 64 bytes long (on sparc32) */
+
+typedef struct sg_iovec32 {
+ u32 iov_base;
+ u32 iov_len;
+} sg_iovec32_t;
+
+#define EMU_SG_MAX 128
+
+static int alloc_sg_iovec(sg_io_hdr_t *sgp, u32 uptr32)
+{
+ sg_iovec32_t *uiov = (sg_iovec32_t *) A(uptr32);
+ sg_iovec_t *kiov;
+ int i;
+
+ if (sgp->iovec_count > EMU_SG_MAX)
+ return -EINVAL;
+ sgp->dxferp = kmalloc(sgp->iovec_count *
+ sizeof(sg_iovec_t), GFP_KERNEL);
+ if (!sgp->dxferp)
+ return -ENOMEM;
+ memset(sgp->dxferp, 0,
+ sgp->iovec_count * sizeof(sg_iovec_t));
+
+ kiov = (sg_iovec_t *) sgp->dxferp;
+ for (i = 0; i < sgp->iovec_count; i++) {
+ u32 iov_base32;
+ if (__get_user(iov_base32, &uiov->iov_base) ||
+ __get_user(kiov->iov_len, &uiov->iov_len))
+ return -EFAULT;
+ if (verify_area(VERIFY_WRITE, (void *)A(iov_base32), kiov->iov_len))
+ return -EFAULT;
+ kiov->iov_base = (void *)A(iov_base32);
+ uiov++;
+ kiov++;
+ }
+
+ return 0;
+}
+
+static void free_sg_iovec(sg_io_hdr_t *sgp)
+{
+ kfree(sgp->dxferp);
+ sgp->dxferp = NULL;
+}
+
+static int sg_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *f)
+{
+ sg_io_hdr32_t *sg_io32;
+ sg_io_hdr_t sg_io64;
+ u32 dxferp32, cmdp32, sbp32;
+ mm_segment_t old_fs;
+ int err = 0;
+
+ sg_io32 = (sg_io_hdr32_t *)arg;
+ err = __get_user(sg_io64.interface_id, &sg_io32->interface_id);
+ err |= __get_user(sg_io64.dxfer_direction, &sg_io32->dxfer_direction);
+ err |= __get_user(sg_io64.cmd_len, &sg_io32->cmd_len);
+ err |= __get_user(sg_io64.mx_sb_len, &sg_io32->mx_sb_len);
+ err |= __get_user(sg_io64.iovec_count, &sg_io32->iovec_count);
+ err |= __get_user(sg_io64.dxfer_len, &sg_io32->dxfer_len);
+ err |= __get_user(sg_io64.timeout, &sg_io32->timeout);
+ err |= __get_user(sg_io64.flags, &sg_io32->flags);
+ err |= __get_user(sg_io64.pack_id, &sg_io32->pack_id);
+
+ sg_io64.dxferp = NULL;
+ sg_io64.cmdp = NULL;
+ sg_io64.sbp = NULL;
+
+ err |= __get_user(cmdp32, &sg_io32->cmdp);
+ sg_io64.cmdp = kmalloc(sg_io64.cmd_len, GFP_KERNEL);
+ if (!sg_io64.cmdp) {
+ err = -ENOMEM;
+ goto out;
+ }
+ if (copy_from_user(sg_io64.cmdp,
+ (void *) A(cmdp32),
+ sg_io64.cmd_len)) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ err |= __get_user(sbp32, &sg_io32->sbp);
+ sg_io64.sbp = kmalloc(sg_io64.mx_sb_len, GFP_KERNEL);
+ if (!sg_io64.sbp) {
+ err = -ENOMEM;
+ goto out;
+ }
+ if (copy_from_user(sg_io64.sbp,
+ (void *) A(sbp32),
+ sg_io64.mx_sb_len)) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ err |= __get_user(dxferp32, &sg_io32->dxferp);
+ if (sg_io64.iovec_count) {
+ int ret;
+
+ if ((ret = alloc_sg_iovec(&sg_io64, dxferp32))) {
+ err = ret;
+ goto out;
+ }
+ } else {
+ if (sg_io64.dxfer_len > 4*PAGE_SIZE) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ sg_io64.dxferp = kmalloc(sg_io64.dxfer_len, GFP_KERNEL);
+ if (!sg_io64.dxferp) {
+ err = -ENOMEM;
+ goto out;
+ }
+ if (copy_from_user(sg_io64.dxferp,
+ (void *) A(dxferp32),
+ sg_io64.dxfer_len)) {
+ err = -EFAULT;
+ goto out;
+ }
+ }
+
+ /* Unused internally, do not even bother to copy it over. */
+ sg_io64.usr_ptr = NULL;
+
+ if (err)
+ return -EFAULT;
+
+ old_fs = get_fs();
+ set_fs (KERNEL_DS);
+ err = sys_ioctl (fd, cmd, (unsigned long) &sg_io64);
+ set_fs (old_fs);
+
+ if (err < 0)
+ goto out;
+
+ err = __put_user(sg_io64.pack_id, &sg_io32->pack_id);
+ err |= __put_user(sg_io64.status, &sg_io32->status);
+ err |= __put_user(sg_io64.masked_status, &sg_io32->masked_status);
+ err |= __put_user(sg_io64.msg_status, &sg_io32->msg_status);
+ err |= __put_user(sg_io64.sb_len_wr, &sg_io32->sb_len_wr);
+ err |= __put_user(sg_io64.host_status, &sg_io32->host_status);
+ err |= __put_user(sg_io64.driver_status, &sg_io32->driver_status);
+ err |= __put_user(sg_io64.resid, &sg_io32->resid);
+ err |= __put_user(sg_io64.duration, &sg_io32->duration);
+ err |= __put_user(sg_io64.info, &sg_io32->info);
+ err |= copy_to_user((void *)A(sbp32), sg_io64.sbp, sg_io64.mx_sb_len);
+ if (sg_io64.dxferp) {
+ if (sg_io64.iovec_count)
+ ;
+ else
+ err |= copy_to_user((void *)A(dxferp32),
+ sg_io64.dxferp,
+ sg_io64.dxfer_len);
+ }
+ if (err)
+ err = -EFAULT;
+
+out:
+ if (sg_io64.cmdp)
+ kfree(sg_io64.cmdp);
+ if (sg_io64.sbp)
+ kfree(sg_io64.sbp);
+ if (sg_io64.dxferp) {
+ if (sg_io64.iovec_count) {
+ free_sg_iovec(&sg_io64);
+ } else {
+ kfree(sg_io64.dxferp);
+ }
+ }
+ return err;
+}
+
+static int __init init_compat(void)
+{
+ register_ioctl32_conversion(SG_IO, sg_ioctl_trans);
+ return 0;
+}
+
+__initcall(init_compat);
+#endif
+
#define FORMAT_UNIT_TIMEOUT (2 * 60 * 60 * HZ)
#define START_STOP_TIMEOUT (60 * HZ)
#define MOVE_MEDIUM_TIMEOUT (5 * 60 * HZ)
--- linux.clean/drivers/scsi/scsi_ioctl.c 2003-04-08 04:20:20.000000000 -0700
+++ linux/drivers/scsi/scsi_ioctl.c 2003-05-06 14:24:19.000000000 -0700
@@ -24,12 +24,12 @@
#include <scsi/scsi_ioctl.h>
#define NORMAL_RETRIES 5
-#define IOCTL_NORMAL_TIMEOUT (10 * HZ)
+#define IOCTL_NORMAL_TIMEOUT (10 * HZ)
#define FORMAT_UNIT_TIMEOUT (2 * 60 * 60 * HZ)
#define START_STOP_TIMEOUT (60 * HZ)
#define MOVE_MEDIUM_TIMEOUT (5 * 60 * HZ)
#define READ_ELEMENT_STATUS_TIMEOUT (5 * 60 * HZ)
-#define READ_DEFECT_DATA_TIMEOUT (60 * HZ ) /* ZIP-250 on parallel port takes as long! */
+#define READ_DEFECT_DATA_TIMEOUT (60 * HZ) /* ZIP-250 on parallel port takes as long! */
#define MAX_BUF PAGE_SIZE
@@ -362,8 +362,6 @@
error:
if (buf)
kfree(buf);
-
-
return result;
#else
{
--- linux.clean/drivers/serial/core.c 2003-05-05 15:50:12.000000000 -0700
+++ linux/drivers/serial/core.c 2003-05-06 14:17:08.000000000 -0700
@@ -32,6 +32,7 @@
#include <linux/smp_lock.h>
#include <linux/device.h>
#include <linux/serial.h> /* for serial_state and serial_icounter_struct */
+#include <linux/ioctl32.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
@@ -1147,6 +1148,67 @@
return ret;
}
+#ifdef CONFIG_COMPAT
+struct serial_struct32 {
+ int type;
+ int line;
+ unsigned int port;
+ int irq;
+ int flags;
+ int xmit_fifo_size;
+ int custom_divisor;
+ int baud_base;
+ unsigned short close_delay;
+ char io_type;
+ char reserved_char[1];
+ int hub6;
+ unsigned short closing_wait; /* time to wait before closing */
+ unsigned short closing_wait2; /* no longer used... */
+ __u32 iomem_base;
+ unsigned short iomem_reg_shift;
+ unsigned int port_high;
+ int reserved[1];
+};
+
+static int serial_struct_ioctl(unsigned fd, unsigned cmd, unsigned long ptr, struct file *f)
+{
+ typedef struct serial_struct SS;
+ struct serial_struct32 *ss32 = (void *) ptr;
+ int err;
+ struct serial_struct ss;
+ mm_segment_t oldseg = get_fs();
+ if (cmd == TIOCSSERIAL) {
+ if (copy_from_user(&ss, ss32, sizeof(struct serial_struct32)))
+ return -EFAULT;
+ memmove(&ss.iomem_reg_shift, ((char*)&ss.iomem_base)+4,
+ sizeof(SS)-offsetof(SS,iomem_reg_shift));
+ ss.iomem_base = (void *)((unsigned long)ss.iomem_base & 0xffffffff);
+ }
+ set_fs(KERNEL_DS);
+ err = sys_ioctl(fd,cmd,(unsigned long)(&ss));
+ set_fs(oldseg);
+ if (cmd == TIOCGSERIAL && err >= 0) {
+ if (__copy_to_user(ss32,&ss,offsetof(SS,iomem_base)) ||
+ __put_user((unsigned long)ss.iomem_base >> 32 ?
+ 0xffffffff : (unsigned)(unsigned long)ss.iomem_base,
+ &ss32->iomem_base) ||
+ __put_user(ss.iomem_reg_shift, &ss32->iomem_reg_shift) ||
+ __put_user(ss.port_high, &ss32->port_high))
+ return -EFAULT;
+ }
+ return err;
+}
+
+static int __init init_compat(void)
+{
+ register_ioctl32_conversion(TIOCGSERIAL, serial_struct_ioctl);
+ register_ioctl32_conversion(TIOCSSERIAL, serial_struct_ioctl);
+ return 0;
+}
+
+__initcall(init_compat);
+#endif
+
static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios)
{
struct uart_state *state = tty->driver_data;
--- linux.clean/include/asm-x86_64/compat.h 2003-04-21 13:41:23.000000000 -0700
+++ linux/include/asm-x86_64/compat.h 2003-05-06 14:00:02.000000000 -0700
@@ -128,4 +128,8 @@
return (void *)(unsigned long)uptr;
}
+/* For compat_ioctl handlers */
+#define A(__x) ((void *)(unsigned long)(__x))
+#define AA(__x) A(__x)
+
#endif /* _ASM_X86_64_COMPAT_H */
--- linux.clean/include/linux/ioctl32.h 2003-05-05 15:53:04.000000000 -0700
+++ linux/include/linux/ioctl32.h 2003-05-06 13:58:57.000000000 -0700
@@ -2,9 +2,9 @@
#define IOCTL32_H 1
struct file;
-
extern long sys_ioctl(unsigned int, unsigned int, unsigned long);
+
/*
* Register an 32bit ioctl translation handler for ioctl cmd.
*
@@ -14,12 +14,15 @@
* arg: ioctl argument
* struct file *file: file descriptor pointer.
*/
+typedef int (*ioctl_trans_handler_t)(unsigned int, unsigned int, unsigned long, struct file *);
extern int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *));
-
extern int unregister_ioctl32_conversion(unsigned int cmd);
-typedef int (*ioctl_trans_handler_t)(unsigned int, unsigned int, unsigned long, struct file *);
+/* FIXME: several conversions make sense for one cmd depending on
+ * device being used (don't complain, its ioctl, its supposed to be
+ * ugly). Therefore unregisters needs *handler, too.
+ */
struct ioctl_trans {
unsigned long cmd;
--
When do you have a heart between your knees?
[Johanka's followup: and *two* hearts?]
Pavel Machek wrote:
> Only change *needed* in each architecture is moving A() macros into
> compat.h, so that generic code can use it. Please apply,
Please don't use A() in new code, we now have compat_ptr() and
compat_uptr_t for this.
Arnd <><
Hi!
> > Only change *needed* in each architecture is moving A() macros into
> > compat.h, so that generic code can use it. Please apply,
>
> Please don't use A() in new code, we now have compat_ptr() and
> compat_uptr_t for this.
Fixed now.
Pavel
--- linux.clean/arch/x86_64/ia32/ia32_ioctl.c 2003-05-05 15:53:57.000000000 -0700
+++ linux/arch/x86_64/ia32/ia32_ioctl.c 2003-05-07 07:01:29.000000000 -0700
@@ -1333,235 +1333,6 @@
return err;
}
-
-typedef struct sg_io_hdr32 {
- s32 interface_id; /* [i] 'S' for SCSI generic (required) */
- s32 dxfer_direction; /* [i] data transfer direction */
- u8 cmd_len; /* [i] SCSI command length ( <= 16 bytes) */
- u8 mx_sb_len; /* [i] max length to write to sbp */
- u16 iovec_count; /* [i] 0 implies no scatter gather */
- u32 dxfer_len; /* [i] byte count of data transfer */
- u32 dxferp; /* [i], [*io] points to data transfer memory
- or scatter gather list */
- u32 cmdp; /* [i], [*i] points to command to perform */
- u32 sbp; /* [i], [*o] points to sense_buffer memory */
- u32 timeout; /* [i] MAX_UINT->no timeout (unit: millisec) */
- u32 flags; /* [i] 0 -> default, see SG_FLAG... */
- s32 pack_id; /* [i->o] unused internally (normally) */
- u32 usr_ptr; /* [i->o] unused internally */
- u8 status; /* [o] scsi status */
- u8 masked_status; /* [o] shifted, masked scsi status */
- u8 msg_status; /* [o] messaging level data (optional) */
- u8 sb_len_wr; /* [o] byte count actually written to sbp */
- u16 host_status; /* [o] errors from host adapter */
- u16 driver_status; /* [o] errors from software driver */
- s32 resid; /* [o] dxfer_len - actual_transferred */
- u32 duration; /* [o] time taken by cmd (unit: millisec) */
- u32 info; /* [o] auxiliary information */
-} sg_io_hdr32_t; /* 64 bytes long (on sparc32) */
-
-typedef struct sg_iovec32 {
- u32 iov_base;
- u32 iov_len;
-} sg_iovec32_t;
-
-static int alloc_sg_iovec(sg_io_hdr_t *sgp, u32 uptr32)
-{
- sg_iovec32_t *uiov = (sg_iovec32_t *) A(uptr32);
- sg_iovec_t *kiov;
- int i;
-
- sgp->dxferp = kmalloc(sgp->iovec_count *
- sizeof(sg_iovec_t), GFP_KERNEL);
- if (!sgp->dxferp)
- return -ENOMEM;
- memset(sgp->dxferp, 0,
- sgp->iovec_count * sizeof(sg_iovec_t));
-
- kiov = (sg_iovec_t *) sgp->dxferp;
- for (i = 0; i < sgp->iovec_count; i++) {
- u32 iov_base32;
- if (__get_user(iov_base32, &uiov->iov_base) ||
- __get_user(kiov->iov_len, &uiov->iov_len))
- return -EFAULT;
-
- kiov->iov_base = kmalloc(kiov->iov_len, GFP_KERNEL);
- if (!kiov->iov_base)
- return -ENOMEM;
- if (copy_from_user(kiov->iov_base,
- (void *) A(iov_base32),
- kiov->iov_len))
- return -EFAULT;
-
- uiov++;
- kiov++;
- }
-
- return 0;
-}
-
-static int copy_back_sg_iovec(sg_io_hdr_t *sgp, u32 uptr32)
-{
- sg_iovec32_t *uiov = (sg_iovec32_t *) A(uptr32);
- sg_iovec_t *kiov = (sg_iovec_t *) sgp->dxferp;
- int i;
-
- for (i = 0; i < sgp->iovec_count; i++) {
- u32 iov_base32;
-
- if (__get_user(iov_base32, &uiov->iov_base))
- return -EFAULT;
-
- if (copy_to_user((void *) A(iov_base32),
- kiov->iov_base,
- kiov->iov_len))
- return -EFAULT;
-
- uiov++;
- kiov++;
- }
-
- return 0;
-}
-
-static void free_sg_iovec(sg_io_hdr_t *sgp)
-{
- sg_iovec_t *kiov = (sg_iovec_t *) sgp->dxferp;
- int i;
-
- for (i = 0; i < sgp->iovec_count; i++) {
- if (kiov->iov_base) {
- kfree(kiov->iov_base);
- kiov->iov_base = NULL;
- }
- kiov++;
- }
- kfree(sgp->dxferp);
- sgp->dxferp = NULL;
-}
-
-static int sg_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg)
-{
- sg_io_hdr32_t *sg_io32;
- sg_io_hdr_t sg_io64;
- u32 dxferp32, cmdp32, sbp32;
- mm_segment_t old_fs;
- int err = 0;
-
- sg_io32 = (sg_io_hdr32_t *)arg;
- err = __get_user(sg_io64.interface_id, &sg_io32->interface_id);
- err |= __get_user(sg_io64.dxfer_direction, &sg_io32->dxfer_direction);
- err |= __get_user(sg_io64.cmd_len, &sg_io32->cmd_len);
- err |= __get_user(sg_io64.mx_sb_len, &sg_io32->mx_sb_len);
- err |= __get_user(sg_io64.iovec_count, &sg_io32->iovec_count);
- err |= __get_user(sg_io64.dxfer_len, &sg_io32->dxfer_len);
- err |= __get_user(sg_io64.timeout, &sg_io32->timeout);
- err |= __get_user(sg_io64.flags, &sg_io32->flags);
- err |= __get_user(sg_io64.pack_id, &sg_io32->pack_id);
-
- sg_io64.dxferp = NULL;
- sg_io64.cmdp = NULL;
- sg_io64.sbp = NULL;
-
- err |= __get_user(cmdp32, &sg_io32->cmdp);
- sg_io64.cmdp = kmalloc(sg_io64.cmd_len, GFP_KERNEL);
- if (!sg_io64.cmdp) {
- err = -ENOMEM;
- goto out;
- }
- if (copy_from_user(sg_io64.cmdp,
- (void *) A(cmdp32),
- sg_io64.cmd_len)) {
- err = -EFAULT;
- goto out;
- }
-
- err |= __get_user(sbp32, &sg_io32->sbp);
- sg_io64.sbp = kmalloc(sg_io64.mx_sb_len, GFP_KERNEL);
- if (!sg_io64.sbp) {
- err = -ENOMEM;
- goto out;
- }
- if (copy_from_user(sg_io64.sbp,
- (void *) A(sbp32),
- sg_io64.mx_sb_len)) {
- err = -EFAULT;
- goto out;
- }
-
- err |= __get_user(dxferp32, &sg_io32->dxferp);
- if (sg_io64.iovec_count) {
- int ret;
-
- if ((ret = alloc_sg_iovec(&sg_io64, dxferp32))) {
- err = ret;
- goto out;
- }
- } else {
- sg_io64.dxferp = kmalloc(sg_io64.dxfer_len, GFP_KERNEL);
- if (!sg_io64.dxferp) {
- err = -ENOMEM;
- goto out;
- }
- if (copy_from_user(sg_io64.dxferp,
- (void *) A(dxferp32),
- sg_io64.dxfer_len)) {
- err = -EFAULT;
- goto out;
- }
- }
-
- /* Unused internally, do not even bother to copy it over. */
- sg_io64.usr_ptr = NULL;
-
- if (err)
- return -EFAULT;
-
- old_fs = get_fs();
- set_fs (KERNEL_DS);
- err = sys_ioctl (fd, cmd, (unsigned long) &sg_io64);
- set_fs (old_fs);
-
- if (err < 0)
- goto out;
-
- err = __put_user(sg_io64.pack_id, &sg_io32->pack_id);
- err |= __put_user(sg_io64.status, &sg_io32->status);
- err |= __put_user(sg_io64.masked_status, &sg_io32->masked_status);
- err |= __put_user(sg_io64.msg_status, &sg_io32->msg_status);
- err |= __put_user(sg_io64.sb_len_wr, &sg_io32->sb_len_wr);
- err |= __put_user(sg_io64.host_status, &sg_io32->host_status);
- err |= __put_user(sg_io64.driver_status, &sg_io32->driver_status);
- err |= __put_user(sg_io64.resid, &sg_io32->resid);
- err |= __put_user(sg_io64.duration, &sg_io32->duration);
- err |= __put_user(sg_io64.info, &sg_io32->info);
- err |= copy_to_user((void *)A(sbp32), sg_io64.sbp, sg_io64.mx_sb_len);
- if (sg_io64.dxferp) {
- if (sg_io64.iovec_count)
- err |= copy_back_sg_iovec(&sg_io64, dxferp32);
- else
- err |= copy_to_user((void *)A(dxferp32),
- sg_io64.dxferp,
- sg_io64.dxfer_len);
- }
- if (err)
- err = -EFAULT;
-
-out:
- if (sg_io64.cmdp)
- kfree(sg_io64.cmdp);
- if (sg_io64.sbp)
- kfree(sg_io64.sbp);
- if (sg_io64.dxferp) {
- if (sg_io64.iovec_count) {
- free_sg_iovec(&sg_io64);
- } else {
- kfree(sg_io64.dxferp);
- }
- }
- return err;
-}
-
struct sock_fprog32 {
__u16 len;
__u32 filter;
@@ -3190,8 +2961,6 @@
/* And these ioctls need translation */
HANDLE_IOCTL(TIOCGDEV, tiocgdev)
-HANDLE_IOCTL(TIOCGSERIAL, serial_struct_ioctl)
-HANDLE_IOCTL(TIOCSSERIAL, serial_struct_ioctl)
HANDLE_IOCTL(MEMREADOOB32, mtd_rw_oob)
HANDLE_IOCTL(MEMWRITEOOB32, mtd_rw_oob)
#ifdef CONFIG_NET
@@ -3274,7 +3043,6 @@
HANDLE_IOCTL(FDPOLLDRVSTAT32, fd_ioctl_trans)
HANDLE_IOCTL(FDGETFDCSTAT32, fd_ioctl_trans)
HANDLE_IOCTL(FDWERRORGET32, fd_ioctl_trans)
-HANDLE_IOCTL(SG_IO,sg_ioctl_trans)
HANDLE_IOCTL(PPPIOCGIDLE32, ppp_ioctl_trans)
HANDLE_IOCTL(PPPIOCSCOMPRESS32, ppp_ioctl_trans)
HANDLE_IOCTL(PPPIOCSPASS32, ppp_sock_fprog_ioctl_trans)
--- linux.clean/drivers/block/Makefile 2003-05-05 15:49:42.000000000 -0700
+++ linux/drivers/block/Makefile 2003-05-06 13:53:24.000000000 -0700
@@ -25,7 +25,7 @@
obj-$(CONFIG_BLK_DEV_PS2) += ps2esdi.o
obj-$(CONFIG_BLK_DEV_XD) += xd.o
obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o
-obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o
+obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o
obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o
obj-$(CONFIG_BLK_DEV_UMEM) += umem.o
--- linux.clean/drivers/block/scsi_ioctl.c 2003-05-05 15:49:42.000000000 -0700
+++ linux/drivers/block/scsi_ioctl.c 2003-05-07 07:03:24.000000000 -0700
@@ -15,6 +15,9 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-
*
+ * Changes:
+ * Pavel Machek <[email protected]> 05/2003
+ * - move ioctl32 emulation here [this patch is dedicated to [email protected]]
*/
#include <linux/kernel.h>
#include <linux/errno.h>
@@ -25,12 +28,14 @@
#include <linux/cdrom.h>
#include <linux/slab.h>
#include <linux/bio.h>
+#include <linux/compat.h>
+#include <linux/ioctl32.h>
+#include <linux/init.h>
#include <asm/uaccess.h>
#include <scsi/scsi.h>
#include <scsi/scsi_ioctl.h>
-
/* Command group 3 is reserved and should never be used. */
const unsigned char scsi_command_size[8] =
{
@@ -294,6 +299,213 @@
return 0;
}
+#ifdef CONFIG_COMPAT
+typedef struct sg_io_hdr32 {
+ s32 interface_id; /* [i] 'S' for SCSI generic (required) */
+ s32 dxfer_direction; /* [i] data transfer direction */
+ u8 cmd_len; /* [i] SCSI command length ( <= 16 bytes) */
+ u8 mx_sb_len; /* [i] max length to write to sbp */
+ u16 iovec_count; /* [i] 0 implies no scatter gather */
+ u32 dxfer_len; /* [i] byte count of data transfer */
+ u32 dxferp; /* [i], [*io] points to data transfer memory
+ or scatter gather list */
+ u32 cmdp; /* [i], [*i] points to command to perform */
+ u32 sbp; /* [i], [*o] points to sense_buffer memory */
+ u32 timeout; /* [i] MAX_UINT->no timeout (unit: millisec) */
+ u32 flags; /* [i] 0 -> default, see SG_FLAG... */
+ s32 pack_id; /* [i->o] unused internally (normally) */
+ u32 usr_ptr; /* [i->o] unused internally */
+ u8 status; /* [o] scsi status */
+ u8 masked_status; /* [o] shifted, masked scsi status */
+ u8 msg_status; /* [o] messaging level data (optional) */
+ u8 sb_len_wr; /* [o] byte count actually written to sbp */
+ u16 host_status; /* [o] errors from host adapter */
+ u16 driver_status; /* [o] errors from software driver */
+ s32 resid; /* [o] dxfer_len - actual_transferred */
+ u32 duration; /* [o] time taken by cmd (unit: millisec) */
+ u32 info; /* [o] auxiliary information */
+} sg_io_hdr32_t; /* 64 bytes long (on sparc32) */
+
+typedef struct sg_iovec32 {
+ u32 iov_base;
+ u32 iov_len;
+} sg_iovec32_t;
+
+#define EMU_SG_MAX 128
+
+static int alloc_sg_iovec(sg_io_hdr_t *sgp, u32 uptr32)
+{
+ sg_iovec32_t *uiov = (sg_iovec32_t *) compat_ptr(uptr32);
+ sg_iovec_t *kiov;
+ int i;
+
+ if (sgp->iovec_count > EMU_SG_MAX)
+ return -EINVAL;
+ sgp->dxferp = kmalloc(sgp->iovec_count *
+ sizeof(sg_iovec_t), GFP_KERNEL);
+ if (!sgp->dxferp)
+ return -ENOMEM;
+ memset(sgp->dxferp, 0,
+ sgp->iovec_count * sizeof(sg_iovec_t));
+
+ kiov = (sg_iovec_t *) sgp->dxferp;
+ for (i = 0; i < sgp->iovec_count; i++) {
+ u32 iov_base32;
+ if (__get_user(iov_base32, &uiov->iov_base) ||
+ __get_user(kiov->iov_len, &uiov->iov_len))
+ return -EFAULT;
+ if (verify_area(VERIFY_WRITE, (void *)compat_ptr(iov_base32), kiov->iov_len))
+ return -EFAULT;
+ kiov->iov_base = (void *)compat_ptr(iov_base32);
+ uiov++;
+ kiov++;
+ }
+
+ return 0;
+}
+
+static void free_sg_iovec(sg_io_hdr_t *sgp)
+{
+ kfree(sgp->dxferp);
+ sgp->dxferp = NULL;
+}
+
+static int sg_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *f)
+{
+ sg_io_hdr32_t *sg_io32;
+ sg_io_hdr_t sg_io64;
+ u32 dxferp32, cmdp32, sbp32;
+ mm_segment_t old_fs;
+ int err = 0;
+
+ sg_io32 = (sg_io_hdr32_t *)arg;
+ err = __get_user(sg_io64.interface_id, &sg_io32->interface_id);
+ err |= __get_user(sg_io64.dxfer_direction, &sg_io32->dxfer_direction);
+ err |= __get_user(sg_io64.cmd_len, &sg_io32->cmd_len);
+ err |= __get_user(sg_io64.mx_sb_len, &sg_io32->mx_sb_len);
+ err |= __get_user(sg_io64.iovec_count, &sg_io32->iovec_count);
+ err |= __get_user(sg_io64.dxfer_len, &sg_io32->dxfer_len);
+ err |= __get_user(sg_io64.timeout, &sg_io32->timeout);
+ err |= __get_user(sg_io64.flags, &sg_io32->flags);
+ err |= __get_user(sg_io64.pack_id, &sg_io32->pack_id);
+
+ sg_io64.dxferp = NULL;
+ sg_io64.cmdp = NULL;
+ sg_io64.sbp = NULL;
+
+ err |= __get_user(cmdp32, &sg_io32->cmdp);
+ sg_io64.cmdp = kmalloc(sg_io64.cmd_len, GFP_KERNEL);
+ if (!sg_io64.cmdp) {
+ err = -ENOMEM;
+ goto out;
+ }
+ if (copy_from_user(sg_io64.cmdp,
+ (void *) compat_ptr(cmdp32),
+ sg_io64.cmd_len)) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ err |= __get_user(sbp32, &sg_io32->sbp);
+ sg_io64.sbp = kmalloc(sg_io64.mx_sb_len, GFP_KERNEL);
+ if (!sg_io64.sbp) {
+ err = -ENOMEM;
+ goto out;
+ }
+ if (copy_from_user(sg_io64.sbp,
+ (void *) compat_ptr(sbp32),
+ sg_io64.mx_sb_len)) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ err |= __get_user(dxferp32, &sg_io32->dxferp);
+ if (sg_io64.iovec_count) {
+ int ret;
+
+ if ((ret = alloc_sg_iovec(&sg_io64, dxferp32))) {
+ err = ret;
+ goto out;
+ }
+ } else {
+ if (sg_io64.dxfer_len > 4*PAGE_SIZE) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ sg_io64.dxferp = kmalloc(sg_io64.dxfer_len, GFP_KERNEL);
+ if (!sg_io64.dxferp) {
+ err = -ENOMEM;
+ goto out;
+ }
+ if (copy_from_user(sg_io64.dxferp,
+ (void *) compat_ptr(dxferp32),
+ sg_io64.dxfer_len)) {
+ err = -EFAULT;
+ goto out;
+ }
+ }
+
+ /* Unused internally, do not even bother to copy it over. */
+ sg_io64.usr_ptr = NULL;
+
+ if (err)
+ return -EFAULT;
+
+ old_fs = get_fs();
+ set_fs (KERNEL_DS);
+ err = sys_ioctl (fd, cmd, (unsigned long) &sg_io64);
+ set_fs (old_fs);
+
+ if (err < 0)
+ goto out;
+
+ err = __put_user(sg_io64.pack_id, &sg_io32->pack_id);
+ err |= __put_user(sg_io64.status, &sg_io32->status);
+ err |= __put_user(sg_io64.masked_status, &sg_io32->masked_status);
+ err |= __put_user(sg_io64.msg_status, &sg_io32->msg_status);
+ err |= __put_user(sg_io64.sb_len_wr, &sg_io32->sb_len_wr);
+ err |= __put_user(sg_io64.host_status, &sg_io32->host_status);
+ err |= __put_user(sg_io64.driver_status, &sg_io32->driver_status);
+ err |= __put_user(sg_io64.resid, &sg_io32->resid);
+ err |= __put_user(sg_io64.duration, &sg_io32->duration);
+ err |= __put_user(sg_io64.info, &sg_io32->info);
+ err |= copy_to_user((void *)compat_ptr(sbp32), sg_io64.sbp, sg_io64.mx_sb_len);
+ if (sg_io64.dxferp) {
+ if (sg_io64.iovec_count)
+ ;
+ else
+ err |= copy_to_user((void *)compat_ptr(dxferp32),
+ sg_io64.dxferp,
+ sg_io64.dxfer_len);
+ }
+ if (err)
+ err = -EFAULT;
+
+out:
+ if (sg_io64.cmdp)
+ kfree(sg_io64.cmdp);
+ if (sg_io64.sbp)
+ kfree(sg_io64.sbp);
+ if (sg_io64.dxferp) {
+ if (sg_io64.iovec_count) {
+ free_sg_iovec(&sg_io64);
+ } else {
+ kfree(sg_io64.dxferp);
+ }
+ }
+ return err;
+}
+
+static int __init init_compat(void)
+{
+ register_ioctl32_conversion(SG_IO, sg_ioctl_trans);
+ return 0;
+}
+
+__initcall(init_compat);
+#endif
+
#define FORMAT_UNIT_TIMEOUT (2 * 60 * 60 * HZ)
#define START_STOP_TIMEOUT (60 * HZ)
#define MOVE_MEDIUM_TIMEOUT (5 * 60 * HZ)
--- linux.clean/drivers/serial/core.c 2003-05-05 15:50:12.000000000 -0700
+++ linux/drivers/serial/core.c 2003-05-06 14:17:08.000000000 -0700
@@ -32,6 +32,7 @@
#include <linux/smp_lock.h>
#include <linux/device.h>
#include <linux/serial.h> /* for serial_state and serial_icounter_struct */
+#include <linux/ioctl32.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
@@ -1147,6 +1148,67 @@
return ret;
}
+#ifdef CONFIG_COMPAT
+struct serial_struct32 {
+ int type;
+ int line;
+ unsigned int port;
+ int irq;
+ int flags;
+ int xmit_fifo_size;
+ int custom_divisor;
+ int baud_base;
+ unsigned short close_delay;
+ char io_type;
+ char reserved_char[1];
+ int hub6;
+ unsigned short closing_wait; /* time to wait before closing */
+ unsigned short closing_wait2; /* no longer used... */
+ __u32 iomem_base;
+ unsigned short iomem_reg_shift;
+ unsigned int port_high;
+ int reserved[1];
+};
+
+static int serial_struct_ioctl(unsigned fd, unsigned cmd, unsigned long ptr, struct file *f)
+{
+ typedef struct serial_struct SS;
+ struct serial_struct32 *ss32 = (void *) ptr;
+ int err;
+ struct serial_struct ss;
+ mm_segment_t oldseg = get_fs();
+ if (cmd == TIOCSSERIAL) {
+ if (copy_from_user(&ss, ss32, sizeof(struct serial_struct32)))
+ return -EFAULT;
+ memmove(&ss.iomem_reg_shift, ((char*)&ss.iomem_base)+4,
+ sizeof(SS)-offsetof(SS,iomem_reg_shift));
+ ss.iomem_base = (void *)((unsigned long)ss.iomem_base & 0xffffffff);
+ }
+ set_fs(KERNEL_DS);
+ err = sys_ioctl(fd,cmd,(unsigned long)(&ss));
+ set_fs(oldseg);
+ if (cmd == TIOCGSERIAL && err >= 0) {
+ if (__copy_to_user(ss32,&ss,offsetof(SS,iomem_base)) ||
+ __put_user((unsigned long)ss.iomem_base >> 32 ?
+ 0xffffffff : (unsigned)(unsigned long)ss.iomem_base,
+ &ss32->iomem_base) ||
+ __put_user(ss.iomem_reg_shift, &ss32->iomem_reg_shift) ||
+ __put_user(ss.port_high, &ss32->port_high))
+ return -EFAULT;
+ }
+ return err;
+}
+
+static int __init init_compat(void)
+{
+ register_ioctl32_conversion(TIOCGSERIAL, serial_struct_ioctl);
+ register_ioctl32_conversion(TIOCSSERIAL, serial_struct_ioctl);
+ return 0;
+}
+
+__initcall(init_compat);
+#endif
+
static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios)
{
struct uart_state *state = tty->driver_data;
--- linux.clean/include/linux/ioctl32.h 2003-05-05 15:53:04.000000000 -0700
+++ linux/include/linux/ioctl32.h 2003-05-06 13:58:57.000000000 -0700
@@ -2,9 +2,9 @@
#define IOCTL32_H 1
struct file;
-
extern long sys_ioctl(unsigned int, unsigned int, unsigned long);
+
/*
* Register an 32bit ioctl translation handler for ioctl cmd.
*
@@ -14,12 +14,15 @@
* arg: ioctl argument
* struct file *file: file descriptor pointer.
*/
+typedef int (*ioctl_trans_handler_t)(unsigned int, unsigned int, unsigned long, struct file *);
extern int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *));
-
extern int unregister_ioctl32_conversion(unsigned int cmd);
-typedef int (*ioctl_trans_handler_t)(unsigned int, unsigned int, unsigned long, struct file *);
+/* FIXME: several conversions make sense for one cmd depending on
+ * device being used (don't complain, its ioctl, its supposed to be
+ * ugly). Therefore unregisters needs *handler, too.
+ */
struct ioctl_trans {
unsigned long cmd;
--
When do you have a heart between your knees?
[Johanka's followup: and *two* hearts?]
On Wed, May 07, 2003 at 02:41:14PM +0200, Pavel Machek wrote:
> Hi!
>
> > > Only change *needed* in each architecture is moving A() macros into
> > > compat.h, so that generic code can use it. Please apply,
> >
> > Please don't use A() in new code, we now have compat_ptr() and
> > compat_uptr_t for this.
>
> Fixed now.
Btw, if you really want to move all the 32bit ioctl compat code to the
drivers a ->ioctl32 file operation might be the better choice..
On Wednesday 07 May 2003 14:41, Pavel Machek wrote:
> Hi!
>
> > > Only change *needed* in each architecture is moving A() macros into
> > > compat.h, so that generic code can use it. Please apply,
> >
> > Please don't use A() in new code, we now have compat_ptr() and
> > compat_uptr_t for this.
>
> Fixed now.
Ok. I now noticed there are some more problems that I did not see
at first:
> --- linux.clean/drivers/block/Makefile 2003-05-05 15:49:42.000000000 -0700
> +++ linux/drivers/block/Makefile 2003-05-06 13:53:24.000000000 -0700
> @@ -25,7 +25,7 @@
> obj-$(CONFIG_BLK_DEV_PS2) += ps2esdi.o
> obj-$(CONFIG_BLK_DEV_XD) += xd.o
> obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o
> -obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o
> +obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o
> obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o
huh?
> +typedef struct sg_io_hdr32 {
> + s32 interface_id; /* [i] 'S' for SCSI generic (required) */
> + s32 dxfer_direction; /* [i] data transfer direction */
> + u8 cmd_len; /* [i] SCSI command length ( <= 16 bytes) */
> + u8 mx_sb_len; /* [i] max length to write to sbp */
> + u16 iovec_count; /* [i] 0 implies no scatter gather */
> + u32 dxfer_len; /* [i] byte count of data transfer */
> + u32 dxferp; /* [i], [*io] points to data transfer memory
> + or scatter gather list */
> + u32 cmdp; /* [i], [*i] points to command to perform */
> + u32 sbp; /* [i], [*o] points to sense_buffer memory */
> + u32 timeout; /* [i] MAX_UINT->no timeout (unit: millisec) */
> + u32 flags; /* [i] 0 -> default, see SG_FLAG... */
> + s32 pack_id; /* [i->o] unused internally (normally) */
> + u32 usr_ptr; /* [i->o] unused internally */
> + u8 status; /* [o] scsi status */
> + u8 masked_status; /* [o] shifted, masked scsi status */
> + u8 msg_status; /* [o] messaging level data (optional) */
> + u8 sb_len_wr; /* [o] byte count actually written to sbp */
> + u16 host_status; /* [o] errors from host adapter */
> + u16 driver_status; /* [o] errors from software driver */
> + s32 resid; /* [o] dxfer_len - actual_transferred */
> + u32 duration; /* [o] time taken by cmd (unit: millisec) */
> + u32 info; /* [o] auxiliary information */
> +} sg_io_hdr32_t; /* 64 bytes long (on sparc32) */
> +
> +typedef struct sg_iovec32 {
> + u32 iov_base;
> + u32 iov_len;
> +} sg_iovec32_t;
These should better be expressed with compat_uptr_t, compat_ulong_t etc.
> + sg_iovec32_t *uiov = (sg_iovec32_t *) compat_ptr(uptr32);
> + if (verify_area(VERIFY_WRITE, (void *)compat_ptr(iov_base32),
> kiov->iov_len)) + return -EFAULT;
> + kiov->iov_base = (void *)compat_ptr(iov_base32);
You don't need to cast to a pointer when using compat_ptr.
> +static int __init init_compat(void)
> +{
> + register_ioctl32_conversion(SG_IO, sg_ioctl_trans);
> + return 0;
> +}
> +
> +__initcall(init_compat);
> +#endif
> +
> #define FORMAT_UNIT_TIMEOUT (2 * 60 * 60 * HZ)
> #define START_STOP_TIMEOUT (60 * HZ)
> #define MOVE_MEDIUM_TIMEOUT (5 * 60 * HZ)
Has anyone solved the register_ioctl32_conversion() from module problem
yet? The patch will break if you build scsi as a module because you
never unregister the conversion helper on unload.
Even if you do the unregister from a module_exit() function, there
will still be a small race against running ioctl handlers. I suppose
we have to add an 'owner' field to struct ioctl_trans in order to
get it right.
> +#ifdef CONFIG_COMPAT
> +struct serial_struct32 {
> + int type;
> + int line;
> + unsigned int port;
> + int irq;
> + int flags;
> + int xmit_fifo_size;
> + int custom_divisor;
> + int baud_base;
> + unsigned short close_delay;
> + char io_type;
> + char reserved_char[1];
> + int hub6;
> + unsigned short closing_wait; /* time to wait before closing */
> + unsigned short closing_wait2; /* no longer used... */
> + __u32 iomem_base;
> + unsigned short iomem_reg_shift;
> + unsigned int port_high;
> + int reserved[1];
> +};
see above.
> + ss.iomem_base = (void *)((unsigned long)ss.iomem_base & 0xffffffff);
you need compat_ptr() for iomem_base as well
> +
> +static int __init init_compat(void)
> +{
> + register_ioctl32_conversion(TIOCGSERIAL, serial_struct_ioctl);
> + register_ioctl32_conversion(TIOCSSERIAL, serial_struct_ioctl);
> + return 0;
> +}
> +
> +__initcall(init_compat);
see above.
Arnd <><
Arnd Bergmann wrote:
> On Wednesday 07 May 2003 14:41, Pavel Machek wrote:
>
> Ok. I now noticed there are some more problems that I did not see
> at first:
>
>
>>--- linux.clean/drivers/block/Makefile 2003-05-05 15:49:42.000000000 -0700
>>+++ linux/drivers/block/Makefile 2003-05-06 13:53:24.000000000 -0700
>>@@ -25,7 +25,7 @@
>> obj-$(CONFIG_BLK_DEV_PS2) += ps2esdi.o
>> obj-$(CONFIG_BLK_DEV_XD) += xd.o
>> obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o
>>-obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o
>>+obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o
>> obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o
>
>
> huh?
Tab vs. spaces
Carl-Daniel
On Wed, 2003-05-07 at 05:56, Christoph Hellwig wrote:
> Btw, if you really want to move all the 32bit ioctl compat code to the
> drivers a ->ioctl32 file operation might be the better choice..
I can't believe I never thought of that. :-)
--
David S. Miller <[email protected]>
On Wed, May 07, 2003 at 07:39:00AM -0700, David S. Miller wrote:
> On Wed, 2003-05-07 at 05:56, Christoph Hellwig wrote:
> > Btw, if you really want to move all the 32bit ioctl compat code to the
> > drivers a ->ioctl32 file operation might be the better choice..
>
> I can't believe I never thought of that. :-)
Likewise. That's a good idea...
Jeff
Hi!
> > +typedef struct sg_io_hdr32 {
> > + s32 interface_id; /* [i] 'S' for SCSI generic (required) */
> > + s32 dxfer_direction; /* [i] data transfer direction */
> > + u8 cmd_len; /* [i] SCSI command length ( <= 16 bytes) */
> > + u8 mx_sb_len; /* [i] max length to write to sbp */
> > + u16 iovec_count; /* [i] 0 implies no scatter gather */
> > + u32 dxfer_len; /* [i] byte count of data transfer */
> > + u32 dxferp; /* [i], [*io] points to data transfer memory
> > + or scatter gather list */
> > + u32 cmdp; /* [i], [*i] points to command to perform */
> > + u32 sbp; /* [i], [*o] points to sense_buffer memory */
> > + u32 timeout; /* [i] MAX_UINT->no timeout (unit: millisec) */
> > + u32 flags; /* [i] 0 -> default, see SG_FLAG... */
> > + s32 pack_id; /* [i->o] unused internally (normally) */
> > + u32 usr_ptr; /* [i->o] unused internally */
> > + u8 status; /* [o] scsi status */
> > + u8 masked_status; /* [o] shifted, masked scsi status */
> > + u8 msg_status; /* [o] messaging level data (optional) */
> > + u8 sb_len_wr; /* [o] byte count actually written to sbp */
> > + u16 host_status; /* [o] errors from host adapter */
> > + u16 driver_status; /* [o] errors from software driver */
> > + s32 resid; /* [o] dxfer_len - actual_transferred */
> > + u32 duration; /* [o] time taken by cmd (unit: millisec) */
> > + u32 info; /* [o] auxiliary information */
> > +} sg_io_hdr32_t; /* 64 bytes long (on sparc32) */
> > +
> > +typedef struct sg_iovec32 {
> > + u32 iov_base;
> > + u32 iov_len;
> > +} sg_iovec32_t;
>
> These should better be expressed with compat_uptr_t, compat_ulong_t etc.
>
>
> > + sg_iovec32_t *uiov = (sg_iovec32_t *) compat_ptr(uptr32);
>
> > + if (verify_area(VERIFY_WRITE, (void *)compat_ptr(iov_base32),
> > kiov->iov_len)) + return -EFAULT;
> > + kiov->iov_base = (void *)compat_ptr(iov_base32);
>
> You don't need to cast to a pointer when using compat_ptr.
I thought so. I originally wanted as clean move as possible. I'll fix it.
> > +static int __init init_compat(void)
> > +{
> > + register_ioctl32_conversion(SG_IO, sg_ioctl_trans);
> > + return 0;
> > +}
> > +
> > +__initcall(init_compat);
> > +#endif
> > +
> > #define FORMAT_UNIT_TIMEOUT (2 * 60 * 60 * HZ)
> > #define START_STOP_TIMEOUT (60 * HZ)
> > #define MOVE_MEDIUM_TIMEOUT (5 * 60 * HZ)
>
> Has anyone solved the register_ioctl32_conversion() from module problem
> yet? The patch will break if you build scsi as a module because you
> never unregister the conversion helper on unload.
> Even if you do the unregister from a module_exit() function, there
> will still be a small race against running ioctl handlers. I suppose
> we have to add an 'owner' field to struct ioctl_trans in order to
> get it right.
Its in drivers/block/scsi_ioctl.c. AFAICS, its always compiled in, so
I'm not hitting that problem *yet*.
> > +#ifdef CONFIG_COMPAT
> > +struct serial_struct32 {
> > + int type;
> > + int line;
> > + unsigned int port;
> > + int irq;
> > + int flags;
> > + int xmit_fifo_size;
> > + int custom_divisor;
> > + int baud_base;
> > + unsigned short close_delay;
> > + char io_type;
> > + char reserved_char[1];
> > + int hub6;
> > + unsigned short closing_wait; /* time to wait before closing */
> > + unsigned short closing_wait2; /* no longer used... */
> > + __u32 iomem_base;
> > + unsigned short iomem_reg_shift;
> > + unsigned int port_high;
> > + int reserved[1];
> > +};
> see above.
>
> > + ss.iomem_base = (void *)((unsigned long)ss.iomem_base & 0xffffffff);
> you need compat_ptr() for iomem_base as well
Its not pointer, AFAICS (at least it can not be dereferenced by
userspace).
Pavel
--
When do you have a heart between your knees?
[Johanka's followup: and *two* hearts?]
From: Jeff Garzik <[email protected]>
Date: Wed, 7 May 2003 11:12:03 -0400
On Wed, May 07, 2003 at 07:39:00AM -0700, David S. Miller wrote:
> On Wed, 2003-05-07 at 05:56, Christoph Hellwig wrote:
> > Btw, if you really want to move all the 32bit ioctl compat code to the
> > drivers a ->ioctl32 file operation might be the better choice..
>
> I can't believe I never thought of that. :-)
Likewise. That's a good idea...
But, of course, name it compat_ioctl() not ioctl32. The compat layer
is not only about 32-bit compatibility even though that is all that it
is used for currently.
Hi!
> > > > Only change *needed* in each architecture is moving A() macros into
> > > > compat.h, so that generic code can use it. Please apply,
> > >
> > > Please don't use A() in new code, we now have compat_ptr() and
> > > compat_uptr_t for this.
> >
> > Fixed now.
>
> Btw, if you really want to move all the 32bit ioctl compat code to the
> drivers a ->ioctl32 file operation might be the better choice..
Not sure if we are not too close to stable release to do that? And I
see no incremental way how to get there. Moving compatibility stuff
closer to drivers can be done close to stable release...
OTOH it forces pain where it belongs, and when someone invents stupid
ioctl, it puts pain on him... I like that.
Pavel
--
When do you have a heart between your knees?
[Johanka's followup: and *two* hearts?]
On Wednesday 07 May 2003 17:16, Pavel Machek wrote:
> > Has anyone solved the register_ioctl32_conversion() from module problem
> > yet? The patch will break if you build scsi as a module because you
> > never unregister the conversion helper on unload.
> > Even if you do the unregister from a module_exit() function, there
> > will still be a small race against running ioctl handlers. I suppose
> > we have to add an 'owner' field to struct ioctl_trans in order to
> > get it right.
>
> Its in drivers/block/scsi_ioctl.c. AFAICS, its always compiled in, so
> I'm not hitting that problem *yet*.
No, it has indeed been possible to build scsi as a module for a long
time and in that case, scsi_ioctl becomes part of that module. The same
problem also exists for any user of register_ioctl32_conversion(), e.g.
ieee1394.
> > > + ss.iomem_base = (void *)((unsigned long)ss.iomem_base & 0xffffffff);
> >
> > you need compat_ptr() for iomem_base as well
>
> Its not pointer, AFAICS (at least it can not be dereferenced by
> userspace).
Right, it appears to be a physical address, which therefore would require
a new mapping macro to be really correct. If this were used on s390, that
macro would be the same as compat_ptr(), the other architectures probably
need a simple cast (again, like compat_ptr()). Either of 1. new macro, 2.
compat_ptr() or 3. the existing code works correctly here, so just do
whichever you prefer. Maybe somebody else has a strong opinion on it.
Arnd <><
On Wed, 2003-05-07 at 08:28, Pavel Machek wrote:
> Not sure if we are not too close to stable release to do that? And I
> see no incremental way how to get there. Moving compatibility stuff
> closer to drivers can be done close to stable release...
You can define it as follows:
1) If entry exists in COMPAT or TRANSLATE table, invoke
fops->ioctl(), else
2) If ->compat_ioctl() exists, invoke that, else
3) Fail.
The COMPAT tables are sort of valuable, in that it eliminates
the need to duplicate code when all of a drivers ioctls need
no translation.
BTW, need to extend this to netdev->do_ioctl as well for the
handling of SIOCDEVPRIVATE based stuff. Oh goody, we can finally
fix up that crap :))))
--
David S. Miller <[email protected]>
On Wed, May 07 2003, Arnd Bergmann wrote:
> On Wednesday 07 May 2003 17:16, Pavel Machek wrote:
>
> > > Has anyone solved the register_ioctl32_conversion() from module problem
> > > yet? The patch will break if you build scsi as a module because you
> > > never unregister the conversion helper on unload.
> > > Even if you do the unregister from a module_exit() function, there
> > > will still be a small race against running ioctl handlers. I suppose
> > > we have to add an 'owner' field to struct ioctl_trans in order to
> > > get it right.
> >
> > Its in drivers/block/scsi_ioctl.c. AFAICS, its always compiled in, so
> > I'm not hitting that problem *yet*.
>
> No, it has indeed been possible to build scsi as a module for a long
> time and in that case, scsi_ioctl becomes part of that module. The same
> problem also exists for any user of register_ioctl32_conversion(), e.g.
> ieee1394.
drivers/block/scsi_ioctl.c is not part of the scsi layer, it provides
generic SG_IO functionality for scsi-like block drivers.
--
Jens Axboe
On Wednesday 07 May 2003 18:07, Jens Axboe wrote:
> On Wed, May 07 2003, Arnd Bergmann wrote:
> > No, it has indeed been possible to build scsi as a module for a long
> > time and in that case, scsi_ioctl becomes part of that module. The same
> > problem also exists for any user of register_ioctl32_conversion(), e.g.
> > ieee1394.
>
> drivers/block/scsi_ioctl.c is not part of the scsi layer, it provides
> generic SG_IO functionality for scsi-like block drivers.
Ok, sorry about the confusion. I was thinking of drivers/scsi/scsi_ioctl.c
all the time. However, the problem I meant is still present in the patch
for drivers/serial/core.c, which does get built as a module, and potentially
in any other module. Note that the whole purpose of
register_ioctl32_conversion() is to be able to put the wrappers in modules:
If you put your wrapper function in a file that is never a module, you
can simply put HANDLE_IOCTL(FOO, compat_foo) in include/linux/compat_ioctl.h.
Arnd <><
On Wednesday 07 May 2003 18:04, David S. Miller wrote:
> You can define it as follows:
>
> 1) If entry exists in COMPAT or TRANSLATE table, invoke
> fops->ioctl(), else
>
> 2) If ->compat_ioctl() exists, invoke that, else
>
> 3) Fail.
Another solution could be to use the tables only if
->compat_ioctl() is undefined or returned -ENOTTY. That
would save the hash table lookup in many cases and makes
it possible for a driver to override a generic handler
with its more specialized version (e.g. CIODEVPRIVATE).
> The COMPAT tables are sort of valuable, in that it eliminates
> the need to duplicate code when all of a drivers ioctls need
> no translation.
Right. Of course you can just as well do
.ioctl = &foo_ioctl,
.compat_ioctl = &foo_ioctl,
in that case.
Btw: is there any bit in the ioctl number definition that can
be (ab)used for compat_ioctl? Maybe we don't even need another
callback if the compatibility mode can be encoded in the
number itself (simplified):
long compat_sys_ioctl(unsigned int fd, unsigned int cmd,
unsigned long arg)
{
long err = sys_ioctl(fd, cmd | _IOC_COMPAT, arg);
if (err == -ENOTTY) /* use ioctl_trans table */
err = compat_do_ioctl(fd, cmd, arg);
return err;
}
...
long foo_ioctl(struct inode * inode, struct file * filp,
unsigned int cmd, unsigned long arg)
{
switch(cmd) {
case SOMEIOCTL:
return do_something(inode, arg);
case SOMEIOCTL | _IOC_COMPAT:
return compat_do_something(inode, arg);
case ANOTHERIOCTL:
case ANOTHERIOCTL | _IOC_COMPAT:
return do_something_else(inode, arg);
}
return -ENOTTY;
}
Arnd <><
From: Arnd Bergmann <[email protected]>
Date: Wed, 7 May 2003 21:13:07 +0200
Btw: is there any bit in the ioctl number definition that can
be (ab)used for compat_ioctl?
Unfortunately no. All the bits are used in order to allow
the size field of the encoding to be as large as possible.
On Mer, 2003-05-07 at 20:13, Arnd Bergmann wrote:
> Another solution could be to use the tables only if
> ->compat_ioctl() is undefined or returned -ENOTTY. That
We have magic -ENOIOCTLCMD type returns never meant for user space to
handle that ambiguity
On Wednesday 07 May 2003 20:12, David S. Miller wrote:
> From: Arnd Bergmann <[email protected]>
> Date: Wed, 7 May 2003 21:13:07 +0200
>
> Btw: is there any bit in the ioctl number definition that can
> be (ab)used for compat_ioctl?
>
> Unfortunately no. All the bits are used in order to allow
> the size field of the encoding to be as large as possible.
I checked the numbers that are in arch/sparc64/kernel/ioctl32.o
and found none that uses more than 9 bits for the size field,
while every architecture has at least 13 bits space. There may
of course be other ioctls that can not live with 12 bit size
fields and it's probably a bad idea to require special-casing
these in the compat code.
Arnd <><
"David S. Miller" <[email protected]> writes:
> On Wed, 2003-05-07 at 05:56, Christoph Hellwig wrote:
> > Btw, if you really want to move all the 32bit ioctl compat code to the
> > drivers a ->ioctl32 file operation might be the better choice..
>
> I can't believe I never thought of that. :-)
Why ->ioctl32()? Wouldn't ->ioctl() work equally well? That assumes
that ioctls which need translation have different numbers through.
Not sure whenever this is a problem, but most ioctls which need
translation need this due to different field sizes. Thus the total
ioctl struct size and the ioctl number (which has the size encoded)
should end up being different too. Anyone aware of corner cases where
this isn't going to work?
I ran into this compat issue while porting my scsi media changer
driver to 2.5 and I decided to register the one ioctl which needs
translation as "compatible" and handle the issue within the ->ioctl
handler of the driver. Patch below, untested, grep for
"CONFIG_COMPAT" to find the intresting places. Comments welcome.
Oh, and maybe move the ioctl compat lists stuff into modutils? So you
just have some magic macros (like EXPORT_SYMBOL + friends) where you
list your drivers ioctls and they will be registered/unregistered
automagically at boot/insmod/rmmod time?
Gerd
diff -u linux-2.5.69/Documentation/scsi-changer.txt linux/Documentation/scsi-changer.txt
--- linux-2.5.69/Documentation/scsi-changer.txt 2003-05-08 11:03:21.000000000 +0200
+++ linux/Documentation/scsi-changer.txt 2003-05-08 11:03:21.000000000 +0200
@@ -0,0 +1,184 @@
+
+README for the SCSI media changer driver
+========================================
+
+This is a driver for SCSI Medium Changer devices, which are listed
+with "Type: Medium Changer" in /proc/scsi/scsi.
+
+This is for *real* Jukeboxes. It is *not* supported to work with
+common small CD-ROM changers, neither one-lun-per-slot SCSI changers
+nor IDE drives.
+
+Userland tools available from: http://bytesex.org/changer.html
+
+
+General Information
+-------------------
+
+First some words about how changers work: A changer has 2 (possibly
+more) SCSI ID's. One for the changer device which controls the robot,
+and one for the device which actually reads and writes the data. The
+later may be anything, a MOD, a CD-ROM, a tape or whatever. For the
+changer device this is a "don't care", he *only* shuffles around the
+media, nothing else.
+
+
+The SCSI changer model is complex, compared to - for example - IDE-CD
+changers. But it allows to handle nearly all possible cases. It knows
+4 different types of changer elements:
+
+ media transport - this one shuffles around the media, i.e. the
+ transport arm. Also known as "picker".
+ storage - a slot which can hold a media.
+ import/export - the same as above, but is accessable from outside,
+ i.e. there the operator (you !) can use this to
+ fill in and remove media from the changer.
+ Sometimes named "mailslot".
+ data transfer - this is the device which reads/writes, i.e. the
+ CD-ROM / Tape / whatever drive.
+
+None of these is limited to one: A huge Jukebox could have slots for
+123 CD-ROM's, 5 CD-ROM readers (and therefore 6 SCSI ID's: the changer
+and each CD-ROM) and 2 transport arms. No problem to handle.
+
+
+How it is implemented
+---------------------
+
+I implemented the driver as character device driver with a NetBSD-like
+ioctl interface. Just grabbed NetBSD's header file and one of the
+other linux SCSI device drivers as starting point. The interface
+should be source code compatible with NetBSD. So if there is any
+software (anybody knows ???) which supports a BSDish changer driver,
+it should work with this driver too.
+
+Over time a few more ioctls where added, volume tag support for example
+wasn't covered by the NetBSD ioctl API.
+
+
+Current State
+-------------
+
+Support for more than one transport arm is not implemented yet (and
+nobody asked for it so far...).
+
+I test and use the driver myself with a 35 slot cdrom jukebox from
+Grundig. I got some reports telling it works ok with tape autoloaders
+(Exabyte, HP and DEC). Some People use this driver with amanda. It
+works fine with small (11 slots) and a huge (4 MOs, 88 slots)
+magneto-optical Jukebox. Probably with lots of other changers too, most
+(but not all :-) people mail me only if it does *not* work...
+
+I don't have any device lists, neither black-list nor white-list. Thus
+it is quite useless to ask me whenever a specific device is supported or
+not. In theory every changer device which supports the SCSI-2 media
+changer command set should work out-of-the-box with this driver. If it
+doesn't, it is a bug. Either within the driver or within the firmware
+of the changer device.
+
+
+Using it
+--------
+
+This is a character device with major number is 86, so use
+"mknod /dev/sch0 c 86 0" to create the special file for the driver.
+
+If the module finds the changer, it prints some messages about the
+device [ try "dmesg" if you don't see anything ] and should show up in
+/proc/devices. If not.... some changers use ID ? / LUN 0 for the
+device and ID ? / LUN 1 for the robot mechanism. But Linux does *not*
+look for LUN's other than 0 as default, becauce there are to many
+broken devices. So you can try:
+
+ 1) echo "scsi add-single-device 0 0 ID 1" > /proc/scsi/scsi
+ (replace ID with the SCSI-ID of the device)
+ 2) boot the kernel with "max_scsi_luns=1" on the command line
+ (append="max_scsi_luns=1" in lilo.conf should do the trick)
+
+
+Trouble?
+--------
+
+If you insmod the driver with "insmod debug=1", it will be verbose and
+prints a lot of stuff to the syslog. Compiling the kernel with
+CONFIG_SCSI_CONSTANTS=y improves the quality of the error messages alot
+because the kernel will translate the error codes into human-readable
+strings then.
+
+You can display these messages with the dmesg command (or check the
+logfiles). If you email me some question becauce of a problem with the
+driver, please include these messages.
+
+
+Insmod options
+--------------
+
+debug=0/1
+ Enable debug messages (see above, default: 0).
+
+verbose=0/1
+ Be verbose (default: 1).
+
+init=0/1
+ Send INITIALIZE ELEMENT STATUS command to the changer
+ at insmod time (default: 1).
+
+check_busy=0/1
+ When moving media from/to data transfer elements, check
+ whenever the device is busy and refuse to move if so
+ (default: 1).
+
+timeout_init=<seconds>
+ timeout for the INITIALIZE ELEMENT STATUS command
+ (default: 3600).
+
+timeout_move=<seconds>
+ timeout for all other commands (default: 120).
+
+dt_id=<id1>,<id2>,...
+dt_lun=<lun1>,<lun2>,...
+ These two allow to specify the SCSI ID and LUN for the data
+ transfer elements. You likely don't need this as the jukebox
+ should provide this information. But some devices don't ...
+
+vendor_firsts=
+vendor_counts=
+vendor_labels=
+ These insmod options can be used to tell the driver that there
+ are some vendor-specific element types. Grundig for example
+ does this. Some jukeboxes have a printer to label fresh burned
+ CDs, which is addressed as element 0xc000 (type 5). To tell the
+ driver about this vendor-specific element, use this:
+ $ insmod ch \
+ vendor_firsts=0xc000 \
+ vendor_counts=1 \
+ vendor_labels=printer
+ All three insmod options accept up to four comma-separated
+ values, this way you can configure the element types 5-8.
+ You likely need the SCSI specs for the device in question to
+ find the correct values as they are not covered by the SCSI-2
+ standard.
+
+
+Credits
+-------
+
+I wrote this driver using the famous mailing-patches-around-the-world
+method. With (more or less) help from:
+
+ Daniel Moehwald <[email protected]>
+ Dane Jasper <[email protected]>
+ R. Scott Bailey <[email protected]>
+ Jonathan Corbet <[email protected]>
+
+Special thanks go to
+ Martin Kuehne <[email protected]>
+for a old, second-hand (but full functional) cdrom jukebox which I use
+to develop/test driver and tools now.
+
+Have fun,
+
+ Gerd
+
+--
+Gerd Knorr <[email protected]>
diff -u linux-2.5.69/drivers/scsi/Kconfig linux/drivers/scsi/Kconfig
--- linux-2.5.69/drivers/scsi/Kconfig 2003-05-08 11:01:34.000000000 +0200
+++ linux/drivers/scsi/Kconfig 2003-05-08 11:03:21.000000000 +0200
@@ -111,6 +111,24 @@
<file:Documentation/scsi/scsi.txt>. The module will be called sg.
If unsure, say N.
+config CHR_DEV_SCH
+ tristate "SCSI media changer support"
+ depends on SCSI
+ ---help---
+ This is a driver for SCSI media changers. Most common devices are
+ tape libraries and MOD/CDROM jukeboxes. *Real* jukeboxes, you
+ don't need this for those tiny 6-slot cdrom changers. Media
+ changers are listed as "Type: Medium Changer" in /proc/scsi/scsi.
+ If you have such hardware and want to use it with linux, say Y
+ here. Check <file:Documentation/scsi-changer.txt> for details.
+
+ If you want to compile this as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want),
+ say M here and read <file:Documentation/modules.txt> and
+ <file:Documentation/scsi.txt>. The module will be called ch.o.
+ If unsure, say N.
+
+
comment "Some SCSI devices (e.g. CD jukebox) support multiple LUNs"
depends on SCSI
diff -u linux-2.5.69/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- linux-2.5.69/drivers/scsi/Makefile 2003-05-08 11:01:30.000000000 +0200
+++ linux/drivers/scsi/Makefile 2003-05-08 11:03:21.000000000 +0200
@@ -121,6 +121,7 @@
obj-$(CONFIG_BLK_DEV_SD) += sd_mod.o
obj-$(CONFIG_BLK_DEV_SR) += sr_mod.o
obj-$(CONFIG_CHR_DEV_SG) += sg.o
+obj-$(CONFIG_CHR_DEV_SCH) += ch.o
scsi_mod-y += scsi.o hosts.o scsi_ioctl.o constants.o \
scsicam.o scsi_error.o scsi_lib.o \
diff -u linux-2.5.69/drivers/scsi/ch.c linux/drivers/scsi/ch.c
--- linux-2.5.69/drivers/scsi/ch.c 2003-05-08 11:03:21.000000000 +0200
+++ linux/drivers/scsi/ch.c 2003-05-08 12:07:25.000000000 +0200
@@ -0,0 +1,1097 @@
+/*
+ * SCSI Media Changer device driver for Linux 2.6
+ *
+ * (c) 1996-2003 Gerd Knorr <[email protected]>
+ *
+ */
+
+#define VERSION "0.22"
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/blk.h>
+#include <linux/completion.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/ioctl32.h>
+#include <linux/compat.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <linux/chio.h> /* here are all the ioctls */
+
+#define MAJOR_NR SCSI_CHANGER_MAJOR
+
+#define CH_DT_MAX 16
+#define CH_TYPES 8
+
+#include "scsi.h"
+#include "hosts.h"
+#include <scsi/scsi_ioctl.h>
+
+MODULE_SUPPORTED_DEVICE("sch");
+MODULE_DESCRIPTION("device driver for scsi media changer devices");
+MODULE_AUTHOR("Gerd Knorr <[email protected]>");
+MODULE_LICENSE("GPL");
+
+static int check_busy = 1;
+MODULE_PARM(check_busy,"i");
+MODULE_PARM_DESC(check_busy, \
+ "enable/disable busy check for data transfer elements (default: on)");
+
+static int init = 1;
+MODULE_PARM(init,"i");
+MODULE_PARM_DESC(init, \
+ "initialize element status on driver load (default: on)");
+
+static int timeout_move = 300;
+MODULE_PARM(timeout_move,"i");
+MODULE_PARM_DESC(timeout_move,"timeout for move commands "
+ "(default: 300 seconds)");
+
+static int timeout_init = 3600;
+MODULE_PARM(timeout_init,"i");
+MODULE_PARM_DESC(timeout_init,"timeout for INITIALIZE ELEMENT STATUS "
+ "(default: 3600 seconds)");
+
+static int verbose = 1;
+MODULE_PARM(verbose,"i");
+MODULE_PARM_DESC(verbose,"be verbose (default: on)");
+
+static int debug = 0;
+MODULE_PARM(debug,"i");
+MODULE_PARM_DESC(debug,"enable/disable debug messages, also prints more "
+ "detailed sense codes on scsi errors (default: off)");
+
+static int dt_id[CH_DT_MAX] = { [ 0 ... (CH_DT_MAX-1) ] = -1 };
+static int dt_lun[CH_DT_MAX];
+MODULE_PARM(dt_id,"1-" __MODULE_STRING(CH_DT_MAX) "i");
+MODULE_PARM(dt_lun,"1-" __MODULE_STRING(CH_DT_MAX) "i");
+
+/* tell the driver about vendor-specific slots */
+static int vendor_firsts[CH_TYPES-4];
+static int vendor_counts[CH_TYPES-4];
+static char *vendor_labels[CH_TYPES-4];
+MODULE_PARM(vendor_firsts,"1-4i");
+MODULE_PARM(vendor_counts,"1-4i");
+MODULE_PARM(vendor_labels,"1-4s");
+
+#define dprintk(fmt, arg...) if (debug) \
+ printk(KERN_DEBUG "%s: " fmt, ch->name, ##arg)
+#define vprintk(fmt, arg...) if (verbose) \
+ printk(KERN_INFO "%s: " fmt, ch->name, ##arg)
+
+/* ------------------------------------------------------------------- */
+
+#define MAX_RETRIES 1
+
+static int ch_attach(Scsi_Device *);
+static void ch_detach(Scsi_Device *);
+static int ch_open(struct inode * inode, struct file * filp);
+static int ch_release(struct inode * inode, struct file * filp);
+static int ch_ioctl(struct inode * inode, struct file * filp,
+ unsigned int cmd, unsigned long arg);
+
+typedef struct {
+ struct list_head list;
+ int minor;
+ char name[8];
+ Scsi_Device *device;
+ Scsi_Device **dt; /* ptrs to data transfer elements */
+ u_int firsts[CH_TYPES];
+ u_int counts[CH_TYPES];
+ u_int unit_attention;
+ u_int voltags;
+ struct semaphore lock;
+} scsi_changer;
+
+static LIST_HEAD(ch_devlist);
+static spinlock_t ch_devlist_lock = SPIN_LOCK_UNLOCKED;
+static int ch_devcount;
+
+struct Scsi_Device_Template ch_template =
+{
+ .module = THIS_MODULE,
+ .list = LIST_HEAD_INIT(ch_template.list),
+ .name = "media changer",
+ .scsi_type = TYPE_MEDIUM_CHANGER,
+ .attach = ch_attach,
+ .detach = ch_detach,
+ .scsi_driverfs_driver = {
+ .name = "ch",
+ },
+};
+
+static struct file_operations changer_fops =
+{
+ .owner = THIS_MODULE,
+ .open = ch_open,
+ .release = ch_release,
+ .ioctl = ch_ioctl,
+};
+
+static struct {
+ unsigned char sense;
+ unsigned char asc;
+ unsigned char ascq;
+ int errno;
+} err[] = {
+/* Just filled in what looks right. Hav'nt checked any standard paper for
+ these errno assignments, so they may be wrong... */
+ {
+ .sense = ILLEGAL_REQUEST,
+ .asc = 0x21,
+ .ascq = 0x01,
+ .errno = EBADSLT, /* Invalid element address */
+ },{
+ .sense = ILLEGAL_REQUEST,
+ .asc = 0x28,
+ .ascq = 0x01,
+ .errno = EBADE, /* Import or export element accessed */
+ },{
+ .sense = ILLEGAL_REQUEST,
+ .asc = 0x3B,
+ .ascq = 0x0D,
+ .errno = EXFULL, /* Medium destination element full */
+ },{
+ .sense = ILLEGAL_REQUEST,
+ .asc = 0x3B,
+ .ascq = 0x0E,
+ .errno = EBADE, /* Medium source element empty */
+ },{
+ .sense = ILLEGAL_REQUEST,
+ .asc = 0x20,
+ .ascq = 0x00,
+ .errno = EBADRQC, /* Invalid command operation code */
+ },{
+ /* end of list */
+ }
+};
+
+/* ------------------------------------------------------------------- */
+/* ioctl32 compat */
+
+#ifdef CONFIG_COMPAT
+
+struct changer_element_status32 {
+ int ces_type;
+ compat_uptr_t ces_data;
+};
+#define CHIOGSTATUS32 _IOW('c', 8,struct changer_element_status32)
+
+static struct {
+ unsigned int cmd;
+ int reg;
+} ioctl32_cmds[] = {
+ { .cmd = CHIOMOVE },
+ { .cmd = CHIOEXCHANGE },
+ { .cmd = CHIOPOSITION },
+ { .cmd = CHIOGPICKER },
+ { .cmd = CHIOSPICKER },
+ { .cmd = CHIOGPARAMS },
+ { .cmd = CHIOGELEM },
+ { .cmd = CHIOINITELEM },
+ { .cmd = CHIOSVOLTAG },
+ { .cmd = CHIOGVPARAMS },
+ { .cmd = CHIOGSTATUS32 },
+};
+
+static int ioctl32_register(void)
+{
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < ARRAY_SIZE(ioctl32_cmds); i++) {
+ err = register_ioctl32_conversion(ioctl32_cmds[i].cmd,NULL);
+ if (err >= 0)
+ ioctl32_cmds[i].reg++;
+ }
+ return 0;
+}
+static int ioctl32_unregister(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(ioctl32_cmds); i++) {
+ if (ioctl32_cmds[i].reg) {
+ unregister_ioctl32_conversion(ioctl32_cmds[i].cmd);
+ ioctl32_cmds[i].reg--;
+ }
+ }
+ return 0;
+}
+
+#else
+
+static int ioctl32_register(void) { return 0; }
+static int ioctl32_unregister(void) { return 0; }
+
+#endif
+
+/* ------------------------------------------------------------------- */
+
+static int ch_find_errno(unsigned char *sense_buffer)
+{
+ int i,errno = 0;
+
+ /* Check to see if additional sense information is available */
+ if (sense_buffer[7] > 5 &&
+ sense_buffer[12] != 0) {
+ for (i = 0; err[i].errno != 0; i++) {
+ if (err[i].sense == sense_buffer[ 2] &&
+ err[i].asc == sense_buffer[12] &&
+ err[i].ascq == sense_buffer[13]) {
+ errno = -err[i].errno;
+ break;
+ }
+ }
+ }
+ if (errno == 0)
+ errno = -EIO;
+ return errno;
+}
+
+static void
+ch_request_done (Scsi_Cmnd * sc)
+{
+ sc->request->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
+ if (sc->request->waiting != NULL)
+ complete(sc->request->waiting);
+}
+
+static int
+ch_do_scsi(scsi_changer *ch, unsigned char *cmd,
+ void *buffer, unsigned buflength)
+{
+ int errno, retries = 0, timeout;
+ DECLARE_COMPLETION(wait);
+ Scsi_Request *sr;
+
+ sr = scsi_allocate_request(ch->device);
+ if (NULL == sr)
+ return -ENOMEM;
+
+ retry:
+ errno = 0;
+ if (debug) {
+ dprintk("command: %s","");
+ print_command(cmd);
+ }
+
+ sr->sr_request->waiting = &wait;
+ timeout = (cmd[0] == INITIALIZE_ELEMENT_STATUS)
+ ? timeout_init : timeout_move;
+ scsi_do_req(sr, cmd, buffer, buflength, ch_request_done,
+ timeout * HZ, MAX_RETRIES);
+ wait_for_completion(&wait);
+ sr->sr_request->waiting = NULL;
+
+ dprintk("result: 0x%x\n",sr->sr_result);
+ if (driver_byte(sr->sr_result) != 0) {
+ if (debug)
+ print_req_sense(ch->name, sr);
+ errno = ch_find_errno(sr->sr_sense_buffer);
+
+ switch(sr->sr_sense_buffer[2] & 0xf) {
+ case UNIT_ATTENTION:
+ ch->unit_attention = 1;
+ if (retries++ < 3)
+ goto retry;
+ break;
+ }
+ }
+ scsi_release_request(sr);
+ return errno;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static Scsi_Device*
+find_device(struct Scsi_Host *host, u_char channel, u_char id, u_char lun)
+{
+ struct list_head *item;
+ Scsi_Device *device;
+
+ list_for_each(item,&host->my_devices) {
+ device = list_entry(item, Scsi_Device, siblings);
+ if (device->channel == channel &&
+ device->id == id &&
+ device->lun == lun)
+ return device;
+ }
+ return NULL;
+}
+
+static int
+ch_elem_to_typecode(scsi_changer *ch, u_int elem)
+{
+ int i;
+
+ for (i = 0; i < CH_TYPES; i++) {
+ if (elem >= ch->firsts[i] &&
+ elem < ch->firsts[i] +
+ ch->counts[i])
+ return i+1;
+ }
+ return 0;
+}
+
+static int
+ch_read_element_status(scsi_changer *ch, u_int elem, char *data)
+{
+ u_char cmd[12];
+ u_char *buffer;
+ int result;
+
+ buffer = kmalloc(512, GFP_KERNEL);
+ if(!buffer)
+ return -ENOMEM;
+
+ retry:
+ memset(cmd,0,sizeof(cmd));
+ cmd[0] = READ_ELEMENT_STATUS;
+ cmd[1] = (ch->device->lun << 5) |
+ (ch->voltags ? 0x10 : 0) |
+ ch_elem_to_typecode(ch,elem);
+ cmd[2] = (elem >> 8) & 0xff;
+ cmd[3] = elem & 0xff;
+ cmd[5] = 1;
+ cmd[9] = 255;
+ if (0 == (result = ch_do_scsi(ch, cmd, buffer, 256))) {
+ if (((buffer[16] << 8) | buffer[17]) != elem) {
+ dprintk("asked for element 0x%02x, got 0x%02x\n",
+ elem,(buffer[16] << 8) | buffer[17]);
+ kfree(buffer);
+ return -EIO;
+ }
+ memcpy(data,buffer+16,16);
+ } else {
+ if (ch->voltags) {
+ ch->voltags = 0;
+ vprintk("device has no volume tag support%s\n","");
+ goto retry;
+ }
+ dprintk("READ ELEMENT STATUS for element 0x%x failed\n",elem);
+ }
+ kfree(buffer);
+ return result;
+}
+
+static int
+ch_init_elem(scsi_changer *ch)
+{
+ int err;
+ u_char cmd[6];
+
+ vprintk("INITIALIZE ELEMENT STATUS, may take some time ...%s\n","");
+ memset(cmd,0,sizeof(cmd));
+ cmd[0] = INITIALIZE_ELEMENT_STATUS;
+ cmd[1] = ch->device->lun << 5;
+ err = ch_do_scsi(ch, cmd, NULL, 0);
+ vprintk("... finished%s\n","");
+ return err;
+}
+
+static int
+ch_readconfig(scsi_changer *ch)
+{
+ u_char cmd[10], data[16];
+ u_char *buffer;
+ int result,id,lun,i;
+ u_int elem;
+
+ buffer = kmalloc(512, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+ memset(buffer,0,512);
+
+ memset(cmd,0,sizeof(cmd));
+ cmd[0] = MODE_SENSE;
+ cmd[1] = ch->device->lun << 5;
+ cmd[2] = 0x1d;
+ cmd[4] = 255;
+ result = ch_do_scsi(ch, cmd, buffer, 255);
+ if (0 != result) {
+ cmd[1] |= (1<<3);
+ result = ch_do_scsi(ch, cmd, buffer, 255);
+ }
+ if (0 == result) {
+ ch->firsts[CHET_MT] =
+ (buffer[buffer[3]+ 6] << 8) | buffer[buffer[3]+ 7];
+ ch->counts[CHET_MT] =
+ (buffer[buffer[3]+ 8] << 8) | buffer[buffer[3]+ 9];
+ ch->firsts[CHET_ST] =
+ (buffer[buffer[3]+10] << 8) | buffer[buffer[3]+11];
+ ch->counts[CHET_ST] =
+ (buffer[buffer[3]+12] << 8) | buffer[buffer[3]+13];
+ ch->firsts[CHET_IE] =
+ (buffer[buffer[3]+14] << 8) | buffer[buffer[3]+15];
+ ch->counts[CHET_IE] =
+ (buffer[buffer[3]+16] << 8) | buffer[buffer[3]+17];
+ ch->firsts[CHET_DT] =
+ (buffer[buffer[3]+18] << 8) | buffer[buffer[3]+19];
+ ch->counts[CHET_DT] =
+ (buffer[buffer[3]+20] << 8) | buffer[buffer[3]+21];
+ vprintk("type #1 (mt): 0x%x+%d [medium transport]\n",
+ ch->firsts[CHET_MT],
+ ch->counts[CHET_MT]);
+ vprintk("type #2 (st): 0x%x+%d [storage]\n",
+ ch->firsts[CHET_ST],
+ ch->counts[CHET_ST]);
+ vprintk("type #3 (ie): 0x%x+%d [import/export]\n",
+ ch->firsts[CHET_IE],
+ ch->counts[CHET_IE]);
+ vprintk("type #4 (dt): 0x%x+%d [data transfer]\n",
+ ch->firsts[CHET_DT],
+ ch->counts[CHET_DT]);
+ } else {
+ vprintk("reading element address assigment page failed!%s\n",
+ "");
+ }
+
+ /* vendor specific element types */
+ for (i = 0; i < 4; i++) {
+ if (0 == vendor_counts[i])
+ continue;
+ if (NULL == vendor_labels[i])
+ continue;
+ ch->firsts[CHET_V1+i] = vendor_firsts[i];
+ ch->counts[CHET_V1+i] = vendor_counts[i];
+ vprintk("type #%d (v%d): 0x%x+%d [%s, vendor specific]\n",
+ i+5,i+1,vendor_firsts[i],vendor_counts[i],
+ vendor_labels[i]);
+ }
+
+ /* look up the devices of the data transfer elements */
+ ch->dt =
+ kmalloc(ch->counts[CHET_DT]*sizeof(Scsi_Device*),
+ GFP_ATOMIC);
+ for (elem = 0; elem < ch->counts[CHET_DT]; elem++) {
+ id = -1;
+ lun = 0;
+ if (-1 != dt_id[elem]) {
+ id = dt_id[elem];
+ lun = dt_lun[elem];
+ vprintk("dt 0x%x: [insmod option] ",
+ elem+ch->firsts[CHET_DT]);
+ } else if (0 != ch_read_element_status
+ (ch,elem+ch->firsts[CHET_DT],data)) {
+ vprintk("dt 0x%x: READ ELEMENT STATUS failed\n",
+ elem+ch->firsts[CHET_DT]);
+ } else {
+ vprintk("dt 0x%x: ",elem+ch->firsts[CHET_DT]);
+ if (data[6] & 0x80) {
+ if (verbose)
+ printk("not this SCSI bus\n");
+ ch->dt[elem] = NULL;
+ } else if (0 == (data[6] & 0x30)) {
+ if (verbose)
+ printk("ID/LUN unknown\n");
+ ch->dt[elem] = NULL;
+ } else {
+ id = ch->device->id;
+ lun = 0;
+ if (data[6] & 0x20) id = data[7];
+ if (data[6] & 0x10) lun = data[6] & 7;
+ }
+ }
+ if (-1 != id) {
+ if (verbose)
+ printk("ID %i, LUN %i, ",id,lun);
+ ch->dt[elem] =
+ find_device(ch->device->host,
+ ch->device->channel,
+ id,lun);
+ if (!ch->dt[elem]) {
+ /* should not happen */
+ if (verbose)
+ printk("Huh? device not found!\n");
+ } else {
+ if (verbose)
+ printk("name: %8.8s %16.16s %4.4s\n",
+ ch->dt[elem]->vendor,
+ ch->dt[elem]->model,
+ ch->dt[elem]->rev);
+ }
+ }
+ }
+ ch->voltags = 1;
+ kfree(buffer);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int
+ch_position(scsi_changer *ch, u_int trans, u_int elem, int rotate)
+{
+ u_char cmd[10];
+
+ dprintk("position: 0x%x\n",elem);
+ if (0 == trans)
+ trans = ch->firsts[CHET_MT];
+ memset(cmd,0,sizeof(cmd));
+ cmd[0] = POSITION_TO_ELEMENT;
+ cmd[1] = ch->device->lun << 5;
+ cmd[2] = (trans >> 8) & 0xff;
+ cmd[3] = trans & 0xff;
+ cmd[4] = (elem >> 8) & 0xff;
+ cmd[5] = elem & 0xff;
+ cmd[8] = rotate ? 1 : 0;
+ return ch_do_scsi(ch, cmd, NULL,0);
+}
+
+static int
+ch_move(scsi_changer *ch, u_int trans, u_int src, u_int dest, int rotate)
+{
+ u_char cmd[12];
+
+ dprintk("move: 0x%x => 0x%x\n",src,dest);
+ if (0 == trans)
+ trans = ch->firsts[CHET_MT];
+ memset(cmd,0,sizeof(cmd));
+ cmd[0] = MOVE_MEDIUM;
+ cmd[1] = ch->device->lun << 5;
+ cmd[2] = (trans >> 8) & 0xff;
+ cmd[3] = trans & 0xff;
+ cmd[4] = (src >> 8) & 0xff;
+ cmd[5] = src & 0xff;
+ cmd[6] = (dest >> 8) & 0xff;
+ cmd[7] = dest & 0xff;
+ cmd[10] = rotate ? 1 : 0;
+ return ch_do_scsi(ch, cmd, NULL,0);
+}
+
+static int
+ch_exchange(scsi_changer *ch, u_int trans, u_int src,
+ u_int dest1, u_int dest2, int rotate1, int rotate2)
+{
+ u_char cmd[12];
+
+ dprintk("exchange: 0x%x => 0x%x => 0x%x\n",
+ src,dest1,dest2);
+ if (0 == trans)
+ trans = ch->firsts[CHET_MT];
+ memset(cmd,0,sizeof(cmd));
+ cmd[0] = EXCHANGE_MEDIUM;
+ cmd[1] = ch->device->lun << 5;
+ cmd[2] = (trans >> 8) & 0xff;
+ cmd[3] = trans & 0xff;
+ cmd[4] = (src >> 8) & 0xff;
+ cmd[5] = src & 0xff;
+ cmd[6] = (dest1 >> 8) & 0xff;
+ cmd[7] = dest1 & 0xff;
+ cmd[8] = (dest2 >> 8) & 0xff;
+ cmd[9] = dest2 & 0xff;
+ cmd[10] = (rotate1 ? 1 : 0) | (rotate2 ? 2 : 0);
+
+ return ch_do_scsi(ch, cmd, NULL,0);
+}
+
+static void
+ch_check_voltag(char *tag)
+{
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ /* restrict to ascii */
+ if (tag[i] >= 0x7f || tag[i] < 0x20)
+ tag[i] = ' ';
+ /* don't allow search wildcards */
+ if (tag[i] == '?' ||
+ tag[i] == '*')
+ tag[i] = ' ';
+ }
+}
+
+static int
+ch_set_voltag(scsi_changer *ch, u_int elem,
+ int alternate, int clear, u_char *tag)
+{
+ u_char cmd[12];
+ u_char *buffer;
+ int result;
+
+ buffer = kmalloc(512, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+ memset(buffer,0,512);
+
+ dprintk("%s %s voltag: 0x%x => \"%s\"\n",
+ clear ? "clear" : "set",
+ alternate ? "alternate" : "primary",
+ elem, tag);
+ memset(cmd,0,sizeof(cmd));
+ cmd[0] = SEND_VOLUME_TAG;
+ cmd[1] = (ch->device->lun << 5) |
+ ch_elem_to_typecode(ch,elem);
+ cmd[2] = (elem >> 8) & 0xff;
+ cmd[3] = elem & 0xff;
+ cmd[5] = clear
+ ? (alternate ? 0x0d : 0x0c)
+ : (alternate ? 0x0b : 0x0a);
+
+ cmd[9] = 255;
+
+ memcpy(buffer,tag,32);
+ ch_check_voltag(buffer);
+
+ result = ch_do_scsi(ch, cmd, buffer, 256);
+ kfree(buffer);
+ return result;
+}
+
+static int ch_gstatus(scsi_changer *ch, int type, unsigned char *dest)
+{
+ int retval = 0;
+ u_char data[16];
+ unsigned int i;
+
+ down(&ch->lock);
+ for (i = 0; i < ch->counts[type]; i++) {
+ if (0 != ch_read_element_status
+ (ch, ch->firsts[type]+i,data)) {
+ retval = -EIO;
+ break;
+ }
+ put_user(data[2], dest+i);
+ if (data[2] & CESTATUS_EXCEPT)
+ vprintk("element 0x%x: asc=0x%x, ascq=0x%x\n",
+ ch->firsts[type]+i,
+ (int)data[4],(int)data[5]);
+ retval = ch_read_element_status
+ (ch, ch->firsts[type]+i,data);
+ if (0 != retval)
+ break;
+ }
+ up(&ch->lock);
+ return retval;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int
+ch_release(struct inode *inode, struct file *file)
+{
+ scsi_changer *ch = file->private_data;
+
+ scsi_device_put(ch->device);
+ file->private_data = NULL;
+ return 0;
+}
+
+static int
+ch_open(struct inode *inode, struct file *file)
+{
+ struct list_head *item;
+ scsi_changer *tmp, *ch;
+ int minor = minor(inode->i_rdev);
+
+ spin_lock(&ch_devlist_lock);
+ ch = NULL;
+ list_for_each(item,&ch_devlist) {
+ tmp = list_entry(item, scsi_changer, list);
+ if (tmp->minor == minor)
+ ch = tmp;
+ }
+ if (NULL == ch || scsi_device_get(ch->device)) {
+ spin_unlock(&ch_devlist_lock);
+ return -ENXIO;
+ }
+ spin_unlock(&ch_devlist_lock);
+
+ file->private_data = ch;
+ return 0;
+}
+
+static int
+ch_checkrange(scsi_changer *ch, unsigned int type, unsigned int unit)
+{
+ if (type >= CH_TYPES || unit >= ch->counts[type])
+ return -1;
+ return 0;
+}
+
+/* for data transfer elements: check if they are busy */
+static int
+ch_is_busy(scsi_changer *ch, int type, int unit)
+{
+ if (!check_busy)
+ return 0;
+ if (type != CHET_DT)
+ return 0;
+ if (!ch->dt[unit])
+ return 0;
+ return ch->dt[unit]->access_count;
+}
+
+static int ch_ioctl(struct inode * inode, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ scsi_changer *ch = file->private_data;
+ int retval;
+
+ switch (cmd) {
+ case CHIOGPARAMS:
+ {
+ struct changer_params params;
+
+ params.cp_curpicker = 0;
+ params.cp_npickers = ch->counts[CHET_MT];
+ params.cp_nslots = ch->counts[CHET_ST];
+ params.cp_nportals = ch->counts[CHET_IE];
+ params.cp_ndrives = ch->counts[CHET_DT];
+
+ if (copy_to_user((void *) arg, ¶ms, sizeof(params)))
+ return -EFAULT;
+ return 0;
+ }
+ case CHIOGVPARAMS:
+ {
+ struct changer_vendor_params vparams;
+
+ memset(&vparams,0,sizeof(vparams));
+ if (ch->counts[CHET_V1]) {
+ vparams.cvp_n1 = ch->counts[CHET_V1];
+ strncpy(vparams.cvp_label1,vendor_labels[0],16);
+ }
+ if (ch->counts[CHET_V2]) {
+ vparams.cvp_n2 = ch->counts[CHET_V2];
+ strncpy(vparams.cvp_label2,vendor_labels[1],16);
+ }
+ if (ch->counts[CHET_V3]) {
+ vparams.cvp_n3 = ch->counts[CHET_V3];
+ strncpy(vparams.cvp_label3,vendor_labels[2],16);
+ }
+ if (ch->counts[CHET_V4]) {
+ vparams.cvp_n4 = ch->counts[CHET_V4];
+ strncpy(vparams.cvp_label4,vendor_labels[3],16);
+ }
+ if (copy_to_user((void *) arg, &vparams, sizeof(vparams)))
+ return -EFAULT;
+ return 0;
+ }
+
+ case CHIOPOSITION:
+ {
+ struct changer_position pos;
+
+ if (copy_from_user(&pos, (void*)arg, sizeof (pos)))
+ return -EFAULT;
+
+ if (0 != ch_checkrange(ch, pos.cp_type, pos.cp_unit)) {
+ dprintk("CHIOPOSITION: invalid parameter%s\n","");
+ return -EBADSLT;
+ }
+ down(&ch->lock);
+ retval = ch_position(ch,0,
+ ch->firsts[pos.cp_type] + pos.cp_unit,
+ pos.cp_flags & CP_INVERT);
+ up(&ch->lock);
+ return retval;
+ }
+
+ case CHIOMOVE:
+ {
+ struct changer_move mv;
+
+ if (copy_from_user(&mv, (void*)arg, sizeof (mv)))
+ return -EFAULT;
+
+ if (0 != ch_checkrange(ch, mv.cm_fromtype, mv.cm_fromunit) ||
+ 0 != ch_checkrange(ch, mv.cm_totype, mv.cm_tounit )) {
+ dprintk("CHIOMOVE: invalid parameter%s\n","");
+ return -EBADSLT;
+ }
+ if (ch_is_busy(ch, mv.cm_fromtype, mv.cm_fromunit) ||
+ ch_is_busy(ch, mv.cm_totype, mv.cm_tounit ))
+ return -EBUSY;
+
+ down(&ch->lock);
+ retval = ch_move(ch,0,
+ ch->firsts[mv.cm_fromtype] + mv.cm_fromunit,
+ ch->firsts[mv.cm_totype] + mv.cm_tounit,
+ mv.cm_flags & CM_INVERT);
+ up(&ch->lock);
+ return retval;
+ }
+
+ case CHIOEXCHANGE:
+ {
+ struct changer_exchange mv;
+
+ if (copy_from_user(&mv, (void*)arg, sizeof (mv)))
+ return -EFAULT;
+
+ if (0 != ch_checkrange(ch, mv.ce_srctype, mv.ce_srcunit ) ||
+ 0 != ch_checkrange(ch, mv.ce_fdsttype, mv.ce_fdstunit) ||
+ 0 != ch_checkrange(ch, mv.ce_sdsttype, mv.ce_sdstunit)) {
+ dprintk("CHIOEXCHANGE: invalid parameter%s\n","");
+ return -EBADSLT;
+ }
+ if (0 != ch_is_busy(ch, mv.ce_srctype, mv.ce_srcunit ) ||
+ 0 != ch_is_busy(ch, mv.ce_fdsttype, mv.ce_fdstunit) ||
+ 0 != ch_is_busy(ch, mv.ce_sdsttype, mv.ce_sdstunit))
+ return -EBUSY;
+
+ down(&ch->lock);
+ retval = ch_exchange
+ (ch,0,
+ ch->firsts[mv.ce_srctype] + mv.ce_srcunit,
+ ch->firsts[mv.ce_fdsttype] + mv.ce_fdstunit,
+ ch->firsts[mv.ce_sdsttype] + mv.ce_sdstunit,
+ mv.ce_flags & CE_INVERT1, mv.ce_flags & CE_INVERT2);
+ up(&ch->lock);
+ return retval;
+ }
+
+ case CHIOGSTATUS:
+ {
+ struct changer_element_status ces;
+
+ if (copy_from_user(&ces, (void*)arg, sizeof (ces)))
+ return -EFAULT;
+ if (ces.ces_type < 0 || ces.ces_type >= CH_TYPES)
+ return -EINVAL;
+
+ return ch_gstatus(ch, ces.ces_type, ces.ces_data);
+ }
+
+#ifdef CONFIG_COMPAT
+ case CHIOGSTATUS32:
+ {
+ struct changer_element_status32 ces32;
+ unsigned char *data;
+
+ if (copy_from_user(&ces32, (void*)arg, sizeof (ces32)))
+ return -EFAULT;
+ if (ces32.ces_type < 0 || ces32.ces_type >= CH_TYPES)
+ return -EINVAL;
+
+ data = compat_ptr(ces32.ces_data);
+ return ch_gstatus(ch, ces32.ces_type, data);
+ }
+#endif
+
+ case CHIOGELEM:
+ {
+ struct changer_get_element cge;
+ u_char cmd[12];
+ u_char *buffer;
+ unsigned int elem;
+ int result,i;
+
+ if (copy_from_user(&cge, (void*)arg, sizeof (cge)))
+ return -EFAULT;
+
+ if (0 != ch_checkrange(ch, cge.cge_type, cge.cge_unit))
+ return -EINVAL;
+ elem = ch->firsts[cge.cge_type] + cge.cge_unit;
+
+ buffer = kmalloc(512, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+ down(&ch->lock);
+
+ voltag_retry:
+ memset(cmd,0,sizeof(cmd));
+ cmd[0] = READ_ELEMENT_STATUS;
+ cmd[1] = (ch->device->lun << 5) |
+ (ch->voltags ? 0x10 : 0) |
+ ch_elem_to_typecode(ch,elem);
+ cmd[2] = (elem >> 8) & 0xff;
+ cmd[3] = elem & 0xff;
+ cmd[5] = 1;
+ cmd[9] = 255;
+
+ if (0 == (result = ch_do_scsi(ch, cmd, buffer, 256))) {
+ cge.cge_status = buffer[18];
+ cge.cge_flags = 0;
+ if (buffer[18] & CESTATUS_EXCEPT) {
+ /* FIXME: fill cge_errno */
+ }
+ if (buffer[25] & 0x80) {
+ cge.cge_flags |= CGE_SRC;
+ if (buffer[25] & 0x40)
+ cge.cge_flags |= CGE_INVERT;
+ elem = (buffer[26]<<8) | buffer[27];
+ for (i = 0; i < 4; i++) {
+ if (elem >= ch->firsts[i] &&
+ elem < ch->firsts[i] + ch->counts[i]) {
+ cge.cge_srctype = i;
+ cge.cge_srcunit = elem-ch->firsts[i];
+ }
+ }
+ }
+ if ((buffer[22] & 0x30) == 0x30) {
+ cge.cge_flags |= CGE_IDLUN;
+ cge.cge_id = buffer[23];
+ cge.cge_lun = buffer[22] & 7;
+ }
+ if (buffer[9] & 0x80) {
+ cge.cge_flags |= CGE_PVOLTAG;
+ memcpy(cge.cge_pvoltag,buffer+28,36);
+ }
+ if (buffer[9] & 0x40) {
+ cge.cge_flags |= CGE_AVOLTAG;
+ memcpy(cge.cge_avoltag,buffer+64,36);
+ }
+ } else if (ch->voltags) {
+ ch->voltags = 0;
+ vprintk("device has no volume tag support%s\n","");
+ goto voltag_retry;
+ }
+ kfree(buffer);
+ up(&ch->lock);
+
+ if (copy_to_user((void*)arg, &cge, sizeof (cge)))
+ return -EFAULT;
+ return result;
+ }
+
+ case CHIOINITELEM:
+ {
+ down(&ch->lock);
+ retval = ch_init_elem(ch);
+ up(&ch->lock);
+ return retval;
+ }
+
+ case CHIOSVOLTAG:
+ {
+ struct changer_set_voltag csv;
+ int elem;
+
+ if (copy_from_user(&csv, (void*)arg, sizeof(csv)))
+ return -EFAULT;
+
+ if (0 != ch_checkrange(ch, csv.csv_type, csv.csv_unit)) {
+ dprintk("CHIOSVOLTAG: invalid parameter%s\n","");
+ return -EBADSLT;
+ }
+ elem = ch->firsts[csv.csv_type] + csv.csv_unit;
+ down(&ch->lock);
+ retval = ch_set_voltag(ch, elem,
+ csv.csv_flags & CSV_AVOLTAG,
+ csv.csv_flags & CSV_CLEARTAG,
+ csv.csv_voltag);
+ up(&ch->lock);
+ return retval;
+ }
+
+ default:
+ return scsi_ioctl(ch->device, cmd, (void*)arg);
+
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int ch_attach(Scsi_Device * sd)
+{
+ scsi_changer *ch;
+
+ if (sd->type != TYPE_MEDIUM_CHANGER)
+ return 1;
+
+ ch = kmalloc(sizeof(*ch), GFP_KERNEL);
+ if (NULL == ch)
+ return 1;
+
+ memset(ch,0,sizeof(*ch));
+ ch->minor = ch_devcount;
+ sprintf(ch->name,"ch%d",ch->minor);
+ init_MUTEX(&ch->lock);
+ ch->device = sd;
+ ch_readconfig(ch);
+ if (init)
+ ch_init_elem(ch);
+
+ devfs_register(NULL, ch->name, 0,
+ MAJOR_NR, ch->minor,
+ S_IFCHR | S_IRUGO | S_IWUGO,
+ &changer_fops, NULL);
+
+ printk(KERN_INFO "Attached scsi changer %s "
+ "at scsi%d, channel %d, id %d, lun %d\n",
+ ch->name, sd->host->host_no, sd->channel, sd->id, sd->lun);
+
+ spin_lock(&ch_devlist_lock);
+ list_add_tail(&ch->list,&ch_devlist);
+ ch_devcount++;
+ spin_unlock(&ch_devlist_lock);
+ return 0;
+}
+
+static void ch_detach(Scsi_Device *sd)
+{
+ struct list_head *item;
+ scsi_changer *tmp, *ch;
+
+ spin_lock(&ch_devlist_lock);
+ ch = NULL;
+ list_for_each(item,&ch_devlist) {
+ tmp = list_entry(item, scsi_changer, list);
+ if (tmp->device == sd)
+ ch = tmp;
+ }
+ BUG_ON(NULL == ch);
+ list_del(&ch->list);
+ spin_unlock(&ch_devlist_lock);
+
+ devfs_remove(ch->name);
+ kfree(ch->dt);
+ kfree(ch);
+ ch_devcount--;
+ return;
+}
+
+static int __init init_ch_module(void)
+{
+ int rc;
+
+ printk(KERN_INFO "SCSI Media Changer driver v" VERSION
+ " for Linux " UTS_RELEASE "\n");
+ rc = register_chrdev(MAJOR_NR,"ch",&changer_fops);
+ if (rc < 0) {
+ printk("Unable to get major %d for SCSI-Changer\n",
+ MAJOR_NR);
+ return rc;
+ }
+ ioctl32_register();
+ rc = scsi_register_device(&ch_template);
+ if (rc < 0)
+ goto fail1;
+ return 0;
+
+ fail1:
+ ioctl32_unregister();
+ unregister_chrdev(MAJOR_NR, "ch");
+ return rc;
+}
+
+static void __exit exit_ch_module(void)
+{
+ scsi_unregister_device(&ch_template);
+ unregister_chrdev(MAJOR_NR, "ch");
+ ioctl32_unregister();
+}
+
+module_init(init_ch_module);
+module_exit(exit_ch_module);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -u linux-2.5.69/include/linux/chio.h linux/include/linux/chio.h
--- linux-2.5.69/include/linux/chio.h 2003-05-08 11:03:21.000000000 +0200
+++ linux/include/linux/chio.h 2003-05-08 11:45:50.000000000 +0200
@@ -0,0 +1,168 @@
+/*
+ * ioctl interface for the scsi media changer driver
+ */
+
+/* changer element types */
+#define CHET_MT 0 /* media transport element (robot) */
+#define CHET_ST 1 /* storage element (media slots) */
+#define CHET_IE 2 /* import/export element */
+#define CHET_DT 3 /* data transfer element (tape/cdrom/whatever) */
+#define CHET_V1 4 /* vendor specific #1 */
+#define CHET_V2 5 /* vendor specific #2 */
+#define CHET_V3 6 /* vendor specific #3 */
+#define CHET_V4 7 /* vendor specific #4 */
+
+
+/*
+ * CHIOGPARAMS
+ * query changer properties
+ *
+ * CHIOVGPARAMS
+ * query vendor-specific element types
+ *
+ * accessing elements works by specifing type and unit of the element.
+ * for eample, storage elements are addressed with type = CHET_ST and
+ * unit = 0 .. cp_nslots-1
+ *
+ */
+struct changer_params {
+ int cp_curpicker; /* current transport element */
+ int cp_npickers; /* number of transport elements (CHET_MT) */
+ int cp_nslots; /* number of storage elements (CHET_ST) */
+ int cp_nportals; /* number of import/export elements (CHET_IE) */
+ int cp_ndrives; /* number of data transfer elements (CHET_DT) */
+};
+struct changer_vendor_params {
+ int cvp_n1; /* number of vendor specific elems (CHET_V1) */
+ char cvp_label1[16];
+ int cvp_n2; /* number of vendor specific elems (CHET_V2) */
+ char cvp_label2[16];
+ int cvp_n3; /* number of vendor specific elems (CHET_V3) */
+ char cvp_label3[16];
+ int cvp_n4; /* number of vendor specific elems (CHET_V4) */
+ char cvp_label4[16];
+ int reserved[8];
+};
+
+
+/*
+ * CHIOMOVE
+ * move a medium from one element to another
+ */
+struct changer_move {
+ int cm_fromtype; /* type/unit of source element */
+ int cm_fromunit;
+ int cm_totype; /* type/unit of destination element */
+ int cm_tounit;
+ int cm_flags;
+};
+#define CM_INVERT 1 /* flag: rotate media (for double-sided like MOD) */
+
+
+/*
+ * CHIOEXCHANGE
+ * move one medium from element #1 to element #2,
+ * and another one from element #2 to element #3.
+ * element #1 and #3 are allowed to be identical.
+ */
+struct changer_exchange {
+ int ce_srctype; /* type/unit of element #1 */
+ int ce_srcunit;
+ int ce_fdsttype; /* type/unit of element #2 */
+ int ce_fdstunit;
+ int ce_sdsttype; /* type/unit of element #3 */
+ int ce_sdstunit;
+ int ce_flags;
+};
+#define CE_INVERT1 1
+#define CE_INVERT2 2
+
+
+/*
+ * CHIOPOSITION
+ * move the transport element (robot arm) to a specific element.
+ */
+struct changer_position {
+ int cp_type;
+ int cp_unit;
+ int cp_flags;
+};
+#define CP_INVERT 1
+
+
+/*
+ * CHIOGSTATUS
+ * get element status for all elements of a specific type
+ */
+struct changer_element_status {
+ int ces_type;
+ unsigned char *ces_data;
+};
+#define CESTATUS_FULL 0x01 /* full */
+#define CESTATUS_IMPEXP 0x02 /* media was imported (inserted by sysop) */
+#define CESTATUS_EXCEPT 0x04 /* error condition */
+#define CESTATUS_ACCESS 0x08 /* access allowed */
+#define CESTATUS_EXENAB 0x10 /* element can export media */
+#define CESTATUS_INENAB 0x20 /* element can import media */
+
+
+/*
+ * CHIOGELEM
+ * get more detailed status informtion for a single element
+ */
+struct changer_get_element {
+ int cge_type; /* type/unit */
+ int cge_unit;
+ int cge_status; /* status */
+ int cge_errno; /* errno */
+ int cge_srctype; /* source element of the last move/exchange */
+ int cge_srcunit;
+ int cge_id; /* scsi id (for data transfer elements) */
+ int cge_lun; /* scsi lun (for data transfer elements) */
+ char cge_pvoltag[36]; /* primary volume tag */
+ char cge_avoltag[36]; /* alternate volume tag */
+ int cge_flags;
+};
+/* flags */
+#define CGE_ERRNO 0x01 /* errno available */
+#define CGE_INVERT 0x02 /* media inverted */
+#define CGE_SRC 0x04 /* media src available */
+#define CGE_IDLUN 0x08 /* ID+LUN available */
+#define CGE_PVOLTAG 0x10 /* primary volume tag available */
+#define CGE_AVOLTAG 0x20 /* alternate volume tag available */
+
+
+/*
+ * CHIOSVOLTAG
+ * set volume tag
+ */
+struct changer_set_voltag {
+ int csv_type; /* type/unit */
+ int csv_unit;
+ char csv_voltag[36]; /* volume tag */
+ int csv_flags;
+};
+#define CSV_PVOLTAG 0x01 /* primary volume tag */
+#define CSV_AVOLTAG 0x02 /* alternate volume tag */
+#define CSV_CLEARTAG 0x04 /* clear volume tag */
+
+/* ioctls */
+#define CHIOMOVE _IOW('c', 1,struct changer_move)
+#define CHIOEXCHANGE _IOW('c', 2,struct changer_exchange)
+#define CHIOPOSITION _IOW('c', 3,struct changer_position)
+#define CHIOGPICKER _IOR('c', 4,int) /* not impl. */
+#define CHIOSPICKER _IOW('c', 5,int) /* not impl. */
+#define CHIOGPARAMS _IOR('c', 6,struct changer_params)
+#define CHIOGSTATUS _IOW('c', 8,struct changer_element_status)
+#define CHIOGELEM _IOW('c',16,struct changer_get_element)
+#define CHIOINITELEM _IO('c',17)
+#define CHIOSVOLTAG _IOW('c',18,struct changer_set_voltag)
+#define CHIOGVPARAMS _IOR('c',19,struct changer_vendor_params)
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -u linux-2.5.69/include/linux/major.h linux/include/linux/major.h
--- linux-2.5.69/include/linux/major.h 2003-05-08 11:00:15.000000000 +0200
+++ linux/include/linux/major.h 2003-05-08 11:03:21.000000000 +0200
@@ -100,6 +100,7 @@
#define I2O_MAJOR 80 /* 80->87 */
#define SHMIQ_MAJOR 85 /* Linux/mips, SGI /dev/shmiq */
+#define SCSI_CHANGER_MAJOR 86
#define IDE6_MAJOR 88
#define IDE7_MAJOR 89
diff -u linux-2.5.69/include/scsi/scsi.h linux/include/scsi/scsi.h
--- linux-2.5.69/include/scsi/scsi.h 2003-05-08 11:01:43.000000000 +0200
+++ linux/include/scsi/scsi.h 2003-05-08 11:03:21.000000000 +0200
@@ -31,6 +31,7 @@
#define FORMAT_UNIT 0x04
#define READ_BLOCK_LIMITS 0x05
#define REASSIGN_BLOCKS 0x07
+#define INITIALIZE_ELEMENT_STATUS 0x07
#define READ_6 0x08
#define WRITE_6 0x0a
#define SEEK_6 0x0b
@@ -55,6 +56,7 @@
#define READ_10 0x28
#define WRITE_10 0x2a
#define SEEK_10 0x2b
+#define POSITION_TO_ELEMENT 0x2b
#define WRITE_VERIFY 0x2e
#define VERIFY 0x2f
#define SEARCH_HIGH 0x30
@@ -87,6 +89,7 @@
#define PERSISTENT_RESERVE_OUT 0x5f
#define REPORT_LUNS 0xa0
#define MOVE_MEDIUM 0xa5
+#define EXCHANGE_MEDIUM 0xa6
#define READ_12 0xa8
#define WRITE_12 0xaa
#define WRITE_VERIFY_12 0xae
From: Arnd Bergmann <[email protected]>
Date: Thu, 8 May 2003 01:50:10 +0200
I checked the numbers that are in arch/sparc64/kernel/ioctl32.o
and found none that uses more than 9 bits for the size field,
I know that we had to change our sparc ioctl macro definitions a few
months ago to accomodate some ioctl that wanted more bits.
It isn't a theoretical problem.
Gerd Knorr wrote:
> ioctl struct size and the ioctl number (which has the size encoded)
> should end up being different too. Anyone aware of corner cases where
> this isn't going to work?
About half of the ioctls that need special care have fixed numbers
instead of using _IOR() etc, see e.g. include/linux/sockios.h,
or they get the definition wrong in some way.
The way you do it in your patch could work for many cases, but it
won't be enough to eliminate HANDLE_IOCTL(), if that is desired.
Adding fops->compat_ioctl() makes it possible to eventually replace
all HANDLE_IOCTL() and keep only COMPATIBLE_IOCTL(), which in turn
would become simpler to deal with.
If we don't add fops->compat_ioctl(), the ioctl handlers could
however look at (current_thread_info()->flags & _TIF_32BIT) to find
out if which user data structure they should expect. Is that reliable?
Do we already have a macro to do it?
Arnd <><
On Wed, May 07, 2003 at 07:39:00AM -0700, David S. Miller wrote:
> On Wed, 2003-05-07 at 05:56, Christoph Hellwig wrote:
> > Btw, if you really want to move all the 32bit ioctl compat code to the
> > drivers a ->ioctl32 file operation might be the better choice..
>
> I can't believe I never thought of that. :-)
How would the driver differentiate between .compat_ioctl == NULL being a
case where it should fail because there is no translation, or a case
where it should use the compatible .ioctl? Maybe there should be an
extra flag like use_compat_ioctl. So:
.use_compat_ioctl = 1;
.ioctl = my_ioctl;
.compat_ioctl = my_compat_ioctl;
Means use my_compat_ioctl() for translation. And just:
.use_compat_ioctl = 1;
.ioctl = my_ioctl;
Means that our standard my_ioctl is 32/64 compatible.
This would also solve the current problem where a module that is
compiled with compat ioctl's using register_ioctl32_conversion() is not
usable on a kernel compiled without CONFIG_COMPAT, even though it very
well should be.
--
Debian - http://www.debian.org/
Linux 1394 - http://www.linux1394.org/
Subversion - http://subversion.tigris.org/
Deqo - http://www.deqo.com/
On Thu, May 08, 2003 at 11:16:43AM -0400, Ben Collins wrote:
> How would the driver differentiate between .compat_ioctl == NULL being a
> case where it should fail because there is no translation, or a case
> where it should use the compatible .ioctl? Maybe there should be an
> extra flag like use_compat_ioctl. So:
.compat_ioctl == NULL: fail
.compat_ioctl == .ioctl: everythings fine, I read the docs
> This would also solve the current problem where a module that is
> compiled with compat ioctl's using register_ioctl32_conversion() is not
> usable on a kernel compiled without CONFIG_COMPAT, even though it very
> well should be.
You mean you want to load the same binary module in differently
compiled kernels? That's a flawed idea to start with..
On Thu, May 08, 2003 at 04:51:18PM +0100, Christoph Hellwig wrote:
> On Thu, May 08, 2003 at 11:16:43AM -0400, Ben Collins wrote:
> > How would the driver differentiate between .compat_ioctl == NULL being a
> > case where it should fail because there is no translation, or a case
> > where it should use the compatible .ioctl? Maybe there should be an
> > extra flag like use_compat_ioctl. So:
>
> .compat_ioctl == NULL: fail
> .compat_ioctl == .ioctl: everythings fine, I read the docs
That makes sense aswell.
> > This would also solve the current problem where a module that is
> > compiled with compat ioctl's using register_ioctl32_conversion() is not
> > usable on a kernel compiled without CONFIG_COMPAT, even though it very
> > well should be.
>
> You mean you want to load the same binary module in differently
> compiled kernels? That's a flawed idea to start with..
I don't, but I don't see the point in it not working in this case.
--
Debian - http://www.debian.org/
Linux 1394 - http://www.linux1394.org/
Subversion - http://subversion.tigris.org/
Deqo - http://www.deqo.com/
Hi!
> This would also solve the current problem where a module that is
> compiled with compat ioctl's using register_ioctl32_conversion() is not
> usable on a kernel compiled without CONFIG_COMPAT, even though it very
> well should be.
CONFIG_COMPAT is pretty much constant depending only on
architecture. I see no point in complicating this.
Pavel
--
When do you have a heart between your knees?
[Johanka's followup: and *two* hearts?]
On Thu, May 08, 2003 at 09:34:30PM +0200, Pavel Machek wrote:
> Hi!
>
> > This would also solve the current problem where a module that is
> > compiled with compat ioctl's using register_ioctl32_conversion() is not
> > usable on a kernel compiled without CONFIG_COMPAT, even though it very
> > well should be.
>
> CONFIG_COMPAT is pretty much constant depending only on
> architecture. I see no point in complicating this.
I don't think so. Sparc64 and ia64 I know allow you to disable 32bit
compatibility. I'd be surprised if the other 32/64 architectures didn't.
--
Debian - http://www.debian.org/
Linux 1394 - http://www.linux1394.org/
Subversion - http://subversion.tigris.org/
Deqo - http://www.deqo.com/
> On Thu, May 08, 2003 at 09:34:30PM +0200, Pavel Machek wrote:
> > Hi!
> >
> > > This would also solve the current problem where a module that is
> > > compiled with compat ioctl's using register_ioctl32_conversion() is not
> > > usable on a kernel compiled without CONFIG_COMPAT, even though it very
> > > well should be.
> >
> > CONFIG_COMPAT is pretty much constant depending only on
> > architecture. I see no point in complicating this.
>
> I don't think so. Sparc64 and ia64 I know allow you to disable 32bit
> compatibility. I'd be surprised if the other 32/64 architectures
didn't.
Really? I thought sparc64 has no real 64-bit userland?
Okay, it might make sense on x86-64, but I do not think savings are
worth the trouble.
Pavel
>
--
Horseback riding is like software...
...vgf orggre jura vgf serr.
From: Pavel Machek <[email protected]>
Date: Thu, 8 May 2003 22:06:30 +0200
Really? I thought sparc64 has no real 64-bit userland?
Wrong. We use 32-bit binaries by default because they're
more efficient.
On Thu, May 08, 2003 at 10:06:30PM +0200, Pavel Machek wrote:
> > On Thu, May 08, 2003 at 09:34:30PM +0200, Pavel Machek wrote:
> > > Hi!
> > >
> > > > This would also solve the current problem where a module that is
> > > > compiled with compat ioctl's using register_ioctl32_conversion() is not
> > > > usable on a kernel compiled without CONFIG_COMPAT, even though it very
> > > > well should be.
> > >
> > > CONFIG_COMPAT is pretty much constant depending only on
> > > architecture. I see no point in complicating this.
> >
> > I don't think so. Sparc64 and ia64 I know allow you to disable 32bit
> > compatibility. I'd be surprised if the other 32/64 architectures
> didn't.
>
>
> Really? I thought sparc64 has no real 64-bit userland?
>
> Okay, it might make sense on x86-64, but I do not think savings are
> worth the trouble.
I never said it was worth just that effort. What I said was is that the
smarter change of using a compat_ioctl in the fops has the cool
side affect of allowing that to happen. Not that I ever thought it was
worth making huge changes to the infrastructure.
--
Debian - http://www.debian.org/
Linux 1394 - http://www.linux1394.org/
Subversion - http://subversion.tigris.org/
Deqo - http://www.deqo.com/
>>>>> On Thu, 8 May 2003 15:27:30 -0400, Ben Collins <[email protected]> said:
Ben> On Thu, May 08, 2003 at 09:34:30PM +0200, Pavel Machek wrote:
>> Hi!
>> > This would also solve the current problem where a module that
>> is > compiled with compat ioctl's using
>> register_ioctl32_conversion() is not > usable on a kernel
>> compiled without CONFIG_COMPAT, even though it very > well should
>> be.
>> CONFIG_COMPAT is pretty much constant depending only on
>> architecture. I see no point in complicating this.
Ben> I don't think so. Sparc64 and ia64 I know allow you to disable
Ben> 32bit compatibility. I'd be surprised if the other 32/64
Ben> architectures didn't.
Definitely. I turn it off on a regular basis and expect to use it
even less in the future.
--david
> Definitely. I turn it off on a regular basis and expect to use it
> even less in the future.
working on a qemu port to ia64 ?
Hi!
> > Not sure if we are not too close to stable release to do that? And I
> > see no incremental way how to get there. Moving compatibility stuff
> > closer to drivers can be done close to stable release...
>
> You can define it as follows:
>
> 1) If entry exists in COMPAT or TRANSLATE table, invoke
> fops->ioctl(), else
>
> 2) If ->compat_ioctl() exists, invoke that, else
>
> 3) Fail.
>
> The COMPAT tables are sort of valuable, in that it eliminates
> the need to duplicate code when all of a drivers ioctls need
> no translation.
>
> BTW, need to extend this to netdev->do_ioctl as well for the
> handling of SIOCDEVPRIVATE based stuff. Oh goody, we can finally
> fix up that crap :))))
There's a *lot* of structs that contain *ioctl:
pavel@amd:/usr/src/linux-test/include/linux$ grep "*ioctl" *
pavel@amd:/usr/src/linux-test/include/linux$ grep "*ioctl" *
atmdev.h: int (*ioctl)(struct atm_dev *dev,unsigned int cmd,void *arg);
atmdev.h: int (*ioctl)(struct atm_dev *dev,unsigned int cmd,void *arg);
fs.h: int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);
fs.h: int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
hdlc.h: int (*ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);
hdlcdrv.h: int (*ioctl)(struct net_device *, struct ifreq *,
ide.h: int (*ioctl)(ide_drive_t *, struct inode *, struct file *, unsigned int, unsigned long);
if_bridge.h:extern void brioctl_set(int (*ioctl_hook)(unsigned long));
if_pppox.h: int (*ioctl)(struct socket *sock, unsigned int cmd,
ioctl32.h:typedef int (*ioctl_trans_handler_t)(unsigned int, unsigned int, unsigned long, struct file *);
loop.h: int (*ioctl)(struct loop_device *, int cmd,
loop.h: int (*ioctl)(struct loop_device *, int cmd, unsigned long arg);
net.h: int (*ioctl) (struct socket *sock, unsigned int cmd,
ppp_channel.h: int (*ioctl)(struct ppp_channel *, unsigned int, unsigned long);
serial_core.h: int (*ioctl)(struct uart_port *, unsigned int, unsigned long);
tty_driver.h: * int (*ioctl)(struct tty_struct *tty, struct file * file,
tty_driver.h: int (*ioctl)(struct tty_struct *tty, struct file * file,
tty_ldisc.h: * int (*ioctl)(struct tty_struct * tty, struct file * file,
tty_ldisc.h: int (*ioctl)(struct tty_struct * tty, struct file * file,
usb.h: int (*ioctl) (struct usb_interface *intf, unsigned int code, void *buf);
wanrouter.h: int (*ioctl) (struct wan_device *wandev, unsigned cmd,
pavel@amd:/usr/src/linux-test/include/linux$
What about this one: redefine it to (*ioctl)( ...., unsigned *long*,
unsinged long). That means we can add
#define IOCTL_COMPAT 0x1 0000 0000
and avoid adding new field to each such structure. Also I will not
have to duplicate lots of middle-level code (I will have to modify
unsigned int -> unsigned long, but no second copies of everything). It
means that architecture with CONFIG_COMPAT needs to have unsigned long
> 32 bits, but I guess we can live with that.
What do you think?
Pavel
--
When do you have a heart between your knees?
[Johanka's followup: and *two* hearts?]
From: Pavel Machek <[email protected]>
Date: Thu, 8 May 2003 22:33:14 +0200
What about this one: redefine it to (*ioctl)( ...., unsigned *long*,
unsinged long). That means we can add
#define IOCTL_COMPAT 0x1 0000 0000
Bzzt! Doesn't work on 32-bit. COMPAT does not mean 64-bit-->32-bit
translations, stop thinking about the compat layer in this way.
It is a generic environment translation system.
Eventually we can use it for things like IBCS2 and stuff like that.
Suggest something sane like defining a macro such as
"compat_task(tsk)" that can be tested by various bits of
code.
>>>>> On 08 May 2003 22:33:42 +0200, Arjan van de Ven <[email protected]> said:
>> Definitely. I turn it off on a regular basis and expect to use it
>> even less in the future.
Arjan> working on a qemu port to ia64 ?
Who, me? I know better than that.
Last time I worked on x86 emulation was on Alpha for BIOS emulation.
It wasn't fun then.
--david
On Thursday 08 May 2003 22:33, Pavel Machek wrote:
> > BTW, need to extend this to netdev->do_ioctl as well for the
> > handling of SIOCDEVPRIVATE based stuff. Oh goody, we can finally
> > fix up that crap :))))
>
> There's a *lot* of structs that contain *ioctl:
> atmdev.h: int (*ioctl)(struct atm_dev *dev,unsigned int cmd,void *arg);
> atmdev.h: int (*ioctl)(struct atm_dev *dev,unsigned int cmd,void *arg);
> fs.h: int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);
> fs.h: int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
> hdlc.h: int (*ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);
> ...
There are even some more that your grep did not catch. However, only
few of them actually need to be changed if we add ->compat_ioctl().
For those subsystems that have a clearly defined set of ioctls that
are implemented by the specific drivers, the compatibility code can
be put in a higher level ioctl handler, e.g. atm_ioctl() instead of
each of the atm drivers.
Finding out exactly which interfaces need to be extended can be done
in the process.
> What about this one: redefine it to (*ioctl)( ...., unsigned *long*,
> unsinged long). That means we can add
>
> #define IOCTL_COMPAT 0x1 0000 0000
I had the same idea before but in addition to the problem that davem
mentioned, this would require changing every single ioctl handler
in the kernel and in third party drivers to use 'long' numbers.
Not really something we want to do during the current freeze.
The three options that currently make sense to me are:
a) keep using register_ioctl32_conversion() for everything
b) add a compat_task() function that handlers may use to decide
if they should use the compat data structures, list those ioctls
as COMPATIBLE_IOCTL()
c) add ->compat_ioctl() to some interfaces, with HANDLE_IOCTL() as
fallback
Arnd <><
Hi!
> From: Pavel Machek <[email protected]>
> Date: Thu, 8 May 2003 22:33:14 +0200
>
> What about this one: redefine it to (*ioctl)( ...., unsigned *long*,
> unsinged long). That means we can add
>
> #define IOCTL_COMPAT 0x1 0000 0000
>
> Bzzt! Doesn't work on 32-bit. COMPAT does not mean 64-bit-->32-bit
> translations, stop thinking about the compat layer in this way.
>
> It is a generic environment translation system.
>
> Eventually we can use it for things like IBCS2 and stuff like that.
I... do not think so.
You'd then need
.compat_linux32_ioctl
.compat_IBCS2_ioctl
...
I do not think that is doable.
> Suggest something sane like defining a macro such as
> "compat_task(tsk)" that can be tested by various bits of
> code.
That makes more sense. Unfortunately, that means that case "okay, it
is compatible" can not be told from "we did not bother to check
compat_task()". :-(. Nor do I see a transition phase.
Pavel
--
When do you have a heart between your knees?
[Johanka's followup: and *two* hearts?]
Hi!
> > > BTW, need to extend this to netdev->do_ioctl as well for the
> > > handling of SIOCDEVPRIVATE based stuff. Oh goody, we can finally
> > > fix up that crap :))))
> >
> > There's a *lot* of structs that contain *ioctl:
>
> > atmdev.h: int (*ioctl)(struct atm_dev *dev,unsigned int cmd,void *arg);
> > atmdev.h: int (*ioctl)(struct atm_dev *dev,unsigned int cmd,void *arg);
> > fs.h: int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);
> > fs.h: int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
> > hdlc.h: int (*ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);
> > ...
>
> There are even some more that your grep did not catch. However, only
> few of them actually need to be changed if we add ->compat_ioctl().
Having few structs with ->ioctl() and few with both ->ioctl() and
->compat_ioctl() seems pretty ugly to me...
> For those subsystems that have a clearly defined set of ioctls that
> are implemented by the specific drivers, the compatibility code can
> be put in a higher level ioctl handler, e.g. atm_ioctl() instead of
> each of the atm drivers.
If it is clearly defined, it should not be an ioctl() in the first
place. Yep, you are right ioctl is probably abused for that at few
points... And finding out where it ->compat_ioctl() is going to be
"interesting".
> Finding out exactly which interfaces need to be extended can be done
> in the process.
> > What about this one: redefine it to (*ioctl)( ...., unsigned *long*,
> > unsinged long). That means we can add
> >
> > #define IOCTL_COMPAT 0x1 0000 0000
>
> I had the same idea before but in addition to the problem that davem
> mentioned, this would require changing every single ioctl handler
> in the kernel and in third party drivers to use 'long' numbers.
Oops; yep, that's a showstopper.
> Not really something we want to do during the current freeze.
>
> The three options that currently make sense to me are:
>
> a) keep using register_ioctl32_conversion() for everything
I believe this makes sense: its too close to 2.6 to do anything else.
> b) add a compat_task() function that handlers may use to decide
> if they should use the compat data structures, list those ioctls
> as COMPATIBLE_IOCTL()
Unfortunately missing compat handler silently does the wrong thing in
this case.
> c) add ->compat_ioctl() to some interfaces, with HANDLE_IOCTL() as
> fallback
Seems like too much rewriting to me...
Pavel
--
When do you have a heart between your knees?
[Johanka's followup: and *two* hearts?]
On Friday 09 May 2003 01:03, Pavel Machek wrote:
> > Suggest something sane like defining a macro such as
> > "compat_task(tsk)" that can be tested by various bits of
> > code.
>
> That makes more sense. Unfortunately, that means that case "okay, it
> is compatible" can not be told from "we did not bother to check
> compat_task()". :-(. Nor do I see a transition phase.
You still need to list them as COMPATIBLE_IOCTL() or call
register_ioctl32_conversion(IOCTLNO, 0) when the ioctl has been
made compatible. Unless we are sure that every single ioctl
has been made compatible (probably never), the default must
be to call sys_ioctl from compat_sys_ioctl only if the number
is explicitly listed. This should solve both problems you
mentioned.
One minor remaining problem is that if multiple files contain
handlers for the same ioctl number, they have to be converted
at the same time because the number can not both be compatible
and incompatible at the same time.
Arnd <><