xref: /haiku/src/add-ons/kernel/busses/scsi/usb/transform_procs.c (revision cd552c7a15cc10c36dae8d7439ba1d6c0bb168c5)
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