scsi_debug.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,869 行 · 第 1/4 页

C
1,869
字号
		break;	case MODE_SENSE:	case MODE_SENSE_10:		errsts = resp_mode_sense(cmd, target, buff, bufflen, devip);		break;	case SYNCHRONIZE_CACHE:		memset(buff, 0, bufflen);		break;	default:		if ((errsts = check_reset(SCpnt, devip)))			break;		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_OPCODE, 0, 18);		errsts = check_condition_result;		break;	}	return schedule_resp(SCpnt, devip, done, errsts, scsi_debug_delay);}static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg){	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {		printk(KERN_INFO "scsi_debug: ioctl: cmd=0x%x\n", cmd);	}	return -EINVAL;	/* return -ENOTTY; // correct return but upsets fdisk */}static int check_reset(struct scsi_cmnd * SCpnt, struct sdebug_dev_info * devip){	if (devip->reset) {		devip->reset = 0;		mk_sense_buffer(devip, UNIT_ATTENTION, POWERON_RESET, 0, 18);		return check_condition_result;	}	return 0;}#define SDEBUG_LONG_INQ_SZ 58#define SDEBUG_MAX_INQ_ARR_SZ 128static const char * vendor_id = "Linux   ";static const char * product_id = "scsi_debug      ";static const char * product_rev = "0004";static int inquiry_evpd_83(unsigned char * arr, int dev_id_num,			   const char * dev_id_str, int dev_id_str_len){	int num;	/* Two identification descriptors: */	/* T10 vendor identifier field format (faked) */	arr[0] = 0x2;	/* ASCII */	arr[1] = 0x1;	arr[2] = 0x0;	memcpy(&arr[4], vendor_id, 8);	memcpy(&arr[12], product_id, 16);	memcpy(&arr[28], dev_id_str, dev_id_str_len);	num = 8 + 16 + dev_id_str_len;	arr[3] = num;	num += 4;	/* NAA IEEE registered identifier (faked) */	arr[num] = 0x1;	/* binary */	arr[num + 1] = 0x3;	arr[num + 2] = 0x0;	arr[num + 3] = 0x8;	arr[num + 4] = 0x51;	/* ieee company id=0x123456 (faked) */	arr[num + 5] = 0x23;	arr[num + 6] = 0x45;	arr[num + 7] = 0x60;	arr[num + 8] = (dev_id_num >> 24);	arr[num + 9] = (dev_id_num >> 16) & 0xff;	arr[num + 10] = (dev_id_num >> 8) & 0xff;	arr[num + 11] = dev_id_num & 0xff;	return num + 12;}static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff,			int bufflen, struct sdebug_dev_info * devip){	unsigned char pq_pdt;	unsigned char arr[SDEBUG_MAX_INQ_ARR_SZ];	int min_len = bufflen > SDEBUG_MAX_INQ_ARR_SZ ?			SDEBUG_MAX_INQ_ARR_SZ : bufflen;	if (bufflen < cmd[4])		printk(KERN_INFO "scsi_debug: inquiry: bufflen=%d "		       "< alloc_length=%d\n", bufflen, (int)cmd[4]);	memset(buff, 0, bufflen);	memset(arr, 0, SDEBUG_MAX_INQ_ARR_SZ);	pq_pdt = (scsi_debug_ptype & 0x1f);	arr[0] = pq_pdt;	if (0x2 & cmd[1]) {  /* CMDDT bit set */		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,			       	0, 18);		return check_condition_result;	} else if (0x1 & cmd[1]) {  /* EVPD bit set */		int dev_id_num, len;		char dev_id_str[6];				dev_id_num = ((devip->sdbg_host->shost->host_no + 1) * 2000) +			     (devip->target * 1000) + devip->lun;		len = scnprintf(dev_id_str, 6, "%d", dev_id_num);		if (0 == cmd[2]) { /* supported vital product data pages */			arr[3] = 3;			arr[4] = 0x0; /* this page */			arr[5] = 0x80; /* unit serial number */			arr[6] = 0x83; /* device identification */		} else if (0x80 == cmd[2]) { /* unit serial number */			arr[1] = 0x80;			arr[3] = len;			memcpy(&arr[4], dev_id_str, len);		} else if (0x83 == cmd[2]) { /* device identification */			arr[1] = 0x83;			arr[3] = inquiry_evpd_83(&arr[4], dev_id_num,						 dev_id_str, len);		} else {			/* Illegal request, invalid field in cdb */			mk_sense_buffer(devip, ILLEGAL_REQUEST,					INVALID_FIELD_IN_CDB, 0, 18);			return check_condition_result;		}		memcpy(buff, arr, min_len);		return 0;	}	/* drops through here for a standard inquiry */	arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0;	/* Removable disk */	arr[2] = scsi_debug_scsi_level;	arr[4] = SDEBUG_LONG_INQ_SZ - 5;	arr[7] = 0x3a; /* claim: WBUS16, SYNC, LINKED + CMDQUE */	memcpy(&arr[8], vendor_id, 8);	memcpy(&arr[16], product_id, 16);	memcpy(&arr[32], product_rev, 4);	memcpy(buff, arr, min_len);	return 0;}/* <<Following mode page info copied from ST318451LW>> */static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target){	/* Read-Write Error Recovery page for mode_sense */	unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0,					5, 0, 0xff, 0xff};	memcpy(p, err_recov_pg, sizeof(err_recov_pg));	if (1 == pcontrol)		memset(p + 2, 0, sizeof(err_recov_pg) - 2);	return sizeof(err_recov_pg);}static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target){ 	/* Disconnect-Reconnect page for mode_sense */	unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0,					 0, 0, 0, 0, 0, 0, 0, 0};	memcpy(p, disconnect_pg, sizeof(disconnect_pg));	if (1 == pcontrol)		memset(p + 2, 0, sizeof(disconnect_pg) - 2);	return sizeof(disconnect_pg);}static int resp_format_pg(unsigned char * p, int pcontrol, int target){       /* Format device page for mode_sense */        unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,                                     0, 0, 0, 0, 0, 0, 0, 0,                                     0, 0, 0, 0, 0x40, 0, 0, 0};        memcpy(p, format_pg, sizeof(format_pg));        p[10] = (sdebug_sectors_per >> 8) & 0xff;        p[11] = sdebug_sectors_per & 0xff;        p[12] = (SECT_SIZE >> 8) & 0xff;        p[13] = SECT_SIZE & 0xff;        if (DEV_REMOVEABLE(target))                p[20] |= 0x20; /* should agree with INQUIRY */        if (1 == pcontrol)                memset(p + 2, 0, sizeof(format_pg) - 2);        return sizeof(format_pg);}static int resp_caching_pg(unsigned char * p, int pcontrol, int target){ 	/* Caching page for mode_sense */	unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0,		0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0,     0, 0, 0, 0};	memcpy(p, caching_pg, sizeof(caching_pg));	if (1 == pcontrol)		memset(p + 2, 0, sizeof(caching_pg) - 2);	return sizeof(caching_pg);}static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target){ 	/* Control mode page for mode_sense */	unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,				     0, 0, 0x2, 0x4b};	memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg));	if (1 == pcontrol)		memset(p + 2, 0, sizeof(ctrl_m_pg) - 2);	return sizeof(ctrl_m_pg);}static int resp_iec_m_pg(unsigned char * p, int pcontrol, int target){	/* Informational Exceptions control mode page for mode_sense */	unsigned char iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,				    0, 0, 0x0, 0x0};	memcpy(p, iec_m_pg, sizeof(iec_m_pg));	if (1 == pcontrol)		memset(p + 2, 0, sizeof(iec_m_pg) - 2);	return sizeof(iec_m_pg);}#define SDEBUG_MAX_MSENSE_SZ 256static int resp_mode_sense(unsigned char * cmd, int target,			   unsigned char * buff, int bufflen,			   struct sdebug_dev_info * devip){	unsigned char dbd;	int pcontrol, pcode;	unsigned char dev_spec;	int alloc_len, msense_6, offset, len;	unsigned char * ap;	unsigned char arr[SDEBUG_MAX_MSENSE_SZ];	int min_len = bufflen > SDEBUG_MAX_MSENSE_SZ ?			SDEBUG_MAX_MSENSE_SZ : bufflen;	SCSI_LOG_LLQUEUE(3, printk("Mode sense ...(%p %d)\n", buff, bufflen));	dbd = cmd[1] & 0x8;	pcontrol = (cmd[2] & 0xc0) >> 6;	pcode = cmd[2] & 0x3f;	msense_6 = (MODE_SENSE == cmd[0]);	alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[8]);	/* printk(KERN_INFO "msense: dbd=%d pcontrol=%d pcode=%d "		"msense_6=%d alloc_len=%d\n", dbd, pcontrol, pcode, "		"msense_6, alloc_len); */	if (bufflen < alloc_len)		printk(KERN_INFO "scsi_debug: mode_sense: bufflen=%d "		       "< alloc_length=%d\n", bufflen, alloc_len);	memset(buff, 0, bufflen);	memset(arr, 0, SDEBUG_MAX_MSENSE_SZ);	if (0x3 == pcontrol) {  /* Saving values not supported */		mk_sense_buffer(devip, ILLEGAL_REQUEST, SAVING_PARAMS_UNSUP,			       	0, 18);		return check_condition_result;	}	dev_spec = DEV_READONLY(target) ? 0x80 : 0x0;	if (msense_6) {		arr[2] = dev_spec;		offset = 4;	} else {		arr[3] = dev_spec;		offset = 8;	}	ap = arr + offset;	switch (pcode) {	case 0x1:	/* Read-Write error recovery page, direct access */		len = resp_err_recov_pg(ap, pcontrol, target);		offset += len;		break;	case 0x2:	/* Disconnect-Reconnect page, all devices */		len = resp_disconnect_pg(ap, pcontrol, target);		offset += len;		break;        case 0x3:       /* Format device page, direct access */                len = resp_format_pg(ap, pcontrol, target);                offset += len;                break;	case 0x8:	/* Caching page, direct access */		len = resp_caching_pg(ap, pcontrol, target);		offset += len;		break;	case 0xa:	/* Control Mode page, all devices */		len = resp_ctrl_m_pg(ap, pcontrol, target);		offset += len;		break;	case 0x1c:	/* Informational Exceptions Mode page, all devices */		len = resp_iec_m_pg(ap, pcontrol, target);		offset += len;		break;	case 0x3f:	/* Read all Mode pages */		len = resp_err_recov_pg(ap, pcontrol, target);		len += resp_disconnect_pg(ap + len, pcontrol, target);		len += resp_format_pg(ap + len, pcontrol, target);		len += resp_caching_pg(ap + len, pcontrol, target);		len += resp_ctrl_m_pg(ap + len, pcontrol, target);		len += resp_iec_m_pg(ap + len, pcontrol, target);		offset += len;		break;	default:		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,			       	0, 18);		return check_condition_result;	}	if (msense_6)		arr[0] = offset - 1;	else {		offset -= 2;		arr[0] = (offset >> 8) & 0xff;		arr[1] = offset & 0xff;	}	memcpy(buff, arr, min_len);	return 0;}static int resp_read(struct scsi_cmnd * SCpnt, int upper_blk, int block,		     int num, struct sdebug_dev_info * devip){        unsigned char *buff = (unsigned char *) SCpnt->request_buffer;        int nbytes, sgcount;        struct scatterlist *sgpnt = NULL;        int bufflen = SCpnt->request_bufflen;	unsigned long iflags;	if (upper_blk || (block + num > sdebug_capacity)) {		mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,				0, 18);		return check_condition_result;	}	if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) &&	    (block <= OPT_MEDIUM_ERR_ADDR) &&	    ((block + num) > OPT_MEDIUM_ERR_ADDR)) {		mk_sense_buffer(devip, MEDIUM_ERROR, UNRECOVERED_READ_ERR,				0, 18);		/* claim unrecoverable read error */		return check_condition_result;	}	read_lock_irqsave(&atomic_rw, iflags);        sgcount = 0;	nbytes = bufflen;	/* printk(KERN_INFO "scsi_debug_read: block=%d, tot_bufflen=%d\n",	       block, bufflen); */	if (SCpnt->use_sg) {		sgcount = 0;		sgpnt = (struct scatterlist *) buff;		buff = scatg2virt(&sgpnt[sgcount]);		bufflen = sgpnt[sgcount].length;	}	do {		memcpy(buff, fake_storep + (block * SECT_SIZE), bufflen);		nbytes -= bufflen;		if (SCpnt->use_sg) {			block += bufflen >> POW2_SECT_SIZE;			sgcount++;			if (nbytes) {				buff = scatg2virt(&sgpnt[sgcount]);				bufflen = sgpnt[sgcount].length;			}		} else if (nbytes > 0)			printk(KERN_WARNING "scsi_debug:resp_read: unexpected "			       "nbytes=%d\n", nbytes);	} while (nbytes);	read_unlock_irqrestore(&atomic_rw, iflags);	return 0;}static int resp_write(struct scsi_cmnd * SCpnt, int upper_blk, int block,		      int num, struct sdebug_dev_info * devip){        unsigned char *buff = (unsigned char *) SCpnt->request_buffer;        int nbytes, sgcount;        struct scatterlist *sgpnt = NULL;        int bufflen = SCpnt->request_bufflen;	unsigned long iflags;	if (upper_blk || (block + num > sdebug_capacity)) {		mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,			       	0, 18);		return check_condition_result;	}	write_lock_irqsave(&atomic_rw, iflags);        sgcount = 0;	nbytes = bufflen;	if (SCpnt->use_sg) {		sgcount = 0;		sgpnt = (struct scatterlist *) buff;		buff = scatg2virt(&sgpnt[sgcount]);		bufflen = sgpnt[sgcount].length;	}	do {		memcpy(fake_storep + (block * SECT_SIZE), buff, bufflen);		nbytes -= bufflen;		if (SCpnt->use_sg) {			block += bufflen >> POW2_SECT_SIZE;			sgcount++;			if (nbytes) {				buff = scatg2virt(&sgpnt[sgcount]);				bufflen = sgpnt[sgcount].length;			}		} else if (nbytes > 0)			printk(KERN_WARNING "scsi_debug:resp_write: "			       "unexpected nbytes=%d\n", nbytes);	} while (nbytes);	write_unlock_irqrestore(&atomic_rw, iflags);	return 0;}static int resp_report_luns(unsigned char * cmd, unsigned char * buff,			    int bufflen, struct sdebug_dev_info * devip){	unsigned int alloc_len;	int lun_cnt, i, upper;	int select_report = (int)cmd[2];	struct scsi_lun *one_lun;	alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24);	if ((alloc_len < 16) || (select_report > 2)) {		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,			       	0, 18);		return check_condition_result;	}	if (bufflen > 8) { /* can produce response with up to 16k luns			      (lun 0 to lun 16383) */		memset(buff, 0, bufflen);		lun_cnt = scsi_debug_max_luns;		buff[2] = ((sizeof(struct scsi_lun) * lun_cnt) >> 8) & 0xff;		buff[3] = (sizeof(struct scsi_lun) * lun_cnt) & 0xff;		lun_cnt = min((int)((bufflen - 8) / sizeof(struct scsi_lun)),			      lun_cnt);		one_lun = (struct scsi_lun *) &buff[8];		for (i = 0; i < lun_cnt; i++) {			upper = (i >> 8) & 0x3f;			if (upper)				one_lun[i].scsi_lun[0] =				    (upper | (SAM2_LUN_ADDRESS_METHOD << 6));			one_lun[i].scsi_lun[1] = i & 0xff;		}	}	return 0;}/* When timer goes off this function is called. */static void timer_intr_handler(unsigned long indx){	struct sdebug_queued_cmd * sqcp;	unsigned long iflags;	if (indx >= SCSI_DEBUG_CANQUEUE) {		printk(KERN_ERR "scsi_debug:timer_intr_handler: indx too "		       "large\n");		return;	}	spin_lock_irqsave(&queued_arr_lock, iflags);	sqcp = &queued_arr[(int)indx];	if (! sqcp->in_use) {		printk(KERN_ERR "scsi_debug:timer_intr_handler: Unexpected "		       "interrupt\n");		spin_unlock_irqrestore(&queued_arr_lock, iflags);		return;	}	sqcp->in_use = 0;	if (sqcp->done_funct) {		sqcp->a_cmnd->result = sqcp->scsi_result;		sqcp->done_funct(sqcp->a_cmnd); /* callback to mid level */	}	sqcp->done_funct = NULL;	spin_unlock_irqrestore(&queued_arr_lock, iflags);}static int scsi_debug_slave_alloc(struct scsi_device * sdp){	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)		printk(KERN_INFO "scsi_debug: slave_alloc <%u %u %u %u>\n",		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);	return 0;}

⌨️ 快捷键说明

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