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/* References:
14 * USB Mass Storage Class specifications:
15 * http://www.usb.org/developers/data/devclass/usbmassover_11.pdf
16 * http://www.usb.org/developers/data/devclass/usbmass-ufi10.pdf
17 * http://www.usb.org/developers/data/devclass/usbmass-cbi10.pdf
18 */
19
20#include <string.h>
21
22#include "usb_scsi.h"
23
24#include "device_info.h"
25
26#include "proto_module.h"
27#include "proto_common.h"
28#include "proto_cbi.h"
29
30#include "usb_defs.h"
31
32#define USB_REQ_CBI_ADSC 0x00
33
34
35typedef struct _usb_mass_CBI_CB{
36	uint8 op;
37	uint8 op2;
38	uint8 padding[14];
39} usb_mass_CBI_CB;
40
41#define CDB_LEN 16
42#define CDB_RESET_LEN 12
43
44//typedef uint8 usb_mass_CDB[CDB_LEN];
45
46typedef union _usb_mass_CBI_IDB{
47	struct {
48		uint8 type;
49		uint8 value;
50	} common;
51	struct {
52		uint8 asc;
53		uint8 ascq;
54	} ufi;
55} usb_mass_CBI_IDB;
56
57#define CBI_IDB_TYPE_CCI			0x00
58#define CBI_IDB_VALUE_PASS			0x00
59#define CBI_IDB_VALUE_FAIL			0x01
60#define CBI_IDB_VALUE_PHASE			0x02
61#define CBI_IDB_VALUE_PERSISTENT	0x03
62#define CBI_IDB_VALUE_STATUS_MASK	0x03
63
64static void trace_CDB(usb_device_info *udi, const usb_mass_CBI_CB *cb, int len);
65static status_t cbi_reset(usb_device_info *udi);
66static status_t cbi_initialize(usb_device_info *udi);
67static void cbi_transfer(usb_device_info *udi, uint8 *cmd, uint8 cmdlen, //sg_buffer *sgb,
68						iovec *sg_data, int32 sg_count,	 int32	transfer_len,
69						EDirection	dir, CCB_SCSIIO *ccbio,	ud_transfer_callback cb);
70
71/** returns size of private protocol buffer */
72//int cbi_buffer_length(){ return sizeof(usb_mass_CBI_CB) + sizeof(usb_mass_CBI_IDB); }
73/** casts private protocol buffer to CBI_IDB */
74/*#define IDB_BUFFER(__buff)\
75							 ((usb_mass_CBI_IDB*)(((uint8*)__buff) + sizeof(usb_mass_CBI_CB)))
76*/
77/** casts private protocol buffer to CBI_CB */
78/*#define CB_BUFFER(__buff)\
79							 ((usb_mass_CBI_CB*)(__buff))
80*/
81void trace_CDB(usb_device_info *udi, const usb_mass_CBI_CB *cb, int len)
82{
83	PTRACE(udi, "CB:{op:0x%02x; op2:0x%02x;}\n", cb->op, cb->op2);
84	udi->trace_bytes(" padding:", cb->padding, len - 2);
85}
86
87static status_t
88send_request_adsc(usb_device_info *udi,	void *cb, int length)
89{
90	status_t status = B_ERROR;
91	size_t len = 0;
92	trace_CDB(udi, cb, length);
93	status = (*udi->usb_m->send_request)(udi->device,
94						USB_REQTYPE_CLASS | USB_REQTYPE_INTERFACE_OUT,
95						USB_REQ_CBI_ADSC, 0,
96						udi->interface, length,
97						cb, &len);
98	return status;
99}
100
101static status_t
102request_interrupt(usb_device_info *udi, usb_mass_CBI_IDB *idb)
103{
104	status_t status = B_ERROR;
105	status = (*udi->usb_m->queue_interrupt)(udi->pipe_intr, idb,
106					 sizeof(usb_mass_CBI_IDB), bulk_callback, udi);
107	if(status != B_OK){
108		PTRACE(udi, "request_interrupt:failed:%08x\n", status);
109	} else {
110		status = acquire_sem_etc(udi->trans_sem, 1, B_RELATIVE_TIMEOUT, udi->trans_timeout/*LOCK_TIMEOUT*/);
111		if(status != B_OK){
112			PTRACE(udi, "request_interrupt:acquire_sem_etc failed:%08x\n", status);
113			(*udi->usb_m->cancel_queued_transfers)(udi->pipe_intr);
114		}
115	}
116	udi->trace_bytes("Intr status:", (uint8*)idb, sizeof(usb_mass_CBI_IDB));
117	return status;
118}
119
120static status_t
121parse_status(usb_device_info *udi, usb_mass_CBI_IDB *idb)
122{
123	status_t command_status = B_OK;
124	if(CMDSET(udi->properties) == CMDSET_UFI){
125		if(idb->ufi.asc == 0 && idb->ufi.ascq == 0){
126			command_status = B_OK;
127		}else{
128			command_status = B_CMD_FAILED;
129		}
130	} else {
131		if(idb->common.type == CBI_IDB_TYPE_CCI){
132			switch(idb->common.value & CBI_IDB_VALUE_STATUS_MASK){
133			case CBI_IDB_VALUE_PASS:
134				command_status = B_OK;
135				break;
136			case CBI_IDB_VALUE_FAIL:
137			case CBI_IDB_VALUE_PERSISTENT:
138				command_status = B_CMD_FAILED;
139				break;
140			case CBI_IDB_VALUE_PHASE:
141			default:
142				command_status = B_CMD_WIRE_FAILED;
143				break;
144			}
145		} /* else ?? */
146	}
147	return command_status;
148}
149
150/*================= "standard" protocol procedures ==============================*/
151
152/**
153	\fn:
154	\param udi: ???
155	\return:??
156
157	??
158*/
159status_t
160cbi_reset(usb_device_info *udi)
161{
162	status_t status = B_ERROR;
163	//usb_mass_CBI_CB *cb = CB_BUFFER(udi->proto_buf);
164	//usb_mass_CDB cdb = {0x1d, 0x04, 0x00};
165	//memset(cdb + 2, 0xff, CDB_RESET_LEN - 2);
166	usb_mass_CBI_CB cb = {
167		.op	= 0x1D,
168		.op2 = 0x04,
169	};
170	memset(cb.padding , 0xff, sizeof(cb.padding));
171	status = send_request_adsc(udi, &cb, CDB_RESET_LEN);
172	if(status != B_OK)
173		PTRACE_ALWAYS(udi, "command_block_reset: reset request failed: %08x\n", status);
174
175	if(B_OK != (status = (*udi->usb_m->clear_feature)(udi->pipe_in,
176										 USB_FEATURE_ENDPOINT_HALT)))
177		PTRACE_ALWAYS(udi, "command_block_reset: clear_feature on pipe_in failed: %08x\n", status);
178
179	if(B_OK != (status = (*udi->usb_m->clear_feature)(udi->pipe_out,
180										 USB_FEATURE_ENDPOINT_HALT)))
181		PTRACE_ALWAYS(udi, "command_block_reset: clear_feature on pipe_out failed: %08x\n", status);
182	PTRACE(udi, "command_block_reset:%08x\n", status);
183
184	return status;
185}
186
187/**
188	\fn:
189*/
190status_t
191cbi_initialize(usb_device_info *udi)
192{
193	status_t status = B_OK;
194	udi->max_lun = 0;
195	return status;
196}
197
198/**
199	\fn:bulk_only_transfer
200	\param udi: ???
201	\param cmd: ???
202	\param cmdlen: ???
203	\return:???
204
205	???
206*/
207void
208cbi_transfer(usb_device_info *udi, uint8 *cmd, uint8	cmdlen,
209											 //sg_buffer *sgb,
210			iovec *sg_data, int32 sg_count, int32	transfer_len,
211			EDirection	dir,CCB_SCSIIO *ccbio, ud_transfer_callback cb)
212{
213	status_t status = B_OK;
214	status_t command_status = B_OK;
215	int32 residue = transfer_len;
216	usb_mass_CBI_IDB idb = {{0}};
217	do{
218		status = send_request_adsc(udi, cmd, cmdlen);
219		if(status != B_OK){
220			PTRACE(udi, "cbi_transfer:send command block failed: %08x\n", status);
221			if(status == B_DEV_STALLED){
222				command_status = B_CMD_FAILED;
223			} else {
224				command_status = B_CMD_WIRE_FAILED;
225				(*udi->protocol_m->reset)(udi);
226			}
227			break;
228		}
229		if(transfer_len != 0){
230			status = process_data_io(udi, sg_data, sg_count, dir);
231			if(status != B_OK){
232				command_status = B_CMD_WIRE_FAILED;
233				break;
234			}
235		}
236
237		if(PROTO(udi->properties) == PROTO_CBI){
238			status = request_interrupt(udi, &idb);
239			PTRACE(udi, "cbi_transfer:request interrupt: %08x(%08x)\n", status, udi->status);
240			if(status != B_OK){
241				command_status = B_CMD_WIRE_FAILED;
242				break;
243			}
244			if(udi->status == B_DEV_STALLED){
245			}
246			command_status = parse_status(udi, &idb);
247		} else { /* PROP_CB ???*/
248			command_status = B_CMD_UNKNOWN;
249		}
250		residue = 0; /* DEBUG!!!!!!!!!*/
251	}while(false);
252	cb(udi, ccbio, residue, command_status);
253}
254
255protocol_module_info cbi_protocol_m = {
256	{0, 0, 0}, /* this is not a real kernel module - just interface */
257	cbi_initialize,
258	cbi_reset,
259	cbi_transfer,
260};
261
262