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