1 | /* $NetBSD: urio.c,v 1.44 2016/07/07 06:55:42 msaitoh Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2000 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) at |
9 | * Carlstedt Research & Technology. |
10 | * |
11 | * Redistribution and use in source and binary forms, with or without |
12 | * modification, are permitted provided that the following conditions |
13 | * are met: |
14 | * 1. Redistributions of source code must retain the above copyright |
15 | * notice, this list of conditions and the following disclaimer. |
16 | * 2. Redistributions in binary form must reproduce the above copyright |
17 | * notice, this list of conditions and the following disclaimer in the |
18 | * documentation and/or other materials provided with the distribution. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
21 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
24 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
30 | * POSSIBILITY OF SUCH DAMAGE. |
31 | */ |
32 | |
33 | /* |
34 | * The inspiration and information for this driver comes from the |
35 | * FreeBSD driver written by Iwasa Kazmi. |
36 | */ |
37 | |
38 | #include <sys/cdefs.h> |
39 | __KERNEL_RCSID(0, "$NetBSD: urio.c,v 1.44 2016/07/07 06:55:42 msaitoh Exp $" ); |
40 | |
41 | #include <sys/param.h> |
42 | #include <sys/systm.h> |
43 | #include <sys/kernel.h> |
44 | #include <sys/kmem.h> |
45 | #include <sys/device.h> |
46 | #include <sys/ioctl.h> |
47 | #include <sys/conf.h> |
48 | #include <sys/file.h> |
49 | #include <sys/select.h> |
50 | #include <sys/proc.h> |
51 | #include <sys/vnode.h> |
52 | #include <sys/poll.h> |
53 | |
54 | #include <dev/usb/usb.h> |
55 | #include <dev/usb/usbdi.h> |
56 | #include <dev/usb/usbdi_util.h> |
57 | |
58 | #include <dev/usb/usbdevs.h> |
59 | #include <dev/usb/urio.h> |
60 | |
61 | #ifdef URIO_DEBUG |
62 | #define DPRINTF(x) if (uriodebug) printf x |
63 | #define DPRINTFN(n,x) if (uriodebug>(n)) printf x |
64 | int uriodebug = 0; |
65 | #else |
66 | #define DPRINTF(x) |
67 | #define DPRINTFN(n,x) |
68 | #endif |
69 | |
70 | |
71 | dev_type_open(urioopen); |
72 | dev_type_close(urioclose); |
73 | dev_type_read(urioread); |
74 | dev_type_write(uriowrite); |
75 | dev_type_ioctl(urioioctl); |
76 | |
77 | const struct cdevsw urio_cdevsw = { |
78 | .d_open = urioopen, |
79 | .d_close = urioclose, |
80 | .d_read = urioread, |
81 | .d_write = uriowrite, |
82 | .d_ioctl = urioioctl, |
83 | .d_stop = nostop, |
84 | .d_tty = notty, |
85 | .d_poll = nopoll, |
86 | .d_mmap = nommap, |
87 | .d_kqfilter = nokqfilter, |
88 | .d_discard = nodiscard, |
89 | .d_flag = D_OTHER |
90 | }; |
91 | |
92 | #define URIO_CONFIG_NO 1 |
93 | #define URIO_IFACE_IDX 0 |
94 | |
95 | |
96 | #define URIO_BSIZE 4096 |
97 | |
98 | |
99 | struct urio_softc { |
100 | device_t sc_dev; |
101 | struct usbd_device * sc_udev; |
102 | struct usbd_interface * sc_iface; |
103 | |
104 | int sc_in_addr; |
105 | struct usbd_pipe * sc_in_pipe; |
106 | int sc_out_addr; |
107 | struct usbd_pipe * sc_out_pipe; |
108 | |
109 | int sc_refcnt; |
110 | char sc_dying; |
111 | }; |
112 | |
113 | #define URIOUNIT(n) (minor(n)) |
114 | |
115 | #define URIO_RW_TIMEOUT 4000 /* ms */ |
116 | |
117 | static const struct usb_devno urio_devs[] = { |
118 | { USB_VENDOR_DIAMOND, USB_PRODUCT_DIAMOND_RIO500USB}, |
119 | { USB_VENDOR_DIAMOND2, USB_PRODUCT_DIAMOND2_RIO600USB}, |
120 | { USB_VENDOR_DIAMOND2, USB_PRODUCT_DIAMOND2_RIO800USB}, |
121 | { USB_VENDOR_DIAMOND2, USB_PRODUCT_DIAMOND2_PSAPLAY120}, |
122 | }; |
123 | #define urio_lookup(v, p) usb_lookup(urio_devs, v, p) |
124 | |
125 | int urio_match(device_t, cfdata_t, void *); |
126 | void urio_attach(device_t, device_t, void *); |
127 | int urio_detach(device_t, int); |
128 | int urio_activate(device_t, enum devact); |
129 | extern struct cfdriver urio_cd; |
130 | CFATTACH_DECL_NEW(urio, sizeof(struct urio_softc), urio_match, urio_attach, |
131 | urio_detach, urio_activate); |
132 | |
133 | int |
134 | urio_match(device_t parent, cfdata_t match, void *aux) |
135 | { |
136 | struct usb_attach_arg *uaa = aux; |
137 | |
138 | DPRINTFN(50,("urio_match\n" )); |
139 | |
140 | return urio_lookup(uaa->uaa_vendor, uaa->uaa_product) != NULL ? |
141 | UMATCH_VENDOR_PRODUCT : UMATCH_NONE; |
142 | } |
143 | |
144 | void |
145 | urio_attach(device_t parent, device_t self, void *aux) |
146 | { |
147 | struct urio_softc *sc = device_private(self); |
148 | struct usb_attach_arg *uaa = aux; |
149 | struct usbd_device * dev = uaa->uaa_device; |
150 | struct usbd_interface * iface; |
151 | char *devinfop; |
152 | usbd_status err; |
153 | usb_endpoint_descriptor_t *ed; |
154 | uint8_t epcount; |
155 | int i; |
156 | |
157 | DPRINTFN(10,("urio_attach: sc=%p\n" , sc)); |
158 | |
159 | sc->sc_dev = self; |
160 | |
161 | aprint_naive("\n" ); |
162 | aprint_normal("\n" ); |
163 | |
164 | devinfop = usbd_devinfo_alloc(dev, 0); |
165 | aprint_normal_dev(self, "%s\n" , devinfop); |
166 | usbd_devinfo_free(devinfop); |
167 | |
168 | err = usbd_set_config_no(dev, URIO_CONFIG_NO, 1); |
169 | if (err) { |
170 | aprint_error_dev(self, "failed to set configuration" |
171 | ", err=%s\n" , usbd_errstr(err)); |
172 | return; |
173 | } |
174 | |
175 | err = usbd_device2interface_handle(dev, URIO_IFACE_IDX, &iface); |
176 | if (err) { |
177 | aprint_error_dev(self, "getting interface handle failed\n" ); |
178 | return; |
179 | } |
180 | |
181 | sc->sc_udev = dev; |
182 | sc->sc_iface = iface; |
183 | |
184 | epcount = 0; |
185 | (void)usbd_endpoint_count(iface, &epcount); |
186 | |
187 | sc->sc_in_addr = -1; |
188 | sc->sc_out_addr = -1; |
189 | for (i = 0; i < epcount; i++) { |
190 | ed = usbd_interface2endpoint_descriptor(iface, i); |
191 | if (ed == NULL) { |
192 | aprint_error_dev(self, "couldn't get ep %d\n" , i); |
193 | return; |
194 | } |
195 | if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && |
196 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { |
197 | sc->sc_in_addr = ed->bEndpointAddress; |
198 | } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && |
199 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { |
200 | sc->sc_out_addr = ed->bEndpointAddress; |
201 | } |
202 | } |
203 | if (sc->sc_in_addr == -1 || sc->sc_out_addr == -1) { |
204 | aprint_error_dev(self, "missing endpoint\n" ); |
205 | return; |
206 | } |
207 | |
208 | DPRINTFN(10, ("urio_attach: %p\n" , sc->sc_udev)); |
209 | |
210 | usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev); |
211 | |
212 | return; |
213 | } |
214 | |
215 | int |
216 | urio_detach(device_t self, int flags) |
217 | { |
218 | struct urio_softc *sc = device_private(self); |
219 | int s; |
220 | int maj, mn; |
221 | |
222 | DPRINTF(("urio_detach: sc=%p flags=%d\n" , sc, flags)); |
223 | |
224 | sc->sc_dying = 1; |
225 | /* Abort all pipes. Causes processes waiting for transfer to wake. */ |
226 | if (sc->sc_in_pipe != NULL) { |
227 | usbd_abort_pipe(sc->sc_in_pipe); |
228 | usbd_close_pipe(sc->sc_in_pipe); |
229 | sc->sc_in_pipe = NULL; |
230 | } |
231 | if (sc->sc_out_pipe != NULL) { |
232 | usbd_abort_pipe(sc->sc_out_pipe); |
233 | usbd_close_pipe(sc->sc_out_pipe); |
234 | sc->sc_out_pipe = NULL; |
235 | } |
236 | |
237 | s = splusb(); |
238 | if (--sc->sc_refcnt >= 0) { |
239 | /* Wait for processes to go away. */ |
240 | usb_detach_waitold(sc->sc_dev); |
241 | } |
242 | splx(s); |
243 | |
244 | /* locate the major number */ |
245 | maj = cdevsw_lookup_major(&urio_cdevsw); |
246 | |
247 | /* Nuke the vnodes for any open instances (calls close). */ |
248 | mn = device_unit(self); |
249 | vdevgone(maj, mn, mn, VCHR); |
250 | |
251 | usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev); |
252 | |
253 | return 0; |
254 | } |
255 | |
256 | int |
257 | urio_activate(device_t self, enum devact act) |
258 | { |
259 | struct urio_softc *sc = device_private(self); |
260 | |
261 | switch (act) { |
262 | case DVACT_DEACTIVATE: |
263 | sc->sc_dying = 1; |
264 | return 0; |
265 | default: |
266 | return EOPNOTSUPP; |
267 | } |
268 | } |
269 | |
270 | int |
271 | urioopen(dev_t dev, int flag, int mode, struct lwp *l) |
272 | { |
273 | struct urio_softc *sc; |
274 | usbd_status err; |
275 | |
276 | sc = device_lookup_private(&urio_cd, URIOUNIT(dev)); |
277 | if (sc == NULL) |
278 | return ENXIO; |
279 | |
280 | DPRINTFN(5, ("urioopen: flag=%d, mode=%d, unit=%d\n" , |
281 | flag, mode, URIOUNIT(dev))); |
282 | |
283 | if (sc->sc_dying) |
284 | return EIO; |
285 | |
286 | if (sc->sc_in_pipe != NULL) |
287 | return EBUSY; |
288 | |
289 | if ((flag & (FWRITE|FREAD)) != (FWRITE|FREAD)) |
290 | return EACCES; |
291 | |
292 | err = usbd_open_pipe(sc->sc_iface, sc->sc_in_addr, 0, &sc->sc_in_pipe); |
293 | if (err) |
294 | return EIO; |
295 | err = usbd_open_pipe(sc->sc_iface, sc->sc_out_addr,0,&sc->sc_out_pipe); |
296 | if (err) { |
297 | usbd_close_pipe(sc->sc_in_pipe); |
298 | sc->sc_in_pipe = NULL; |
299 | return EIO; |
300 | } |
301 | |
302 | return 0; |
303 | } |
304 | |
305 | int |
306 | urioclose(dev_t dev, int flag, int mode, |
307 | struct lwp *l) |
308 | { |
309 | struct urio_softc *sc; |
310 | sc = device_lookup_private(&urio_cd, URIOUNIT(dev)); |
311 | |
312 | DPRINTFN(5, ("urioclose: flag=%d, mode=%d, unit=%d\n" , |
313 | flag, mode, URIOUNIT(dev))); |
314 | |
315 | if (sc->sc_in_pipe != NULL) { |
316 | usbd_abort_pipe(sc->sc_in_pipe); |
317 | usbd_close_pipe(sc->sc_in_pipe); |
318 | sc->sc_in_pipe = NULL; |
319 | } |
320 | if (sc->sc_out_pipe != NULL) { |
321 | usbd_abort_pipe(sc->sc_out_pipe); |
322 | usbd_close_pipe(sc->sc_out_pipe); |
323 | sc->sc_out_pipe = NULL; |
324 | } |
325 | |
326 | return 0; |
327 | } |
328 | |
329 | int |
330 | urioread(dev_t dev, struct uio *uio, int flag) |
331 | { |
332 | struct urio_softc *sc; |
333 | struct usbd_xfer *xfer; |
334 | usbd_status err; |
335 | void *bufp; |
336 | uint32_t n, tn; |
337 | int error = 0; |
338 | |
339 | sc = device_lookup_private(&urio_cd, URIOUNIT(dev)); |
340 | |
341 | DPRINTFN(5, ("urioread: %d\n" , URIOUNIT(dev))); |
342 | |
343 | if (sc->sc_dying) |
344 | return EIO; |
345 | |
346 | error = usbd_create_xfer(sc->sc_in_pipe, URIO_BSIZE, 0, 0, &xfer); |
347 | if (error) { |
348 | return error; |
349 | } |
350 | bufp = usbd_get_buffer(xfer); |
351 | |
352 | sc->sc_refcnt++; |
353 | |
354 | while ((n = min(URIO_BSIZE, uio->uio_resid)) != 0) { |
355 | DPRINTFN(1, ("urioread: start transfer %d bytes\n" , n)); |
356 | tn = n; |
357 | err = usbd_bulk_transfer(xfer, sc->sc_in_pipe, 0, |
358 | URIO_RW_TIMEOUT, bufp, &tn); |
359 | if (err) { |
360 | if (err == USBD_INTERRUPTED) |
361 | error = EINTR; |
362 | else if (err == USBD_TIMEOUT) |
363 | error = ETIMEDOUT; |
364 | else |
365 | error = EIO; |
366 | break; |
367 | } |
368 | |
369 | DPRINTFN(1, ("urioread: got %d bytes\n" , tn)); |
370 | |
371 | error = uiomove(bufp, tn, uio); |
372 | if (error || tn < n) |
373 | break; |
374 | } |
375 | usbd_destroy_xfer(xfer); |
376 | |
377 | if (--sc->sc_refcnt < 0) |
378 | usb_detach_wakeupold(sc->sc_dev); |
379 | |
380 | return error; |
381 | } |
382 | |
383 | int |
384 | uriowrite(dev_t dev, struct uio *uio, int flag) |
385 | { |
386 | struct urio_softc *sc; |
387 | struct usbd_xfer *xfer; |
388 | usbd_status err; |
389 | void *bufp; |
390 | uint32_t n; |
391 | int error = 0; |
392 | |
393 | sc = device_lookup_private(&urio_cd, URIOUNIT(dev)); |
394 | |
395 | DPRINTFN(5, ("uriowrite: unit=%d, len=%ld\n" , URIOUNIT(dev), |
396 | (long)uio->uio_resid)); |
397 | |
398 | if (sc->sc_dying) |
399 | return EIO; |
400 | |
401 | error = usbd_create_xfer(sc->sc_out_pipe, URIO_BSIZE, 0, 0, &xfer); |
402 | if (error) { |
403 | return error; |
404 | } |
405 | bufp = usbd_get_buffer(xfer); |
406 | sc->sc_refcnt++; |
407 | |
408 | while ((n = min(URIO_BSIZE, uio->uio_resid)) != 0) { |
409 | error = uiomove(bufp, n, uio); |
410 | if (error) |
411 | break; |
412 | |
413 | DPRINTFN(1, ("uriowrite: transfer %d bytes\n" , n)); |
414 | |
415 | err = usbd_bulk_transfer(xfer, sc->sc_out_pipe, 0, |
416 | URIO_RW_TIMEOUT, bufp, &n); |
417 | DPRINTFN(2, ("uriowrite: err=%d\n" , err)); |
418 | if (err) { |
419 | if (err == USBD_INTERRUPTED) |
420 | error = EINTR; |
421 | else if (err == USBD_TIMEOUT) |
422 | error = ETIMEDOUT; |
423 | else |
424 | error = EIO; |
425 | break; |
426 | } |
427 | } |
428 | |
429 | usbd_destroy_xfer(xfer); |
430 | |
431 | if (--sc->sc_refcnt < 0) |
432 | usb_detach_wakeupold(sc->sc_dev); |
433 | |
434 | DPRINTFN(5, ("uriowrite: done unit=%d, error=%d\n" , URIOUNIT(dev), |
435 | error)); |
436 | |
437 | return error; |
438 | } |
439 | |
440 | |
441 | int |
442 | urioioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l) |
443 | { |
444 | struct urio_softc * sc; |
445 | int unit = URIOUNIT(dev); |
446 | struct urio_command *rcmd; |
447 | int requesttype, len; |
448 | struct iovec iov; |
449 | struct uio uio; |
450 | usb_device_request_t req; |
451 | usbd_status err; |
452 | int req_flags = 0; |
453 | uint32_t req_actlen = 0; |
454 | void *ptr = NULL; |
455 | int error = 0; |
456 | |
457 | sc = device_lookup_private(&urio_cd, unit); |
458 | |
459 | if (sc->sc_dying) |
460 | return EIO; |
461 | |
462 | rcmd = (struct urio_command *)addr; |
463 | |
464 | switch (cmd) { |
465 | case URIO_RECV_COMMAND: |
466 | requesttype = rcmd->requesttype | UT_READ_VENDOR_DEVICE; |
467 | break; |
468 | |
469 | case URIO_SEND_COMMAND: |
470 | requesttype = rcmd->requesttype | UT_WRITE_VENDOR_DEVICE; |
471 | break; |
472 | |
473 | default: |
474 | return EINVAL; |
475 | break; |
476 | } |
477 | |
478 | if (!(flag & FWRITE)) |
479 | return EPERM; |
480 | len = rcmd->length; |
481 | |
482 | DPRINTFN(1,("urio_ioctl: cmd=0x%08lx reqtype=0x%0x req=0x%0x " |
483 | "value=0x%0x index=0x%0x len=0x%0x\n" , |
484 | cmd, requesttype, rcmd->request, rcmd->value, |
485 | rcmd->index, len)); |
486 | |
487 | /* Send rio control message */ |
488 | req.bmRequestType = requesttype; |
489 | req.bRequest = rcmd->request; |
490 | USETW(req.wValue, rcmd->value); |
491 | USETW(req.wIndex, rcmd->index); |
492 | USETW(req.wLength, len); |
493 | |
494 | if (len < 0 || len > 32767) |
495 | return EINVAL; |
496 | if (len != 0) { |
497 | iov.iov_base = (void *)rcmd->buffer; |
498 | iov.iov_len = len; |
499 | uio.uio_iov = &iov; |
500 | uio.uio_iovcnt = 1; |
501 | uio.uio_resid = len; |
502 | uio.uio_offset = 0; |
503 | uio.uio_rw = req.bmRequestType & UT_READ ? |
504 | UIO_READ : UIO_WRITE; |
505 | uio.uio_vmspace = l->l_proc->p_vmspace; |
506 | ptr = kmem_alloc(len, KM_SLEEP); |
507 | if (uio.uio_rw == UIO_WRITE) { |
508 | error = uiomove(ptr, len, &uio); |
509 | if (error) |
510 | goto ret; |
511 | } |
512 | } |
513 | |
514 | sc->sc_refcnt++; |
515 | |
516 | err = usbd_do_request_flags(sc->sc_udev, &req, ptr, req_flags, |
517 | &req_actlen, USBD_DEFAULT_TIMEOUT); |
518 | |
519 | if (--sc->sc_refcnt < 0) |
520 | usb_detach_wakeupold(sc->sc_dev); |
521 | |
522 | if (err) { |
523 | error = EIO; |
524 | } else { |
525 | if (len != 0 && uio.uio_rw == UIO_READ) |
526 | error = uiomove(ptr, len, &uio); |
527 | } |
528 | |
529 | ret: |
530 | if (ptr != NULL) |
531 | kmem_free(ptr, len); |
532 | return error; |
533 | } |
534 | |