r2646 - in trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian: . patches patches/series

maximilian attems maks-guest@costa.debian.org
Tue, 08 Mar 2005 14:54:33 +0100


Author: maks-guest
Date: 2005-03-08 14:54:32 +0100 (Tue, 08 Mar 2005)
New Revision: 2646

Added:
   trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/drivers-add-scsi_changer.dpatch
Removed:
   trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/drivers-scsi_changer.dpatch
Modified:
   trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/changelog
   trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/TODO
   trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/series/2.6.11-1
Log:
add newer version of the scsi changer.


Modified: trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/changelog
===================================================================
--- trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/changelog	2005-03-08 12:59:56 UTC (rev 2645)
+++ trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/changelog	2005-03-08 13:54:32 UTC (rev 2646)
@@ -7,8 +7,10 @@
 
   * [powerpc] 750Cxe and tau calibration patch from Nicolas Det (Sven Luther)
 
-  * Fix Docbook to allow kernel-doc to build (Maximilian Attems
+  * Fix Docbook to allow kernel-doc to build (Maximilian Attems)
 
+  * Add newer scsi-changer patch (Maximilian Attems)
+
  -- Sven Luther <luther@debian.org>  Mon,  7 Mar 2005 14:56:32 +0100
 
 kernel-source-2.6.10 (2.6.10-6) UNRELEASED; urgency=low

Modified: trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/TODO
===================================================================
--- trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/TODO	2005-03-08 12:59:56 UTC (rev 2645)
+++ trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/TODO	2005-03-08 13:54:32 UTC (rev 2646)
@@ -1,6 +1,5 @@
 # + modular-ide-pnp.dpatch			# OK, needs rediff
 # + drivers-net-tg3-readd.dpatch		# OK, needs rediff
-# + drivers-scsi_changer.dpatch			# OK, needs rediff
 # + x86-i486_emu.dpatch				# OK, needs rediff
 
 # + modular-ide.dpatch 				# FAILED

Added: trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/drivers-add-scsi_changer.dpatch
===================================================================
--- trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/drivers-add-scsi_changer.dpatch	2005-03-08 12:59:56 UTC (rev 2645)
+++ trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/drivers-add-scsi_changer.dpatch	2005-03-08 13:54:32 UTC (rev 2646)
@@ -0,0 +1,1484 @@
+
+From: Gerd Knorr <kraxel@bytesex.org>
+
+This patch adds a device driver for scsi media changer devices (tape
+libraries for example).
+
+It exists for quite some time (since 2.2.x or so ...), is used by several
+people and also is in some vendor kernels (debian, suse, maybe others as
+well).  People bug be from time to time to submit it ;)
+
+Recently I went over it and did some 2.6 cleanups, module parameters and
+the like.  And now, finally, here it is.
+
+Signed-off-by: Gerd Knorr <kraxel@bytesex.org>
+Signed-off-by: Andrew Morton <akpm@osdl.org>
+---
+
+ 25-akpm/Documentation/scsi-changer.txt |  180 +++++
+ 25-akpm/drivers/scsi/Kconfig           |   18 
+ 25-akpm/drivers/scsi/Makefile          |    1 
+ 25-akpm/drivers/scsi/ch.c              | 1020 +++++++++++++++++++++++++++++++++
+ 25-akpm/include/linux/chio.h           |  168 +++++
+ 25-akpm/include/linux/major.h          |    1 
+ 25-akpm/include/scsi/scsi.h            |    3 
+ 7 files changed, 1391 insertions(+)
+
+diff -puN /dev/null Documentation/scsi-changer.txt
+--- /dev/null	Thu Apr 11 07:25:15 2002
++++ 25-akpm/Documentation/scsi-changer.txt	Thu Feb 17 16:55:13 2005
+@@ -0,0 +1,180 @@
++
++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 here:
++	http://linux.bytesex.org/misc/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).
++
++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 <moehwald@hdg.de>
++	Dane Jasper <dane@sonic.net>
++	R. Scott Bailey <sbailey@dsddi.eds.com>
++	Jonathan Corbet <corbet@atd.ucar.edu>
++
++Special thanks go to
++	Martin Kuehne <martin.kuehne@bnbt.de>
++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 <kraxel@bytesex.org>
+diff -puN /dev/null drivers/scsi/ch.c
+--- /dev/null	Thu Apr 11 07:25:15 2002
++++ 25-akpm/drivers/scsi/ch.c	Thu Feb 17 16:55:14 2005
+@@ -0,0 +1,1020 @@
++/*
++ * SCSI Media Changer device driver for Linux 2.6
++ *
++ *     (c) 1996-2003 Gerd Knorr <kraxel@bytesex.org>
++ *
++ */
++
++#define VERSION "0.24"
++
++#include <linux/config.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/blkdev.h>
++#include <linux/completion.h>
++#include <linux/devfs_fs_kernel.h>
++#include <linux/ioctl32.h>
++#include <linux/compat.h>
++#include <linux/chio.h>			/* here are all the ioctls */
++
++#include <scsi/scsi.h>
++#include <scsi/scsi_cmnd.h>
++#include <scsi/scsi_driver.h>
++#include <scsi/scsi_ioctl.h>
++#include <scsi/scsi_host.h>
++#include <scsi/scsi_device.h>
++#include <scsi/scsi_request.h>
++#include <scsi/scsi_dbg.h>
++
++#define CH_DT_MAX       16
++#define CH_TYPES        8
++
++MODULE_DESCRIPTION("device driver for scsi media changer devices");
++MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org>");
++MODULE_LICENSE("GPL");
++
++static int init = 1;
++module_param(init, int, 0444);
++MODULE_PARM_DESC(init, \
++    "initialize element status on driver load (default: on)");
++
++static int timeout_move = 300;
++module_param(timeout_move, int, 0644);
++MODULE_PARM_DESC(timeout_move,"timeout for move commands "
++		 "(default: 300 seconds)");
++
++static int timeout_init = 3600;
++module_param(timeout_init, int, 0644);
++MODULE_PARM_DESC(timeout_init,"timeout for INITIALIZE ELEMENT STATUS "
++		 "(default: 3600 seconds)");
++
++static int verbose = 1;
++module_param(verbose, int, 0644);
++MODULE_PARM_DESC(verbose,"be verbose (default: on)");
++
++static int debug = 0;
++module_param(debug, int, 0644);
++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_param_array(dt_id,  int, NULL, 0444);
++module_param_array(dt_lun, int, NULL, 0444);
++
++/* tell the driver about vendor-specific slots */
++static int vendor_firsts[CH_TYPES-4];
++static int vendor_counts[CH_TYPES-4];
++module_param_array(vendor_firsts, int, NULL, 0444);
++module_param_array(vendor_counts, int, NULL, 0444);
++
++static char *vendor_labels[CH_TYPES-4] = {
++	"v0", "v1", "v2", "v3"
++};
++// module_param_string_array(vendor_labels, NULL, 0444);
++
++#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_probe(struct device *);
++static int  ch_remove(struct 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);
++static long ch_ioctl_compat(struct file * filp,
++			    unsigned int cmd, unsigned long arg);
++
++typedef struct {
++	struct list_head    list;
++	int                 minor;
++	char                name[8];
++	struct scsi_device  *device;
++	struct 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_driver ch_template =
++{
++	.owner     	= THIS_MODULE,
++	.gendrv     	= {
++		.name	= "ch",
++		.probe  = ch_probe,
++		.remove = ch_remove,
++	},
++};
++
++static struct file_operations changer_fops =
++{
++	.owner        = THIS_MODULE,
++	.open         = ch_open,
++	.release      = ch_release,
++	.ioctl        = ch_ioctl,
++#ifdef CONFIG_COMPAT
++	.compat_ioctl = ch_ioctl_compat,
++#endif
++};
++
++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 */
++	}
++};
++
++/* ------------------------------------------------------------------- */
++
++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(struct 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);
++	struct scsi_request *sr;
++
++	sr = scsi_allocate_request(ch->device, GFP_KERNEL);
++	if (NULL == sr)
++		return -ENOMEM;
++
++ retry:
++	errno = 0;
++	if (debug) {
++		dprintk("command: ");
++		__scsi_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)
++			scsi_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 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(struct scsi_device),
++			 GFP_KERNEL);
++	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] =
++				scsi_device_lookup(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)
++{
++	scsi_changer *tmp, *ch;
++	int minor = iminor(inode);
++
++	spin_lock(&ch_devlist_lock);
++	ch = NULL;
++	list_for_each_entry(tmp,&ch_devlist,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;
++}
++
++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, &params, 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;
++		}
++
++		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;
++		}
++
++		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);
++	}
++
++	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);
++
++	}
++}
++
++#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 long ch_ioctl_compat(struct file * file,
++			    unsigned int cmd, unsigned long arg)
++{
++	scsi_changer *ch = file->private_data;
++
++	switch (cmd) {
++	case CHIOGPARAMS:
++	case CHIOGVPARAMS:
++	case CHIOPOSITION:
++	case CHIOMOVE:
++	case CHIOEXCHANGE:
++	case CHIOGELEM:
++	case CHIOINITELEM:
++	case CHIOSVOLTAG:
++		/* compatible */
++		return ch_ioctl(NULL /* inode, unused */,
++				file, cmd, arg);
++	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);
++	}
++	default:
++		// return scsi_ioctl_compat(ch->device, cmd, (void*)arg);
++		return -ENOIOCTLCMD;
++
++	}
++}
++#endif
++
++/* ------------------------------------------------------------------------ */
++
++static int ch_probe(struct device *dev)
++{
++	struct scsi_device *sd = to_scsi_device(dev);
++	scsi_changer *ch;
++
++	if (sd->type != TYPE_MEDIUM_CHANGER)
++		return -ENODEV;
++
++	ch = kmalloc(sizeof(*ch), GFP_KERNEL);
++	if (NULL == ch)
++		return -ENOMEM;
++
++	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_mk_cdev(MKDEV(SCSI_CHANGER_MAJOR,ch->minor),
++		      S_IFCHR | S_IRUGO | S_IWUGO, ch->name);
++
++	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 int ch_remove(struct device *dev)
++{
++	struct scsi_device *sd = to_scsi_device(dev);
++	scsi_changer *tmp, *ch;
++
++	spin_lock(&ch_devlist_lock);
++	ch = NULL;
++	list_for_each_entry(tmp,&ch_devlist,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 0;
++}
++
++static int __init init_ch_module(void)
++{
++	int rc;
++
++	printk(KERN_INFO "SCSI Media Changer driver v" VERSION " \n");
++	rc = register_chrdev(SCSI_CHANGER_MAJOR,"ch",&changer_fops);
++	if (rc < 0) {
++		printk("Unable to get major %d for SCSI-Changer\n",
++		       SCSI_CHANGER_MAJOR);
++		return rc;
++	}
++	rc = scsi_register_driver(&ch_template.gendrv);
++	if (rc < 0)
++		goto fail1;
++	return 0;
++
++ fail1:
++	unregister_chrdev(SCSI_CHANGER_MAJOR, "ch");
++	return rc;
++}
++
++static void __exit exit_ch_module(void)
++{
++	scsi_unregister_driver(&ch_template.gendrv);
++	unregister_chrdev(SCSI_CHANGER_MAJOR, "ch");
++}
++
++module_init(init_ch_module);
++module_exit(exit_ch_module);
++
++/*
++ * Local variables:
++ * c-basic-offset: 8
++ * End:
++ */
+diff -puN drivers/scsi/Kconfig~add-scsi-changer-driver drivers/scsi/Kconfig
+--- 25/drivers/scsi/Kconfig~add-scsi-changer-driver	Thu Feb 17 16:55:13 2005
++++ 25-akpm/drivers/scsi/Kconfig	Thu Feb 17 16:55:13 2005
+@@ -137,6 +137,24 @@ config CHR_DEV_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 -puN drivers/scsi/Makefile~add-scsi-changer-driver drivers/scsi/Makefile
+--- 25/drivers/scsi/Makefile~add-scsi-changer-driver	Thu Feb 17 16:55:13 2005
++++ 25-akpm/drivers/scsi/Makefile	Thu Feb 17 16:55:13 2005
+@@ -141,6 +141,7 @@ obj-$(CONFIG_CHR_DEV_OSST)	+= osst.o
+ 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 -puN /dev/null include/linux/chio.h
+--- /dev/null	Thu Apr 11 07:25:15 2002
++++ 25-akpm/include/linux/chio.h	Thu Feb 17 16:55:14 2005
+@@ -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 -puN include/linux/major.h~add-scsi-changer-driver include/linux/major.h
+--- 25/include/linux/major.h~add-scsi-changer-driver	Thu Feb 17 16:55:13 2005
++++ 25-akpm/include/linux/major.h	Thu Feb 17 16:55:14 2005
+@@ -101,6 +101,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 -puN include/scsi/scsi.h~add-scsi-changer-driver include/scsi/scsi.h
+--- 25/include/scsi/scsi.h~add-scsi-changer-driver	Thu Feb 17 16:55:13 2005
++++ 25-akpm/include/scsi/scsi.h	Thu Feb 17 16:55:14 2005
+@@ -41,6 +41,7 @@ extern const char *const scsi_device_typ
+ #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
+@@ -65,6 +66,7 @@ extern const char *const scsi_device_typ
+ #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
+@@ -97,6 +99,7 @@ extern const char *const scsi_device_typ
+ #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
+_

Deleted: trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/drivers-scsi_changer.dpatch
===================================================================
--- trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/drivers-scsi_changer.dpatch	2005-03-08 12:59:56 UTC (rev 2645)
+++ trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/drivers-scsi_changer.dpatch	2005-03-08 13:54:32 UTC (rev 2646)
@@ -1,1537 +0,0 @@
-#! /bin/sh -e 
-## <PATCHNAME>.dpatch by <PATCH_AUTHOR@EMAI>
-##
-## All lines beginning with `## DP:' are a description of the patch.
-## DP: Description: SCSI Media Changer - http://bytesex.org/patches/
-## DP: Patch author: unknown
-## DP: Upstream status: not submitted
-
-. $(dirname $0)/DPATCH
-
-@DPATCH@
-diff -urN kernel-source-2.6.6/Documentation/scsi-changer.txt kernel-source-2.6.6-1/Documentation/scsi-changer.txt
---- kernel-source-2.6.6/Documentation/scsi-changer.txt	1970-01-01 10:00:00.000000000 +1000
-+++ kernel-source-2.6.6-1/Documentation/scsi-changer.txt	2004-03-17 21:24:11.000000000 +1100
-@@ -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 <moehwald@hdg.de>
-+	Dane Jasper <dane@sonic.net>
-+	R. Scott Bailey <sbailey@dsddi.eds.com>
-+	Jonathan Corbet <corbet@atd.ucar.edu>
-+
-+Special thanks go to
-+	Martin Kuehne <Martin.KUEHNE@GRUNDIG.com>
-+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 <kraxel@bytesex.org>
-diff -urN kernel-source-2.6.6/drivers/scsi/Kconfig kernel-source-2.6.6-1/drivers/scsi/Kconfig
---- kernel-source-2.6.6/drivers/scsi/Kconfig	2004-05-10 19:47:58.000000000 +1000
-+++ kernel-source-2.6.6-1/drivers/scsi/Kconfig	2004-05-10 22:21:38.000000000 +1000
-@@ -137,6 +137,24 @@
- 
- 	  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 -urN kernel-source-2.6.6/drivers/scsi/Makefile kernel-source-2.6.6-1/drivers/scsi/Makefile
---- kernel-source-2.6.6/drivers/scsi/Makefile	2004-05-10 19:47:58.000000000 +1000
-+++ kernel-source-2.6.6-1/drivers/scsi/Makefile	2004-05-10 22:21:38.000000000 +1000
-@@ -132,6 +131,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 -urN kernel-source-2.6.6/drivers/scsi/ch.c kernel-source-2.6.6-1/drivers/scsi/ch.c
---- kernel-source-2.6.6/drivers/scsi/ch.c	1970-01-01 10:00:00.000000000 +1000
-+++ kernel-source-2.6.6-1/drivers/scsi/ch.c	2004-03-17 21:24:11.000000000 +1100
-@@ -0,0 +1,1085 @@
-+/*
-+ * SCSI Media Changer device driver for Linux 2.6
-+ *
-+ *     (c) 1996-2003 Gerd Knorr <kraxel@bytesex.org>
-+ *
-+ */
-+
-+#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/blkdev.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/scsi_cmnd.h>
-+#include <scsi/scsi_driver.h>
-+#include <scsi/scsi_ioctl.h>
-+
-+#include "scsi.h"
-+#include "hosts.h"
-+
-+MODULE_SUPPORTED_DEVICE("sch");
-+MODULE_DESCRIPTION("device driver for scsi media changer devices");
-+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org>");
-+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_probe(struct device *);
-+static int  ch_remove(struct 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_driver ch_template =
-+{
-+	.owner     	= THIS_MODULE,
-+	.gendrv     	= {
-+		.name	= "ch",
-+		.probe  = ch_probe,
-+		.remove = ch_remove,
-+	},
-+};
-+
-+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, GFP_ATOMIC);
-+	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 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] =
-+				scsi_device_lookup(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 = iminor(inode);
-+
-+	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 0 /* hmm, access_count is gone :-/ */
-+	if (!check_busy)
-+		return 0;
-+	if (type != CHET_DT)
-+		return 0;
-+	if (!ch->dt[unit])
-+		return 0;
-+	return atomic_read(&ch->dt[unit]->access_count);
-+#else
-+	return 0;
-+#endif
-+}
-+
-+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, &params, 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_probe(struct device *dev)
-+{
-+	struct scsi_device *sd = to_scsi_device(dev);
-+	scsi_changer *ch;
-+	
-+	if (sd->type != TYPE_MEDIUM_CHANGER)
-+		return -ENODEV;
-+    
-+	ch = kmalloc(sizeof(*ch), GFP_KERNEL);
-+	if (NULL == ch)
-+		return -ENOMEM;
-+
-+	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_mk_cdev(MKDEV(MAJOR_NR,ch->minor),
-+		      S_IFCHR | S_IRUGO | S_IWUGO, ch->name);
-+
-+	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 int ch_remove(struct device *dev)
-+{
-+	struct scsi_device *sd = to_scsi_device(dev);
-+	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 0;
-+}
-+
-+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_driver(&ch_template.gendrv);
-+	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_driver(&ch_template.gendrv);
-+	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 -urN kernel-source-2.6.6/include/linux/chio.h kernel-source-2.6.6-1/include/linux/chio.h
---- kernel-source-2.6.6/include/linux/chio.h	1970-01-01 10:00:00.000000000 +1000
-+++ kernel-source-2.6.6-1/include/linux/chio.h	2004-03-17 21:24:11.000000000 +1100
-@@ -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 -urN kernel-source-2.6.6/include/linux/major.h kernel-source-2.6.6-1/include/linux/major.h
---- kernel-source-2.6.6/include/linux/major.h	2004-04-05 19:49:42.000000000 +1000
-+++ kernel-source-2.6.6-1/include/linux/major.h	2004-04-05 20:54:51.000000000 +1000
-@@ -101,6 +101,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 -urN kernel-source-2.6.6/include/scsi/scsi.h kernel-source-2.6.6-1/include/scsi/scsi.h
---- kernel-source-2.6.6/include/scsi/scsi.h	2004-04-05 19:49:43.000000000 +1000
-+++ kernel-source-2.6.6-1/include/scsi/scsi.h	2004-04-05 20:54:52.000000000 +1000
-@@ -41,6 +41,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
-@@ -65,6 +66,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
-@@ -97,6 +99,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

Modified: trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/series/2.6.11-1
===================================================================
--- trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/series/2.6.11-1	2005-03-08 12:59:56 UTC (rev 2645)
+++ trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/series/2.6.11-1	2005-03-08 13:54:32 UTC (rev 2646)
@@ -45,4 +45,4 @@
 + docbook-allow-preprocessor-directives-between-kernel-doc-and-function.dpatch
 + docbook-fix-function-parameter-descriptin-in-fbmem.dpatch
 + docbook-move-kernel-doc-comment-next-to-function.dpatch
-
++ drivers-add-scsi_changer.dpatch