sg.c

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

C
2,278
字号
		    (DRIVER_SENSE & hp->driver_status)) {			int sb_len = sizeof (dummy_cmdp->sr_sense_buffer);			sb_len = (hp->mx_sb_len > sb_len) ? sb_len : hp->mx_sb_len;			len = 8 + (int) srp->sense_b[7];	/* Additional sense length field */			len = (len > sb_len) ? sb_len : len;			if (copy_to_user(hp->sbp, srp->sense_b, len)) {				err = -EFAULT;				goto err_out;			}			hp->sb_len_wr = len;		}	}	if (hp->masked_status || hp->host_status || hp->driver_status)		hp->info |= SG_INFO_CHECK;	if (copy_to_user(buf, hp, SZ_SG_IO_HDR)) {		err = -EFAULT;		goto err_out;	}	err = sg_read_xfer(srp);      err_out:	sg_finish_rem_req(srp);	return (0 == err) ? count : err;}static ssize_tsg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos){	int mxsize, cmd_size, k;	int input_size, blocking;	unsigned char opcode;	Sg_device *sdp;	Sg_fd *sfp;	Sg_request *srp;	struct sg_header old_hdr;	sg_io_hdr_t *hp;	unsigned char cmnd[sizeof (dummy_cmdp->sr_cmnd)];	if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))		return -ENXIO;	SCSI_LOG_TIMEOUT(3, printk("sg_write: %s, count=%d\n",				   sdp->disk->disk_name, (int) count));	if (sdp->detached)		return -ENODEV;	if (!((filp->f_flags & O_NONBLOCK) ||	      scsi_block_when_processing_errors(sdp->device)))		return -ENXIO;	if ((k = verify_area(VERIFY_READ, buf, count)))		return k;	/* protects following copy_from_user()s + get_user()s */	if (count < SZ_SG_HEADER)		return -EIO;	if (__copy_from_user(&old_hdr, buf, SZ_SG_HEADER))		return -EFAULT;	blocking = !(filp->f_flags & O_NONBLOCK);	if (old_hdr.reply_len < 0)		return sg_new_write(sfp, buf, count, blocking, 0, NULL);	if (count < (SZ_SG_HEADER + 6))		return -EIO;	/* The minimum scsi command length is 6 bytes. */	if (!(srp = sg_add_request(sfp))) {		SCSI_LOG_TIMEOUT(1, printk("sg_write: queue full\n"));		return -EDOM;	}	buf += SZ_SG_HEADER;	__get_user(opcode, buf);	if (sfp->next_cmd_len > 0) {		if (sfp->next_cmd_len > MAX_COMMAND_SIZE) {			SCSI_LOG_TIMEOUT(1, printk("sg_write: command length too long\n"));			sfp->next_cmd_len = 0;			sg_remove_request(sfp, srp);			return -EIO;		}		cmd_size = sfp->next_cmd_len;		sfp->next_cmd_len = 0;	/* reset so only this write() effected */	} else {		cmd_size = COMMAND_SIZE(opcode);	/* based on SCSI command group */		if ((opcode >= 0xc0) && old_hdr.twelve_byte)			cmd_size = 12;	}	SCSI_LOG_TIMEOUT(4, printk(		"sg_write:   scsi opcode=0x%02x, cmd_size=%d\n", (int) opcode, cmd_size));/* Determine buffer size.  */	input_size = count - cmd_size;	mxsize = (input_size > old_hdr.reply_len) ? input_size : old_hdr.reply_len;	mxsize -= SZ_SG_HEADER;	input_size -= SZ_SG_HEADER;	if (input_size < 0) {		sg_remove_request(sfp, srp);		return -EIO;	/* User did not pass enough bytes for this command. */	}	hp = &srp->header;	hp->interface_id = '\0';	/* indicator of old interface tunnelled */	hp->cmd_len = (unsigned char) cmd_size;	hp->iovec_count = 0;	hp->mx_sb_len = 0;	if (input_size > 0)		hp->dxfer_direction = (old_hdr.reply_len > SZ_SG_HEADER) ?		    SG_DXFER_TO_FROM_DEV : SG_DXFER_TO_DEV;	else		hp->dxfer_direction = (mxsize > 0) ? SG_DXFER_FROM_DEV : SG_DXFER_NONE;	hp->dxfer_len = mxsize;	hp->dxferp = (char __user *)buf + cmd_size;	hp->sbp = NULL;	hp->timeout = old_hdr.reply_len;	/* structure abuse ... */	hp->flags = input_size;	/* structure abuse ... */	hp->pack_id = old_hdr.pack_id;	hp->usr_ptr = NULL;	if (__copy_from_user(cmnd, buf, cmd_size))		return -EFAULT;	k = sg_common_write(sfp, srp, cmnd, sfp->timeout, blocking);	return (k < 0) ? k : count;}static ssize_tsg_new_write(Sg_fd * sfp, const char __user *buf, size_t count,	     int blocking, int read_only, Sg_request ** o_srp){	int k;	Sg_request *srp;	sg_io_hdr_t *hp;	unsigned char cmnd[sizeof (dummy_cmdp->sr_cmnd)];	int timeout;	if (count < SZ_SG_IO_HDR)		return -EINVAL;	if ((k = verify_area(VERIFY_READ, buf, count)))		return k; /* protects following copy_from_user()s + get_user()s */	sfp->cmd_q = 1;	/* when sg_io_hdr seen, set command queuing on */	if (!(srp = sg_add_request(sfp))) {		SCSI_LOG_TIMEOUT(1, printk("sg_new_write: queue full\n"));		return -EDOM;	}	hp = &srp->header;	if (__copy_from_user(hp, buf, SZ_SG_IO_HDR)) {		sg_remove_request(sfp, srp);		return -EFAULT;	}	if (hp->interface_id != 'S') {		sg_remove_request(sfp, srp);		return -ENOSYS;	}	if (hp->flags & SG_FLAG_MMAP_IO) {		if (hp->dxfer_len > sfp->reserve.bufflen) {			sg_remove_request(sfp, srp);			return -ENOMEM;	/* MMAP_IO size must fit in reserve buffer */		}		if (hp->flags & SG_FLAG_DIRECT_IO) {			sg_remove_request(sfp, srp);			return -EINVAL;	/* either MMAP_IO or DIRECT_IO (not both) */		}		if (sg_res_in_use(sfp)) {			sg_remove_request(sfp, srp);			return -EBUSY;	/* reserve buffer already being used */		}	}	timeout = msecs_to_jiffies(srp->header.timeout);	if ((!hp->cmdp) || (hp->cmd_len < 6) || (hp->cmd_len > sizeof (cmnd))) {		sg_remove_request(sfp, srp);		return -EMSGSIZE;	}	if ((k = verify_area(VERIFY_READ, hp->cmdp, hp->cmd_len))) {		sg_remove_request(sfp, srp);		return k;	/* protects following copy_from_user()s + get_user()s */	}	if (__copy_from_user(cmnd, hp->cmdp, hp->cmd_len)) {		sg_remove_request(sfp, srp);		return -EFAULT;	}	if (read_only &&	    (!sg_allow_access(cmnd[0], sfp->parentdp->device->type))) {		sg_remove_request(sfp, srp);		return -EPERM;	}	k = sg_common_write(sfp, srp, cmnd, timeout, blocking);	if (k < 0)		return k;	if (o_srp)		*o_srp = srp;	return count;}static intsg_common_write(Sg_fd * sfp, Sg_request * srp,		unsigned char *cmnd, int timeout, int blocking){	int k;	Scsi_Request *SRpnt;	Sg_device *sdp = sfp->parentdp;	sg_io_hdr_t *hp = &srp->header;	request_queue_t *q;	srp->data.cmd_opcode = cmnd[0];	/* hold opcode of command */	hp->status = 0;	hp->masked_status = 0;	hp->msg_status = 0;	hp->info = 0;	hp->host_status = 0;	hp->driver_status = 0;	hp->resid = 0;	SCSI_LOG_TIMEOUT(4, printk("sg_common_write:  scsi opcode=0x%02x, cmd_size=%d\n",			  (int) cmnd[0], (int) hp->cmd_len));	if ((k = sg_start_req(srp))) {		SCSI_LOG_TIMEOUT(1, printk("sg_write: start_req err=%d\n", k));		sg_finish_rem_req(srp);		return k;	/* probably out of space --> ENOMEM */	}	if ((k = sg_write_xfer(srp))) {		SCSI_LOG_TIMEOUT(1, printk("sg_write: write_xfer, bad address\n"));		sg_finish_rem_req(srp);		return k;	}	if (sdp->detached) {		sg_finish_rem_req(srp);		return -ENODEV;	}	SRpnt = scsi_allocate_request(sdp->device, GFP_ATOMIC);	if (SRpnt == NULL) {		SCSI_LOG_TIMEOUT(1, printk("sg_write: no mem\n"));		sg_finish_rem_req(srp);		return -ENOMEM;	}	srp->my_cmdp = SRpnt;	q = SRpnt->sr_device->request_queue;	SRpnt->sr_request->rq_disk = sdp->disk;	SRpnt->sr_sense_buffer[0] = 0;	SRpnt->sr_cmd_len = hp->cmd_len;	SRpnt->sr_use_sg = srp->data.k_use_sg;	SRpnt->sr_sglist_len = srp->data.sglist_len;	SRpnt->sr_bufflen = srp->data.bufflen;	SRpnt->sr_underflow = 0;	SRpnt->sr_buffer = srp->data.buffer;	switch (hp->dxfer_direction) {	case SG_DXFER_TO_FROM_DEV:	case SG_DXFER_FROM_DEV:		SRpnt->sr_data_direction = SCSI_DATA_READ;		break;	case SG_DXFER_TO_DEV:		SRpnt->sr_data_direction = SCSI_DATA_WRITE;		break;	case SG_DXFER_UNKNOWN:		SRpnt->sr_data_direction = SCSI_DATA_UNKNOWN;		break;	default:		SRpnt->sr_data_direction = SCSI_DATA_NONE;		break;	}	SRpnt->upper_private_data = srp;	srp->data.k_use_sg = 0;	srp->data.sglist_len = 0;	srp->data.bufflen = 0;	srp->data.buffer = NULL;	hp->duration = jiffies;	/* unit jiffies now, millisecs after done *//* Now send everything of to mid-level. The next time we hear about this   packet is when sg_cmd_done() is called (i.e. a callback). */	scsi_do_req(SRpnt, (void *) cmnd,		    (void *) SRpnt->sr_buffer, hp->dxfer_len,		    sg_cmd_done, timeout, SG_DEFAULT_RETRIES);	/* dxfer_len overwrites SRpnt->sr_bufflen, hence need for b_malloc_len */	return 0;}static intsg_ioctl(struct inode *inode, struct file *filp,	 unsigned int cmd_in, unsigned long arg){	void __user *p = (void __user *)arg;	int __user *ip = p;	int result, val, read_only;	Sg_device *sdp;	Sg_fd *sfp;	Sg_request *srp;	unsigned long iflags;	if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))		return -ENXIO;	SCSI_LOG_TIMEOUT(3, printk("sg_ioctl: %s, cmd=0x%x\n",				   sdp->disk->disk_name, (int) cmd_in));	read_only = (O_RDWR != (filp->f_flags & O_ACCMODE));	switch (cmd_in) {	case SG_IO:		{			int blocking = 1;	/* ignore O_NONBLOCK flag */			if (sdp->detached)				return -ENODEV;			if (!scsi_block_when_processing_errors(sdp->device))				return -ENXIO;			result = verify_area(VERIFY_WRITE, p, SZ_SG_IO_HDR);			if (result)				return result;			result =			    sg_new_write(sfp, p, SZ_SG_IO_HDR,					 blocking, read_only, &srp);			if (result < 0)				return result;			srp->sg_io_owned = 1;			while (1) {				result = 0;	/* following macro to beat race condition */				__wait_event_interruptible(sfp->read_wait,					(sdp->detached || sfp->closed || srp->done),							   result);				if (sdp->detached)					return -ENODEV;				if (sfp->closed)					return 0;	/* request packet dropped already */				if (0 == result)					break;				srp->orphan = 1;				return result;	/* -ERESTARTSYS because signal hit process */			}			srp->done = 2;			result = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp);			return (result < 0) ? result : 0;		}	case SG_SET_TIMEOUT:		result = get_user(val, ip);		if (result)			return result;		if (val < 0)			return -EIO;		if (val >= MULDIV (INT_MAX, USER_HZ, HZ))		    val = MULDIV (INT_MAX, USER_HZ, HZ);		sfp->timeout_user = val;		sfp->timeout = MULDIV (val, HZ, USER_HZ);		return 0;	case SG_GET_TIMEOUT:	/* N.B. User receives timeout as return value */				/* strange ..., for backward compatibility */		return sfp->timeout_user;	case SG_SET_FORCE_LOW_DMA:		result = get_user(val, ip);		if (result)			return result;		if (val) {			sfp->low_dma = 1;			if ((0 == sfp->low_dma) && (0 == sg_res_in_use(sfp))) {				val = (int) sfp->reserve.bufflen;				sg_remove_scat(&sfp->reserve);				sg_build_reserve(sfp, val);			}		} else {			if (sdp->detached)				return -ENODEV;			sfp->low_dma = sdp->device->host->unchecked_isa_dma;		}		return 0;	case SG_GET_LOW_DMA:		return put_user((int) sfp->low_dma, ip);	case SG_GET_SCSI_ID:		result =		    verify_area(VERIFY_WRITE, p, sizeof (sg_scsi_id_t));		if (result)			return result;		else {			sg_scsi_id_t __user *sg_idp = p;			if (sdp->detached)				return -ENODEV;			__put_user((int) sdp->device->host->host_no,				   &sg_idp->host_no);			__put_user((int) sdp->device->channel,				   &sg_idp->channel);			__put_user((int) sdp->device->id, &sg_idp->scsi_id);			__put_user((int) sdp->device->lun, &sg_idp->lun);			__put_user((int) sdp->device->type, &sg_idp->scsi_type);			__put_user((short) sdp->device->host->cmd_per_lun,				   &sg_idp->h_cmd_per_lun);			__put_user((short) sdp->device->queue_depth,				   &sg_idp->d_queue_depth);			__put_user(0, &sg_idp->unused[0]);			__put_user(0, &sg_idp->unused[1]);			return 0;		}	case SG_SET_FORCE_PACK_ID:		result = get_user(val, ip);		if (result)			return result;		sfp->force_packid = val ? 1 : 0;		return 0;	case SG_GET_PACK_ID:		result = verify_area(VERIFY_WRITE, ip, sizeof (int));		if (result)			return result;		read_lock_irqsave(&sfp->rq_list_lock, iflags);		for (srp = sfp->headrp; srp; srp = srp->nextrp) {			if ((1 == srp->done) && (!srp->sg_io_owned)) {				read_unlock_irqrestore(&sfp->rq_list_lock,						       iflags);				__put_user(srp->header.pack_id, ip);				return 0;			}		}		read_unlock_irqrestore(&sfp->rq_list_lock, iflags);		__put_user(-1, ip);		return 0;	case SG_GET_NUM_WAITING:		read_lock_irqsave(&sfp->rq_list_lock, iflags);		for (val = 0, srp = sfp->headrp; srp; srp = srp->nextrp) {			if ((1 == srp->done) && (!srp->sg_io_owned))				++val;		}		read_unlock_irqrestore(&sfp->rq_list_lock, iflags);		return put_user(val, ip);	case SG_GET_SG_TABLESIZE:		return put_user(sdp->sg_tablesize, ip);	case SG_SET_RESERVED_SIZE:		result = get_user(val, ip);		if (result)			return result;                if (val < 0)                        return -EINVAL;		if (val != sfp->reserve.bufflen) {			if (sg_res_in_use(sfp) || sfp->mmap_called)				return -EBUSY;			sg_remove_scat(&sfp->reserve);			sg_build_reserve(sfp, val);		}		return 0;	case SG_GET_RESERVED_SIZE:		val = (int) sfp->reserve.bufflen;		return put_user(val, ip);	case SG_SET_COMMAND_Q:		result = get_user(val, ip);		if (result)			return result;		sfp->cmd_q = val ? 1 : 0;		return 0;	case SG_GET_COMMAND_Q:		return put_user((int) sfp->cmd_q, ip);	case SG_SET_KEEP_ORPHAN:		result = get_user(val, ip);		if (result)			return result;		sfp->keep_orphan = val;		return 0;	case SG_GET_KEEP_ORPHAN:		return put_user((int) sfp->keep_orphan, ip);	case SG_NEXT_CMD_LEN:		result = get_user(val, ip);		if (result)			return result;		sfp->next_cmd_len = (val > 0) ? val : 0;		return 0;	case SG_GET_VERSION_NUM:		return put_user(sg_version_num, ip);	case SG_GET_ACCESS_COUNT:		/* faked - we don't have a real access count anymore */		val = (sdp->device ? 1 : 0);		return put_user(val, ip);	case SG_GET_REQUEST_TABLE:		result = verify_area(VERIFY_WRITE, p,				     SZ_SG_REQ_INFO * SG_MAX_QUEUE);

⌨️ 快捷键说明

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