📄 sg.c
字号:
if (sdp->detached) return -ENODEV; if (filp->f_flags & O_NONBLOCK) return -EAGAIN; while (1) { res = 0; /* following is a macro that beats race condition */ __wait_event_interruptible(sfp->read_wait, (sdp->detached || (srp = sg_get_rq_mark(sfp, req_pack_id))), res); if (sdp->detached) return -ENODEV; if (0 == res) break; return res; /* -ERESTARTSYS because signal hit process */ } } if (srp->header.interface_id != '\0') return sg_new_read(sfp, buf, count, srp); hp = &srp->header; memset(&old_hdr, 0, SZ_SG_HEADER); old_hdr.reply_len = (int)hp->timeout; old_hdr.pack_len = old_hdr.reply_len; /* very old, strange behaviour */ old_hdr.pack_id = hp->pack_id; old_hdr.twelve_byte = ((srp->data.cmd_opcode >= 0xc0) && (12 == hp->cmd_len)) ? 1 : 0; old_hdr.target_status = hp->masked_status; old_hdr.host_status = hp->host_status; old_hdr.driver_status = hp->driver_status; if ((CHECK_CONDITION & hp->masked_status) || (DRIVER_SENSE & hp->driver_status)) memcpy(old_hdr.sense_buffer, srp->sense_b, sizeof(old_hdr.sense_buffer)); switch (hp->host_status) { /* This setup of 'result' is for backward compatibility and is best ignored by the user who should use target, host + driver status */ case DID_OK: case DID_PASSTHROUGH: case DID_SOFT_ERROR: old_hdr.result = 0; break; case DID_NO_CONNECT: case DID_BUS_BUSY: case DID_TIME_OUT: old_hdr.result = EBUSY; break; case DID_BAD_TARGET: case DID_ABORT: case DID_PARITY: case DID_RESET: case DID_BAD_INTR: old_hdr.result = EIO; break; case DID_ERROR: old_hdr.result = (srp->sense_b[0] == 0 && hp->masked_status == GOOD) ? 0 : EIO; break; default: old_hdr.result = EIO; break; } /* Now copy the result back to the user buffer. */ if (count >= SZ_SG_HEADER) { __copy_to_user(buf, &old_hdr, SZ_SG_HEADER); buf += SZ_SG_HEADER; if (count > old_hdr.reply_len) count = old_hdr.reply_len; if (count > SZ_SG_HEADER) sg_read_oxfer(srp, buf, count - SZ_SG_HEADER); } else count = (old_hdr.result == 0) ? 0 : -EIO; sg_finish_rem_req(srp); return count;}static ssize_t sg_new_read(Sg_fd * sfp, char * buf, size_t count, Sg_request * srp){ sg_io_hdr_t * hp = &srp->header; int err = 0; int len; if (count < SZ_SG_IO_HDR) { err = -EINVAL; goto err_out; } hp->sb_len_wr = 0; if ((hp->mx_sb_len > 0) && hp->sbp) { if ((CHECK_CONDITION & hp->masked_status) || (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 ((err = verify_area(VERIFY_WRITE, hp->sbp, len))) goto err_out; __copy_to_user(hp->sbp, srp->sense_b, len); hp->sb_len_wr = len; } } if (hp->masked_status || hp->host_status || hp->driver_status) hp->info |= SG_INFO_CHECK; copy_to_user(buf, hp, SZ_SG_IO_HDR); err = sg_read_xfer(srp);err_out: sg_finish_rem_req(srp); return (0 == err) ? count : err;}static ssize_t sg_write(struct file * filp, const char * 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: dev=%d, count=%d\n", MINOR(sdp->i_rdev), (int)count)); if (sdp->detached) return -ENODEV; if (! ((filp->f_flags & O_NONBLOCK) || scsi_block_when_processing_errors(sdp->device))) return -ENXIO; if (ppos != &filp->f_pos) ; /* FIXME: Hmm. Seek to the right place, or fail? */ 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; __copy_from_user(&old_hdr, buf, SZ_SG_HEADER); 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) > 0) ? 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 = (unsigned char *)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; __copy_from_user(cmnd, buf, cmd_size); k = sg_common_write(sfp, srp, cmnd, sfp->timeout, blocking); return (k < 0) ? k : count;}static ssize_t sg_new_write(Sg_fd * sfp, const char * 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; __copy_from_user(hp, buf, SZ_SG_IO_HDR); 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 = sg_ms_to_jif(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 */ } __copy_from_user(cmnd, hp->cmdp, hp->cmd_len); if (read_only && (! sg_allow_access(cmnd[0], sfp->parentdp->device->type))) { sg_remove_request(sfp, srp); return -EACCES; } k = sg_common_write(sfp, srp, cmnd, timeout, blocking); if (k < 0) return k; if (o_srp) *o_srp = srp; return count;}static int sg_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); 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_dev = sdp->i_rdev; SRpnt->sr_request.rq_status = RQ_ACTIVE; SRpnt->sr_sense_buffer[0] = 0; SRpnt->sr_cmd_len = hp->cmd_len; if (! (hp->flags & SG_FLAG_LUN_INHIBIT)) { if (sdp->device->scsi_level <= SCSI_2) cmnd[1] = (cmnd[1] & 0x1f) | (sdp->device->lun << 5); } 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; } 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_bh() is called (i.e. a callback). */ scsi_do_req(SRpnt, (void *)cmnd, (void *)SRpnt->sr_buffer, hp->dxfer_len, sg_cmd_done_bh, timeout, SG_DEFAULT_RETRIES); /* dxfer_len overwrites SRpnt->sr_bufflen, hence need for b_malloc_len */#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,1) generic_unplug_device(q);#endif return 0;}static int sg_ioctl(struct inode * inode, struct file * filp, unsigned int cmd_in, unsigned long arg){ 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: dev=%d, cmd=0x%x\n", MINOR(sdp->i_rdev), (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, (void *)arg, SZ_SG_IO_HDR); if (result) return result; result = sg_new_write(sfp, (const char *)arg, 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, (char *)arg, SZ_SG_IO_HDR, srp); return (result < 0) ? result : 0; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -