diff --git a/examples/iscsiclient.c b/examples/iscsiclient.c index 102d5a3..006cd04 100644 --- a/examples/iscsiclient.c +++ b/examples/iscsiclient.c @@ -13,7 +13,7 @@ */ /* This is the host/port we connect to.*/ -#define TARGET "10.1.1.27:3260" +#define TARGET "127.0.0.1:3260" #include #include @@ -210,10 +210,12 @@ void modesense6_cb(struct iscsi_context *iscsi, int status, void *command_data, { struct client_state *clnt = (struct client_state *)private_data; struct scsi_task *task = command_data; + struct scsi_mode_sense *ms; int full_size; if (status == SCSI_STATUS_CHECK_CONDITION) { printf("Modesense6 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq); + exit(10); } else { full_size = scsi_datain_getfullsize(task); if (full_size > task->datain.size) { @@ -227,7 +229,13 @@ void modesense6_cb(struct iscsi_context *iscsi, int status, void *command_data, return; } - printf("MODESENSE6 successful.\n"); + } + printf("MODESENSE6 successful.\n"); + ms = scsi_datain_unmarshall(task); + if (ms == NULL) { + printf("failed to unmarshall mode sense datain blob\n"); + scsi_free_scsi_task(task); + exit(10); } printf("Send READCAPACITY10\n"); diff --git a/include/scsi-lowlevel.h b/include/scsi-lowlevel.h index 21a78bb..8eb3c95 100644 --- a/include/scsi-lowlevel.h +++ b/include/scsi-lowlevel.h @@ -379,8 +379,90 @@ enum scsi_modesense_page_control { SCSI_MODESENSE_PC_SAVED = 0x03 }; +struct scsi_mode_page_caching { + int ic; + int abpf; + int cap; + int disc; + int size; + int wce; + int mf; + int rcd; + + int demand_read_retention_priority; + int write_retention_priority; + + int disable_prefetch_transfer_length; + int minimum_prefetch; + int maximum_prefetch; + int maximum_prefetch_ceiling; + + int fsw; + int lbcss; + int dra; + int nv_dis; + + int number_of_cache_segments; + int cache_segment_size; +}; + +struct scsi_mode_page_disconnect_reconnect { + int buffer_full_ratio; + int buffer_empty_ratio; + int bus_inactivity_limit; + int disconnect_time_limit; + int connect_time_limit; + int maximum_burst_size; + int emdp; + int fair_arbitration; + int dimm; + int dtdc; + int first_burst_size; +}; + +struct scsi_mode_page_informational_exceptions_control { + int perf; + int ebf; + int ewasc; + int dexcpt; + int test; + int ebackerr; + int logerr; + int mrie; + int interval_timer; + int report_count; +}; + enum scsi_modesense_page_code { - SCSI_MODESENSE_PAGECODE_RETURN_ALL_PAGES = 0x3f + SCSI_MODESENSE_PAGECODE_READ_WRITE_ERROR_RECOVERY = 0x01, + SCSI_MODESENSE_PAGECODE_DISCONNECT_RECONNECT = 0x02, + SCSI_MODESENSE_PAGECODE_VERIFY_ERROR_RECOVERY = 0x07, + SCSI_MODESENSE_PAGECODE_CACHING = 0x08, + SCSI_MODESENSE_PAGECODE_XOR_CONTROL = 0x10, + SCSI_MODESENSE_PAGECODE_INFORMATIONAL_EXCEPTIONS_CONTROL = 0x1c, + SCSI_MODESENSE_PAGECODE_RETURN_ALL_PAGES = 0x3f +}; + +struct scsi_mode_page { + struct scsi_mode_page *next; + int ps; + int spf; + enum scsi_modesense_page_code page_code; + int subpage_code; + int len; + union { + struct scsi_mode_page_caching caching; + struct scsi_mode_page_disconnect_reconnect disconnect_reconnect; + struct scsi_mode_page_informational_exceptions_control iec; + }; +}; + +struct scsi_mode_sense { + uint8_t mode_data_length; + uint8_t medium_type; + uint8_t device_specific_parameter; + uint8_t block_descriptor_length; + struct scsi_mode_page *pages; }; struct scsi_task *scsi_cdb_modesense6(int dbd, diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index 92503c0..18fe17a 100644 --- a/lib/scsi-lowlevel.c +++ b/lib/scsi-lowlevel.c @@ -677,6 +677,138 @@ scsi_modesense6_datain_getfullsize(struct scsi_task *task) return len; } +static void +scsi_parse_mode_caching(struct scsi_task *task, int pos, struct scsi_mode_page *mp) +{ + mp->caching.ic = task->datain.data[pos] & 0x80; + mp->caching.abpf = task->datain.data[pos] & 0x40; + mp->caching.cap = task->datain.data[pos] & 0x20; + mp->caching.disc = task->datain.data[pos] & 0x10; + mp->caching.size = task->datain.data[pos] & 0x08; + mp->caching.wce = task->datain.data[pos] & 0x04; + mp->caching.mf = task->datain.data[pos] & 0x02; + mp->caching.rcd = task->datain.data[pos] & 0x01; + + mp->caching.demand_read_retention_priority = (task->datain.data[pos+1] >> 4) & 0x0f; + mp->caching.write_retention_priority = task->datain.data[pos+1] & 0x0f; + + mp->caching.disable_prefetch_transfer_length = htons(*(uint16_t *)&(task->datain.data[pos+2])); + mp->caching.minimum_prefetch = htons(*(uint16_t *)&(task->datain.data[pos+4])); + mp->caching.maximum_prefetch = htons(*(uint16_t *)&(task->datain.data[pos+6])); + mp->caching.maximum_prefetch_ceiling = htons(*(uint16_t *)&(task->datain.data[pos+8])); + + mp->caching.fsw = task->datain.data[pos+10] & 0x80; + mp->caching.lbcss = task->datain.data[pos+10] & 0x40; + mp->caching.dra = task->datain.data[pos+10] & 0x20; + mp->caching.nv_dis = task->datain.data[pos+10] & 0x01; + + mp->caching.number_of_cache_segments = task->datain.data[pos+11]; + mp->caching.cache_segment_size = htons(*(uint16_t *)&(task->datain.data[pos+12])); +} + +static void +scsi_parse_mode_disconnect_reconnect(struct scsi_task *task, int pos, struct scsi_mode_page *mp) +{ + mp->disconnect_reconnect.buffer_full_ratio = task->datain.data[pos]; + mp->disconnect_reconnect.buffer_empty_ratio = task->datain.data[pos+1]; + mp->disconnect_reconnect.bus_inactivity_limit = htons(*(uint16_t *)&(task->datain.data[pos+2])); + mp->disconnect_reconnect.disconnect_time_limit = htons(*(uint16_t *)&(task->datain.data[pos+4])); + mp->disconnect_reconnect.connect_time_limit = htons(*(uint16_t *)&(task->datain.data[pos+6])); + mp->disconnect_reconnect.maximum_burst_size = htons(*(uint16_t *)&(task->datain.data[pos+8])); + mp->disconnect_reconnect.emdp = task->datain.data[pos+10] & 0x80; + mp->disconnect_reconnect.fair_arbitration = (task->datain.data[pos+10]>>4) & 0x0f; + mp->disconnect_reconnect.dimm = task->datain.data[pos+10] & 0x08; + mp->disconnect_reconnect.dtdc = task->datain.data[pos+10] & 0x07; + mp->disconnect_reconnect.first_burst_size = htons(*(uint16_t *)&(task->datain.data[pos+12])); +} + +static void +scsi_parse_mode_informational_exceptions_control(struct scsi_task *task, int pos, struct scsi_mode_page *mp) +{ + mp->iec.perf = task->datain.data[pos] & 0x80; + mp->iec.ebf = task->datain.data[pos] & 0x20; + mp->iec.ewasc = task->datain.data[pos] & 0x10; + mp->iec.dexcpt = task->datain.data[pos] & 0x08; + mp->iec.test = task->datain.data[pos] & 0x04; + mp->iec.ebackerr = task->datain.data[pos] & 0x02; + mp->iec.logerr = task->datain.data[pos] & 0x01; + mp->iec.mrie = task->datain.data[pos+1] & 0x0f; + mp->iec.interval_timer = htonl(*(uint32_t *)&(task->datain.data[pos+2])); + mp->iec.report_count = htonl(*(uint32_t *)&(task->datain.data[pos+6])); +} + + +/* + * parse and unmarshall the mode sense data in buffer + */ +static struct scsi_mode_sense * +scsi_modesense_datain_unmarshall(struct scsi_task *task) +{ + struct scsi_mode_sense *ms; + int pos; + + if (task->datain.size < 4) { + return NULL; + } + + ms = scsi_malloc(task, sizeof(struct scsi_mode_sense)); + if (ms == NULL) { + return NULL; + } + + ms->mode_data_length = task->datain.data[0]; + ms->medium_type = task->datain.data[1]; + ms->device_specific_parameter = task->datain.data[2]; + ms->block_descriptor_length = task->datain.data[3]; + ms->pages = NULL; + + if (ms->mode_data_length + 1 > task->datain.size) { + return NULL; + } + + pos = 4 + ms->block_descriptor_length; + while (pos < task->datain.size) { + struct scsi_mode_page *mp; + + mp = scsi_malloc(task, sizeof(struct scsi_mode_page)); + if (mp == NULL) { + return ms; + } + mp->ps = task->datain.data[pos] & 0x80; + mp->spf = task->datain.data[pos] & 0x40; + mp->page_code = task->datain.data[pos] & 0x3f; + pos++; + + if (mp->spf) { + mp->subpage_code = task->datain.data[pos++]; + mp->len = ntohs(*(uint16_t *)&task->datain.data[pos]); + pos += 2; + } else { + mp->subpage_code = 0; + mp->len = task->datain.data[pos++]; + } + + switch (mp->page_code) { + case SCSI_MODESENSE_PAGECODE_CACHING: + scsi_parse_mode_caching(task, pos, mp); + break; + case SCSI_MODESENSE_PAGECODE_DISCONNECT_RECONNECT: + scsi_parse_mode_disconnect_reconnect(task, pos, mp); + break; + case SCSI_MODESENSE_PAGECODE_INFORMATIONAL_EXCEPTIONS_CONTROL: + scsi_parse_mode_informational_exceptions_control(task, pos, mp); + break; + } + + mp->next = ms->pages; + ms->pages = mp; + + pos += mp->len; + } + + return ms; +} + /* * SYNCHRONIZECACHE10 @@ -740,6 +872,8 @@ scsi_datain_unmarshall(struct scsi_task *task) return NULL; case SCSI_OPCODE_INQUIRY: return scsi_inquiry_datain_unmarshall(task); + case SCSI_OPCODE_MODESENSE6: + return scsi_modesense_datain_unmarshall(task); case SCSI_OPCODE_READCAPACITY10: return scsi_readcapacity10_datain_unmarshall(task); case SCSI_OPCODE_SYNCHRONIZECACHE10: