uslsa.c revision 1.1 1 /*
2 * Copyright (c) 2007 The NetBSD Foundation, Inc.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to The NetBSD Foundation
6 * by Jonathan A. Kollasch <jakllsch (at) kollasch.net>. Craig Shelley's Linux
7 * driver provided invaluable documentation.
8 *
9 * This code is derived from ugensa.c, software contributed to
10 * The NetBSD Foundation by Roland C. Dowdeswell <elric (at) netbsd.org>.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
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.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by the NetBSD
23 * Foundation, Inc. and its contributors.
24 * 4. Neither the name of The NetBSD Foundation nor the names of its
25 * contributors may be used to endorse or promote products derived
26 * from this software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
29 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
32 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38 * POSSIBILITY OF SUCH DAMAGE.
39 */
40
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/kernel.h>
44 #include <sys/device.h>
45 #include <sys/conf.h>
46 #include <sys/tty.h>
47
48 #include <dev/usb/usb.h>
49 #include <dev/usb/usbhid.h>
50
51 #include <dev/usb/usbdi.h>
52 #include <dev/usb/usbdi_util.h>
53 #include <dev/usb/usbdevs.h>
54
55 #include <dev/usb/ucomvar.h>
56
57 #ifdef DEBUG
58 #define USLSA_DEBUG
59 #endif
60
61 #ifdef USLSA_DEBUG
62 #define DPRINTF(x) if (uslsadebug) printf x
63 #define DPRINTFN(n,x) if (uslsadebug>(n)) printf x
64 int uslsadebug = 0;
65 #else
66 #define DPRINTF(x)
67 #define DPRINTFN(n,x)
68 #endif
69
70 #define USLSA_REQUEST_SET 0x41
71 #define USLSA_REQUEST_GET 0xc1
72
73 #define USLSA_REQ_SET_STATE 0x00
74
75 #define USLSA_REQ_SET_BPS 0x01
76 #define USLSA_REQ_GET_BPS 0x02
77
78 #define USLSA_REQ_SET_DPS 0x03
79 #define USLSA_REQ_GET_DPS 0x04
80
81 #define USLSA_REQ_SET_BREAK 0x05
82 #define USLSA_REQ_GET_BREAK 0x06
83
84 #define USLSA_REQ_SET_FLOW 0x07
85 #define USLSA_REQ_GET_FLOW 0x08
86
87 #define USLSA_REQ_SET_MODEM 0x13
88 #define USLSA_REQ_GET_MODEM 0x14
89
90 #define USLSA_REQ_SET_MISC 0x19
91 #define USLSA_REQ_GET_MISC 0x20
92
93 #define USLSA_STATE_DISABLE 0x0000
94 #define USLSA_STATE_ENABLE 0x0001
95
96 #define USLSA_BPS(b) (3686400/b)
97
98 #define USLSA_DPS_DATA_MASK 0x0f00
99 #define USLSA_DPS_DATA_FIVE 0x0500
100 #define USLSA_DPS_DATA_SIX 0x0600
101 #define USLSA_DPS_DATA_SEVEN 0x0700
102 #define USLSA_DPS_DATA_EIGHT 0x0800
103 #define USLSA_DPS_DATA_NINE 0x0900
104
105 #define USLSA_DPS_PARITY_MASK 0x00f0
106 #define USLSA_DPS_PARITY_SPACE 0x0040
107 #define USLSA_DPS_PARITY_MARK 0x0030
108 #define USLSA_DPS_PARITY_EVEN 0x0020
109 #define USLSA_DPS_PARITY_ODD 0x0010
110 #define USLSA_DPS_PARITY_NONE 0x0000
111
112 #define USLSA_DPS_STOP_MASK 0x000f
113 #define USLSA_DPS_STOP_TWO 0x0002
114 #define USLSA_DPS_STOP_ONE_FIVE 0x0001
115 #define USLSA_DPS_STOP_ONE 0x0000
116
117 #define USLSA_BREAK_DISABLE 0x0001
118 #define USLSA_BREAK_ENABLE 0x0000
119
120 #define USLSA_FLOW_SET_RTS 0x0200
121 #define USLSA_FLOW_SET_DTR 0x0100
122 #define USLSA_FLOW_MSR_MASK 0x00f0
123 #define USLSA_FLOW_MSR_DCD 0x0080
124 #define USLSA_FLOW_MSR_RI 0x0040
125 #define USLSA_FLOW_MSR_DSR 0x0020
126 #define USLSA_FLOW_MSR_CTS 0x0010
127 #define USLSA_FLOW_RTS 0x0002
128 #define USLSA_FLOW_DTR 0x0001
129
130 struct uslsa_softc {
131 USBBASEDEVICE sc_dev; /* base device */
132 usbd_device_handle sc_udev; /* device */
133 usbd_interface_handle sc_iface; /* interface */
134
135 device_ptr_t sc_subdev; /* ucom device */
136
137 u_char sc_dying; /* disconnecting */
138
139 u_char sc_lsr; /* local status register */
140 u_char sc_msr; /* uslsa status register */
141 };
142
143 static void uslsa_get_status(void *sc, int, u_char *, u_char *);
144 static void uslsa_set(void *, int, int, int);
145 static int uslsa_param(void *, int, struct termios *);
146 static int uslsa_open(void *, int);
147 static void uslsa_close(void *, int);
148
149 static int uslsa_request_set(struct uslsa_softc *, uint8_t, uint16_t);
150 static void uslsa_set_flow(struct uslsa_softc *, tcflag_t, tcflag_t);
151
152 struct ucom_methods uslsa_methods = {
153 uslsa_get_status,
154 uslsa_set,
155 uslsa_param,
156 NULL,
157 uslsa_open,
158 uslsa_close,
159 NULL,
160 NULL,
161 };
162
163 #define USLSA_CONFIG_INDEX 0
164 #define USLSA_IFACE_INDEX 0
165 #define USLSA_BUFSIZE 256
166
167 static const struct usb_devno uslsa_devs[] = {
168 { USB_VENDOR_BALTECH, USB_PRODUCT_BALTECH_CARDREADER },
169 { USB_VENDOR_DYNASTREAM, USB_PRODUCT_DYNASTREAM_ANTDEVBOARD },
170 { USB_VENDOR_JABLOTRON, USB_PRODUCT_JABLOTRON_PC60B },
171 { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_ARGUSISP },
172 { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CRUMB128 },
173 { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_DEGREECONT },
174 { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_DESKTOPMOBILE },
175 { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_IPLINK1220 },
176 { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_HARP },
177 { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_JTAG },
178 { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_LIN },
179 { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_POLOLU },
180 { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP210X_1 },
181 { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP210X_2 },
182 { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_SUNNTO },
183 { USB_VENDOR_SILABS2, USB_PRODUCT_SILABS2_DCU11CLONE },
184 { USB_VENDOR_USI, USB_PRODUCT_USI_MC60 }
185 };
186 #define uslsa_lookup(v, p) usb_lookup(uslsa_devs, v, p)
187
188 USB_DECLARE_DRIVER(uslsa);
189
190 USB_MATCH(uslsa)
191 {
192 USB_MATCH_START(uslsa, uaa);
193
194 return (uslsa_lookup(uaa->vendor, uaa->product) != NULL ?
195 UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
196 }
197
198 USB_ATTACH(uslsa)
199 {
200 USB_ATTACH_START(uslsa, sc, uaa);
201 usbd_device_handle dev = uaa->device;
202 usbd_interface_handle iface;
203 usb_interface_descriptor_t *id;
204 usb_endpoint_descriptor_t *ed;
205 char *devinfop;
206 char *devname;
207 usbd_status err;
208 struct ucom_attach_args uca;
209 int i;
210
211 devname = USBDEVNAME(sc->sc_dev);
212
213 DPRINTFN(10, ("\nuslsa_attach: sc=%p\n", sc));
214
215 /* Move the device into the configured state. */
216 err = usbd_set_config_index(dev, USLSA_CONFIG_INDEX, 1);
217 if (err) {
218 printf("\n%s: failed to set configuration, err=%s\n",
219 devname, usbd_errstr(err));
220 goto bad;
221 }
222
223 err = usbd_device2interface_handle(dev, USLSA_IFACE_INDEX, &iface);
224 if (err) {
225 printf("\n%s: failed to get interface, err=%s\n",
226 devname, usbd_errstr(err));
227 goto bad;
228 }
229
230 devinfop = usbd_devinfo_alloc(dev, 0);
231 USB_ATTACH_SETUP;
232 printf("%s: %s\n", devname, devinfop);
233 usbd_devinfo_free(devinfop);
234
235 id = usbd_get_interface_descriptor(iface);
236
237 sc->sc_udev = dev;
238 sc->sc_iface = iface;
239
240 uca.info = "Silicon Labs CP210x";
241 uca.ibufsize = USLSA_BUFSIZE;
242 uca.obufsize = USLSA_BUFSIZE;
243 uca.ibufsizepad = USLSA_BUFSIZE;
244 uca.opkthdrlen = 0;
245 uca.device = dev;
246 uca.iface = iface;
247 uca.methods = &uslsa_methods;
248 uca.arg = sc;
249
250 usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
251 USBDEV(sc->sc_dev));
252
253 uca.bulkin = uca.bulkout = -1;
254 for (i = 0; i < id->bNumEndpoints; i++) {
255 int addr, dir, attr;
256
257 ed = usbd_interface2endpoint_descriptor(iface, i);
258 if (ed == NULL) {
259 printf("%s: could not read endpoint descriptor"
260 ": %s\n", devname, usbd_errstr(err));
261 goto bad;
262 }
263 addr = ed->bEndpointAddress;
264 dir = UE_GET_DIR(ed->bEndpointAddress);
265 attr = ed->bmAttributes & UE_XFERTYPE;
266 if (dir == UE_DIR_IN && attr == UE_BULK)
267 uca.bulkin = addr;
268 else if (dir == UE_DIR_OUT && attr == UE_BULK)
269 uca.bulkout = addr;
270 else
271 printf("%s: unexpected endpoint\n", devname);
272 }
273 if (uca.bulkin == -1) {
274 printf("%s: Could not find data bulk in\n",
275 USBDEVNAME(sc->sc_dev));
276 goto bad;
277 }
278 if (uca.bulkout == -1) {
279 printf("%s: Could not find data bulk out\n",
280 USBDEVNAME(sc->sc_dev));
281 goto bad;
282 }
283
284 DPRINTF(("uslsa: in=0x%x out=0x%x\n", uca.bulkin, uca.bulkout));
285 sc->sc_subdev = config_found_sm_loc(self, "ucombus", NULL, &uca,
286 ucomprint, ucomsubmatch);
287
288 USB_ATTACH_SUCCESS_RETURN;
289
290 bad:
291 DPRINTF(("uslsa_attach: ATTACH ERROR\n"));
292 sc->sc_dying = 1;
293 USB_ATTACH_ERROR_RETURN;
294 }
295
296 int
297 uslsa_activate(device_ptr_t self, enum devact act)
298 {
299 struct uslsa_softc *sc = (struct uslsa_softc *) self;
300 int rv = 0;
301
302 switch (act) {
303 case DVACT_ACTIVATE:
304 return (EOPNOTSUPP);
305 break;
306
307 case DVACT_DEACTIVATE:
308 sc->sc_dying = 1;
309 if (sc->sc_subdev)
310 rv = config_deactivate(sc->sc_subdev);
311 break;
312 }
313 return (rv);
314 }
315
316 USB_DETACH(uslsa)
317 {
318 USB_DETACH_START(uslsa, sc);
319 int rv = 0;
320
321 DPRINTF(("uslsa_detach: sc=%p flags=%d\n", sc, flags));
322
323 sc->sc_dying = 1;
324
325 if (sc->sc_subdev != NULL)
326 rv = config_detach(sc->sc_subdev, flags);
327
328 usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
329 USBDEV(sc->sc_dev));
330
331 return (rv);
332 }
333
334 static void
335 uslsa_get_status(void *vsc, int portno, u_char *lsr, u_char *msr)
336 {
337 struct uslsa_softc *sc;
338 usb_device_request_t req;
339 usbd_status err;
340 int actlen;
341 uint16_t flowreg;
342
343 sc = vsc;
344
345 DPRINTF(("uslsa_get_status:\n"));
346
347 req.bmRequestType = USLSA_REQUEST_GET;
348 req.bRequest = USLSA_REQ_GET_FLOW;
349 USETW(req.wValue, 0);
350 USETW(req.wIndex, 0);
351 USETW(req.wLength, sizeof(flowreg));
352
353 err = usbd_do_request_flags(sc->sc_udev, &req, &flowreg,
354 USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT);
355 if (err)
356 printf("%s: uslsa_get_status: %s\n",
357 USBDEVNAME(sc->sc_dev), usbd_errstr(err));
358
359 DPRINTF(("uslsa_get_status: flowreg=0x%x\n", flowreg));
360
361 sc->sc_msr = (u_char)(USLSA_FLOW_MSR_MASK & flowreg);
362
363 if (lsr != NULL)
364 *lsr = sc->sc_lsr;
365 if (msr != NULL)
366 *msr = sc->sc_msr;
367 }
368
369 static void
370 uslsa_set(void *vsc, int portno, int reg, int onoff)
371 {
372 struct uslsa_softc *sc;
373
374 sc = vsc;
375
376 DPRINTF(("uslsa_set: sc=%p, port=%d reg=%d onoff=%d\n", sc, portno,
377 reg, onoff));
378
379 switch (reg) {
380 case UCOM_SET_DTR:
381 if (uslsa_request_set(sc, USLSA_REQ_SET_FLOW,
382 (onoff ? (USLSA_FLOW_DTR | USLSA_FLOW_SET_DTR) :
383 USLSA_FLOW_SET_DTR)))
384 printf("%s: uslsa_set_dtr failed\n",
385 USBDEVNAME(sc->sc_dev));
386 break;
387 case UCOM_SET_RTS:
388 if (uslsa_request_set(sc, USLSA_REQ_SET_FLOW,
389 (onoff ? (USLSA_FLOW_RTS | USLSA_FLOW_SET_RTS) :
390 USLSA_FLOW_SET_RTS)))
391 printf("%s: uslsa_set_rts failed\n",
392 USBDEVNAME(sc->sc_dev));
393 break;
394 case UCOM_SET_BREAK:
395 if (uslsa_request_set(sc, USLSA_REQ_SET_BREAK,
396 (onoff ? USLSA_BREAK_ENABLE :
397 USLSA_BREAK_DISABLE)))
398 printf("%s: uslsa_set_break failed\n",
399 USBDEVNAME(sc->sc_dev));
400 break;
401 default:
402 break;
403 }
404 }
405
406 static int
407 uslsa_param(void *vsc, int portno, struct termios * t)
408 {
409 struct uslsa_softc *sc;
410 uint16_t data;
411
412 sc = vsc;
413
414 DPRINTF(("uslsa_param: sc=%p\n", sc));
415
416 switch (t->c_ospeed) {
417 case B600:
418 case B1200:
419 case B2400:
420 case B4800:
421 case B9600:
422 case B19200:
423 case B38400:
424 case B57600:
425 case B115200:
426 case B230400:
427 case B460800:
428 case B921600:
429 data = USLSA_BPS(t->c_ospeed);
430 break;
431 default:
432 printf("%s: uslsa_param: unsupported data rate, "
433 "forcing default of 115200bps\n",
434 USBDEVNAME(sc->sc_dev));
435 data = USLSA_BPS(B115200);
436 };
437
438 if (uslsa_request_set(sc, USLSA_REQ_SET_BPS, data))
439 printf("%s: uslsa_param: setting data rate failed\n",
440 USBDEVNAME(sc->sc_dev));
441
442 data = 0;
443
444 if (ISSET(t->c_cflag, CSTOPB))
445 data |= USLSA_DPS_STOP_TWO;
446 else
447 data |= USLSA_DPS_STOP_ONE;
448
449 if (ISSET(t->c_cflag, PARENB)) {
450 if (ISSET(t->c_cflag, PARODD))
451 data |= USLSA_DPS_PARITY_ODD;
452 else
453 data |= USLSA_DPS_PARITY_EVEN;
454 } else
455 data |= USLSA_DPS_PARITY_NONE;
456
457 switch (ISSET(t->c_cflag, CSIZE)) {
458 case CS5:
459 data |= USLSA_DPS_DATA_FIVE;
460 break;
461 case CS6:
462 data |= USLSA_DPS_DATA_SIX;
463 break;
464 case CS7:
465 data |= USLSA_DPS_DATA_SEVEN;
466 break;
467 case CS8:
468 data |= USLSA_DPS_DATA_EIGHT;
469 break;
470 }
471
472 DPRINTF(("uslsa_param: setting DPS register to 0x%x\n", data));
473 if (uslsa_request_set(sc, USLSA_REQ_SET_DPS, data))
474 printf("%s: setting DPS register failed: invalid argument\n",
475 USBDEVNAME(sc->sc_dev));
476
477 uslsa_set_flow(sc, t->c_cflag, t->c_iflag);
478
479 return 0;
480 }
481
482
483 static int
484 uslsa_open(void *vsc, int portno)
485 {
486 struct uslsa_softc *sc;
487
488 sc = vsc;
489
490 DPRINTF(("uslsa_open: sc=%p\n", sc));
491
492 if (sc->sc_dying)
493 return (EIO);
494
495 if (uslsa_request_set(sc, USLSA_REQ_SET_STATE, USLSA_STATE_ENABLE))
496 return (EIO);
497
498 return 0;
499 }
500
501 void
502 uslsa_close(void *vsc, int portno)
503 {
504 struct uslsa_softc *sc;
505
506 sc = vsc;
507
508 if (sc->sc_dying)
509 return;
510
511 DPRINTF(("uslsa_close: sc=%p\n", sc));
512
513 if (uslsa_request_set(sc, USLSA_REQ_SET_STATE,
514 USLSA_STATE_DISABLE))
515 printf("%s: disable-on-close failed\n",
516 USBDEVNAME(sc->sc_dev));
517 }
518
519 /*
520 * uslsa_request_set(), wrapper for doing sets with usbd_do_request()
521 */
522 static int
523 uslsa_request_set(struct uslsa_softc * sc, uint8_t request, uint16_t value)
524 {
525 usb_device_request_t req;
526 usbd_status err;
527
528 req.bmRequestType = USLSA_REQUEST_SET;
529 req.bRequest = request;
530 USETW(req.wValue, value);
531 USETW(req.wIndex, 0);
532 USETW(req.wLength, 0);
533
534 err = usbd_do_request(sc->sc_udev, &req, 0);
535 if (err)
536 printf("%s: uslsa_request: %s\n",
537 USBDEVNAME(sc->sc_dev), usbd_errstr(err));
538 return err;
539 }
540
541 /*
542 * uslsa_set_flow() does some magic to set up hardware flow control
543 */
544 static void
545 uslsa_set_flow(struct uslsa_softc *sc, tcflag_t cflag, tcflag_t iflag)
546 {
547 uint8_t mysterydata[16];
548 usb_device_request_t req;
549 usbd_status err;
550
551 DPRINTF(("uslsa_set_flow: cflag = 0x%x, iflag = 0x%x\n",
552 cflag, iflag));
553
554 req.bmRequestType = USLSA_REQUEST_GET;
555 req.bRequest = USLSA_REQ_GET_MODEM;
556 USETW(req.wValue, 0);
557 USETW(req.wIndex, 0);
558 USETW(req.wLength, 16);
559
560 err = usbd_do_request(sc->sc_udev, &req, mysterydata);
561 if (err)
562 printf("%s: uslsa_set_flow: %s\n",
563 USBDEVNAME(sc->sc_dev), usbd_errstr(err));
564
565 if (ISSET(cflag, CRTSCTS)) {
566 mysterydata[0] &= ~0x7b;
567 mysterydata[0] |= 0x09;
568 mysterydata[4] = 0x80;
569 } else {
570 mysterydata[0] &= ~0x7b;
571 mysterydata[0] |= 0x01;
572 mysterydata[4] = 0x40;
573 }
574
575 req.bmRequestType = USLSA_REQUEST_SET;
576 req.bRequest = USLSA_REQ_SET_MODEM;
577 USETW(req.wValue, 0);
578 USETW(req.wIndex, 0);
579 USETW(req.wLength, 16);
580
581 err = usbd_do_request(sc->sc_udev, &req, mysterydata);
582 if (err)
583 printf("%s: uslsa_set_flow: %s\n",
584 USBDEVNAME(sc->sc_dev), usbd_errstr(err));
585 }
586