1 | /* $NetBSD: uirda.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). |
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 | #include <sys/cdefs.h> |
33 | __KERNEL_RCSID(0, "$NetBSD: uirda.c,v 1.40 2016/07/07 06:55:42 msaitoh Exp $" ); |
34 | |
35 | #include <sys/param.h> |
36 | #include <sys/systm.h> |
37 | #include <sys/kernel.h> |
38 | #include <sys/device.h> |
39 | #include <sys/mutex.h> |
40 | #include <sys/ioctl.h> |
41 | #include <sys/conf.h> |
42 | #include <sys/file.h> |
43 | #include <sys/poll.h> |
44 | #include <sys/select.h> |
45 | #include <sys/proc.h> |
46 | |
47 | #include <dev/usb/usb.h> |
48 | #include <dev/usb/usbdi.h> |
49 | #include <dev/usb/usbdi_util.h> |
50 | #include <dev/usb/usbdevs.h> |
51 | |
52 | #include <dev/ir/ir.h> |
53 | #include <dev/ir/irdaio.h> |
54 | #include <dev/ir/irframevar.h> |
55 | |
56 | #include <dev/usb/uirdavar.h> |
57 | |
58 | #ifdef UIRDA_DEBUG |
59 | #define DPRINTF(x) if (uirdadebug) printf x |
60 | #define DPRINTFN(n,x) if (uirdadebug>(n)) printf x |
61 | int uirdadebug = 0; |
62 | #else |
63 | #define DPRINTF(x) |
64 | #define DPRINTFN(n,x) |
65 | #endif |
66 | |
67 | |
68 | /* Class specific requests */ |
69 | #define UR_IRDA_RECEIVING 0x01 /* Receive in progress? */ |
70 | #define UR_IRDA_CHECK_MEDIA_BUSY 0x03 |
71 | #define UR_IRDA_SET_RATE_SNIFF 0x04 /* opt */ |
72 | #define UR_IRDA_SET_UNICAST_LIST 0x05 /* opt */ |
73 | #define UR_IRDA_GET_DESC 0x06 |
74 | |
75 | #define UIRDA_NEBOFS 8 |
76 | static struct { |
77 | int count; |
78 | int mask; |
79 | int ; |
80 | } uirda_ebofs[UIRDA_NEBOFS] = { |
81 | { 0, UI_EB_0, UIRDA_EB_0 }, |
82 | { 1, UI_EB_1, UIRDA_EB_1 }, |
83 | { 2, UI_EB_2, UIRDA_EB_2 }, |
84 | { 3, UI_EB_3, UIRDA_EB_3 }, |
85 | { 6, UI_EB_6, UIRDA_EB_6 }, |
86 | { 12, UI_EB_12, UIRDA_EB_12 }, |
87 | { 24, UI_EB_24, UIRDA_EB_24 }, |
88 | { 48, UI_EB_48, UIRDA_EB_48 } |
89 | }; |
90 | |
91 | #define UIRDA_NSPEEDS 9 |
92 | static struct { |
93 | int speed; |
94 | int mask; |
95 | int ; |
96 | } uirda_speeds[UIRDA_NSPEEDS] = { |
97 | { 4000000, UI_BR_4000000, UIRDA_4000000 }, |
98 | { 1152000, UI_BR_1152000, UIRDA_1152000 }, |
99 | { 576000, UI_BR_576000, UIRDA_576000 }, |
100 | { 115200, UI_BR_115200, UIRDA_115200 }, |
101 | { 57600, UI_BR_57600, UIRDA_57600 }, |
102 | { 38400, UI_BR_38400, UIRDA_38400 }, |
103 | { 19200, UI_BR_19200, UIRDA_19200 }, |
104 | { 9600, UI_BR_9600, UIRDA_9600 }, |
105 | { 2400, UI_BR_2400, UIRDA_2400 }, |
106 | }; |
107 | |
108 | |
109 | |
110 | int uirda_open(void *, int, int, struct lwp *); |
111 | int uirda_close(void *, int, int, struct lwp *); |
112 | int uirda_read(void *, struct uio *, int); |
113 | int uirda_write(void *, struct uio *, int); |
114 | int uirda_set_params(void *, struct irda_params *); |
115 | int uirda_get_speeds(void *, int *); |
116 | int uirda_get_turnarounds(void *, int *); |
117 | int uirda_poll(void *, int, struct lwp *); |
118 | int uirda_kqfilter(void *, struct knote *); |
119 | |
120 | struct irframe_methods uirda_methods = { |
121 | uirda_open, uirda_close, uirda_read, uirda_write, uirda_poll, |
122 | uirda_kqfilter, uirda_set_params, uirda_get_speeds, |
123 | uirda_get_turnarounds |
124 | }; |
125 | |
126 | void uirda_rd_cb(struct usbd_xfer *xfer, void *priv, |
127 | usbd_status status); |
128 | usbd_status uirda_start_read(struct uirda_softc *sc); |
129 | |
130 | /* |
131 | * These devices don't quite follow the spec. Speed changing is broken |
132 | * and they don't handle windows. |
133 | * But we change speed in a safe way, and don't use windows now. |
134 | * Some devices also seem to have an interrupt pipe that can be ignored. |
135 | * |
136 | * Table information taken from Linux driver. |
137 | */ |
138 | Static const struct usb_devno uirda_devs[] = { |
139 | { USB_VENDOR_ACTISYS, USB_PRODUCT_ACTISYS_IR2000U }, |
140 | { USB_VENDOR_EXTENDED, USB_PRODUCT_EXTENDED_XTNDACCESS }, |
141 | { USB_VENDOR_KAWATSU, USB_PRODUCT_KAWATSU_KC180 }, |
142 | }; |
143 | #define uirda_lookup(v, p) (usb_lookup(uirda_devs, v, p)) |
144 | |
145 | int uirda_match(device_t, cfdata_t, void *); |
146 | void uirda_attach(device_t, device_t, void *); |
147 | void uirda_childdet(device_t, device_t); |
148 | int uirda_detach(device_t, int); |
149 | int uirda_activate(device_t, enum devact); |
150 | extern struct cfdriver uirda_cd; |
151 | CFATTACH_DECL2_NEW(uirda, sizeof(struct uirda_softc), uirda_match, |
152 | uirda_attach, uirda_detach, uirda_activate, NULL, uirda_childdet); |
153 | |
154 | int |
155 | uirda_match(device_t parent, cfdata_t match, void *aux) |
156 | { |
157 | struct usbif_attach_arg *uiaa = aux; |
158 | |
159 | DPRINTFN(50,("uirda_match\n" )); |
160 | |
161 | if (uirda_lookup(uiaa->uiaa_vendor, uiaa->uiaa_product) != NULL) |
162 | return UMATCH_VENDOR_PRODUCT; |
163 | |
164 | if (uiaa->uiaa_class == UICLASS_APPL_SPEC && |
165 | uiaa->uiaa_subclass == UISUBCLASS_IRDA && |
166 | uiaa->uiaa_proto == UIPROTO_IRDA) |
167 | return UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO; |
168 | return UMATCH_NONE; |
169 | } |
170 | |
171 | void |
172 | uirda_attach(device_t parent, device_t self, void *aux) |
173 | { |
174 | struct uirda_softc *sc = device_private(self); |
175 | struct usbif_attach_arg *uiaa = aux; |
176 | struct usbd_device * dev = uiaa->uiaa_device; |
177 | struct usbd_interface *iface = uiaa->uiaa_iface; |
178 | char *devinfop; |
179 | usb_endpoint_descriptor_t *ed; |
180 | usbd_status err; |
181 | uint8_t epcount; |
182 | u_int specrev; |
183 | int i; |
184 | struct ir_attach_args ia; |
185 | |
186 | DPRINTFN(10,("uirda_attach: sc=%p\n" , sc)); |
187 | |
188 | sc->sc_dev = self; |
189 | |
190 | aprint_naive("\n" ); |
191 | aprint_normal("\n" ); |
192 | |
193 | devinfop = usbd_devinfo_alloc(dev, 0); |
194 | aprint_normal_dev(self, "%s\n" , devinfop); |
195 | usbd_devinfo_free(devinfop); |
196 | |
197 | sc->sc_udev = dev; |
198 | sc->sc_iface = iface; |
199 | |
200 | if (sc->sc_hdszi == 0) |
201 | sc->sc_hdszi = UIRDA_INPUT_HEADER_SIZE; |
202 | |
203 | epcount = 0; |
204 | (void)usbd_endpoint_count(iface, &epcount); |
205 | |
206 | sc->sc_rd_addr = -1; |
207 | sc->sc_wr_addr = -1; |
208 | for (i = 0; i < epcount; i++) { |
209 | ed = usbd_interface2endpoint_descriptor(iface, i); |
210 | if (ed == NULL) { |
211 | aprint_error_dev(self, "couldn't get ep %d\n" , i); |
212 | return; |
213 | } |
214 | if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && |
215 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { |
216 | sc->sc_rd_addr = ed->bEndpointAddress; |
217 | } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && |
218 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { |
219 | sc->sc_wr_addr = ed->bEndpointAddress; |
220 | } |
221 | } |
222 | if (sc->sc_rd_addr == -1 || sc->sc_wr_addr == -1) { |
223 | aprint_error_dev(self, "missing endpoint\n" ); |
224 | return; |
225 | } |
226 | |
227 | if (sc->sc_loadfw(sc) != 0) { |
228 | return; |
229 | } |
230 | |
231 | /* Get the IrDA descriptor */ |
232 | err = usbd_get_class_desc(sc->sc_udev, UDESC_IRDA, 0, |
233 | USB_IRDA_DESCRIPTOR_SIZE, &sc->sc_irdadesc); |
234 | aprint_error_dev(self, "error %d reading class desc\n" , err); |
235 | if (err) { |
236 | err = usbd_get_desc(sc->sc_udev, UDESC_IRDA, 0, |
237 | USB_IRDA_DESCRIPTOR_SIZE, &sc->sc_irdadesc); |
238 | } |
239 | aprint_error_dev(self, "error %d reading desc\n" , err); |
240 | if (err) { |
241 | /* maybe it's embedded in the config desc? */ |
242 | usbd_desc_iter_t iter; |
243 | const usb_descriptor_t *d; |
244 | usb_desc_iter_init(sc->sc_udev, &iter); |
245 | for (;;) { |
246 | d = usb_desc_iter_next(&iter); |
247 | if (!d || d->bDescriptorType == UDESC_IRDA) |
248 | break; |
249 | } |
250 | if (d == NULL) { |
251 | aprint_error_dev(self, |
252 | "Cannot get IrDA descriptor\n" ); |
253 | return; |
254 | } |
255 | memcpy(&sc->sc_irdadesc, d, USB_IRDA_DESCRIPTOR_SIZE); |
256 | } |
257 | DPRINTF(("uirda_attach: bDescriptorSize %d bDescriptorType 0x%x " |
258 | "bmDataSize=0x%02x bmWindowSize=0x%02x " |
259 | "bmMinTurnaroundTime=0x%02x wBaudRate=0x%04x " |
260 | "bmAdditionalBOFs=0x%02x bIrdaSniff=%d bMaxUnicastList=%d\n" , |
261 | sc->sc_irdadesc.bLength, |
262 | sc->sc_irdadesc.bDescriptorType, |
263 | sc->sc_irdadesc.bmDataSize, |
264 | sc->sc_irdadesc.bmWindowSize, |
265 | sc->sc_irdadesc.bmMinTurnaroundTime, |
266 | UGETW(sc->sc_irdadesc.wBaudRate), |
267 | sc->sc_irdadesc.bmAdditionalBOFs, |
268 | sc->sc_irdadesc.bIrdaSniff, |
269 | sc->sc_irdadesc.bMaxUnicastList)); |
270 | |
271 | specrev = UGETW(sc->sc_irdadesc.bcdSpecRevision); |
272 | aprint_normal_dev(self, "USB-IrDA protocol version %x.%02x\n" , |
273 | specrev >> 8, specrev & 0xff); |
274 | |
275 | DPRINTFN(10, ("uirda_attach: %p\n" , sc->sc_udev)); |
276 | |
277 | usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev); |
278 | |
279 | mutex_init(&sc->sc_wr_buf_lk, MUTEX_DEFAULT, IPL_NONE); |
280 | mutex_init(&sc->sc_rd_buf_lk, MUTEX_DEFAULT, IPL_NONE); |
281 | selinit(&sc->sc_rd_sel); |
282 | selinit(&sc->sc_wr_sel); |
283 | |
284 | ia.ia_type = IR_TYPE_IRFRAME; |
285 | ia.ia_methods = sc->sc_irm ? sc->sc_irm : &uirda_methods; |
286 | ia.ia_handle = sc; |
287 | |
288 | sc->sc_child = config_found(self, &ia, ir_print); |
289 | |
290 | return; |
291 | } |
292 | |
293 | int |
294 | uirda_detach(device_t self, int flags) |
295 | { |
296 | struct uirda_softc *sc = device_private(self); |
297 | int s; |
298 | int rv = 0; |
299 | |
300 | DPRINTF(("uirda_detach: sc=%p flags=%d\n" , sc, flags)); |
301 | |
302 | sc->sc_dying = 1; |
303 | /* Abort all pipes. Causes processes waiting for transfer to wake. */ |
304 | if (sc->sc_rd_pipe != NULL) { |
305 | usbd_abort_pipe(sc->sc_rd_pipe); |
306 | } |
307 | if (sc->sc_wr_pipe != NULL) { |
308 | usbd_abort_pipe(sc->sc_wr_pipe); |
309 | } |
310 | if (sc->sc_rd_xfer != NULL) { |
311 | usbd_destroy_xfer(sc->sc_rd_xfer); |
312 | sc->sc_rd_xfer = NULL; |
313 | sc->sc_rd_buf = NULL; |
314 | } |
315 | if (sc->sc_wr_xfer != NULL) { |
316 | usbd_destroy_xfer(sc->sc_wr_xfer); |
317 | sc->sc_wr_xfer = NULL; |
318 | sc->sc_wr_buf = NULL; |
319 | } |
320 | if (sc->sc_rd_pipe != NULL) { |
321 | usbd_close_pipe(sc->sc_rd_pipe); |
322 | sc->sc_rd_pipe = NULL; |
323 | } |
324 | if (sc->sc_wr_pipe != NULL) { |
325 | usbd_close_pipe(sc->sc_wr_pipe); |
326 | sc->sc_wr_pipe = NULL; |
327 | } |
328 | wakeup(&sc->sc_rd_count); |
329 | |
330 | s = splusb(); |
331 | if (--sc->sc_refcnt >= 0) { |
332 | /* Wait for processes to go away. */ |
333 | usb_detach_waitold(sc->sc_dev); |
334 | } |
335 | splx(s); |
336 | |
337 | if (sc->sc_child != NULL) |
338 | rv = config_detach(sc->sc_child, flags); |
339 | |
340 | usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev); |
341 | |
342 | mutex_destroy(&sc->sc_wr_buf_lk); |
343 | mutex_destroy(&sc->sc_rd_buf_lk); |
344 | seldestroy(&sc->sc_rd_sel); |
345 | seldestroy(&sc->sc_wr_sel); |
346 | |
347 | return rv; |
348 | } |
349 | |
350 | void |
351 | uirda_childdet(device_t self, device_t child) |
352 | { |
353 | struct uirda_softc *sc = device_private(self); |
354 | |
355 | KASSERT(sc->sc_child == child); |
356 | sc->sc_child = NULL; |
357 | } |
358 | |
359 | int |
360 | uirda_activate(device_t self, enum devact act) |
361 | { |
362 | struct uirda_softc *sc = device_private(self); |
363 | |
364 | switch (act) { |
365 | case DVACT_DEACTIVATE: |
366 | sc->sc_dying = 1; |
367 | return 0; |
368 | default: |
369 | return EOPNOTSUPP; |
370 | } |
371 | } |
372 | |
373 | int |
374 | uirda_open(void *h, int flag, int mode, |
375 | struct lwp *l) |
376 | { |
377 | struct uirda_softc *sc = h; |
378 | int error; |
379 | usbd_status err; |
380 | |
381 | DPRINTF(("%s: sc=%p\n" , __func__, sc)); |
382 | |
383 | err = usbd_open_pipe(sc->sc_iface, sc->sc_rd_addr, 0, &sc->sc_rd_pipe); |
384 | if (err) { |
385 | error = EIO; |
386 | goto bad1; |
387 | } |
388 | err = usbd_open_pipe(sc->sc_iface, sc->sc_wr_addr, 0, &sc->sc_wr_pipe); |
389 | if (err) { |
390 | error = EIO; |
391 | goto bad2; |
392 | } |
393 | error = usbd_create_xfer(sc->sc_rd_pipe, |
394 | IRDA_MAX_FRAME_SIZE + sc->sc_hdszi, USBD_SHORT_XFER_OK, 0, |
395 | &sc->sc_rd_xfer); |
396 | if (error) |
397 | goto bad3; |
398 | sc->sc_rd_buf = usbd_get_buffer(sc->sc_rd_xfer); |
399 | |
400 | /* worst case ST-UIRDA length */ |
401 | error = usbd_create_xfer(sc->sc_wr_pipe, |
402 | IRDA_MAX_FRAME_SIZE + UIRDA_OUTPUT_HEADER_SIZE + 2 + 1, |
403 | USBD_FORCE_SHORT_XFER, 0, &sc->sc_wr_xfer); |
404 | if (error) |
405 | goto bad4; |
406 | sc->sc_wr_buf = usbd_get_buffer(sc->sc_wr_xfer); |
407 | |
408 | sc->sc_rd_count = 0; |
409 | sc->sc_rd_err = 0; |
410 | sc->sc_params.speed = 0; |
411 | sc->sc_params.ebofs = 0; |
412 | sc->sc_params.maxsize = IRDA_MAX_FRAME_SIZE; |
413 | sc->sc_wr_hdr = -1; |
414 | |
415 | err = uirda_start_read(sc); |
416 | /* XXX check err */ |
417 | |
418 | return 0; |
419 | |
420 | bad4: |
421 | usbd_destroy_xfer(sc->sc_rd_xfer); |
422 | sc->sc_rd_xfer = NULL; |
423 | bad3: |
424 | usbd_close_pipe(sc->sc_wr_pipe); |
425 | sc->sc_wr_pipe = NULL; |
426 | bad2: |
427 | usbd_close_pipe(sc->sc_rd_pipe); |
428 | sc->sc_rd_pipe = NULL; |
429 | bad1: |
430 | return error; |
431 | } |
432 | |
433 | int |
434 | uirda_close(void *h, int flag, int mode, |
435 | struct lwp *l) |
436 | { |
437 | struct uirda_softc *sc = h; |
438 | |
439 | DPRINTF(("%s: sc=%p\n" , __func__, sc)); |
440 | |
441 | if (sc->sc_rd_pipe != NULL) { |
442 | usbd_abort_pipe(sc->sc_rd_pipe); |
443 | } |
444 | if (sc->sc_wr_pipe != NULL) { |
445 | usbd_abort_pipe(sc->sc_wr_pipe); |
446 | } |
447 | if (sc->sc_rd_xfer != NULL) { |
448 | usbd_destroy_xfer(sc->sc_rd_xfer); |
449 | sc->sc_rd_xfer = NULL; |
450 | sc->sc_rd_buf = NULL; |
451 | } |
452 | if (sc->sc_wr_xfer != NULL) { |
453 | usbd_destroy_xfer(sc->sc_wr_xfer); |
454 | sc->sc_wr_xfer = NULL; |
455 | sc->sc_wr_buf = NULL; |
456 | } |
457 | if (sc->sc_rd_pipe != NULL) { |
458 | usbd_close_pipe(sc->sc_rd_pipe); |
459 | sc->sc_rd_pipe = NULL; |
460 | } |
461 | if (sc->sc_wr_pipe != NULL) { |
462 | usbd_close_pipe(sc->sc_wr_pipe); |
463 | sc->sc_wr_pipe = NULL; |
464 | } |
465 | |
466 | return 0; |
467 | } |
468 | |
469 | int |
470 | uirda_read(void *h, struct uio *uio, int flag) |
471 | { |
472 | struct uirda_softc *sc = h; |
473 | int s; |
474 | int error; |
475 | u_int n; |
476 | |
477 | DPRINTFN(1,("%s: sc=%p\n" , __func__, sc)); |
478 | |
479 | if (sc->sc_dying) |
480 | return EIO; |
481 | |
482 | #ifdef DIAGNOSTIC |
483 | if (sc->sc_rd_buf == NULL) |
484 | return EINVAL; |
485 | #endif |
486 | |
487 | sc->sc_refcnt++; |
488 | |
489 | do { |
490 | s = splusb(); |
491 | while (sc->sc_rd_count == 0) { |
492 | DPRINTFN(5,("uirda_read: calling tsleep()\n" )); |
493 | error = tsleep(&sc->sc_rd_count, PZERO | PCATCH, |
494 | "uirdrd" , 0); |
495 | if (sc->sc_dying) |
496 | error = EIO; |
497 | if (error) { |
498 | splx(s); |
499 | DPRINTF(("uirda_read: tsleep() = %d\n" , error)); |
500 | goto ret; |
501 | } |
502 | } |
503 | splx(s); |
504 | |
505 | mutex_enter(&sc->sc_rd_buf_lk); |
506 | n = sc->sc_rd_count - sc->sc_hdszi; |
507 | DPRINTFN(1,("%s: sc=%p n=%u, hdr=0x%02x\n" , __func__, |
508 | sc, n, sc->sc_rd_buf[0])); |
509 | if (n > uio->uio_resid) |
510 | error = EINVAL; |
511 | else |
512 | error = uiomove(sc->sc_rd_buf + sc->sc_hdszi, n, uio); |
513 | sc->sc_rd_count = 0; |
514 | mutex_exit(&sc->sc_rd_buf_lk); |
515 | |
516 | uirda_start_read(sc); |
517 | /* XXX check uirda_start_read() return value */ |
518 | |
519 | } while (n == 0); |
520 | |
521 | DPRINTFN(1,("uirda_read: return %d\n" , error)); |
522 | |
523 | ret: |
524 | if (--sc->sc_refcnt < 0) |
525 | usb_detach_wakeupold(sc->sc_dev); |
526 | return error; |
527 | } |
528 | |
529 | int |
530 | uirda_write(void *h, struct uio *uio, int flag) |
531 | { |
532 | struct uirda_softc *sc = h; |
533 | usbd_status err; |
534 | uint32_t n; |
535 | int error = 0; |
536 | |
537 | DPRINTFN(1,("%s: sc=%p\n" , __func__, sc)); |
538 | |
539 | if (sc->sc_dying) |
540 | return EIO; |
541 | |
542 | #ifdef DIAGNOSTIC |
543 | if (sc->sc_wr_buf == NULL) |
544 | return EINVAL; |
545 | #endif |
546 | |
547 | n = uio->uio_resid; |
548 | if (n > sc->sc_params.maxsize) |
549 | return EINVAL; |
550 | |
551 | sc->sc_refcnt++; |
552 | mutex_enter(&sc->sc_wr_buf_lk); |
553 | |
554 | sc->sc_wr_buf[0] = UIRDA_EB_NO_CHANGE | UIRDA_NO_SPEED; |
555 | error = uiomove(sc->sc_wr_buf + UIRDA_OUTPUT_HEADER_SIZE, n, uio); |
556 | if (error) |
557 | goto done; |
558 | |
559 | DPRINTFN(1, ("uirdawrite: transfer %d bytes\n" , n)); |
560 | |
561 | n += UIRDA_OUTPUT_HEADER_SIZE; |
562 | err = usbd_bulk_transfer(sc->sc_wr_xfer, sc->sc_wr_pipe, |
563 | USBD_FORCE_SHORT_XFER, UIRDA_WR_TIMEOUT, sc->sc_wr_buf, &n); |
564 | DPRINTFN(2, ("uirdawrite: err=%d\n" , err)); |
565 | if (err) { |
566 | if (err == USBD_INTERRUPTED) |
567 | error = EINTR; |
568 | else if (err == USBD_TIMEOUT) |
569 | error = ETIMEDOUT; |
570 | else |
571 | error = EIO; |
572 | } |
573 | done: |
574 | mutex_exit(&sc->sc_wr_buf_lk); |
575 | if (--sc->sc_refcnt < 0) |
576 | usb_detach_wakeupold(sc->sc_dev); |
577 | |
578 | DPRINTFN(1,("%s: sc=%p done\n" , __func__, sc)); |
579 | return error; |
580 | } |
581 | |
582 | int |
583 | uirda_poll(void *h, int events, struct lwp *l) |
584 | { |
585 | struct uirda_softc *sc = h; |
586 | int revents = 0; |
587 | int s; |
588 | |
589 | DPRINTFN(1,("%s: sc=%p\n" , __func__, sc)); |
590 | |
591 | s = splusb(); |
592 | if (events & (POLLOUT | POLLWRNORM)) |
593 | revents |= events & (POLLOUT | POLLWRNORM); |
594 | if (events & (POLLIN | POLLRDNORM)) { |
595 | if (sc->sc_rd_count != 0) { |
596 | DPRINTFN(2,("%s: have data\n" , __func__)); |
597 | revents |= events & (POLLIN | POLLRDNORM); |
598 | } else { |
599 | DPRINTFN(2,("%s: recording select\n" , __func__)); |
600 | selrecord(l, &sc->sc_rd_sel); |
601 | } |
602 | } |
603 | splx(s); |
604 | |
605 | return revents; |
606 | } |
607 | |
608 | static void |
609 | filt_uirdardetach(struct knote *kn) |
610 | { |
611 | struct uirda_softc *sc = kn->kn_hook; |
612 | int s; |
613 | |
614 | s = splusb(); |
615 | SLIST_REMOVE(&sc->sc_rd_sel.sel_klist, kn, knote, kn_selnext); |
616 | splx(s); |
617 | } |
618 | |
619 | static int |
620 | filt_uirdaread(struct knote *kn, long hint) |
621 | { |
622 | struct uirda_softc *sc = kn->kn_hook; |
623 | |
624 | kn->kn_data = sc->sc_rd_count; |
625 | return kn->kn_data > 0; |
626 | } |
627 | |
628 | static void |
629 | filt_uirdawdetach(struct knote *kn) |
630 | { |
631 | struct uirda_softc *sc = kn->kn_hook; |
632 | int s; |
633 | |
634 | s = splusb(); |
635 | SLIST_REMOVE(&sc->sc_wr_sel.sel_klist, kn, knote, kn_selnext); |
636 | splx(s); |
637 | } |
638 | |
639 | static const struct filterops uirdaread_filtops = |
640 | { 1, NULL, filt_uirdardetach, filt_uirdaread }; |
641 | static const struct filterops uirdawrite_filtops = |
642 | { 1, NULL, filt_uirdawdetach, filt_seltrue }; |
643 | |
644 | int |
645 | uirda_kqfilter(void *h, struct knote *kn) |
646 | { |
647 | struct uirda_softc *sc = kn->kn_hook; |
648 | struct klist *klist; |
649 | int s; |
650 | |
651 | switch (kn->kn_filter) { |
652 | case EVFILT_READ: |
653 | klist = &sc->sc_rd_sel.sel_klist; |
654 | kn->kn_fop = &uirdaread_filtops; |
655 | break; |
656 | case EVFILT_WRITE: |
657 | klist = &sc->sc_wr_sel.sel_klist; |
658 | kn->kn_fop = &uirdawrite_filtops; |
659 | break; |
660 | default: |
661 | return EINVAL; |
662 | } |
663 | |
664 | kn->kn_hook = sc; |
665 | |
666 | s = splusb(); |
667 | SLIST_INSERT_HEAD(klist, kn, kn_selnext); |
668 | splx(s); |
669 | |
670 | return 0; |
671 | } |
672 | |
673 | int |
674 | uirda_set_params(void *h, struct irda_params *p) |
675 | { |
676 | struct uirda_softc *sc = h; |
677 | usbd_status err; |
678 | int i; |
679 | uint8_t hdr; |
680 | uint32_t n; |
681 | u_int mask; |
682 | |
683 | DPRINTF(("%s: sc=%p, speed=%d ebofs=%d maxsize=%d\n" , __func__, |
684 | sc, p->speed, p->ebofs, p->maxsize)); |
685 | |
686 | if (sc->sc_dying) |
687 | return EIO; |
688 | |
689 | hdr = 0; |
690 | if (p->ebofs != sc->sc_params.ebofs) { |
691 | /* round up ebofs */ |
692 | mask = 1 /* sc->sc_irdadesc.bmAdditionalBOFs*/; |
693 | DPRINTF(("u.s.p.: mask=0x%x, sc->ebofs=%d, p->ebofs=%d\n" , |
694 | mask, sc->sc_params.ebofs, p->ebofs)); |
695 | for (i = 0; i < UIRDA_NEBOFS; i++) { |
696 | DPRINTF(("u.s.p.: u_e[%d].mask=0x%x, count=%d\n" , |
697 | i, uirda_ebofs[i].mask, uirda_ebofs[i].count)); |
698 | if ((mask & uirda_ebofs[i].mask) && |
699 | uirda_ebofs[i].count >= p->ebofs) { |
700 | hdr = uirda_ebofs[i].header; |
701 | goto found1; |
702 | } |
703 | } |
704 | for (i = 0; i < UIRDA_NEBOFS; i++) { |
705 | DPRINTF(("u.s.p.: u_e[%d].mask=0x%x, count=%d\n" , |
706 | i, uirda_ebofs[i].mask, uirda_ebofs[i].count)); |
707 | if ((mask & uirda_ebofs[i].mask)) { |
708 | hdr = uirda_ebofs[i].header; |
709 | goto found1; |
710 | } |
711 | } |
712 | /* no good value found */ |
713 | return EINVAL; |
714 | found1: |
715 | DPRINTF(("uirda_set_params: ebofs hdr=0x%02x\n" , hdr)); |
716 | ; |
717 | |
718 | } |
719 | if (hdr != 0 || p->speed != sc->sc_params.speed) { |
720 | /* find speed */ |
721 | mask = UGETW(sc->sc_irdadesc.wBaudRate); |
722 | for (i = 0; i < UIRDA_NSPEEDS; i++) { |
723 | if ((mask & uirda_speeds[i].mask) && |
724 | uirda_speeds[i].speed == p->speed) { |
725 | hdr |= uirda_speeds[i].header; |
726 | goto found2; |
727 | } |
728 | } |
729 | /* no good value found */ |
730 | return EINVAL; |
731 | found2: |
732 | DPRINTF(("uirda_set_params: speed hdr=0x%02x\n" , hdr)); |
733 | ; |
734 | } |
735 | if (p->maxsize != sc->sc_params.maxsize) { |
736 | if (p->maxsize > IRDA_MAX_FRAME_SIZE) |
737 | return EINVAL; |
738 | sc->sc_params.maxsize = p->maxsize; |
739 | #if 0 |
740 | DPRINTF(("%s: new buffers, old size=%d\n" , __func__, |
741 | sc->sc_params.maxsize)); |
742 | if (p->maxsize > 10000 || p < 0) /* XXX */ |
743 | return EINVAL; |
744 | |
745 | /* Change the write buffer */ |
746 | mutex_enter(&sc->sc_wr_buf_lk); |
747 | if (sc->sc_wr_buf != NULL) |
748 | usbd_free_buffer(sc->sc_wr_xfer); |
749 | sc->sc_wr_buf = usbd_get_buffer(sc->sc_wr_xfer, p->maxsize+1); |
750 | mutex_exit(&sc->sc_wr_buf_lk); |
751 | if (sc->sc_wr_buf == NULL) |
752 | return ENOMEM; |
753 | |
754 | /* Change the read buffer */ |
755 | mutex_enter(&sc->sc_rd_buf_lk); |
756 | usbd_abort_pipe(sc->sc_rd_pipe); |
757 | if (sc->sc_rd_buf != NULL) |
758 | usbd_free_buffer(sc->sc_rd_xfer); |
759 | sc->sc_rd_buf = usbd_get_buffer(sc->sc_rd_xfer, p->maxsize+1); |
760 | sc->sc_rd_count = 0; |
761 | if (sc->sc_rd_buf == NULL) { |
762 | mutex_exit(&sc->sc_rd_buf_lk); |
763 | return ENOMEM; |
764 | } |
765 | sc->sc_params.maxsize = p->maxsize; |
766 | err = uirda_start_read(sc); /* XXX check */ |
767 | mutex_exit(&sc->sc_rd_buf_lk); |
768 | #endif |
769 | } |
770 | if (hdr != 0 && hdr != sc->sc_wr_hdr) { |
771 | /* |
772 | * A change has occurred, transmit a 0 length frame with |
773 | * the new settings. The 0 length frame is not sent to the |
774 | * device. |
775 | */ |
776 | DPRINTF(("%s: sc=%p setting header 0x%02x\n" , |
777 | __func__, sc, hdr)); |
778 | sc->sc_wr_hdr = hdr; |
779 | mutex_enter(&sc->sc_wr_buf_lk); |
780 | sc->sc_wr_buf[0] = hdr; |
781 | n = UIRDA_OUTPUT_HEADER_SIZE; |
782 | err = usbd_bulk_transfer(sc->sc_wr_xfer, sc->sc_wr_pipe, |
783 | USBD_FORCE_SHORT_XFER, UIRDA_WR_TIMEOUT, |
784 | sc->sc_wr_buf, &n); |
785 | if (err) { |
786 | aprint_error_dev(sc->sc_dev, "set failed, err=%d\n" , |
787 | err); |
788 | usbd_clear_endpoint_stall(sc->sc_wr_pipe); |
789 | } |
790 | mutex_exit(&sc->sc_wr_buf_lk); |
791 | } |
792 | |
793 | sc->sc_params = *p; |
794 | |
795 | return 0; |
796 | } |
797 | |
798 | int |
799 | uirda_get_speeds(void *h, int *speeds) |
800 | { |
801 | struct uirda_softc *sc = h; |
802 | u_int isp; |
803 | u_int usp; |
804 | |
805 | DPRINTF(("%s: sc=%p\n" , __func__, sc)); |
806 | |
807 | if (sc->sc_dying) |
808 | return EIO; |
809 | |
810 | usp = UGETW(sc->sc_irdadesc.wBaudRate); |
811 | isp = 0; |
812 | if (usp & UI_BR_4000000) isp |= IRDA_SPEED_4000000; |
813 | if (usp & UI_BR_1152000) isp |= IRDA_SPEED_1152000; |
814 | if (usp & UI_BR_576000) isp |= IRDA_SPEED_576000; |
815 | if (usp & UI_BR_115200) isp |= IRDA_SPEED_115200; |
816 | if (usp & UI_BR_57600) isp |= IRDA_SPEED_57600; |
817 | if (usp & UI_BR_38400) isp |= IRDA_SPEED_38400; |
818 | if (usp & UI_BR_19200) isp |= IRDA_SPEED_19200; |
819 | if (usp & UI_BR_9600) isp |= IRDA_SPEED_9600; |
820 | if (usp & UI_BR_2400) isp |= IRDA_SPEED_2400; |
821 | *speeds = isp; |
822 | DPRINTF(("%s: speeds = 0x%x\n" , __func__, isp)); |
823 | return 0; |
824 | } |
825 | |
826 | int |
827 | uirda_get_turnarounds(void *h, int *turnarounds) |
828 | { |
829 | struct uirda_softc *sc = h; |
830 | u_int ita; |
831 | u_int uta; |
832 | |
833 | DPRINTF(("%s: sc=%p\n" , __func__, sc)); |
834 | |
835 | if (sc->sc_dying) |
836 | return EIO; |
837 | |
838 | uta = sc->sc_irdadesc.bmMinTurnaroundTime; |
839 | ita = 0; |
840 | if (uta & UI_TA_0) ita |= IRDA_TURNT_0; |
841 | if (uta & UI_TA_10) ita |= IRDA_TURNT_10; |
842 | if (uta & UI_TA_50) ita |= IRDA_TURNT_50; |
843 | if (uta & UI_TA_100) ita |= IRDA_TURNT_100; |
844 | if (uta & UI_TA_500) ita |= IRDA_TURNT_500; |
845 | if (uta & UI_TA_1000) ita |= IRDA_TURNT_1000; |
846 | if (uta & UI_TA_5000) ita |= IRDA_TURNT_5000; |
847 | if (uta & UI_TA_10000) ita |= IRDA_TURNT_10000; |
848 | *turnarounds = ita; |
849 | return 0; |
850 | } |
851 | |
852 | void |
853 | uirda_rd_cb(struct usbd_xfer *xfer, void *priv, |
854 | usbd_status status) |
855 | { |
856 | struct uirda_softc *sc = priv; |
857 | uint32_t size; |
858 | |
859 | DPRINTFN(1,("%s: sc=%p\n" , __func__, sc)); |
860 | |
861 | if (status == USBD_CANCELLED) /* this is normal */ |
862 | return; |
863 | if (status) { |
864 | size = sc->sc_hdszi; |
865 | sc->sc_rd_err = 1; |
866 | } else { |
867 | usbd_get_xfer_status(xfer, NULL, NULL, &size, NULL); |
868 | } |
869 | DPRINTFN(1,("%s: sc=%p size=%u, err=%d\n" , __func__, sc, size, |
870 | sc->sc_rd_err)); |
871 | sc->sc_rd_count = size; |
872 | wakeup(&sc->sc_rd_count); /* XXX should use flag */ |
873 | selnotify(&sc->sc_rd_sel, 0, 0); |
874 | } |
875 | |
876 | usbd_status |
877 | uirda_start_read(struct uirda_softc *sc) |
878 | { |
879 | usbd_status err; |
880 | |
881 | DPRINTFN(1,("%s: sc=%p, size=%d\n" , __func__, sc, |
882 | sc->sc_params.maxsize + UIRDA_INPUT_HEADER_SIZE)); |
883 | |
884 | if (sc->sc_dying) |
885 | return USBD_IOERROR; |
886 | |
887 | if (sc->sc_rd_err) { |
888 | sc->sc_rd_err = 0; |
889 | DPRINTF(("uirda_start_read: clear stall\n" )); |
890 | usbd_clear_endpoint_stall(sc->sc_rd_pipe); |
891 | } |
892 | |
893 | usbd_setup_xfer(sc->sc_rd_xfer, sc, sc->sc_rd_buf, |
894 | sc->sc_params.maxsize + sc->sc_hdszi, USBD_SHORT_XFER_OK, |
895 | USBD_NO_TIMEOUT, uirda_rd_cb); |
896 | err = usbd_transfer(sc->sc_rd_xfer); |
897 | if (err != USBD_IN_PROGRESS) { |
898 | DPRINTF(("uirda_start_read: err=%d\n" , err)); |
899 | return err; |
900 | } |
901 | return USBD_NORMAL_COMPLETION; |
902 | } |
903 | |
904 | usbd_status |
905 | usbd_get_class_desc(struct usbd_device *dev, int type, int index, int len, void *desc) |
906 | { |
907 | usb_device_request_t req; |
908 | |
909 | DPRINTFN(3,("usbd_get_desc: type=%d, index=%d, len=%d\n" , |
910 | type, index, len)); |
911 | |
912 | req.bmRequestType = 0xa1; /* XXX ? */ |
913 | req.bRequest = UR_GET_DESCRIPTOR; |
914 | USETW2(req.wValue, type, index); |
915 | USETW(req.wIndex, 0); |
916 | USETW(req.wLength, len); |
917 | return usbd_do_request(dev, &req, desc); |
918 | } |
919 | |