Deleted Added
full compact
usb_msctest.c (194228) usb_msctest.c (194677)
1/* $FreeBSD: head/sys/dev/usb/usb_msctest.c 194228 2009-06-15 01:02:43Z thompsa $ */
1/* $FreeBSD: head/sys/dev/usb/usb_msctest.c 194677 2009-06-23 02:19:59Z thompsa $ */
2/*-
3 * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27/*
28 * The following file contains code that will detect USB autoinstall
29 * disks.
30 *
31 * TODO: Potentially we could add code to automatically detect USB
32 * mass storage quirks for not supported SCSI commands!
33 */
34
2/*-
3 * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27/*
28 * The following file contains code that will detect USB autoinstall
29 * disks.
30 *
31 * TODO: Potentially we could add code to automatically detect USB
32 * mass storage quirks for not supported SCSI commands!
33 */
34
35#include <dev/usb/usb_mfunc.h>
36#include <dev/usb/usb_error.h>
35#include <sys/stdint.h>
36#include <sys/stddef.h>
37#include <sys/param.h>
38#include <sys/queue.h>
39#include <sys/types.h>
40#include <sys/systm.h>
41#include <sys/kernel.h>
42#include <sys/bus.h>
43#include <sys/linker_set.h>
44#include <sys/module.h>
45#include <sys/lock.h>
46#include <sys/mutex.h>
47#include <sys/condvar.h>
48#include <sys/sysctl.h>
49#include <sys/sx.h>
50#include <sys/unistd.h>
51#include <sys/callout.h>
52#include <sys/malloc.h>
53#include <sys/priv.h>
54
37#include <dev/usb/usb.h>
55#include <dev/usb/usb.h>
56#include <dev/usb/usbdi.h>
57#include <dev/usb/usbdi_util.h>
38
39#define USB_DEBUG_VAR usb_debug
40
58
59#define USB_DEBUG_VAR usb_debug
60
41#include <dev/usb/usb_core.h>
42#include <dev/usb/usb_busdma.h>
43#include <dev/usb/usb_process.h>
44#include <dev/usb/usb_transfer.h>
45#include <dev/usb/usb_msctest.h>
46#include <dev/usb/usb_debug.h>
47#include <dev/usb/usb_busdma.h>
48#include <dev/usb/usb_device.h>
49#include <dev/usb/usb_request.h>
50#include <dev/usb/usb_util.h>
61#include <dev/usb/usb_busdma.h>
62#include <dev/usb/usb_process.h>
63#include <dev/usb/usb_transfer.h>
64#include <dev/usb/usb_msctest.h>
65#include <dev/usb/usb_debug.h>
66#include <dev/usb/usb_busdma.h>
67#include <dev/usb/usb_device.h>
68#include <dev/usb/usb_request.h>
69#include <dev/usb/usb_util.h>
51#include <dev/usb/usb_lookup.h>
52
70
53#include <dev/usb/usb_mfunc.h>
54#include <dev/usb/usb_error.h>
55#include <dev/usb/usb.h>
56
57enum {
58 ST_COMMAND,
59 ST_DATA_RD,
60 ST_DATA_RD_CS,
61 ST_DATA_WR,
62 ST_DATA_WR_CS,
63 ST_STATUS,
64 ST_MAX,
65};
66
67enum {
68 DIR_IN,
69 DIR_OUT,
70 DIR_NONE,
71};
72
73#define BULK_SIZE 64 /* dummy */
74
75/* Command Block Wrapper */
76struct bbb_cbw {
77 uDWord dCBWSignature;
78#define CBWSIGNATURE 0x43425355
79 uDWord dCBWTag;
80 uDWord dCBWDataTransferLength;
81 uByte bCBWFlags;
82#define CBWFLAGS_OUT 0x00
83#define CBWFLAGS_IN 0x80
84 uByte bCBWLUN;
85 uByte bCDBLength;
86#define CBWCDBLENGTH 16
87 uByte CBWCDB[CBWCDBLENGTH];
88} __packed;
89
90/* Command Status Wrapper */
91struct bbb_csw {
92 uDWord dCSWSignature;
93#define CSWSIGNATURE 0x53425355
94 uDWord dCSWTag;
95 uDWord dCSWDataResidue;
96 uByte bCSWStatus;
97#define CSWSTATUS_GOOD 0x0
98#define CSWSTATUS_FAILED 0x1
99#define CSWSTATUS_PHASE 0x2
100} __packed;
101
102struct bbb_transfer {
103 struct mtx mtx;
104 struct cv cv;
105 struct bbb_cbw cbw;
106 struct bbb_csw csw;
107
108 struct usb_xfer *xfer[ST_MAX];
109
110 uint8_t *data_ptr;
111
112 usb_size_t data_len; /* bytes */
113 usb_size_t data_rem; /* bytes */
114 usb_timeout_t data_timeout; /* ms */
115 usb_frlength_t actlen; /* bytes */
116
117 uint8_t cmd_len; /* bytes */
118 uint8_t dir;
119 uint8_t lun;
120 uint8_t state;
121 uint8_t error;
122 uint8_t status_try;
123
124 uint8_t buffer[256];
125};
126
127static usb_callback_t bbb_command_callback;
128static usb_callback_t bbb_data_read_callback;
129static usb_callback_t bbb_data_rd_cs_callback;
130static usb_callback_t bbb_data_write_callback;
131static usb_callback_t bbb_data_wr_cs_callback;
132static usb_callback_t bbb_status_callback;
133
134static const struct usb_config bbb_config[ST_MAX] = {
135
136 [ST_COMMAND] = {
137 .type = UE_BULK,
138 .endpoint = UE_ADDR_ANY,
139 .direction = UE_DIR_OUT,
140 .bufsize = sizeof(struct bbb_cbw),
141 .callback = &bbb_command_callback,
142 .timeout = 4 * USB_MS_HZ, /* 4 seconds */
143 },
144
145 [ST_DATA_RD] = {
146 .type = UE_BULK,
147 .endpoint = UE_ADDR_ANY,
148 .direction = UE_DIR_IN,
149 .bufsize = BULK_SIZE,
150 .flags = {.proxy_buffer = 1,.short_xfer_ok = 1,},
151 .callback = &bbb_data_read_callback,
152 .timeout = 4 * USB_MS_HZ, /* 4 seconds */
153 },
154
155 [ST_DATA_RD_CS] = {
156 .type = UE_CONTROL,
157 .endpoint = 0x00, /* Control pipe */
158 .direction = UE_DIR_ANY,
159 .bufsize = sizeof(struct usb_device_request),
160 .callback = &bbb_data_rd_cs_callback,
161 .timeout = 1 * USB_MS_HZ, /* 1 second */
162 },
163
164 [ST_DATA_WR] = {
165 .type = UE_BULK,
166 .endpoint = UE_ADDR_ANY,
167 .direction = UE_DIR_OUT,
168 .bufsize = BULK_SIZE,
169 .flags = {.proxy_buffer = 1,},
170 .callback = &bbb_data_write_callback,
171 .timeout = 4 * USB_MS_HZ, /* 4 seconds */
172 },
173
174 [ST_DATA_WR_CS] = {
175 .type = UE_CONTROL,
176 .endpoint = 0x00, /* Control pipe */
177 .direction = UE_DIR_ANY,
178 .bufsize = sizeof(struct usb_device_request),
179 .callback = &bbb_data_wr_cs_callback,
180 .timeout = 1 * USB_MS_HZ, /* 1 second */
181 },
182
183 [ST_STATUS] = {
184 .type = UE_BULK,
185 .endpoint = UE_ADDR_ANY,
186 .direction = UE_DIR_IN,
187 .bufsize = sizeof(struct bbb_csw),
188 .flags = {.short_xfer_ok = 1,},
189 .callback = &bbb_status_callback,
190 .timeout = 1 * USB_MS_HZ, /* 1 second */
191 },
192};
193
194static void
195bbb_done(struct bbb_transfer *sc, uint8_t error)
196{
197 struct usb_xfer *xfer;
198
199 xfer = sc->xfer[sc->state];
200
201 /* verify the error code */
202
203 if (error) {
204 switch (USB_GET_STATE(xfer)) {
205 case USB_ST_SETUP:
206 case USB_ST_TRANSFERRED:
207 error = 1;
208 break;
209 default:
210 error = 2;
211 break;
212 }
213 }
214 sc->error = error;
215 sc->state = ST_COMMAND;
216 sc->status_try = 1;
217 cv_signal(&sc->cv);
218}
219
220static void
221bbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index)
222{
223 sc->state = xfer_index;
224 usbd_transfer_start(sc->xfer[xfer_index]);
225}
226
227static void
228bbb_data_clear_stall_callback(struct usb_xfer *xfer,
229 uint8_t next_xfer, uint8_t stall_xfer)
230{
71#include <dev/usb/usb.h>
72
73enum {
74 ST_COMMAND,
75 ST_DATA_RD,
76 ST_DATA_RD_CS,
77 ST_DATA_WR,
78 ST_DATA_WR_CS,
79 ST_STATUS,
80 ST_MAX,
81};
82
83enum {
84 DIR_IN,
85 DIR_OUT,
86 DIR_NONE,
87};
88
89#define BULK_SIZE 64 /* dummy */
90
91/* Command Block Wrapper */
92struct bbb_cbw {
93 uDWord dCBWSignature;
94#define CBWSIGNATURE 0x43425355
95 uDWord dCBWTag;
96 uDWord dCBWDataTransferLength;
97 uByte bCBWFlags;
98#define CBWFLAGS_OUT 0x00
99#define CBWFLAGS_IN 0x80
100 uByte bCBWLUN;
101 uByte bCDBLength;
102#define CBWCDBLENGTH 16
103 uByte CBWCDB[CBWCDBLENGTH];
104} __packed;
105
106/* Command Status Wrapper */
107struct bbb_csw {
108 uDWord dCSWSignature;
109#define CSWSIGNATURE 0x53425355
110 uDWord dCSWTag;
111 uDWord dCSWDataResidue;
112 uByte bCSWStatus;
113#define CSWSTATUS_GOOD 0x0
114#define CSWSTATUS_FAILED 0x1
115#define CSWSTATUS_PHASE 0x2
116} __packed;
117
118struct bbb_transfer {
119 struct mtx mtx;
120 struct cv cv;
121 struct bbb_cbw cbw;
122 struct bbb_csw csw;
123
124 struct usb_xfer *xfer[ST_MAX];
125
126 uint8_t *data_ptr;
127
128 usb_size_t data_len; /* bytes */
129 usb_size_t data_rem; /* bytes */
130 usb_timeout_t data_timeout; /* ms */
131 usb_frlength_t actlen; /* bytes */
132
133 uint8_t cmd_len; /* bytes */
134 uint8_t dir;
135 uint8_t lun;
136 uint8_t state;
137 uint8_t error;
138 uint8_t status_try;
139
140 uint8_t buffer[256];
141};
142
143static usb_callback_t bbb_command_callback;
144static usb_callback_t bbb_data_read_callback;
145static usb_callback_t bbb_data_rd_cs_callback;
146static usb_callback_t bbb_data_write_callback;
147static usb_callback_t bbb_data_wr_cs_callback;
148static usb_callback_t bbb_status_callback;
149
150static const struct usb_config bbb_config[ST_MAX] = {
151
152 [ST_COMMAND] = {
153 .type = UE_BULK,
154 .endpoint = UE_ADDR_ANY,
155 .direction = UE_DIR_OUT,
156 .bufsize = sizeof(struct bbb_cbw),
157 .callback = &bbb_command_callback,
158 .timeout = 4 * USB_MS_HZ, /* 4 seconds */
159 },
160
161 [ST_DATA_RD] = {
162 .type = UE_BULK,
163 .endpoint = UE_ADDR_ANY,
164 .direction = UE_DIR_IN,
165 .bufsize = BULK_SIZE,
166 .flags = {.proxy_buffer = 1,.short_xfer_ok = 1,},
167 .callback = &bbb_data_read_callback,
168 .timeout = 4 * USB_MS_HZ, /* 4 seconds */
169 },
170
171 [ST_DATA_RD_CS] = {
172 .type = UE_CONTROL,
173 .endpoint = 0x00, /* Control pipe */
174 .direction = UE_DIR_ANY,
175 .bufsize = sizeof(struct usb_device_request),
176 .callback = &bbb_data_rd_cs_callback,
177 .timeout = 1 * USB_MS_HZ, /* 1 second */
178 },
179
180 [ST_DATA_WR] = {
181 .type = UE_BULK,
182 .endpoint = UE_ADDR_ANY,
183 .direction = UE_DIR_OUT,
184 .bufsize = BULK_SIZE,
185 .flags = {.proxy_buffer = 1,},
186 .callback = &bbb_data_write_callback,
187 .timeout = 4 * USB_MS_HZ, /* 4 seconds */
188 },
189
190 [ST_DATA_WR_CS] = {
191 .type = UE_CONTROL,
192 .endpoint = 0x00, /* Control pipe */
193 .direction = UE_DIR_ANY,
194 .bufsize = sizeof(struct usb_device_request),
195 .callback = &bbb_data_wr_cs_callback,
196 .timeout = 1 * USB_MS_HZ, /* 1 second */
197 },
198
199 [ST_STATUS] = {
200 .type = UE_BULK,
201 .endpoint = UE_ADDR_ANY,
202 .direction = UE_DIR_IN,
203 .bufsize = sizeof(struct bbb_csw),
204 .flags = {.short_xfer_ok = 1,},
205 .callback = &bbb_status_callback,
206 .timeout = 1 * USB_MS_HZ, /* 1 second */
207 },
208};
209
210static void
211bbb_done(struct bbb_transfer *sc, uint8_t error)
212{
213 struct usb_xfer *xfer;
214
215 xfer = sc->xfer[sc->state];
216
217 /* verify the error code */
218
219 if (error) {
220 switch (USB_GET_STATE(xfer)) {
221 case USB_ST_SETUP:
222 case USB_ST_TRANSFERRED:
223 error = 1;
224 break;
225 default:
226 error = 2;
227 break;
228 }
229 }
230 sc->error = error;
231 sc->state = ST_COMMAND;
232 sc->status_try = 1;
233 cv_signal(&sc->cv);
234}
235
236static void
237bbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index)
238{
239 sc->state = xfer_index;
240 usbd_transfer_start(sc->xfer[xfer_index]);
241}
242
243static void
244bbb_data_clear_stall_callback(struct usb_xfer *xfer,
245 uint8_t next_xfer, uint8_t stall_xfer)
246{
231 struct bbb_transfer *sc = xfer->priv_sc;
247 struct bbb_transfer *sc = usbd_xfer_softc(xfer);
232
233 if (usbd_clear_stall_callback(xfer, sc->xfer[stall_xfer])) {
234 switch (USB_GET_STATE(xfer)) {
235 case USB_ST_SETUP:
236 case USB_ST_TRANSFERRED:
237 bbb_transfer_start(sc, next_xfer);
238 break;
239 default:
240 bbb_done(sc, 1);
241 break;
242 }
243 }
244}
245
246static void
248
249 if (usbd_clear_stall_callback(xfer, sc->xfer[stall_xfer])) {
250 switch (USB_GET_STATE(xfer)) {
251 case USB_ST_SETUP:
252 case USB_ST_TRANSFERRED:
253 bbb_transfer_start(sc, next_xfer);
254 break;
255 default:
256 bbb_done(sc, 1);
257 break;
258 }
259 }
260}
261
262static void
247bbb_command_callback(struct usb_xfer *xfer)
263bbb_command_callback(struct usb_xfer *xfer, usb_error_t error)
248{
264{
249 struct bbb_transfer *sc = xfer->priv_sc;
265 struct bbb_transfer *sc = usbd_xfer_softc(xfer);
250 uint32_t tag;
251
252 switch (USB_GET_STATE(xfer)) {
253 case USB_ST_TRANSFERRED:
254 bbb_transfer_start
255 (sc, ((sc->dir == DIR_IN) ? ST_DATA_RD :
256 (sc->dir == DIR_OUT) ? ST_DATA_WR :
257 ST_STATUS));
258 break;
259
260 case USB_ST_SETUP:
261 sc->status_try = 0;
262 tag = UGETDW(sc->cbw.dCBWTag) + 1;
263 USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE);
264 USETDW(sc->cbw.dCBWTag, tag);
265 USETDW(sc->cbw.dCBWDataTransferLength, (uint32_t)sc->data_len);
266 sc->cbw.bCBWFlags = ((sc->dir == DIR_IN) ? CBWFLAGS_IN : CBWFLAGS_OUT);
267 sc->cbw.bCBWLUN = sc->lun;
268 sc->cbw.bCDBLength = sc->cmd_len;
269 if (sc->cbw.bCDBLength > sizeof(sc->cbw.CBWCDB)) {
270 sc->cbw.bCDBLength = sizeof(sc->cbw.CBWCDB);
271 DPRINTFN(0, "Truncating long command!\n");
272 }
266 uint32_t tag;
267
268 switch (USB_GET_STATE(xfer)) {
269 case USB_ST_TRANSFERRED:
270 bbb_transfer_start
271 (sc, ((sc->dir == DIR_IN) ? ST_DATA_RD :
272 (sc->dir == DIR_OUT) ? ST_DATA_WR :
273 ST_STATUS));
274 break;
275
276 case USB_ST_SETUP:
277 sc->status_try = 0;
278 tag = UGETDW(sc->cbw.dCBWTag) + 1;
279 USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE);
280 USETDW(sc->cbw.dCBWTag, tag);
281 USETDW(sc->cbw.dCBWDataTransferLength, (uint32_t)sc->data_len);
282 sc->cbw.bCBWFlags = ((sc->dir == DIR_IN) ? CBWFLAGS_IN : CBWFLAGS_OUT);
283 sc->cbw.bCBWLUN = sc->lun;
284 sc->cbw.bCDBLength = sc->cmd_len;
285 if (sc->cbw.bCDBLength > sizeof(sc->cbw.CBWCDB)) {
286 sc->cbw.bCDBLength = sizeof(sc->cbw.CBWCDB);
287 DPRINTFN(0, "Truncating long command!\n");
288 }
273 xfer->frlengths[0] = sizeof(sc->cbw);
274
275 usbd_set_frame_data(xfer, &sc->cbw, 0);
289 usbd_xfer_set_frame_data(xfer, 0, &sc->cbw, sizeof(sc->cbw));
276 usbd_transfer_submit(xfer);
277 break;
278
279 default: /* Error */
280 bbb_done(sc, 1);
281 break;
282 }
283}
284
285static void
290 usbd_transfer_submit(xfer);
291 break;
292
293 default: /* Error */
294 bbb_done(sc, 1);
295 break;
296 }
297}
298
299static void
286bbb_data_read_callback(struct usb_xfer *xfer)
300bbb_data_read_callback(struct usb_xfer *xfer, usb_error_t error)
287{
301{
288 struct bbb_transfer *sc = xfer->priv_sc;
289 usb_frlength_t max_bulk = xfer->max_data_length;
302 struct bbb_transfer *sc = usbd_xfer_softc(xfer);
303 usb_frlength_t max_bulk = usbd_xfer_max_len(xfer);
304 int actlen, sumlen;
290
305
306 usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
307
291 switch (USB_GET_STATE(xfer)) {
292 case USB_ST_TRANSFERRED:
308 switch (USB_GET_STATE(xfer)) {
309 case USB_ST_TRANSFERRED:
293 sc->data_rem -= xfer->actlen;
294 sc->data_ptr += xfer->actlen;
295 sc->actlen += xfer->actlen;
310 sc->data_rem -= actlen;
311 sc->data_ptr += actlen;
312 sc->actlen += actlen;
296
313
297 if (xfer->actlen < xfer->sumlen) {
314 if (actlen < sumlen) {
298 /* short transfer */
299 sc->data_rem = 0;
300 }
301 case USB_ST_SETUP:
302 DPRINTF("max_bulk=%d, data_rem=%d\n",
303 max_bulk, sc->data_rem);
304
305 if (sc->data_rem == 0) {
306 bbb_transfer_start(sc, ST_STATUS);
307 break;
308 }
309 if (max_bulk > sc->data_rem) {
310 max_bulk = sc->data_rem;
311 }
315 /* short transfer */
316 sc->data_rem = 0;
317 }
318 case USB_ST_SETUP:
319 DPRINTF("max_bulk=%d, data_rem=%d\n",
320 max_bulk, sc->data_rem);
321
322 if (sc->data_rem == 0) {
323 bbb_transfer_start(sc, ST_STATUS);
324 break;
325 }
326 if (max_bulk > sc->data_rem) {
327 max_bulk = sc->data_rem;
328 }
312 xfer->timeout = sc->data_timeout;
313 xfer->frlengths[0] = max_bulk;
314
315 usbd_set_frame_data(xfer, sc->data_ptr, 0);
329 usbd_xfer_set_timeout(xfer, sc->data_timeout);
330 usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk);
316 usbd_transfer_submit(xfer);
317 break;
318
319 default: /* Error */
331 usbd_transfer_submit(xfer);
332 break;
333
334 default: /* Error */
320 if (xfer->error == USB_ERR_CANCELLED) {
335 if (error == USB_ERR_CANCELLED) {
321 bbb_done(sc, 1);
322 } else {
323 bbb_transfer_start(sc, ST_DATA_RD_CS);
324 }
325 break;
326 }
327}
328
329static void
336 bbb_done(sc, 1);
337 } else {
338 bbb_transfer_start(sc, ST_DATA_RD_CS);
339 }
340 break;
341 }
342}
343
344static void
330bbb_data_rd_cs_callback(struct usb_xfer *xfer)
345bbb_data_rd_cs_callback(struct usb_xfer *xfer, usb_error_t error)
331{
332 bbb_data_clear_stall_callback(xfer, ST_STATUS,
333 ST_DATA_RD);
334}
335
336static void
346{
347 bbb_data_clear_stall_callback(xfer, ST_STATUS,
348 ST_DATA_RD);
349}
350
351static void
337bbb_data_write_callback(struct usb_xfer *xfer)
352bbb_data_write_callback(struct usb_xfer *xfer, usb_error_t error)
338{
353{
339 struct bbb_transfer *sc = xfer->priv_sc;
340 usb_frlength_t max_bulk = xfer->max_data_length;
354 struct bbb_transfer *sc = usbd_xfer_softc(xfer);
355 usb_frlength_t max_bulk = usbd_xfer_max_len(xfer);
356 int actlen, sumlen;
341
357
358 usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
359
342 switch (USB_GET_STATE(xfer)) {
343 case USB_ST_TRANSFERRED:
360 switch (USB_GET_STATE(xfer)) {
361 case USB_ST_TRANSFERRED:
344 sc->data_rem -= xfer->actlen;
345 sc->data_ptr += xfer->actlen;
346 sc->actlen += xfer->actlen;
362 sc->data_rem -= actlen;
363 sc->data_ptr += actlen;
364 sc->actlen += actlen;
347
365
348 if (xfer->actlen < xfer->sumlen) {
366 if (actlen < sumlen) {
349 /* short transfer */
350 sc->data_rem = 0;
351 }
352 case USB_ST_SETUP:
353 DPRINTF("max_bulk=%d, data_rem=%d\n",
354 max_bulk, sc->data_rem);
355
356 if (sc->data_rem == 0) {
357 bbb_transfer_start(sc, ST_STATUS);
358 return;
359 }
360 if (max_bulk > sc->data_rem) {
361 max_bulk = sc->data_rem;
362 }
367 /* short transfer */
368 sc->data_rem = 0;
369 }
370 case USB_ST_SETUP:
371 DPRINTF("max_bulk=%d, data_rem=%d\n",
372 max_bulk, sc->data_rem);
373
374 if (sc->data_rem == 0) {
375 bbb_transfer_start(sc, ST_STATUS);
376 return;
377 }
378 if (max_bulk > sc->data_rem) {
379 max_bulk = sc->data_rem;
380 }
363 xfer->timeout = sc->data_timeout;
364 xfer->frlengths[0] = max_bulk;
365
366 usbd_set_frame_data(xfer, sc->data_ptr, 0);
381 usbd_xfer_set_timeout(xfer, sc->data_timeout);
382 usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk);
367 usbd_transfer_submit(xfer);
368 return;
369
370 default: /* Error */
383 usbd_transfer_submit(xfer);
384 return;
385
386 default: /* Error */
371 if (xfer->error == USB_ERR_CANCELLED) {
387 if (error == USB_ERR_CANCELLED) {
372 bbb_done(sc, 1);
373 } else {
374 bbb_transfer_start(sc, ST_DATA_WR_CS);
375 }
376 return;
377
378 }
379}
380
381static void
388 bbb_done(sc, 1);
389 } else {
390 bbb_transfer_start(sc, ST_DATA_WR_CS);
391 }
392 return;
393
394 }
395}
396
397static void
382bbb_data_wr_cs_callback(struct usb_xfer *xfer)
398bbb_data_wr_cs_callback(struct usb_xfer *xfer, usb_error_t error)
383{
384 bbb_data_clear_stall_callback(xfer, ST_STATUS,
385 ST_DATA_WR);
386}
387
388static void
399{
400 bbb_data_clear_stall_callback(xfer, ST_STATUS,
401 ST_DATA_WR);
402}
403
404static void
389bbb_status_callback(struct usb_xfer *xfer)
405bbb_status_callback(struct usb_xfer *xfer, usb_error_t error)
390{
406{
391 struct bbb_transfer *sc = xfer->priv_sc;
407 struct bbb_transfer *sc = usbd_xfer_softc(xfer);
408 int actlen, sumlen;
392
409
410 usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
411
393 switch (USB_GET_STATE(xfer)) {
394 case USB_ST_TRANSFERRED:
395
396 /* very simple status check */
397
412 switch (USB_GET_STATE(xfer)) {
413 case USB_ST_TRANSFERRED:
414
415 /* very simple status check */
416
398 if (xfer->actlen < sizeof(sc->csw)) {
417 if (actlen < sizeof(sc->csw)) {
399 bbb_done(sc, 1);/* error */
400 } else if (sc->csw.bCSWStatus == CSWSTATUS_GOOD) {
401 bbb_done(sc, 0);/* success */
402 } else {
403 bbb_done(sc, 1);/* error */
404 }
405 break;
406
407 case USB_ST_SETUP:
418 bbb_done(sc, 1);/* error */
419 } else if (sc->csw.bCSWStatus == CSWSTATUS_GOOD) {
420 bbb_done(sc, 0);/* success */
421 } else {
422 bbb_done(sc, 1);/* error */
423 }
424 break;
425
426 case USB_ST_SETUP:
408 xfer->frlengths[0] = sizeof(sc->csw);
409
410 usbd_set_frame_data(xfer, &sc->csw, 0);
427 usbd_xfer_set_frame_data(xfer, 0, &sc->csw, sizeof(sc->csw));
411 usbd_transfer_submit(xfer);
412 break;
413
414 default:
415 DPRINTFN(0, "Failed to read CSW: %s, try %d\n",
428 usbd_transfer_submit(xfer);
429 break;
430
431 default:
432 DPRINTFN(0, "Failed to read CSW: %s, try %d\n",
416 usbd_errstr(xfer->error), sc->status_try);
433 usbd_errstr(error), sc->status_try);
417
434
418 if ((xfer->error == USB_ERR_CANCELLED) ||
419 (sc->status_try)) {
435 if (error == USB_ERR_CANCELLED || sc->status_try) {
420 bbb_done(sc, 1);
421 } else {
422 sc->status_try = 1;
423 bbb_transfer_start(sc, ST_DATA_RD_CS);
424 }
425 break;
426 }
427}
428
429/*------------------------------------------------------------------------*
430 * bbb_command_start - execute a SCSI command synchronously
431 *
432 * Return values
433 * 0: Success
434 * Else: Failure
435 *------------------------------------------------------------------------*/
436static uint8_t
437bbb_command_start(struct bbb_transfer *sc, uint8_t dir, uint8_t lun,
438 void *data_ptr, usb_size_t data_len, uint8_t cmd_len,
439 usb_timeout_t data_timeout)
440{
441 sc->lun = lun;
442 sc->dir = data_len ? dir : DIR_NONE;
443 sc->data_ptr = data_ptr;
444 sc->data_len = data_len;
445 sc->data_rem = data_len;
446 sc->data_timeout = (data_timeout + USB_MS_HZ);
447 sc->actlen = 0;
448 sc->cmd_len = cmd_len;
449
450 usbd_transfer_start(sc->xfer[sc->state]);
451
452 while (usbd_transfer_pending(sc->xfer[sc->state])) {
453 cv_wait(&sc->cv, &sc->mtx);
454 }
455 return (sc->error);
456}
457
458/*------------------------------------------------------------------------*
459 * usb_test_autoinstall
460 *
461 * Return values:
462 * 0: This interface is an auto install disk (CD-ROM)
463 * Else: Not an auto install disk.
464 *------------------------------------------------------------------------*/
465usb_error_t
466usb_test_autoinstall(struct usb_device *udev, uint8_t iface_index,
467 uint8_t do_eject)
468{
469 struct usb_interface *iface;
470 struct usb_interface_descriptor *id;
471 usb_error_t err;
472 uint8_t timeout;
473 uint8_t sid_type;
474 struct bbb_transfer *sc;
475
476 if (udev == NULL) {
477 return (USB_ERR_INVAL);
478 }
479 iface = usbd_get_iface(udev, iface_index);
480 if (iface == NULL) {
481 return (USB_ERR_INVAL);
482 }
483 id = iface->idesc;
484 if (id == NULL) {
485 return (USB_ERR_INVAL);
486 }
487 if (id->bInterfaceClass != UICLASS_MASS) {
488 return (USB_ERR_INVAL);
489 }
490 switch (id->bInterfaceSubClass) {
491 case UISUBCLASS_SCSI:
492 case UISUBCLASS_UFI:
493 break;
494 default:
495 return (USB_ERR_INVAL);
496 }
497
498 switch (id->bInterfaceProtocol) {
499 case UIPROTO_MASS_BBB_OLD:
500 case UIPROTO_MASS_BBB:
501 break;
502 default:
503 return (USB_ERR_INVAL);
504 }
505
506 sc = malloc(sizeof(*sc), M_USB, M_WAITOK | M_ZERO);
507 if (sc == NULL) {
508 return (USB_ERR_NOMEM);
509 }
510 mtx_init(&sc->mtx, "USB autoinstall", NULL, MTX_DEF);
511 cv_init(&sc->cv, "WBBB");
512
513 err = usbd_transfer_setup(udev,
514 &iface_index, sc->xfer, bbb_config,
515 ST_MAX, sc, &sc->mtx);
516
517 if (err) {
518 goto done;
519 }
520 mtx_lock(&sc->mtx);
521
522 timeout = 4; /* tries */
523
524repeat_inquiry:
525
526 sc->cbw.CBWCDB[0] = 0x12; /* INQUIRY */
527 sc->cbw.CBWCDB[1] = 0;
528 sc->cbw.CBWCDB[2] = 0;
529 sc->cbw.CBWCDB[3] = 0;
530 sc->cbw.CBWCDB[4] = 0x24; /* length */
531 sc->cbw.CBWCDB[5] = 0;
532 err = bbb_command_start(sc, DIR_IN, 0,
533 sc->buffer, 0x24, 6, USB_MS_HZ);
534
535 if ((sc->actlen != 0) && (err == 0)) {
536 sid_type = sc->buffer[0] & 0x1F;
537 if (sid_type == 0x05) {
538 /* CD-ROM */
539 if (do_eject) {
540 /* 0: opcode: SCSI START/STOP */
541 sc->cbw.CBWCDB[0] = 0x1b;
542 /* 1: byte2: Not immediate */
543 sc->cbw.CBWCDB[1] = 0x00;
544 /* 2..3: reserved */
545 sc->cbw.CBWCDB[2] = 0x00;
546 sc->cbw.CBWCDB[3] = 0x00;
547 /* 4: Load/Eject command */
548 sc->cbw.CBWCDB[4] = 0x02;
549 /* 5: control */
550 sc->cbw.CBWCDB[5] = 0x00;
551 err = bbb_command_start(sc, DIR_OUT, 0,
552 NULL, 0, 6, USB_MS_HZ);
553
554 DPRINTFN(0, "Eject CD command "
555 "status: %s\n", usbd_errstr(err));
556 }
557 err = 0;
558 goto done;
559 }
560 } else if ((err != 2) && --timeout) {
561 usb_pause_mtx(&sc->mtx, hz);
562 goto repeat_inquiry;
563 }
564 err = USB_ERR_INVAL;
565 goto done;
566
567done:
568 mtx_unlock(&sc->mtx);
569 usbd_transfer_unsetup(sc->xfer, ST_MAX);
570 mtx_destroy(&sc->mtx);
571 cv_destroy(&sc->cv);
572 free(sc, M_USB);
573 return (err);
574}
436 bbb_done(sc, 1);
437 } else {
438 sc->status_try = 1;
439 bbb_transfer_start(sc, ST_DATA_RD_CS);
440 }
441 break;
442 }
443}
444
445/*------------------------------------------------------------------------*
446 * bbb_command_start - execute a SCSI command synchronously
447 *
448 * Return values
449 * 0: Success
450 * Else: Failure
451 *------------------------------------------------------------------------*/
452static uint8_t
453bbb_command_start(struct bbb_transfer *sc, uint8_t dir, uint8_t lun,
454 void *data_ptr, usb_size_t data_len, uint8_t cmd_len,
455 usb_timeout_t data_timeout)
456{
457 sc->lun = lun;
458 sc->dir = data_len ? dir : DIR_NONE;
459 sc->data_ptr = data_ptr;
460 sc->data_len = data_len;
461 sc->data_rem = data_len;
462 sc->data_timeout = (data_timeout + USB_MS_HZ);
463 sc->actlen = 0;
464 sc->cmd_len = cmd_len;
465
466 usbd_transfer_start(sc->xfer[sc->state]);
467
468 while (usbd_transfer_pending(sc->xfer[sc->state])) {
469 cv_wait(&sc->cv, &sc->mtx);
470 }
471 return (sc->error);
472}
473
474/*------------------------------------------------------------------------*
475 * usb_test_autoinstall
476 *
477 * Return values:
478 * 0: This interface is an auto install disk (CD-ROM)
479 * Else: Not an auto install disk.
480 *------------------------------------------------------------------------*/
481usb_error_t
482usb_test_autoinstall(struct usb_device *udev, uint8_t iface_index,
483 uint8_t do_eject)
484{
485 struct usb_interface *iface;
486 struct usb_interface_descriptor *id;
487 usb_error_t err;
488 uint8_t timeout;
489 uint8_t sid_type;
490 struct bbb_transfer *sc;
491
492 if (udev == NULL) {
493 return (USB_ERR_INVAL);
494 }
495 iface = usbd_get_iface(udev, iface_index);
496 if (iface == NULL) {
497 return (USB_ERR_INVAL);
498 }
499 id = iface->idesc;
500 if (id == NULL) {
501 return (USB_ERR_INVAL);
502 }
503 if (id->bInterfaceClass != UICLASS_MASS) {
504 return (USB_ERR_INVAL);
505 }
506 switch (id->bInterfaceSubClass) {
507 case UISUBCLASS_SCSI:
508 case UISUBCLASS_UFI:
509 break;
510 default:
511 return (USB_ERR_INVAL);
512 }
513
514 switch (id->bInterfaceProtocol) {
515 case UIPROTO_MASS_BBB_OLD:
516 case UIPROTO_MASS_BBB:
517 break;
518 default:
519 return (USB_ERR_INVAL);
520 }
521
522 sc = malloc(sizeof(*sc), M_USB, M_WAITOK | M_ZERO);
523 if (sc == NULL) {
524 return (USB_ERR_NOMEM);
525 }
526 mtx_init(&sc->mtx, "USB autoinstall", NULL, MTX_DEF);
527 cv_init(&sc->cv, "WBBB");
528
529 err = usbd_transfer_setup(udev,
530 &iface_index, sc->xfer, bbb_config,
531 ST_MAX, sc, &sc->mtx);
532
533 if (err) {
534 goto done;
535 }
536 mtx_lock(&sc->mtx);
537
538 timeout = 4; /* tries */
539
540repeat_inquiry:
541
542 sc->cbw.CBWCDB[0] = 0x12; /* INQUIRY */
543 sc->cbw.CBWCDB[1] = 0;
544 sc->cbw.CBWCDB[2] = 0;
545 sc->cbw.CBWCDB[3] = 0;
546 sc->cbw.CBWCDB[4] = 0x24; /* length */
547 sc->cbw.CBWCDB[5] = 0;
548 err = bbb_command_start(sc, DIR_IN, 0,
549 sc->buffer, 0x24, 6, USB_MS_HZ);
550
551 if ((sc->actlen != 0) && (err == 0)) {
552 sid_type = sc->buffer[0] & 0x1F;
553 if (sid_type == 0x05) {
554 /* CD-ROM */
555 if (do_eject) {
556 /* 0: opcode: SCSI START/STOP */
557 sc->cbw.CBWCDB[0] = 0x1b;
558 /* 1: byte2: Not immediate */
559 sc->cbw.CBWCDB[1] = 0x00;
560 /* 2..3: reserved */
561 sc->cbw.CBWCDB[2] = 0x00;
562 sc->cbw.CBWCDB[3] = 0x00;
563 /* 4: Load/Eject command */
564 sc->cbw.CBWCDB[4] = 0x02;
565 /* 5: control */
566 sc->cbw.CBWCDB[5] = 0x00;
567 err = bbb_command_start(sc, DIR_OUT, 0,
568 NULL, 0, 6, USB_MS_HZ);
569
570 DPRINTFN(0, "Eject CD command "
571 "status: %s\n", usbd_errstr(err));
572 }
573 err = 0;
574 goto done;
575 }
576 } else if ((err != 2) && --timeout) {
577 usb_pause_mtx(&sc->mtx, hz);
578 goto repeat_inquiry;
579 }
580 err = USB_ERR_INVAL;
581 goto done;
582
583done:
584 mtx_unlock(&sc->mtx);
585 usbd_transfer_unsetup(sc->xfer, ST_MAX);
586 mtx_destroy(&sc->mtx);
587 cv_destroy(&sc->cv);
588 free(sc, M_USB);
589 return (err);
590}