xref: /haiku/src/add-ons/kernel/busses/scsi/usb/proto_bulk.c (revision 0e50eab75e25d0d82090e22dbff766dfaa6f5e86)
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 /** bulk-only protocol specific implementation */
8 
9 /* References:
10  * USB Mass Storage Class specifications:
11  * http://www.usb.org/developers/data/devclass/usbmassover_11.pdf  [1]
12  * http://www.usb.org/developers/data/devclass/usbmassbulk_10.pdf  [2]
13  */
14 #include "usb_scsi.h"
15 
16 #include "device_info.h"
17 
18 #include "proto_module.h"
19 #include "proto_common.h"
20 #include "proto_bulk.h"
21 
22 #include "usb_defs.h"
23 
24 #include <string.h>  /* strncpy */
25 
26 /*Bulk-Only protocol specifics*/
27 #define USB_REQ_MS_RESET       0xff		/* Bulk-Only reset */
28 #define USB_REQ_MS_GET_MAX_LUN 0xfe		/* Get maximum lun */
29 
30 /* Command Block Wrapper */
31 typedef struct _usb_mass_CBW{
32   uint32 signature;
33   uint32 tag;
34   uint32 data_transfer_len;
35   uint8  flags;
36   uint8  lun;
37   uint8  cdb_len;
38 #define CBW_CDB_LENGTH	16
39   uint8  CDB[CBW_CDB_LENGTH];
40 } usb_mass_CBW; /*sizeof(usb_mass_CBW) must be 31*/
41 #define CBW_LENGTH 0x1f
42 
43 /* Command Status Wrapper */
44 typedef struct _usb_mass_CSW{
45   uint32 signature;
46   uint32 tag;
47   uint32 data_residue;
48   uint8  status;
49 } usb_mass_CSW; /*sizeof(usb_mass_CSW) must be 13*/
50 #define CSW_LENGTH 0x0d
51 
52 #define CSW_STATUS_GOOD   0x0
53 #define CSW_STATUS_FAILED 0x1
54 #define CSW_STATUS_PHASE  0x2
55 
56 #define CBW_SIGNATURE 0x43425355
57 #define CSW_SIGNATURE 0x53425355
58 
59 #define CBW_FLAGS_OUT	0x00
60 #define CBW_FLAGS_IN	0x80
61 
62 /*=========================== tracing helpers ==================================*/
63 void trace_CBW(usb_device_info *udi, const usb_mass_CBW *cbw)
64 {
65   char buf[sizeof(uint32) + 1] = {0};
66   strncpy(buf, (char *)&cbw->signature, sizeof(uint32));
67   PTRACE(udi, "\nCBW:{'%s'; tag:%d; data_len:%d; flags:0x%02x; lun:%d; cdb_len:%d;}\n",
68     buf,
69     cbw->tag,
70     cbw->data_transfer_len,
71     cbw->flags,
72     cbw->lun,
73     cbw->cdb_len);
74   udi->trace_bytes("CDB:\n", cbw->CDB, CBW_CDB_LENGTH);
75 }
76 
77 void trace_CSW(usb_device_info *udi, const usb_mass_CSW *csw)
78 {
79   char buf[sizeof(uint32) + 1] = {0};
80   strncpy(buf, (char *)&csw->signature, sizeof(uint32));
81   PTRACE(udi, "CSW:{'%s'; tag:%d; residue:%d; status:0x%02x}\n",
82     buf,
83     csw->tag,
84     csw->data_residue,
85     csw->status);
86 }
87 
88 /**
89   \fn:get_max_luns
90   \param udi: device for wich max LUN info is requested
91   \return:always B_OK - if info was not retrieved max LUN is defaulted to 0
92 
93   tries to retrieve the maximal Logical Unit Number supported by
94   this device. If device doesn't support GET_MAX_LUN request - single LUN is
95   assumed. ([2] 3.2)
96 */
97 static status_t
98 get_max_luns(usb_device_info *udi)
99 {
100   status_t status = B_OK;
101   udi->max_lun = 0;
102   if(!HAS_FIXES(udi->properties, FIX_NO_GETMAXLUN)){
103     size_t len = 0;
104     if(B_OK != (status = (*udi->usb_m->send_request)(udi->device,
105                                USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_CLASS,
106                                USB_REQ_MS_GET_MAX_LUN, 0x0, udi->interface,
107                                0x1, &udi->max_lun, &len)))
108     {
109       if(status == B_DEV_STALLED){
110         PTRACE_ALWAYS(udi, "get_max_luns[%d]:not supported. "
111                         "Assuming single LUN available.\n", udi->dev_num);
112       } else {
113         PTRACE(udi, "get_max_luns[%d]:failed(%08x)."
114                     "Assuming single LUN available.\n", udi->dev_num, status);
115       }
116       udi->max_lun = 0;
117       status = B_OK;
118     } /* else - all is OK - max luns info readed */
119   }
120   return status;
121 }
122 /**
123   \fn:queue_bulk
124   \param udi: device for which que_bulk request is performed
125   \param buffer: data buffer, used in bulk i/o operation
126   \param len: length of data buffer
127   \param b_in: is "true" if input (device->host) data transfer, "false" otherwise
128   \return: status of operation.
129 
130   performs queue_bulk USB request for corresponding pipe and handle timeout of this
131   operation.
132 */
133 static status_t
134 queue_bulk(usb_device_info *udi,
135                      void  *buffer,
136                      size_t len,
137                      bool   b_in)
138 {
139   status_t status = B_OK;
140   usb_pipe pipe = b_in ? udi->pipe_in : udi->pipe_out;
141   status = (*udi->usb_m->queue_bulk)(pipe, buffer, len, bulk_callback, udi);
142   if(status != B_OK){
143     PTRACE_ALWAYS(udi, "queue_bulk:failed:%08x\n", status);
144   } else {
145     status = acquire_sem_etc(udi->trans_sem, 1, B_RELATIVE_TIMEOUT, udi->trans_timeout/*LOCK_TIMEOUT*/);
146     if(status != B_OK){
147       PTRACE_ALWAYS(udi, "queue_bulk:acquire_sem_etc failed:%08x\n", status);
148       (*udi->usb_m->cancel_queued_transfers)(pipe);
149     }
150   }
151   return status;
152 }
153 /**
154   \fn:check_CSW
155   \param udi:corresponding device info
156   \param csw: CSW to be checked for validity and meaningfullness
157   \param transfer_len: data transferred during operation, which is checked for status
158   \return: "true" if CSW valid and meanigfull, "false" otherwise
159 
160   checks CSW for validity and meaningfullness as required by USB mass strorge
161   BulkOnly specification ([2] 6.3)
162 */
163 static bool
164 check_CSW(usb_device_info *udi,
165           usb_mass_CSW    *csw,
166           int              transfer_len)
167 {
168   bool is_valid = false;
169   do{
170     /* check for CSW validity */
171     if(udi->actual_len != CSW_LENGTH){
172       PTRACE_ALWAYS(udi, "check_CSW:wrong length %d instead of %d\n",
173                                                    udi->actual_len, CSW_LENGTH);
174       break;/* failed */
175     }
176     if(csw->signature != CSW_SIGNATURE){
177       PTRACE_ALWAYS(udi, "check_CSW:wrong signature %08x instead of %08x\n",
178                                                  csw->signature, CSW_SIGNATURE);
179       break;/* failed */
180     }
181     if(csw->tag != udi->tag - 1){
182       PTRACE_ALWAYS(udi, "check_CSW:tag mismatch received:%d, awaited:%d\n",
183                                                          csw->tag, udi->tag-1);
184       break;/* failed */
185     }
186     /* check for CSW meaningfullness */
187     if(CSW_STATUS_PHASE == csw->status){
188       PTRACE_ALWAYS(udi, "check_CSW:not meaningfull: phase error\n");
189       break;/* failed */
190     }
191     if(transfer_len < csw->data_residue){
192       PTRACE_ALWAYS(udi, "check_CSW:not meaningfull: "
193                        "residue:%d is greater than transfer length:%d\n",
194                                                 csw->data_residue, transfer_len);
195       break;/* failed */
196     }
197     is_valid = true;
198   }while(false);
199   return is_valid;
200 }
201 /**
202   \fn:read_status
203   \param udi: corresponding device
204   \param csw: buffer for CSW data
205   \param transfer_len: data transferred during operation, which is checked for status
206   \return: success status code
207 
208   reads CSW from device as proposed in ([2] 5.3.3; Figure 2.).
209 */
210 static status_t
211 read_status(usb_device_info *udi,
212             usb_mass_CSW    *csw,
213             int              transfer_len)
214 {
215   status_t status = B_ERROR;
216   int try = 0;
217   do{
218     status = queue_bulk(udi, csw, CSW_LENGTH, true);
219     if(0 == try){
220       if(B_OK != status || B_OK != udi->status){
221         status = (*udi->usb_m->clear_feature)(udi->pipe_in, USB_FEATURE_ENDPOINT_HALT);
222         if(status != 0){
223           PTRACE_ALWAYS(udi, "read_status:failed 1st try, "
224                           "status:%08x; usb_status:%08x\n", status, udi->status);
225           (*udi->protocol_m->reset)(udi);
226           break;
227         }
228         continue; /* go to second try*/
229       }
230       /* CSW was readed without errors */
231     } else { /* second try */
232       if(B_OK != status || B_OK != udi->status){
233         PTRACE_ALWAYS(udi, "read_status:failed 2nd try status:%08x; usb_status:%08x\n",
234                                                               status, udi->status);
235         (*udi->protocol_m->reset)(udi);
236         status = (B_OK == status) ? udi->status : status;
237         break;
238       }
239     }
240     if(!check_CSW(udi, csw, transfer_len)){
241       (*udi->protocol_m->reset)(udi);
242       status = B_ERROR;
243       break;
244     }
245     trace_CSW(udi, csw);
246     break; /* CSW was read successfully */
247   }while(try++ < 2);
248   return status;
249 }
250 
251 /*================= "standard" protocol procedures ==============================*/
252 
253 /**
254   \fn:bulk_only_initialize
255   \param udi: device on wich we should perform initialization
256   \return:error code if initialization failed or B_OK if it passed
257 
258   initialize procedure for bulk only protocol devices.
259 */
260 status_t
261 bulk_only_initialize(usb_device_info *udi)
262 {
263   status_t status = B_OK;
264   status = get_max_luns(udi);
265   return status;
266 }
267 /**
268   \fn:bulk_only_reset
269   \param udi: device on wich we should perform reset
270   \return:error code if reset failed or B_OK if it passed
271 
272   reset procedure for bulk only protocol devices. Tries to send
273   BulkOnlyReset USB request and clear USB_FEATURE_ENDPOINT_HALT features on
274   input and output pipes. ([2] 3.1)
275 */
276 status_t
277 bulk_only_reset(usb_device_info *udi)
278 {
279   status_t status = B_ERROR;
280   status = (*udi->usb_m->send_request)(udi->device,
281                                 USB_REQTYPE_CLASS | USB_REQTYPE_INTERFACE_OUT,
282                                 USB_REQ_MS_RESET, 0,
283                                 udi->interface, 0, 0, 0);
284   if(status != B_OK){
285     PTRACE_ALWAYS(udi, "bulk_only_reset: reset request failed: %08x\n", status);
286   }
287   if(B_OK != (status = (*udi->usb_m->clear_feature)(udi->pipe_in,
288                                              USB_FEATURE_ENDPOINT_HALT)))
289   {
290     PTRACE_ALWAYS(udi, "bulk_only_reset: clear_feature on pipe_in failed: %08x\n", status);
291   }
292   if(B_OK != (status = (*udi->usb_m->clear_feature)(udi->pipe_out,
293                                              USB_FEATURE_ENDPOINT_HALT)))
294   {
295     PTRACE_ALWAYS(udi, "bulk_only_reset: clear_feature on pipe_out failed: %08x\n", status);
296   }
297   PTRACE(udi, "bulk_only_reset:%08x\n", status);
298   return status;
299 }
300 /**
301   \fn:bulk_only_transfer
302   \param udi: corresponding device
303   \param cmd: SCSI command to be performed on USB device
304   \param cmdlen: length of SCSI command
305   \param data_sg: io vectors array with data to transfer
306   \param sglist_count: count of entries in io vector array
307   \param transfer_len: overall length of data to be transferred
308   \param dir: direction of data transfer
309   \param ccbio: CCB_SCSIIO struct for original SCSI command
310   \param cb: callback to handle of final stage of command performing (autosense \
311              request etc.)
312 
313   transfer procedure for bulk-only protocol. Performs  SCSI command on USB device
314   [2]
315 */
316 void
317 bulk_only_transfer(usb_device_info *udi,
318                              uint8 *cmd,
319                              uint8  cmdlen,
320                              //sg_buffer *sgb,
321                              iovec *sg_data,
322                          int32 sg_count,
323                              int32  transfer_len,
324                         EDirection  dir,
325                         CCB_SCSIIO *ccbio,
326                ud_transfer_callback cb)
327 {
328   status_t status         = B_OK;
329   status_t command_status = B_OK;
330   int32 residue           = transfer_len;
331   usb_mass_CSW csw = {0};
332   /* initialize and fill in Command Block Wrapper */
333   usb_mass_CBW cbw = {
334     .signature         = CBW_SIGNATURE,
335     .tag               = atomic_add(&udi->tag, 1),
336     .data_transfer_len = transfer_len,
337     .flags             = (dir == eDirIn) ? CBW_FLAGS_IN : CBW_FLAGS_OUT,
338     .lun               = ccbio->cam_ch.cam_target_lun & 0xf,
339     .cdb_len           = cmdlen,
340   };
341   memcpy(cbw.CDB, cmd, cbw.cdb_len);
342   do{
343     trace_CBW(udi, &cbw);
344     /* send CBW to device */
345     status = queue_bulk(udi, &cbw, CBW_LENGTH, false);
346     if(status != B_OK || udi->status != B_OK){
347       PTRACE_ALWAYS(udi, "bulk_only_transfer: queue_bulk failed:"
348                       "status:%08x usb status:%08x\n", status, udi->status);
349       (*udi->protocol_m->reset)(udi);
350       command_status = B_CMD_WIRE_FAILED;
351       break;
352     }
353     /* perform data transfer if required */
354     if(transfer_len != 0x0){
355       status = process_data_io(udi, sg_data, sg_count, dir);
356       if(status != B_OK && status != B_DEV_STALLED){
357         command_status = B_CMD_WIRE_FAILED;
358         break;
359       }
360     }
361     /* get status of command */
362     status = read_status(udi, &csw, transfer_len);
363     if(B_OK != status){
364       command_status = B_CMD_WIRE_FAILED;
365       break;
366     }
367     residue = csw.data_residue;
368     if(csw.status == CSW_STATUS_FAILED){
369       command_status = B_CMD_FAILED;
370     }else{
371       command_status = B_OK;
372     }
373   }while(false);
374   /* finalize transfer */
375   cb(udi, ccbio, residue, command_status);
376 }
377 
378 protocol_module_info bulk_only_protocol_m = {
379   {0, 0, 0}, /* this is not a real kernel module - just interface */
380   bulk_only_initialize,
381   bulk_only_reset,
382   bulk_only_transfer,
383 };
384