ch.c

来自「linux 内核源代码」· C语言 代码 · 共 1,014 行 · 第 1/2 页

C
1,014
字号
static voidch_check_voltag(char *tag){	int i;	for (i = 0; i < 32; i++) {		/* restrict to ascii */		if (tag[i] >= 0x7f || tag[i] < 0x20)			tag[i] = ' ';		/* don't allow search wildcards */		if (tag[i] == '?' ||		    tag[i] == '*')			tag[i] = ' ';	}}static intch_set_voltag(scsi_changer *ch, u_int elem,	      int alternate, int clear, u_char *tag){	u_char  cmd[12];	u_char  *buffer;	int result;	buffer = kzalloc(512, GFP_KERNEL);	if (!buffer)		return -ENOMEM;	dprintk("%s %s voltag: 0x%x => \"%s\"\n",		clear     ? "clear"     : "set",		alternate ? "alternate" : "primary",		elem, tag);	memset(cmd,0,sizeof(cmd));	cmd[0]  = SEND_VOLUME_TAG;	cmd[1] = (ch->device->lun << 5) | 		ch_elem_to_typecode(ch,elem);	cmd[2] = (elem >> 8) & 0xff;	cmd[3] = elem        & 0xff;	cmd[5] = clear		? (alternate ? 0x0d : 0x0c)		: (alternate ? 0x0b : 0x0a);		cmd[9] = 255;	memcpy(buffer,tag,32);	ch_check_voltag(buffer);	result = ch_do_scsi(ch, cmd, buffer, 256, DMA_TO_DEVICE);	kfree(buffer);	return result;}static int ch_gstatus(scsi_changer *ch, int type, unsigned char __user *dest){	int retval = 0;	u_char data[16];	unsigned int i;		mutex_lock(&ch->lock);	for (i = 0; i < ch->counts[type]; i++) {		if (0 != ch_read_element_status		    (ch, ch->firsts[type]+i,data)) {			retval = -EIO;			break;		}		put_user(data[2], dest+i);		if (data[2] & CESTATUS_EXCEPT)			vprintk("element 0x%x: asc=0x%x, ascq=0x%x\n",				ch->firsts[type]+i,				(int)data[4],(int)data[5]);		retval = ch_read_element_status			(ch, ch->firsts[type]+i,data);		if (0 != retval)			break;	}	mutex_unlock(&ch->lock);	return retval;}/* ------------------------------------------------------------------------ */static intch_release(struct inode *inode, struct file *file){	scsi_changer *ch = file->private_data;	scsi_device_put(ch->device);	file->private_data = NULL;	return 0;}static intch_open(struct inode *inode, struct file *file){	scsi_changer *tmp, *ch;	int minor = iminor(inode);	spin_lock(&ch_devlist_lock);	ch = NULL;	list_for_each_entry(tmp,&ch_devlist,list) {		if (tmp->minor == minor)			ch = tmp;	}	if (NULL == ch || scsi_device_get(ch->device)) {		spin_unlock(&ch_devlist_lock);		return -ENXIO;	}	spin_unlock(&ch_devlist_lock);	file->private_data = ch;	return 0;}static intch_checkrange(scsi_changer *ch, unsigned int type, unsigned int unit){	if (type >= CH_TYPES  ||  unit >= ch->counts[type])		return -1;	return 0;}static int ch_ioctl(struct inode * inode, struct file * file,		    unsigned int cmd, unsigned long arg){	scsi_changer *ch = file->private_data;	int retval;	void __user *argp = (void __user *)arg;		switch (cmd) {	case CHIOGPARAMS:	{		struct changer_params params;				params.cp_curpicker = 0;		params.cp_npickers  = ch->counts[CHET_MT];		params.cp_nslots    = ch->counts[CHET_ST];		params.cp_nportals  = ch->counts[CHET_IE];		params.cp_ndrives   = ch->counts[CHET_DT];				if (copy_to_user(argp, &params, sizeof(params)))			return -EFAULT;		return 0;	}	case CHIOGVPARAMS:	{		struct changer_vendor_params vparams;		memset(&vparams,0,sizeof(vparams));		if (ch->counts[CHET_V1]) {			vparams.cvp_n1  = ch->counts[CHET_V1];			strncpy(vparams.cvp_label1,vendor_labels[0],16);		}		if (ch->counts[CHET_V2]) {			vparams.cvp_n2  = ch->counts[CHET_V2];			strncpy(vparams.cvp_label2,vendor_labels[1],16);		}		if (ch->counts[CHET_V3]) {			vparams.cvp_n3  = ch->counts[CHET_V3];			strncpy(vparams.cvp_label3,vendor_labels[2],16);		}		if (ch->counts[CHET_V4]) {			vparams.cvp_n4  = ch->counts[CHET_V4];			strncpy(vparams.cvp_label4,vendor_labels[3],16);		}		if (copy_to_user(argp, &vparams, sizeof(vparams)))			return -EFAULT;		return 0;	}		case CHIOPOSITION:	{		struct changer_position pos;				if (copy_from_user(&pos, argp, sizeof (pos)))			return -EFAULT;		if (0 != ch_checkrange(ch, pos.cp_type, pos.cp_unit)) {			dprintk("CHIOPOSITION: invalid parameter\n");			return -EBADSLT;		}		mutex_lock(&ch->lock);		retval = ch_position(ch,0,				     ch->firsts[pos.cp_type] + pos.cp_unit,				     pos.cp_flags & CP_INVERT);		mutex_unlock(&ch->lock);		return retval;	}		case CHIOMOVE:	{		struct changer_move mv;		if (copy_from_user(&mv, argp, sizeof (mv)))			return -EFAULT;		if (0 != ch_checkrange(ch, mv.cm_fromtype, mv.cm_fromunit) ||		    0 != ch_checkrange(ch, mv.cm_totype,   mv.cm_tounit  )) {			dprintk("CHIOMOVE: invalid parameter\n");			return -EBADSLT;		}				mutex_lock(&ch->lock);		retval = ch_move(ch,0,				 ch->firsts[mv.cm_fromtype] + mv.cm_fromunit,				 ch->firsts[mv.cm_totype]   + mv.cm_tounit,				 mv.cm_flags & CM_INVERT);		mutex_unlock(&ch->lock);		return retval;	}	case CHIOEXCHANGE:	{		struct changer_exchange mv;				if (copy_from_user(&mv, argp, sizeof (mv)))			return -EFAULT;		if (0 != ch_checkrange(ch, mv.ce_srctype,  mv.ce_srcunit ) ||		    0 != ch_checkrange(ch, mv.ce_fdsttype, mv.ce_fdstunit) ||		    0 != ch_checkrange(ch, mv.ce_sdsttype, mv.ce_sdstunit)) {			dprintk("CHIOEXCHANGE: invalid parameter\n");			return -EBADSLT;		}				mutex_lock(&ch->lock);		retval = ch_exchange			(ch,0,			 ch->firsts[mv.ce_srctype]  + mv.ce_srcunit,			 ch->firsts[mv.ce_fdsttype] + mv.ce_fdstunit,			 ch->firsts[mv.ce_sdsttype] + mv.ce_sdstunit,			 mv.ce_flags & CE_INVERT1, mv.ce_flags & CE_INVERT2);		mutex_unlock(&ch->lock);		return retval;	}	case CHIOGSTATUS:	{		struct changer_element_status ces;				if (copy_from_user(&ces, argp, sizeof (ces)))			return -EFAULT;		if (ces.ces_type < 0 || ces.ces_type >= CH_TYPES)			return -EINVAL;		return ch_gstatus(ch, ces.ces_type, ces.ces_data);	}	case CHIOGELEM:	{		struct changer_get_element cge;		u_char  cmd[12];		u_char  *buffer;		unsigned int elem;		int     result,i;				if (copy_from_user(&cge, argp, sizeof (cge)))			return -EFAULT;		if (0 != ch_checkrange(ch, cge.cge_type, cge.cge_unit))			return -EINVAL;		elem = ch->firsts[cge.cge_type] + cge.cge_unit;				buffer = kmalloc(512, GFP_KERNEL | GFP_DMA);		if (!buffer)			return -ENOMEM;		mutex_lock(&ch->lock);			voltag_retry:		memset(cmd,0,sizeof(cmd));		cmd[0] = READ_ELEMENT_STATUS;		cmd[1] = (ch->device->lun << 5) |			(ch->voltags ? 0x10 : 0) |			ch_elem_to_typecode(ch,elem);		cmd[2] = (elem >> 8) & 0xff;		cmd[3] = elem        & 0xff;		cmd[5] = 1;		cmd[9] = 255;				if (0 == (result = ch_do_scsi(ch, cmd, buffer, 256, DMA_FROM_DEVICE))) {			cge.cge_status = buffer[18];			cge.cge_flags = 0;			if (buffer[18] & CESTATUS_EXCEPT) {				cge.cge_errno = EIO;			}			if (buffer[25] & 0x80) {				cge.cge_flags |= CGE_SRC;				if (buffer[25] & 0x40)					cge.cge_flags |= CGE_INVERT;				elem = (buffer[26]<<8) | buffer[27];				for (i = 0; i < 4; i++) {					if (elem >= ch->firsts[i] &&					    elem <  ch->firsts[i] + ch->counts[i]) {						cge.cge_srctype = i;						cge.cge_srcunit = elem-ch->firsts[i];					}				}			}			if ((buffer[22] & 0x30) == 0x30) {				cge.cge_flags |= CGE_IDLUN;				cge.cge_id  = buffer[23];				cge.cge_lun = buffer[22] & 7;			}			if (buffer[9] & 0x80) {				cge.cge_flags |= CGE_PVOLTAG;				memcpy(cge.cge_pvoltag,buffer+28,36);			}			if (buffer[9] & 0x40) {				cge.cge_flags |= CGE_AVOLTAG;				memcpy(cge.cge_avoltag,buffer+64,36);			}		} else if (ch->voltags) {			ch->voltags = 0;			vprintk("device has no volume tag support\n");			goto voltag_retry;		}		kfree(buffer);		mutex_unlock(&ch->lock);				if (copy_to_user(argp, &cge, sizeof (cge)))			return -EFAULT;		return result;	}	case CHIOINITELEM:	{		mutex_lock(&ch->lock);		retval = ch_init_elem(ch);		mutex_unlock(&ch->lock);		return retval;	}			case CHIOSVOLTAG:	{		struct changer_set_voltag csv;		int elem;		if (copy_from_user(&csv, argp, sizeof(csv)))			return -EFAULT;		if (0 != ch_checkrange(ch, csv.csv_type, csv.csv_unit)) {			dprintk("CHIOSVOLTAG: invalid parameter\n");			return -EBADSLT;		}		elem = ch->firsts[csv.csv_type] + csv.csv_unit;		mutex_lock(&ch->lock);		retval = ch_set_voltag(ch, elem,				       csv.csv_flags & CSV_AVOLTAG,				       csv.csv_flags & CSV_CLEARTAG,				       csv.csv_voltag);		mutex_unlock(&ch->lock);		return retval;	}	default:		return scsi_ioctl(ch->device, cmd, argp);	}}#ifdef CONFIG_COMPATstruct changer_element_status32 {	int		ces_type;	compat_uptr_t	ces_data;};#define CHIOGSTATUS32  _IOW('c', 8,struct changer_element_status32)static long ch_ioctl_compat(struct file * file,			    unsigned int cmd, unsigned long arg){	scsi_changer *ch = file->private_data;		switch (cmd) {	case CHIOGPARAMS:	case CHIOGVPARAMS:	case CHIOPOSITION:	case CHIOMOVE:	case CHIOEXCHANGE:	case CHIOGELEM:	case CHIOINITELEM:	case CHIOSVOLTAG:		/* compatible */		return ch_ioctl(NULL /* inode, unused */,				file, cmd, arg);	case CHIOGSTATUS32:	{		struct changer_element_status32 ces32;		unsigned char __user *data;				if (copy_from_user(&ces32, (void __user *)arg, sizeof (ces32)))			return -EFAULT;		if (ces32.ces_type < 0 || ces32.ces_type >= CH_TYPES)			return -EINVAL;		data = compat_ptr(ces32.ces_data);		return ch_gstatus(ch, ces32.ces_type, data);	}	default:		// return scsi_ioctl_compat(ch->device, cmd, (void*)arg);		return -ENOIOCTLCMD;	}}#endif/* ------------------------------------------------------------------------ */static int ch_probe(struct device *dev){	struct scsi_device *sd = to_scsi_device(dev);	scsi_changer *ch;		if (sd->type != TYPE_MEDIUM_CHANGER)		return -ENODEV;    	ch = kzalloc(sizeof(*ch), GFP_KERNEL);	if (NULL == ch)		return -ENOMEM;	ch->minor = ch_devcount;	sprintf(ch->name,"ch%d",ch->minor);	mutex_init(&ch->lock);	ch->device = sd;	ch_readconfig(ch);	if (init)		ch_init_elem(ch);	class_device_create(ch_sysfs_class, NULL,			    MKDEV(SCSI_CHANGER_MAJOR,ch->minor),			    dev, "s%s", ch->name);	sdev_printk(KERN_INFO, sd, "Attached scsi changer %s\n", ch->name);		spin_lock(&ch_devlist_lock);	list_add_tail(&ch->list,&ch_devlist);	ch_devcount++;	spin_unlock(&ch_devlist_lock);	return 0;}static int ch_remove(struct device *dev){	struct scsi_device *sd = to_scsi_device(dev);	scsi_changer *tmp, *ch;	spin_lock(&ch_devlist_lock);	ch = NULL;	list_for_each_entry(tmp,&ch_devlist,list) {		if (tmp->device == sd)			ch = tmp;	}	BUG_ON(NULL == ch);	list_del(&ch->list);	spin_unlock(&ch_devlist_lock);	class_device_destroy(ch_sysfs_class,			     MKDEV(SCSI_CHANGER_MAJOR,ch->minor));	kfree(ch->dt);	kfree(ch);	ch_devcount--;	return 0;}static int __init init_ch_module(void){	int rc;		printk(KERN_INFO "SCSI Media Changer driver v" VERSION " \n");        ch_sysfs_class = class_create(THIS_MODULE, "scsi_changer");        if (IS_ERR(ch_sysfs_class)) {		rc = PTR_ERR(ch_sysfs_class);		return rc;        }	rc = register_chrdev(SCSI_CHANGER_MAJOR,"ch",&changer_fops);	if (rc < 0) {		printk("Unable to get major %d for SCSI-Changer\n",		       SCSI_CHANGER_MAJOR);		goto fail1;	}	rc = scsi_register_driver(&ch_template.gendrv);	if (rc < 0)		goto fail2;	return 0; fail2:	unregister_chrdev(SCSI_CHANGER_MAJOR, "ch"); fail1:	class_destroy(ch_sysfs_class);	return rc;}static void __exit exit_ch_module(void) {	scsi_unregister_driver(&ch_template.gendrv);	unregister_chrdev(SCSI_CHANGER_MAJOR, "ch");	class_destroy(ch_sysfs_class);}module_init(init_ch_module);module_exit(exit_ch_module);/* * Local variables: * c-basic-offset: 8 * End: */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?