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