From 6fd00f80e8ebb9c1d4fa17caa9db128bad9a7f1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Wed, 9 Apr 2014 20:35:26 +0200 Subject: [PATCH] scsi_periph: implemented VPD inquiry. * declared block limits and logical block provisioning structs. * based on SPC-4 and SBC-4 standards. --- headers/private/drivers/scsi_cmds.h | 75 ++++++++++++++++++- src/add-ons/kernel/generic/scsi_periph/io.cpp | 64 ++++++++++++++++ 2 files changed, 136 insertions(+), 3 deletions(-) diff --git a/headers/private/drivers/scsi_cmds.h b/headers/private/drivers/scsi_cmds.h index 732583d9ba..8ad2a0b0ac 100644 --- a/headers/private/drivers/scsi_cmds.h +++ b/headers/private/drivers/scsi_cmds.h @@ -309,10 +309,28 @@ enum scsi_device_type { }; +// vital product data: pages +#define SCSI_PAGE_SUPPORTED_VPD 0x00 /* Supported VPD Pages */ +#define SCSI_PAGE_USN 0x80 /* Unit serial number */ +#define SCSI_PAGE_BLOCK_LIMITS 0xb0 /* Block limits */ +#define SCSI_PAGE_BLOCK_DEVICE_CHARS 0xb1 /* Block device characteristics */ +#define SCSI_PAGE_LB_PROVISIONING 0xb2 /* Logical block provisioning */ +#define SCSI_PAGE_REFERRALS 0xb3 /* Referrals */ + +// vital product data: supported pages +typedef struct scsi_page_list { + LBITFIELD8_2( + device_type : 5, + device_qualifier : 3 + ); + uint8 page_code; + uint8 _res2; + + uint8 page_length; + uint8 pages[1]; // size according to page_length +} _PACKED scsi_page_list; + // vital product data: unit serial number page - -#define SCSI_PAGE_USN 0x80 - typedef struct scsi_page_usn { LBITFIELD8_2( device_type : 5, @@ -325,6 +343,57 @@ typedef struct scsi_page_usn { char psn[1]; // size according to page_length } _PACKED scsi_page_usn; +typedef struct scsi_page_block_limits { + LBITFIELD8_2( + device_type : 5, + device_qualifier : 3 + ); + uint8 page_code; + + uint16 _page_length; + LBITFIELD8_2( + wsnz : 1, + _res4_1 : 7 + ); + uint8 max_cmp_write_length; + uint16 opt_transfer_length_grain; + uint32 max_transfer_length; + uint32 opt_transfer_length; + uint32 max_prefetch_length; + uint32 max_unmap_lba_count; + uint32 max_unmap_blk_count; + uint32 opt_unmap_grain; + uint32 unmap_grain_align; + uint64 max_write_same_length; + uint8 _res44[20]; +} _PACKED scsi_page_block_limits; + +typedef struct scsi_page_lb_provisioning { + LBITFIELD8_2( + device_type : 5, + device_qualifier : 3 + ); + uint8 page_code; + + uint16 page_length; + uint8 threshold_exponent; + LBITFIELD8_7( + dp : 1, + anc_sup : 1, + lbprz : 1, + _res5_3 : 2, + lbpws10 : 1, + lbpws : 1, + lbpu : 1 + ); + LBITFIELD8_2( + provisioning_type : 3, + _res6_3 : 5 + ); + uint8 _res7; +} _PACKED scsi_page_lb_provisioning; + + // READ CAPACITY (10) typedef struct scsi_cmd_read_capacity { diff --git a/src/add-ons/kernel/generic/scsi_periph/io.cpp b/src/add-ons/kernel/generic/scsi_periph/io.cpp index c6d3f115ab..5f044e5296 100644 --- a/src/add-ons/kernel/generic/scsi_periph/io.cpp +++ b/src/add-ons/kernel/generic/scsi_periph/io.cpp @@ -31,6 +31,70 @@ inquiry(scsi_periph_device_info *device, scsi_inquiry *inquiry) } +static status_t +vpd_page_inquiry(scsi_periph_device_info *device, uint8 page, void* data, + uint16 length) +{ + SHOW_FLOW0(0, ""); + + scsi_ccb* ccb = device->scsi->alloc_ccb(device->scsi_device); + if (ccb == NULL) + return B_NO_MEMORY; + + scsi_cmd_inquiry *cmd = (scsi_cmd_inquiry *)ccb->cdb; + memset(cmd, 0, sizeof(scsi_cmd_inquiry)); + cmd->opcode = SCSI_OP_INQUIRY; + cmd->lun = ccb->target_lun; + cmd->evpd = 1; + cmd->page_code = page; + cmd->allocation_length = length; + + ccb->flags = SCSI_DIR_IN; + ccb->cdb_length = sizeof(scsi_cmd_inquiry); + + ccb->sort = -1; + ccb->timeout = device->std_timeout; + + ccb->data = (uint8*)data; + ccb->sg_list = NULL; + ccb->data_length = length; + + status_t status = periph_safe_exec(device, ccb); + + device->scsi->free_ccb(ccb); + + return status; +} + + +static status_t +vpd_page_get(scsi_periph_device_info *device, uint8 page, void* data, + uint16 length) +{ + SHOW_FLOW0(0, ""); + + status_t status = vpd_page_inquiry(device, 0, data, length); + if (status != B_OK) + return status; // or B_BAD_VALUE + + if (page == 0) + return B_OK; + + scsi_page_list *list_data = (scsi_page_list*)data; + int page_length = min_c(list_data->page_length, length - + offsetof(scsi_page_list, pages)); + for (int i = 0; i < page_length; i++) { + if (list_data->pages[i] == page) + return vpd_page_inquiry(device, page, data, length); + } + + // TODO buffer might be not big enough + + return B_BAD_VALUE; +} + + + static status_t prevent_allow(scsi_periph_device_info *device, bool prevent) {