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