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