1 /* $NetBSD: uslsa.c,v 1.10 2009/09/23 19:07:19 plunky Exp $ */
6 * Copyright (c) 2004, 2005 The NetBSD Foundation, Inc.
9 * This code is derived from software contributed to The NetBSD Foundation
10 * by Roland C. Dowdeswell <elric@netbsd.org>.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
35 * Copyright (c) 2007, 2009 Jonathan A. Kollasch.
36 * All rights reserved.
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
41 * 1. Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * 2. Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in the
45 * documentation and/or other materials provided with the distribution.
47 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
48 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
49 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
50 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
51 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
52 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
53 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
54 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
55 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
56 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61 * Craig Shelley's Linux driver was used for documentation.
64 #include <sys/cdefs.h>
65 __KERNEL_RCSID(0, "$NetBSD: uslsa.c,v 1.10 2009/09/23 19:07:19 plunky Exp $");
67 #include <sys/param.h>
68 #include <sys/systm.h>
69 #include <sys/kernel.h>
70 #include <sys/device.h>
74 #include <dev/usb/usb.h>
76 #include <dev/usb/usbdi.h>
77 #include <dev/usb/usbdi_util.h>
78 #include <dev/usb/usbdevs.h>
80 #include <dev/usb/ucomvar.h>
87 #define DPRINTF(x) if (uslsadebug) printf x
88 #define DPRINTFN(n,x) if (uslsadebug>(n)) printf x
95 #define USLSA_REQ_SET_STATE 0x00
97 #define USLSA_REQ_SET_BPS 0x01
98 #define USLSA_REQ_GET_BPS 0x02
100 #define USLSA_REQ_SET_DPS 0x03
101 #define USLSA_REQ_GET_DPS 0x04
103 #define USLSA_REQ_SET_BREAK 0x05
104 #define USLSA_REQ_GET_BREAK 0x06
106 #define USLSA_REQ_SET_FLOW 0x07
107 #define USLSA_REQ_GET_FLOW 0x08
109 #define USLSA_REQ_SET_MODEM 0x13
110 #define USLSA_REQ_GET_MODEM 0x14
112 #define USLSA_REQ_SET_MISC 0x19
113 #define USLSA_REQ_GET_MISC 0x20
115 #define USLSA_STATE_DISABLE 0x0000
116 #define USLSA_STATE_ENABLE 0x0001
118 #define USLSA_BPS(b) (3686400/b)
120 #define USLSA_DPS_DATA_MASK 0x0f00
121 #define USLSA_DPS_DATA_FIVE 0x0500
122 #define USLSA_DPS_DATA_SIX 0x0600
123 #define USLSA_DPS_DATA_SEVEN 0x0700
124 #define USLSA_DPS_DATA_EIGHT 0x0800
125 #define USLSA_DPS_DATA_NINE 0x0900
127 #define USLSA_DPS_PARITY_MASK 0x00f0
128 #define USLSA_DPS_PARITY_SPACE 0x0040
129 #define USLSA_DPS_PARITY_MARK 0x0030
130 #define USLSA_DPS_PARITY_EVEN 0x0020
131 #define USLSA_DPS_PARITY_ODD 0x0010
132 #define USLSA_DPS_PARITY_NONE 0x0000
134 #define USLSA_DPS_STOP_MASK 0x000f
135 #define USLSA_DPS_STOP_TWO 0x0002
136 #define USLSA_DPS_STOP_ONE_FIVE 0x0001
137 #define USLSA_DPS_STOP_ONE 0x0000
139 #define USLSA_BREAK_DISABLE 0x0001
140 #define USLSA_BREAK_ENABLE 0x0000
142 #define USLSA_FLOW_SET_RTS 0x0200
143 #define USLSA_FLOW_SET_DTR 0x0100
144 #define USLSA_FLOW_MSR_MASK 0x00f0
145 #define USLSA_FLOW_MSR_DCD 0x0080
146 #define USLSA_FLOW_MSR_RI 0x0040
147 #define USLSA_FLOW_MSR_DSR 0x0020
148 #define USLSA_FLOW_MSR_CTS 0x0010
149 #define USLSA_FLOW_RTS 0x0002
150 #define USLSA_FLOW_DTR 0x0001
153 USBBASEDEVICE sc_dev
; /* base device */
154 usbd_device_handle sc_udev
; /* device */
155 usbd_interface_handle sc_iface
; /* interface */
157 device_t sc_subdev
; /* ucom device */
159 u_char sc_dying
; /* disconnecting */
161 u_char sc_lsr
; /* local status register */
162 u_char sc_msr
; /* uslsa status register */
165 static void uslsa_get_status(void *sc
, int, u_char
*, u_char
*);
166 static void uslsa_set(void *, int, int, int);
167 static int uslsa_param(void *, int, struct termios
*);
168 static int uslsa_open(void *, int);
169 static void uslsa_close(void *, int);
171 static int uslsa_request_set(struct uslsa_softc
*, uint8_t, uint16_t);
172 static void uslsa_set_flow(struct uslsa_softc
*, tcflag_t
, tcflag_t
);
174 struct ucom_methods uslsa_methods
= {
185 #define USLSA_CONFIG_INDEX 0
186 #define USLSA_IFACE_INDEX 0
187 #define USLSA_BUFSIZE 256
189 static const struct usb_devno uslsa_devs
[] = {
190 { USB_VENDOR_BALTECH
, USB_PRODUCT_BALTECH_CARDREADER
},
191 { USB_VENDOR_DYNASTREAM
, USB_PRODUCT_DYNASTREAM_ANTDEVBOARD
},
192 { USB_VENDOR_JABLOTRON
, USB_PRODUCT_JABLOTRON_PC60B
},
193 { USB_VENDOR_SILABS
, USB_PRODUCT_SILABS_ARGUSISP
},
194 { USB_VENDOR_SILABS
, USB_PRODUCT_SILABS_CRUMB128
},
195 { USB_VENDOR_SILABS
, USB_PRODUCT_SILABS_DEGREECONT
},
196 { USB_VENDOR_SILABS
, USB_PRODUCT_SILABS_DESKTOPMOBILE
},
197 { USB_VENDOR_SILABS
, USB_PRODUCT_SILABS_IPLINK1220
},
198 { USB_VENDOR_SILABS
, USB_PRODUCT_SILABS_LIPOWSKY_HARP
},
199 { USB_VENDOR_SILABS
, USB_PRODUCT_SILABS_LIPOWSKY_JTAG
},
200 { USB_VENDOR_SILABS
, USB_PRODUCT_SILABS_LIPOWSKY_LIN
},
201 { USB_VENDOR_SILABS
, USB_PRODUCT_SILABS_POLOLU
},
202 { USB_VENDOR_SILABS
, USB_PRODUCT_SILABS_CP210X_1
},
203 { USB_VENDOR_SILABS
, USB_PRODUCT_SILABS_CP210X_2
},
204 { USB_VENDOR_SILABS
, USB_PRODUCT_SILABS_SUNNTO
},
205 { USB_VENDOR_SILABS2
, USB_PRODUCT_SILABS2_DCU11CLONE
},
206 { USB_VENDOR_USI
, USB_PRODUCT_USI_MC60
}
208 #define uslsa_lookup(v, p) usb_lookup(uslsa_devs, v, p)
210 int uslsa_match(device_t
, cfdata_t
, void *);
211 void uslsa_attach(device_t
, device_t
, void *);
212 void uslsa_childdet(device_t
, device_t
);
213 int uslsa_detach(device_t
, int);
214 int uslsa_activate(device_t
, enum devact
);
215 extern struct cfdriver uslsa_cd
;
216 CFATTACH_DECL2_NEW(uslsa
, sizeof(struct uslsa_softc
), uslsa_match
,
217 uslsa_attach
, uslsa_detach
, uslsa_activate
, NULL
, uslsa_childdet
);
221 USB_MATCH_START(uslsa
, uaa
);
223 return (uslsa_lookup(uaa
->vendor
, uaa
->product
) != NULL
?
224 UMATCH_VENDOR_PRODUCT
: UMATCH_NONE
);
229 USB_ATTACH_START(uslsa
, sc
, uaa
);
230 usbd_device_handle dev
= uaa
->device
;
231 usbd_interface_handle iface
;
232 usb_interface_descriptor_t
*id
;
233 usb_endpoint_descriptor_t
*ed
;
236 struct ucom_attach_args uca
;
241 DPRINTFN(10, ("\nuslsa_attach: sc=%p\n", sc
));
246 devinfop
= usbd_devinfo_alloc(dev
, 0);
247 aprint_normal_dev(self
, "%s\n", devinfop
);
248 usbd_devinfo_free(devinfop
);
250 /* Move the device into the configured state. */
251 err
= usbd_set_config_index(dev
, USLSA_CONFIG_INDEX
, 1);
253 aprint_error_dev(self
, "failed to set configuration, err=%s\n",
258 err
= usbd_device2interface_handle(dev
, USLSA_IFACE_INDEX
, &iface
);
260 aprint_error_dev(self
, "failed to get interface, err=%s\n",
265 id
= usbd_get_interface_descriptor(iface
);
268 sc
->sc_iface
= iface
;
270 uca
.info
= "Silicon Labs CP210x";
271 uca
.portno
= UCOM_UNK_PORTNO
;
272 uca
.ibufsize
= USLSA_BUFSIZE
;
273 uca
.obufsize
= USLSA_BUFSIZE
;
274 uca
.ibufsizepad
= USLSA_BUFSIZE
;
278 uca
.methods
= &uslsa_methods
;
281 usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH
, sc
->sc_udev
,
284 uca
.bulkin
= uca
.bulkout
= -1;
285 for (i
= 0; i
< id
->bNumEndpoints
; i
++) {
288 ed
= usbd_interface2endpoint_descriptor(iface
, i
);
290 aprint_error_dev(self
,
291 "could not read endpoint descriptor: %s\n",
295 addr
= ed
->bEndpointAddress
;
296 dir
= UE_GET_DIR(ed
->bEndpointAddress
);
297 attr
= ed
->bmAttributes
& UE_XFERTYPE
;
298 if (dir
== UE_DIR_IN
&& attr
== UE_BULK
)
300 else if (dir
== UE_DIR_OUT
&& attr
== UE_BULK
)
303 aprint_error_dev(self
, "unexpected endpoint\n");
305 if (uca
.bulkin
== -1) {
306 aprint_error_dev(self
, "Could not find data bulk in\n");
309 if (uca
.bulkout
== -1) {
310 aprint_error_dev(self
, "Could not find data bulk out\n");
314 DPRINTF(("uslsa: in=0x%x out=0x%x\n", uca
.bulkin
, uca
.bulkout
));
315 sc
->sc_subdev
= config_found_sm_loc(self
, "ucombus", NULL
, &uca
,
316 ucomprint
, ucomsubmatch
);
318 USB_ATTACH_SUCCESS_RETURN
;
321 DPRINTF(("uslsa_attach: ATTACH ERROR\n"));
323 USB_ATTACH_ERROR_RETURN
;
327 uslsa_activate(device_t self
, enum devact act
)
329 struct uslsa_softc
*sc
= device_private(self
);
332 case DVACT_DEACTIVATE
:
341 uslsa_childdet(device_t self
, device_t child
)
343 struct uslsa_softc
*sc
= device_private(self
);
345 KASSERT(sc
->sc_subdev
== child
);
346 sc
->sc_subdev
= NULL
;
351 USB_DETACH_START(uslsa
, sc
);
354 DPRINTF(("uslsa_detach: sc=%p flags=%d\n", sc
, flags
));
358 if (sc
->sc_subdev
!= NULL
)
359 rv
= config_detach(sc
->sc_subdev
, flags
);
361 usbd_add_drv_event(USB_EVENT_DRIVER_DETACH
, sc
->sc_udev
,
368 uslsa_get_status(void *vsc
, int portno
, u_char
*lsr
, u_char
*msr
)
370 struct uslsa_softc
*sc
;
371 usb_device_request_t req
;
378 DPRINTF(("uslsa_get_status:\n"));
380 req
.bmRequestType
= UT_READ_VENDOR_INTERFACE
;
381 req
.bRequest
= USLSA_REQ_GET_FLOW
;
382 USETW(req
.wValue
, 0);
383 USETW(req
.wIndex
, 0);
384 USETW(req
.wLength
, sizeof(flowreg
));
386 err
= usbd_do_request_flags(sc
->sc_udev
, &req
, &flowreg
,
387 USBD_SHORT_XFER_OK
, &actlen
, USBD_DEFAULT_TIMEOUT
);
389 printf("%s: uslsa_get_status: %s\n",
390 USBDEVNAME(sc
->sc_dev
), usbd_errstr(err
));
392 DPRINTF(("uslsa_get_status: flowreg=0x%x\n", flowreg
));
394 sc
->sc_msr
= (u_char
)(USLSA_FLOW_MSR_MASK
& flowreg
);
403 uslsa_set(void *vsc
, int portno
, int reg
, int onoff
)
405 struct uslsa_softc
*sc
;
409 DPRINTF(("uslsa_set: sc=%p, port=%d reg=%d onoff=%d\n", sc
, portno
,
414 if (uslsa_request_set(sc
, USLSA_REQ_SET_FLOW
,
415 (onoff
? (USLSA_FLOW_DTR
| USLSA_FLOW_SET_DTR
) :
416 USLSA_FLOW_SET_DTR
)))
417 printf("%s: uslsa_set_dtr failed\n",
418 USBDEVNAME(sc
->sc_dev
));
421 if (uslsa_request_set(sc
, USLSA_REQ_SET_FLOW
,
422 (onoff
? (USLSA_FLOW_RTS
| USLSA_FLOW_SET_RTS
) :
423 USLSA_FLOW_SET_RTS
)))
424 printf("%s: uslsa_set_rts failed\n",
425 USBDEVNAME(sc
->sc_dev
));
428 if (uslsa_request_set(sc
, USLSA_REQ_SET_BREAK
,
429 (onoff
? USLSA_BREAK_ENABLE
:
430 USLSA_BREAK_DISABLE
)))
431 printf("%s: uslsa_set_break failed\n",
432 USBDEVNAME(sc
->sc_dev
));
440 uslsa_param(void *vsc
, int portno
, struct termios
* t
)
442 struct uslsa_softc
*sc
;
447 DPRINTF(("uslsa_param: sc=%p\n", sc
));
449 switch (t
->c_ospeed
) {
462 data
= USLSA_BPS(t
->c_ospeed
);
465 printf("%s: uslsa_param: unsupported data rate, "
466 "forcing default of 115200bps\n",
467 USBDEVNAME(sc
->sc_dev
));
468 data
= USLSA_BPS(B115200
);
471 if (uslsa_request_set(sc
, USLSA_REQ_SET_BPS
, data
))
472 printf("%s: uslsa_param: setting data rate failed\n",
473 USBDEVNAME(sc
->sc_dev
));
477 if (ISSET(t
->c_cflag
, CSTOPB
))
478 data
|= USLSA_DPS_STOP_TWO
;
480 data
|= USLSA_DPS_STOP_ONE
;
482 if (ISSET(t
->c_cflag
, PARENB
)) {
483 if (ISSET(t
->c_cflag
, PARODD
))
484 data
|= USLSA_DPS_PARITY_ODD
;
486 data
|= USLSA_DPS_PARITY_EVEN
;
488 data
|= USLSA_DPS_PARITY_NONE
;
490 switch (ISSET(t
->c_cflag
, CSIZE
)) {
492 data
|= USLSA_DPS_DATA_FIVE
;
495 data
|= USLSA_DPS_DATA_SIX
;
498 data
|= USLSA_DPS_DATA_SEVEN
;
501 data
|= USLSA_DPS_DATA_EIGHT
;
505 DPRINTF(("uslsa_param: setting DPS register to 0x%x\n", data
));
506 if (uslsa_request_set(sc
, USLSA_REQ_SET_DPS
, data
))
507 printf("%s: setting DPS register failed: invalid argument\n",
508 USBDEVNAME(sc
->sc_dev
));
510 uslsa_set_flow(sc
, t
->c_cflag
, t
->c_iflag
);
517 uslsa_open(void *vsc
, int portno
)
519 struct uslsa_softc
*sc
;
523 DPRINTF(("uslsa_open: sc=%p\n", sc
));
528 if (uslsa_request_set(sc
, USLSA_REQ_SET_STATE
, USLSA_STATE_ENABLE
))
535 uslsa_close(void *vsc
, int portno
)
537 struct uslsa_softc
*sc
;
544 DPRINTF(("uslsa_close: sc=%p\n", sc
));
546 if (uslsa_request_set(sc
, USLSA_REQ_SET_STATE
,
547 USLSA_STATE_DISABLE
))
548 printf("%s: disable-on-close failed\n",
549 USBDEVNAME(sc
->sc_dev
));
553 * uslsa_request_set(), wrapper for doing sets with usbd_do_request()
556 uslsa_request_set(struct uslsa_softc
* sc
, uint8_t request
, uint16_t value
)
558 usb_device_request_t req
;
561 req
.bmRequestType
= UT_WRITE_VENDOR_INTERFACE
;
562 req
.bRequest
= request
;
563 USETW(req
.wValue
, value
);
564 USETW(req
.wIndex
, 0);
565 USETW(req
.wLength
, 0);
567 err
= usbd_do_request(sc
->sc_udev
, &req
, 0);
569 printf("%s: uslsa_request: %s\n",
570 USBDEVNAME(sc
->sc_dev
), usbd_errstr(err
));
575 * uslsa_set_flow() does some magic to set up hardware flow control
578 uslsa_set_flow(struct uslsa_softc
*sc
, tcflag_t cflag
, tcflag_t iflag
)
580 uint8_t mysterydata
[16];
581 usb_device_request_t req
;
584 DPRINTF(("uslsa_set_flow: cflag = 0x%x, iflag = 0x%x\n",
587 req
.bmRequestType
= UT_READ_VENDOR_INTERFACE
;
588 req
.bRequest
= USLSA_REQ_GET_MODEM
;
589 USETW(req
.wValue
, 0);
590 USETW(req
.wIndex
, 0);
591 USETW(req
.wLength
, 16);
593 err
= usbd_do_request(sc
->sc_udev
, &req
, mysterydata
);
595 printf("%s: uslsa_set_flow: %s\n",
596 USBDEVNAME(sc
->sc_dev
), usbd_errstr(err
));
598 if (ISSET(cflag
, CRTSCTS
)) {
599 mysterydata
[0] &= ~0x7b;
600 mysterydata
[0] |= 0x09;
601 mysterydata
[4] = 0x80;
603 mysterydata
[0] &= ~0x7b;
604 mysterydata
[0] |= 0x01;
605 mysterydata
[4] = 0x40;
608 req
.bmRequestType
= UT_WRITE_VENDOR_INTERFACE
;
609 req
.bRequest
= USLSA_REQ_SET_MODEM
;
610 USETW(req
.wValue
, 0);
611 USETW(req
.wIndex
, 0);
612 USETW(req
.wLength
, 16);
614 err
= usbd_do_request(sc
->sc_udev
, &req
, mysterydata
);
616 printf("%s: uslsa_set_flow: %s\n",
617 USBDEVNAME(sc
->sc_dev
), usbd_errstr(err
));