xref: /haiku/src/add-ons/kernel/busses/scsi/usb/usb_scsi.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 /** Main part of USB SIM implementation */
8 
9 #include "usb_scsi.h"
10 
11 #include <KernelExport.h>
12 #include <module.h>
13 #include <malloc.h>
14 #include <strings.h>
15 #include <stdio.h>
16 #include "device_info.h"
17 #include "settings.h"
18 #include "transform_procs.h"
19 #include "tracing.h"
20 #include "scsi_commands.h"
21 #include "proto_common.h"
22 #include "proto_bulk.h"
23 #include "proto_cbi.h"
24 #include "usb_defs.h"
25 #include "fake_device.h"
26 #include "sg_buffer.h"
27 
28 status_t device_added(const usb_device device,
29                       void           **cookie);
30 
31 status_t device_removed(void *cookie);
32 
33 static long sim_action(CCB_HEADER *ccbh);
34 static long sim_init();
35 
36 #define SIM_VERSION 1
37 #define HBA_VERSION 1
38 
39 //#define ROUNDUP(size, seg) (((size) + (seg) - 1) & ~((seg) - 1))
40 
41 #define INQ_VENDOR_LEN    0x08
42 #define INQ_PRODUCT_LEN   0x10
43 #define INQ_REVISION_LEN  0x04
44 
45 #define TRANS_TIMEOUT     7500000
46 
47 static long path_id = -1;
48 static int32 load_count = 0;
49 
50 static char sim_vendor_name[]   = "S.Zharski";  /* who wrote this driver */
51 static char hba_vendor_name[]   = "USB";        /* who made the hardware */
52 static char controller_family[] = "USB SCSI";   /* what family of products */
53 
54 struct usb_support_descriptor supported_devices[] = {
55     {0, 0, 0, 0, 0}
56 };
57 
58 #define SIZEOF(array) (sizeof(array)/sizeof(array[0])) //?????
59 
60 usb_device_info *usb_devices[MAX_DEVICES_COUNT];
61 /* main devices table locking semaphore */
62 sem_id usb_serial_lock = -1;
63 
64 usb_module_info *usb;
65 static cam_for_sim_module_info *cam;
66 
67 struct usb_notify_hooks notify_hooks = {
68   device_added,
69   device_removed
70 };
71 /**
72   \fn:match_device
73   \param device:???
74   \param uii:???
75   \param pproperties:???
76   \return:B_BAD_TYPE , B_ENTRY_NOT_FOUND, B_OK
77 
78   ??
79 */
80 status_t get_interface_properties(usb_interface_info *uii,
81                                   uint32             *pproperties)
82 {
83   status_t status = B_BAD_TYPE;
84   if(uii->descr->interface_class == USB_DEV_CLASS_MASS){
85     status = B_OK;
86     switch(uii->descr->interface_subclass){
87     case USB_DEV_SUBCLASS_RBC:      *pproperties |= CMDSET_RBC; break;
88     case USB_DEV_SUBCLASS_UFI:      *pproperties |= CMDSET_UFI; break;
89     case USB_DEV_SUBCLASS_SFF8020I:
90     case USB_DEV_SUBCLASS_SFF8070I: *pproperties |= CMDSET_ATAPI; break;
91     case USB_DEV_SUBCLASS_SCSI:     *pproperties |= CMDSET_SCSI; break;
92     case USB_DEV_SUBCLASS_QIC157:   *pproperties |= CMDSET_QIC157; break;
93     default:
94       TRACE_ALWAYS("get_interface_properties:unknown USB subclass:%02x\n",
95                                            uii->descr->interface_subclass);
96       /*status = B_ENTRY_NOT_FOUND;*/ /*B_BAD_TYPE assumed*/
97       break;
98     }
99     switch(uii->descr->interface_protocol){
100     case USB_DEV_PROTOCOL_CBI:  *pproperties |= PROTO_CBI; break;
101     case USB_DEV_PROTOCOL_CB:   *pproperties |= PROTO_CB; break;
102     case USB_DEV_PROTOCOL_BULK: *pproperties |= PROTO_BULK_ONLY; break;
103     default:
104       TRACE_ALWAYS("get_interface_properties:unknown USB protocol:%02x\n",
105                                            uii->descr->interface_protocol);
106       /*status = B_ENTRY_NOT_FOUND;*/ /*B_BAD_TYPE assumed*/
107       break;
108     }
109     if(status == B_OK){
110       TRACE("get_interface_properties: standard properties:%08x\n", *pproperties);
111     }
112   }
113   return status;
114 }
115 /**
116   \fn:
117 
118 */
119 status_t load_vendor_module(char **path, const char *path_mask,
120                             const char *prop, module_info **mi)
121 {
122   status_t status = B_NO_MEMORY;
123   int path_len = strlen(path_mask) + strlen(prop);
124   *path = malloc(path_len);
125   if(*path){
126     sprintf(*path, path_mask, prop);
127     status = get_module(*path, mi);
128     if(status != B_OK)
129       TRACE_ALWAYS("load_vendor_module:get_module(%s) failed:%08x\n", *path, status);
130   } else {
131     TRACE_ALWAYS("load_vendor_module:couldn't allocate %d bytes\n", path_len);
132   }
133   return status;
134 }
135 /**
136   \fn:
137 
138 */
139 status_t setup_transport_modules(usb_device_info *udi,
140                                  usb_device_settings *uds)
141 {
142   status_t status = B_OK;
143   switch(PROTO(udi->properties)){
144   case PROTO_BULK_ONLY:
145     udi->protocol_m = &bulk_only_protocol_m;
146     break;
147   case PROTO_CB:
148   case PROTO_CBI:
149     udi->protocol_m = &cbi_protocol_m;
150     break;
151   case PROTO_VENDOR:{
152       status = load_vendor_module(&udi->protocol_m_path,
153                                   PROTOCOL_MODULE_MASK,
154                                   uds->vendor_protocol,
155                                   (module_info**)&udi->protocol_m);
156     }break;
157   default:
158     TRACE_ALWAYS("setup_transport_modules: "
159                  "transport %02x is not supported\n", PROTO(udi->properties));
160     status = B_ENTRY_NOT_FOUND;
161   }
162   if(status == B_OK){
163     switch(CMDSET(udi->properties)){
164     case CMDSET_SCSI:
165       udi->transform_m = &scsi_transform_m;
166       break;
167     case CMDSET_UFI:
168       udi->transform_m = &ufi_transform_m;
169       udi->properties |= FIX_FORCE_MS_TO_10; /* always use 10-byte request */
170       break;
171     case CMDSET_ATAPI:
172       udi->transform_m = &atapi_transform_m;
173       udi->properties |= FIX_FORCE_MS_TO_10; /* always use 10-byte request */
174       break;
175     case CMDSET_RBC:
176       udi->transform_m = &rbc_transform_m;
177       break;
178     case CMDSET_QIC157:
179       udi->transform_m = &qic157_transform_m;
180       udi->properties |= FIX_FORCE_MS_TO_10; /* always use 10-byte request */
181       break;
182     case CMDSET_VENDOR:{
183         status = load_vendor_module(&udi->transform_m_path,
184                                     TRANSFORM_MODULE_MASK,
185                                     uds->vendor_commandset,
186                                     (module_info**)&udi->transform_m);
187       }break;
188     default:
189       TRACE_ALWAYS("setup_transport_modules: "
190                    "protocol %02x is not supported\n", CMDSET(udi->properties));
191       status = B_ENTRY_NOT_FOUND;
192     }
193   }
194   return status;
195 }
196 
197 static void
198 release_transport_modules(usb_device_info *udi)
199 {
200   if(PROTO(udi->properties) == PROTO_VENDOR && 0 != udi->protocol_m_path){
201     put_module(udi->protocol_m_path);
202     udi->protocol_m = 0;
203     free(udi->protocol_m_path);
204   }
205   if(CMDSET(udi->properties) == CMDSET_VENDOR && 0 != udi->transform_m_path){
206     put_module(udi->transform_m_path);
207     udi->transform_m = 0;
208     free(udi->transform_m_path);
209   }
210 }
211 
212 /**
213   \fn:
214 */
215 status_t setup_endpoints(usb_interface_info *uii,
216                          usb_device_info *udi)
217 {
218   status_t status = B_OK;
219   int16 idx = 0;
220   enum{ epIn = 0, epOut, epIntr, epCount };
221   size_t epts[epCount] = { -1, -1, -1 };
222   char  *epnames[epCount] = {"input", "output", "interrupt"};
223   size_t ep = 0;
224   for(; ep < uii->endpoint_count; ep++){
225     usb_endpoint_descriptor *ed = uii->endpoint[ep].descr;
226     TRACE("try endpoint:%d %x %x %x\n", ep, (int32)ed->attributes, (int32)ed->endpoint_address, uii->endpoint[ep].handle);
227     if((ed->attributes & USB_EP_ATTR_MASK) == USB_EP_ATTR_BULK){
228       if((ed->endpoint_address & USB_EP_ADDR_DIR_IN) == USB_EP_ADDR_DIR_IN){
229         epts[epIn]  = ep;
230       }else{
231         epts[epOut] = ep;
232       }
233     }else{
234       if((ed->attributes & USB_EP_ATTR_MASK) == USB_EP_ATTR_INTERRUPT)
235         epts[epIntr] = ep;
236     }
237   }
238   switch(PROTO(udi->properties)){
239   case PROTO_CB:
240   case PROTO_BULK_ONLY:
241     if(epts[epIntr] == -1)
242       epts[epIntr] = 0; /* not required for this transports - set it to default*/
243     break;
244   case PROTO_CBI:
245   default:
246   }
247   for(idx = 0; idx < epCount; idx++){
248     if(epts[idx] == -1 && PROTO(udi->properties) != PROTO_VENDOR){
249       TRACE_ALWAYS("setup_endpoints: required %s endpoint not found. "
250                    "ignore this interface\n", epnames[idx]);
251       // DEBUG!!!!!!!!!!
252       status = B_ERROR;
253     }
254   }
255   if(status == B_OK){
256     udi->pipe_in    = uii->endpoint[epts[epIn]].handle;
257     udi->pipe_out   = uii->endpoint[epts[epOut]].handle;
258     udi->pipe_intr  = uii->endpoint[epts[epIntr]].handle;
259     //TRACE("setup_endpoints: input:%d output:%d "
260     //                     "interrupt:%d\n", epts[epIn], epts[epOut], epts[epIntr]);
261     TRACE("endpoint:%x %x %x\n", udi->pipe_in, udi->pipe_out, udi->pipe_intr);
262   }
263   return status;
264 }
265 /**
266   \fn:create_device_info
267   \param device:???
268   \param ppudi:???
269   \return:???
270 
271   ???
272 */
273 status_t allocate_resources(usb_device_info *udi)
274 {
275   char name[32];
276   status_t status = B_NO_MEMORY;
277   uint16 dev = 0;
278   acquire_sem(usb_serial_lock);
279   for(; dev < MAX_DEVICES_COUNT; dev++){
280     if(!usb_devices[dev])
281       break;
282   }
283   if(dev < MAX_DEVICES_COUNT){
284     usb_devices[dev] = udi;
285     udi->lock_sem =
286     udi->trans_sem = -1;
287     sprintf(name, "usb_scsi lock_sem:%d", dev);
288     if((udi->lock_sem = create_sem(0, name)) >= 0){
289       sprintf(name, "usb_scsi trans_sem:%d", dev);
290       if((udi->trans_sem = create_sem(0, name))>=0){
291         udi->dev_num = dev;
292         release_sem(udi->lock_sem);
293         status = B_OK;
294       }else
295         status = udi->trans_sem;
296     } else
297       status = udi->lock_sem;
298 
299     if(status != B_OK){
300       TRACE_ALWAYS("allocate_resources:error:%s", strerror(status));
301       if(udi->lock_sem >= 0)
302         delete_sem(udi->lock_sem);
303       if(udi->trans_sem >= 0)
304         delete_sem(udi->trans_sem);
305       usb_devices[dev] = NULL;
306       free(udi);
307     }
308   }else{
309     TRACE_ALWAYS("allocate_resources:reserved devices space exhausted."
310                  "Unplug unnesesary devices or reconfigure this driver.\n");
311   }
312   release_sem(usb_serial_lock);
313   return status;
314 }
315 /**
316   \fn:
317 
318 */
319 void release_resources(usb_device_info *udi)
320 {
321   release_transport_modules(udi);
322   udi->usb_m = 0;
323   (*usb->cancel_queued_transfers)(udi->pipe_in);
324   (*usb->cancel_queued_transfers)(udi->pipe_out);
325   delete_sem(udi->lock_sem);
326   delete_sem(udi->trans_sem);
327   acquire_sem(usb_serial_lock);
328   usb_devices[udi->dev_num] = NULL;
329   release_sem(usb_serial_lock);
330 }
331 /**
332   \fn:device_added
333   \param device:??
334   \param cookie:??
335   \return:??
336 
337   ??
338 */
339 status_t device_added(const usb_device device,
340                       void           **cookie){
341   status_t status = B_NO_MEMORY;
342   const usb_configuration_info *uci = NULL;
343   uint16 cfg = 0;
344   bool b_found = false;
345   bool b_has_extra_settings = false;
346   const usb_device_descriptor *udd = (*usb->get_device_descriptor)(device);
347   usb_device_info *udi = (usb_device_info *)malloc(sizeof(usb_device_info));
348   TRACE("device_added: probing canidate: "
349         "%04x/%04x %02d/%02d/%02d\n", udd->vendor_id, udd->product_id,
350                    udd->device_class, udd->device_subclass, udd->device_protocol);
351   if(udi){
352     status = B_NO_INIT;
353     while(!b_found && (uci = (*usb->get_nth_configuration)(device, cfg++))){
354       uint16 itf = 0;
355       for(; itf < uci->interface_count && !b_found; itf++){
356         usb_interface_list *ifl = &uci->interface[itf];
357         uint16 alt = 0;
358         for(; alt < ifl->alt_count; alt++){
359           usb_interface_info *uii = &ifl->alt[alt];
360           usb_device_settings ud_settings;
361           memset(udi, 0, sizeof(usb_device_info));
362           memset(&ud_settings, 0, sizeof(usb_device_settings));
363           udi->device = device;
364           udi->interface  = itf;
365           b_has_extra_settings = lookup_device_settings(udd, &ud_settings);
366           switch(get_interface_properties(uii, &udi->properties)){
367           case B_BAD_TYPE: /* non-standard USB class*/
368             if(!b_has_extra_settings){
369               continue; /* skip to next interface */
370             } /* no break - fall through */
371           case B_OK:
372             if(b_has_extra_settings){
373               if(PROTO(ud_settings.properties) != PROTO_NONE){
374                 udi->properties &= ~PROTO_MASK;
375                 udi->properties |= PROTO(ud_settings.properties);
376               }
377               if(CMDSET(ud_settings.properties) != CMDSET_NONE){
378                 udi->properties &= ~CMDSET_MASK;
379                 udi->properties |= CMDSET(ud_settings.properties);
380               }
381               udi->properties |= ud_settings.properties & FIX_MASK;
382               TRACE("device_added: properties merged:%08x\n", udi->properties);
383             }
384             if( B_OK == setup_transport_modules(udi, &ud_settings)){
385               break;
386             } /* else - no break - fall through */
387           default:
388             continue; /* skip to next interface */
389           }
390           if(alt != 0){ /*TODO: are we need this ???*/
391             if((status = (*usb->set_alt_interface)(device, uii)) != B_OK){
392               TRACE_ALWAYS("device_added:setting alt interface failed:%s",
393                                                                   strerror(status));
394               goto Failed;/* Break - is it right?*/
395             }
396           }
397           if((*usb->get_configuration)(device) != uci){
398             if((status = (*usb->set_configuration)(device, uci)) != B_OK){
399                 TRACE_ALWAYS("device_added:setting configuration failed:%08x uci: %08x\n",
400                                  (*usb->get_configuration)(device), uci);
401               TRACE_ALWAYS("device_added:setting configuration failed:%s\n",
402                                                                   strerror(status));
403 
404               goto Failed;/* Break - is it right?*/
405             }
406           }
407           if(B_OK != setup_endpoints(uii, udi)){
408             continue; /* skip to next interface */
409           }
410           if((status = allocate_resources(udi)) == B_OK){
411             udi->b_trace = b_log_protocol;
412             udi->trace = usb_scsi_trace;
413             udi->trace_bytes = usb_scsi_trace_bytes;
414             udi->trans_timeout = TRANS_TIMEOUT;
415             udi->usb_m = usb;
416             udi->not_ready_luns = 0xff; /*assume all LUNs initially not ready */
417             if((status = (*udi->protocol_m->init)(udi)) == B_OK){
418               TRACE("device_added[%d]: SUCCESS! Enjoy using!\n", udi->dev_num);
419               *cookie = udi;
420               b_found = true; /* we have found something useful - time to go out! */
421               break;          /* ... now break alternatives iteration.*/
422             } else {
423               release_resources(udi);
424             }
425           }
426           /* go to next iteration - check all configurations for possible devices */
427         }/* for(...) iterate interface alternates*/
428       }/* for(...) iterate interfaces*/
429     }/* while(...) iterate configurations */
430     if(status == B_OK){
431       (*cam->minfo.rescan)();
432     } else {
433       free(udi);
434     }
435   } /* if(udi){ */
436   if(status != B_OK){
437     TRACE("device_added: probing failed (%s) for: %04x/%04x\n",
438                               strerror(status), udd->vendor_id, udd->product_id);
439   }
440 Failed:
441   return status;
442 }
443 /**
444   \fn:device_removed
445   \param cookie:???
446   \return:???
447 
448   ???
449 */
450 status_t device_removed(void *cookie)
451 {
452   status_t status = B_OK;
453   usb_device_info *udi = (usb_device_info *)cookie;
454   acquire_sem(udi->lock_sem); /* wait for possible I/O operation complete */
455   release_resources(udi);
456   /* no corresponding call of release_sem(udi->lock_sem);
457        - semaphore was deleted in release_resources, any waiting thread
458          was failed with BAD_SEM_ID.
459   */
460   TRACE_ALWAYS("device_removed[%d]:All The Best !!!\n", udi->dev_num);
461   free(udi);
462   (*cam->minfo.rescan)();
463   return status;
464 }
465 /**
466   \fn:sim_init
467   \return: ???
468 
469   called on SIM init
470 */
471 static long sim_init(void)
472 {
473   status_t status = B_OK;
474   TRACE("sim_init\n");
475   return status;
476 }
477 
478 /**
479 */
480 static bool
481 pre_check_scsi_io_request(usb_device_info *udi,
482                               CCB_SCSIIO *ccbio,
483                                 status_t *ret_status)
484 {
485   int target_id    = ccbio->cam_ch.cam_target_id;
486   uint8 target_lun = ccbio->cam_ch.cam_target_lun;
487   *ret_status = B_OK;
488   /* handle reserved device and luns entries */
489   if(b_reservation_on && udi == NULL &&
490      target_id < reserved_devices &&
491      target_lun < reserved_luns)
492   {
493     *ret_status = fake_scsi_io(ccbio);
494     return false;
495   }
496   /* no device for this target | LUN */
497   if(udi == NULL || target_lun > udi->max_lun){
498     ccbio->cam_ch.cam_status = CAM_DEV_NOT_THERE;
499     *ret_status = B_DEV_BAD_DRIVE_NUM;
500     return false;
501   }
502   /* check command length */
503   if(ccbio->cam_cdb_len != 6  &&
504      ccbio->cam_cdb_len != 10 &&
505      ccbio->cam_cdb_len != 12)
506   {
507     TRACE("Bad SCSI command length:%d.Ignore\n", ccbio->cam_cdb_len);
508     ccbio->cam_ch.cam_status = CAM_REQ_INVALID;
509     *ret_status = B_BAD_VALUE;
510     return false;
511   }
512   /* Clean up auto sense buffer.
513      To avoid misunderstanding in not ready luns logic detection.*/
514   if(NULL == ccbio->cam_sense_ptr){
515     memset(&udi->autosense_data, 0, sizeof(udi->autosense_data));
516   } else {
517     memset(ccbio->cam_sense_ptr, 0, ccbio->cam_sense_len);
518   }
519   return true;
520 }
521 /**
522 */
523 static bool
524 pre_handle_features(usb_device_info *udi,
525                           CCB_SCSIIO *ccbio,
526                     scsi_cmd_generic *command,
527                            sg_buffer *sgb,
528                             status_t *ret_status)
529 {
530   uint8 target_lun = ccbio->cam_ch.cam_target_lun;
531   *ret_status = B_OK;
532   udi->trans_timeout = TRANS_TIMEOUT;
533   switch(command->opcode){
534   case MODE_SELECT_6:
535   case MODE_SENSE_6:{
536     bool b_select = (MODE_SELECT_6 == command->opcode);
537     const char*cmd_name = b_select ? "MODE_SELECT" : "MODE_SENSE";
538     if(udi->not_ready_luns & (1 << target_lun)){
539       TRACE("pre_handle_features:%s_6 bypassed for LUN:%d\n", cmd_name, target_lun);
540       goto set_REQ_INVALID_and_return;
541     }
542     if(HAS_FIXES(udi->properties, FIX_FORCE_MS_TO_10)){
543       if( B_OK != realloc_sg_buffer(sgb, ccbio->cam_dxfer_len + 4)){ /* TODO: 4 - hardcoded? */
544         TRACE_ALWAYS("pre_handle_features:error allocating %d bytes for %s_10\n",
545                                            ccbio->cam_dxfer_len + 4, cmd_name);
546         goto set_REQ_INVALID_and_return;
547       }
548       if(b_select){
549         sg_buffer sgb_sense_6;
550         /*TODO: implemenet and try refragment_sg_buffer - to check handling of real scatter/gather!!*/
551         if(B_OK != init_sg_buffer(&sgb_sense_6, ccbio)){
552           /* In case of error TRACE-ing was already performed in sg_ functions */
553           goto set_REQ_INVALID_and_return;
554         }
555         TRACE_MODE_SENSE_SGB("MODE SELECT 6:", &sgb_sense_6);
556         if(B_OK != sg_memcpy(sgb, 1, &sgb_sense_6, 0, 3) ||
557            B_OK != sg_memcpy(sgb, 7, &sgb_sense_6, 3, 1) ||
558            B_OK != sg_memcpy(sgb, 8, &sgb_sense_6, 4, ccbio->cam_dxfer_len - sizeof(scsi_mode_param_header_6)))
559         {
560           /* In case of error TRACE-ing was already performed in sg_ functions */
561           goto set_REQ_INVALID_and_return;
562         }
563         TRACE_MODE_SENSE_SGB("MODE SELECT 10:", sgb);
564       }
565     } /*else {
566       if(b_select){ / * trace data if configured in settings * /
567         TRACE_MODE_SENSE_DATA("MODE_SELECT:", ccbio->cam_data_ptr, ccbio->cam_dxfer_len);
568       }
569     }*/
570   }break;
571   case INQUIRY: /* fake INQUIRY request */
572     if(HAS_FIXES(udi->properties, FIX_NO_INQUIRY)){
573       fake_inquiry_request(udi, ccbio);
574       goto set_REQ_CMP_and_return;
575     } break;
576   case TEST_UNIT_READY: /* fake INQUIRY request */
577     if(HAS_FIXES(udi->properties, FIX_NO_TEST_UNIT)){
578       goto set_REQ_CMP_and_return;
579     } break;
580   case PREVENT_ALLOW_MEDIA_REMOVAL: /* fake PREVENT_ALLOW_MEDIA_REMOVAL request */
581     if(HAS_FIXES(udi->properties, FIX_NO_PREVENT_MEDIA)){
582       goto set_REQ_CMP_and_return;
583     } break;
584   case FORMAT_UNIT:
585     udi->trans_timeout = B_INFINITE_TIMEOUT;
586     break;
587   default: break;
588   }
589   return true;
590 
591 set_REQ_CMP_and_return:
592   ccbio->cam_ch.cam_status = CAM_REQ_CMP;
593   return false;//*ret_status = B_OK;
594 
595 set_REQ_INVALID_and_return:
596   ccbio->cam_ch.cam_status = CAM_REQ_INVALID;
597   *ret_status = B_BAD_VALUE;
598   return false;
599 }
600 /**
601 */
602 static bool
603 post_handle_features(usb_device_info *udi,
604                           CCB_SCSIIO *ccbio,
605                     scsi_cmd_generic *command,
606                            sg_buffer *sgb,
607                             status_t *ret_status)
608 {
609   bool b_cmd_ok = (ccbio->cam_ch.cam_status == CAM_REQ_CMP);
610   uint8 target_lun = ccbio->cam_ch.cam_target_lun;
611   switch(command->opcode){
612   case READ_6:
613   case WRITE_6:
614 #if 0
615 /* Disabled - single problem can switch to 6-bytes mode. If device doesn't
616    support 6-bytes command all goes totally wrong. That's bad. */
617     if(!b_cmd_ok && !HAS_FEATURES(udi->descr.properties, PROP_FORCE_RW_TO_6)){
618       TRACE("post_handle_features:READ(10)/WRITE(10) failed - retry 6-byte one\n");
619       udi->descr.properties |= PROP_FORCE_RW_TO_6;
620       ccbio->cam_scsi_status = SCSI_STATUS_OK; /* clear the scsi_status. */
621       ccbio->cam_ch.cam_status = CAM_REQ_INPROG; /* set status in progress again. */
622       b_retry = true; /* inform caller about retry */
623     }
624 #endif
625     break;
626   case MODE_SENSE_6:
627     if(!b_cmd_ok && !HAS_FIXES(udi->properties, FIX_FORCE_MS_TO_10)){
628       TRACE("post_handle_features:MODE SENSE(6) failed - retry 10-byte one\n");
629       udi->properties |= FIX_FORCE_MS_TO_10;
630       ccbio->cam_scsi_status = SCSI_STATUS_OK; /* clear the scsi_status. */
631       ccbio->cam_ch.cam_status = CAM_REQ_INPROG; /* set status in progress again. */
632       return true; /* inform caller about retry */
633     }  /* no break, - fallthrough! */
634   case MODE_SELECT_6:{
635     if(MODE_SENSE_6 == command->opcode){
636       sg_buffer sgb_sense_6;
637       if(B_OK != init_sg_buffer(&sgb_sense_6, ccbio)){
638         TRACE_ALWAYS("post_hanlde_features: initialize sgb failed\n");
639         goto set_REQ_INVALID_and_return;
640       }
641       /* convert sense information from 10-byte request result to 6-byte one */
642       if(HAS_FIXES(udi->properties, FIX_FORCE_MS_TO_10)){
643         uchar mode_data_len_msb = 0, block_descr_len_msb = 0;
644         if( B_OK != sg_access_byte(sgb, 0, &mode_data_len_msb, false) ||
645             B_OK != sg_access_byte(sgb, 6, &block_descr_len_msb, false) ||
646                0 != mode_data_len_msb || 0 != block_descr_len_msb)
647         {
648           /* In case of error TRACE-ing was already performed in sg_ functions */
649           TRACE_ALWAYS("MODE_SENSE 10->6 conversion overflow: %d, %d\n",
650                                            mode_data_len_msb, block_descr_len_msb);
651           goto set_REQ_INVALID_and_return;
652         }
653         TRACE_MODE_SENSE_SGB("MODE SENSE 10:", sgb);
654         if( B_OK != sg_memcpy(&sgb_sense_6, 0, sgb, 1, 3) ||
655             B_OK != sg_memcpy(&sgb_sense_6, 3, sgb, 7, 1) ||
656             B_OK != sg_memcpy(&sgb_sense_6, 4, sgb, 8, ccbio->cam_dxfer_len - sizeof(scsi_mode_param_header_6)))
657         {
658           /* In case of error TRACE-ing was already performed in sg_ functions */
659           TRACE_ALWAYS("MODE_SENSE 10->6 conversion failed\n");
660           goto set_REQ_INVALID_and_return;
661         }
662       }
663       /* set write-protected flag if required by user */
664       if(HAS_FIXES(udi->properties, FIX_FORCE_READ_ONLY)){
665         status_t status = B_OK;
666         uchar device_spec_params = 0;
667         if(B_OK == (status = sg_access_byte(sgb, 2, &device_spec_params, false))){
668           device_spec_params |= 0x80;
669           status = sg_access_byte(sgb, 2, &device_spec_params, true);
670         }
671         if(B_OK != status){
672           TRACE_ALWAYS("MODE_SENSE set READ-ONLY mode failed. Writing ALLOWED!\n");
673           /*goto set_req_invalid_and_return;*/ /* not urgent. do not fail processing...  */
674         }
675       }
676       TRACE_MODE_SENSE_SGB("MODE SENSE 6:", &sgb_sense_6);
677     }
678     if(HAS_FIXES(udi->properties, FIX_FORCE_MS_TO_10)){
679       free_sg_buffer(sgb);
680     }
681   } break;
682   case TEST_UNIT_READY: { /* set the not ready luns flag */
683       scsi_sense_data *sense_data = (ccbio->cam_sense_ptr == NULL) ?
684                  &udi->autosense_data : (scsi_sense_data *)ccbio->cam_sense_ptr;
685       if((sense_data->flags & SSD_KEY) == SSD_KEY_NOT_READY){
686         udi->not_ready_luns |= (1 << target_lun);
687       } else {
688         udi->not_ready_luns &= ~(1 << target_lun);
689       }
690       usb_scsi_trace_bytes("NOT_READY_LUNS:", &udi->not_ready_luns, 1);
691     } break;
692   case READ_CAPACITY:{
693     /*uint8 *bts = sgb->piov->iov_base;
694     uint32 capacity = (bts[0]<<24) + (bts[1]<<16) + (bts[2]<<8) + (bts[3]);
695     TRACE_ALWAYS("CAPAC:%d\n", capacity);
696     //bts[3] -= 3;*/
697     TRACE_CAPACITY("READ_CAPACITY:", sgb);
698   } break;
699   default: break;
700   }
701   return false; /* do not retry - all is OK */ //b_retry;
702 
703 set_REQ_INVALID_and_return:
704   ccbio->cam_ch.cam_status = CAM_REQ_INVALID;
705   *ret_status = B_BAD_VALUE;
706   return false; /* do not retry - fatal error */
707 }
708 /**
709   \fn:xpt_scsi_io
710   \param ccbio: ????
711   \return: ???
712 
713   xpt_scsi_io - handle XPT_SCSI_IO sim action
714 */
715 status_t xpt_scsi_io(CCB_SCSIIO *ccbio)
716 {
717   status_t status      = B_OK;
718   usb_device_info *udi = usb_devices[ccbio->cam_ch.cam_target_id/*target_id*/];
719   uint8 *cmd;
720   sg_buffer sgb;
721 
722   /* clear the scsi_status. It can come from system with some garbage value ...*/
723   ccbio->cam_scsi_status = SCSI_STATUS_OK;
724   /* check the request for correct parameters, valid targets, luns etc ... */
725   if(false == pre_check_scsi_io_request(udi, ccbio, &status)){
726     return status;
727   }
728 
729 #if 0 /* activate if you need detailed logging of CCB_SCSI request*/
730   usb_scsi_trace_CCB_SCSIIO(ccbio);
731 #endif
732 
733 
734   /* acquire semaphore - avoid re-enters */
735 /*  if((status = acquire_sem_etc(udi->lock_sem, 1,
736                               B_RELATIVE_TIMEOUT,
737                               udi->trans_timeout)) != B_OK) */
738 //  TRACE_ALWAYS("sem before acq:%08x\n",udi->lock_sem);
739   if((status = acquire_sem(udi->lock_sem)) != B_OK){
740     /* disabled - CAM_BUSY flag is not recognized by BeOS ... :-(
741     if(status == B_WOULD_BLOCK){
742       TRACE("locked sema bypass OK\n");
743       ccbio->cam_ch.cam_status = CAM_BUSY;
744       return B_OK;
745     }*/
746     TRACE("xpt_scsi_io:acquire_sem_etc() failed:%08x\n", status);
747     ccbio->cam_ch.cam_status = CAM_DEV_NOT_THERE;
748     return B_DEV_BAD_DRIVE_NUM;
749   }
750   /* set the command data pointer */
751   if(ccbio->cam_ch.cam_flags & CAM_CDB_POINTER){
752     cmd = ccbio->cam_cdb_io.cam_cdb_ptr;
753   }else{
754     cmd = ccbio->cam_cdb_io.cam_cdb_bytes;
755   }
756   /* NOTE: using stack copy of sg_buffer sgb. It can be modified/freed in
757            *_handle_features() functions! Twice reallocation is also not awaited!
758            Note this on refactoring!!! */
759   /*TODO returns!*/
760   init_sg_buffer(&sgb, ccbio);
761   do{ /* <-- will be repeated if 6-byte RW/MS commands failed */
762     uint8 *rcmd;
763     uint8 rcmdlen;
764     uint32 transfer_len = 0;
765     EDirection dir = eDirNone;
766     /* handle various features for this device */
767     if(!pre_handle_features(udi, ccbio, (scsi_cmd_generic *)cmd, &sgb, &status)){
768       release_sem(udi->lock_sem);
769       return status;
770     }
771     /* transform command as required by protocol */
772     rcmd    = udi->scsi_command_buf;
773     rcmdlen = sizeof(udi->scsi_command_buf);
774     if((status = (*udi->transform_m->transform)(udi, cmd, ccbio->cam_cdb_len & 0x1f,
775                                              &rcmd, &rcmdlen)) != B_OK)
776     {
777       TRACE_ALWAYS("xpt_scsi_io: transform failed: %08x\n", status);
778       ccbio->cam_ch.cam_status = CAM_REQ_INVALID;
779       release_sem(udi->lock_sem);
780       return B_BAD_VALUE;
781     }
782     /* set correct direction flag */
783     switch(CAM_DIR_MASK & ccbio->cam_ch.cam_flags){
784     case CAM_DIR_IN:  dir = eDirIn;   break;
785     case CAM_DIR_OUT: dir = eDirOut;  break;
786     default:          dir = eDirNone; break;
787     }
788 
789     TRACE_DATA_IO_SG(sgb.piov, sgb.count);
790 
791     /*TODO: return!*/
792     sg_buffer_len(&sgb, &transfer_len);
793     /* transfer command to device. SCSI status will be handled in callback */
794     (*udi->protocol_m->transfer)(udi, rcmd, rcmdlen, sgb.piov, sgb.count,
795                                          transfer_len/*ccbio->cam_dxfer_len*/,
796                                                         dir, ccbio, transfer_callback);
797     /* perform some post-tranfer features handling
798        and automatic 6-10 bytes command support detection */
799   } while(post_handle_features(udi, ccbio, (scsi_cmd_generic *)cmd, &sgb, &status));
800   release_sem(udi->lock_sem);
801   return status;
802 }
803 /**
804   \fn:xpt_path_inquiry
805   \param ccbp:
806   \return:
807 
808   xpt_path_inquiry - handle XPT_PATH_INQ sim action
809 */
810 status_t xpt_path_inquiry(CCB_PATHINQ *ccbp)
811 {
812   status_t status = B_OK;
813   ccbp->cam_version_num  = SIM_VERSION;
814   ccbp->cam_target_sprt  = 0;
815   ccbp->cam_hba_eng_cnt  = 0;
816   memset (ccbp->cam_vuhba_flags, 0, VUHBA);
817   ccbp->cam_sim_priv     = SIM_PRIV;
818   ccbp->cam_async_flags  = 0;
819   ccbp->cam_initiator_id = CONTROLLER_SCSI_ID;
820   ccbp->cam_hba_inquiry  = CONTROLLER_SCSI_BUS; /* Narrow SCSI bus */
821   ccbp->cam_hba_misc     = PIM_NOINQUIRY;
822   ccbp->cam_osd_usage    = 0;
823   /* There should be special handling of path_id == 0xff
824      but looks like it's not used by BeOS now */
825   /*ccbp->cam_hpath_id = path_id;*/
826   strncpy(ccbp->cam_sim_vid, sim_vendor_name, SIM_ID);
827   strncpy(ccbp->cam_hba_vid, hba_vendor_name, HBA_ID);
828   ccbp->cam_ch.cam_status = CAM_REQ_CMP;
829   return status;
830 }
831 /**
832   \fn:xpt_extended_path_inquiry
833   \param ccbep: ???
834   \return:???
835 
836   xpt_extended_path_inquiry - handle XPT_EXTENDED_PATH_INQ sim action
837 */
838 status_t xpt_extended_path_inquiry(CCB_EXTENDED_PATHINQ *ccbep)
839 {
840   status_t status = B_OK;
841   xpt_path_inquiry((CCB_PATHINQ *)ccbep);
842   sprintf(ccbep->cam_sim_version, "%d.0", SIM_VERSION);
843   sprintf(ccbep->cam_hba_version, "%d.0", HBA_VERSION);
844   strncpy(ccbep->cam_controller_family, controller_family, FAM_ID);
845   strncpy(ccbep->cam_controller_type, "USB-SCSI", TYPE_ID);
846   return status;
847 }
848 /**
849   \fn:sim_action
850   \param ccbh: ????
851   \return: ????
852 
853   This fucntion performs SCSI interface module actions -
854   calls corresponding xpt_* - functions.
855 */
856 static long sim_action(CCB_HEADER *ccbh)
857 {
858   status_t status = B_ERROR;
859   if(path_id != ccbh->cam_path_id){
860     TRACE_ALWAYS("sim_action:path_id mismatch of func:%d:our:%d,requested:%d\n",
861                                  ccbh->cam_func_code, path_id, ccbh->cam_path_id);
862     ccbh->cam_status = CAM_PATH_INVALID;
863   } else {
864     ccbh->cam_status = CAM_REQ_INPROG;
865     switch(ccbh->cam_func_code){
866       case XPT_SCSI_IO:
867         status = xpt_scsi_io((CCB_SCSIIO *)ccbh);
868         break;
869       case XPT_PATH_INQ:
870         status = xpt_path_inquiry((CCB_PATHINQ *)ccbh);
871         break;
872       case XPT_EXTENDED_PATH_INQ:
873         status = xpt_extended_path_inquiry((CCB_EXTENDED_PATHINQ *)ccbh);
874         break;
875       default:
876         TRACE_ALWAYS("sim_action: unsupported function: %x\n", ccbh->cam_func_code);
877         ccbh->cam_status = CAM_REQ_INVALID;
878         break;
879     }
880   }
881   return status;
882 }
883 /**
884   \fn:std_ops
885   \param op: operation to be performed on this module
886   \param ...: possible additional arguments
887   \return: B_OK on success, error status on failure
888 
889   This function deals with standard module operations. Currently, the only
890   two things that entails are initialization and uninitialization.
891   - get the SCSI Bus Manager Module and USB Manager Module
892   - put them when we're finished
893 */
894 static status_t std_ops(int32 op, ...)
895 {
896   int i;
897   status_t status = B_ERROR;
898   CAM_SIM_ENTRY entry;
899   switch(op) {
900   case B_MODULE_INIT:
901     if(0 == atomic_add(&load_count, 1)){
902       thread_info tinfo = {0};
903       load_module_settings();
904       get_thread_info(find_thread(0), &tinfo);
905       if(!b_ignore_sysinit2 || (0 != strcmp(tinfo.name, "sysinit2"))){
906         create_log();
907         if(get_module(B_USB_MODULE_NAME, (module_info **)&usb) == B_OK){
908           if(get_module(B_CAM_FOR_SIM_MODULE_NAME, (module_info **)&cam) == B_OK){
909             for(i = 0; i < MAX_DEVICES_COUNT; i++)
910               usb_devices[i] = NULL;
911 
912             if((*usb->register_driver)(MODULE_NAME, supported_devices, SIZEOF(supported_devices), "usb_dsk") == B_OK){
913               if((*usb->install_notify)(MODULE_NAME, &notify_hooks) == B_OK){
914                 entry.sim_init = sim_init;
915                 entry.sim_action = sim_action;
916                 path_id =(*cam->xpt_bus_register)(&entry);
917                 usb_serial_lock = create_sem(1, MODULE_NAME"_devices_table_lock");
918                 status = B_OK;
919                 break;
920               }
921             }
922             put_module(B_CAM_FOR_SIM_MODULE_NAME);
923           }
924           put_module(B_USB_MODULE_NAME);
925         }
926       } else {
927         TRACE_ALWAYS("std_ops INIT call was ignored for thread:%s\n", tinfo.name);
928       }
929     } else {
930       atomic_add(&load_count, -1);
931     }
932     break;
933   case B_MODULE_UNINIT:
934     if(1 == atomic_add(&load_count, -1)){
935       (*usb->uninstall_notify)(MODULE_NAME);
936       status = B_OK;
937       if(path_id != -1){
938         (*cam->xpt_bus_deregister)(path_id);
939         path_id = -1;
940       }
941       delete_sem(usb_serial_lock);
942       put_module(B_USB_MODULE_NAME);
943       put_module(B_CAM_FOR_SIM_MODULE_NAME);
944     } else {
945       atomic_add(&load_count, 1);
946     }
947     break;
948   }
949   return status;
950 }
951 
952 /**
953   Declare our module_info so we can be loaded as a kernel module
954 */
955 static sim_module_info sim_usb_module = {
956   { "busses/scsi/usb/v1", 0, &std_ops }
957 };
958 /**
959   Export module_info-s list
960 */
961 _EXPORT module_info *modules[] = {
962   (module_info *) &sim_usb_module,
963   NULL
964 };
965