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 */
37typedef 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 */
50typedef 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
68static void 	trace_CBW(usb_device_info *udi, const usb_mass_CBW *cbw);
69static void 	trace_CSW(usb_device_info *udi, const usb_mass_CSW *csw);
70static status_t	bulk_only_initialize(usb_device_info *udi);
71static status_t	bulk_only_reset(usb_device_info *udi);
72static 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 ==================================*/
79void 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
89void 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*/
106static status_t
107get_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*/
142static status_t
143queue_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*/
169static bool
170check_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*/
214static status_t
215read_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(0 == try){
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*/
262status_t
263bulk_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*/
278status_t
279bulk_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*/
318void
319bulk_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
373protocol_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