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