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