ch.c
来自「linux 内核源代码」· C语言 代码 · 共 1,014 行 · 第 1/2 页
C
1,014 行
/* * SCSI Media Changer device driver for Linux 2.6 * * (c) 1996-2003 Gerd Knorr <kraxel@bytesex.org> * */#define VERSION "0.25"#include <linux/module.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/kernel.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/compat.h>#include <linux/chio.h> /* here are all the ioctls */#include <linux/mutex.h>#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_eh.h>#include <scsi/scsi_dbg.h>#define CH_DT_MAX 16#define CH_TYPES 8MODULE_DESCRIPTION("device driver for scsi media changer devices");MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org>");MODULE_LICENSE("GPL");MODULE_ALIAS_CHARDEV_MAJOR(SCSI_CHANGER_MAJOR);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 const 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 1static 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);#ifdef CONFIG_COMPATstatic long ch_ioctl_compat(struct file * filp, unsigned int cmd, unsigned long arg);#endifstatic struct class * ch_sysfs_class;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 mutex lock;} scsi_changer;static LIST_HEAD(ch_devlist);static DEFINE_SPINLOCK(ch_devlist_lock);static int ch_devcount;static struct scsi_driver ch_template ={ .owner = THIS_MODULE, .gendrv = { .name = "ch", .probe = ch_probe, .remove = ch_remove, },};static const 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 const 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(struct scsi_sense_hdr *sshdr){ int i,errno = 0; /* Check to see if additional sense information is available */ if (scsi_sense_valid(sshdr) && sshdr->asc != 0) { for (i = 0; err[i].errno != 0; i++) { if (err[i].sense == sshdr->sense_key && err[i].asc == sshdr->asc && err[i].ascq == sshdr->ascq) { errno = -err[i].errno; break; } } } if (errno == 0) errno = -EIO; return errno;}static intch_do_scsi(scsi_changer *ch, unsigned char *cmd, void *buffer, unsigned buflength, enum dma_data_direction direction){ int errno, retries = 0, timeout, result; struct scsi_sense_hdr sshdr; timeout = (cmd[0] == INITIALIZE_ELEMENT_STATUS) ? timeout_init : timeout_move; retry: errno = 0; if (debug) { dprintk("command: "); __scsi_print_command(cmd); } result = scsi_execute_req(ch->device, cmd, direction, buffer, buflength, &sshdr, timeout * HZ, MAX_RETRIES); dprintk("result: 0x%x\n",result); if (driver_byte(result) & DRIVER_SENSE) { if (debug) scsi_print_sense_hdr(ch->name, &sshdr); errno = ch_find_errno(&sshdr); switch(sshdr.sense_key) { case UNIT_ATTENTION: ch->unit_attention = 1; if (retries++ < 3) goto retry; break; } } return errno;}/* ------------------------------------------------------------------------ */static intch_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 intch_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 | GFP_DMA); 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, DMA_FROM_DEVICE))) { 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\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 ...\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, DMA_NONE); vprintk("... finished\n"); return err;}static intch_readconfig(scsi_changer *ch){ u_char cmd[10], data[16]; u_char *buffer; int result,id,lun,i; u_int elem; buffer = kzalloc(512, GFP_KERNEL | GFP_DMA); if (!buffer) return -ENOMEM; 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, DMA_FROM_DEVICE); if (0 != result) { cmd[1] |= (1<<3); result = ch_do_scsi(ch, cmd, buffer, 255, DMA_FROM_DEVICE); } 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!\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 (elem < CH_DT_MAX && -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 intch_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, DMA_NONE);}static intch_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, DMA_NONE);}static intch_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, DMA_NONE);}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?