Annotation of sys/dev/usb/ucycom.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: ucycom.c,v 1.11 2007/06/14 10:11:15 mbalmer Exp $ */
2: /* $NetBSD: ucycom.c,v 1.3 2005/08/05 07:27:47 skrll Exp $ */
3:
4: /*
5: * Copyright (c) 2005 The NetBSD Foundation, Inc.
6: * All rights reserved.
7: *
8: * This code is derived from software contributed to The NetBSD Foundation
9: * by Nick Hudson
10: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
19: * 3. All advertising materials mentioning features or use of this software
20: * must display the following acknowledgement:
21: * This product includes software developed by the NetBSD
22: * Foundation, Inc. and its contributors.
23: * 4. Neither the name of The NetBSD Foundation nor the names of its
24: * contributors may be used to endorse or promote products derived
25: * from this software without specific prior written permission.
26: *
27: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37: * POSSIBILITY OF SUCH DAMAGE.
38: */
39: /*
40: * This code is based on the ucom driver.
41: */
42:
43: /*
44: * Device driver for Cypress CY7C637xx and CY7C640/1xx series USB to
45: * RS232 bridges.
46: */
47:
48: #include <sys/param.h>
49: #include <sys/systm.h>
50: #include <sys/conf.h>
51: #include <sys/kernel.h>
52: #include <sys/malloc.h>
53: #include <sys/device.h>
54: #include <sys/sysctl.h>
55: #include <sys/tty.h>
56: #include <sys/file.h>
57: #include <sys/vnode.h>
58:
59: #include <dev/usb/usb.h>
60: #include <dev/usb/usbhid.h>
61:
62: #include <dev/usb/usbdi.h>
63: #include <dev/usb/usbdi_util.h>
64: #include <dev/usb/usbdevs.h>
65: #include <dev/usb/uhidev.h>
66: #include <dev/usb/hid.h>
67:
68: #include <dev/usb/ucomvar.h>
69:
70: #ifdef UCYCOM_DEBUG
71: #define DPRINTF(x) if (ucycomdebug) printf x
72: #define DPRINTFN(n, x) if (ucycomdebug > (n)) printf x
73: int ucycomdebug = 200;
74: #else
75: #define DPRINTF(x)
76: #define DPRINTFN(n,x)
77: #endif
78:
79: /* Configuration Byte */
80: #define UCYCOM_RESET 0x80
81: #define UCYCOM_PARITY_TYPE_MASK 0x20
82: #define UCYCOM_PARITY_ODD 0x20
83: #define UCYCOM_PARITY_EVEN 0x00
84: #define UCYCOM_PARITY_MASK 0x10
85: #define UCYCOM_PARITY_ON 0x10
86: #define UCYCOM_PARITY_OFF 0x00
87: #define UCYCOM_STOP_MASK 0x08
88: #define UCYCOM_STOP_BITS_2 0x08
89: #define UCYCOM_STOP_BITS_1 0x00
90: #define UCYCOM_DATA_MASK 0x03
91: #define UCYCOM_DATA_BITS_8 0x03
92: #define UCYCOM_DATA_BITS_7 0x02
93: #define UCYCOM_DATA_BITS_6 0x01
94: #define UCYCOM_DATA_BITS_5 0x00
95:
96: /* Modem (Input) status byte */
97: #define UCYCOM_RI 0x80
98: #define UCYCOM_DCD 0x40
99: #define UCYCOM_DSR 0x20
100: #define UCYCOM_CTS 0x10
101: #define UCYCOM_ERROR 0x08
102: #define UCYCOM_LMASK 0x07
103:
104: /* Modem (Output) control byte */
105: #define UCYCOM_DTR 0x20
106: #define UCYCOM_RTS 0x10
107: #define UCYCOM_ORESET 0x08
108:
109: struct ucycom_softc {
110: struct uhidev sc_hdev;
111: usbd_device_handle sc_udev;
112:
113: /* uhidev parameters */
114: size_t sc_flen; /* feature report length */
115: size_t sc_ilen; /* input report length */
116: size_t sc_olen; /* output report length */
117:
118: uint8_t *sc_obuf;
119:
120: uint8_t *sc_ibuf;
121: uint32_t sc_icnt;
122:
123: /* settings */
124: uint32_t sc_baud;
125: uint8_t sc_cfg; /* Data format */
126: uint8_t sc_mcr; /* Modem control */
127: uint8_t sc_msr; /* Modem status */
128: uint8_t sc_newmsr; /* from HID intr */
129: int sc_swflags;
130:
131: struct device *sc_subdev;
132:
133: /* flags */
134: u_char sc_dying;
135: };
136:
137: /* Callback routines */
138: void ucycom_set(void *, int, int, int);
139: int ucycom_param(void *, int, struct termios *);
140: void ucycom_get_status(void *, int, u_char *, u_char *);
141: int ucycom_open(void *, int);
142: void ucycom_close(void *, int);
143: void ucycom_write(void *, int, u_char *, u_char *, u_int32_t *);
144: void ucycom_read(void *, int, u_char **, u_int32_t *);
145:
146: struct ucom_methods ucycom_methods = {
147: NULL, /* ucycom_get_status, */
148: ucycom_set,
149: ucycom_param,
150: NULL,
151: ucycom_open,
152: ucycom_close,
153: ucycom_read,
154: ucycom_write,
155: };
156:
157: void ucycom_intr(struct uhidev *, void *, u_int);
158:
159: void ucycom_get_cfg(struct ucycom_softc *);
160:
161: const struct usb_devno ucycom_devs[] = {
162: { USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_USBRS232 },
163: { USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EMUSB },
164: { USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EMLT20 },
165: };
166: #define ucycom_lookup(v, p) usb_lookup(ucycom_devs, v, p)
167:
168: int ucycom_match(struct device *, void *, void *);
169: void ucycom_attach(struct device *, struct device *, void *);
170: int ucycom_detach(struct device *, int);
171: int ucycom_activate(struct device *, enum devact);
172:
173: struct cfdriver ucycom_cd = {
174: NULL, "ucycom", DV_DULL
175: };
176:
177: const struct cfattach ucycom_ca = {
178: sizeof(struct ucycom_softc),
179: ucycom_match,
180: ucycom_attach,
181: ucycom_detach,
182: ucycom_activate,
183: };
184:
185: int
186: ucycom_match(struct device *parent, void *match, void *aux)
187: {
188: struct uhidev_attach_arg *uha = aux;
189:
190: DPRINTF(("ucycom match\n"));
191: return (ucycom_lookup(uha->uaa->vendor, uha->uaa->product) != NULL ?
192: UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
193: }
194:
195: void
196: ucycom_attach(struct device *parent, struct device *self, void *aux)
197: {
198: struct ucycom_softc *sc = (struct ucycom_softc *)self;
199: struct usb_attach_arg *uaa = aux;
200: struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
201: usbd_device_handle dev = uha->parent->sc_udev;
202: struct ucom_attach_args uca;
203: int size, repid, err;
204: void *desc;
205:
206: sc->sc_hdev.sc_intr = ucycom_intr;
207: sc->sc_hdev.sc_parent = uha->parent;
208: sc->sc_hdev.sc_report_id = uha->reportid;
209:
210: uhidev_get_report_desc(uha->parent, &desc, &size);
211: repid = uha->reportid;
212: sc->sc_ilen = hid_report_size(desc, size, hid_input, repid);
213: sc->sc_olen = hid_report_size(desc, size, hid_output, repid);
214: sc->sc_flen = hid_report_size(desc, size, hid_feature, repid);
215:
216: DPRINTF(("ucycom_open: olen %d ilen %d flen %d\n", sc->sc_ilen,
217: sc->sc_olen, sc->sc_flen));
218:
219: printf("\n");
220:
221: sc->sc_udev = dev;
222:
223: sc->sc_msr = sc->sc_mcr = 0;
224:
225: err = uhidev_open(&sc->sc_hdev);
226: if (err) {
227: DPRINTF(("ucycom_open: uhidev_open %d\n", err));
228: return;
229: }
230:
231: DPRINTF(("ucycom attach: sc %p opipe %p ipipe %p report_id %d\n",
232: sc, sc->sc_hdev.sc_parent->sc_opipe, sc->sc_hdev.sc_parent->sc_ipipe,
233: uha->reportid));
234:
235: /* bulkin, bulkout set above */
236: bzero(&uca, sizeof uca);
237: uca.bulkin = uca.bulkout = -1;
238: uca.uhidev = sc->sc_hdev.sc_parent;
239: uca.ibufsize = sc->sc_ilen - 1;
240: uca.obufsize = sc->sc_olen - 1;
241: uca.ibufsizepad = 1;
242: uca.opkthdrlen = 0;
243: uca.device = uaa->device;
244: uca.iface = uaa->iface;
245: uca.methods = &ucycom_methods;
246: uca.arg = sc;
247: uca.info = NULL;
248:
249: usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
250: &sc->sc_hdev.sc_dev);
251:
252: sc->sc_subdev = config_found_sm(self, &uca, ucomprint, ucomsubmatch);
253: DPRINTF(("ucycom_attach: complete %p\n", sc->sc_subdev));
254: }
255:
256: void
257: ucycom_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
258: {
259: struct ucycom_softc *sc = addr;
260:
261: DPRINTF(("ucycom_get_status:\n"));
262:
263: #if 0
264: if (lsr != NULL)
265: *lsr = sc->sc_lsr;
266: #endif
267: if (msr != NULL)
268: *msr = sc->sc_msr;
269: }
270:
271: int
272: ucycom_open(void *addr, int portno)
273: {
274: struct ucycom_softc *sc = addr;
275: struct termios t;
276: int err;
277:
278: DPRINTF(("ucycom_open: complete\n"));
279:
280: if (sc->sc_dying)
281: return (EIO);
282:
283: /* Allocate an output report buffer */
284: sc->sc_obuf = malloc(sc->sc_olen, M_USBDEV, M_WAITOK);
285:
286: /* Allocate an input report buffer */
287: sc->sc_ibuf = malloc(sc->sc_ilen, M_USBDEV, M_WAITOK);
288:
289: DPRINTF(("ucycom_open: sc->sc_ibuf=%p sc->sc_obuf=%p \n",
290: sc->sc_ibuf, sc->sc_obuf));
291:
292: t.c_ospeed = 9600;
293: t.c_cflag = CSTOPB | CS8;
294: (void)ucycom_param(sc, portno, &t);
295:
296: sc->sc_mcr = UCYCOM_DTR | UCYCOM_RTS;
297: memset(sc->sc_obuf, 0, sc->sc_olen);
298: sc->sc_obuf[0] = sc->sc_mcr;
299: err = uhidev_write(sc->sc_hdev.sc_parent, sc->sc_obuf, sc->sc_olen);
300: if (err) {
301: DPRINTF(("ucycom_open: set RTS err=%d\n", err));
302: return (EIO);
303: }
304:
305: return (0);
306: }
307:
308: void
309: ucycom_close(void *addr, int portno)
310: {
311: struct ucycom_softc *sc = addr;
312: int s;
313:
314: if (sc->sc_dying)
315: return;
316:
317: s = splusb();
318: if (sc->sc_obuf != NULL) {
319: free(sc->sc_obuf, M_USBDEV);
320: sc->sc_obuf = NULL;
321: }
322: if (sc->sc_ibuf != NULL) {
323: free(sc->sc_ibuf, M_USBDEV);
324: sc->sc_ibuf = NULL;
325: }
326: splx(s);
327: }
328:
329: void
330: ucycom_read(void *addr, int portno, u_char **ptr, u_int32_t *count)
331: {
332: struct ucycom_softc *sc = addr;
333:
334: if (sc->sc_newmsr ^ sc->sc_msr) {
335: DPRINTF(("ucycom_read: msr %d new %d\n",
336: sc->sc_msr, sc->sc_newmsr));
337: sc->sc_msr = sc->sc_newmsr;
338: ucom_status_change((struct ucom_softc *)sc->sc_subdev);
339: }
340:
341: DPRINTF(("ucycom_read: buf %p chars %d\n", sc->sc_ibuf, sc->sc_icnt));
342: *ptr = sc->sc_ibuf;
343: *count = sc->sc_icnt;
344: }
345:
346: void
347: ucycom_write(void *addr, int portno, u_char *to, u_char *data, u_int32_t *cnt)
348: {
349: struct ucycom_softc *sc = addr;
350: u_int32_t len;
351: #ifdef UCYCOM_DEBUG
352: u_int32_t want = *cnt;
353: #endif
354:
355: /*
356: * The 8 byte output report uses byte 0 for control and byte
357: * count.
358: *
359: * The 32 byte output report uses byte 0 for control. Byte 1
360: * is used for byte count.
361: */
362: len = sc->sc_olen;
363: memset(to, 0, len);
364: switch (sc->sc_olen) {
365: case 8:
366: to[0] = *cnt | sc->sc_mcr;
367: memcpy(&to[1], data, *cnt);
368: DPRINTF(("ucycomstart(8): to[0] = %d | %d = %d\n",
369: *cnt, sc->sc_mcr, to[0]));
370: break;
371:
372: case 32:
373: to[0] = sc->sc_mcr;
374: to[1] = *cnt;
375: memcpy(&to[2], data, *cnt);
376: DPRINTF(("ucycomstart(32): to[0] = %d\nto[1] = %d\n",
377: to[0], to[1]));
378: break;
379: }
380:
381: #ifdef UCYCOM_DEBUG
382: if (ucycomdebug > 5) {
383: int i;
384:
385: if (len != 0) {
386: DPRINTF(("ucycomstart: to[0..%d) =", len-1));
387: for (i = 0; i < len; i++)
388: DPRINTF((" %02x", to[i]));
389: DPRINTF(("\n"));
390: }
391: }
392: #endif
393: *cnt = len;
394:
395: #if 0
396: ucycom_get_cfg(sc);
397: #endif
398: DPRINTFN(4,("ucycomstart: req %d chars did %d chars\n", want, len));
399: }
400:
401: int
402: ucycom_param(void *addr, int portno, struct termios *t)
403: {
404: struct ucycom_softc *sc = addr;
405: uint8_t report[5];
406: uint32_t baud = 0;
407: uint8_t cfg;
408: int err;
409:
410: if (sc->sc_dying)
411: return (EIO);
412:
413: switch (t->c_ospeed) {
414: case 600:
415: case 1200:
416: case 2400:
417: case 4800:
418: case 9600:
419: case 19200:
420: case 38400:
421: case 57600:
422: #if 0
423: /*
424: * Stock chips only support standard baud rates in the 600 - 57600
425: * range, but higher rates can be achieved using custom firmware.
426: */
427: case 115200:
428: case 153600:
429: case 192000:
430: #endif
431: baud = t->c_ospeed;
432: break;
433: default:
434: return (EINVAL);
435: }
436:
437: if (t->c_cflag & CIGNORE) {
438: cfg = sc->sc_cfg;
439: } else {
440: cfg = 0;
441: switch (t->c_cflag & CSIZE) {
442: case CS8:
443: cfg |= UCYCOM_DATA_BITS_8;
444: break;
445: case CS7:
446: cfg |= UCYCOM_DATA_BITS_7;
447: break;
448: case CS6:
449: cfg |= UCYCOM_DATA_BITS_6;
450: break;
451: case CS5:
452: cfg |= UCYCOM_DATA_BITS_5;
453: break;
454: default:
455: return (EINVAL);
456: }
457: cfg |= ISSET(t->c_cflag, CSTOPB) ?
458: UCYCOM_STOP_BITS_2 : UCYCOM_STOP_BITS_1;
459: cfg |= ISSET(t->c_cflag, PARENB) ?
460: UCYCOM_PARITY_ON : UCYCOM_PARITY_OFF;
461: cfg |= ISSET(t->c_cflag, PARODD) ?
462: UCYCOM_PARITY_ODD : UCYCOM_PARITY_EVEN;
463: }
464:
465: DPRINTF(("ucycom_param: setting %d baud, %d-%c-%d (%d)\n", baud,
466: 5 + (cfg & UCYCOM_DATA_MASK),
467: (cfg & UCYCOM_PARITY_MASK) ?
468: ((cfg & UCYCOM_PARITY_TYPE_MASK) ? 'O' : 'E') : 'N',
469: (cfg & UCYCOM_STOP_MASK) ? 2 : 1, cfg));
470:
471: report[0] = baud & 0xff;
472: report[1] = (baud >> 8) & 0xff;
473: report[2] = (baud >> 16) & 0xff;
474: report[3] = (baud >> 24) & 0xff;
475: report[4] = cfg;
476: err = uhidev_set_report(&sc->sc_hdev, UHID_FEATURE_REPORT,
477: report, sc->sc_flen);
478: if (err != 0) {
479: DPRINTF(("ucycom_param: uhidev_set_report %d %s\n",
480: err, usbd_errstr(err)));
481: return EIO;
482: }
483: sc->sc_baud = baud;
484: return (err);
485: }
486:
487: void
488: ucycom_intr(struct uhidev *addr, void *ibuf, u_int len)
489: {
490: extern void ucomreadcb(usbd_xfer_handle, usbd_private_handle, usbd_status);
491: struct ucycom_softc *sc = (struct ucycom_softc *)addr;
492: uint8_t *cp = ibuf;
493: int n, st, s;
494:
495: /* not accepting data anymore.. */
496: if (sc->sc_ibuf == NULL)
497: return;
498:
499: /* We understand 8 byte and 32 byte input records */
500: switch (len) {
501: case 8:
502: n = cp[0] & UCYCOM_LMASK;
503: st = cp[0] & ~UCYCOM_LMASK;
504: cp++;
505: break;
506:
507: case 32:
508: st = cp[0];
509: n = cp[1];
510: cp += 2;
511: break;
512:
513: default:
514: DPRINTFN(3,("ucycom_intr: Unknown input report length\n"));
515: return;
516: }
517:
518: #ifdef UCYCOM_DEBUG
519: if (ucycomdebug > 5) {
520: u_int32_t i;
521:
522: if (n != 0) {
523: DPRINTF(("ucycom_intr: ibuf[0..%d) =", n));
524: for (i = 0; i < n; i++)
525: DPRINTF((" %02x", cp[i]));
526: DPRINTF(("\n"));
527: }
528: }
529: #endif
530:
531: if (n > 0 || st != sc->sc_msr) {
532: s = spltty();
533: sc->sc_newmsr = st;
534: bcopy(cp, sc->sc_ibuf, n);
535: sc->sc_icnt = n;
536: ucomreadcb(addr->sc_parent->sc_ixfer, sc->sc_subdev,
537: USBD_NORMAL_COMPLETION);
538: splx(s);
539: }
540: }
541:
542: void
543: ucycom_set(void *addr, int portno, int reg, int onoff)
544: {
545: struct ucycom_softc *sc = addr;
546: int err;
547:
548: switch (reg) {
549: case UCOM_SET_DTR:
550: if (onoff)
551: SET(sc->sc_mcr, UCYCOM_DTR);
552: else
553: CLR(sc->sc_mcr, UCYCOM_DTR);
554: break;
555: case UCOM_SET_RTS:
556: if (onoff)
557: SET(sc->sc_mcr, UCYCOM_RTS);
558: else
559: CLR(sc->sc_mcr, UCYCOM_RTS);
560: break;
561: case UCOM_SET_BREAK:
562: break;
563: }
564:
565: memset(sc->sc_obuf, 0, sc->sc_olen);
566: sc->sc_obuf[0] = sc->sc_mcr;
567:
568: err = uhidev_write(sc->sc_hdev.sc_parent, sc->sc_obuf, sc->sc_olen);
569: if (err)
570: DPRINTF(("ucycom_set_status: err=%d\n", err));
571: }
572:
573: void
574: ucycom_get_cfg(struct ucycom_softc *sc)
575: {
576: int err, cfg, baud;
577: uint8_t report[5];
578:
579: err = uhidev_get_report(&sc->sc_hdev, UHID_FEATURE_REPORT,
580: report, sc->sc_flen);
581: cfg = report[4];
582: baud = (report[3] << 24) + (report[2] << 16) + (report[1] << 8) + report[0];
583: DPRINTF(("ucycom_configure: device reports %d baud, %d-%c-%d (%d)\n", baud,
584: 5 + (cfg & UCYCOM_DATA_MASK),
585: (cfg & UCYCOM_PARITY_MASK) ?
586: ((cfg & UCYCOM_PARITY_TYPE_MASK) ? 'O' : 'E') : 'N',
587: (cfg & UCYCOM_STOP_MASK) ? 2 : 1, cfg));
588: }
589:
590: int
591: ucycom_detach(struct device *self, int flags)
592: {
593: struct ucycom_softc *sc = (struct ucycom_softc *)self;
594:
595: DPRINTF(("ucycom_detach: sc=%p flags=%d\n", sc, flags));
596: sc->sc_dying = 1;
597: if (sc->sc_subdev != NULL) {
598: config_detach(sc->sc_subdev, flags);
599: sc->sc_subdev = NULL;
600: }
601: return (0);
602: }
603:
604: int
605: ucycom_activate(struct device *self, enum devact act)
606: {
607: struct ucycom_softc *sc = (struct ucycom_softc *)self;
608: int rv = 0;
609:
610: DPRINTFN(5,("ucycom_activate: %d\n", act));
611:
612: switch (act) {
613: case DVACT_ACTIVATE:
614: break;
615:
616: case DVACT_DEACTIVATE:
617: if (sc->sc_subdev != NULL)
618: rv = config_deactivate(sc->sc_subdev);
619: sc->sc_dying = 1;
620: break;
621: }
622: return (rv);
623: }
CVSweb