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