Index: kernel/raw-io.c =================================================================== --- kernel/raw-io.c (revision 0) +++ kernel/raw-io.c (revision 0) @@ -0,0 +1,236 @@ +/* + * Target device pass through I/O + * (C) 2005-2007 Ming Zhang + * This code is licenced under the GPL. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "iscsi.h" +#include "iscsi_dbg.h" +#include "iotype.h" + +struct raw_sdev; + +struct rawio_data { + struct scsi_device *sdev; + unsigned int h, i, l, c; +}; + +struct raw_sdev { + struct scsi_device *sdev; + struct rawio_data *p; +}; + +#define MAX_RAWDEV 8 +static struct raw_sdev sdev_arr[MAX_RAWDEV]; + +struct scsi_device *get_raw_sdev(struct iet_volume *lu) +{ + if (unlikely(!lu) || unlikely(!lu->private)) + return NULL; + return ((struct rawio_data *)lu->private)->sdev; +} + +static int raw_add(struct class_device *cdev, struct class_interface *intf) +{ + struct scsi_device *scsidp; + int i, res = 0, a = -1; + + scsidp = to_scsi_device(cdev->dev); + + printk("Add (%p) H:%u, C:%u, I:%u, L:%u\n", scsidp, + scsidp->host->host_no, scsidp->channel, scsidp->id, scsidp->lun); + + for (i = 0; i < MAX_RAWDEV; i++) { + if (sdev_arr[i].sdev == scsidp) + res = -EEXIST; + else if ((a == -1) && (sdev_arr[i].sdev == NULL)) + a = i; + } + if (res != 0) + return res; + if (a != -1) { + sdev_arr[a].sdev = scsidp; + sdev_arr[a].p = NULL; + } + return res; +} + +static void raw_remove(struct class_device *cdev, struct class_interface *intf) +{ + struct scsi_device *scsidp; + int i; + + scsidp = to_scsi_device(cdev->dev); + for (i = 0; i < MAX_RAWDEV; i++) { + if (sdev_arr[i].sdev == scsidp) { + sdev_arr[i].sdev = NULL; + break; + } + } +} + +static struct class_interface raw_interface = { + .add = raw_add, + .remove = raw_remove, +}; + +enum { + Opt_h, Opt_c, Opt_i, Opt_l, Opt_ignore, Opt_err, +}; + +static match_table_t tokens = { + {Opt_h, "H=%u"}, + {Opt_c, "C=%u"}, + {Opt_i, "I=%u"}, + {Opt_l, "L=%u"}, + {Opt_ignore, "Type=%s"}, + {Opt_err, NULL}, +}; + +static int parse_rawio_params(struct iet_volume *volume, char *params) +{ + int err = 0; + char *p; + struct rawio_data *data = (struct rawio_data *)volume->private; + + while ((p = strsep(¶ms, ",")) != NULL) { + substring_t args[MAX_OPT_ARGS]; + int token; + if (!*p) + continue; + token = match_token(p, tokens, args); + switch (token) { + case Opt_h: + err = match_int(&args[0], &data->h); + if (err < 0) + goto out; + break; + case Opt_c: + err = match_int(&args[0], &data->c); + if (err < 0) + goto out; + break; + case Opt_i: + err = match_int(&args[0], &data->i); + if (err < 0) + goto out; + break; + case Opt_l: + err = match_int(&args[0], &data->l); + if (err < 0) + goto out; + break; + case Opt_ignore: + break; + default: + eprintk("Unknown %s\n", p); + } + } +out: + if (err > 0) + err = 0; + return err; +} + +static void rawio_detach(struct iet_volume *lu) +{ + struct rawio_data *p; + + p = (struct rawio_data *)lu->private; + + if (likely(p)) { + kfree(p); + lu->private = NULL; + } +} + +static int rawio_attach(struct iet_volume *lu, char *args) +{ + int res = 0, i; + struct rawio_data *p; + + if (unlikely(lu->private)) { + eprintk("already attached ? %d\n", lu->lun); + return -EBUSY; + } + if (unlikely(!(p = kzalloc(sizeof(*p), GFP_KERNEL|__GFP_NOFAIL)))) + return -ENOMEM; + lu->private = p; + + if ((res = parse_rawio_params(lu, args)) < 0) { + eprintk("%d\n", res); + goto out; + } + res = -ENOENT; + for (i = 0; i < MAX_RAWDEV; i++) { + struct scsi_device *sdp = sdev_arr[i].sdev; + + if (sdp == NULL) + continue; + if ((sdp->host->host_no == p->h) && (sdp->id == p->i) + && (sdp->lun == p->l) && (sdp->channel == p->c)) { + p->sdev = sdp; + res = 0; + break; + } + } +out: + if (res) + rawio_detach(lu); + return res; +} + +static int rawio_make_request(struct iet_volume *lu, struct tio *tio, int rw) +{ + eprintk("%s\n", "should not come here"); + return -EACCES; +} + +static int rawio_sync(struct iet_volume *lu, struct tio *tio) +{ + eprintk("%s\n", "should not come here"); + return -EACCES; +} + +static void rawio_show(struct iet_volume *lu, struct seq_file *seq) +{ + struct rawio_data *p = (struct rawio_data *)lu->private; + + seq_printf(seq, " H:%u, C:%u, I:%u, L:%u\n", p->h, p->c, p->i, p->l); +} + +static int raw_init(void) +{ + return scsi_register_interface(&raw_interface); +} + +static void raw_exit(void) +{ + scsi_unregister_interface(&raw_interface); +} + +struct iotype rawio = +{ + .name = "rawio", + .attach = rawio_attach, + .make_request = rawio_make_request, + .sync = rawio_sync, + .detach = rawio_detach, + .show = rawio_show, + .init = raw_init, + .exit = raw_exit, +}; Index: kernel/target_raw.c =================================================================== --- kernel/target_raw.c (revision 0) +++ kernel/target_raw.c (revision 0) @@ -0,0 +1,758 @@ +/* + * (C) 2005-2007 Ming Zhang + * This code is licenced under the GPL. + */ + +#include +#include + +#include +#include +#include + +#include "iscsi.h" +#include "iscsi_dbg.h" +#include "iotype.h" + +#define RAW_MAX_RETRIES 3 +#define RAW_TIMEOUT (30 * HZ) + +/* FIXME: get rid of this when vrom is merged */ +static int build_fail_response(struct iscsi_cmnd *cmnd) +{ + return -1; +} + +static int cmd_timeout(const unsigned char cmd, u32 type) +{ + int t = RAW_TIMEOUT; + + switch (cmd) { + case FORMAT_UNIT: + case ERASE: + case ERASE_16: + t = RAW_TIMEOUT * 240; + break; + case START_STOP: + case MOVE_MEDIUM: + case READ_ELEMENT_STATUS: + case REZERO_UNIT: + case SPACE: + case SPACE_16: + t = RAW_TIMEOUT * 10; + break; + default: + if (type == TYPE_TAPE || type == TYPE_MEDIUM_CHANGER) + t = RAW_TIMEOUT * 5; + break; + } + return t; +} + +u32 find_order(u32 size) +{ + u32 o = (u32)-1; + u32 s = size; + + while (size) { + o++; + size >>= 1; + }; + if (s > (1 << o)) + o++; + return o; +} + +struct raw_req_data { + struct completion waiting; + char *sense; + int res; +}; + +static void raw_done(void *data, char *sense, int result, int resid) +{ + struct raw_req_data *p = data; + + memcpy(p->sense, sense, SCSI_SENSE_BUFFERSIZE); + p->res = result; + complete(&p->waiting); +} + +static int raw_wait_req(struct scsi_device *sdev, struct tio *tio, const u8 *scb, + int direction, unsigned char *sense, int timeout) +{ + struct scatterlist *psg = NULL; + char *buffer; + int use_sg, i, ret; + u32 bufflen; + struct raw_req_data raw_data; + + switch (tio->pg_cnt) { + case 0: + buffer = NULL; + use_sg = 0; + bufflen = 0; + break; + case 1: + buffer = page_address(tio->pvec[0]) + tio->offset; + use_sg = 0; + bufflen = PAGE_SIZE - tio->offset; + break; + default: + psg = kzalloc(tio->pg_cnt * sizeof(struct scatterlist), + GFP_KERNEL | __GFP_NOFAIL); + + psg[0].page = tio->pvec[0]; + sg_dma_address(psg) = (dma_addr_t)page_address(psg[0].page); + psg[0].offset = tio->offset; + psg[0].length = PAGE_SIZE - tio->offset; + for (i = 1; i < tio->pg_cnt; i++) { + psg[i].page = tio->pvec[i]; + sg_dma_address(&psg[i]) = (dma_addr_t)page_address(psg[i].page); + psg[i].offset = 0; + psg[i].length = PAGE_SIZE; + } + buffer = (char *)psg; + use_sg = tio->pg_cnt; + bufflen = (tio->pg_cnt * PAGE_SIZE) - tio->offset; + break; + } + + init_completion(&raw_data.waiting); + raw_data.sense = sense; + + ret = scsi_execute_async(sdev, scb, COMMAND_SIZE(scb[0]), direction, buffer, + bufflen, use_sg, timeout, RAW_MAX_RETRIES, &raw_data, raw_done, 0); + if (ret) { + ret = -EIO; + } else { + wait_for_completion(&raw_data.waiting); + ret = raw_data.res; + } + if (psg) + kfree(psg); + + return ret; +} + +static int build_inquiry_response(struct iscsi_cmnd *cmnd) +{ + BUG(); + return -1; +} + +inline static void __simple_data_req(struct scsi_device *sdev, struct tio *tio, const u8 *scb, + int direction, unsigned char *sense, int timeout, u32 len, + struct iscsi_cmnd *cmnd) +{ + int res; + + res = raw_wait_req(sdev, tio, scb, direction, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + tio_set(tio, len, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + } +} + +static int raw_execute_cmnd(struct iscsi_cmnd *cmnd) +{ + struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd); + struct tio *tio = NULL; + struct iet_volume *lu = NULL; + struct scsi_device *sdev = NULL; + u32 type = 0, len; + int res, timeout; + unsigned char sense[SCSI_SENSE_BUFFERSIZE]; + unsigned char *scb = req->scb; + unsigned char opcode = scb[0]; + + if (likely(lu = cmnd->lun)) { + assert(!strcmp(lu->iotype->name, "rawio")); + sdev = get_raw_sdev(lu); + assert(sdev); + type = lu->l_type; + } else { + assert(opcode == INQUIRY || opcode == REPORT_LUNS); + } + +#if 1 + if (opcode != TEST_UNIT_READY) + eprintk("ENTER raw_execute_cmnd with %s(0x%02x)\n", show_scsi_command(opcode), opcode); +#endif + + timeout = cmd_timeout(opcode, type); + + switch (opcode) { + case INQUIRY: + if (lu) { + len = (scb[3] << 8) + scb[4]; + + tio = cmnd->tio = tio_alloc(!len ? 1 : get_pgcnt(len, 0)); + res = raw_wait_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + int cmddt = (scb[1] & 0x2) >> 1; + int evpd = scb[1] & 0x1; + + lu->l_type = *(u8 *)page_address(tio->pvec[0]) & 0x1f; + if (cmddt) // SPC2 7.3.5 + tio_set(tio, *((u8 *)page_address(tio->pvec[0]) + 5) + 6, 0); + else if (evpd) // SPC2 8.4 + tio_set(tio, *((u8 *)page_address(tio->pvec[0]) + 3) + 4, 0); + else // SPC2 7.3.2 + tio_set(tio, *((u8 *)page_address(tio->pvec[0]) + 4) + 5, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + } + } else + send_data_rsp_with_sense(cmnd, build_inquiry_response, NULL); + break; + case REPORT_LUNS: + send_data_rsp_with_sense(cmnd, build_report_luns_response, NULL); + break; + case TEST_UNIT_READY: + case VERIFY: + case VERIFY_16: + case START_STOP: + case REZERO_UNIT: + case ALLOW_MEDIUM_REMOVAL: + case ERASE: + case ERASE_16: + case SPACE: + case WRITE_FILEMARKS: + case WRITE_FILEMARKS_16: + case SEEK_6: + case SEEK_10: + case SYNCHRONIZE_CACHE: + case GPCMD_SET_SPEED: + case GPCMD_BLANK: + case GPCMD_STOP_PLAY_SCAN: + case GPCMD_PAUSE_RESUME: + case GPCMD_PLAY_AUDIO_MSF: + case GPCMD_CLOSE_TRACK: + case GPCMD_RESERVE_RZONE_TRACK: + case GPCMD_REPAIR_RZONE_TRACK: + case GPCMD_PLAY_AUDIO_10: + case MOVE_MEDIUM: // MMC4: PLAY AUDIO 12 + case REASSIGN_BLOCKS: // SMC2: INITIALIZE ELEMENT STATUS + case EXABYTE_INI_ELE_ST_WITH_RANGE: + case GPCMD_LOAD_UNLOAD: // SSC: EXCHANGE_MEDIUM + case GPCMD_SET_READ_AHEAD: + case GPCMD_SCAN: + tio = cmnd->tio = tio_alloc(0); + res = raw_wait_req(sdev, tio, scb, DMA_NONE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else + send_scsi_rsp(cmnd, build_generic_response); + break; + case READ_CAPACITY: + tio = cmnd->tio = tio_alloc(1); + res = raw_wait_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + lu->blk_cnt = be32_to_cpu(*((u32 *)page_address(tio->pvec[0]))) + 1; + lu->blk_shift = find_order(be32_to_cpu(*((u32 *)page_address(tio->pvec[0]) + 1))); + tio_set(tio, 8, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + } + break; + case SERVICE_ACTION_IN: + assert((scb[1] & 0x1f) == SAI_READ_CAPACITY_16); + tio = cmnd->tio = tio_alloc(1); + res = raw_wait_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + lu->blk_cnt = be64_to_cpu(*((u64 *)page_address(tio->pvec[0]))) + 1; + lu->blk_shift = find_order(be32_to_cpu(*((u32 *)page_address(tio->pvec[0]) + 2))); + tio_set(tio, 32, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + } + break; + case READ_BLOCK_LIMITS: + tio = cmnd->tio = tio_alloc(1); + __simple_data_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout, 6, cmnd); + break; + case RECEIVE_DIAGNOSTIC: + len = (u32)(scb[3] << 8) + scb[4]; + tio = cmnd->tio = tio_alloc(!len ? 1 : get_pgcnt(len, 0)); + __simple_data_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout, len, cmnd); + break; + case READ_BUFFER: + len = (scb[6] << 16) + (scb[7] << 8) + scb[8]; + tio = cmnd->tio = tio_alloc(!len ? 1 : get_pgcnt(len, 0)); + __simple_data_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout, len, cmnd); + break; + case REPORT_DENSITY_SUPPORT: + /* same cmd code as GPCMD_READ_HEADER, but can not find reference about + it in MMC or other SPEC */ + len = (scb[7] << 8) + scb[8]; + tio = cmnd->tio = tio_alloc(!len ? 1 : get_pgcnt(len, 0)); + res = raw_wait_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + u8 *data = (u8 *)page_address(tio->pvec[0]); + u32 size = (data[0] << 8) + data[1] + 4; + + tio_set(tio, len < size ? len : size, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + } + break; + case READ_POSITION: + len = (scb[7] << 8) + scb[8]; + tio = cmnd->tio = tio_alloc(!len ? 1 : get_pgcnt(len, 0)); + __simple_data_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout, + ((scb[1] & 0x1f) == 0x6) ? 32 : 20, cmnd); + break; + case READ_ELEMENT_STATUS: + len = (scb[7] << 16) + (scb[8] << 8) + scb[9]; + tio = cmnd->tio = tio_alloc(!len ? 1 : get_pgcnt(len, 0)); + res = raw_wait_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + u8 *data = (u8 *)page_address(tio->pvec[0]); + u32 size = (data[5] << 16) + (data[6] << 8) + data[7] + 8; + + tio_set(tio, len < size ? len : size, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + } + break; + case GPCMD_MECHANISM_STATUS: + len = (scb[8] << 8) + scb[9]; + tio = cmnd->tio = tio_alloc(!len ? 1 : get_pgcnt(len, 0)); + res = raw_wait_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + u8 *data = (u8 *)page_address(tio->pvec[0]); + u32 size = (data[6] << 8) + data[7] + 8; + + tio_set(tio, len < size ? len : size, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + } + break; + case GPCMD_READ_BUFFER_CAPACITY: + len = (scb[7] << 8) + scb[8]; + tio = cmnd->tio = tio_alloc(!len ? 1 : get_pgcnt(len, 0)); + res = raw_wait_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + u8 *data = (u8 *)page_address(tio->pvec[0]); + u32 size = (data[0] << 8) + data[1] + 2; + + tio_set(tio, len < size ? len : size, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + } + break; + case GPCMD_READ_FORMAT_CAPACITIES: + len = (scb[7] << 8) + scb[8]; + tio = cmnd->tio = tio_alloc(!len ? 1 : get_pgcnt(len, 0)); + res = raw_wait_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + u8 *data = (u8 *)page_address(tio->pvec[0]); + u32 size = data[3] + 4; + + tio_set(tio, len < size ? len : size, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + } + break; + case GPCMD_READ_DISC_INFO: + case GPCMD_READ_TRACK_RZONE_INFO: + case READ_TOC: + len = (scb[7] << 8) + scb[8]; + tio = cmnd->tio = tio_alloc(!len ? 1 : get_pgcnt(len, 0)); + res = raw_wait_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + tio_set(tio, be16_to_cpu(*((u16 *)page_address(tio->pvec[0]))) + 2, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + } + break; + case GPCMD_GET_PERFORMANCE: + { + u32 type, max; + + type = scb[1] & 0x1F; + max = (scb[8] << 8) + scb[9]; + switch (type) { + case 0: + len = 8 + (max << 4); + break; + case 1: + len = max << 3; + break; + case 2: + len = max << 11; + break; + case 3: + len = max << 4; + break; + case 4: + len = 8 + (max << 3); + break; + case 5: + default: + eprintk("Unprocessed type %d\n", type); + len = 0; + } + + tio = cmnd->tio = tio_alloc(!len ? 1 : get_pgcnt(len, 0)); + res = raw_wait_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + tio_set(tio, be32_to_cpu(*((u32 *)page_address(tio->pvec[0]))) + 4, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + } + break; + } + case GPCMD_READ_SUBCHANNEL: + len = (scb[7] << 8) + scb[8]; + tio = cmnd->tio = tio_alloc(!len ? 1 : get_pgcnt(len, 0)); + res = raw_wait_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + tio_set(tio, be16_to_cpu(*((u16 *)page_address(tio->pvec[0]) + 1)) + 4, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + } + break; + case GPCMD_GET_EVENT_STATUS_NOTIFICATION: + len = (scb[7] << 8) + scb[8]; + tio = cmnd->tio = tio_alloc(!len ? 1 : get_pgcnt(len, 0)); + res = raw_wait_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + tio_set(tio, be16_to_cpu(*((u16 *)page_address(tio->pvec[0]))) + 4, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + } + break; + case GPCMD_READ_DVD_STRUCTURE: + len = (scb[8] << 8) + scb[9]; + tio = cmnd->tio = tio_alloc(!len ? 1 : get_pgcnt(len, 0)); + res = raw_wait_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + tio_set(tio, be16_to_cpu(*((u16 *)page_address(tio->pvec[0]))) + 2, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + } + break; + case GPCMD_GET_CONFIGURATION: + len = (scb[7] << 8) + scb[8]; + tio = cmnd->tio = tio_alloc(!len ? 1 : get_pgcnt(len, 0)); + res = raw_wait_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + tio_set(tio, be32_to_cpu(*((u32 *)page_address(tio->pvec[0]))) + 8, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + } + break; + case GPCMD_REPORT_KEY: + len = (scb[8] << 8) + scb[9]; + tio = cmnd->tio = tio_alloc(!len ? 1 : get_pgcnt(len, 0)); + res = raw_wait_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + tio_set(tio, *((u8 *)page_address(tio->pvec[0]) + 1) + 2, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + } + break; + case REQUEST_SENSE: + tio = cmnd->tio = tio_alloc(1); + res = raw_wait_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + tio_set(tio, *((u8 *)page_address(tio->pvec[0]) + 7) + 1, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + } + break; + case RECOVER_BUFFERED_DATA: + len = (scb[2] << 16) + (scb[3] << 8) + scb[4]; + tio = cmnd->tio = tio_alloc(!len ? 1 : get_pgcnt(len, 0)); + __simple_data_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout, len, cmnd); + break; + case READ_6: + case READ_REVERSE: + case READ_10: + case READ_12: + case READ_16: + case GPCMD_READ_CD: + res = raw_wait_req(sdev, cmnd->tio, scb, DMA_FROM_DEVICE, sense, timeout); + if (!res) + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + else if (lu->l_type != TYPE_TAPE) + send_sense_rsp(cmnd, sense); + else { /* ssc-3 6.4 */ + u16 off = (opcode == READ_6 || opcode == READ_REVERSE) ? 2 : 12; + u32 processed = be32_to_cpu(*((u32 *)&sense[3])); + + processed = (((u32)scb[off] << 16) | ((u32)scb[off + 1] << 8) | + ((u32)scb[off + 2])) - processed; + /* DFFIXME: will this has problem for non 2-order block size? */ + if (scb[1] & 1) + processed <<= cmnd->lun->blk_shift; + /* DFFIXME: the SRpnt->sr_overflow is not implemented yet */ + tio_set(cmnd->tio, processed, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, + create_vendor_sense_rsp(cmnd, sense)); + } + break; + case GPCMD_SEND_KEY: + case GPCMD_SEND_OPC: + case GPCMD_SEND_CUE_SHEET: + case GPCMD_SET_STREAMING: + case GPCMD_SEND_DVD_STRUCTURE: + case WRITE_BUFFER: + case LOG_SELECT: + case SEND_DIAGNOSTIC: + case FORMAT_UNIT: + case WRITE_6: + case WRITE_10: + case WRITE_12: + case WRITE_16: + case SPACE_16: + res = raw_wait_req(sdev, cmnd->tio, scb, DMA_TO_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else + send_scsi_rsp(cmnd, build_generic_response); + break; + case MODE_SELECT: + res = raw_wait_req(sdev, cmnd->tio, scb, DMA_TO_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + tio = cmnd->tio; + if (((u8 *)page_address(tio->pvec[0]))[3]) { // SPC2 8.3.2 + u8 *data = (u8 *)page_address(tio->pvec[0]); + u32 len = (data[9] << 16) + (data[10] << 8) + data[11]; + + if (len) + lu->blk_shift = find_order(len); + } + send_scsi_rsp(cmnd, build_generic_response); + } + break; + case MODE_SELECT_10: + res = raw_wait_req(sdev, cmnd->tio, scb, DMA_TO_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + u16 *b16; + u8 *b8; + u32 len = 0; + + tio = cmnd->tio; + b16 = (u16 *)page_address(tio->pvec[0]); + b8 = (u8 *)page_address(tio->pvec[0]); + if (be16_to_cpu(b16[3])) { + if (b8[4] & 0x1) + len = (b8[20] << 24) + (b8[21] << 16) + (b8[22] << 8) + b8[23]; + else + len = (b8[13] << 16) + (b8[14] << 8) + b8[15]; + if (len) + lu->blk_shift = find_order(len); + } + send_scsi_rsp(cmnd, build_generic_response); + } + break; + case MODE_SENSE: + len = scb[4]; + tio = cmnd->tio = tio_alloc(!len ? 1 : get_pgcnt(len, 0)); + res = raw_wait_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + if (((u8 *)page_address(tio->pvec[0]))[3]) { + u8 *data = (u8 *)page_address(tio->pvec[0]); + u32 len = (data[9] << 16) + (data[10] << 8) + data[11]; + + if (len) + lu->blk_shift = find_order(len); + } + tio_set(tio, *(u8 *)page_address(tio->pvec[0]) + 1, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + } + break; + case MODE_SENSE_10: + len = (scb[7] << 8) + scb[8]; + tio = cmnd->tio = tio_alloc(!len ? 1 : get_pgcnt(len, 0)); + res = raw_wait_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + u16 *b16 = (u16 *)page_address(tio->pvec[0]); + u8 *b8 = (u8 *)page_address(tio->pvec[0]); + u32 len = 0; + + if (be16_to_cpu(b16[3])) { + if (b8[4] & 0x1) { // LONGLBA = 1 + assert(0); + } else + len = (b8[13] << 16) + (b8[14] << 8) + b8[15]; + if (len) + lu->blk_shift = find_order(len); + } + tio_set(tio, be16_to_cpu(*(u16 *)page_address(tio->pvec[0])) + 2, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + } + break; + case LOG_SENSE: + len = (scb[7] << 8) + scb[8]; + tio = cmnd->tio = tio_alloc(!len ? 1 : get_pgcnt(len, 0)); + res = raw_wait_req(sdev, tio, scb, DMA_FROM_DEVICE, sense, timeout); + if (res) + send_sense_rsp(cmnd, sense); + else { + if (scb[1] & 0x2) { + tio_set(tio, be16_to_cpu(*((u16 *)page_address(tio->pvec[0]) + 1)) + 4, 0); + } else + tio_set(tio, len, 0); + send_data_rsp_with_sense(cmnd, build_generic_response, NULL); + } + break; + case RESERVE: + case RELEASE: + case RESERVE_10: + case RELEASE_10: + send_scsi_rsp(cmnd, build_generic_response); + break; + default: + eprintk("raw_execute_cmnd miss command 0x%x\n", opcode); + send_data_rsp_with_sense(cmnd, build_fail_response, NULL); + break; + } + return 0; +} + +struct target_type raw_ops = +{ + .id = 1, + .execute_cmnd = raw_execute_cmnd, +}; + +char *show_scsi_command(const unsigned char cmd) +{ + char *what = NULL; + + switch (cmd) { + case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break; + case REZERO_UNIT: what = "REZERO_UNIT or REWIND"; break; + case REQUEST_SENSE: what = "REQUEST_SENSE"; break; + case FORMAT_UNIT: what = "FORMAT_UNIT"; break; + case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break; + case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break; + case READ_6: what = "READ_6"; break; + case WRITE_6: what = "WRITE_6"; break; + case SEEK_6: what = "SEEK_6 or SET_CAPACITY"; break; + case READ_REVERSE: what = "READ_REVERSE"; break; + case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break; + case WRITE_FILEMARKS_16: what = "WRITE_FILEMARKS_16"; break; + case SPACE: what = "SPACE"; break; + case SPACE_16: what = "SPACE_16"; break; + case INQUIRY: what = "INQUIRY"; break; + case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break; + case MODE_SELECT: what = "MODE_SELECT"; break; + case RESERVE: what = "RESERVE"; break; + case RELEASE: what = "RELEASE"; break; + case COPY: what = "COPY"; break; + case ERASE: what = "ERASE"; break; + case ERASE_16: what = "ERASE_16"; break; + case MODE_SENSE: what = "MODE_SENSE"; break; + case START_STOP: what = "START_STOP"; break; + case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break; + case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break; + case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break; + case SET_WINDOW: what = "SET_WINDOW"; break; + case READ_CAPACITY: what = "READ_CAPACITY"; break; + case READ_10: what = "READ_10"; break; + case WRITE_10: what = "WRITE_10"; break; + case SEEK_10: what = "SEEK_10 or LOCATE_10"; break; + case WRITE_VERIFY: what = "WRITE_VERIFY"; break; + case VERIFY: what = "VERIFY"; break; + case VERIFY_16: what = "VERIFY_16"; break; + case SEARCH_HIGH: what = "SEARCH_HIGH"; break; + case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break; + case SEARCH_LOW: what = "SEARCH_LOW"; break; + case SET_LIMITS: what = "SET_LIMITS"; break; + case READ_POSITION: what = "READ_POSITION"; break; + case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break; + case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break; + case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break; + case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break; + case COMPARE: what = "COMPARE"; break; + case COPY_VERIFY: what = "COPY_VERIFY"; break; + case WRITE_BUFFER: what = "WRITE_BUFFER"; break; + case READ_BUFFER: what = "READ_BUFFER"; break; + case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break; + case READ_LONG: what = "READ_LONG"; break; + case WRITE_LONG: what = "WRITE_LONG"; break; + case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break; + case WRITE_SAME: what = "WRITE_SAME"; break; + case GPCMD_READ_SUBCHANNEL: what = "READ SUBCHANNEL"; break; + case READ_TOC: what = "READ_TOC"; break; + case GPCMD_READ_HEADER: what = "READ HEADER or REPORT_DENSITY_SUPPORT"; break; + case GPCMD_PLAY_AUDIO_10: what = "PLAY AUDIO (10)"; break; + case GPCMD_PLAY_AUDIO_MSF: what = "PLAY AUDIO MSF"; break; + case GPCMD_GET_EVENT_STATUS_NOTIFICATION: + what = "GET EVENT/STATUS NOTIFICATION"; break; + case GPCMD_PAUSE_RESUME: what = "PAUSE/RESUME"; break; + case LOG_SELECT: what = "LOG_SELECT"; break; + case LOG_SENSE: what = "LOG_SENSE"; break; + case GPCMD_STOP_PLAY_SCAN: what = "STOP PLAY/SCAN"; break; + case GPCMD_READ_DISC_INFO: what = "READ DISC INFORMATION"; break; + case GPCMD_READ_TRACK_RZONE_INFO: + what = "READ TRACK INFORMATION"; break; + case GPCMD_RESERVE_RZONE_TRACK: what = "RESERVE TRACK"; break; + case GPCMD_SEND_OPC: what = "SEND OPC"; break; + case MODE_SELECT_10: what = "MODE_SELECT_10"; break; + case GPCMD_REPAIR_RZONE_TRACK: what = "REPAIR TRACK"; break; + case 0x59: what = "READ MASTER CUE"; break; + case MODE_SENSE_10: what = "MODE_SENSE_10"; break; + case GPCMD_CLOSE_TRACK: what = "CLOSE TRACK/SESSION"; break; + case GPCMD_READ_BUFFER_CAPACITY: what = "READ BUFFER CAPACITY"; break; + case GPCMD_SEND_CUE_SHEET: what = "SEND CUE SHEET"; break; + case GPCMD_BLANK: what = "BLANK"; break; + case MOVE_MEDIUM: what = "MOVE_MEDIUM or PLAY AUDIO (12)"; break; + case READ_12: what = "READ_12"; break; + case WRITE_12: what = "WRITE_12"; break; + case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break; + case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break; + case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break; + case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break; + case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break; + case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break; + case GPCMD_READ_CD_MSF: what = "READ CD MSF"; break; + case GPCMD_SCAN: what = "SCAN"; break; + case GPCMD_SET_SPEED: what = "SET CD SPEED"; break; + case GPCMD_MECHANISM_STATUS: what = "MECHANISM STATUS"; break; + case GPCMD_READ_CD: what = "READ CD"; break; + case GPCMD_GET_CONFIGURATION: what = "GET CONFIGURATION"; break; + case GPCMD_READ_DVD_STRUCTURE: what = "READ DVD STRUCTURE"; break; + case GPCMD_REPORT_KEY: what = "REPORT KEY"; break; + case REPORT_LUNS: what = "REPORT_LUNS"; break; + case GPCMD_GET_PERFORMANCE: what = "GET PERFORMANCE"; break; + case GPCMD_READ_FORMAT_CAPACITIES: what = "READ FORMAT CAPACITIES"; break; + case GPCMD_SEND_DVD_STRUCTURE: what = "SEND DVD STRUCTURE"; break; + case EXABYTE_INI_ELE_ST_WITH_RANGE: what = "EXABYTE INI ELE ST WITH RANGE"; break; + case 0xE1: what = "WRITE CONTINUE"; break; + case WRITE_LONG_2: what = "WRITE_LONG_2"; break; + default: what = "(unknown command)"; break; + } + return what; +} Index: kernel/param.c =================================================================== --- kernel/param.c (revision 91) +++ kernel/param.c (working copy) @@ -10,6 +10,7 @@ struct target_type *target_type_array[] = { &disk_ops, + &raw_ops, }; #define CHECK_PARAM(info, iparam, word, min, max) \ Index: kernel/target_disk.c =================================================================== --- kernel/target_disk.c (revision 91) +++ kernel/target_disk.c (working copy) @@ -222,11 +222,13 @@ static int build_inquiry_response(struct tio_set(tio, min_t(u8, tio->size, scb[4]), 0); if (!cmnd->lun) data[0] = TYPE_NO_LUN; + else + cmnd->lun->l_type = TYPE_DISK; return err; } -static int build_report_luns_response(struct iscsi_cmnd *cmnd) +int build_report_luns_response(struct iscsi_cmnd *cmnd) { struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd); struct tio *tio = cmnd->tio; @@ -360,7 +362,7 @@ static int build_sync_cache_response(str return tio_sync(cmnd->lun, NULL); } -static int build_generic_response(struct iscsi_cmnd *cmnd) +int build_generic_response(struct iscsi_cmnd *cmnd) { return 0; } Index: kernel/iotype.c =================================================================== --- kernel/iotype.c (revision 91) +++ kernel/iotype.c (working copy) @@ -79,6 +79,7 @@ static int unregister_iotype(struct ioty struct iotype *iotype_array[] = { &fileio, &nullio, + &rawio, }; int iotype_init(void) @@ -86,9 +87,11 @@ int iotype_init(void) int i, err; for (i = 0; i < ARRAY_SIZE(iotype_array); i++) { - if (!(err = register_iotype(iotype_array[i]))) + if (!(err = register_iotype(iotype_array[i]))) { eprintk("register %s\n", iotype_array[i]->name); - else { + if (iotype_array[i]->init) + iotype_array[i]->init(); + } else { eprintk("failed to register %s\n", iotype_array[i]->name); break; } @@ -101,6 +104,9 @@ void iotype_exit(void) { int i; - for (i = 0; i < ARRAY_SIZE(iotype_array); i++) + for (i = 0; i < ARRAY_SIZE(iotype_array); i++) { + if (iotype_array[i]->exit) + (*iotype_array[i]->exit)(); unregister_iotype(iotype_array[i]); + } } Index: kernel/iscsi.c =================================================================== --- kernel/iscsi.c (revision 91) +++ kernel/iscsi.c (working copy) @@ -237,7 +237,7 @@ static void iscsi_cmnd_init_write(struct iscsi_cmnds_init_write(&head); } -static void do_send_data_rsp(struct iscsi_cmnd *cmnd) +static void do_send_data_rsp(struct iscsi_cmnd *cmnd, struct iscsi_cmnd *check_cond) { struct iscsi_conn *conn = cmnd->conn; struct iscsi_cmnd *data_cmnd; @@ -255,7 +255,10 @@ static void do_send_data_rsp(struct iscs sn = 0; while (1) { - data_cmnd = iscsi_cmnd_create_rsp_cmnd(cmnd, size <= pdusize); + if (!size && check_cond) + break; + + data_cmnd = iscsi_cmnd_create_rsp_cmnd(cmnd, size <= pdusize && !check_cond); tio_get(tio); data_cmnd->tio = tio; rsp = (struct iscsi_data_in_hdr *)&data_cmnd->pdu.bhs; @@ -268,7 +271,9 @@ static void do_send_data_rsp(struct iscs if (size <= pdusize) { data_cmnd->pdu.datasize = size; - rsp->flags = ISCSI_FLG_FINAL | ISCSI_FLG_STATUS; + rsp->flags = ISCSI_FLG_FINAL; + if (!check_cond) + rsp->flags |= ISCSI_FLG_STATUS; scsisize = tio->size; if (scsisize < expsize) { @@ -294,6 +299,15 @@ static void do_send_data_rsp(struct iscs list_add_tail(&data_cmnd->list, &send); } + if (check_cond) { + rsp = (struct iscsi_data_in_hdr *)&check_cond->pdu.bhs; + + rsp->itt = req->itt; + rsp->ttt = cpu_to_be32(ISCSI_RESERVED_TAG); + rsp->data_sn = cpu_to_be32(sn + 1); + list_add_tail(&check_cond->list, &send); + } + iscsi_cmnds_init_write(&send); } @@ -350,6 +364,91 @@ static struct iscsi_cmnd *create_sense_r return rsp; } +static char *decode_sensekey(const unsigned char key) +{ + char *what = NULL; + + switch (key) { + case NO_SENSE: what = "NO_SENSE"; break; + case RECOVERED_ERROR: what = "RECOVERED_ERROR"; break; + case NOT_READY: what = "NOT_READY"; break; + case MEDIUM_ERROR: what = "MEDIUM_ERROR"; break; + case HARDWARE_ERROR: what = "HARDWARE_ERROR"; break; + case ILLEGAL_REQUEST: what = "ILLEGAL_REQUEST"; break; + case UNIT_ATTENTION: what = "UNIT_ATTENTION"; break; + case DATA_PROTECT: what = "DATA_PROTECT"; break; + case BLANK_CHECK: what = "BLANK_CHECK"; break; + case ABORTED_COMMAND: what = "ABORTED_COMMAND"; break; + case MISCOMPARE: what = "MISCOMPARE"; break; + default: what = "UNKOWN"; break; + } + return what; +} + +static void decode_sense_buffer(const unsigned char *sr_sense_buffer) +{ + unsigned char sense_key = 0, asc = 0, ascq = 0; + + if ((sr_sense_buffer[0] & 0x7f) == 0x70) { + sense_key = sr_sense_buffer[2] & 0xF; + asc = sr_sense_buffer[12]; + ascq = sr_sense_buffer[13]; + } else if ((sr_sense_buffer[0] & 0x7f) == 0x72) { + sense_key = sr_sense_buffer[1] & 0xF; + asc = sr_sense_buffer[2]; + ascq = sr_sense_buffer[3]; + } + eprintk("sense_key = 0x%02x(%s), asc = 0x%02x, ascq = 0x%02x\n", sense_key, + decode_sensekey(sense_key), asc, ascq); +} + +struct iscsi_cmnd *create_vendor_sense_rsp(struct iscsi_cmnd *req, const unsigned char *sr_sense_buffer) +{ + struct iscsi_cmnd *rsp; + struct iscsi_scsi_rsp_hdr *rsp_hdr; + struct tio *tio; + struct iscsi_sense_data *sense; + u16 sense_len = sr_sense_buffer[7] + 8; + + rsp = iscsi_cmnd_create_rsp_cmnd(req, 1); + + rsp_hdr = (struct iscsi_scsi_rsp_hdr *)&rsp->pdu.bhs; + rsp_hdr->opcode = ISCSI_OP_SCSI_RSP; + rsp_hdr->flags = ISCSI_FLG_FINAL; + rsp_hdr->response = ISCSI_RESPONSE_COMMAND_COMPLETED; + rsp_hdr->cmd_status = SAM_STAT_CHECK_CONDITION; + rsp_hdr->itt = cmnd_hdr(req)->itt; + + tio = rsp->tio = tio_alloc(1); + sense = (struct iscsi_sense_data *) page_address(tio->pvec[0]); + assert(sense); + clear_page(sense); + +#if 1 + if (req->lun) + eprintk("LUN %d : ", req->lun->lun); + else + printk("No LUN : "); + printk("cmd %s:\n", show_scsi_command(cmnd_hdr(req)->scb[0])); + decode_sense_buffer(sr_sense_buffer); +#endif + + assert(sense_len < PAGE_SIZE); + sense->length = cpu_to_be16(sense_len); + memcpy(sense->data, sr_sense_buffer, sense_len); + + rsp->pdu.datasize = sizeof(struct iscsi_sense_data) + sense_len; + tio->size = (rsp->pdu.datasize + 3) & -4; + tio->offset = 0; + + return rsp; +} + +void send_sense_rsp(struct iscsi_cmnd *req, const unsigned char *sr_sense_buffer) +{ + iscsi_cmnd_init_write(create_vendor_sense_rsp(req, sr_sense_buffer)); +} + void send_scsi_rsp(struct iscsi_cmnd *req, int (*func)(struct iscsi_cmnd *)) { struct iscsi_cmnd *rsp; @@ -381,7 +480,7 @@ void send_data_rsp(struct iscsi_cmnd *re switch (func(req)) { case 0: - do_send_data_rsp(req); + do_send_data_rsp(req, NULL); return; case -EIO: /* Medium Error/Unrecovered Read Error */ @@ -393,6 +492,20 @@ void send_data_rsp(struct iscsi_cmnd *re iscsi_cmnd_init_write(rsp); } +void send_data_rsp_with_sense(struct iscsi_cmnd *req, int (*func)(struct iscsi_cmnd *), + struct iscsi_cmnd *sense) +{ + struct iscsi_cmnd *rsp; + + if (func(req) < 0) { + rsp = create_sense_rsp(req, ILLEGAL_REQUEST, 0x24, 0x0); + iscsi_cmnd_init_write(rsp); + } else { + //printk("send_data_rsp_with_sense: %lx %lx\n", (unsigned long)req, (unsigned long)sense); + do_send_data_rsp(req, sense); + } +} + /** * Free a command. * Also frees the additional header. @@ -708,35 +821,104 @@ static int cmnd_recv_pdu(struct iscsi_co return 0; } -static void set_offset_and_length(struct iet_volume *lu, u8 *cmd, loff_t *off, u32 *len) +static inline void __set_offset_and_length__readcd12(struct iet_volume *lu, u8 *cmd, loff_t *off, u32 *len) { + u16 unit_len = 0; + assert(lu); + *off = 0; //FIXME??? + *len = (cmd[6] << 16) + (cmd[7] << 8) + cmd[8]; + if (cmd[9] & 0x80) + unit_len += 12; + if (cmd[9] & 0x40) + unit_len += 8; + if (cmd[9] & 0x20) + unit_len += 4; + if (cmd[9] & 0x10) { + switch ((cmd[1] & 0x1e) >> 2) { + case 5: // mode2 form 2 + unit_len += 2324; + break; + case 3: + unit_len += 2336; + break; + case 1: + unit_len += 2352; + break; + case 2: + case 4: + default: + unit_len += 2048; + } + } + if (cmd[9] & 0x08) + unit_len += 280; + *len *= unit_len; +} + +static void set_offset_and_length(struct iet_volume *lu, u8 *cmd, loff_t *off, u32 *len) +{ + loff_t o = 0; + u32 l = 0; + u32 type = lu->l_type; + switch (cmd[0]) { case READ_6: + case READ_REVERSE: case WRITE_6: - *off = ((cmd[1] & 0x1f) << 16) + (cmd[2] << 8) + cmd[3]; - *len = cmd[4]; - if (!*len) - *len = 256; + if (type == TYPE_TAPE) { + o = 0; + l = (cmd[2] << 16) + (cmd[3] << 8) + cmd[4]; + } else { + o = ((cmd[1] & 0x1f) << 16) + (cmd[2] << 8) + cmd[3]; + l = cmd[4]; + if (unlikely(!l)) + l = 256; + } break; case READ_10: case WRITE_10: case WRITE_VERIFY: - *off = be32_to_cpu(*(u32 *)&cmd[2]); - *len = (cmd[7] << 8) + cmd[8]; + o = be32_to_cpu(*(u32 *)&cmd[2]); + l = (cmd[7] << 8) + cmd[8]; + break; + case READ_12: + case WRITE_12: + { + u16 *t = (u16 *)cmd; + + o = (be16_to_cpu(t[1]) << 16) + be16_to_cpu(t[2]); + l = (be16_to_cpu(t[3]) << 16) + be16_to_cpu(t[4]); break; + } + case GPCMD_READ_CD: + __set_offset_and_length__readcd12(lu, cmd, off, len); + return; case READ_16: case WRITE_16: - *off = be64_to_cpu(*(u64 *)&cmd[2]); - *len = be32_to_cpu(*(u32 *)&cmd[10]); + if (type == TYPE_TAPE) { + o = 0; + l = (cmd[12] << 16) + (cmd[13] << 8) + cmd[14]; + } else { + o = be64_to_cpu(*(u64 *)&cmd[2]); + l = be32_to_cpu(*(u32 *)&cmd[10]); + } break; default: BUG(); } - *off <<= lu->blk_shift; - *len <<= lu->blk_shift; + if (type == TYPE_TAPE) { + if (cmd[1] & 0x1) + l <<= lu->blk_shift; + } else { + o <<= lu->blk_shift; + l <<= lu->blk_shift; + } + + *off = o; + *len = l; } static u32 translate_lun(u16 * data) @@ -896,11 +1078,34 @@ static u32 get_next_ttt(struct iscsi_ses return cpu_to_be32(ttt); } +static void __data_out_cmnd_start_default(struct iscsi_conn *conn, struct iscsi_cmnd *req, u32 length) +{ + struct iscsi_scsi_cmd_hdr *req_hdr = cmnd_hdr(req); + + req->r2t_length = be32_to_cpu(req_hdr->data_length) - req->pdu.datasize; + req->is_unsolicited_data = !(req_hdr->flags & ISCSI_CMD_FINAL); + req->target_task_tag = get_next_ttt(conn->session); + + /* FIXME: some dumb SCSI CDB has length = 0 and do not think that is an error. a work around here. */ + req->tio = tio_alloc(length ? get_pgcnt(length, 0) : 1); + tio_set(req->tio, length, 0); + + if (req->pdu.datasize) { + cmnd_recv_pdu(conn, req->tio, 0, req->pdu.datasize); + } +} + static void scsi_cmnd_start(struct iscsi_conn *conn, struct iscsi_cmnd *req) { struct iscsi_scsi_cmd_hdr *req_hdr = cmnd_hdr(req); + u32 length; +#if 0 + if (req_hdr->scb[0] != TEST_UNIT_READY) /* skip TEST_UNIT_READY, too many. */ + printk("start cmnd %s(0x%02x)\n", show_scsi_command(req_hdr->scb[0]), req_hdr->scb[0]); +#else dprintk(D_GENERIC, "scsi command: %02x\n", req_hdr->scb[0]); +#endif req->lun = volume_get(conn->session->target, translate_lun(req_hdr->lun)); if (!req->lun) { @@ -935,6 +1140,52 @@ static void scsi_cmnd_start(struct iscsi case RELEASE: case RESERVE_10: case RELEASE_10: + /* MMC/SSC/SMC */ + case RECEIVE_DIAGNOSTIC: + case READ_BLOCK_LIMITS: + case READ_POSITION: + case REZERO_UNIT: + case ALLOW_MEDIUM_REMOVAL: + case ERASE: + case ERASE_16: + case SPACE: + case WRITE_FILEMARKS: + case WRITE_FILEMARKS_16: + case SEEK_6: + case SEEK_10: // LOCATE_10 + case MODE_SENSE_10: + case READ_TOC: + case GPCMD_READ_FORMAT_CAPACITIES: + case GPCMD_READ_SUBCHANNEL: + case GPCMD_GET_CONFIGURATION: + case GPCMD_REPORT_KEY: + case LOG_SENSE: + case READ_BUFFER: + case GPCMD_READ_DVD_STRUCTURE: + case GPCMD_READ_DISC_INFO: + case GPCMD_SET_SPEED: + case GPCMD_READ_TRACK_RZONE_INFO: + case GPCMD_BLANK: + case GPCMD_STOP_PLAY_SCAN: + case GPCMD_GET_PERFORMANCE: + case GPCMD_GET_EVENT_STATUS_NOTIFICATION: + case GPCMD_PAUSE_RESUME: + case GPCMD_PLAY_AUDIO_MSF: + case GPCMD_CLOSE_TRACK: + case GPCMD_MECHANISM_STATUS: + case GPCMD_READ_BUFFER_CAPACITY: + case GPCMD_RESERVE_RZONE_TRACK: + case GPCMD_REPAIR_RZONE_TRACK: + case GPCMD_PLAY_AUDIO_10: + case GPCMD_LOAD_UNLOAD: + case GPCMD_SET_READ_AHEAD: + case GPCMD_SCAN: + case READ_ELEMENT_STATUS: + case MOVE_MEDIUM: + case REASSIGN_BLOCKS: + case EXABYTE_INI_ELE_ST_WITH_RANGE: + case RECOVER_BUFFERED_DATA: + case REPORT_DENSITY_SUPPORT: { if (!(req_hdr->flags & ISCSI_CMD_FINAL) || req->pdu.datasize) { /* unexpected unsolicited data */ @@ -947,6 +1198,9 @@ static void scsi_cmnd_start(struct iscsi case READ_6: case READ_10: case READ_16: + case READ_REVERSE: + case READ_12: + case GPCMD_READ_CD: { loff_t offset; u32 length; @@ -968,6 +1222,7 @@ static void scsi_cmnd_start(struct iscsi case WRITE_10: case WRITE_16: case WRITE_VERIFY: + case WRITE_12: { struct iscsi_sess_param *param = &conn->session->param; loff_t offset; @@ -1005,6 +1260,77 @@ static void scsi_cmnd_start(struct iscsi } break; } + case FORMAT_UNIT: + { + u32 type = req->lun->l_type; + + switch (type) { + case TYPE_DISK: + case TYPE_WORM: + case TYPE_ROM: + /* quick hack, hope one PDU hold all information. */ + __data_out_cmnd_start_default(conn, req, req->pdu.datasize); + break; + case TYPE_TAPE: + length = (req_hdr->scb[3] << 8) + req_hdr->scb[4]; + __data_out_cmnd_start_default(conn, req, length); + break; + default: + eprintk("unkown type %d\n", type); + goto error; + } + break; + } + case WRITE_BUFFER: + case GPCMD_SEND_CUE_SHEET: + length = (req_hdr->scb[6] << 16) + (req_hdr->scb[7] << 8) + req_hdr->scb[8]; + __data_out_cmnd_start_default(conn, req, length); + break; + case GPCMD_SEND_KEY: + { + u32 type = req->lun->l_type; + + if (type == TYPE_TAPE || type == TYPE_DISK) { + if (!(req_hdr->flags & ISCSI_CMD_FINAL) || req->pdu.datasize) { + /* unexpected unsolicited data */ + eprintk("%x %x\n", cmnd_itt(req), req_hdr->scb[0]); + create_sense_rsp(req, ABORTED_COMMAND, 0xc, 0xc); + cmnd_skip_data(req); + } + } else if (type == TYPE_ROM) { + length = (req_hdr->scb[8] << 8) + req_hdr->scb[9]; + __data_out_cmnd_start_default(conn, req, length); + } else { + eprintk("unprocessed device type %x", type); + } + break; + } + case GPCMD_SEND_DVD_STRUCTURE: + length = (req_hdr->scb[8] << 8) + req_hdr->scb[9]; + __data_out_cmnd_start_default(conn, req, length); + break; + case MODE_SELECT_10: + case LOG_SELECT: + case GPCMD_SEND_OPC: + length = (req_hdr->scb[7] << 8) + req_hdr->scb[8]; + __data_out_cmnd_start_default(conn, req, length); + break; + case GPCMD_SET_STREAMING: + length = (req_hdr->scb[9] << 8) + req_hdr->scb[10]; + __data_out_cmnd_start_default(conn, req, length); + break; + case MODE_SELECT: + length = req_hdr->scb[4]; + __data_out_cmnd_start_default(conn, req, length); + break; + case SEND_DIAGNOSTIC: + length = (req_hdr->scb[3] << 8) + req_hdr->scb[4]; + __data_out_cmnd_start_default(conn, req, length); + break; + case SPACE_16: + length = (req_hdr->scb[12] << 8) + req_hdr->scb[13]; + __data_out_cmnd_start_default(conn, req, length); + break; error: default: eprintk("Unsupported %x\n", req_hdr->scb[0]); Index: kernel/iotype.h =================================================================== --- kernel/iotype.h (revision 91) +++ kernel/iotype.h (working copy) @@ -17,10 +17,13 @@ struct iotype { int (*sync)(struct iet_volume *dev, struct tio *tio); void (*detach)(struct iet_volume *dev); void (*show)(struct iet_volume *dev, struct seq_file *seq); + int (*init)(void); + void (*exit)(void); }; extern struct iotype fileio; extern struct iotype nullio; +extern struct iotype rawio; extern int iotype_init(void); extern void iotype_exit(void); Index: kernel/iscsi.h =================================================================== --- kernel/iscsi.h (revision 91) +++ kernel/iscsi.h (working copy) @@ -12,6 +12,7 @@ #include #include #include +#include #include "iscsi_hdr.h" #include "iet_u.h" @@ -144,6 +145,8 @@ struct iet_volume { struct iotype *iotype; void *private; + /* FIXME: reorganized the member */ + u32 l_type; }; enum lu_flags { @@ -281,6 +284,9 @@ extern void cmnd_tx_end(struct iscsi_cmn extern void cmnd_release(struct iscsi_cmnd *, int); extern void send_data_rsp(struct iscsi_cmnd *, int (*)(struct iscsi_cmnd *)); extern void send_scsi_rsp(struct iscsi_cmnd *, int (*)(struct iscsi_cmnd *)); +extern void send_data_rsp_with_sense(struct iscsi_cmnd *req, int (*func)(struct iscsi_cmnd *), struct iscsi_cmnd *sense); +extern void send_sense_rsp(struct iscsi_cmnd *scsi_cmnd, const unsigned char *sr_sense_buffer); +extern struct iscsi_cmnd *create_vendor_sense_rsp(struct iscsi_cmnd *req, const unsigned char *sr_sense_buffer); /* conn.c */ extern struct iscsi_conn *conn_lookup(struct iscsi_session *, u16); @@ -350,6 +356,16 @@ extern int iscsi_param_set(struct iscsi_ /* target_disk.c */ extern struct target_type disk_ops; +extern int build_generic_response(struct iscsi_cmnd *cmnd); +extern int build_report_luns_response(struct iscsi_cmnd *cmnd); + +/* target_raw.c */ +extern struct target_type raw_ops; +extern u32 find_order(u32 size); +extern char *show_scsi_command(const unsigned char cmd); + +/* raw-io.c */ +extern struct scsi_device *get_raw_sdev(struct iet_volume *lu); /* event.c */ extern int event_send(u32, u64, u32, u32, int); @@ -435,4 +451,25 @@ enum cmnd_flags { #define PRODUCT_ID "VIRTUAL-DISK" #define PRODUCT_REV "0" +/* extra SCSI cmd opcode */ +#ifndef ERASE_16 +#define ERASE_16 0x93 +#endif + +#ifndef SPACE_16 +#define SPACE_16 0x91 +#endif + +#ifndef WRITE_FILEMARKS_16 +#define WRITE_FILEMARKS_16 0x80 +#endif + +#ifndef EXABYTE_INI_ELE_ST_WITH_RANGE +#define EXABYTE_INI_ELE_ST_WITH_RANGE 0xe7 +#endif + +#ifndef REPORT_DENSITY_SUPPORT +#define REPORT_DENSITY_SUPPORT 0x44 +#endif + #endif /* __ISCSI_H__ */ Index: kernel/target.c =================================================================== --- kernel/target.c (revision 91) +++ kernel/target.c (working copy) @@ -266,7 +266,8 @@ int iet_info_show(struct seq_file *seq, return err; list_for_each_entry(target, &target_list, t_list) { - seq_printf(seq, "tid:%u name:%s\n", target->tid, target->name); + seq_printf(seq, "tid:%u type:%u name:%s\n", target->tid, + target->trgt_param.target_type, target->name); if ((err = target_lock(target, 1)) < 0) break; Index: kernel/Makefile =================================================================== --- kernel/Makefile (revision 91) +++ kernel/Makefile (working copy) @@ -12,5 +12,6 @@ EXTRA_CFLAGS += -I$(src)/../include obj-m += iscsi_trgt.o iscsi_trgt-objs := tio.o iscsi.o nthread.o wthread.o config.o digest.o \ conn.o session.o target.o volume.o iotype.o \ - file-io.o null-io.o target_disk.o event.o param.o + file-io.o null-io.o target_disk.o event.o param.o \ + raw-io.o target_raw.o Index: etc/ietd.conf =================================================================== --- etc/ietd.conf (revision 91) +++ etc/ietd.conf (working copy) @@ -57,3 +57,11 @@ Target iqn.2001-04.com.example:storage.d #DataDigest CRC32C,None # various target parameters #Wthreads 8 + +Target iqn.2001-04.com.example:storage.raw + # Must give complete HCIL here, get the HCIL from /proc/scsi/scsi or sysfs. + # MUST specify Type as rawio + Lun 0 H=0,C=0,I=1,L=2,Type=rawio + # MUST specify Type as 1 + Type 1 +