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