Annotation of sys/dev/usb/uslcom.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: uslcom.c,v 1.13 2007/06/14 10:11:16 mbalmer Exp $ */
2:
3: /*
4: * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
18:
19: #include <sys/param.h>
20: #include <sys/systm.h>
21: #include <sys/kernel.h>
22: #include <sys/conf.h>
23: #include <sys/tty.h>
24: #include <sys/device.h>
25:
26: #include <dev/usb/usb.h>
27: #include <dev/usb/usbdi.h>
28: #include <dev/usb/usbdi_util.h>
29: #include <dev/usb/usbdevs.h>
30:
31: #include <dev/usb/usbdevs.h>
32: #include <dev/usb/ucomvar.h>
33:
34: #ifdef USLCOM_DEBUG
35: #define DPRINTFN(n, x) do { if (uslcomdebug > (n)) printf x; } while (0)
36: int uslcomdebug = 0;
37: #else
38: #define DPRINTFN(n, x)
39: #endif
40: #define DPRINTF(x) DPRINTFN(0, x)
41:
42: #define USLCOMBUFSZ 256
43: #define USLCOM_CONFIG_NO 0
44: #define USLCOM_IFACE_NO 0
45:
46: #define USLCOM_SET_DATA_BITS(x) (x << 8)
47:
48: #define USLCOM_WRITE 0x41
49: #define USLCOM_READ 0xc1
50:
51: #define USLCOM_UART 0x00
52: #define USLCOM_BAUD_RATE 0x01
53: #define USLCOM_DATA 0x03
54: #define USLCOM_BREAK 0x05
55: #define USLCOM_CTRL 0x07
56:
57: #define USLCOM_UART_DISABLE 0x00
58: #define USLCOM_UART_ENABLE 0x01
59:
60: #define USLCOM_CTRL_DTR_ON 0x0001
61: #define USLCOM_CTRL_DTR_SET 0x0100
62: #define USLCOM_CTRL_RTS_ON 0x0002
63: #define USLCOM_CTRL_RTS_SET 0x0200
64: #define USLCOM_CTRL_CTS 0x0010
65: #define USLCOM_CTRL_DSR 0x0020
66: #define USLCOM_CTRL_DCD 0x0080
67:
68:
69: #define USLCOM_BAUD_REF 0x384000
70:
71: #define USLCOM_STOP_BITS_1 0x00
72: #define USLCOM_STOP_BITS_2 0x02
73:
74: #define USLCOM_PARITY_NONE 0x00
75: #define USLCOM_PARITY_ODD 0x10
76: #define USLCOM_PARITY_EVEN 0x20
77:
78: #define USLCOM_BREAK_OFF 0x00
79: #define USLCOM_BREAK_ON 0x01
80:
81:
82: struct uslcom_softc {
83: struct device sc_dev;
84: usbd_device_handle sc_udev;
85: usbd_interface_handle sc_iface;
86: struct device *sc_subdev;
87:
88: u_char sc_msr;
89: u_char sc_lsr;
90:
91: u_char sc_dying;
92: };
93:
94: void uslcom_get_status(void *, int portno, u_char *lsr, u_char *msr);
95: void uslcom_set(void *, int, int, int);
96: int uslcom_param(void *, int, struct termios *);
97: int uslcom_open(void *sc, int portno);
98: void uslcom_close(void *, int);
99: void uslcom_break(void *sc, int portno, int onoff);
100:
101: struct ucom_methods uslcom_methods = {
102: uslcom_get_status,
103: uslcom_set,
104: uslcom_param,
105: NULL,
106: uslcom_open,
107: uslcom_close,
108: NULL,
109: NULL,
110: };
111:
112: static const struct usb_devno uslcom_devs[] = {
113: { USB_VENDOR_BALTECH, USB_PRODUCT_BALTECH_CARDREADER },
114: { USB_VENDOR_DYNASTREAM, USB_PRODUCT_DYNASTREAM_ANTDEVBOARD },
115: { USB_VENDOR_JABLOTRON, USB_PRODUCT_JABLOTRON_PC60B },
116: { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_ARGUSISP },
117: { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CRUMB128 },
118: { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_DEGREECONT },
119: { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_DESKTOPMOBILE },
120: { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_IPLINK1220 },
121: { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_HARP },
122: { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_JTAG },
123: { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_LIN },
124: { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_POLOLU },
125: { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP210X_1 },
126: { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP210X_2 },
127: { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_SUNNTO },
128: { USB_VENDOR_SILABS2, USB_PRODUCT_SILABS2_DCU11CLONE },
129: { USB_VENDOR_USI, USB_PRODUCT_USI_MC60 }
130: };
131:
132: int uslcom_match(struct device *, void *, void *);
133: void uslcom_attach(struct device *, struct device *, void *);
134: int uslcom_detach(struct device *, int);
135: int uslcom_activate(struct device *, enum devact);
136:
137: struct cfdriver uslcom_cd = {
138: NULL, "uslcom", DV_DULL
139: };
140:
141: const struct cfattach uslcom_ca = {
142: sizeof(struct uslcom_softc),
143: uslcom_match,
144: uslcom_attach,
145: uslcom_detach,
146: uslcom_activate,
147: };
148:
149: int
150: uslcom_match(struct device *parent, void *match, void *aux)
151: {
152: struct usb_attach_arg *uaa = aux;
153:
154: if (uaa->iface != NULL)
155: return UMATCH_NONE;
156:
157: return (usb_lookup(uslcom_devs, uaa->vendor, uaa->product) != NULL) ?
158: UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
159: }
160:
161: void
162: uslcom_attach(struct device *parent, struct device *self, void *aux)
163: {
164: struct uslcom_softc *sc = (struct uslcom_softc *)self;
165: struct usb_attach_arg *uaa = aux;
166: struct ucom_attach_args uca;
167: usb_interface_descriptor_t *id;
168: usb_endpoint_descriptor_t *ed;
169: usbd_status error;
170: char *devinfop;
171: int i;
172:
173: bzero(&uca, sizeof(uca));
174: sc->sc_udev = uaa->device;
175: devinfop = usbd_devinfo_alloc(uaa->device, 0);
176: printf("\n%s: %s\n", sc->sc_dev.dv_xname, devinfop);
177: usbd_devinfo_free(devinfop);
178:
179: if (usbd_set_config_index(sc->sc_udev, USLCOM_CONFIG_NO, 1) != 0) {
180: printf("%s: could not set configuration no\n",
181: sc->sc_dev.dv_xname);
182: sc->sc_dying = 1;
183: return;
184: }
185:
186: /* get the first interface handle */
187: error = usbd_device2interface_handle(sc->sc_udev, USLCOM_IFACE_NO,
188: &sc->sc_iface);
189: if (error != 0) {
190: printf("%s: could not get interface handle\n",
191: sc->sc_dev.dv_xname);
192: sc->sc_dying = 1;
193: return;
194: }
195:
196: id = usbd_get_interface_descriptor(sc->sc_iface);
197:
198: uca.bulkin = uca.bulkout = -1;
199: for (i = 0; i < id->bNumEndpoints; i++) {
200: ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
201: if (ed == NULL) {
202: printf("%s: no endpoint descriptor found for %d\n",
203: sc->sc_dev.dv_xname, i);
204: sc->sc_dying = 1;
205: return;
206: }
207:
208: if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
209: UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
210: uca.bulkin = ed->bEndpointAddress;
211: else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
212: UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
213: uca.bulkout = ed->bEndpointAddress;
214: }
215:
216: if (uca.bulkin == -1 || uca.bulkout == -1) {
217: printf("%s: missing endpoint\n", sc->sc_dev.dv_xname);
218: sc->sc_dying = 1;
219: return;
220: }
221:
222: uca.ibufsize = USLCOMBUFSZ;
223: uca.obufsize = USLCOMBUFSZ;
224: uca.ibufsizepad = USLCOMBUFSZ;
225: uca.opkthdrlen = 0;
226: uca.device = sc->sc_udev;
227: uca.iface = sc->sc_iface;
228: uca.methods = &uslcom_methods;
229: uca.arg = sc;
230: uca.info = NULL;
231:
232: usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
233: &sc->sc_dev);
234:
235: sc->sc_subdev = config_found_sm(self, &uca, ucomprint, ucomsubmatch);
236: }
237:
238: int
239: uslcom_detach(struct device *self, int flags)
240: {
241: struct uslcom_softc *sc = (struct uslcom_softc *)self;
242: int rv = 0;
243:
244: sc->sc_dying = 1;
245: if (sc->sc_subdev != NULL) {
246: rv = config_detach(sc->sc_subdev, flags);
247: sc->sc_subdev = NULL;
248: }
249:
250: usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
251: &sc->sc_dev);
252:
253: return (rv);
254: }
255:
256: int
257: uslcom_activate(struct device *self, enum devact act)
258: {
259: struct uslcom_softc *sc = (struct uslcom_softc *)self;
260: int rv = 0;
261:
262: switch (act) {
263: case DVACT_ACTIVATE:
264: break;
265:
266: case DVACT_DEACTIVATE:
267: if (sc->sc_subdev != NULL)
268: rv = config_deactivate(sc->sc_subdev);
269: sc->sc_dying = 1;
270: break;
271: }
272: return (rv);
273: }
274:
275: int
276: uslcom_open(void *vsc, int portno)
277: {
278: struct uslcom_softc *sc = vsc;
279: usb_device_request_t req;
280: usbd_status err;
281:
282: if (sc->sc_dying)
283: return (EIO);
284:
285: req.bmRequestType = USLCOM_WRITE;
286: req.bRequest = USLCOM_UART;
287: USETW(req.wValue, USLCOM_UART_ENABLE);
288: USETW(req.wIndex, portno);
289: USETW(req.wLength, 0);
290: err = usbd_do_request(sc->sc_udev, &req, NULL);
291: if (err)
292: return (EIO);
293:
294: return (0);
295: }
296:
297: void
298: uslcom_close(void *vsc, int portno)
299: {
300: struct uslcom_softc *sc = vsc;
301: usb_device_request_t req;
302:
303: if (sc->sc_dying)
304: return;
305:
306: req.bmRequestType = USLCOM_WRITE;
307: req.bRequest = USLCOM_UART;
308: USETW(req.wValue, USLCOM_UART_DISABLE);
309: USETW(req.wIndex, portno);
310: USETW(req.wLength, 0);
311: usbd_do_request(sc->sc_udev, &req, NULL);
312: }
313:
314: void
315: uslcom_set(void *vsc, int portno, int reg, int onoff)
316: {
317: struct uslcom_softc *sc = vsc;
318: usb_device_request_t req;
319: int ctl;
320:
321: switch (reg) {
322: case UCOM_SET_DTR:
323: ctl = onoff ? USLCOM_CTRL_DTR_ON : 0;
324: ctl |= USLCOM_CTRL_DTR_SET;
325: break;
326: case UCOM_SET_RTS:
327: ctl = onoff ? USLCOM_CTRL_RTS_ON : 0;
328: ctl |= USLCOM_CTRL_RTS_SET;
329: break;
330: case UCOM_SET_BREAK:
331: uslcom_break(sc, portno, onoff);
332: return;
333: default:
334: return;
335: }
336: req.bmRequestType = USLCOM_WRITE;
337: req.bRequest = USLCOM_CTRL;
338: USETW(req.wValue, ctl);
339: USETW(req.wIndex, portno);
340: USETW(req.wLength, 0);
341: usbd_do_request(sc->sc_udev, &req, NULL);
342: }
343:
344: int
345: uslcom_param(void *vsc, int portno, struct termios *t)
346: {
347: struct uslcom_softc *sc = (struct uslcom_softc *)vsc;
348: usbd_status err;
349: usb_device_request_t req;
350: int data;
351:
352: switch (t->c_ospeed) {
353: case 600:
354: case 1200:
355: case 1800:
356: case 2400:
357: case 4800:
358: case 9600:
359: case 19200:
360: case 38400:
361: case 57600:
362: case 115200:
363: case 460800:
364: case 921600:
365: req.bmRequestType = USLCOM_WRITE;
366: req.bRequest = USLCOM_BAUD_RATE;
367: USETW(req.wValue, USLCOM_BAUD_REF / t->c_ospeed);
368: USETW(req.wIndex, portno);
369: USETW(req.wLength, 0);
370: err = usbd_do_request(sc->sc_udev, &req, NULL);
371: if (err)
372: return (EIO);
373: break;
374: default:
375: return (EINVAL);
376: }
377:
378: if (ISSET(t->c_cflag, CSTOPB))
379: data = USLCOM_STOP_BITS_2;
380: else
381: data = USLCOM_STOP_BITS_1;
382: if (ISSET(t->c_cflag, PARENB)) {
383: if (ISSET(t->c_cflag, PARODD))
384: data |= USLCOM_PARITY_ODD;
385: else
386: data |= USLCOM_PARITY_EVEN;
387: } else
388: data |= USLCOM_PARITY_NONE;
389: switch (ISSET(t->c_cflag, CSIZE)) {
390: case CS5:
391: data |= USLCOM_SET_DATA_BITS(5);
392: break;
393: case CS6:
394: data |= USLCOM_SET_DATA_BITS(6);
395: break;
396: case CS7:
397: data |= USLCOM_SET_DATA_BITS(7);
398: break;
399: case CS8:
400: data |= USLCOM_SET_DATA_BITS(8);
401: break;
402: }
403:
404: req.bmRequestType = USLCOM_WRITE;
405: req.bRequest = USLCOM_DATA;
406: USETW(req.wValue, data);
407: USETW(req.wIndex, portno);
408: USETW(req.wLength, 0);
409: err = usbd_do_request(sc->sc_udev, &req, NULL);
410: if (err)
411: return (EIO);
412:
413: #if 0
414: /* XXX flow control */
415: if (ISSET(t->c_cflag, CRTSCTS))
416: /* rts/cts flow ctl */
417: } else if (ISSET(t->c_iflag, IXON|IXOFF)) {
418: /* xon/xoff flow ctl */
419: } else {
420: /* disable flow ctl */
421: }
422: #endif
423:
424: return (0);
425: }
426:
427: void
428: uslcom_get_status(void *vsc, int portno, u_char *lsr, u_char *msr)
429: {
430: struct uslcom_softc *sc = vsc;
431:
432: if (msr != NULL)
433: *msr = sc->sc_msr;
434: if (lsr != NULL)
435: *lsr = sc->sc_lsr;
436: }
437:
438: void
439: uslcom_break(void *vsc, int portno, int onoff)
440: {
441: struct uslcom_softc *sc = vsc;
442: usb_device_request_t req;
443: int brk = onoff ? USLCOM_BREAK_ON : USLCOM_BREAK_OFF;
444:
445: req.bmRequestType = USLCOM_WRITE;
446: req.bRequest = USLCOM_BREAK;
447: USETW(req.wValue, brk);
448: USETW(req.wIndex, portno);
449: USETW(req.wLength, 0);
450: usbd_do_request(sc->sc_udev, &req, NULL);
451: }
CVSweb