1 /* 2 * Copyright 2004-2007, Haiku, Inc. All RightsReserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Author: 6 * Siarzhuk Zharski <imker@gmx.li> 7 */ 8 9 /*! SCSI commands transformations support */ 10 11 #include "usb_scsi.h" 12 13 #include "device_info.h" 14 #include "transform_procs.h" 15 #include "tracing.h" 16 #include "scsi_commands.h" 17 #include "settings.h" 18 #include "strings.h" 19 20 #define UFI_COMMAND_LEN 12 21 #define ATAPI_COMMAND_LEN 12 22 23 24 /*! Transforms a 6-byte command to 10-byte one if required. Transformed command 25 is returned in rcmd parameter. In case if no transformation was performed 26 the return buffer is untouched. 27 28 \param cmd: SCSI command buffer to be transformed 29 \param len: length of buffer, pointed by cmd parameter 30 \param rcmd: a place for buffer pointer with transformed command 31 \param rlen: a place for length of transformed command 32 \return: true if transformation was performed 33 */ 34 static void 35 transform_6_to_10(uint8 *cmd, uint8 len, uint8 **rcmd, uint8 *rlen) 36 { 37 scsi_cmd_generic_6 *from = (scsi_cmd_generic_6 *)cmd; 38 39 *rlen = 10; 40 memset(*rcmd, 0, 10); 41 42 switch (from->opcode) { 43 case READ_6: 44 case WRITE_6: 45 { 46 scsi_cmd_rw_10 *to = (scsi_cmd_rw_10 *)(*rcmd); 47 48 to->opcode = (from->opcode == READ_6) ? READ_10 : WRITE_10; 49 to->lba = B_HOST_TO_BENDIAN_INT32(((from->addr[0] & 0x1f) << 16) 50 | (from->addr[1] << 8) | from->addr[0]); 51 to->lun = (from->addr[0] & CMD_LUN) >> CMD_LUN_SHIFT; 52 to->control = from->ctrl; 53 if (from->len == 0) { 54 /* special case! in 6-byte R/W commands */ 55 /* the length 0x00 assume transfering 0x100 blocks! */ 56 to->length = B_HOST_TO_BENDIAN_INT16((uint16)256); 57 } else 58 to->length = B_HOST_TO_BENDIAN_INT16((uint16)from->len); 59 } 60 61 case MODE_SENSE_6: 62 case MODE_SELECT_6: 63 { 64 scsi_cmd_generic_10 *to = (scsi_cmd_generic_10 *)(*rcmd); 65 66 if (from->opcode == MODE_SENSE_6) { 67 to->opcode = MODE_SENSE_10; 68 ((scsi_cmd_mode_sense_10 *)to)->byte3 69 = ((scsi_cmd_mode_sense_6 *)from)->byte3; 70 } else 71 to->opcode = MODE_SELECT_10; 72 to->byte2 = from->addr[0]; 73 to->len[1] = from->len + 4; /*TODO: hardcoded length*/ 74 to->ctrl = from->ctrl; 75 } 76 77 default: 78 /* no transformation needed */ 79 break; 80 } 81 } 82 83 84 /*! Transforms a 6-byte command to 10-byte depending on information provided 85 with udi object. 86 87 \param udi: usb_device_info object for wich transformation is requested 88 \param cmd: SCSI command buffer to be transformed 89 \param len: length of buffer, pointed by cmd parameter 90 \param rcmd: a place for buffer pointer with transformed command 91 \param rlen: a place for length of transformed command 92 \return: true if transformation was performed 93 */ 94 static bool 95 transform_cmd_6_to_10(usb_device_info *udi, uint8 *cmd, uint8 len, 96 uint8 **rcmd, uint8 *rlen) 97 { 98 scsi_cmd_generic_6 *from = (scsi_cmd_generic_6 *)cmd; 99 100 switch (from->opcode) { 101 case READ_6: 102 case WRITE_6: 103 { 104 if (!HAS_FIXES(udi->properties, FIX_FORCE_RW_TO_6)) { 105 transform_6_to_10(cmd, len, rcmd, rlen); 106 return true; 107 } 108 break; 109 } 110 111 case MODE_SENSE_6: 112 case MODE_SELECT_6: 113 { 114 if (HAS_FIXES(udi->properties, FIX_FORCE_MS_TO_10)) { 115 transform_6_to_10(cmd, len, rcmd, rlen); 116 return true; 117 } 118 break; 119 } 120 } 121 122 return false; 123 } 124 125 126 /*! Transforms a TEST_UNIT_COMAND SCSI command to START_STOP_UNIT one depending 127 on properties provided with udi object. 128 129 \param udi: usb_device_info object for wich transformation is requested 130 \param cmd: SCSI command buffer to be transformed 131 \param len: length of buffer, pointed by cmd parameter 132 \param rcmd: a place for buffer pointer with transformed command 133 \param rlen: a place for length of transformed command 134 \return: true if transformation was performed 135 */ 136 static bool 137 transform_cmd_test_unit_ready(usb_device_info *udi, uint8 *cmd, uint8 len, 138 uint8 **rcmd, uint8 *rlen) 139 { 140 scsi_cmd_start_stop_unit *command = (scsi_cmd_start_stop_unit *)(*rcmd); 141 142 if (!HAS_FIXES(udi->properties, FIX_TRANS_TEST_UNIT)) 143 return false; 144 145 memset(*rcmd, 0, *rlen); 146 command->opcode = START_STOP_UNIT; 147 command->start_loej = CMD_SSU_START; 148 *rlen = 6; 149 150 return true; 151 } 152 153 154 // #pragma mark - 155 156 157 /*! This is the "transformation procedure" for transparent SCSI (0x06) USB 158 subclass. It performs all SCSI commands transformations required by this 159 protocol. Additionally it tries to make some workarounds for "broken" USB 160 devices. If no transformation was performed resulting command buffer 161 points to original one. 162 163 \param udi: usb_device_info object for wich transformation is requested 164 \param cmd: SCSI command buffer to be transformed 165 \param len: length of buffer, pointed by cmd parameter 166 \param rcmd: a place for buffer pointer with transformed command 167 \param rlen: a place for length of transformed command 168 \return: B_OK if transformation was successfull, B_ERROR otherwise 169 */ 170 static status_t 171 scsi_transform(usb_device_info *udi, uint8 *cmd, uint8 len, uint8 **rcmd, 172 uint8 *rlen) 173 { 174 bool transformed = false; 175 scsi_cmd_generic *command = (scsi_cmd_generic *)cmd; 176 TRACE_SCSI_COMMAND(cmd, len); 177 178 switch (command->opcode) { 179 case READ_6: 180 case WRITE_6: 181 case MODE_SENSE_6: 182 case MODE_SELECT_6: 183 transformed = transform_cmd_6_to_10(udi, cmd, len, rcmd, rlen); 184 break; 185 186 case TEST_UNIT_READY: 187 transformed = transform_cmd_test_unit_ready(udi, cmd, len, rcmd, 188 rlen); 189 break; 190 191 default: 192 break; 193 } 194 195 if (!transformed) { 196 /* transformation was not required */ 197 *rcmd = cmd; 198 *rlen = len; 199 } else 200 TRACE_SCSI_COMMAND_HLIGHT(*rcmd, *rlen); 201 202 return B_OK; 203 } 204 205 206 /*! This is the "transformation procedure" for RBC USB subclass (0x01). It 207 performs all SCSI commands transformations required by this protocol. 208 Additionally it tries to make some workarounds for "broken" USB devices. 209 If no transformation was performed resulting command buffer points to 210 original one. 211 212 \param udi: usb_device_info object for wich transformation is requested 213 \param cmd: SCSI command buffer to be transformed 214 \param len: length of buffer, pointed by cmd parameter 215 \param rcmd: a place for buffer pointer with transformed command 216 \param rlen: a place for length of transformed command 217 \return: B_OK if transformation was successfull, B_ERROR otherwise 218 */ 219 static status_t 220 rbc_transform(usb_device_info *udi, uint8 *cmd, uint8 len, uint8 **rcmd, 221 uint8 *rlen) 222 { 223 bool transformed = false; 224 scsi_cmd_generic *command = (scsi_cmd_generic *)cmd; 225 TRACE_SCSI_COMMAND(cmd, len); 226 227 switch (command->opcode) { 228 case TEST_UNIT_READY: 229 transformed = transform_cmd_test_unit_ready(udi, cmd, len, rcmd, rlen); 230 break; 231 232 case READ_6: 233 case WRITE_6: /* there are no such command in list of allowed - transform*/ 234 transformed = transform_cmd_6_to_10(udi, cmd, len, rcmd, rlen); 235 break; 236 237 /* TODO: all following ones are not checked against specs !!!*/ 238 case FORMAT_UNIT: 239 case INQUIRY: /*TODO: check !!! */ 240 case MODE_SELECT_6: /*TODO: check !!! */ 241 case MODE_SENSE_6: /*TODO: check !!! */ 242 case PERSISTENT_RESERVE_IN: /*TODO: check !!! */ 243 case PERSISTENT_RESERVE_OUT: /*TODO: check !!! */ 244 case PREVENT_ALLOW_MEDIA_REMOVAL: /*TODO: check !!! */ 245 case READ_10: /*TODO: check !!! */ 246 case READ_CAPACITY: /*TODO: check !!! */ 247 case RELEASE_6: /*TODO: check !!! */ 248 case REQUEST_SENSE: /*TODO: check !!! */ 249 case RESERVE_6: /*TODO: check !!! */ 250 case START_STOP_UNIT: /*TODO: check !!! */ 251 case SYNCHRONIZE_CACHE: /*TODO: check !!! */ 252 case VERIFY: /*TODO: check !!! */ 253 case WRITE_10: /*TODO: check !!! */ 254 case WRITE_BUFFER: /*TODO Check correctnes of such translation!*/ 255 *rcmd = cmd; /* No need to copy */ 256 *rlen = len; /*TODO: check !!! */ 257 break; 258 259 default: 260 TRACE_ALWAYS("An unsupported RBC command: %08x\n", command->opcode); 261 return B_ERROR; 262 } 263 264 if (transformed) 265 TRACE_SCSI_COMMAND_HLIGHT(*rcmd, *rlen); 266 267 return B_OK; 268 } 269 270 271 /*! This is the "transformation procedure" for UFI USB subclass (0x04). It 272 performs all SCSI commands transformations required by this protocol. 273 Additionally it tries to make some workarounds for "broken" USB devices. 274 If no transformation was performed resulting command buffer points to 275 the original one. 276 277 \param udi: usb_device_info object for wich transformation is requested 278 \param cmd: SCSI command buffer to be transformed 279 \param len: length of buffer, pointed by cmd parameter 280 \param rcmd: a place for buffer pointer with transformed command 281 \param rlen: a place for length of transformed command 282 \return: B_OK if transformation was successfull, B_ERROR otherwise 283 */ 284 static status_t 285 ufi_transform(usb_device_info *udi, uint8 *cmd, uint8 len, uint8 **rcmd, 286 uint8 *rlen) 287 { 288 scsi_cmd_generic *command = (scsi_cmd_generic *)cmd; 289 status_t status = B_OK; 290 291 TRACE_SCSI_COMMAND(cmd, len); 292 memset(*rcmd, 0, UFI_COMMAND_LEN); 293 294 switch (command->opcode) { 295 case READ_6: 296 case WRITE_6: 297 case MODE_SENSE_6: 298 case MODE_SELECT_6: 299 // TODO: not transform_cmd_*()? 300 transform_6_to_10(cmd, len, rcmd, rlen); 301 break; 302 case TEST_UNIT_READY: 303 if (transform_cmd_test_unit_ready(udi, cmd, len, rcmd, rlen)) 304 break; /* if TEST UNIT READY was transformed*/ 305 case FORMAT_UNIT: /* TODO: mismatch */ 306 case INQUIRY: 307 case START_STOP_UNIT: 308 case MODE_SELECT_10: 309 case MODE_SENSE_10: 310 case PREVENT_ALLOW_MEDIA_REMOVAL: 311 case READ_10: 312 case READ_12: 313 case READ_CAPACITY: 314 case READ_FORMAT_CAPACITY: /* TODO: not in the SCSI-2 specs */ 315 case REQUEST_SENSE: 316 case REZERO_UNIT: 317 case SEEK_10: 318 case SEND_DIAGNOSTICS: /* TODO: parameter list len mismatch */ 319 case VERIFY: 320 case WRITE_10: 321 case WRITE_12: /* TODO: EBP. mismatch */ 322 case WRITE_AND_VERIFY: 323 memcpy(*rcmd, cmd, len); 324 /*TODO what about control? ignored in UFI?*/ 325 break; 326 default: 327 TRACE_ALWAYS("An unsupported UFI command: %08x\n", 328 command->opcode); 329 status = B_ERROR; 330 break; 331 } 332 333 *rlen = UFI_COMMAND_LEN; /* override any value set in transform funcs !!!*/ 334 335 if (status == B_OK) 336 TRACE_SCSI_COMMAND_HLIGHT(*rcmd, *rlen); 337 338 return status; 339 } 340 341 342 /*! This is the "transformation procedure" for SFF8020I and SFF8070I 343 USB subclassses (0x02 and 0x05). It performs all SCSI commands 344 transformations required by this protocol. Additionally it tries to make 345 some workarounds for "broken" USB devices. If no transformation was 346 performed resulting command buffer points to the original one. 347 348 \param udi: usb_device_info object for wich transformation is requested 349 \param cmd: SCSI command buffer to be transformed 350 \param len: length of buffer, pointed by cmd parameter 351 \param rcmd: a place for buffer pointer with transformed command 352 \param rlen: a place for length of transformed command 353 \return: B_OK if transformation was successfull, B_ERROR otherwise 354 */ 355 static status_t 356 atapi_transform(usb_device_info *udi, uint8 *cmd, uint8 len, uint8 **rcmd, 357 uint8 *rlen) 358 { 359 scsi_cmd_generic *command = (scsi_cmd_generic *)cmd; 360 status_t status = B_OK; 361 362 TRACE_SCSI_COMMAND(cmd, len); 363 memset(*rcmd, 0, ATAPI_COMMAND_LEN); 364 365 switch (command->opcode) { 366 case READ_6: 367 case WRITE_6: 368 case MODE_SENSE_6: 369 case MODE_SELECT_6: 370 // TODO: not transform_cmd_*()? 371 transform_6_to_10(cmd, len, rcmd, rlen); 372 break; 373 case TEST_UNIT_READY: 374 if (transform_cmd_test_unit_ready(udi, cmd, len, rcmd, rlen)) 375 break; 376 case FORMAT_UNIT: 377 case INQUIRY: 378 case MODE_SELECT_10: 379 case MODE_SENSE_10: 380 case PREVENT_ALLOW_MEDIA_REMOVAL: 381 case READ_10: 382 case READ_12: /* mismatch in byte 1 */ 383 case READ_CAPACITY: /* mismatch. no transf len defined... */ 384 case READ_FORMAT_CAPACITY: /* TODO: check!!! */ 385 case REQUEST_SENSE: 386 case SEEK_10: 387 case START_STOP_UNIT: 388 case VERIFY: /* mismatch DPO */ 389 case WRITE_10: /* mismatch in byte 1 */ 390 case WRITE_12: /* mismatch in byte 1 */ 391 case WRITE_AND_VERIFY: /* mismatch byte 1 */ 392 case PAUSE_RESUME: 393 case PLAY_AUDIO: 394 case PLAY_AUDIO_MSF: 395 case REWIND: 396 case PLAY_AUDIO_TRACK: 397 /* are in FreeBSD driver but no in 8070/8020 specs ... 398 //case REZERO_UNIT: 399 //case SEND_DIAGNOSTIC: 400 //case POSITION_TO_ELEMENT: */ 401 case GET_CONFIGURATION: 402 case SYNCHRONIZE_CACHE: 403 case READ_BUFFER: 404 case READ_SUBCHANNEL: 405 case READ_TOC: /* some mismatch */ 406 case READ_HEADER: 407 case READ_DISK_INFO: 408 case READ_TRACK_INFO: 409 case SEND_OPC: 410 case READ_MASTER_CUE: 411 case CLOSE_TR_SESSION: 412 case READ_BUFFER_CAP: 413 case SEND_CUE_SHEET: 414 case BLANK: 415 case EXCHANGE_MEDIUM: 416 case READ_DVD_STRUCTURE: 417 case SET_CD_SPEED: 418 case DVD_REPORT_KEY: 419 case DVD_SEND_KEY: 420 //case 0xe5: /* READ_TRACK_INFO_PHILIPS *//* TODO: check!!! */ 421 memcpy(*rcmd, cmd, len); /* TODO: check!!! */ 422 break; 423 424 default: 425 TRACE_ALWAYS("An unsupported (?) ATAPI command: %08x\n", 426 command->opcode); 427 status = B_ERROR; 428 break; 429 } 430 431 *rlen = ATAPI_COMMAND_LEN; 432 433 if (status == B_OK) 434 TRACE_SCSI_COMMAND_HLIGHT(*rcmd, *rlen); 435 436 return status; 437 } 438 439 440 /*! This is the "transformation procedure" for QIC157 USB subclass (0x03). It 441 performs all SCSI commands transformations required by this protocol. 442 Additionally it tries to make some workarounds for "broken" USB devices. 443 If no transformation was performed the resulting command buffer points to 444 the original one. 445 446 \param udi: usb_device_info object for wich transformation is requested 447 \param cmd: SCSI command buffer to be transformed 448 \param len: length of buffer, pointed by cmd parameter 449 \param rcmd: a place for buffer pointer with transformed command 450 \param rlen: a place for length of transformed command 451 \return: B_OK if transformation was successfull, B_ERROR otherwise 452 */ 453 static status_t 454 qic157_transform(usb_device_info *udi, uint8 *cmd, uint8 len, uint8 **rcmd, 455 uint8 *rlen) 456 { 457 scsi_cmd_generic *command = (scsi_cmd_generic *)cmd; 458 status_t status = B_OK; 459 460 TRACE_SCSI_COMMAND(cmd, len); 461 *rlen = ATAPI_COMMAND_LEN; 462 memset(*rcmd, 0, *rlen); 463 464 switch (command->opcode) { 465 case READ_6: 466 case WRITE_6: 467 case MODE_SENSE_6: 468 case MODE_SELECT_6: 469 // TODO: not transform_cmd_*()? 470 transform_6_to_10(cmd, len, rcmd, rlen); 471 break; 472 case TEST_UNIT_READY: 473 if (transform_cmd_test_unit_ready(udi, cmd, len, rcmd, rlen)) 474 break; // if TEST UNIT READY was transformed 475 case ERASE: /*TODO: check !!! */ 476 case INQUIRY: /*TODO: check !!! */ 477 case LOAD_UNLOAD: /*TODO: check !!! */ 478 case LOCATE: /*TODO: check !!! */ 479 case LOG_SELECT: /*TODO: check !!! */ 480 case LOG_SENSE: /*TODO: check !!! */ 481 case READ_POSITION: /*TODO: check !!! */ 482 case REQUEST_SENSE: /*TODO: check !!! */ 483 case REWIND: /*TODO: check !!! */ 484 case SPACE: /*TODO: check !!! */ 485 case WRITE_FILEMARK: /*TODO: check !!! */ 486 *rcmd = cmd; /*TODO: check !!! */ 487 *rlen = len; 488 break; 489 default: 490 TRACE_ALWAYS("An unsupported QIC-157 command: %08x\n", 491 command->opcode); 492 status = B_ERROR; 493 break; 494 } 495 496 if (status == B_OK) 497 TRACE_SCSI_COMMAND_HLIGHT(*rcmd, *rlen); 498 499 return status; 500 } 501 502 503 transform_module_info scsi_transform_m = { 504 {0, 0, 0}, /* this is not a real kernel module - just interface */ 505 scsi_transform, 506 }; 507 508 transform_module_info rbc_transform_m = { 509 {0, 0, 0}, /* this is not a real kernel module - just interface */ 510 rbc_transform, 511 }; 512 513 transform_module_info ufi_transform_m = { 514 {0, 0, 0}, /* this is not a real kernel module - just interface */ 515 ufi_transform, 516 }; 517 518 transform_module_info atapi_transform_m = { 519 {0, 0, 0}, /* this is not a real kernel module - just interface */ 520 atapi_transform, 521 }; 522 523 transform_module_info qic157_transform_m = { 524 {0, 0, 0}, /* this is not a real kernel module - just interface */ 525 qic157_transform, 526 }; 527