1 | /* $NetBSD: umct.c,v 1.35 2016/07/07 06:55:42 msaitoh Exp $ */ |
2 | /* |
3 | * Copyright (c) 2001 The NetBSD Foundation, Inc. |
4 | * All rights reserved. |
5 | * |
6 | * This code is derived from software contributed to The NetBSD Foundation |
7 | * by Ichiro FUKUHARA (ichiro@ichiro.org). |
8 | * |
9 | * Redistribution and use in source and binary forms, with or without |
10 | * modification, are permitted provided that the following conditions |
11 | * are met: |
12 | * 1. Redistributions of source code must retain the above copyright |
13 | * notice, this list of conditions and the following disclaimer. |
14 | * 2. Redistributions in binary form must reproduce the above copyright |
15 | * notice, this list of conditions and the following disclaimer in the |
16 | * documentation and/or other materials provided with the distribution. |
17 | * |
18 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
19 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
20 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
21 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
22 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
28 | * POSSIBILITY OF SUCH DAMAGE. |
29 | */ |
30 | |
31 | /* |
32 | * MCT USB-RS232 Interface Controller |
33 | * http://www.mct.com.tw/prod/rs232.html |
34 | * http://www.dlink.com/products/usb/dsbs25 |
35 | */ |
36 | |
37 | #include <sys/cdefs.h> |
38 | __KERNEL_RCSID(0, "$NetBSD: umct.c,v 1.35 2016/07/07 06:55:42 msaitoh Exp $" ); |
39 | |
40 | #include <sys/param.h> |
41 | #include <sys/systm.h> |
42 | #include <sys/kernel.h> |
43 | #include <sys/ioctl.h> |
44 | #include <sys/conf.h> |
45 | #include <sys/tty.h> |
46 | #include <sys/file.h> |
47 | #include <sys/select.h> |
48 | #include <sys/proc.h> |
49 | #include <sys/vnode.h> |
50 | #include <sys/device.h> |
51 | #include <sys/poll.h> |
52 | |
53 | #include <dev/usb/usb.h> |
54 | #include <dev/usb/usbcdc.h> |
55 | |
56 | #include <dev/usb/usbdi.h> |
57 | #include <dev/usb/usbdi_util.h> |
58 | #include <dev/usb/usbdevs.h> |
59 | #include <dev/usb/usb_quirks.h> |
60 | |
61 | #include <dev/usb/ucomvar.h> |
62 | #include <dev/usb/umct.h> |
63 | |
64 | #ifdef UMCT_DEBUG |
65 | #define DPRINTFN(n, x) if (umctdebug > (n)) printf x |
66 | int umctdebug = 0; |
67 | #else |
68 | #define DPRINTFN(n, x) |
69 | #endif |
70 | #define DPRINTF(x) DPRINTFN(0, x) |
71 | |
72 | #define UMCT_CONFIG_INDEX 0 |
73 | #define UMCT_IFACE_INDEX 0 |
74 | |
75 | struct umct_softc { |
76 | device_t sc_dev; /* base device */ |
77 | struct usbd_device * sc_udev; /* USB device */ |
78 | struct usbd_interface * sc_iface; /* interface */ |
79 | int sc_iface_number; /* interface number */ |
80 | uint16_t sc_product; |
81 | |
82 | int sc_intr_number; /* interrupt number */ |
83 | struct usbd_pipe * sc_intr_pipe; /* interrupt pipe */ |
84 | u_char *sc_intr_buf; /* interrupt buffer */ |
85 | int sc_isize; |
86 | |
87 | usb_cdc_line_state_t sc_line_state; /* current line state */ |
88 | u_char sc_dtr; /* current DTR state */ |
89 | u_char sc_rts; /* current RTS state */ |
90 | u_char sc_break; /* set break */ |
91 | |
92 | u_char sc_status; |
93 | |
94 | device_t sc_subdev; /* ucom device */ |
95 | |
96 | u_char sc_dying; /* disconnecting */ |
97 | |
98 | u_char sc_lsr; /* Local status register */ |
99 | u_char sc_msr; /* umct status register */ |
100 | |
101 | u_int last_lcr; /* keep lcr register */ |
102 | }; |
103 | |
104 | /* |
105 | * These are the maximum number of bytes transferred per frame. |
106 | * The output buffer size cannot be increased due to the size encoding. |
107 | */ |
108 | #define UMCTIBUFSIZE 256 |
109 | #define UMCTOBUFSIZE 256 |
110 | |
111 | Static void umct_init(struct umct_softc *); |
112 | Static void umct_set_baudrate(struct umct_softc *, u_int); |
113 | Static void umct_set_lcr(struct umct_softc *, u_int); |
114 | Static void umct_intr(struct usbd_xfer *, void *, usbd_status); |
115 | |
116 | Static void umct_set(void *, int, int, int); |
117 | Static void umct_dtr(struct umct_softc *, int); |
118 | Static void umct_rts(struct umct_softc *, int); |
119 | Static void umct_break(struct umct_softc *, int); |
120 | Static void umct_set_line_state(struct umct_softc *); |
121 | Static void umct_get_status(void *, int, u_char *, u_char *); |
122 | Static int umct_param(void *, int, struct termios *); |
123 | Static int umct_open(void *, int); |
124 | Static void umct_close(void *, int); |
125 | |
126 | struct ucom_methods umct_methods = { |
127 | .ucom_get_status = umct_get_status, |
128 | .ucom_set = umct_set, |
129 | .ucom_param = umct_param, |
130 | .ucom_ioctl = NULL, |
131 | .ucom_open = umct_open, |
132 | .ucom_close = umct_close, |
133 | .ucom_read = NULL, |
134 | .ucom_write = NULL, |
135 | }; |
136 | |
137 | static const struct usb_devno umct_devs[] = { |
138 | /* MCT USB-232 Interface Products */ |
139 | { USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232 }, |
140 | /* Sitecom USB-232 Products */ |
141 | { USB_VENDOR_MCT, USB_PRODUCT_MCT_SITECOM_USB232 }, |
142 | /* D-Link DU-H3SP USB BAY Hub Products */ |
143 | { USB_VENDOR_MCT, USB_PRODUCT_MCT_DU_H3SP_USB232 }, |
144 | /* BELKIN F5U109 */ |
145 | { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U109 }, |
146 | }; |
147 | #define umct_lookup(v, p) usb_lookup(umct_devs, v, p) |
148 | |
149 | int umct_match(device_t, cfdata_t, void *); |
150 | void umct_attach(device_t, device_t, void *); |
151 | void umct_childdet(device_t, device_t); |
152 | int umct_detach(device_t, int); |
153 | int umct_activate(device_t, enum devact); |
154 | extern struct cfdriver umct_cd; |
155 | CFATTACH_DECL2_NEW(umct, sizeof(struct umct_softc), umct_match, |
156 | umct_attach, umct_detach, umct_activate, NULL, umct_childdet); |
157 | |
158 | int |
159 | umct_match(device_t parent, cfdata_t match, void *aux) |
160 | { |
161 | struct usb_attach_arg *uaa = aux; |
162 | |
163 | return umct_lookup(uaa->uaa_vendor, uaa->uaa_product) != NULL ? |
164 | UMATCH_VENDOR_PRODUCT : UMATCH_NONE; |
165 | } |
166 | |
167 | void |
168 | umct_attach(device_t parent, device_t self, void *aux) |
169 | { |
170 | struct umct_softc *sc = device_private(self); |
171 | struct usb_attach_arg *uaa = aux; |
172 | struct usbd_device *dev = uaa->uaa_device; |
173 | usb_config_descriptor_t *cdesc; |
174 | usb_interface_descriptor_t *id; |
175 | usb_endpoint_descriptor_t *ed; |
176 | |
177 | char *devinfop; |
178 | usbd_status err; |
179 | int i; |
180 | struct ucom_attach_args ucaa; |
181 | |
182 | sc->sc_dev = self; |
183 | |
184 | aprint_naive("\n" ); |
185 | aprint_normal("\n" ); |
186 | |
187 | devinfop = usbd_devinfo_alloc(dev, 0); |
188 | aprint_normal_dev(self, "%s\n" , devinfop); |
189 | usbd_devinfo_free(devinfop); |
190 | |
191 | sc->sc_udev = dev; |
192 | sc->sc_product = uaa->uaa_product; |
193 | |
194 | DPRINTF(("\n\numct attach: sc=%p\n" , sc)); |
195 | |
196 | /* initialize endpoints */ |
197 | ucaa.ucaa_bulkin = ucaa.ucaa_bulkout = -1; |
198 | sc->sc_intr_number = -1; |
199 | sc->sc_intr_pipe = NULL; |
200 | |
201 | /* Move the device into the configured state. */ |
202 | err = usbd_set_config_index(dev, UMCT_CONFIG_INDEX, 1); |
203 | if (err) { |
204 | aprint_error_dev(self, "failed to set configuration, err=%s\n" , |
205 | usbd_errstr(err)); |
206 | sc->sc_dying = 1; |
207 | return; |
208 | } |
209 | |
210 | /* get the config descriptor */ |
211 | cdesc = usbd_get_config_descriptor(sc->sc_udev); |
212 | |
213 | if (cdesc == NULL) { |
214 | aprint_error_dev(self, |
215 | "failed to get configuration descriptor\n" ); |
216 | sc->sc_dying = 1; |
217 | return; |
218 | } |
219 | |
220 | /* get the interface */ |
221 | err = usbd_device2interface_handle(dev, UMCT_IFACE_INDEX, |
222 | &sc->sc_iface); |
223 | if (err) { |
224 | aprint_error_dev(self, "failed to get interface, err=%s\n" , |
225 | usbd_errstr(err)); |
226 | sc->sc_dying = 1; |
227 | return; |
228 | } |
229 | |
230 | /* Find the bulk{in,out} and interrupt endpoints */ |
231 | |
232 | id = usbd_get_interface_descriptor(sc->sc_iface); |
233 | sc->sc_iface_number = id->bInterfaceNumber; |
234 | |
235 | for (i = 0; i < id->bNumEndpoints; i++) { |
236 | ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); |
237 | if (ed == NULL) { |
238 | aprint_error_dev(self, |
239 | "no endpoint descriptor for %d\n" , i); |
240 | sc->sc_dying = 1; |
241 | return; |
242 | } |
243 | |
244 | /* |
245 | * The Bulkin endpoint is marked as an interrupt. Since |
246 | * we can't rely on the endpoint descriptor order, we'll |
247 | * check the wMaxPacketSize field to differentiate. |
248 | */ |
249 | if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && |
250 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT && |
251 | UGETW(ed->wMaxPacketSize) != 0x2) { |
252 | ucaa.ucaa_bulkin = ed->bEndpointAddress; |
253 | } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && |
254 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { |
255 | ucaa.ucaa_bulkout = ed->bEndpointAddress; |
256 | } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && |
257 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { |
258 | sc->sc_intr_number = ed->bEndpointAddress; |
259 | sc->sc_isize = UGETW(ed->wMaxPacketSize); |
260 | } |
261 | } |
262 | |
263 | if (ucaa.ucaa_bulkin == -1) { |
264 | aprint_error_dev(self, "Could not find data bulk in\n" ); |
265 | sc->sc_dying = 1; |
266 | return; |
267 | } |
268 | |
269 | if (ucaa.ucaa_bulkout == -1) { |
270 | aprint_error_dev(self, "Could not find data bulk out\n" ); |
271 | sc->sc_dying = 1; |
272 | return; |
273 | } |
274 | |
275 | if (sc->sc_intr_number == -1) { |
276 | aprint_error_dev(self, "Could not find interrupt in\n" ); |
277 | sc->sc_dying = 1; |
278 | return; |
279 | } |
280 | |
281 | sc->sc_dtr = sc->sc_rts = 0; |
282 | ucaa.ucaa_portno = UCOM_UNK_PORTNO; |
283 | /* ucaa_bulkin, ucaa_bulkout set above */ |
284 | ucaa.ucaa_ibufsize = UMCTIBUFSIZE; |
285 | if (sc->sc_product == USB_PRODUCT_MCT_SITECOM_USB232) |
286 | ucaa.ucaa_obufsize = 16; /* device is broken */ |
287 | else |
288 | ucaa.ucaa_obufsize = UMCTOBUFSIZE; |
289 | ucaa.ucaa_ibufsizepad = UMCTIBUFSIZE; |
290 | ucaa.ucaa_opkthdrlen = 0; |
291 | ucaa.ucaa_device = dev; |
292 | ucaa.ucaa_iface = sc->sc_iface; |
293 | ucaa.ucaa_methods = &umct_methods; |
294 | ucaa.ucaa_arg = sc; |
295 | ucaa.ucaa_info = NULL; |
296 | |
297 | umct_init(sc); |
298 | |
299 | usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev); |
300 | |
301 | DPRINTF(("umct: in=0x%x out=0x%x intr=0x%x\n" , |
302 | ucaa.ucaa_bulkin, ucaa.ucaa_bulkout, sc->sc_intr_number)); |
303 | sc->sc_subdev = config_found_sm_loc(self, "ucombus" , NULL, &ucaa, |
304 | ucomprint, ucomsubmatch); |
305 | |
306 | return; |
307 | } |
308 | |
309 | void |
310 | umct_childdet(device_t self, device_t child) |
311 | { |
312 | struct umct_softc *sc = device_private(self); |
313 | |
314 | KASSERT(sc->sc_subdev == child); |
315 | sc->sc_subdev = NULL; |
316 | } |
317 | |
318 | int |
319 | umct_detach(device_t self, int flags) |
320 | { |
321 | struct umct_softc *sc = device_private(self); |
322 | int rv = 0; |
323 | |
324 | DPRINTF(("umct_detach: sc=%p flags=%d\n" , sc, flags)); |
325 | |
326 | if (sc->sc_intr_pipe != NULL) { |
327 | usbd_abort_pipe(sc->sc_intr_pipe); |
328 | usbd_close_pipe(sc->sc_intr_pipe); |
329 | kmem_free(sc->sc_intr_buf, sc->sc_isize); |
330 | sc->sc_intr_pipe = NULL; |
331 | } |
332 | |
333 | sc->sc_dying = 1; |
334 | if (sc->sc_subdev != NULL) |
335 | rv = config_detach(sc->sc_subdev, flags); |
336 | |
337 | usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev); |
338 | |
339 | return rv; |
340 | } |
341 | |
342 | int |
343 | umct_activate(device_t self, enum devact act) |
344 | { |
345 | struct umct_softc *sc = device_private(self); |
346 | |
347 | switch (act) { |
348 | case DVACT_DEACTIVATE: |
349 | sc->sc_dying = 1; |
350 | return 0; |
351 | default: |
352 | return EOPNOTSUPP; |
353 | } |
354 | } |
355 | |
356 | void |
357 | umct_set_line_state(struct umct_softc *sc) |
358 | { |
359 | usb_device_request_t req; |
360 | uByte ls; |
361 | |
362 | ls = (sc->sc_dtr ? MCR_DTR : 0) | |
363 | (sc->sc_rts ? MCR_RTS : 0); |
364 | |
365 | DPRINTF(("umct_set_line_state: DTR=%d,RTS=%d,ls=%02x\n" , |
366 | sc->sc_dtr, sc->sc_rts, ls)); |
367 | |
368 | req.bmRequestType = UMCT_SET_REQUEST; |
369 | req.bRequest = REQ_SET_MCR; |
370 | USETW(req.wValue, 0); |
371 | USETW(req.wIndex, sc->sc_iface_number); |
372 | USETW(req.wLength, LENGTH_SET_MCR); |
373 | |
374 | (void)usbd_do_request(sc->sc_udev, &req, &ls); |
375 | } |
376 | |
377 | void |
378 | umct_set(void *addr, int portno, int reg, int onoff) |
379 | { |
380 | struct umct_softc *sc = addr; |
381 | |
382 | switch (reg) { |
383 | case UCOM_SET_DTR: |
384 | umct_dtr(sc, onoff); |
385 | break; |
386 | case UCOM_SET_RTS: |
387 | umct_rts(sc, onoff); |
388 | break; |
389 | case UCOM_SET_BREAK: |
390 | umct_break(sc, onoff); |
391 | break; |
392 | default: |
393 | break; |
394 | } |
395 | } |
396 | |
397 | void |
398 | umct_dtr(struct umct_softc *sc, int onoff) |
399 | { |
400 | |
401 | DPRINTF(("umct_dtr: onoff=%d\n" , onoff)); |
402 | |
403 | if (sc->sc_dtr == onoff) |
404 | return; |
405 | sc->sc_dtr = onoff; |
406 | |
407 | umct_set_line_state(sc); |
408 | } |
409 | |
410 | void |
411 | umct_rts(struct umct_softc *sc, int onoff) |
412 | { |
413 | DPRINTF(("umct_rts: onoff=%d\n" , onoff)); |
414 | |
415 | if (sc->sc_rts == onoff) |
416 | return; |
417 | sc->sc_rts = onoff; |
418 | |
419 | umct_set_line_state(sc); |
420 | } |
421 | |
422 | void |
423 | umct_break(struct umct_softc *sc, int onoff) |
424 | { |
425 | DPRINTF(("umct_break: onoff=%d\n" , onoff)); |
426 | |
427 | umct_set_lcr(sc, onoff ? sc->last_lcr | LCR_SET_BREAK : |
428 | sc->last_lcr); |
429 | } |
430 | |
431 | void |
432 | umct_set_lcr(struct umct_softc *sc, u_int data) |
433 | { |
434 | usb_device_request_t req; |
435 | uByte adata; |
436 | |
437 | adata = data; |
438 | req.bmRequestType = UMCT_SET_REQUEST; |
439 | req.bRequest = REQ_SET_LCR; |
440 | USETW(req.wValue, 0); |
441 | USETW(req.wIndex, sc->sc_iface_number); |
442 | USETW(req.wLength, LENGTH_SET_LCR); |
443 | |
444 | (void)usbd_do_request(sc->sc_udev, &req, &adata); /* XXX should check */ |
445 | } |
446 | |
447 | void |
448 | umct_set_baudrate(struct umct_softc *sc, u_int rate) |
449 | { |
450 | usb_device_request_t req; |
451 | uDWord arate; |
452 | u_int val; |
453 | |
454 | if (sc->sc_product == USB_PRODUCT_MCT_SITECOM_USB232 || |
455 | sc->sc_product == USB_PRODUCT_BELKIN_F5U109) { |
456 | switch (rate) { |
457 | case 300: val = 0x01; break; |
458 | case 600: val = 0x02; break; |
459 | case 1200: val = 0x03; break; |
460 | case 2400: val = 0x04; break; |
461 | case 4800: val = 0x06; break; |
462 | case 9600: val = 0x08; break; |
463 | case 19200: val = 0x09; break; |
464 | case 38400: val = 0x0a; break; |
465 | case 57600: val = 0x0b; break; |
466 | case 115200: val = 0x0c; break; |
467 | default: val = -1; break; |
468 | } |
469 | } else { |
470 | val = UMCT_BAUD_RATE(rate); |
471 | } |
472 | USETDW(arate, val); |
473 | |
474 | req.bmRequestType = UMCT_SET_REQUEST; |
475 | req.bRequest = REQ_SET_BAUD_RATE; |
476 | USETW(req.wValue, 0); |
477 | USETW(req.wIndex, sc->sc_iface_number); |
478 | USETW(req.wLength, LENGTH_BAUD_RATE); |
479 | |
480 | (void)usbd_do_request(sc->sc_udev, &req, arate); /* XXX should check */ |
481 | } |
482 | |
483 | void |
484 | umct_init(struct umct_softc *sc) |
485 | { |
486 | umct_set_baudrate(sc, 9600); |
487 | umct_set_lcr(sc, LCR_DATA_BITS_8 | LCR_PARITY_NONE | LCR_STOP_BITS_1); |
488 | } |
489 | |
490 | int |
491 | umct_param(void *addr, int portno, struct termios *t) |
492 | { |
493 | struct umct_softc *sc = addr; |
494 | u_int data = 0; |
495 | |
496 | DPRINTF(("umct_param: sc=%p\n" , sc)); |
497 | |
498 | DPRINTF(("umct_param: BAUDRATE=%d\n" , t->c_ospeed)); |
499 | |
500 | if (ISSET(t->c_cflag, CSTOPB)) |
501 | data |= LCR_STOP_BITS_2; |
502 | else |
503 | data |= LCR_STOP_BITS_1; |
504 | if (ISSET(t->c_cflag, PARENB)) { |
505 | if (ISSET(t->c_cflag, PARODD)) |
506 | data |= LCR_PARITY_ODD; |
507 | else |
508 | data |= LCR_PARITY_EVEN; |
509 | } else |
510 | data |= LCR_PARITY_NONE; |
511 | switch (ISSET(t->c_cflag, CSIZE)) { |
512 | case CS5: |
513 | data |= LCR_DATA_BITS_5; |
514 | break; |
515 | case CS6: |
516 | data |= LCR_DATA_BITS_6; |
517 | break; |
518 | case CS7: |
519 | data |= LCR_DATA_BITS_7; |
520 | break; |
521 | case CS8: |
522 | data |= LCR_DATA_BITS_8; |
523 | break; |
524 | } |
525 | |
526 | umct_set_baudrate(sc, t->c_ospeed); |
527 | |
528 | sc->last_lcr = data; |
529 | umct_set_lcr(sc, data); |
530 | |
531 | return 0; |
532 | } |
533 | |
534 | int |
535 | umct_open(void *addr, int portno) |
536 | { |
537 | struct umct_softc *sc = addr; |
538 | int err, lcr_data; |
539 | |
540 | if (sc->sc_dying) |
541 | return EIO; |
542 | |
543 | DPRINTF(("umct_open: sc=%p\n" , sc)); |
544 | |
545 | /* initialize LCR */ |
546 | lcr_data = LCR_DATA_BITS_8 | LCR_PARITY_NONE | |
547 | LCR_STOP_BITS_1; |
548 | umct_set_lcr(sc, lcr_data); |
549 | |
550 | if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) { |
551 | sc->sc_status = 0; /* clear status bit */ |
552 | sc->sc_intr_buf = kmem_alloc(sc->sc_isize, KM_SLEEP); |
553 | err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_intr_number, |
554 | USBD_SHORT_XFER_OK, &sc->sc_intr_pipe, sc, |
555 | sc->sc_intr_buf, sc->sc_isize, |
556 | umct_intr, USBD_DEFAULT_INTERVAL); |
557 | if (err) { |
558 | DPRINTF(("%s: cannot open interrupt pipe (addr %d)\n" , |
559 | device_xname(sc->sc_dev), sc->sc_intr_number)); |
560 | return EIO; |
561 | } |
562 | } |
563 | |
564 | return 0; |
565 | } |
566 | |
567 | void |
568 | umct_close(void *addr, int portno) |
569 | { |
570 | struct umct_softc *sc = addr; |
571 | int err; |
572 | |
573 | if (sc->sc_dying) |
574 | return; |
575 | |
576 | DPRINTF(("umct_close: close\n" )); |
577 | |
578 | if (sc->sc_intr_pipe != NULL) { |
579 | err = usbd_abort_pipe(sc->sc_intr_pipe); |
580 | if (err) |
581 | printf("%s: abort interrupt pipe failed: %s\n" , |
582 | device_xname(sc->sc_dev), usbd_errstr(err)); |
583 | err = usbd_close_pipe(sc->sc_intr_pipe); |
584 | if (err) |
585 | printf("%s: close interrupt pipe failed: %s\n" , |
586 | device_xname(sc->sc_dev), usbd_errstr(err)); |
587 | kmem_free(sc->sc_intr_buf, sc->sc_isize); |
588 | sc->sc_intr_pipe = NULL; |
589 | } |
590 | } |
591 | |
592 | void |
593 | umct_intr(struct usbd_xfer *xfer, void *priv, |
594 | usbd_status status) |
595 | { |
596 | struct umct_softc *sc = priv; |
597 | u_char *tbuf = sc->sc_intr_buf; |
598 | u_char mstatus; |
599 | |
600 | if (sc->sc_dying) |
601 | return; |
602 | |
603 | if (status != USBD_NORMAL_COMPLETION) { |
604 | if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) |
605 | return; |
606 | |
607 | DPRINTF(("%s: abnormal status: %s\n" , device_xname(sc->sc_dev), |
608 | usbd_errstr(status))); |
609 | usbd_clear_endpoint_stall_async(sc->sc_intr_pipe); |
610 | return; |
611 | } |
612 | |
613 | DPRINTF(("%s: umct status = MSR:%02x, LSR:%02x\n" , |
614 | device_xname(sc->sc_dev), tbuf[0],tbuf[1])); |
615 | |
616 | sc->sc_lsr = sc->sc_msr = 0; |
617 | mstatus = tbuf[0]; |
618 | if (ISSET(mstatus, MSR_DSR)) |
619 | sc->sc_msr |= UMSR_DSR; |
620 | if (ISSET(mstatus, MSR_DCD)) |
621 | sc->sc_msr |= UMSR_DCD; |
622 | ucom_status_change(device_private(sc->sc_subdev)); |
623 | } |
624 | |
625 | void |
626 | umct_get_status(void *addr, int portno, u_char *lsr, u_char *msr) |
627 | { |
628 | struct umct_softc *sc = addr; |
629 | |
630 | DPRINTF(("umct_get_status:\n" )); |
631 | |
632 | if (lsr != NULL) |
633 | *lsr = sc->sc_lsr; |
634 | if (msr != NULL) |
635 | *msr = sc->sc_msr; |
636 | } |
637 | |