1 /* $NetBSD: umct.c,v 1.28 2009/09/23 19:07:19 plunky Exp $ */
3 * Copyright (c) 2001 The NetBSD Foundation, Inc.
6 * This code is derived from software contributed to The NetBSD Foundation
7 * by Ichiro FUKUHARA (ichiro@ichiro.org).
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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.
32 * MCT USB-RS232 Interface Controller
33 * http://www.mct.com.tw/prod/rs232.html
34 * http://www.dlink.com/products/usb/dsbs25
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: umct.c,v 1.28 2009/09/23 19:07:19 plunky Exp $");
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/malloc.h>
44 #include <sys/ioctl.h>
48 #include <sys/select.h>
50 #include <sys/vnode.h>
51 #include <sys/device.h>
54 #include <dev/usb/usb.h>
55 #include <dev/usb/usbcdc.h>
57 #include <dev/usb/usbdi.h>
58 #include <dev/usb/usbdi_util.h>
59 #include <dev/usb/usbdevs.h>
60 #include <dev/usb/usb_quirks.h>
62 #include <dev/usb/ucomvar.h>
63 #include <dev/usb/umct.h>
66 #define DPRINTFN(n, x) if (umctdebug > (n)) logprintf x
69 #define DPRINTFN(n, x)
71 #define DPRINTF(x) DPRINTFN(0, x)
73 #define UMCT_CONFIG_INDEX 0
74 #define UMCT_IFACE_INDEX 0
77 USBBASEDEVICE sc_dev
; /* base device */
78 usbd_device_handle sc_udev
; /* USB device */
79 usbd_interface_handle sc_iface
; /* interface */
80 int sc_iface_number
; /* interface number */
83 int sc_intr_number
; /* interrupt number */
84 usbd_pipe_handle sc_intr_pipe
; /* interrupt pipe */
85 u_char
*sc_intr_buf
; /* interrupt buffer */
88 usb_cdc_line_state_t sc_line_state
; /* current line state */
89 u_char sc_dtr
; /* current DTR state */
90 u_char sc_rts
; /* current RTS state */
91 u_char sc_break
; /* set break */
95 device_t sc_subdev
; /* ucom device */
97 u_char sc_dying
; /* disconnecting */
99 u_char sc_lsr
; /* Local status register */
100 u_char sc_msr
; /* umct status register */
102 u_int last_lcr
; /* keep lcr register */
106 * These are the maximum number of bytes transferred per frame.
107 * The output buffer size cannot be increased due to the size encoding.
109 #define UMCTIBUFSIZE 256
110 #define UMCTOBUFSIZE 256
112 Static
void umct_init(struct umct_softc
*);
113 Static
void umct_set_baudrate(struct umct_softc
*, u_int
);
114 Static
void umct_set_lcr(struct umct_softc
*, u_int
);
115 Static
void umct_intr(usbd_xfer_handle
, usbd_private_handle
, usbd_status
);
117 Static
void umct_set(void *, int, int, int);
118 Static
void umct_dtr(struct umct_softc
*, int);
119 Static
void umct_rts(struct umct_softc
*, int);
120 Static
void umct_break(struct umct_softc
*, int);
121 Static
void umct_set_line_state(struct umct_softc
*);
122 Static
void umct_get_status(void *, int portno
, u_char
*lsr
, u_char
*msr
);
123 Static
int umct_param(void *, int, struct termios
*);
124 Static
int umct_open(void *, int);
125 Static
void umct_close(void *, int);
127 struct ucom_methods umct_methods
= {
138 static const struct usb_devno umct_devs
[] = {
139 /* MCT USB-232 Interface Products */
140 { USB_VENDOR_MCT
, USB_PRODUCT_MCT_USB232
},
141 /* Sitecom USB-232 Products */
142 { USB_VENDOR_MCT
, USB_PRODUCT_MCT_SITECOM_USB232
},
143 /* D-Link DU-H3SP USB BAY Hub Products */
144 { USB_VENDOR_MCT
, USB_PRODUCT_MCT_DU_H3SP_USB232
},
146 { USB_VENDOR_BELKIN
, USB_PRODUCT_BELKIN_F5U109
},
148 #define umct_lookup(v, p) usb_lookup(umct_devs, v, p)
150 int umct_match(device_t
, cfdata_t
, void *);
151 void umct_attach(device_t
, device_t
, void *);
152 void umct_childdet(device_t
, device_t
);
153 int umct_detach(device_t
, int);
154 int umct_activate(device_t
, enum devact
);
155 extern struct cfdriver umct_cd
;
156 CFATTACH_DECL2_NEW(umct
, sizeof(struct umct_softc
), umct_match
,
157 umct_attach
, umct_detach
, umct_activate
, NULL
, umct_childdet
);
161 USB_MATCH_START(umct
, uaa
);
163 return (umct_lookup(uaa
->vendor
, uaa
->product
) != NULL
?
164 UMATCH_VENDOR_PRODUCT
: UMATCH_NONE
);
169 USB_ATTACH_START(umct
, sc
, uaa
);
170 usbd_device_handle dev
= uaa
->device
;
171 usb_config_descriptor_t
*cdesc
;
172 usb_interface_descriptor_t
*id
;
173 usb_endpoint_descriptor_t
*ed
;
178 struct ucom_attach_args uca
;
185 devinfop
= usbd_devinfo_alloc(dev
, 0);
186 aprint_normal_dev(self
, "%s\n", devinfop
);
187 usbd_devinfo_free(devinfop
);
190 sc
->sc_product
= uaa
->product
;
192 DPRINTF(("\n\numct attach: sc=%p\n", sc
));
194 /* initialize endpoints */
195 uca
.bulkin
= uca
.bulkout
= -1;
196 sc
->sc_intr_number
= -1;
197 sc
->sc_intr_pipe
= NULL
;
199 /* Move the device into the configured state. */
200 err
= usbd_set_config_index(dev
, UMCT_CONFIG_INDEX
, 1);
202 aprint_error_dev(self
, "failed to set configuration, err=%s\n",
205 USB_ATTACH_ERROR_RETURN
;
208 /* get the config descriptor */
209 cdesc
= usbd_get_config_descriptor(sc
->sc_udev
);
212 aprint_error_dev(self
,
213 "failed to get configuration descriptor\n");
215 USB_ATTACH_ERROR_RETURN
;
218 /* get the interface */
219 err
= usbd_device2interface_handle(dev
, UMCT_IFACE_INDEX
,
222 aprint_error_dev(self
, "failed to get interface, err=%s\n",
225 USB_ATTACH_ERROR_RETURN
;
228 /* Find the bulk{in,out} and interrupt endpoints */
230 id
= usbd_get_interface_descriptor(sc
->sc_iface
);
231 sc
->sc_iface_number
= id
->bInterfaceNumber
;
233 for (i
= 0; i
< id
->bNumEndpoints
; i
++) {
234 ed
= usbd_interface2endpoint_descriptor(sc
->sc_iface
, i
);
236 aprint_error_dev(self
,
237 "no endpoint descriptor for %d\n", i
);
239 USB_ATTACH_ERROR_RETURN
;
243 * The Bulkin endpoint is marked as an interrupt. Since
244 * we can't rely on the endpoint descriptor order, we'll
245 * check the wMaxPacketSize field to differentiate.
247 if (UE_GET_DIR(ed
->bEndpointAddress
) == UE_DIR_IN
&&
248 UE_GET_XFERTYPE(ed
->bmAttributes
) == UE_INTERRUPT
&&
249 UGETW(ed
->wMaxPacketSize
) != 0x2) {
250 uca
.bulkin
= ed
->bEndpointAddress
;
251 } else if (UE_GET_DIR(ed
->bEndpointAddress
) == UE_DIR_OUT
&&
252 UE_GET_XFERTYPE(ed
->bmAttributes
) == UE_BULK
) {
253 uca
.bulkout
= ed
->bEndpointAddress
;
254 } else if (UE_GET_DIR(ed
->bEndpointAddress
) == UE_DIR_IN
&&
255 UE_GET_XFERTYPE(ed
->bmAttributes
) == UE_INTERRUPT
) {
256 sc
->sc_intr_number
= ed
->bEndpointAddress
;
257 sc
->sc_isize
= UGETW(ed
->wMaxPacketSize
);
261 if (uca
.bulkin
== -1) {
262 aprint_error_dev(self
, "Could not find data bulk in\n");
264 USB_ATTACH_ERROR_RETURN
;
267 if (uca
.bulkout
== -1) {
268 aprint_error_dev(self
, "Could not find data bulk out\n");
270 USB_ATTACH_ERROR_RETURN
;
273 if (sc
->sc_intr_number
== -1) {
274 aprint_error_dev(self
, "Could not find interrupt in\n");
276 USB_ATTACH_ERROR_RETURN
;
279 sc
->sc_dtr
= sc
->sc_rts
= 0;
280 uca
.portno
= UCOM_UNK_PORTNO
;
281 /* bulkin, bulkout set above */
282 uca
.ibufsize
= UMCTIBUFSIZE
;
283 if (sc
->sc_product
== USB_PRODUCT_MCT_SITECOM_USB232
)
284 uca
.obufsize
= 16; /* device is broken */
286 uca
.obufsize
= UMCTOBUFSIZE
;
287 uca
.ibufsizepad
= UMCTIBUFSIZE
;
290 uca
.iface
= sc
->sc_iface
;
291 uca
.methods
= &umct_methods
;
297 usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH
, sc
->sc_udev
,
300 DPRINTF(("umct: in=0x%x out=0x%x intr=0x%x\n",
301 uca
.bulkin
, uca
.bulkout
, sc
->sc_intr_number
));
302 sc
->sc_subdev
= config_found_sm_loc(self
, "ucombus", NULL
, &uca
,
303 ucomprint
, ucomsubmatch
);
305 USB_ATTACH_SUCCESS_RETURN
;
309 umct_childdet(device_t self
, device_t child
)
311 struct umct_softc
*sc
= device_private(self
);
313 KASSERT(sc
->sc_subdev
== child
);
314 sc
->sc_subdev
= NULL
;
319 USB_DETACH_START(umct
, sc
);
322 DPRINTF(("umct_detach: sc=%p flags=%d\n", sc
, flags
));
324 if (sc
->sc_intr_pipe
!= NULL
) {
325 usbd_abort_pipe(sc
->sc_intr_pipe
);
326 usbd_close_pipe(sc
->sc_intr_pipe
);
327 free(sc
->sc_intr_buf
, M_USBDEV
);
328 sc
->sc_intr_pipe
= NULL
;
332 if (sc
->sc_subdev
!= NULL
)
333 rv
= config_detach(sc
->sc_subdev
, flags
);
335 usbd_add_drv_event(USB_EVENT_DRIVER_DETACH
, sc
->sc_udev
,
342 umct_activate(device_t self
, enum devact act
)
344 struct umct_softc
*sc
= device_private(self
);
347 case DVACT_DEACTIVATE
:
356 umct_set_line_state(struct umct_softc
*sc
)
358 usb_device_request_t req
;
361 ls
= (sc
->sc_dtr
? MCR_DTR
: 0) |
362 (sc
->sc_rts
? MCR_RTS
: 0);
364 DPRINTF(("umct_set_line_state: DTR=%d,RTS=%d,ls=%02x\n",
365 sc
->sc_dtr
, sc
->sc_rts
, ls
));
367 req
.bmRequestType
= UMCT_SET_REQUEST
;
368 req
.bRequest
= REQ_SET_MCR
;
369 USETW(req
.wValue
, 0);
370 USETW(req
.wIndex
, sc
->sc_iface_number
);
371 USETW(req
.wLength
, LENGTH_SET_MCR
);
373 (void)usbd_do_request(sc
->sc_udev
, &req
, &ls
);
377 umct_set(void *addr
, int portno
, int reg
, int onoff
)
379 struct umct_softc
*sc
= addr
;
389 umct_break(sc
, onoff
);
397 umct_dtr(struct umct_softc
*sc
, int onoff
)
400 DPRINTF(("umct_dtr: onoff=%d\n", onoff
));
402 if (sc
->sc_dtr
== onoff
)
406 umct_set_line_state(sc
);
410 umct_rts(struct umct_softc
*sc
, int onoff
)
412 DPRINTF(("umct_rts: onoff=%d\n", onoff
));
414 if (sc
->sc_rts
== onoff
)
418 umct_set_line_state(sc
);
422 umct_break(struct umct_softc
*sc
, int onoff
)
424 DPRINTF(("umct_break: onoff=%d\n", onoff
));
426 umct_set_lcr(sc
, onoff
? sc
->last_lcr
| LCR_SET_BREAK
:
431 umct_set_lcr(struct umct_softc
*sc
, u_int data
)
433 usb_device_request_t req
;
437 req
.bmRequestType
= UMCT_SET_REQUEST
;
438 req
.bRequest
= REQ_SET_LCR
;
439 USETW(req
.wValue
, 0);
440 USETW(req
.wIndex
, sc
->sc_iface_number
);
441 USETW(req
.wLength
, LENGTH_SET_LCR
);
443 (void)usbd_do_request(sc
->sc_udev
, &req
, &adata
); /* XXX should check */
447 umct_set_baudrate(struct umct_softc
*sc
, u_int rate
)
449 usb_device_request_t req
;
453 if (sc
->sc_product
== USB_PRODUCT_MCT_SITECOM_USB232
||
454 sc
->sc_product
== USB_PRODUCT_BELKIN_F5U109
) {
456 case 300: val
= 0x01; break;
457 case 600: val
= 0x02; break;
458 case 1200: val
= 0x03; break;
459 case 2400: val
= 0x04; break;
460 case 4800: val
= 0x06; break;
461 case 9600: val
= 0x08; break;
462 case 19200: val
= 0x09; break;
463 case 38400: val
= 0x0a; break;
464 case 57600: val
= 0x0b; break;
465 case 115200: val
= 0x0c; break;
466 default: val
= -1; break;
469 val
= UMCT_BAUD_RATE(rate
);
473 req
.bmRequestType
= UMCT_SET_REQUEST
;
474 req
.bRequest
= REQ_SET_BAUD_RATE
;
475 USETW(req
.wValue
, 0);
476 USETW(req
.wIndex
, sc
->sc_iface_number
);
477 USETW(req
.wLength
, LENGTH_BAUD_RATE
);
479 (void)usbd_do_request(sc
->sc_udev
, &req
, arate
); /* XXX should check */
483 umct_init(struct umct_softc
*sc
)
485 umct_set_baudrate(sc
, 9600);
486 umct_set_lcr(sc
, LCR_DATA_BITS_8
| LCR_PARITY_NONE
| LCR_STOP_BITS_1
);
490 umct_param(void *addr
, int portno
, struct termios
*t
)
492 struct umct_softc
*sc
= addr
;
495 DPRINTF(("umct_param: sc=%p\n", sc
));
497 DPRINTF(("umct_param: BAUDRATE=%d\n", t
->c_ospeed
));
499 if (ISSET(t
->c_cflag
, CSTOPB
))
500 data
|= LCR_STOP_BITS_2
;
502 data
|= LCR_STOP_BITS_1
;
503 if (ISSET(t
->c_cflag
, PARENB
)) {
504 if (ISSET(t
->c_cflag
, PARODD
))
505 data
|= LCR_PARITY_ODD
;
507 data
|= LCR_PARITY_EVEN
;
509 data
|= LCR_PARITY_NONE
;
510 switch (ISSET(t
->c_cflag
, CSIZE
)) {
512 data
|= LCR_DATA_BITS_5
;
515 data
|= LCR_DATA_BITS_6
;
518 data
|= LCR_DATA_BITS_7
;
521 data
|= LCR_DATA_BITS_8
;
525 umct_set_baudrate(sc
, t
->c_ospeed
);
528 umct_set_lcr(sc
, data
);
534 umct_open(void *addr
, int portno
)
536 struct umct_softc
*sc
= addr
;
542 DPRINTF(("umct_open: sc=%p\n", sc
));
545 lcr_data
= LCR_DATA_BITS_8
| LCR_PARITY_NONE
|
547 umct_set_lcr(sc
, lcr_data
);
549 if (sc
->sc_intr_number
!= -1 && sc
->sc_intr_pipe
== NULL
) {
550 sc
->sc_status
= 0; /* clear status bit */
551 sc
->sc_intr_buf
= malloc(sc
->sc_isize
, M_USBDEV
, M_WAITOK
);
552 err
= usbd_open_pipe_intr(sc
->sc_iface
, sc
->sc_intr_number
,
553 USBD_SHORT_XFER_OK
, &sc
->sc_intr_pipe
, sc
,
554 sc
->sc_intr_buf
, sc
->sc_isize
,
555 umct_intr
, USBD_DEFAULT_INTERVAL
);
557 DPRINTF(("%s: cannot open interrupt pipe (addr %d)\n",
558 USBDEVNAME(sc
->sc_dev
), sc
->sc_intr_number
));
567 umct_close(void *addr
, int portno
)
569 struct umct_softc
*sc
= addr
;
575 DPRINTF(("umct_close: close\n"));
577 if (sc
->sc_intr_pipe
!= NULL
) {
578 err
= usbd_abort_pipe(sc
->sc_intr_pipe
);
580 printf("%s: abort interrupt pipe failed: %s\n",
581 USBDEVNAME(sc
->sc_dev
), usbd_errstr(err
));
582 err
= usbd_close_pipe(sc
->sc_intr_pipe
);
584 printf("%s: close interrupt pipe failed: %s\n",
585 USBDEVNAME(sc
->sc_dev
), usbd_errstr(err
));
586 free(sc
->sc_intr_buf
, M_USBDEV
);
587 sc
->sc_intr_pipe
= NULL
;
592 umct_intr(usbd_xfer_handle xfer
, usbd_private_handle priv
,
595 struct umct_softc
*sc
= priv
;
596 u_char
*tbuf
= sc
->sc_intr_buf
;
602 if (status
!= USBD_NORMAL_COMPLETION
) {
603 if (status
== USBD_NOT_STARTED
|| status
== USBD_CANCELLED
)
606 DPRINTF(("%s: abnormal status: %s\n", USBDEVNAME(sc
->sc_dev
),
607 usbd_errstr(status
)));
608 usbd_clear_endpoint_stall_async(sc
->sc_intr_pipe
);
612 DPRINTF(("%s: umct status = MSR:%02x, LSR:%02x\n",
613 USBDEVNAME(sc
->sc_dev
), tbuf
[0],tbuf
[1]));
615 sc
->sc_lsr
= sc
->sc_msr
= 0;
617 if (ISSET(mstatus
, MSR_DSR
))
618 sc
->sc_msr
|= UMSR_DSR
;
619 if (ISSET(mstatus
, MSR_DCD
))
620 sc
->sc_msr
|= UMSR_DCD
;
621 ucom_status_change(device_private(sc
->sc_subdev
));
625 umct_get_status(void *addr
, int portno
, u_char
*lsr
, u_char
*msr
)
627 struct umct_softc
*sc
= addr
;
629 DPRINTF(("umct_get_status:\n"));