ucycom.c revision 1.21.2.1 1 /* $NetBSD: ucycom.c,v 1.21.2.1 2008/05/18 12:34:50 yamt Exp $ */
2
3 /*
4 * Copyright (c) 2005 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Nick Hudson
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31 /*
32 * This code is based on the ucom driver.
33 */
34
35 /*
36 * Device driver for Cypress CY7C637xx and CY7C640/1xx series USB to
37 * RS232 bridges.
38 */
39
40 #include <sys/cdefs.h>
41 __KERNEL_RCSID(0, "$NetBSD: ucycom.c,v 1.21.2.1 2008/05/18 12:34:50 yamt Exp $");
42
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/conf.h>
46 #include <sys/kernel.h>
47 #include <sys/malloc.h>
48 #include <sys/device.h>
49 #include <sys/sysctl.h>
50 #include <sys/tty.h>
51 #include <sys/file.h>
52 #include <sys/vnode.h>
53 #include <sys/kauth.h>
54
55 #include <dev/usb/usb.h>
56 #include <dev/usb/usbhid.h>
57
58 #include <dev/usb/usbdi.h>
59 #include <dev/usb/usbdi_util.h>
60 #include <dev/usb/usbdevs.h>
61 #include <dev/usb/uhidev.h>
62 #include <dev/usb/hid.h>
63
64 #include "ioconf.h"
65
66 #ifdef UCYCOM_DEBUG
67 #define DPRINTF(x) if (ucycomdebug) logprintf x
68 #define DPRINTFN(n, x) if (ucycomdebug > (n)) logprintf x
69 int ucycomdebug = 0;
70 #else
71 #define DPRINTF(x)
72 #define DPRINTFN(n,x)
73 #endif
74
75
76 #define UCYCOMUNIT_MASK 0x3ffff
77 #define UCYCOMDIALOUT_MASK 0x80000
78 #define UCYCOMCALLUNIT_MASK 0x40000
79
80 #define UCYCOMUNIT(x) (minor(x) & UCYCOMUNIT_MASK)
81 #define UCYCOMDIALOUT(x) (minor(x) & UCYCOMDIALOUT_MASK)
82 #define UCYCOMCALLUNIT(x) (minor(x) & UCYCOMCALLUNIT_MASK)
83
84 /* Configuration Byte */
85 #define UCYCOM_RESET 0x80
86 #define UCYCOM_PARITY_TYPE_MASK 0x20
87 #define UCYCOM_PARITY_ODD 0x20
88 #define UCYCOM_PARITY_EVEN 0x00
89 #define UCYCOM_PARITY_MASK 0x10
90 #define UCYCOM_PARITY_ON 0x10
91 #define UCYCOM_PARITY_OFF 0x00
92 #define UCYCOM_STOP_MASK 0x08
93 #define UCYCOM_STOP_BITS_2 0x08
94 #define UCYCOM_STOP_BITS_1 0x00
95 #define UCYCOM_DATA_MASK 0x03
96 #define UCYCOM_DATA_BITS_8 0x03
97 #define UCYCOM_DATA_BITS_7 0x02
98 #define UCYCOM_DATA_BITS_6 0x01
99 #define UCYCOM_DATA_BITS_5 0x00
100
101 /* Modem (Input) status byte */
102 #define UCYCOM_RI 0x80
103 #define UCYCOM_DCD 0x40
104 #define UCYCOM_DSR 0x20
105 #define UCYCOM_CTS 0x10
106 #define UCYCOM_ERROR 0x08
107 #define UCYCOM_LMASK 0x07
108
109 /* Modem (Output) control byte */
110 #define UCYCOM_DTR 0x20
111 #define UCYCOM_RTS 0x10
112 #define UCYCOM_ORESET 0x08
113
114 struct ucycom_softc {
115 struct uhidev sc_hdev;
116
117 struct tty *sc_tty;
118
119 /* uhidev parameters */
120 size_t sc_flen; /* feature report length */
121 size_t sc_ilen; /* input report length */
122 size_t sc_olen; /* output report length */
123
124 uint8_t *sc_obuf;
125
126 /* settings */
127 uint32_t sc_baud;
128 uint8_t sc_cfg; /* Data format */
129 uint8_t sc_mcr; /* Modem control */
130 uint8_t sc_msr; /* Modem status */
131 int sc_swflags;
132
133 /* flags */
134 char sc_dying;
135 };
136
137 dev_type_open(ucycomopen);
138 dev_type_close(ucycomclose);
139 dev_type_read(ucycomread);
140 dev_type_write(ucycomwrite);
141 dev_type_ioctl(ucycomioctl);
142 dev_type_stop(ucycomstop);
143 dev_type_tty(ucycomtty);
144 dev_type_poll(ucycompoll);
145
146 const struct cdevsw ucycom_cdevsw = {
147 ucycomopen, ucycomclose, ucycomread, ucycomwrite, ucycomioctl,
148 ucycomstop, ucycomtty, ucycompoll, nommap, ttykqfilter, D_TTY
149 };
150
151 Static int ucycomparam(struct tty *, struct termios *);
152 Static void ucycomstart(struct tty *);
153 Static void ucycom_intr(struct uhidev *, void *, u_int);
154 Static int ucycom_configure(struct ucycom_softc *, uint32_t, uint8_t);
155 Static void tiocm_to_ucycom(struct ucycom_softc *, u_long, int);
156 Static int ucycom_to_tiocm(struct ucycom_softc *);
157 Static void ucycom_set_status(struct ucycom_softc *);
158 Static void ucycom_dtr(struct ucycom_softc *, int);
159 #if 0
160 Static void ucycom_rts(struct ucycom_softc *, int);
161 #endif
162 Static void ucycom_cleanup(struct ucycom_softc *sc);
163
164 #ifdef UCYCOM_DEBUG
165 Static void ucycom_get_cfg(struct ucycom_softc *);
166 #endif
167
168 Static const struct usb_devno ucycom_devs[] = {
169 { USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_USBRS232 },
170 { USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE },
171 };
172 #define ucycom_lookup(v, p) usb_lookup(ucycom_devs, v, p)
173
174 USB_DECLARE_DRIVER(ucycom);
175
176 int
177 ucycom_match(struct device *parent, struct cfdata *match,
178 void *aux)
179 {
180 struct uhidev_attach_arg *uha = aux;
181
182 return (ucycom_lookup(uha->uaa->vendor, uha->uaa->product) != NULL ?
183 UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
184 }
185
186 void
187 ucycom_attach(struct device *parent, struct device *self, void *aux)
188 {
189 struct ucycom_softc *sc = (struct ucycom_softc *)self;
190 struct uhidev_attach_arg *uha = aux;
191 int size, repid;
192 void *desc;
193
194 sc->sc_hdev.sc_intr = ucycom_intr;
195 sc->sc_hdev.sc_parent = uha->parent;
196 sc->sc_hdev.sc_report_id = uha->reportid;
197
198 uhidev_get_report_desc(uha->parent, &desc, &size);
199 repid = uha->reportid;
200 sc->sc_ilen = hid_report_size(desc, size, hid_input, repid);
201 sc->sc_olen = hid_report_size(desc, size, hid_output, repid);
202 sc->sc_flen = hid_report_size(desc, size, hid_feature, repid);
203
204 sc->sc_msr = sc->sc_mcr = 0;
205
206 /* set up tty */
207 sc->sc_tty = ttymalloc();
208 sc->sc_tty->t_sc = sc;
209 sc->sc_tty->t_oproc = ucycomstart;
210 sc->sc_tty->t_param = ucycomparam;
211
212 tty_attach(sc->sc_tty);
213
214 /* Nothing interesting to report */
215 printf("\n");
216
217 }
218
219
220 int
221 ucycom_detach(struct device *self, int flags)
222 {
223 struct ucycom_softc *sc = (struct ucycom_softc *)self;
224 struct tty *tp = sc->sc_tty;
225 int maj, mn;
226 int s;
227
228 DPRINTF(("ucycom_detach: sc=%p flags=%d tp=%p\n", sc, flags, tp));
229
230 sc->sc_dying = 1;
231
232 s = splusb();
233 if (tp != NULL) {
234 mutex_spin_enter(&tty_lock);
235 CLR(tp->t_state, TS_CARR_ON);
236 CLR(tp->t_cflag, CLOCAL | MDMBUF);
237 ttyflush(tp, FREAD|FWRITE);
238 mutex_spin_exit(&tty_lock);
239 }
240 /* Wait for processes to go away. */
241 usb_detach_wait(USBDEV(sc->sc_hdev.sc_dev));
242 splx(s);
243
244 /* locate the major number */
245 maj = cdevsw_lookup_major(&ucycom_cdevsw);
246
247 /* Nuke the vnodes for any open instances. */
248 mn = device_unit(self);
249
250 DPRINTFN(2, ("ucycom_detach: maj=%d mn=%d\n", maj, mn));
251 vdevgone(maj, mn, mn, VCHR);
252 vdevgone(maj, mn | UCYCOMDIALOUT_MASK, mn | UCYCOMDIALOUT_MASK, VCHR);
253 vdevgone(maj, mn | UCYCOMCALLUNIT_MASK, mn | UCYCOMCALLUNIT_MASK, VCHR);
254
255 /* Detach and free the tty. */
256 if (tp != NULL) {
257 DPRINTF(("ucycom_detach: tty_detach %p\n", tp));
258 tty_detach(tp);
259 ttyfree(tp);
260 sc->sc_tty = NULL;
261 }
262
263 return 0;
264 }
265
266 int
267 ucycom_activate(device_ptr_t self, enum devact act)
268 {
269 struct ucycom_softc *sc = (struct ucycom_softc *)self;
270
271 DPRINTFN(5,("ucycom_activate: %d\n", act));
272
273 switch (act) {
274 case DVACT_ACTIVATE:
275 return (EOPNOTSUPP);
276
277 case DVACT_DEACTIVATE:
278 sc->sc_dying = 1;
279 break;
280 }
281 return (0);
282 }
283
284 #if 0
285 void
286 ucycom_shutdown(struct ucycom_softc *sc)
287 {
288 struct tty *tp = sc->sc_tty;
289
290 DPRINTF(("ucycom_shutdown\n"));
291 /*
292 * Hang up if necessary. Wait a bit, so the other side has time to
293 * notice even if we immediately open the port again.
294 */
295 if (ISSET(tp->t_cflag, HUPCL)) {
296 ucycom_dtr(sc, 0);
297 (void)tsleep(sc, TTIPRI, ttclos, hz);
298 }
299 }
300 #endif
301
302 int
303 ucycomopen(dev_t dev, int flag, int mode, struct lwp *l)
304 {
305 int unit = UCYCOMUNIT(dev);
306 struct ucycom_softc *sc;
307 struct tty *tp;
308 int s, err;
309
310 DPRINTF(("ucycomopen: unit=%d\n", unit));
311
312 if (unit >= ucycom_cd.cd_ndevs)
313 return (ENXIO);
314 sc = ucycom_cd.cd_devs[unit];
315
316 DPRINTF(("ucycomopen: sc=%p\n", sc));
317
318 if (sc == NULL)
319 return (ENXIO);
320
321 if (sc->sc_dying)
322 return (EIO);
323
324 if (!device_is_active(&sc->sc_hdev.sc_dev))
325 return (ENXIO);
326
327 tp = sc->sc_tty;
328
329 DPRINTF(("ucycomopen: tp=%p\n", tp));
330
331 if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
332 return (EBUSY);
333
334 s = spltty();
335
336 if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
337 struct termios t;
338
339 tp->t_dev = dev;
340
341 err = uhidev_open(&sc->sc_hdev);
342 if (err) {
343 /* Any cleanup? */
344 splx(s);
345 return (err);
346 }
347
348 /*
349 * Initialize the termios status to the defaults. Add in the
350 * sticky bits from TIOCSFLAGS.
351 */
352 t.c_ispeed = 0;
353 t.c_ospeed = TTYDEF_SPEED;
354 t.c_cflag = TTYDEF_CFLAG;
355 if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL))
356 SET(t.c_cflag, CLOCAL);
357 if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS))
358 SET(t.c_cflag, CRTSCTS);
359 if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF))
360 SET(t.c_cflag, MDMBUF);
361
362 tp->t_ospeed = 0;
363 (void) ucycomparam(tp, &t);
364 tp->t_iflag = TTYDEF_IFLAG;
365 tp->t_oflag = TTYDEF_OFLAG;
366 tp->t_lflag = TTYDEF_LFLAG;
367 ttychars(tp);
368 ttsetwater(tp);
369
370 /* Allocate an output report buffer */
371 sc->sc_obuf = malloc(sc->sc_olen, M_USBDEV, M_WAITOK);
372
373 DPRINTF(("ucycomopen: sc->sc_obuf=%p\n", sc->sc_obuf));
374
375 #if 0
376 /* XXX Don't do this as for some reason trying to do an interrupt
377 * XXX out transfer at this point means everything gets stuck!?!
378 */
379 /*
380 * Turn on DTR. We must always do this, even if carrier is not
381 * present, because otherwise we'd have to use TIOCSDTR
382 * immediately after setting CLOCAL, which applications do not
383 * expect. We always assert DTR while the device is open
384 * unless explicitly requested to deassert it.
385 */
386 ucycom_dtr(sc, 1);
387 #endif
388
389 #if 0
390 /* XXX CLR(sc->sc_rx_flags, RX_ANY_BLOCK);*/
391 ucycom_hwiflow(sc);
392 #endif
393
394 }
395 splx(s);
396
397 err = ttyopen(tp, UCYCOMDIALOUT(dev), ISSET(flag, O_NONBLOCK));
398 if (err)
399 goto bad;
400
401 err = (*tp->t_linesw->l_open)(dev, tp);
402 if (err)
403 goto bad;
404
405 return (0);
406
407 bad:
408 if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
409 /*
410 * We failed to open the device, and nobody else had it opened.
411 * Clean up the state as appropriate.
412 */
413 ucycom_cleanup(sc);
414 }
415
416 return (err);
417
418 }
419
420
421 int
422 ucycomclose(dev_t dev, int flag, int mode, struct lwp *l)
423 {
424 struct ucycom_softc *sc = ucycom_cd.cd_devs[UCYCOMUNIT(dev)];
425 struct tty *tp = sc->sc_tty;
426
427 DPRINTF(("ucycomclose: unit=%d\n", UCYCOMUNIT(dev)));
428 if (!ISSET(tp->t_state, TS_ISOPEN))
429 return (0);
430
431 (*tp->t_linesw->l_close)(tp, flag);
432 ttyclose(tp);
433
434 if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
435 /*
436 * Although we got a last close, the device may still be in
437 * use; e.g. if this was the dialout node, and there are still
438 * processes waiting for carrier on the non-dialout node.
439 */
440 ucycom_cleanup(sc);
441 }
442
443 return (0);
444 }
445
446 Static void
447 ucycomstart(struct tty *tp)
448 {
449 struct ucycom_softc *sc = ucycom_cd.cd_devs[UCYCOMUNIT(tp->t_dev)];
450 u_char *data;
451 int cnt, len, err, s;
452
453 if (sc->sc_dying)
454 return;
455
456 s = spltty();
457 if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) {
458 DPRINTFN(4,("ucycomstart: no go, state=0x%x\n", tp->t_state));
459 goto out;
460 }
461
462 #if 0
463 /* HW FLOW CTL */
464 if (sc->sc_tx_stopped)
465 goto out;
466 #endif
467
468 if (ttypull(tp) == 0)
469 goto out;
470
471 /* Grab the first contiguous region of buffer space. */
472 data = tp->t_outq.c_cf;
473 cnt = ndqb(&tp->t_outq, 0);
474
475 if (cnt == 0) {
476 DPRINTF(("ucycomstart: cnt == 0\n"));
477 goto out;
478 }
479
480 SET(tp->t_state, TS_BUSY);
481
482 /*
483 * The 8 byte output report uses byte 0 for control and byte
484 * count.
485 *
486 * The 32 byte output report uses byte 0 for control. Byte 1
487 * is used for byte count.
488 */
489 memset(sc->sc_obuf, 0, sc->sc_olen);
490 len = cnt;
491 switch (sc->sc_olen) {
492 case 8:
493 if (cnt > sc->sc_olen - 1) {
494 DPRINTF(("ucycomstart(8): big buffer %d chars\n", len));
495 len = sc->sc_olen - 1;
496 }
497
498 memcpy(sc->sc_obuf + 1, data, len);
499 sc->sc_obuf[0] = len | sc->sc_mcr;
500
501 DPRINTF(("ucycomstart(8): sc->sc_obuf[0] = %d | %d = %d\n", len, sc->sc_mcr, sc->sc_obuf[0]));
502 #ifdef UCYCOM_DEBUG
503 if (ucycomdebug > 10) {
504 u_int32_t i;
505 u_int8_t *d = data;
506
507 DPRINTF(("ucycomstart(8): data ="));
508 for (i = 0; i < len; i++)
509 DPRINTF((" %02x", d[i]));
510 DPRINTF(("\n"));
511 }
512 #endif
513 break;
514
515 case 32:
516 if (cnt > sc->sc_olen - 2) {
517 DPRINTF(("ucycomstart(32): big buffer %d chars\n", len));
518 len = sc->sc_olen - 2;
519 }
520
521 memcpy(sc->sc_obuf + 2, data, len);
522 sc->sc_obuf[0] = sc->sc_mcr;
523 sc->sc_obuf[1] = len;
524 DPRINTF(("ucycomstart(32): sc->sc_obuf[0] = %d\nsc->sc_obuf[1] = %d\n", sc->sc_obuf[0], sc->sc_obuf[1]));
525 #ifdef UCYCOM_DEBUG
526 if (ucycomdebug > 10) {
527 u_int32_t i;
528 u_int8_t *d = data;
529
530 DPRINTF(("ucycomstart(32): data ="));
531 for (i = 0; i < len; i++)
532 DPRINTF((" %02x", d[i]));
533 DPRINTF(("\n"));
534 }
535 #endif
536 break;
537
538 default:
539 DPRINTFN(2,("ucycomstart: unknown output report size (%zd)\n",
540 sc->sc_olen));
541 goto out;
542 }
543 splx(s);
544
545 #ifdef UCYCOM_DEBUG
546 if (ucycomdebug > 5) {
547 int i;
548
549 if (len != 0) {
550 DPRINTF(("ucycomstart: sc->sc_obuf[0..%zd) =", sc->sc_olen));
551 for (i = 0; i < sc->sc_olen; i++)
552 DPRINTF((" %02x", sc->sc_obuf[i]));
553 DPRINTF(("\n"));
554 }
555 }
556 #endif
557 err = uhidev_write(sc->sc_hdev.sc_parent, sc->sc_obuf, sc->sc_olen);
558
559 if (err) {
560 DPRINTF(("ucycomstart: error doing uhidev_write = %d\n", err));
561 }
562
563 #ifdef UCYCOM_DEBUG
564 ucycom_get_cfg(sc);
565 #endif
566 DPRINTFN(4,("ucycomstart: req %d chars did %d chars\n", cnt, len));
567
568 s = spltty();
569 CLR(tp->t_state, TS_BUSY);
570 if (ISSET(tp->t_state, TS_FLUSH))
571 CLR(tp->t_state, TS_FLUSH);
572 else
573 ndflush(&tp->t_outq, len);
574 (*tp->t_linesw->l_start)(tp);
575
576 out:
577 splx(s);
578 }
579
580 Static int
581 ucycomparam(struct tty *tp, struct termios *t)
582 {
583 struct ucycom_softc *sc = tp->t_sc;
584 uint32_t baud;
585 uint8_t cfg;
586 int err;
587
588 if (t->c_ospeed < 0) {
589 DPRINTF(("ucycomparam: c_ospeed < 0\n"));
590 return (EINVAL);
591 }
592
593 /* Check requested parameters. */
594 if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
595 return (EINVAL);
596
597 /*
598 * For the console, always force CLOCAL and !HUPCL, so that the port
599 * is always active.
600 */
601 if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR)) {
602 SET(t->c_cflag, CLOCAL);
603 CLR(t->c_cflag, HUPCL);
604 }
605
606 /*
607 * If there were no changes, don't do anything. This avoids dropping
608 * input and improves performance when all we did was frob things like
609 * VMIN and VTIME.
610 */
611 if (tp->t_ospeed == t->c_ospeed &&
612 tp->t_cflag == t->c_cflag)
613 return (0);
614
615 /* XXX lcr = ISSET(sc->sc_lcr, LCR_SBREAK) | cflag2lcr(t->c_cflag); */
616
617 /* And copy to tty. */
618 tp->t_ispeed = 0;
619 tp->t_ospeed = t->c_ospeed;
620 tp->t_cflag = t->c_cflag;
621
622 baud = t->c_ispeed;
623 DPRINTF(("ucycomparam: baud=%d\n", baud));
624
625 if (t->c_cflag & CIGNORE) {
626 cfg = sc->sc_cfg;
627 } else {
628 cfg = 0;
629 switch (t->c_cflag & CSIZE) {
630 case CS8:
631 cfg |= UCYCOM_DATA_BITS_8;
632 break;
633 case CS7:
634 cfg |= UCYCOM_DATA_BITS_7;
635 break;
636 case CS6:
637 cfg |= UCYCOM_DATA_BITS_6;
638 break;
639 case CS5:
640 cfg |= UCYCOM_DATA_BITS_5;
641 break;
642 default:
643 return (EINVAL);
644 }
645 cfg |= ISSET(t->c_cflag, CSTOPB) ?
646 UCYCOM_STOP_BITS_2 : UCYCOM_STOP_BITS_1;
647 cfg |= ISSET(t->c_cflag, PARENB) ?
648 UCYCOM_PARITY_ON : UCYCOM_PARITY_OFF;
649 cfg |= ISSET(t->c_cflag, PARODD) ?
650 UCYCOM_PARITY_ODD : UCYCOM_PARITY_EVEN;
651 }
652
653 /*
654 * Update the tty layer's idea of the carrier bit, in case we changed
655 * CLOCAL or MDMBUF. We don't hang up here; we only do that by
656 * explicit request.
657 */
658 DPRINTF(("ucycomparam: l_modem\n"));
659 (void) (*tp->t_linesw->l_modem)(tp, 1 /* XXX carrier */ );
660
661 err = ucycom_configure(sc, baud, cfg);
662 return (err);
663 }
664
665 void
666 ucycomstop(struct tty *tp, int flag)
667 {
668 DPRINTF(("ucycomstop: flag=%d\n", flag));
669 }
670
671 int
672 ucycomread(dev_t dev, struct uio *uio, int flag)
673 {
674 struct ucycom_softc *sc = ucycom_cd.cd_devs[UCYCOMUNIT(dev)];
675 struct tty *tp = sc->sc_tty;
676 int err;
677
678 DPRINTF(("ucycomread: sc=%p, tp=%p, uio=%p, flag=%d\n", sc, tp, uio, flag));
679 if (sc->sc_dying)
680 return (EIO);
681
682 err = ((*tp->t_linesw->l_read)(tp, uio, flag));
683 return (err);
684 }
685
686
687 int
688 ucycomwrite(dev_t dev, struct uio *uio, int flag)
689 {
690 struct ucycom_softc *sc = ucycom_cd.cd_devs[UCYCOMUNIT(dev)];
691 struct tty *tp = sc->sc_tty;
692 int err;
693
694 DPRINTF(("ucycomwrite: sc=%p, tp=%p, uio=%p, flag=%d\n", sc, tp, uio, flag));
695 if (sc->sc_dying)
696 return (EIO);
697
698 err = ((*tp->t_linesw->l_write)(tp, uio, flag));
699 return (err);
700 }
701
702 struct tty *
703 ucycomtty(dev_t dev)
704 {
705 struct ucycom_softc *sc = ucycom_cd.cd_devs[UCYCOMUNIT(dev)];
706 struct tty *tp = sc->sc_tty;
707
708 DPRINTF(("ucycomtty: sc=%p, tp=%p\n", sc, tp));
709
710 return (tp);
711 }
712
713 int
714 ucycomioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
715 {
716 struct ucycom_softc *sc = ucycom_cd.cd_devs[UCYCOMUNIT(dev)];
717 struct tty *tp = sc->sc_tty;
718 int err;
719 int s;
720
721 if (sc->sc_dying)
722 return (EIO);
723
724 DPRINTF(("ucycomioctl: sc=%p, tp=%p, data=%p\n", sc, tp, data));
725
726 err = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
727 if (err != EPASSTHROUGH)
728 return (err);
729
730 err = ttioctl(tp, cmd, data, flag, l);
731 if (err != EPASSTHROUGH)
732 return (err);
733
734 err = 0;
735
736 DPRINTF(("ucycomioctl: our cmd=0x%08lx\n", cmd));
737 s = spltty();
738
739 switch (cmd) {
740 /* case TIOCSBRK:
741 ucycom_break(sc, 1);
742 break;
743
744 case TIOCCBRK:
745 ucycom_break(sc, 0);
746 break;
747 */
748 case TIOCSDTR:
749 ucycom_dtr(sc, 1);
750 break;
751
752 case TIOCCDTR:
753 ucycom_dtr(sc, 0);
754 break;
755
756 case TIOCGFLAGS:
757 *(int *)data = sc->sc_swflags;
758 break;
759
760 case TIOCSFLAGS:
761 err = kauth_authorize_device_tty(l->l_cred,
762 KAUTH_DEVICE_TTY_PRIVSET, tp);
763 if (err)
764 break;
765 sc->sc_swflags = *(int *)data;
766 break;
767
768 case TIOCMSET:
769 case TIOCMBIS:
770 case TIOCMBIC:
771 tiocm_to_ucycom(sc, cmd, *(int *)data);
772 break;
773
774 case TIOCMGET:
775 *(int *)data = ucycom_to_tiocm(sc);
776 break;
777
778 default:
779 err = EPASSTHROUGH;
780 break;
781 }
782
783 splx(s);
784
785 return (err);
786 }
787
788 int
789 ucycompoll(dev_t dev, int events, struct lwp *l)
790 {
791 struct ucycom_softc *sc = ucycom_cd.cd_devs[UCYCOMUNIT(dev)];
792 struct tty *tp = sc->sc_tty;
793 int err;
794
795 DPRINTF(("ucycompoll: sc=%p, tp=%p, events=%d, lwp=%p\n", sc, tp, events, l));
796
797 if (sc->sc_dying)
798 return (EIO);
799
800 err = ((*tp->t_linesw->l_poll)(tp, events, l));
801 return (err);
802 }
803
804 Static int
805 ucycom_configure(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg)
806 {
807 uint8_t report[5];
808 int err;
809
810 switch (baud) {
811 case 600:
812 case 1200:
813 case 2400:
814 case 4800:
815 case 9600:
816 case 19200:
817 case 38400:
818 case 57600:
819 #if 0
820 /*
821 * Stock chips only support standard baud rates in the 600 - 57600
822 * range, but higher rates can be achieved using custom firmware.
823 */
824 case 115200:
825 case 153600:
826 case 192000:
827 #endif
828 break;
829 default:
830 return (EINVAL);
831 }
832
833 DPRINTF(("ucycom_configure: setting %d baud, %d-%c-%d (%d)\n", baud,
834 5 + (cfg & UCYCOM_DATA_MASK),
835 (cfg & UCYCOM_PARITY_MASK) ?
836 ((cfg & UCYCOM_PARITY_TYPE_MASK) ? 'O' : 'E') : 'N',
837 (cfg & UCYCOM_STOP_MASK) ? 2 : 1, cfg));
838
839 report[0] = baud & 0xff;
840 report[1] = (baud >> 8) & 0xff;
841 report[2] = (baud >> 16) & 0xff;
842 report[3] = (baud >> 24) & 0xff;
843 report[4] = cfg;
844 err = uhidev_set_report(&sc->sc_hdev, UHID_FEATURE_REPORT,
845 report, sc->sc_flen);
846 if (err != 0) {
847 DPRINTF(("%s\n", usbd_errstr(err)));
848 return EIO;
849 }
850 sc->sc_baud = baud;
851 sc->sc_cfg = cfg;
852
853 return 0;
854 }
855
856 Static void
857 ucycom_intr(struct uhidev *addr, void *ibuf, u_int len)
858 {
859 struct ucycom_softc *sc = (struct ucycom_softc *)addr;
860 struct tty *tp = sc->sc_tty;
861 int (*rint)(int , struct tty *) = tp->t_linesw->l_rint;
862 uint8_t *cp = ibuf;
863 int s, n, st, chg;
864
865 /* We understand 8 byte and 32 byte input records */
866 switch (len) {
867 case 8:
868 n = cp[0] & UCYCOM_LMASK;
869 st = cp[0] & ~UCYCOM_LMASK;
870 cp++;
871 break;
872
873 case 32:
874 st = cp[0];
875 n = cp[1];
876 cp += 2;
877 break;
878
879 default:
880 DPRINTFN(3,("ucycom_intr: Unknown input report length\n"));
881 return;
882 }
883
884 #ifdef UCYCOM_DEBUG
885 if (ucycomdebug > 5) {
886 u_int32_t i;
887
888 if (n != 0) {
889 DPRINTF(("ucycom_intr: ibuf[0..%d) =", n));
890 for (i = 0; i < n; i++)
891 DPRINTF((" %02x", cp[i]));
892 DPRINTF(("\n"));
893 }
894 }
895 #endif
896 s = spltty();
897
898 /* Give characters to tty layer. */
899 while (n-- > 0) {
900 DPRINTFN(7,("ucycom_intr: char=0x%02x\n", *cp));
901 if ((*rint)(*cp++, tp) == -1) {
902 /* XXX what should we do? */
903 aprint_error_dev(&sc->sc_hdev.sc_dev, "lost a character\n");
904 break;
905 }
906 }
907 splx(s);
908 chg = st ^ sc->sc_msr;
909 sc->sc_msr = st;
910 if (ISSET(chg, UCYCOM_DCD))
911 (*tp->t_linesw->l_modem)(tp,
912 ISSET(sc->sc_msr, UCYCOM_DCD));
913
914 }
915
916 Static void
917 tiocm_to_ucycom(struct ucycom_softc *sc, u_long how, int ttybits)
918 {
919 u_char combits;
920 u_char before = sc->sc_mcr;
921
922 combits = 0;
923 if (ISSET(ttybits, TIOCM_DTR))
924 SET(combits, UCYCOM_DTR);
925 if (ISSET(ttybits, TIOCM_RTS))
926 SET(combits, UCYCOM_RTS);
927
928 switch (how) {
929 case TIOCMBIC:
930 CLR(sc->sc_mcr, combits);
931 break;
932
933 case TIOCMBIS:
934 SET(sc->sc_mcr, combits);
935 break;
936
937 case TIOCMSET:
938 CLR(sc->sc_mcr, UCYCOM_DTR | UCYCOM_RTS);
939 SET(sc->sc_mcr, combits);
940 break;
941 }
942 if (before ^ sc->sc_mcr) {
943 DPRINTF(("tiocm_to_ucycom: something has changed\n"));
944 ucycom_set_status(sc);
945 }
946 }
947
948 Static int
949 ucycom_to_tiocm(struct ucycom_softc *sc)
950 {
951 u_char combits;
952 int ttybits = 0;
953
954 combits = sc->sc_mcr;
955 if (ISSET(combits, UCYCOM_DTR))
956 SET(ttybits, TIOCM_DTR);
957 if (ISSET(combits, UCYCOM_RTS))
958 SET(ttybits, TIOCM_RTS);
959
960 combits = sc->sc_msr;
961 if (ISSET(combits, UCYCOM_DCD))
962 SET(ttybits, TIOCM_CD);
963 if (ISSET(combits, UCYCOM_CTS))
964 SET(ttybits, TIOCM_CTS);
965 if (ISSET(combits, UCYCOM_DSR))
966 SET(ttybits, TIOCM_DSR);
967 if (ISSET(combits, UCYCOM_RI))
968 SET(ttybits, TIOCM_RI);
969
970 return (ttybits);
971 }
972
973 Static void
974 ucycom_dtr(struct ucycom_softc *sc, int set)
975 {
976 uint8_t old;
977
978 old = sc->sc_mcr;
979 if (set)
980 SET(sc->sc_mcr, UCYCOM_DTR);
981 else
982 CLR(sc->sc_mcr, UCYCOM_DTR);
983
984 if (old ^ sc->sc_mcr)
985 ucycom_set_status(sc);
986 }
987
988 #if 0
989 Static void
990 ucycom_rts(struct ucycom_softc *sc, int set)
991 {
992 uint8_t old;
993
994 old = sc->sc_msr;
995 if (set)
996 SET(sc->sc_mcr, UCYCOM_RTS);
997 else
998 CLR(sc->sc_mcr, UCYCOM_RTS);
999
1000 if (old ^ sc->sc_mcr)
1001 ucycom_set_status(sc);
1002 }
1003 #endif
1004
1005 Static void
1006 ucycom_set_status(struct ucycom_softc *sc)
1007 {
1008 int err;
1009
1010 if (sc->sc_olen != 8 && sc->sc_olen != 32) {
1011 DPRINTFN(2,("ucycom_set_status: unknown output report size (%zd)\n",
1012 sc->sc_olen));
1013 return;
1014 }
1015
1016 DPRINTF(("ucycom_set_status: %d\n", sc->sc_mcr));
1017
1018 memset(sc->sc_obuf, 0, sc->sc_olen);
1019 sc->sc_obuf[0] = sc->sc_mcr;
1020
1021 err = uhidev_write(sc->sc_hdev.sc_parent, sc->sc_obuf, sc->sc_olen);
1022 if (err) {
1023 DPRINTF(("ucycom_set_status: err=%d\n", err));
1024 }
1025 }
1026
1027 #ifdef UCYCOM_DEBUG
1028 Static void
1029 ucycom_get_cfg(struct ucycom_softc *sc)
1030 {
1031 int err, cfg, baud;
1032 uint8_t report[5];
1033
1034 err = uhidev_get_report(&sc->sc_hdev, UHID_FEATURE_REPORT,
1035 report, sc->sc_flen);
1036 cfg = report[4];
1037 baud = (report[3] << 24) + (report[2] << 16) + (report[1] << 8) + report[0];
1038 DPRINTF(("ucycom_configure: device reports %d baud, %d-%c-%d (%d)\n", baud,
1039 5 + (cfg & UCYCOM_DATA_MASK),
1040 (cfg & UCYCOM_PARITY_MASK) ?
1041 ((cfg & UCYCOM_PARITY_TYPE_MASK) ? 'O' : 'E') : 'N',
1042 (cfg & UCYCOM_STOP_MASK) ? 2 : 1, cfg));
1043 }
1044 #endif
1045
1046 Static void
1047 ucycom_cleanup(struct ucycom_softc *sc)
1048 {
1049 DPRINTF(("ucycom_cleanup: closing uhidev\n"));
1050
1051 if (sc->sc_obuf !=NULL)
1052 free (sc->sc_obuf, M_USBDEV);
1053 uhidev_close(&sc->sc_hdev);
1054 }
1055