1 /** 2 * 3 * TODO: description 4 * 5 * This file is a part of USB SCSI CAM for Haiku. 6 * May be used under terms of the MIT License 7 * 8 * Author(s): 9 * Siarzhuk Zharski <imker@gmx.li> 10 * 11 * 12 */ 13 /** bulk-only protocol specific implementation */ 14 15 /* References: 16 * USB Mass Storage Class specifications: 17 * http://www.usb.org/developers/data/devclass/usbmassover_11.pdf [1] 18 * http://www.usb.org/developers/data/devclass/usbmassbulk_10.pdf [2] 19 */ 20 #include "usb_scsi.h" 21 22 #include "device_info.h" 23 24 #include "proto_module.h" 25 #include "proto_common.h" 26 #include "proto_bulk.h" 27 28 #include "usb_defs.h" 29 30 #include <string.h> /* strncpy */ 31 32 /*Bulk-Only protocol specifics*/ 33 #define USB_REQ_MS_RESET 0xff /* Bulk-Only reset */ 34 #define USB_REQ_MS_GET_MAX_LUN 0xfe /* Get maximum lun */ 35 36 /* Command Block Wrapper */ 37 typedef struct _usb_mass_CBW{ 38 uint32 signature; 39 uint32 tag; 40 uint32 data_transfer_len; 41 uint8 flags; 42 uint8 lun; 43 uint8 cdb_len; 44 #define CBW_CDB_LENGTH 16 45 uint8 CDB[CBW_CDB_LENGTH]; 46 } usb_mass_CBW; /*sizeof(usb_mass_CBW) must be 31*/ 47 #define CBW_LENGTH 0x1f 48 49 /* Command Status Wrapper */ 50 typedef struct _usb_mass_CSW{ 51 uint32 signature; 52 uint32 tag; 53 uint32 data_residue; 54 uint8 status; 55 } usb_mass_CSW; /*sizeof(usb_mass_CSW) must be 13*/ 56 #define CSW_LENGTH 0x0d 57 58 #define CSW_STATUS_GOOD 0x0 59 #define CSW_STATUS_FAILED 0x1 60 #define CSW_STATUS_PHASE 0x2 61 62 #define CBW_SIGNATURE 0x43425355 63 #define CSW_SIGNATURE 0x53425355 64 65 #define CBW_FLAGS_OUT 0x00 66 #define CBW_FLAGS_IN 0x80 67 68 static void trace_CBW(usb_device_info *udi, const usb_mass_CBW *cbw); 69 static void trace_CSW(usb_device_info *udi, const usb_mass_CSW *csw); 70 static status_t bulk_only_initialize(usb_device_info *udi); 71 static status_t bulk_only_reset(usb_device_info *udi); 72 static void bulk_only_transfer(usb_device_info *udi, uint8 *cmd, 73 uint8 cmdlen, //sg_buffer *sgb, 74 iovec *sg_data, int32 sg_count, 75 int32 transfer_len, EDirection dir, 76 CCB_SCSIIO *ccbio, ud_transfer_callback cb); 77 78 /*=========================== tracing helpers ==================================*/ 79 void trace_CBW(usb_device_info *udi, const usb_mass_CBW *cbw) 80 { 81 char buf[sizeof(uint32) + 1] = {0}; 82 strncpy(buf, (char *)&cbw->signature, sizeof(uint32)); 83 PTRACE(udi, "\nCBW:{'%s'; tag:%d; data_len:%d; flags:0x%02x; lun:%d; cdb_len:%d;}\n", 84 buf, cbw->tag, cbw->data_transfer_len, 85 cbw->flags, cbw->lun, cbw->cdb_len); 86 udi->trace_bytes("CDB:\n", cbw->CDB, CBW_CDB_LENGTH); 87 } 88 89 void trace_CSW(usb_device_info *udi, const usb_mass_CSW *csw) 90 { 91 char buf[sizeof(uint32) + 1] = {0}; 92 strncpy(buf, (char *)&csw->signature, sizeof(uint32)); 93 PTRACE(udi, "CSW:{'%s'; tag:%d; residue:%d; status:0x%02x}\n", 94 buf, csw->tag, csw->data_residue, csw->status); 95 } 96 97 /** 98 \fn:get_max_luns 99 \param udi: device for wich max LUN info is requested 100 \return:always B_OK - if info was not retrieved max LUN is defaulted to 0 101 102 tries to retrieve the maximal Logical Unit Number supported by 103 this device. If device doesn't support GET_MAX_LUN request - single LUN is 104 assumed. ([2] 3.2) 105 */ 106 static status_t 107 get_max_luns(usb_device_info *udi) 108 { 109 status_t status = B_OK; 110 udi->max_lun = 0; 111 if(!HAS_FIXES(udi->properties, FIX_NO_GETMAXLUN)){ 112 size_t len = 0; 113 if(B_OK != (status = (*udi->usb_m->send_request)(udi->device, 114 USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_CLASS, 115 USB_REQ_MS_GET_MAX_LUN, 0x0, udi->interface, 116 0x1, &udi->max_lun, &len))) 117 { 118 if(status == B_DEV_STALLED){ 119 PTRACE_ALWAYS(udi, "get_max_luns[%d]:not supported. " 120 "Assuming single LUN available.\n", udi->dev_num); 121 } else { 122 PTRACE(udi, "get_max_luns[%d]:failed(%08x)." 123 "Assuming single LUN available.\n", udi->dev_num, status); 124 } 125 udi->max_lun = 0; 126 status = B_OK; 127 } /* else - all is OK - max luns info readed */ 128 } 129 return status; 130 } 131 /** 132 \fn:queue_bulk 133 \param udi: device for which que_bulk request is performed 134 \param buffer: data buffer, used in bulk i/o operation 135 \param len: length of data buffer 136 \param b_in: is "true" if input (device->host) data transfer, "false" otherwise 137 \return: status of operation. 138 139 performs queue_bulk USB request for corresponding pipe and handle timeout of this 140 operation. 141 */ 142 static status_t 143 queue_bulk(usb_device_info *udi, void* buffer, size_t len, bool b_in) 144 { 145 status_t status = B_OK; 146 usb_pipe pipe = b_in ? udi->pipe_in : udi->pipe_out; 147 status = (*udi->usb_m->queue_bulk)(pipe, buffer, len, bulk_callback, udi); 148 if(status != B_OK){ 149 PTRACE_ALWAYS(udi, "queue_bulk:failed:%08x\n", status); 150 } else { 151 status = acquire_sem_etc(udi->trans_sem, 1, B_RELATIVE_TIMEOUT, udi->trans_timeout/*LOCK_TIMEOUT*/); 152 if(status != B_OK){ 153 PTRACE_ALWAYS(udi, "queue_bulk:acquire_sem_etc failed:%08x\n", status); 154 (*udi->usb_m->cancel_queued_transfers)(pipe); 155 } 156 } 157 return status; 158 } 159 /** 160 \fn:check_CSW 161 \param udi:corresponding device info 162 \param csw: CSW to be checked for validity and meaningfullness 163 \param transfer_len: data transferred during operation, which is checked for status 164 \return: "true" if CSW valid and meanigfull, "false" otherwise 165 166 checks CSW for validity and meaningfullness as required by USB mass strorge 167 BulkOnly specification ([2] 6.3) 168 */ 169 static bool 170 check_CSW(usb_device_info *udi, usb_mass_CSW* csw, int transfer_len) 171 { 172 bool is_valid = false; 173 do{ 174 /* check for CSW validity */ 175 if(udi->actual_len != CSW_LENGTH){ 176 PTRACE_ALWAYS(udi, "check_CSW:wrong length %d instead of %d\n", 177 udi->actual_len, CSW_LENGTH); 178 break;/* failed */ 179 } 180 if(csw->signature != CSW_SIGNATURE){ 181 PTRACE_ALWAYS(udi, "check_CSW:wrong signature %08x instead of %08x\n", 182 csw->signature, CSW_SIGNATURE); 183 break;/* failed */ 184 } 185 if(csw->tag != udi->tag - 1){ 186 PTRACE_ALWAYS(udi, "check_CSW:tag mismatch received:%d, awaited:%d\n", 187 csw->tag, udi->tag-1); 188 break;/* failed */ 189 } 190 /* check for CSW meaningfullness */ 191 if(CSW_STATUS_PHASE == csw->status){ 192 PTRACE_ALWAYS(udi, "check_CSW:not meaningfull: phase error\n"); 193 break;/* failed */ 194 } 195 if(transfer_len < csw->data_residue){ 196 PTRACE_ALWAYS(udi, "check_CSW:not meaningfull: " 197 "residue:%d is greater than transfer length:%d\n", 198 csw->data_residue, transfer_len); 199 break;/* failed */ 200 } 201 is_valid = true; 202 }while(false); 203 return is_valid; 204 } 205 /** 206 \fn:read_status 207 \param udi: corresponding device 208 \param csw: buffer for CSW data 209 \param transfer_len: data transferred during operation, which is checked for status 210 \return: success status code 211 212 reads CSW from device as proposed in ([2] 5.3.3; Figure 2.). 213 */ 214 static status_t 215 read_status(usb_device_info *udi, usb_mass_CSW* csw, int transfer_len) 216 { 217 status_t status = B_ERROR; 218 int try = 0; 219 do{ 220 status = queue_bulk(udi, csw, CSW_LENGTH, true); 221 if(try == 0){ 222 if(B_OK != status || B_OK != udi->status){ 223 status = (*udi->usb_m->clear_feature)(udi->pipe_in, USB_FEATURE_ENDPOINT_HALT); 224 if(status != 0){ 225 PTRACE_ALWAYS(udi, "read_status:failed 1st try, " 226 "status:%08x; usb_status:%08x\n", status, udi->status); 227 (*udi->protocol_m->reset)(udi); 228 break; 229 } 230 continue; /* go to second try*/ 231 } 232 /* CSW was readed without errors */ 233 } else { /* second try */ 234 if(B_OK != status || B_OK != udi->status){ 235 PTRACE_ALWAYS(udi, "read_status:failed 2nd try status:%08x; usb_status:%08x\n", 236 status, udi->status); 237 (*udi->protocol_m->reset)(udi); 238 status = (B_OK == status) ? udi->status : status; 239 break; 240 } 241 } 242 if(!check_CSW(udi, csw, transfer_len)){ 243 (*udi->protocol_m->reset)(udi); 244 status = B_ERROR; 245 break; 246 } 247 trace_CSW(udi, csw); 248 break; /* CSW was read successfully */ 249 }while(try++ < 2); 250 return status; 251 } 252 253 /*================= "standard" protocol procedures ==============================*/ 254 255 /** 256 \fn:bulk_only_initialize 257 \param udi: device on wich we should perform initialization 258 \return:error code if initialization failed or B_OK if it passed 259 260 initialize procedure for bulk only protocol devices. 261 */ 262 status_t 263 bulk_only_initialize(usb_device_info *udi) 264 { 265 status_t status = B_OK; 266 status = get_max_luns(udi); 267 return status; 268 } 269 /** 270 \fn:bulk_only_reset 271 \param udi: device on wich we should perform reset 272 \return:error code if reset failed or B_OK if it passed 273 274 reset procedure for bulk only protocol devices. Tries to send 275 BulkOnlyReset USB request and clear USB_FEATURE_ENDPOINT_HALT features on 276 input and output pipes. ([2] 3.1) 277 */ 278 status_t 279 bulk_only_reset(usb_device_info *udi) 280 { 281 status_t status = B_ERROR; 282 status = (*udi->usb_m->send_request)(udi->device, 283 USB_REQTYPE_CLASS | USB_REQTYPE_INTERFACE_OUT, 284 USB_REQ_MS_RESET, 0, 285 udi->interface, 0, 0, 0); 286 if(status != B_OK){ 287 PTRACE_ALWAYS(udi, "bulk_only_reset: reset request failed: %08x\n", status); 288 } 289 if(B_OK != (status = (*udi->usb_m->clear_feature)(udi->pipe_in, 290 USB_FEATURE_ENDPOINT_HALT))) 291 { 292 PTRACE_ALWAYS(udi, "bulk_only_reset: clear_feature on pipe_in failed: %08x\n", status); 293 } 294 if(B_OK != (status = (*udi->usb_m->clear_feature)(udi->pipe_out, 295 USB_FEATURE_ENDPOINT_HALT))) 296 { 297 PTRACE_ALWAYS(udi, "bulk_only_reset: clear_feature on pipe_out failed: %08x\n", status); 298 } 299 PTRACE(udi, "bulk_only_reset:%08x\n", status); 300 return status; 301 } 302 /** 303 \fn:bulk_only_transfer 304 \param udi: corresponding device 305 \param cmd: SCSI command to be performed on USB device 306 \param cmdlen: length of SCSI command 307 \param data_sg: io vectors array with data to transfer 308 \param sglist_count: count of entries in io vector array 309 \param transfer_len: overall length of data to be transferred 310 \param dir: direction of data transfer 311 \param ccbio: CCB_SCSIIO struct for original SCSI command 312 \param cb: callback to handle of final stage of command performing (autosense \ 313 request etc.) 314 315 transfer procedure for bulk-only protocol. Performs SCSI command on USB device 316 [2] 317 */ 318 void 319 bulk_only_transfer(usb_device_info *udi, uint8 *cmd, uint8 cmdlen, //sg_buffer *sgb, 320 iovec *sg_data,int32 sg_count, int32 transfer_len, 321 EDirection dir, CCB_SCSIIO *ccbio, ud_transfer_callback cb) 322 { 323 status_t status = B_OK; 324 status_t command_status = B_OK; 325 int32 residue = transfer_len; 326 usb_mass_CSW csw = {0}; 327 /* initialize and fill in Command Block Wrapper */ 328 usb_mass_CBW cbw = { 329 .signature = CBW_SIGNATURE, 330 .tag = atomic_add(&udi->tag, 1), 331 .data_transfer_len = transfer_len, 332 .flags = (dir == eDirIn) ? CBW_FLAGS_IN : CBW_FLAGS_OUT, 333 .lun = ccbio->cam_ch.cam_target_lun & 0xf, 334 .cdb_len = cmdlen, 335 }; 336 memcpy(cbw.CDB, cmd, cbw.cdb_len); 337 do{ 338 trace_CBW(udi, &cbw); 339 /* send CBW to device */ 340 status = queue_bulk(udi, &cbw, CBW_LENGTH, false); 341 if(status != B_OK || udi->status != B_OK){ 342 PTRACE_ALWAYS(udi, "bulk_only_transfer: queue_bulk failed:" 343 "status:%08x usb status:%08x\n", status, udi->status); 344 (*udi->protocol_m->reset)(udi); 345 command_status = B_CMD_WIRE_FAILED; 346 break; 347 } 348 /* perform data transfer if required */ 349 if(transfer_len != 0x0){ 350 status = process_data_io(udi, sg_data, sg_count, dir); 351 if(status != B_OK && status != B_DEV_STALLED){ 352 command_status = B_CMD_WIRE_FAILED; 353 break; 354 } 355 } 356 /* get status of command */ 357 status = read_status(udi, &csw, transfer_len); 358 if(B_OK != status){ 359 command_status = B_CMD_WIRE_FAILED; 360 break; 361 } 362 residue = csw.data_residue; 363 if(csw.status == CSW_STATUS_FAILED){ 364 command_status = B_CMD_FAILED; 365 }else{ 366 command_status = B_OK; 367 } 368 }while(false); 369 /* finalize transfer */ 370 cb(udi, ccbio, residue, command_status); 371 } 372 373 protocol_module_info bulk_only_protocol_m = { 374 {0, 0, 0}, /* this is not a real kernel module - just interface */ 375 bulk_only_initialize, 376 bulk_only_reset, 377 bulk_only_transfer, 378 }; 379 380