1 | /* $NetBSD: usscanner.c,v 1.40 2016/07/07 06:55:42 msaitoh Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2001 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Lennart Augustsson (lennart@augustsson.net) and LLoyd Parkes. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | /* |
33 | * This driver is partly based on information taken from the Linux driver |
34 | * by John Fremlin, Oliver Neukum, and Jeremy Hall. |
35 | */ |
36 | /* |
37 | * Protocol: |
38 | * Send raw SCSI command on the bulk-out pipe. |
39 | * If output command then |
40 | * send further data on the bulk-out pipe |
41 | * else if input command then |
42 | * read data on the bulk-in pipe |
43 | * else |
44 | * don't do anything. |
45 | * Read status byte on the interrupt pipe (which doesn't seem to be |
46 | * an interrupt pipe at all). This operation sometimes times out. |
47 | */ |
48 | |
49 | #include <sys/cdefs.h> |
50 | __KERNEL_RCSID(0, "$NetBSD: usscanner.c,v 1.40 2016/07/07 06:55:42 msaitoh Exp $" ); |
51 | |
52 | #include "scsibus.h" |
53 | #include <sys/param.h> |
54 | #include <sys/systm.h> |
55 | #include <sys/kernel.h> |
56 | #include <sys/lwp.h> |
57 | #include <sys/device.h> |
58 | #include <sys/conf.h> |
59 | #include <sys/buf.h> |
60 | |
61 | #include <dev/usb/usb.h> |
62 | #include <dev/usb/usbdi.h> |
63 | #include <dev/usb/usbdi_util.h> |
64 | |
65 | #include <dev/usb/usbdevs.h> |
66 | |
67 | #include <sys/scsiio.h> |
68 | #include <dev/scsipi/scsi_spc.h> |
69 | #include <dev/scsipi/scsi_all.h> |
70 | #include <dev/scsipi/scsipi_all.h> |
71 | #include <dev/scsipi/scsiconf.h> |
72 | #include <dev/scsipi/atapiconf.h> |
73 | |
74 | #ifdef USSCANNER_DEBUG |
75 | #define DPRINTF(x) if (usscannerdebug) printf x |
76 | #define DPRINTFN(n,x) if (usscannerdebug>(n)) printf x |
77 | int usscannerdebug = 0; |
78 | #else |
79 | #define DPRINTF(x) |
80 | #define DPRINTFN(n,x) |
81 | #endif |
82 | |
83 | |
84 | #define USSCANNER_CONFIG_NO 1 |
85 | #define USSCANNER_IFACE_IDX 0 |
86 | |
87 | #define USSCANNER_SCSIID_HOST 0x00 |
88 | #define USSCANNER_SCSIID_DEVICE 0x01 |
89 | |
90 | #define USSCANNER_MAX_TRANSFER_SIZE MAXPHYS |
91 | |
92 | #define USSCANNER_TIMEOUT 2000 |
93 | |
94 | struct usscanner_softc { |
95 | device_t sc_dev; |
96 | struct usbd_device *sc_udev; |
97 | struct usbd_interface *sc_iface; |
98 | |
99 | int sc_in_addr; |
100 | struct usbd_pipe *sc_in_pipe; |
101 | |
102 | int sc_intr_addr; |
103 | struct usbd_pipe *sc_intr_pipe; |
104 | struct usbd_xfer *sc_intr_xfer; |
105 | u_char sc_status; |
106 | |
107 | int sc_out_addr; |
108 | struct usbd_pipe *sc_out_pipe; |
109 | |
110 | struct usbd_xfer *sc_cmd_xfer; |
111 | void *sc_cmd_buffer; |
112 | struct usbd_xfer *sc_datain_xfer; |
113 | void *sc_datain_buffer; |
114 | struct usbd_xfer *sc_dataout_xfer; |
115 | void *sc_dataout_buffer; |
116 | |
117 | int sc_state; |
118 | #define UAS_IDLE 0 |
119 | #define UAS_CMD 1 |
120 | #define UAS_DATA 2 |
121 | #define UAS_SENSECMD 3 |
122 | #define UAS_SENSEDATA 4 |
123 | #define UAS_STATUS 5 |
124 | |
125 | struct scsipi_xfer *sc_xs; |
126 | |
127 | device_t sc_child; /* child device, for detach */ |
128 | |
129 | struct scsipi_adapter sc_adapter; |
130 | struct scsipi_channel sc_channel; |
131 | |
132 | int sc_refcnt; |
133 | char sc_dying; |
134 | }; |
135 | |
136 | |
137 | Static void usscanner_cleanup(struct usscanner_softc *); |
138 | Static void usscanner_scsipi_request(struct scsipi_channel *, |
139 | scsipi_adapter_req_t, void *); |
140 | Static void usscanner_scsipi_minphys(struct buf *); |
141 | Static void usscanner_done(struct usscanner_softc *); |
142 | Static void usscanner_sense(struct usscanner_softc *); |
143 | typedef void callback(struct usbd_xfer *, void *, usbd_status); |
144 | Static callback usscanner_intr_cb; |
145 | Static callback usscanner_cmd_cb; |
146 | Static callback usscanner_data_cb; |
147 | Static callback usscanner_sensecmd_cb; |
148 | Static callback usscanner_sensedata_cb; |
149 | |
150 | int usscanner_match(device_t, cfdata_t, void *); |
151 | void usscanner_attach(device_t, device_t, void *); |
152 | void usscanner_childdet(device_t, device_t); |
153 | int usscanner_detach(device_t, int); |
154 | int usscanner_activate(device_t, enum devact); |
155 | extern struct cfdriver usscanner_cd; |
156 | CFATTACH_DECL2_NEW(usscanner, sizeof(struct usscanner_softc), |
157 | usscanner_match, usscanner_attach, usscanner_detach, usscanner_activate, |
158 | NULL, usscanner_childdet); |
159 | |
160 | int |
161 | usscanner_match(device_t parent, cfdata_t match, void *aux) |
162 | { |
163 | struct usb_attach_arg *uaa = aux; |
164 | |
165 | DPRINTFN(50,("usscanner_match\n" )); |
166 | |
167 | if (uaa->uaa_vendor == USB_VENDOR_HP && |
168 | uaa->uaa_product == USB_PRODUCT_HP_5300C) |
169 | return UMATCH_VENDOR_PRODUCT; |
170 | else |
171 | return UMATCH_NONE; |
172 | } |
173 | |
174 | void |
175 | usscanner_attach(device_t parent, device_t self, void *aux) |
176 | { |
177 | struct usscanner_softc *sc = device_private(self); |
178 | struct usb_attach_arg *uaa = aux; |
179 | struct usbd_device * dev = uaa->uaa_device; |
180 | struct usbd_interface * iface; |
181 | char *devinfop; |
182 | usbd_status err; |
183 | usb_endpoint_descriptor_t *ed; |
184 | uint8_t epcount; |
185 | int i; |
186 | int error; |
187 | |
188 | DPRINTFN(10,("usscanner_attach: sc=%p\n" , sc)); |
189 | |
190 | sc->sc_dev = self; |
191 | |
192 | aprint_naive("\n" ); |
193 | aprint_normal("\n" ); |
194 | |
195 | devinfop = usbd_devinfo_alloc(dev, 0); |
196 | aprint_normal_dev(self, "%s\n" , devinfop); |
197 | usbd_devinfo_free(devinfop); |
198 | |
199 | err = usbd_set_config_no(dev, USSCANNER_CONFIG_NO, 1); |
200 | if (err) { |
201 | aprint_error_dev(self, "failed to set configuration, err=%s\n" , |
202 | usbd_errstr(err)); |
203 | return; |
204 | } |
205 | |
206 | err = usbd_device2interface_handle(dev, USSCANNER_IFACE_IDX, &iface); |
207 | if (err) { |
208 | aprint_error_dev(self, "getting interface handle failed\n" ); |
209 | return; |
210 | } |
211 | |
212 | sc->sc_udev = dev; |
213 | sc->sc_iface = iface; |
214 | |
215 | epcount = 0; |
216 | (void)usbd_endpoint_count(iface, &epcount); |
217 | |
218 | sc->sc_in_addr = -1; |
219 | sc->sc_intr_addr = -1; |
220 | sc->sc_out_addr = -1; |
221 | for (i = 0; i < epcount; i++) { |
222 | ed = usbd_interface2endpoint_descriptor(iface, i); |
223 | if (ed == NULL) { |
224 | aprint_error_dev(self, "couldn't get ep %d\n" , i); |
225 | return; |
226 | } |
227 | if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && |
228 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { |
229 | sc->sc_in_addr = ed->bEndpointAddress; |
230 | } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && |
231 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { |
232 | sc->sc_intr_addr = ed->bEndpointAddress; |
233 | } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && |
234 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { |
235 | sc->sc_out_addr = ed->bEndpointAddress; |
236 | } |
237 | } |
238 | if (sc->sc_in_addr == -1 || sc->sc_intr_addr == -1 || |
239 | sc->sc_out_addr == -1) { |
240 | aprint_error_dev(self, "missing endpoint\n" ); |
241 | return; |
242 | } |
243 | |
244 | err = usbd_open_pipe(sc->sc_iface, sc->sc_in_addr, |
245 | USBD_EXCLUSIVE_USE, &sc->sc_in_pipe); |
246 | if (err) { |
247 | aprint_error_dev(self, "open in pipe failed, err=%d\n" , err); |
248 | return; |
249 | } |
250 | |
251 | /* The interrupt endpoint must be opened as a normal pipe. */ |
252 | err = usbd_open_pipe(sc->sc_iface, sc->sc_intr_addr, |
253 | USBD_EXCLUSIVE_USE, &sc->sc_intr_pipe); |
254 | |
255 | if (err) { |
256 | aprint_error_dev(self, "open intr pipe failed, err=%d\n" , err); |
257 | usscanner_cleanup(sc); |
258 | return; |
259 | } |
260 | err = usbd_open_pipe(sc->sc_iface, sc->sc_out_addr, |
261 | USBD_EXCLUSIVE_USE, &sc->sc_out_pipe); |
262 | if (err) { |
263 | aprint_error_dev(self, "open out pipe failed, err=%d\n" , err); |
264 | usscanner_cleanup(sc); |
265 | return; |
266 | } |
267 | |
268 | /* XXX too big */ |
269 | error = usbd_create_xfer(sc->sc_out_pipe, USSCANNER_MAX_TRANSFER_SIZE, |
270 | 0, 0, &sc->sc_cmd_xfer); |
271 | if (error) { |
272 | aprint_error_dev(self, "alloc cmd xfer failed, error=%d\n" , |
273 | error); |
274 | usscanner_cleanup(sc); |
275 | return; |
276 | } |
277 | |
278 | sc->sc_cmd_buffer = usbd_get_buffer(sc->sc_cmd_xfer); |
279 | |
280 | error = usbd_create_xfer(sc->sc_intr_pipe, 1, USBD_SHORT_XFER_OK, |
281 | 0, &sc->sc_intr_xfer); |
282 | if (error) { |
283 | aprint_error_dev(self, "alloc intr xfer failed, error=%d\n" , |
284 | error); |
285 | usscanner_cleanup(sc); |
286 | return; |
287 | } |
288 | |
289 | error = usbd_create_xfer(sc->sc_in_pipe, USSCANNER_MAX_TRANSFER_SIZE, |
290 | USBD_SHORT_XFER_OK, 0, &sc->sc_datain_xfer); |
291 | if (error) { |
292 | aprint_error_dev(self, "alloc data xfer failed, error=%d\n" , |
293 | error); |
294 | usscanner_cleanup(sc); |
295 | return; |
296 | } |
297 | sc->sc_datain_buffer = usbd_get_buffer(sc->sc_datain_xfer); |
298 | |
299 | error = usbd_create_xfer(sc->sc_out_pipe, USSCANNER_MAX_TRANSFER_SIZE, |
300 | USBD_SHORT_XFER_OK, 0, &sc->sc_dataout_xfer); |
301 | if (error) { |
302 | aprint_error_dev(self, "alloc data xfer failed, err=%d\n" , err); |
303 | usscanner_cleanup(sc); |
304 | return; |
305 | } |
306 | sc->sc_dataout_buffer = usbd_get_buffer(sc->sc_dataout_xfer); |
307 | |
308 | /* |
309 | * Fill in the adapter. |
310 | */ |
311 | sc->sc_adapter.adapt_request = usscanner_scsipi_request; |
312 | sc->sc_adapter.adapt_dev = sc->sc_dev; |
313 | sc->sc_adapter.adapt_nchannels = 1; |
314 | sc->sc_adapter.adapt_openings = 1; |
315 | sc->sc_adapter.adapt_max_periph = 1; |
316 | sc->sc_adapter.adapt_minphys = usscanner_scsipi_minphys; |
317 | |
318 | #if NSCSIBUS > 0 |
319 | /* |
320 | * fill in the scsipi_channel. |
321 | */ |
322 | sc->sc_channel.chan_adapter = &sc->sc_adapter; |
323 | sc->sc_channel.chan_bustype = &scsi_bustype; |
324 | sc->sc_channel.chan_channel = 0; |
325 | sc->sc_channel.chan_ntargets = USSCANNER_SCSIID_DEVICE + 1; |
326 | sc->sc_channel.chan_nluns = 1; |
327 | sc->sc_channel.chan_id = USSCANNER_SCSIID_HOST; |
328 | |
329 | usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev); |
330 | |
331 | sc->sc_child = config_found(sc->sc_dev, &sc->sc_channel, scsiprint); |
332 | |
333 | DPRINTFN(10, ("usscanner_attach: %p\n" , sc->sc_udev)); |
334 | |
335 | return; |
336 | |
337 | #else |
338 | /* No SCSI bus, just ignore it */ |
339 | usscanner_cleanup(sc); |
340 | |
341 | aprint_error_dev(self, |
342 | "no scsibus configured, see usscanner(4) for details\n" ); |
343 | |
344 | return; |
345 | |
346 | #endif |
347 | } |
348 | |
349 | void |
350 | usscanner_childdet(device_t self, device_t child) |
351 | { |
352 | struct usscanner_softc *sc = device_private(self); |
353 | |
354 | KASSERT(sc->sc_child == NULL); |
355 | sc->sc_child = NULL; |
356 | } |
357 | |
358 | int |
359 | usscanner_detach(device_t self, int flags) |
360 | { |
361 | struct usscanner_softc *sc = device_private(self); |
362 | int rv, s; |
363 | |
364 | DPRINTF(("usscanner_detach: sc=%p flags=%d\n" , sc, flags)); |
365 | |
366 | sc->sc_dying = 1; |
367 | /* Abort all pipes. Causes processes waiting for transfer to wake. */ |
368 | if (sc->sc_in_pipe != NULL) |
369 | usbd_abort_pipe(sc->sc_in_pipe); |
370 | if (sc->sc_intr_pipe != NULL) |
371 | usbd_abort_pipe(sc->sc_intr_pipe); |
372 | if (sc->sc_out_pipe != NULL) |
373 | usbd_abort_pipe(sc->sc_out_pipe); |
374 | |
375 | s = splusb(); |
376 | if (--sc->sc_refcnt >= 0) { |
377 | /* Wait for processes to go away. */ |
378 | usb_detach_waitold(sc->sc_dev); |
379 | } |
380 | splx(s); |
381 | |
382 | if (sc->sc_child != NULL) |
383 | rv = config_detach(sc->sc_child, flags); |
384 | else |
385 | rv = 0; |
386 | |
387 | usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev); |
388 | |
389 | return rv; |
390 | } |
391 | |
392 | Static void |
393 | usscanner_cleanup(struct usscanner_softc *sc) |
394 | { |
395 | if (sc->sc_cmd_xfer != NULL) { |
396 | usbd_destroy_xfer(sc->sc_cmd_xfer); |
397 | sc->sc_cmd_xfer = NULL; |
398 | } |
399 | if (sc->sc_datain_xfer != NULL) { |
400 | usbd_destroy_xfer(sc->sc_datain_xfer); |
401 | sc->sc_datain_xfer = NULL; |
402 | } |
403 | if (sc->sc_dataout_xfer != NULL) { |
404 | usbd_destroy_xfer(sc->sc_dataout_xfer); |
405 | sc->sc_dataout_xfer = NULL; |
406 | } |
407 | if (sc->sc_in_pipe != NULL) { |
408 | usbd_close_pipe(sc->sc_in_pipe); |
409 | sc->sc_in_pipe = NULL; |
410 | } |
411 | if (sc->sc_intr_pipe != NULL) { |
412 | usbd_close_pipe(sc->sc_intr_pipe); |
413 | sc->sc_intr_pipe = NULL; |
414 | } |
415 | if (sc->sc_out_pipe != NULL) { |
416 | usbd_close_pipe(sc->sc_out_pipe); |
417 | sc->sc_out_pipe = NULL; |
418 | } |
419 | } |
420 | |
421 | int |
422 | usscanner_activate(device_t self, enum devact act) |
423 | { |
424 | struct usscanner_softc *sc = device_private(self); |
425 | |
426 | switch (act) { |
427 | case DVACT_DEACTIVATE: |
428 | sc->sc_dying = 1; |
429 | return 0; |
430 | default: |
431 | return EOPNOTSUPP; |
432 | } |
433 | } |
434 | |
435 | Static void |
436 | usscanner_scsipi_minphys(struct buf *bp) |
437 | { |
438 | if (bp->b_bcount > USSCANNER_MAX_TRANSFER_SIZE) |
439 | bp->b_bcount = USSCANNER_MAX_TRANSFER_SIZE; |
440 | minphys(bp); |
441 | } |
442 | |
443 | Static void |
444 | usscanner_sense(struct usscanner_softc *sc) |
445 | { |
446 | struct scsipi_xfer *xs = sc->sc_xs; |
447 | struct scsipi_periph *periph = xs->xs_periph; |
448 | struct scsi_request_sense sense_cmd; |
449 | usbd_status err; |
450 | |
451 | /* fetch sense data */ |
452 | memset(&sense_cmd, 0, sizeof(sense_cmd)); |
453 | sense_cmd.opcode = SCSI_REQUEST_SENSE; |
454 | sense_cmd.byte2 = periph->periph_lun << SCSI_CMD_LUN_SHIFT; |
455 | sense_cmd.length = sizeof(xs->sense); |
456 | |
457 | sc->sc_state = UAS_SENSECMD; |
458 | memcpy(sc->sc_cmd_buffer, &sense_cmd, sizeof(sense_cmd)); |
459 | |
460 | usbd_setup_xfer(sc->sc_cmd_xfer, sc, sc->sc_cmd_buffer, |
461 | sizeof(sense_cmd), 0, USSCANNER_TIMEOUT, |
462 | usscanner_sensecmd_cb); |
463 | err = usbd_transfer(sc->sc_cmd_xfer); |
464 | if (err == USBD_IN_PROGRESS) |
465 | return; |
466 | |
467 | xs->error = XS_DRIVER_STUFFUP; |
468 | usscanner_done(sc); |
469 | } |
470 | |
471 | Static void |
472 | usscanner_intr_cb(struct usbd_xfer *xfer, void *priv, usbd_status status) |
473 | { |
474 | struct usscanner_softc *sc = priv; |
475 | int s; |
476 | |
477 | DPRINTFN(10, ("usscanner_data_cb status=%d\n" , status)); |
478 | |
479 | #ifdef USSCANNER_DEBUG |
480 | if (sc->sc_state != UAS_STATUS) { |
481 | printf("%s: !UAS_STATUS\n" , device_xname(sc->sc_dev)); |
482 | } |
483 | if (sc->sc_status != 0) { |
484 | printf("%s: status byte=0x%02x\n" , device_xname(sc->sc_dev), |
485 | sc->sc_status); |
486 | } |
487 | #endif |
488 | /* XXX what should we do on non-0 status */ |
489 | |
490 | sc->sc_state = UAS_IDLE; |
491 | |
492 | s = splbio(); |
493 | KERNEL_LOCK(1, curlwp); |
494 | scsipi_done(sc->sc_xs); |
495 | KERNEL_UNLOCK_ONE(curlwp); |
496 | splx(s); |
497 | } |
498 | |
499 | Static void |
500 | usscanner_data_cb(struct usbd_xfer *xfer, void *priv, usbd_status status) |
501 | { |
502 | struct usscanner_softc *sc = priv; |
503 | struct scsipi_xfer *xs = sc->sc_xs; |
504 | uint32_t len; |
505 | |
506 | DPRINTFN(10, ("usscanner_data_cb status=%d\n" , status)); |
507 | |
508 | #ifdef USSCANNER_DEBUG |
509 | if (sc->sc_state != UAS_DATA) { |
510 | printf("%s: !UAS_DATA\n" , device_xname(sc->sc_dev)); |
511 | } |
512 | #endif |
513 | |
514 | usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL); |
515 | |
516 | xs->resid = xs->datalen - len; |
517 | |
518 | switch (status) { |
519 | case USBD_NORMAL_COMPLETION: |
520 | xs->error = XS_NOERROR; |
521 | break; |
522 | case USBD_TIMEOUT: |
523 | xs->error = XS_TIMEOUT; |
524 | break; |
525 | case USBD_CANCELLED: |
526 | if (xs->error == XS_SENSE) { |
527 | usscanner_sense(sc); |
528 | return; |
529 | } |
530 | break; |
531 | default: |
532 | xs->error = XS_DRIVER_STUFFUP; /* XXX ? */ |
533 | break; |
534 | } |
535 | usscanner_done(sc); |
536 | } |
537 | |
538 | Static void |
539 | usscanner_sensedata_cb(struct usbd_xfer *xfer, void *priv, usbd_status status) |
540 | { |
541 | struct usscanner_softc *sc = priv; |
542 | struct scsipi_xfer *xs = sc->sc_xs; |
543 | uint32_t len; |
544 | |
545 | DPRINTFN(10, ("usscanner_sensedata_cb status=%d\n" , status)); |
546 | |
547 | #ifdef USSCANNER_DEBUG |
548 | if (sc->sc_state != UAS_SENSEDATA) { |
549 | printf("%s: !UAS_SENSEDATA\n" , device_xname(sc->sc_dev)); |
550 | } |
551 | #endif |
552 | |
553 | usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL); |
554 | |
555 | switch (status) { |
556 | case USBD_NORMAL_COMPLETION: |
557 | memcpy(&xs->sense, sc->sc_datain_buffer, len); |
558 | if (len < sizeof(xs->sense)) |
559 | xs->error = XS_SHORTSENSE; |
560 | break; |
561 | case USBD_TIMEOUT: |
562 | xs->error = XS_TIMEOUT; |
563 | break; |
564 | case USBD_CANCELLED: |
565 | xs->error = XS_RESET; |
566 | break; |
567 | default: |
568 | xs->error = XS_DRIVER_STUFFUP; /* XXX ? */ |
569 | break; |
570 | } |
571 | usscanner_done(sc); |
572 | } |
573 | |
574 | Static void |
575 | usscanner_done(struct usscanner_softc *sc) |
576 | { |
577 | struct scsipi_xfer *xs = sc->sc_xs; |
578 | usbd_status err; |
579 | |
580 | DPRINTFN(10,("usscanner_done: error=%d\n" , sc->sc_xs->error)); |
581 | |
582 | sc->sc_state = UAS_STATUS; |
583 | usbd_setup_xfer(sc->sc_intr_xfer, sc, &sc->sc_status, 1, |
584 | USBD_SHORT_XFER_OK, USSCANNER_TIMEOUT, usscanner_intr_cb); |
585 | err = usbd_transfer(sc->sc_intr_xfer); |
586 | if (err == USBD_IN_PROGRESS) |
587 | return; |
588 | xs->error = XS_DRIVER_STUFFUP; |
589 | } |
590 | |
591 | Static void |
592 | usscanner_sensecmd_cb(struct usbd_xfer *xfer, void *priv, usbd_status status) |
593 | { |
594 | struct usscanner_softc *sc = priv; |
595 | struct scsipi_xfer *xs = sc->sc_xs; |
596 | usbd_status err; |
597 | |
598 | DPRINTFN(10, ("usscanner_sensecmd_cb status=%d\n" , status)); |
599 | |
600 | #ifdef USSCANNER_DEBUG |
601 | if (usscannerdebug > 15) |
602 | xs->xs_periph->periph_flags |= 1; /* XXX 1 */ |
603 | |
604 | if (sc->sc_state != UAS_SENSECMD) { |
605 | aprint_error_dev(sc->sc_dev, "!UAS_SENSECMD\n" ); |
606 | xs->error = XS_DRIVER_STUFFUP; |
607 | goto done; |
608 | } |
609 | #endif |
610 | |
611 | switch (status) { |
612 | case USBD_NORMAL_COMPLETION: |
613 | break; |
614 | case USBD_TIMEOUT: |
615 | xs->error = XS_TIMEOUT; |
616 | goto done; |
617 | default: |
618 | xs->error = XS_DRIVER_STUFFUP; /* XXX ? */ |
619 | goto done; |
620 | } |
621 | |
622 | sc->sc_state = UAS_SENSEDATA; |
623 | usbd_setup_xfer(sc->sc_datain_xfer, sc, sc->sc_datain_buffer, |
624 | sizeof(xs->sense), USBD_SHORT_XFER_OK, |
625 | USSCANNER_TIMEOUT, usscanner_sensedata_cb); |
626 | err = usbd_transfer(sc->sc_datain_xfer); |
627 | if (err == USBD_IN_PROGRESS) |
628 | return; |
629 | xs->error = XS_DRIVER_STUFFUP; |
630 | done: |
631 | usscanner_done(sc); |
632 | } |
633 | |
634 | Static void |
635 | usscanner_cmd_cb(struct usbd_xfer *xfer, void *priv, usbd_status status) |
636 | { |
637 | struct usscanner_softc *sc = priv; |
638 | struct scsipi_xfer *xs = sc->sc_xs; |
639 | struct usbd_xfer *dxfer; |
640 | usbd_status err; |
641 | |
642 | DPRINTFN(10, ("usscanner_cmd_cb status=%d\n" , status)); |
643 | |
644 | #ifdef USSCANNER_DEBUG |
645 | if (usscannerdebug > 15) |
646 | xs->xs_periph->periph_flags |= 1; /* XXX 1 */ |
647 | |
648 | if (sc->sc_state != UAS_CMD) { |
649 | aprint_error_dev(sc->sc_dev, "!UAS_CMD\n" ); |
650 | xs->error = XS_DRIVER_STUFFUP; |
651 | goto done; |
652 | } |
653 | #endif |
654 | |
655 | switch (status) { |
656 | case USBD_NORMAL_COMPLETION: |
657 | break; |
658 | case USBD_TIMEOUT: |
659 | xs->error = XS_TIMEOUT; |
660 | goto done; |
661 | case USBD_CANCELLED: |
662 | goto done; |
663 | default: |
664 | xs->error = XS_DRIVER_STUFFUP; /* XXX ? */ |
665 | goto done; |
666 | } |
667 | |
668 | if (xs->datalen == 0) { |
669 | DPRINTFN(4, ("usscanner_cmd_cb: no data phase\n" )); |
670 | xs->error = XS_NOERROR; |
671 | goto done; |
672 | } |
673 | |
674 | if (xs->xs_control & XS_CTL_DATA_IN) { |
675 | DPRINTFN(4, ("usscanner_cmd_cb: data in len=%d\n" , |
676 | xs->datalen)); |
677 | dxfer = sc->sc_datain_xfer; |
678 | } else { |
679 | DPRINTFN(4, ("usscanner_cmd_cb: data out len=%d\n" , |
680 | xs->datalen)); |
681 | dxfer = sc->sc_dataout_xfer; |
682 | } |
683 | sc->sc_state = UAS_DATA; |
684 | usbd_setup_xfer(dxfer, sc, xs->data, xs->datalen, |
685 | USBD_SHORT_XFER_OK, xs->timeout, usscanner_data_cb); |
686 | err = usbd_transfer(dxfer); |
687 | if (err == USBD_IN_PROGRESS) |
688 | return; |
689 | xs->error = XS_DRIVER_STUFFUP; |
690 | |
691 | done: |
692 | usscanner_done(sc); |
693 | } |
694 | |
695 | Static void |
696 | usscanner_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req, |
697 | void *arg) |
698 | { |
699 | struct scsipi_xfer *xs; |
700 | struct usscanner_softc *sc = |
701 | device_private(chan->chan_adapter->adapt_dev); |
702 | usbd_status err; |
703 | |
704 | switch (req) { |
705 | case ADAPTER_REQ_RUN_XFER: |
706 | xs = arg; |
707 | |
708 | DPRINTFN(8, ("%s: usscanner_scsipi_request: %d:%d " |
709 | "xs=%p cmd=0x%02x datalen=%d (quirks=0x%x, poll=%d)\n" , |
710 | device_xname(sc->sc_dev), |
711 | xs->xs_periph->periph_target, xs->xs_periph->periph_lun, |
712 | xs, xs->cmd->opcode, xs->datalen, |
713 | xs->xs_periph->periph_quirks, |
714 | xs->xs_control & XS_CTL_POLL)); |
715 | |
716 | if (sc->sc_dying) { |
717 | xs->error = XS_DRIVER_STUFFUP; |
718 | goto done; |
719 | } |
720 | |
721 | #ifdef USSCANNER_DEBUG |
722 | if (xs->xs_periph->periph_target != USSCANNER_SCSIID_DEVICE) { |
723 | DPRINTF(("%s: wrong SCSI ID %d\n" , |
724 | device_xname(sc->sc_dev), |
725 | xs->xs_periph->periph_target)); |
726 | xs->error = XS_DRIVER_STUFFUP; |
727 | goto done; |
728 | } |
729 | if (sc->sc_state != UAS_IDLE) { |
730 | printf("%s: !UAS_IDLE\n" , device_xname(sc->sc_dev)); |
731 | xs->error = XS_DRIVER_STUFFUP; |
732 | goto done; |
733 | } |
734 | #endif |
735 | |
736 | if (xs->datalen > USSCANNER_MAX_TRANSFER_SIZE) { |
737 | aprint_normal_dev(sc->sc_dev, |
738 | "usscanner_scsipi_request: large datalen, %d\n" , |
739 | xs->datalen); |
740 | xs->error = XS_DRIVER_STUFFUP; |
741 | goto done; |
742 | } |
743 | |
744 | DPRINTFN(4, ("%s: usscanner_scsipi_request: async cmdlen=%d" |
745 | " datalen=%d\n" , device_xname(sc->sc_dev), xs->cmdlen, |
746 | xs->datalen)); |
747 | sc->sc_state = UAS_CMD; |
748 | sc->sc_xs = xs; |
749 | memcpy(sc->sc_cmd_buffer, xs->cmd, xs->cmdlen); |
750 | usbd_setup_xfer(sc->sc_cmd_xfer, sc, sc->sc_cmd_buffer, |
751 | xs->cmdlen, 0, USSCANNER_TIMEOUT, usscanner_cmd_cb); |
752 | err = usbd_transfer(sc->sc_cmd_xfer); |
753 | if (err != USBD_IN_PROGRESS) { |
754 | xs->error = XS_DRIVER_STUFFUP; |
755 | goto done; |
756 | } |
757 | |
758 | return; |
759 | |
760 | |
761 | done: |
762 | sc->sc_state = UAS_IDLE; |
763 | KERNEL_LOCK(1, curlwp); |
764 | scsipi_done(xs); |
765 | KERNEL_UNLOCK_ONE(curlwp); |
766 | return; |
767 | |
768 | case ADAPTER_REQ_GROW_RESOURCES: |
769 | /* XXX Not supported. */ |
770 | return; |
771 | case ADAPTER_REQ_SET_XFER_MODE: |
772 | /* XXX Not supported. */ |
773 | return; |
774 | } |
775 | |
776 | } |
777 | |