aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGerd Knorr <kraxel@bytesex.org>2005-05-10 04:59:13 -0400
committerJames Bottomley <jejb@mulgrave.(none)>2005-05-20 13:53:50 -0400
commitdaa6eda65a53e5addf86c6bc829129ff51b08bda (patch)
tree19ecc387d09110aedc9f48927b78079f3f595ace
parent5cbf5eaef7e4430f60844748fd33e22a5fb15167 (diff)
[SCSI] add scsi changer driver
This patch adds a device driver for scsi media changer devices. Signed-off-by: Gerd Knorr <kraxel@bytesex.org> Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
-rw-r--r--Documentation/scsi/scsi-changer.txt180
-rw-r--r--drivers/scsi/Kconfig18
-rw-r--r--drivers/scsi/Makefile1
-rw-r--r--drivers/scsi/ch.c1025
-rw-r--r--include/linux/chio.h168
-rw-r--r--include/linux/major.h1
-rw-r--r--include/scsi/scsi.h3
7 files changed, 1396 insertions, 0 deletions
diff --git a/Documentation/scsi/scsi-changer.txt b/Documentation/scsi/scsi-changer.txt
new file mode 100644
index 000000000000..c132687b017a
--- /dev/null
+++ b/Documentation/scsi/scsi-changer.txt
@@ -0,0 +1,180 @@
1
2README for the SCSI media changer driver
3========================================
4
5This is a driver for SCSI Medium Changer devices, which are listed
6with "Type: Medium Changer" in /proc/scsi/scsi.
7
8This is for *real* Jukeboxes. It is *not* supported to work with
9common small CD-ROM changers, neither one-lun-per-slot SCSI changers
10nor IDE drives.
11
12Userland tools available from here:
13 http://linux.bytesex.org/misc/changer.html
14
15
16General Information
17-------------------
18
19First some words about how changers work: A changer has 2 (possibly
20more) SCSI ID's. One for the changer device which controls the robot,
21and one for the device which actually reads and writes the data. The
22later may be anything, a MOD, a CD-ROM, a tape or whatever. For the
23changer device this is a "don't care", he *only* shuffles around the
24media, nothing else.
25
26
27The SCSI changer model is complex, compared to - for example - IDE-CD
28changers. But it allows to handle nearly all possible cases. It knows
294 different types of changer elements:
30
31 media transport - this one shuffles around the media, i.e. the
32 transport arm. Also known as "picker".
33 storage - a slot which can hold a media.
34 import/export - the same as above, but is accessable from outside,
35 i.e. there the operator (you !) can use this to
36 fill in and remove media from the changer.
37 Sometimes named "mailslot".
38 data transfer - this is the device which reads/writes, i.e. the
39 CD-ROM / Tape / whatever drive.
40
41None of these is limited to one: A huge Jukebox could have slots for
42123 CD-ROM's, 5 CD-ROM readers (and therefore 6 SCSI ID's: the changer
43and each CD-ROM) and 2 transport arms. No problem to handle.
44
45
46How it is implemented
47---------------------
48
49I implemented the driver as character device driver with a NetBSD-like
50ioctl interface. Just grabbed NetBSD's header file and one of the
51other linux SCSI device drivers as starting point. The interface
52should be source code compatible with NetBSD. So if there is any
53software (anybody knows ???) which supports a BSDish changer driver,
54it should work with this driver too.
55
56Over time a few more ioctls where added, volume tag support for example
57wasn't covered by the NetBSD ioctl API.
58
59
60Current State
61-------------
62
63Support for more than one transport arm is not implemented yet (and
64nobody asked for it so far...).
65
66I test and use the driver myself with a 35 slot cdrom jukebox from
67Grundig. I got some reports telling it works ok with tape autoloaders
68(Exabyte, HP and DEC). Some People use this driver with amanda. It
69works fine with small (11 slots) and a huge (4 MOs, 88 slots)
70magneto-optical Jukebox. Probably with lots of other changers too, most
71(but not all :-) people mail me only if it does *not* work...
72
73I don't have any device lists, neither black-list nor white-list. Thus
74it is quite useless to ask me whenever a specific device is supported or
75not. In theory every changer device which supports the SCSI-2 media
76changer command set should work out-of-the-box with this driver. If it
77doesn't, it is a bug. Either within the driver or within the firmware
78of the changer device.
79
80
81Using it
82--------
83
84This is a character device with major number is 86, so use
85"mknod /dev/sch0 c 86 0" to create the special file for the driver.
86
87If the module finds the changer, it prints some messages about the
88device [ try "dmesg" if you don't see anything ] and should show up in
89/proc/devices. If not.... some changers use ID ? / LUN 0 for the
90device and ID ? / LUN 1 for the robot mechanism. But Linux does *not*
91look for LUN's other than 0 as default, becauce there are to many
92broken devices. So you can try:
93
94 1) echo "scsi add-single-device 0 0 ID 1" > /proc/scsi/scsi
95 (replace ID with the SCSI-ID of the device)
96 2) boot the kernel with "max_scsi_luns=1" on the command line
97 (append="max_scsi_luns=1" in lilo.conf should do the trick)
98
99
100Trouble?
101--------
102
103If you insmod the driver with "insmod debug=1", it will be verbose and
104prints a lot of stuff to the syslog. Compiling the kernel with
105CONFIG_SCSI_CONSTANTS=y improves the quality of the error messages alot
106because the kernel will translate the error codes into human-readable
107strings then.
108
109You can display these messages with the dmesg command (or check the
110logfiles). If you email me some question becauce of a problem with the
111driver, please include these messages.
112
113
114Insmod options
115--------------
116
117debug=0/1
118 Enable debug messages (see above, default: 0).
119
120verbose=0/1
121 Be verbose (default: 1).
122
123init=0/1
124 Send INITIALIZE ELEMENT STATUS command to the changer
125 at insmod time (default: 1).
126
127timeout_init=<seconds>
128 timeout for the INITIALIZE ELEMENT STATUS command
129 (default: 3600).
130
131timeout_move=<seconds>
132 timeout for all other commands (default: 120).
133
134dt_id=<id1>,<id2>,...
135dt_lun=<lun1>,<lun2>,...
136 These two allow to specify the SCSI ID and LUN for the data
137 transfer elements. You likely don't need this as the jukebox
138 should provide this information. But some devices don't ...
139
140vendor_firsts=
141vendor_counts=
142vendor_labels=
143 These insmod options can be used to tell the driver that there
144 are some vendor-specific element types. Grundig for example
145 does this. Some jukeboxes have a printer to label fresh burned
146 CDs, which is addressed as element 0xc000 (type 5). To tell the
147 driver about this vendor-specific element, use this:
148 $ insmod ch \
149 vendor_firsts=0xc000 \
150 vendor_counts=1 \
151 vendor_labels=printer
152 All three insmod options accept up to four comma-separated
153 values, this way you can configure the element types 5-8.
154 You likely need the SCSI specs for the device in question to
155 find the correct values as they are not covered by the SCSI-2
156 standard.
157
158
159Credits
160-------
161
162I wrote this driver using the famous mailing-patches-around-the-world
163method. With (more or less) help from:
164
165 Daniel Moehwald <moehwald@hdg.de>
166 Dane Jasper <dane@sonic.net>
167 R. Scott Bailey <sbailey@dsddi.eds.com>
168 Jonathan Corbet <corbet@lwn.net>
169
170Special thanks go to
171 Martin Kuehne <martin.kuehne@bnbt.de>
172for a old, second-hand (but full functional) cdrom jukebox which I use
173to develop/test driver and tools now.
174
175Have fun,
176
177 Gerd
178
179--
180Gerd Knorr <kraxel@bytesex.org>
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 2ef5aee86b29..ba88be399a59 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -137,6 +137,24 @@ config CHR_DEV_SG
137 137
138 If unsure, say N. 138 If unsure, say N.
139 139
140config CHR_DEV_SCH
141 tristate "SCSI media changer support"
142 depends on SCSI
143 ---help---
144 This is a driver for SCSI media changers. Most common devices are
145 tape libraries and MOD/CDROM jukeboxes. *Real* jukeboxes, you
146 don't need this for those tiny 6-slot cdrom changers. Media
147 changers are listed as "Type: Medium Changer" in /proc/scsi/scsi.
148 If you have such hardware and want to use it with linux, say Y
149 here. Check <file:Documentation/scsi-changer.txt> for details.
150
151 If you want to compile this as a module ( = code which can be
152 inserted in and removed from the running kernel whenever you want),
153 say M here and read <file:Documentation/modules.txt> and
154 <file:Documentation/scsi.txt>. The module will be called ch.o.
155 If unsure, say N.
156
157
140comment "Some SCSI devices (e.g. CD jukebox) support multiple LUNs" 158comment "Some SCSI devices (e.g. CD jukebox) support multiple LUNs"
141 depends on SCSI 159 depends on SCSI
142 160
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 51d9c1e1884b..3746fb9fa2f5 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -140,6 +140,7 @@ obj-$(CONFIG_CHR_DEV_OSST) += osst.o
140obj-$(CONFIG_BLK_DEV_SD) += sd_mod.o 140obj-$(CONFIG_BLK_DEV_SD) += sd_mod.o
141obj-$(CONFIG_BLK_DEV_SR) += sr_mod.o 141obj-$(CONFIG_BLK_DEV_SR) += sr_mod.o
142obj-$(CONFIG_CHR_DEV_SG) += sg.o 142obj-$(CONFIG_CHR_DEV_SG) += sg.o
143obj-$(CONFIG_CHR_DEV_SCH) += ch.o
143 144
144scsi_mod-y += scsi.o hosts.o scsi_ioctl.o constants.o \ 145scsi_mod-y += scsi.o hosts.o scsi_ioctl.o constants.o \
145 scsicam.o scsi_error.o scsi_lib.o \ 146 scsicam.o scsi_error.o scsi_lib.o \
diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c
new file mode 100644
index 000000000000..44f5a71ec34a
--- /dev/null
+++ b/drivers/scsi/ch.c
@@ -0,0 +1,1025 @@
1/*
2 * SCSI Media Changer device driver for Linux 2.6
3 *
4 * (c) 1996-2003 Gerd Knorr <kraxel@bytesex.org>
5 *
6 */
7
8#define VERSION "0.25"
9
10#include <linux/config.h>
11#include <linux/module.h>
12#include <linux/init.h>
13#include <linux/fs.h>
14#include <linux/kernel.h>
15#include <linux/sched.h>
16#include <linux/mm.h>
17#include <linux/major.h>
18#include <linux/string.h>
19#include <linux/errno.h>
20#include <linux/interrupt.h>
21#include <linux/blkdev.h>
22#include <linux/completion.h>
23#include <linux/devfs_fs_kernel.h>
24#include <linux/ioctl32.h>
25#include <linux/compat.h>
26#include <linux/chio.h> /* here are all the ioctls */
27
28#include <scsi/scsi.h>
29#include <scsi/scsi_cmnd.h>
30#include <scsi/scsi_driver.h>
31#include <scsi/scsi_ioctl.h>
32#include <scsi/scsi_host.h>
33#include <scsi/scsi_device.h>
34#include <scsi/scsi_request.h>
35#include <scsi/scsi_dbg.h>
36
37#define CH_DT_MAX 16
38#define CH_TYPES 8
39
40MODULE_DESCRIPTION("device driver for scsi media changer devices");
41MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org>");
42MODULE_LICENSE("GPL");
43
44static int init = 1;
45module_param(init, int, 0444);
46MODULE_PARM_DESC(init, \
47 "initialize element status on driver load (default: on)");
48
49static int timeout_move = 300;
50module_param(timeout_move, int, 0644);
51MODULE_PARM_DESC(timeout_move,"timeout for move commands "
52 "(default: 300 seconds)");
53
54static int timeout_init = 3600;
55module_param(timeout_init, int, 0644);
56MODULE_PARM_DESC(timeout_init,"timeout for INITIALIZE ELEMENT STATUS "
57 "(default: 3600 seconds)");
58
59static int verbose = 1;
60module_param(verbose, int, 0644);
61MODULE_PARM_DESC(verbose,"be verbose (default: on)");
62
63static int debug = 0;
64module_param(debug, int, 0644);
65MODULE_PARM_DESC(debug,"enable/disable debug messages, also prints more "
66 "detailed sense codes on scsi errors (default: off)");
67
68static int dt_id[CH_DT_MAX] = { [ 0 ... (CH_DT_MAX-1) ] = -1 };
69static int dt_lun[CH_DT_MAX];
70module_param_array(dt_id, int, NULL, 0444);
71module_param_array(dt_lun, int, NULL, 0444);
72
73/* tell the driver about vendor-specific slots */
74static int vendor_firsts[CH_TYPES-4];
75static int vendor_counts[CH_TYPES-4];
76module_param_array(vendor_firsts, int, NULL, 0444);
77module_param_array(vendor_counts, int, NULL, 0444);
78
79static char *vendor_labels[CH_TYPES-4] = {
80 "v0", "v1", "v2", "v3"
81};
82// module_param_string_array(vendor_labels, NULL, 0444);
83
84#define dprintk(fmt, arg...) if (debug) \
85 printk(KERN_DEBUG "%s: " fmt, ch->name , ## arg)
86#define vprintk(fmt, arg...) if (verbose) \
87 printk(KERN_INFO "%s: " fmt, ch->name , ## arg)
88
89/* ------------------------------------------------------------------- */
90
91#define MAX_RETRIES 1
92
93static int ch_probe(struct device *);
94static int ch_remove(struct device *);
95static int ch_open(struct inode * inode, struct file * filp);
96static int ch_release(struct inode * inode, struct file * filp);
97static int ch_ioctl(struct inode * inode, struct file * filp,
98 unsigned int cmd, unsigned long arg);
99#ifdef CONFIG_COMPAT
100static long ch_ioctl_compat(struct file * filp,
101 unsigned int cmd, unsigned long arg);
102#endif
103
104static struct class_simple * ch_sysfs_class;
105
106typedef struct {
107 struct list_head list;
108 int minor;
109 char name[8];
110 struct scsi_device *device;
111 struct scsi_device **dt; /* ptrs to data transfer elements */
112 u_int firsts[CH_TYPES];
113 u_int counts[CH_TYPES];
114 u_int unit_attention;
115 u_int voltags;
116 struct semaphore lock;
117} scsi_changer;
118
119static LIST_HEAD(ch_devlist);
120static spinlock_t ch_devlist_lock = SPIN_LOCK_UNLOCKED;
121static int ch_devcount;
122
123static struct scsi_driver ch_template =
124{
125 .owner = THIS_MODULE,
126 .gendrv = {
127 .name = "ch",
128 .probe = ch_probe,
129 .remove = ch_remove,
130 },
131};
132
133static struct file_operations changer_fops =
134{
135 .owner = THIS_MODULE,
136 .open = ch_open,
137 .release = ch_release,
138 .ioctl = ch_ioctl,
139#ifdef CONFIG_COMPAT
140 .compat_ioctl = ch_ioctl_compat,
141#endif
142};
143
144static struct {
145 unsigned char sense;
146 unsigned char asc;
147 unsigned char ascq;
148 int errno;
149} err[] = {
150/* Just filled in what looks right. Hav'nt checked any standard paper for
151 these errno assignments, so they may be wrong... */
152 {
153 .sense = ILLEGAL_REQUEST,
154 .asc = 0x21,
155 .ascq = 0x01,
156 .errno = EBADSLT, /* Invalid element address */
157 },{
158 .sense = ILLEGAL_REQUEST,
159 .asc = 0x28,
160 .ascq = 0x01,
161 .errno = EBADE, /* Import or export element accessed */
162 },{
163 .sense = ILLEGAL_REQUEST,
164 .asc = 0x3B,
165 .ascq = 0x0D,
166 .errno = EXFULL, /* Medium destination element full */
167 },{
168 .sense = ILLEGAL_REQUEST,
169 .asc = 0x3B,
170 .ascq = 0x0E,
171 .errno = EBADE, /* Medium source element empty */
172 },{
173 .sense = ILLEGAL_REQUEST,
174 .asc = 0x20,
175 .ascq = 0x00,
176 .errno = EBADRQC, /* Invalid command operation code */
177 },{
178 /* end of list */
179 }
180};
181
182/* ------------------------------------------------------------------- */
183
184static int ch_find_errno(unsigned char *sense_buffer)
185{
186 int i,errno = 0;
187
188 /* Check to see if additional sense information is available */
189 if (sense_buffer[7] > 5 &&
190 sense_buffer[12] != 0) {
191 for (i = 0; err[i].errno != 0; i++) {
192 if (err[i].sense == sense_buffer[ 2] &&
193 err[i].asc == sense_buffer[12] &&
194 err[i].ascq == sense_buffer[13]) {
195 errno = -err[i].errno;
196 break;
197 }
198 }
199 }
200 if (errno == 0)
201 errno = -EIO;
202 return errno;
203}
204
205static int
206ch_do_scsi(scsi_changer *ch, unsigned char *cmd,
207 void *buffer, unsigned buflength,
208 enum dma_data_direction direction)
209{
210 int errno, retries = 0, timeout;
211 struct scsi_request *sr;
212
213 sr = scsi_allocate_request(ch->device, GFP_KERNEL);
214 if (NULL == sr)
215 return -ENOMEM;
216
217 timeout = (cmd[0] == INITIALIZE_ELEMENT_STATUS)
218 ? timeout_init : timeout_move;
219
220 retry:
221 errno = 0;
222 if (debug) {
223 dprintk("command: ");
224 __scsi_print_command(cmd);
225 }
226
227 scsi_wait_req(sr, cmd, buffer, buflength,
228 timeout * HZ, MAX_RETRIES);
229
230 dprintk("result: 0x%x\n",sr->sr_result);
231 if (driver_byte(sr->sr_result) & DRIVER_SENSE) {
232 if (debug)
233 scsi_print_req_sense(ch->name, sr);
234 errno = ch_find_errno(sr->sr_sense_buffer);
235
236 switch(sr->sr_sense_buffer[2] & 0xf) {
237 case UNIT_ATTENTION:
238 ch->unit_attention = 1;
239 if (retries++ < 3)
240 goto retry;
241 break;
242 }
243 }
244 scsi_release_request(sr);
245 return errno;
246}
247
248/* ------------------------------------------------------------------------ */
249
250static int
251ch_elem_to_typecode(scsi_changer *ch, u_int elem)
252{
253 int i;
254
255 for (i = 0; i < CH_TYPES; i++) {
256 if (elem >= ch->firsts[i] &&
257 elem < ch->firsts[i] +
258 ch->counts[i])
259 return i+1;
260 }
261 return 0;
262}
263
264static int
265ch_read_element_status(scsi_changer *ch, u_int elem, char *data)
266{
267 u_char cmd[12];
268 u_char *buffer;
269 int result;
270
271 buffer = kmalloc(512, GFP_KERNEL | GFP_DMA);
272 if(!buffer)
273 return -ENOMEM;
274
275 retry:
276 memset(cmd,0,sizeof(cmd));
277 cmd[0] = READ_ELEMENT_STATUS;
278 cmd[1] = (ch->device->lun << 5) |
279 (ch->voltags ? 0x10 : 0) |
280 ch_elem_to_typecode(ch,elem);
281 cmd[2] = (elem >> 8) & 0xff;
282 cmd[3] = elem & 0xff;
283 cmd[5] = 1;
284 cmd[9] = 255;
285 if (0 == (result = ch_do_scsi(ch, cmd, buffer, 256, DMA_FROM_DEVICE))) {
286 if (((buffer[16] << 8) | buffer[17]) != elem) {
287 dprintk("asked for element 0x%02x, got 0x%02x\n",
288 elem,(buffer[16] << 8) | buffer[17]);
289 kfree(buffer);
290 return -EIO;
291 }
292 memcpy(data,buffer+16,16);
293 } else {
294 if (ch->voltags) {
295 ch->voltags = 0;
296 vprintk("device has no volume tag support\n");
297 goto retry;
298 }
299 dprintk("READ ELEMENT STATUS for element 0x%x failed\n",elem);
300 }
301 kfree(buffer);
302 return result;
303}
304
305static int
306ch_init_elem(scsi_changer *ch)
307{
308 int err;
309 u_char cmd[6];
310
311 vprintk("INITIALIZE ELEMENT STATUS, may take some time ...\n");
312 memset(cmd,0,sizeof(cmd));
313 cmd[0] = INITIALIZE_ELEMENT_STATUS;
314 cmd[1] = ch->device->lun << 5;
315 err = ch_do_scsi(ch, cmd, NULL, 0, DMA_NONE);
316 vprintk("... finished\n");
317 return err;
318}
319
320static int
321ch_readconfig(scsi_changer *ch)
322{
323 u_char cmd[10], data[16];
324 u_char *buffer;
325 int result,id,lun,i;
326 u_int elem;
327
328 buffer = kmalloc(512, GFP_KERNEL | GFP_DMA);
329 if (!buffer)
330 return -ENOMEM;
331 memset(buffer,0,512);
332
333 memset(cmd,0,sizeof(cmd));
334 cmd[0] = MODE_SENSE;
335 cmd[1] = ch->device->lun << 5;
336 cmd[2] = 0x1d;
337 cmd[4] = 255;
338 result = ch_do_scsi(ch, cmd, buffer, 255, DMA_FROM_DEVICE);
339 if (0 != result) {
340 cmd[1] |= (1<<3);
341 result = ch_do_scsi(ch, cmd, buffer, 255, DMA_FROM_DEVICE);
342 }
343 if (0 == result) {
344 ch->firsts[CHET_MT] =
345 (buffer[buffer[3]+ 6] << 8) | buffer[buffer[3]+ 7];
346 ch->counts[CHET_MT] =
347 (buffer[buffer[3]+ 8] << 8) | buffer[buffer[3]+ 9];
348 ch->firsts[CHET_ST] =
349 (buffer[buffer[3]+10] << 8) | buffer[buffer[3]+11];
350 ch->counts[CHET_ST] =
351 (buffer[buffer[3]+12] << 8) | buffer[buffer[3]+13];
352 ch->firsts[CHET_IE] =
353 (buffer[buffer[3]+14] << 8) | buffer[buffer[3]+15];
354 ch->counts[CHET_IE] =
355 (buffer[buffer[3]+16] << 8) | buffer[buffer[3]+17];
356 ch->firsts[CHET_DT] =
357 (buffer[buffer[3]+18] << 8) | buffer[buffer[3]+19];
358 ch->counts[CHET_DT] =
359 (buffer[buffer[3]+20] << 8) | buffer[buffer[3]+21];
360 vprintk("type #1 (mt): 0x%x+%d [medium transport]\n",
361 ch->firsts[CHET_MT],
362 ch->counts[CHET_MT]);
363 vprintk("type #2 (st): 0x%x+%d [storage]\n",
364 ch->firsts[CHET_ST],
365 ch->counts[CHET_ST]);
366 vprintk("type #3 (ie): 0x%x+%d [import/export]\n",
367 ch->firsts[CHET_IE],
368 ch->counts[CHET_IE]);
369 vprintk("type #4 (dt): 0x%x+%d [data transfer]\n",
370 ch->firsts[CHET_DT],
371 ch->counts[CHET_DT]);
372 } else {
373 vprintk("reading element address assigment page failed!\n");
374 }
375
376 /* vendor specific element types */
377 for (i = 0; i < 4; i++) {
378 if (0 == vendor_counts[i])
379 continue;
380 if (NULL == vendor_labels[i])
381 continue;
382 ch->firsts[CHET_V1+i] = vendor_firsts[i];
383 ch->counts[CHET_V1+i] = vendor_counts[i];
384 vprintk("type #%d (v%d): 0x%x+%d [%s, vendor specific]\n",
385 i+5,i+1,vendor_firsts[i],vendor_counts[i],
386 vendor_labels[i]);
387 }
388
389 /* look up the devices of the data transfer elements */
390 ch->dt = kmalloc(ch->counts[CHET_DT]*sizeof(struct scsi_device),
391 GFP_KERNEL);
392 for (elem = 0; elem < ch->counts[CHET_DT]; elem++) {
393 id = -1;
394 lun = 0;
395 if (elem < CH_DT_MAX && -1 != dt_id[elem]) {
396 id = dt_id[elem];
397 lun = dt_lun[elem];
398 vprintk("dt 0x%x: [insmod option] ",
399 elem+ch->firsts[CHET_DT]);
400 } else if (0 != ch_read_element_status
401 (ch,elem+ch->firsts[CHET_DT],data)) {
402 vprintk("dt 0x%x: READ ELEMENT STATUS failed\n",
403 elem+ch->firsts[CHET_DT]);
404 } else {
405 vprintk("dt 0x%x: ",elem+ch->firsts[CHET_DT]);
406 if (data[6] & 0x80) {
407 if (verbose)
408 printk("not this SCSI bus\n");
409 ch->dt[elem] = NULL;
410 } else if (0 == (data[6] & 0x30)) {
411 if (verbose)
412 printk("ID/LUN unknown\n");
413 ch->dt[elem] = NULL;
414 } else {
415 id = ch->device->id;
416 lun = 0;
417 if (data[6] & 0x20) id = data[7];
418 if (data[6] & 0x10) lun = data[6] & 7;
419 }
420 }
421 if (-1 != id) {
422 if (verbose)
423 printk("ID %i, LUN %i, ",id,lun);
424 ch->dt[elem] =
425 scsi_device_lookup(ch->device->host,
426 ch->device->channel,
427 id,lun);
428 if (!ch->dt[elem]) {
429 /* should not happen */
430 if (verbose)
431 printk("Huh? device not found!\n");
432 } else {
433 if (verbose)
434 printk("name: %8.8s %16.16s %4.4s\n",
435 ch->dt[elem]->vendor,
436 ch->dt[elem]->model,
437 ch->dt[elem]->rev);
438 }
439 }
440 }
441 ch->voltags = 1;
442 kfree(buffer);
443
444 return 0;
445}
446
447/* ------------------------------------------------------------------------ */
448
449static int
450ch_position(scsi_changer *ch, u_int trans, u_int elem, int rotate)
451{
452 u_char cmd[10];
453
454 dprintk("position: 0x%x\n",elem);
455 if (0 == trans)
456 trans = ch->firsts[CHET_MT];
457 memset(cmd,0,sizeof(cmd));
458 cmd[0] = POSITION_TO_ELEMENT;
459 cmd[1] = ch->device->lun << 5;
460 cmd[2] = (trans >> 8) & 0xff;
461 cmd[3] = trans & 0xff;
462 cmd[4] = (elem >> 8) & 0xff;
463 cmd[5] = elem & 0xff;
464 cmd[8] = rotate ? 1 : 0;
465 return ch_do_scsi(ch, cmd, NULL, 0, DMA_NONE);
466}
467
468static int
469ch_move(scsi_changer *ch, u_int trans, u_int src, u_int dest, int rotate)
470{
471 u_char cmd[12];
472
473 dprintk("move: 0x%x => 0x%x\n",src,dest);
474 if (0 == trans)
475 trans = ch->firsts[CHET_MT];
476 memset(cmd,0,sizeof(cmd));
477 cmd[0] = MOVE_MEDIUM;
478 cmd[1] = ch->device->lun << 5;
479 cmd[2] = (trans >> 8) & 0xff;
480 cmd[3] = trans & 0xff;
481 cmd[4] = (src >> 8) & 0xff;
482 cmd[5] = src & 0xff;
483 cmd[6] = (dest >> 8) & 0xff;
484 cmd[7] = dest & 0xff;
485 cmd[10] = rotate ? 1 : 0;
486 return ch_do_scsi(ch, cmd, NULL,0, DMA_NONE);
487}
488
489static int
490ch_exchange(scsi_changer *ch, u_int trans, u_int src,
491 u_int dest1, u_int dest2, int rotate1, int rotate2)
492{
493 u_char cmd[12];
494
495 dprintk("exchange: 0x%x => 0x%x => 0x%x\n",
496 src,dest1,dest2);
497 if (0 == trans)
498 trans = ch->firsts[CHET_MT];
499 memset(cmd,0,sizeof(cmd));
500 cmd[0] = EXCHANGE_MEDIUM;
501 cmd[1] = ch->device->lun << 5;
502 cmd[2] = (trans >> 8) & 0xff;
503 cmd[3] = trans & 0xff;
504 cmd[4] = (src >> 8) & 0xff;
505 cmd[5] = src & 0xff;
506 cmd[6] = (dest1 >> 8) & 0xff;
507 cmd[7] = dest1 & 0xff;
508 cmd[8] = (dest2 >> 8) & 0xff;
509 cmd[9] = dest2 & 0xff;
510 cmd[10] = (rotate1 ? 1 : 0) | (rotate2 ? 2 : 0);
511
512 return ch_do_scsi(ch, cmd, NULL,0, DMA_NONE);
513}
514
515static void
516ch_check_voltag(char *tag)
517{
518 int i;
519
520 for (i = 0; i < 32; i++) {
521 /* restrict to ascii */
522 if (tag[i] >= 0x7f || tag[i] < 0x20)
523 tag[i] = ' ';
524 /* don't allow search wildcards */
525 if (tag[i] == '?' ||
526 tag[i] == '*')
527 tag[i] = ' ';
528 }
529}
530
531static int
532ch_set_voltag(scsi_changer *ch, u_int elem,
533 int alternate, int clear, u_char *tag)
534{
535 u_char cmd[12];
536 u_char *buffer;
537 int result;
538
539 buffer = kmalloc(512, GFP_KERNEL);
540 if (!buffer)
541 return -ENOMEM;
542 memset(buffer,0,512);
543
544 dprintk("%s %s voltag: 0x%x => \"%s\"\n",
545 clear ? "clear" : "set",
546 alternate ? "alternate" : "primary",
547 elem, tag);
548 memset(cmd,0,sizeof(cmd));
549 cmd[0] = SEND_VOLUME_TAG;
550 cmd[1] = (ch->device->lun << 5) |
551 ch_elem_to_typecode(ch,elem);
552 cmd[2] = (elem >> 8) & 0xff;
553 cmd[3] = elem & 0xff;
554 cmd[5] = clear
555 ? (alternate ? 0x0d : 0x0c)
556 : (alternate ? 0x0b : 0x0a);
557
558 cmd[9] = 255;
559
560 memcpy(buffer,tag,32);
561 ch_check_voltag(buffer);
562
563 result = ch_do_scsi(ch, cmd, buffer, 256, DMA_TO_DEVICE);
564 kfree(buffer);
565 return result;
566}
567
568static int ch_gstatus(scsi_changer *ch, int type, unsigned char *dest)
569{
570 int retval = 0;
571 u_char data[16];
572 unsigned int i;
573
574 down(&ch->lock);
575 for (i = 0; i < ch->counts[type]; i++) {
576 if (0 != ch_read_element_status
577 (ch, ch->firsts[type]+i,data)) {
578 retval = -EIO;
579 break;
580 }
581 put_user(data[2], dest+i);
582 if (data[2] & CESTATUS_EXCEPT)
583 vprintk("element 0x%x: asc=0x%x, ascq=0x%x\n",
584 ch->firsts[type]+i,
585 (int)data[4],(int)data[5]);
586 retval = ch_read_element_status
587 (ch, ch->firsts[type]+i,data);
588 if (0 != retval)
589 break;
590 }
591 up(&ch->lock);
592 return retval;
593}
594
595/* ------------------------------------------------------------------------ */
596
597static int
598ch_release(struct inode *inode, struct file *file)
599{
600 scsi_changer *ch = file->private_data;
601
602 scsi_device_put(ch->device);
603 file->private_data = NULL;
604 return 0;
605}
606
607static int
608ch_open(struct inode *inode, struct file *file)
609{
610 scsi_changer *tmp, *ch;
611 int minor = iminor(inode);
612
613 spin_lock(&ch_devlist_lock);
614 ch = NULL;
615 list_for_each_entry(tmp,&ch_devlist,list) {
616 if (tmp->minor == minor)
617 ch = tmp;
618 }
619 if (NULL == ch || scsi_device_get(ch->device)) {
620 spin_unlock(&ch_devlist_lock);
621 return -ENXIO;
622 }
623 spin_unlock(&ch_devlist_lock);
624
625 file->private_data = ch;
626 return 0;
627}
628
629static int
630ch_checkrange(scsi_changer *ch, unsigned int type, unsigned int unit)
631{
632 if (type >= CH_TYPES || unit >= ch->counts[type])
633 return -1;
634 return 0;
635}
636
637static int ch_ioctl(struct inode * inode, struct file * file,
638 unsigned int cmd, unsigned long arg)
639{
640 scsi_changer *ch = file->private_data;
641 int retval;
642
643 switch (cmd) {
644 case CHIOGPARAMS:
645 {
646 struct changer_params params;
647
648 params.cp_curpicker = 0;
649 params.cp_npickers = ch->counts[CHET_MT];
650 params.cp_nslots = ch->counts[CHET_ST];
651 params.cp_nportals = ch->counts[CHET_IE];
652 params.cp_ndrives = ch->counts[CHET_DT];
653
654 if (copy_to_user((void *) arg, &params, sizeof(params)))
655 return -EFAULT;
656 return 0;
657 }
658 case CHIOGVPARAMS:
659 {
660 struct changer_vendor_params vparams;
661
662 memset(&vparams,0,sizeof(vparams));
663 if (ch->counts[CHET_V1]) {
664 vparams.cvp_n1 = ch->counts[CHET_V1];
665 strncpy(vparams.cvp_label1,vendor_labels[0],16);
666 }
667 if (ch->counts[CHET_V2]) {
668 vparams.cvp_n2 = ch->counts[CHET_V2];
669 strncpy(vparams.cvp_label2,vendor_labels[1],16);
670 }
671 if (ch->counts[CHET_V3]) {
672 vparams.cvp_n3 = ch->counts[CHET_V3];
673 strncpy(vparams.cvp_label3,vendor_labels[2],16);
674 }
675 if (ch->counts[CHET_V4]) {
676 vparams.cvp_n4 = ch->counts[CHET_V4];
677 strncpy(vparams.cvp_label4,vendor_labels[3],16);
678 }
679 if (copy_to_user((void *) arg, &vparams, sizeof(vparams)))
680 return -EFAULT;
681 return 0;
682 }
683
684 case CHIOPOSITION:
685 {
686 struct changer_position pos;
687
688 if (copy_from_user(&pos, (void*)arg, sizeof (pos)))
689 return -EFAULT;
690
691 if (0 != ch_checkrange(ch, pos.cp_type, pos.cp_unit)) {
692 dprintk("CHIOPOSITION: invalid parameter\n");
693 return -EBADSLT;
694 }
695 down(&ch->lock);
696 retval = ch_position(ch,0,
697 ch->firsts[pos.cp_type] + pos.cp_unit,
698 pos.cp_flags & CP_INVERT);
699 up(&ch->lock);
700 return retval;
701 }
702
703 case CHIOMOVE:
704 {
705 struct changer_move mv;
706
707 if (copy_from_user(&mv, (void*)arg, sizeof (mv)))
708 return -EFAULT;
709
710 if (0 != ch_checkrange(ch, mv.cm_fromtype, mv.cm_fromunit) ||
711 0 != ch_checkrange(ch, mv.cm_totype, mv.cm_tounit )) {
712 dprintk("CHIOMOVE: invalid parameter\n");
713 return -EBADSLT;
714 }
715
716 down(&ch->lock);
717 retval = ch_move(ch,0,
718 ch->firsts[mv.cm_fromtype] + mv.cm_fromunit,
719 ch->firsts[mv.cm_totype] + mv.cm_tounit,
720 mv.cm_flags & CM_INVERT);
721 up(&ch->lock);
722 return retval;
723 }
724
725 case CHIOEXCHANGE:
726 {
727 struct changer_exchange mv;
728
729 if (copy_from_user(&mv, (void*)arg, sizeof (mv)))
730 return -EFAULT;
731
732 if (0 != ch_checkrange(ch, mv.ce_srctype, mv.ce_srcunit ) ||
733 0 != ch_checkrange(ch, mv.ce_fdsttype, mv.ce_fdstunit) ||
734 0 != ch_checkrange(ch, mv.ce_sdsttype, mv.ce_sdstunit)) {
735 dprintk("CHIOEXCHANGE: invalid parameter\n");
736 return -EBADSLT;
737 }
738
739 down(&ch->lock);
740 retval = ch_exchange
741 (ch,0,
742 ch->firsts[mv.ce_srctype] + mv.ce_srcunit,
743 ch->firsts[mv.ce_fdsttype] + mv.ce_fdstunit,
744 ch->firsts[mv.ce_sdsttype] + mv.ce_sdstunit,
745 mv.ce_flags & CE_INVERT1, mv.ce_flags & CE_INVERT2);
746 up(&ch->lock);
747 return retval;
748 }
749
750 case CHIOGSTATUS:
751 {
752 struct changer_element_status ces;
753
754 if (copy_from_user(&ces, (void*)arg, sizeof (ces)))
755 return -EFAULT;
756 if (ces.ces_type < 0 || ces.ces_type >= CH_TYPES)
757 return -EINVAL;
758
759 return ch_gstatus(ch, ces.ces_type, ces.ces_data);
760 }
761
762 case CHIOGELEM:
763 {
764 struct changer_get_element cge;
765 u_char cmd[12];
766 u_char *buffer;
767 unsigned int elem;
768 int result,i;
769
770 if (copy_from_user(&cge, (void*)arg, sizeof (cge)))
771 return -EFAULT;
772
773 if (0 != ch_checkrange(ch, cge.cge_type, cge.cge_unit))
774 return -EINVAL;
775 elem = ch->firsts[cge.cge_type] + cge.cge_unit;
776
777 buffer = kmalloc(512, GFP_KERNEL | GFP_DMA);
778 if (!buffer)
779 return -ENOMEM;
780 down(&ch->lock);
781
782 voltag_retry:
783 memset(cmd,0,sizeof(cmd));
784 cmd[0] = READ_ELEMENT_STATUS;
785 cmd[1] = (ch->device->lun << 5) |
786 (ch->voltags ? 0x10 : 0) |
787 ch_elem_to_typecode(ch,elem);
788 cmd[2] = (elem >> 8) & 0xff;
789 cmd[3] = elem & 0xff;
790 cmd[5] = 1;
791 cmd[9] = 255;
792
793 if (0 == (result = ch_do_scsi(ch, cmd, buffer, 256, DMA_FROM_DEVICE))) {
794 cge.cge_status = buffer[18];
795 cge.cge_flags = 0;
796 if (buffer[18] & CESTATUS_EXCEPT) {
797 cge.cge_errno = EIO;
798 }
799 if (buffer[25] & 0x80) {
800 cge.cge_flags |= CGE_SRC;
801 if (buffer[25] & 0x40)
802 cge.cge_flags |= CGE_INVERT;
803 elem = (buffer[26]<<8) | buffer[27];
804 for (i = 0; i < 4; i++) {
805 if (elem >= ch->firsts[i] &&
806 elem < ch->firsts[i] + ch->counts[i]) {
807 cge.cge_srctype = i;
808 cge.cge_srcunit = elem-ch->firsts[i];
809 }
810 }
811 }
812 if ((buffer[22] & 0x30) == 0x30) {
813 cge.cge_flags |= CGE_IDLUN;
814 cge.cge_id = buffer[23];
815 cge.cge_lun = buffer[22] & 7;
816 }
817 if (buffer[9] & 0x80) {
818 cge.cge_flags |= CGE_PVOLTAG;
819 memcpy(cge.cge_pvoltag,buffer+28,36);
820 }
821 if (buffer[9] & 0x40) {
822 cge.cge_flags |= CGE_AVOLTAG;
823 memcpy(cge.cge_avoltag,buffer+64,36);
824 }
825 } else if (ch->voltags) {
826 ch->voltags = 0;
827 vprintk("device has no volume tag support\n");
828 goto voltag_retry;
829 }
830 kfree(buffer);
831 up(&ch->lock);
832
833 if (copy_to_user((void*)arg, &cge, sizeof (cge)))
834 return -EFAULT;
835 return result;
836 }
837
838 case CHIOINITELEM:
839 {
840 down(&ch->lock);
841 retval = ch_init_elem(ch);
842 up(&ch->lock);
843 return retval;
844 }
845
846 case CHIOSVOLTAG:
847 {
848 struct changer_set_voltag csv;
849 int elem;
850
851 if (copy_from_user(&csv, (void*)arg, sizeof(csv)))
852 return -EFAULT;
853
854 if (0 != ch_checkrange(ch, csv.csv_type, csv.csv_unit)) {
855 dprintk("CHIOSVOLTAG: invalid parameter\n");
856 return -EBADSLT;
857 }
858 elem = ch->firsts[csv.csv_type] + csv.csv_unit;
859 down(&ch->lock);
860 retval = ch_set_voltag(ch, elem,
861 csv.csv_flags & CSV_AVOLTAG,
862 csv.csv_flags & CSV_CLEARTAG,
863 csv.csv_voltag);
864 up(&ch->lock);
865 return retval;
866 }
867
868 default:
869 return scsi_ioctl(ch->device, cmd, (void*)arg);
870
871 }
872}
873
874#ifdef CONFIG_COMPAT
875
876struct changer_element_status32 {
877 int ces_type;
878 compat_uptr_t ces_data;
879};
880#define CHIOGSTATUS32 _IOW('c', 8,struct changer_element_status32)
881
882static long ch_ioctl_compat(struct file * file,
883 unsigned int cmd, unsigned long arg)
884{
885 scsi_changer *ch = file->private_data;
886
887 switch (cmd) {
888 case CHIOGPARAMS:
889 case CHIOGVPARAMS:
890 case CHIOPOSITION:
891 case CHIOMOVE:
892 case CHIOEXCHANGE:
893 case CHIOGELEM:
894 case CHIOINITELEM:
895 case CHIOSVOLTAG:
896 /* compatible */
897 return ch_ioctl(NULL /* inode, unused */,
898 file, cmd, arg);
899 case CHIOGSTATUS32:
900 {
901 struct changer_element_status32 ces32;
902 unsigned char *data;
903
904 if (copy_from_user(&ces32, (void*)arg, sizeof (ces32)))
905 return -EFAULT;
906 if (ces32.ces_type < 0 || ces32.ces_type >= CH_TYPES)
907 return -EINVAL;
908
909 data = compat_ptr(ces32.ces_data);
910 return ch_gstatus(ch, ces32.ces_type, data);
911 }
912 default:
913 // return scsi_ioctl_compat(ch->device, cmd, (void*)arg);
914 return -ENOIOCTLCMD;
915
916 }
917}
918#endif
919
920/* ------------------------------------------------------------------------ */
921
922static int ch_probe(struct device *dev)
923{
924 struct scsi_device *sd = to_scsi_device(dev);
925 scsi_changer *ch;
926
927 if (sd->type != TYPE_MEDIUM_CHANGER)
928 return -ENODEV;
929
930 ch = kmalloc(sizeof(*ch), GFP_KERNEL);
931 if (NULL == ch)
932 return -ENOMEM;
933
934 memset(ch,0,sizeof(*ch));
935 ch->minor = ch_devcount;
936 sprintf(ch->name,"ch%d",ch->minor);
937 init_MUTEX(&ch->lock);
938 ch->device = sd;
939 ch_readconfig(ch);
940 if (init)
941 ch_init_elem(ch);
942
943 devfs_mk_cdev(MKDEV(SCSI_CHANGER_MAJOR,ch->minor),
944 S_IFCHR | S_IRUGO | S_IWUGO, ch->name);
945 class_simple_device_add(ch_sysfs_class,
946 MKDEV(SCSI_CHANGER_MAJOR,ch->minor),
947 dev, "s%s", ch->name);
948
949 printk(KERN_INFO "Attached scsi changer %s "
950 "at scsi%d, channel %d, id %d, lun %d\n",
951 ch->name, sd->host->host_no, sd->channel, sd->id, sd->lun);
952
953 spin_lock(&ch_devlist_lock);
954 list_add_tail(&ch->list,&ch_devlist);
955 ch_devcount++;
956 spin_unlock(&ch_devlist_lock);
957 return 0;
958}
959
960static int ch_remove(struct device *dev)
961{
962 struct scsi_device *sd = to_scsi_device(dev);
963 scsi_changer *tmp, *ch;
964
965 spin_lock(&ch_devlist_lock);
966 ch = NULL;
967 list_for_each_entry(tmp,&ch_devlist,list) {
968 if (tmp->device == sd)
969 ch = tmp;
970 }
971 BUG_ON(NULL == ch);
972 list_del(&ch->list);
973 spin_unlock(&ch_devlist_lock);
974
975 class_simple_device_remove(MKDEV(SCSI_CHANGER_MAJOR,ch->minor));
976 devfs_remove(ch->name);
977 kfree(ch->dt);
978 kfree(ch);
979 ch_devcount--;
980 return 0;
981}
982
983static int __init init_ch_module(void)
984{
985 int rc;
986
987 printk(KERN_INFO "SCSI Media Changer driver v" VERSION " \n");
988 ch_sysfs_class = class_simple_create(THIS_MODULE, "scsi_changer");
989 if (IS_ERR(ch_sysfs_class)) {
990 rc = PTR_ERR(ch_sysfs_class);
991 return rc;
992 }
993 rc = register_chrdev(SCSI_CHANGER_MAJOR,"ch",&changer_fops);
994 if (rc < 0) {
995 printk("Unable to get major %d for SCSI-Changer\n",
996 SCSI_CHANGER_MAJOR);
997 goto fail1;
998 }
999 rc = scsi_register_driver(&ch_template.gendrv);
1000 if (rc < 0)
1001 goto fail2;
1002 return 0;
1003
1004 fail2:
1005 unregister_chrdev(SCSI_CHANGER_MAJOR, "ch");
1006 fail1:
1007 class_simple_destroy(ch_sysfs_class);
1008 return rc;
1009}
1010
1011static void __exit exit_ch_module(void)
1012{
1013 scsi_unregister_driver(&ch_template.gendrv);
1014 unregister_chrdev(SCSI_CHANGER_MAJOR, "ch");
1015 class_simple_destroy(ch_sysfs_class);
1016}
1017
1018module_init(init_ch_module);
1019module_exit(exit_ch_module);
1020
1021/*
1022 * Local variables:
1023 * c-basic-offset: 8
1024 * End:
1025 */
diff --git a/include/linux/chio.h b/include/linux/chio.h
new file mode 100644
index 000000000000..63035ae67e63
--- /dev/null
+++ b/include/linux/chio.h
@@ -0,0 +1,168 @@
1/*
2 * ioctl interface for the scsi media changer driver
3 */
4
5/* changer element types */
6#define CHET_MT 0 /* media transport element (robot) */
7#define CHET_ST 1 /* storage element (media slots) */
8#define CHET_IE 2 /* import/export element */
9#define CHET_DT 3 /* data transfer element (tape/cdrom/whatever) */
10#define CHET_V1 4 /* vendor specific #1 */
11#define CHET_V2 5 /* vendor specific #2 */
12#define CHET_V3 6 /* vendor specific #3 */
13#define CHET_V4 7 /* vendor specific #4 */
14
15
16/*
17 * CHIOGPARAMS
18 * query changer properties
19 *
20 * CHIOVGPARAMS
21 * query vendor-specific element types
22 *
23 * accessing elements works by specifing type and unit of the element.
24 * for eample, storage elements are addressed with type = CHET_ST and
25 * unit = 0 .. cp_nslots-1
26 *
27 */
28struct changer_params {
29 int cp_curpicker; /* current transport element */
30 int cp_npickers; /* number of transport elements (CHET_MT) */
31 int cp_nslots; /* number of storage elements (CHET_ST) */
32 int cp_nportals; /* number of import/export elements (CHET_IE) */
33 int cp_ndrives; /* number of data transfer elements (CHET_DT) */
34};
35struct changer_vendor_params {
36 int cvp_n1; /* number of vendor specific elems (CHET_V1) */
37 char cvp_label1[16];
38 int cvp_n2; /* number of vendor specific elems (CHET_V2) */
39 char cvp_label2[16];
40 int cvp_n3; /* number of vendor specific elems (CHET_V3) */
41 char cvp_label3[16];
42 int cvp_n4; /* number of vendor specific elems (CHET_V4) */
43 char cvp_label4[16];
44 int reserved[8];
45};
46
47
48/*
49 * CHIOMOVE
50 * move a medium from one element to another
51 */
52struct changer_move {
53 int cm_fromtype; /* type/unit of source element */
54 int cm_fromunit;
55 int cm_totype; /* type/unit of destination element */
56 int cm_tounit;
57 int cm_flags;
58};
59#define CM_INVERT 1 /* flag: rotate media (for double-sided like MOD) */
60
61
62/*
63 * CHIOEXCHANGE
64 * move one medium from element #1 to element #2,
65 * and another one from element #2 to element #3.
66 * element #1 and #3 are allowed to be identical.
67 */
68struct changer_exchange {
69 int ce_srctype; /* type/unit of element #1 */
70 int ce_srcunit;
71 int ce_fdsttype; /* type/unit of element #2 */
72 int ce_fdstunit;
73 int ce_sdsttype; /* type/unit of element #3 */
74 int ce_sdstunit;
75 int ce_flags;
76};
77#define CE_INVERT1 1
78#define CE_INVERT2 2
79
80
81/*
82 * CHIOPOSITION
83 * move the transport element (robot arm) to a specific element.
84 */
85struct changer_position {
86 int cp_type;
87 int cp_unit;
88 int cp_flags;
89};
90#define CP_INVERT 1
91
92
93/*
94 * CHIOGSTATUS
95 * get element status for all elements of a specific type
96 */
97struct changer_element_status {
98 int ces_type;
99 unsigned char *ces_data;
100};
101#define CESTATUS_FULL 0x01 /* full */
102#define CESTATUS_IMPEXP 0x02 /* media was imported (inserted by sysop) */
103#define CESTATUS_EXCEPT 0x04 /* error condition */
104#define CESTATUS_ACCESS 0x08 /* access allowed */
105#define CESTATUS_EXENAB 0x10 /* element can export media */
106#define CESTATUS_INENAB 0x20 /* element can import media */
107
108
109/*
110 * CHIOGELEM
111 * get more detailed status informtion for a single element
112 */
113struct changer_get_element {
114 int cge_type; /* type/unit */
115 int cge_unit;
116 int cge_status; /* status */
117 int cge_errno; /* errno */
118 int cge_srctype; /* source element of the last move/exchange */
119 int cge_srcunit;
120 int cge_id; /* scsi id (for data transfer elements) */
121 int cge_lun; /* scsi lun (for data transfer elements) */
122 char cge_pvoltag[36]; /* primary volume tag */
123 char cge_avoltag[36]; /* alternate volume tag */
124 int cge_flags;
125};
126/* flags */
127#define CGE_ERRNO 0x01 /* errno available */
128#define CGE_INVERT 0x02 /* media inverted */
129#define CGE_SRC 0x04 /* media src available */
130#define CGE_IDLUN 0x08 /* ID+LUN available */
131#define CGE_PVOLTAG 0x10 /* primary volume tag available */
132#define CGE_AVOLTAG 0x20 /* alternate volume tag available */
133
134
135/*
136 * CHIOSVOLTAG
137 * set volume tag
138 */
139struct changer_set_voltag {
140 int csv_type; /* type/unit */
141 int csv_unit;
142 char csv_voltag[36]; /* volume tag */
143 int csv_flags;
144};
145#define CSV_PVOLTAG 0x01 /* primary volume tag */
146#define CSV_AVOLTAG 0x02 /* alternate volume tag */
147#define CSV_CLEARTAG 0x04 /* clear volume tag */
148
149/* ioctls */
150#define CHIOMOVE _IOW('c', 1,struct changer_move)
151#define CHIOEXCHANGE _IOW('c', 2,struct changer_exchange)
152#define CHIOPOSITION _IOW('c', 3,struct changer_position)
153#define CHIOGPICKER _IOR('c', 4,int) /* not impl. */
154#define CHIOSPICKER _IOW('c', 5,int) /* not impl. */
155#define CHIOGPARAMS _IOR('c', 6,struct changer_params)
156#define CHIOGSTATUS _IOW('c', 8,struct changer_element_status)
157#define CHIOGELEM _IOW('c',16,struct changer_get_element)
158#define CHIOINITELEM _IO('c',17)
159#define CHIOSVOLTAG _IOW('c',18,struct changer_set_voltag)
160#define CHIOGVPARAMS _IOR('c',19,struct changer_vendor_params)
161
162/* ---------------------------------------------------------------------- */
163
164/*
165 * Local variables:
166 * c-basic-offset: 8
167 * End:
168 */
diff --git a/include/linux/major.h b/include/linux/major.h
index 4b62c42b842c..e36a46702d94 100644
--- a/include/linux/major.h
+++ b/include/linux/major.h
@@ -100,6 +100,7 @@
100#define I2O_MAJOR 80 /* 80->87 */ 100#define I2O_MAJOR 80 /* 80->87 */
101 101
102#define SHMIQ_MAJOR 85 /* Linux/mips, SGI /dev/shmiq */ 102#define SHMIQ_MAJOR 85 /* Linux/mips, SGI /dev/shmiq */
103#define SCSI_CHANGER_MAJOR 86
103 104
104#define IDE6_MAJOR 88 105#define IDE6_MAJOR 88
105#define IDE7_MAJOR 89 106#define IDE7_MAJOR 89
diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h
index 659ecf48fb4a..ca1e3b4a3183 100644
--- a/include/scsi/scsi.h
+++ b/include/scsi/scsi.h
@@ -41,6 +41,7 @@ extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE];
41#define FORMAT_UNIT 0x04 41#define FORMAT_UNIT 0x04
42#define READ_BLOCK_LIMITS 0x05 42#define READ_BLOCK_LIMITS 0x05
43#define REASSIGN_BLOCKS 0x07 43#define REASSIGN_BLOCKS 0x07
44#define INITIALIZE_ELEMENT_STATUS 0x07
44#define READ_6 0x08 45#define READ_6 0x08
45#define WRITE_6 0x0a 46#define WRITE_6 0x0a
46#define SEEK_6 0x0b 47#define SEEK_6 0x0b
@@ -65,6 +66,7 @@ extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE];
65#define READ_10 0x28 66#define READ_10 0x28
66#define WRITE_10 0x2a 67#define WRITE_10 0x2a
67#define SEEK_10 0x2b 68#define SEEK_10 0x2b
69#define POSITION_TO_ELEMENT 0x2b
68#define WRITE_VERIFY 0x2e 70#define WRITE_VERIFY 0x2e
69#define VERIFY 0x2f 71#define VERIFY 0x2f
70#define SEARCH_HIGH 0x30 72#define SEARCH_HIGH 0x30
@@ -97,6 +99,7 @@ extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE];
97#define PERSISTENT_RESERVE_OUT 0x5f 99#define PERSISTENT_RESERVE_OUT 0x5f
98#define REPORT_LUNS 0xa0 100#define REPORT_LUNS 0xa0
99#define MOVE_MEDIUM 0xa5 101#define MOVE_MEDIUM 0xa5
102#define EXCHANGE_MEDIUM 0xa6
100#define READ_12 0xa8 103#define READ_12 0xa8
101#define WRITE_12 0xaa 104#define WRITE_12 0xaa
102#define WRITE_VERIFY_12 0xae 105#define WRITE_VERIFY_12 0xae