aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi
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 /drivers/scsi
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>
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/Kconfig18
-rw-r--r--drivers/scsi/Makefile1
-rw-r--r--drivers/scsi/ch.c1025
3 files changed, 1044 insertions, 0 deletions
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 */