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 + -
显示快捷键?