xref: /haiku/src/add-ons/kernel/busses/scsi/usb/proto_cbi.c (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
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 
35 typedef 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 
46 typedef 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 
64 static void trace_CDB(usb_device_info *udi, const usb_mass_CBI_CB *cb, int len);
65 static status_t cbi_reset(usb_device_info *udi);
66 static status_t cbi_initialize(usb_device_info *udi);
67 static 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 */
81 void 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 
87 static status_t
88 send_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 
101 static status_t
102 request_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 
120 static status_t
121 parse_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 */
159 status_t
160 cbi_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 */
190 status_t
191 cbi_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 */
207 void
208 cbi_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 
255 protocol_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