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