Change the kernel dev_t, representing a pointer to a specinfo structure,
[dragonfly.git] / sys / dev / usbmisc / ucom / ucom.c
1 /*
2  * $NetBSD: ucom.c,v 1.39 2001/08/16 22:31:24 augustss Exp $
3  * $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $
4  * $FreeBSD: src/sys/dev/usb/ucom.c,v 1.35 2003/11/16 11:58:21 akiyama Exp $
5  * $DragonFly: src/sys/dev/usbmisc/ucom/ucom.c,v 1.21 2006/09/10 01:26:37 dillon Exp $
6  */
7 /*-
8  * Copyright (c) 2001-2002, Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
9  * All rights reserved.
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  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32
33 /*
34  * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
35  * All rights reserved.
36  *
37  * This code is derived from software contributed to The NetBSD Foundation
38  * by Lennart Augustsson (lennart@augustsson.net) at
39  * Carlstedt Research & Technology.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. All advertising materials mentioning features or use of this software
50  *    must display the following acknowledgement:
51  *        This product includes software developed by the NetBSD
52  *        Foundation, Inc. and its contributors.
53  * 4. Neither the name of The NetBSD Foundation nor the names of its
54  *    contributors may be used to endorse or promote products derived
55  *    from this software without specific prior written permission.
56  *
57  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
58  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
59  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
60  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
61  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
62  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
63  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
64  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
65  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
66  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
67  * POSSIBILITY OF SUCH DAMAGE.
68  */
69
70 #include <sys/param.h>
71 #include <sys/systm.h>
72 #include <sys/kernel.h>
73 #include <sys/malloc.h>
74 #include <sys/bus.h>
75 #include <sys/ioccom.h>
76 #include <sys/fcntl.h>
77 #include <sys/conf.h>
78 #include <sys/tty.h>
79 #include <sys/clist.h>
80 #include <sys/file.h>
81 #if defined(__FreeBSD__) && __FreeBSD_version >= 500014
82 #include <sys/selinfo.h>
83 #else
84 #include <sys/select.h>
85 #endif
86 #include <sys/proc.h>
87 #include <sys/vnode.h>
88 #include <sys/poll.h>
89 #include <sys/sysctl.h>
90 #include <sys/thread2.h>
91
92 #include <bus/usb/usb.h>
93 #include <bus/usb/usbcdc.h>
94
95 #include <bus/usb/usbdi.h>
96 #include <bus/usb/usbdi_util.h>
97 #include <bus/usb/usbdevs.h>
98 #include <bus/usb/usb_quirks.h>
99
100 #include "ucomvar.h"
101
102 #ifdef USB_DEBUG
103 static int      ucomdebug = 0;
104 SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
105 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
106            &ucomdebug, 0, "ucom debug level");
107 #define DPRINTF(x)      do { \
108                                 if (ucomdebug) \
109                                         logprintf x; \
110                         } while (0)
111
112 #define DPRINTFN(n, x)  do { \
113                                 if (ucomdebug > (n)) \
114                                         logprintf x; \
115                         } while (0)
116 #else
117 #define DPRINTF(x)
118 #define DPRINTFN(n, x)
119 #endif
120
121 Static d_open_t  ucomopen;
122 Static d_close_t ucomclose;
123 Static d_read_t  ucomread;
124 Static d_write_t ucomwrite;
125 Static d_ioctl_t ucomioctl;
126
127 #define UCOM_CDEV_MAJOR  138
128
129 static struct dev_ops ucom_ops = {
130         { "ucom", UCOM_CDEV_MAJOR, D_TTY | D_KQFILTER },
131         .d_open =       ucomopen,
132         .d_close =      ucomclose,
133         .d_read =       ucomread,
134         .d_write =      ucomwrite,
135         .d_ioctl =      ucomioctl,
136         .d_poll =       ttypoll,
137         .d_kqfilter =   ttykqfilter
138 };
139
140 Static void ucom_cleanup(struct ucom_softc *);
141 Static int ucomctl(struct ucom_softc *, int, int);
142 Static int ucomparam(struct tty *, struct termios *);
143 Static void ucomstart(struct tty *);
144 Static void ucomstop(struct tty *, int);
145 Static void ucom_shutdown(struct ucom_softc *);
146 Static void ucom_dtr(struct ucom_softc *, int);
147 Static void ucom_rts(struct ucom_softc *, int);
148 Static void ucom_break(struct ucom_softc *, int);
149 Static usbd_status ucomstartread(struct ucom_softc *);
150 Static void ucomreadcb(usbd_xfer_handle, usbd_private_handle, usbd_status);
151 Static void ucomwritecb(usbd_xfer_handle, usbd_private_handle, usbd_status);
152 Static void ucomstopread(struct ucom_softc *);
153 static void disc_optim(struct tty *, struct termios *, struct ucom_softc *);
154
155 devclass_t ucom_devclass;
156
157 static moduledata_t ucom_mod = {
158         "ucom",
159         NULL,
160         NULL
161 };
162
163 DECLARE_MODULE(ucom, ucom_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
164 MODULE_DEPEND(ucom, usb, 1, 1, 1);
165 MODULE_VERSION(ucom, UCOM_MODVER);
166
167 int
168 ucom_attach(struct ucom_softc *sc)
169 {
170         struct tty *tp;
171         int unit;
172         cdev_t dev;
173
174         unit = device_get_unit(sc->sc_dev);
175
176         sc->sc_tty = tp = ttymalloc(sc->sc_tty);
177         tp->t_oproc = ucomstart;
178         tp->t_param = ucomparam;
179         tp->t_stop = ucomstop;
180
181         DPRINTF(("ucom_attach: tty_attach tp = %p\n", tp));
182
183         DPRINTF(("ucom_attach: make_dev: ucom%d\n", unit));
184
185         dev_ops_add(&ucom_ops, UCOMUNIT_MASK, unit);
186         dev = make_dev(&ucom_ops, unit | UCOM_CALLOUT_MASK,
187                         UID_UUCP, GID_DIALER, 0660,
188                         "ucom%d", unit);
189         dev->si_tty = tp;
190
191         return (0);
192 }
193
194 int
195 ucom_detach(struct ucom_softc *sc)
196 {
197         struct tty *tp = sc->sc_tty;
198         int unit;
199
200         DPRINTF(("ucom_detach: sc = %p, tp = %p\n", sc, sc->sc_tty));
201
202         sc->sc_dying = 1;
203
204         if (sc->sc_bulkin_pipe != NULL)
205                 usbd_abort_pipe(sc->sc_bulkin_pipe);
206         if (sc->sc_bulkout_pipe != NULL)
207                 usbd_abort_pipe(sc->sc_bulkout_pipe);
208
209         if (tp != NULL) {
210                 if (tp->t_state & TS_ISOPEN) {
211                         device_printf(sc->sc_dev,
212                                       "still open, forcing close\n");
213                         (*linesw[tp->t_line].l_close)(tp, 0);
214                         tp->t_gen++;
215                         ttyclose(tp);
216                         ttwakeup(tp);
217                         ttwwakeup(tp);
218                 }
219         } else {
220                 DPRINTF(("ucom_detach: no tty\n"));
221                 return (0);
222         }
223
224         crit_enter();
225         if (--sc->sc_refcnt >= 0) {
226                 /* Wait for processes to go away. */
227                 usb_detach_wait(USBDEV(sc->sc_dev));
228         }
229         crit_exit();
230
231         unit = device_get_unit(sc->sc_dev);
232         dev_ops_remove(&ucom_ops, UCOMUNIT_MASK, unit);
233
234         return (0);
235 }
236
237 Static void
238 ucom_shutdown(struct ucom_softc *sc)
239 {
240         struct tty *tp = sc->sc_tty;
241
242         DPRINTF(("ucom_shutdown\n"));
243         /*
244          * Hang up if necessary.  Wait a bit, so the other side has time to
245          * notice even if we immediately open the port again.
246          */
247         if (ISSET(tp->t_cflag, HUPCL)) {
248                 (void)ucomctl(sc, TIOCM_DTR, DMBIC);
249                 (void)tsleep(sc, 0, "ucomsd", hz);
250         }
251 }
252
253 Static int
254 ucomopen(struct dev_open_args *ap)
255 {
256         cdev_t dev = ap->a_head.a_dev;
257         int unit = UCOMUNIT(dev);
258         struct ucom_softc *sc;
259         usbd_status err;
260         struct tty *tp;
261         int error;
262
263         USB_GET_SC_OPEN(ucom, unit, sc);
264
265         if (sc->sc_dying)
266                 return (ENXIO);
267
268         tp = sc->sc_tty;
269
270         DPRINTF(("%s: ucomopen: tp = %p\n", USBDEVNAME(sc->sc_dev), tp));
271
272         if (ISSET(tp->t_state, TS_ISOPEN) &&
273             ISSET(tp->t_state, TS_XCLUDE) &&
274             suser_cred(ap->a_cred, 0)
275         ) {
276                 return (EBUSY);
277         }
278
279         /*
280          * Do the following iff this is a first open.
281          */
282         crit_enter();
283         while (sc->sc_opening)
284                 tsleep(&sc->sc_opening, 0, "ucomop", 0);
285         sc->sc_opening = 1;
286
287         if (!ISSET(tp->t_state, TS_ISOPEN)) {
288                 struct termios t;
289
290                 sc->sc_poll = 0;
291                 sc->sc_lsr = sc->sc_msr = sc->sc_mcr = 0;
292
293                 tp->t_dev = reference_dev(dev);
294
295                 /*
296                  * Initialize the termios status to the defaults.  Add in the
297                  * sticky bits from TIOCSFLAGS.
298                  */
299                 t.c_ispeed = 0;
300                 t.c_ospeed = TTYDEF_SPEED;
301                 t.c_cflag = TTYDEF_CFLAG;
302                 /* Make sure ucomparam() will do something. */
303                 tp->t_ospeed = 0;
304                 (void)ucomparam(tp, &t);
305                 tp->t_iflag = TTYDEF_IFLAG;
306                 tp->t_oflag = TTYDEF_OFLAG;
307                 tp->t_lflag = TTYDEF_LFLAG;
308                 ttychars(tp);
309                 ttsetwater(tp);
310
311                 /*
312                  * Turn on DTR.  We must always do this, even if carrier is not
313                  * present, because otherwise we'd have to use TIOCSDTR
314                  * immediately after setting CLOCAL, which applications do not
315                  * expect.  We always assert DTR while the device is open
316                  * unless explicitly requested to deassert it.
317                  */
318                 (void)ucomctl(sc, TIOCM_DTR | TIOCM_RTS, DMBIS);
319
320                 /* Device specific open */
321                 if (sc->sc_callback->ucom_open != NULL) {
322                         error = sc->sc_callback->ucom_open(sc->sc_parent,
323                                                            sc->sc_portno);
324                         if (error) {
325                                 ucom_cleanup(sc);
326                                 sc->sc_opening = 0;
327                                 wakeup(&sc->sc_opening);
328                                 crit_exit();
329                                 return (error);
330                         }
331                 }
332
333                 DPRINTF(("ucomopen: open pipes in = %d out = %d\n",
334                          sc->sc_bulkin_no, sc->sc_bulkout_no));
335
336                 /* Open the bulk pipes */
337                 /* Bulk-in pipe */
338                 err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_no, 0,
339                                      &sc->sc_bulkin_pipe);
340                 if (err) {
341                         printf("%s: open bulk in error (addr %d): %s\n",
342                                USBDEVNAME(sc->sc_dev), sc->sc_bulkin_no,
343                                usbd_errstr(err));
344                         error = EIO;
345                         goto fail_0;
346                 }
347                 /* Bulk-out pipe */
348                 err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_no,
349                                      USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
350                 if (err) {
351                         printf("%s: open bulk out error (addr %d): %s\n",
352                                USBDEVNAME(sc->sc_dev), sc->sc_bulkout_no,
353                                usbd_errstr(err));
354                         error = EIO;
355                         goto fail_1;
356                 }
357
358                 /* Allocate a request and an input buffer and start reading. */
359                 sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev);
360                 if (sc->sc_ixfer == NULL) {
361                         error = ENOMEM;
362                         goto fail_2;
363                 }
364
365                 sc->sc_ibuf = usbd_alloc_buffer(sc->sc_ixfer,
366                                                 sc->sc_ibufsizepad);
367                 if (sc->sc_ibuf == NULL) {
368                         error = ENOMEM;
369                         goto fail_3;
370                 }
371
372                 sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev);
373                 if (sc->sc_oxfer == NULL) {
374                         error = ENOMEM;
375                         goto fail_3;
376                 }
377
378                 sc->sc_obuf = usbd_alloc_buffer(sc->sc_oxfer,
379                                                 sc->sc_obufsize +
380                                                 sc->sc_opkthdrlen);
381                 if (sc->sc_obuf == NULL) {
382                         error = ENOMEM;
383                         goto fail_4;
384                 }
385
386                 /*
387                  * Handle initial DCD.
388                  */
389                 if (ISSET(sc->sc_msr, UMSR_DCD) ||
390                     (minor(dev) & UCOM_CALLOUT_MASK))
391                         (*linesw[tp->t_line].l_modem)(tp, 1);
392
393                 ucomstartread(sc);
394         }
395
396         sc->sc_opening = 0;
397         wakeup(&sc->sc_opening);
398         crit_exit();
399
400         error = ttyopen(dev, tp);
401         if (error)
402                 goto bad;
403
404         error = (*linesw[tp->t_line].l_open)(dev, tp);
405         if (error)
406                 goto bad;
407
408         disc_optim(tp, &tp->t_termios, sc);
409
410         DPRINTF(("%s: ucomopen: success\n", USBDEVNAME(sc->sc_dev)));
411
412         sc->sc_poll = 1;
413         sc->sc_refcnt++;
414
415         return (0);
416
417 fail_4:
418         usbd_free_xfer(sc->sc_oxfer);
419         sc->sc_oxfer = NULL;
420 fail_3:
421         usbd_free_xfer(sc->sc_ixfer);
422         sc->sc_ixfer = NULL;
423 fail_2:
424         usbd_close_pipe(sc->sc_bulkout_pipe);
425         sc->sc_bulkout_pipe = NULL;
426 fail_1:
427         usbd_close_pipe(sc->sc_bulkin_pipe);
428         sc->sc_bulkin_pipe = NULL;
429 fail_0:
430         sc->sc_opening = 0;
431         wakeup(&sc->sc_opening);
432         crit_exit();
433         return (error);
434
435 bad:
436         if (!ISSET(tp->t_state, TS_ISOPEN)) {
437                 /*
438                  * We failed to open the device, and nobody else had it opened.
439                  * Clean up the state as appropriate.
440                  */
441                 ucom_cleanup(sc);
442         }
443
444         DPRINTF(("%s: ucomopen: failed\n", USBDEVNAME(sc->sc_dev)));
445
446         return (error);
447 }
448
449 static int
450 ucomclose(struct dev_close_args *ap)
451 {
452         cdev_t dev = ap->a_head.a_dev;
453         struct ucom_softc *sc;
454         struct tty *tp;
455
456         USB_GET_SC(ucom, UCOMUNIT(dev), sc);
457
458         tp = sc->sc_tty;
459
460         DPRINTF(("%s: ucomclose: unit = %d\n",
461                 USBDEVNAME(sc->sc_dev), UCOMUNIT(dev)));
462
463         if (!ISSET(tp->t_state, TS_ISOPEN))
464                 goto quit;
465
466         crit_enter();
467         (*linesw[tp->t_line].l_close)(tp, ap->a_fflag);
468         disc_optim(tp, &tp->t_termios, sc);
469         ttyclose(tp);
470         crit_exit();
471
472         if (sc->sc_dying)
473                 goto quit;
474
475         if (!ISSET(tp->t_state, TS_ISOPEN)) {
476                 /*
477                  * Although we got a last close, the device may still be in
478                  * use; e.g. if this was the dialout node, and there are still
479                  * processes waiting for carrier on the non-dialout node.
480                  */
481                 ucom_cleanup(sc);
482         }
483
484         if (sc->sc_callback->ucom_close != NULL)
485                 sc->sc_callback->ucom_close(sc->sc_parent, sc->sc_portno);
486
487 quit:
488         if (tp->t_dev) {
489                 release_dev(tp->t_dev);
490                 tp->t_dev = NULL;
491         }
492
493         if (--sc->sc_refcnt < 0)
494                 usb_detach_wakeup(USBDEV(sc->sc_dev));
495
496         return (0);
497 }
498
499 static int
500 ucomread(struct dev_read_args *ap)
501 {
502         cdev_t dev = ap->a_head.a_dev;
503         struct ucom_softc *sc;
504         struct tty *tp;
505         int error;
506
507         USB_GET_SC(ucom, UCOMUNIT(dev), sc);
508         tp = sc->sc_tty;
509
510         DPRINTF(("ucomread: tp = %p, flag = 0x%x\n", tp, ap->a_ioflag));
511
512         if (sc->sc_dying)
513                 return (EIO);
514
515         error = (*linesw[tp->t_line].l_read)(tp, ap->a_uio, ap->a_ioflag);
516
517         DPRINTF(("ucomread: error = %d\n", error));
518
519         return (error);
520 }
521
522 static int
523 ucomwrite(struct dev_write_args *ap)
524 {
525         cdev_t dev = ap->a_head.a_dev;
526         struct ucom_softc *sc;
527         struct tty *tp;
528         int error;
529
530         USB_GET_SC(ucom, UCOMUNIT(dev), sc);
531         tp = sc->sc_tty;
532
533         DPRINTF(("ucomwrite: tp = %p, flag = 0x%x\n", tp, ap->a_ioflag));
534
535         if (sc->sc_dying)
536                 return (EIO);
537
538         error = (*linesw[tp->t_line].l_write)(tp, ap->a_uio, ap->a_ioflag);
539
540         DPRINTF(("ucomwrite: error = %d\n", error));
541
542         return (error);
543 }
544
545 static int
546 ucomioctl(struct dev_ioctl_args *ap)
547 {
548         cdev_t dev = ap->a_head.a_dev;
549         struct ucom_softc *sc;
550         struct tty *tp;
551         int error;
552         int d;
553
554         USB_GET_SC(ucom, UCOMUNIT(dev), sc);
555         tp = sc->sc_tty;
556
557         if (sc->sc_dying)
558                 return (EIO);
559
560         DPRINTF(("ucomioctl: cmd = 0x%08lx\n", ap->a_cmd));
561
562         error = (*linesw[tp->t_line].l_ioctl)(tp, ap->a_cmd, ap->a_data,
563                                               ap->a_fflag, ap->a_cred);
564         if (error != ENOIOCTL) {
565                 DPRINTF(("ucomioctl: l_ioctl: error = %d\n", error));
566                 return (error);
567         }
568
569         crit_enter();
570
571         error = ttioctl(tp, ap->a_cmd, ap->a_data, ap->a_fflag);
572         disc_optim(tp, &tp->t_termios, sc);
573         if (error != ENOIOCTL) {
574                 crit_exit();
575                 DPRINTF(("ucomioctl: ttioctl: error = %d\n", error));
576                 return (error);
577         }
578
579         if (sc->sc_callback->ucom_ioctl != NULL) {
580                 error = sc->sc_callback->ucom_ioctl(sc->sc_parent,
581                                                     sc->sc_portno,
582                                                     ap->a_cmd, ap->a_data,
583                                                     ap->a_fflag, curthread);
584                 if (error >= 0) {
585                         crit_exit();
586                         return (error);
587                 }
588         }
589
590         error = 0;
591
592         DPRINTF(("ucomioctl: our cmd = 0x%08lx\n", ap->a_cmd));
593
594         switch (ap->a_cmd) {
595         case TIOCSBRK:
596                 DPRINTF(("ucomioctl: TIOCSBRK\n"));
597                 ucom_break(sc, 1);
598                 break;
599         case TIOCCBRK:
600                 DPRINTF(("ucomioctl: TIOCCBRK\n"));
601                 ucom_break(sc, 0);
602                 break;
603
604         case TIOCSDTR:
605                 DPRINTF(("ucomioctl: TIOCSDTR\n"));
606                 (void)ucomctl(sc, TIOCM_DTR, DMBIS);
607                 break;
608         case TIOCCDTR:
609                 DPRINTF(("ucomioctl: TIOCCDTR\n"));
610                 (void)ucomctl(sc, TIOCM_DTR, DMBIC);
611                 break;
612
613         case TIOCMSET:
614                 d = *(int *)ap->a_data;
615                 DPRINTF(("ucomioctl: TIOCMSET, 0x%x\n", d));
616                 (void)ucomctl(sc, d, DMSET);
617                 break;
618         case TIOCMBIS:
619                 d = *(int *)ap->a_data;
620                 DPRINTF(("ucomioctl: TIOCMBIS, 0x%x\n", d));
621                 (void)ucomctl(sc, d, DMBIS);
622                 break;
623         case TIOCMBIC:
624                 d = *(int *)ap->a_data;
625                 DPRINTF(("ucomioctl: TIOCMBIC, 0x%x\n", d));
626                 (void)ucomctl(sc, d, DMBIC);
627                 break;
628         case TIOCMGET:
629                 d = ucomctl(sc, 0, DMGET);
630                 DPRINTF(("ucomioctl: TIOCMGET, 0x%x\n", d));
631                 *(int *)ap->a_data = d;
632                 break;
633
634         default:
635                 DPRINTF(("ucomioctl: error: our cmd = 0x%08lx\n", ap->a_cmd));
636                 error = ENOTTY;
637                 break;
638         }
639
640         crit_exit();
641
642         return (error);
643 }
644
645 Static int
646 ucomctl(struct ucom_softc *sc, int bits, int how)
647 {
648         int     mcr;
649         int     msr;
650         int     onoff;
651
652         DPRINTF(("ucomctl: bits = 0x%x, how = %d\n", bits, how));
653
654         if (how == DMGET) {
655                 SET(bits, TIOCM_LE);            /* always set TIOCM_LE bit */
656                 DPRINTF(("ucomctl: DMGET: LE"));
657
658                 mcr = sc->sc_mcr;
659                 if (ISSET(mcr, UMCR_DTR)) {
660                         SET(bits, TIOCM_DTR);
661                         DPRINTF((" DTR"));
662                 }
663                 if (ISSET(mcr, UMCR_RTS)) {
664                         SET(bits, TIOCM_RTS);
665                         DPRINTF((" RTS"));
666                 }
667
668                 msr = sc->sc_msr;
669                 if (ISSET(msr, UMSR_CTS)) {
670                         SET(bits, TIOCM_CTS);
671                         DPRINTF((" CTS"));
672                 }
673                 if (ISSET(msr, UMSR_DCD)) {
674                         SET(bits, TIOCM_CD);
675                         DPRINTF((" CD"));
676                 }
677                 if (ISSET(msr, UMSR_DSR)) {
678                         SET(bits, TIOCM_DSR);
679                         DPRINTF((" DSR"));
680                 }
681                 if (ISSET(msr, UMSR_RI)) {
682                         SET(bits, TIOCM_RI);
683                         DPRINTF((" RI"));
684                 }
685
686                 DPRINTF(("\n"));
687
688                 return (bits);
689         }
690
691         mcr = 0;
692         if (ISSET(bits, TIOCM_DTR))
693                 SET(mcr, UMCR_DTR);
694         if (ISSET(bits, TIOCM_RTS))
695                 SET(mcr, UMCR_RTS);
696
697         switch (how) {
698         case DMSET:
699                 sc->sc_mcr = mcr;
700                 break;
701         case DMBIS:
702                 sc->sc_mcr |= mcr;
703                 break;
704         case DMBIC:
705                 sc->sc_mcr &= ~mcr;
706                 break;
707         }
708
709         onoff = ISSET(sc->sc_mcr, UMCR_DTR) ? 1 : 0;
710         ucom_dtr(sc, onoff);
711
712         onoff = ISSET(sc->sc_mcr, UMCR_RTS) ? 1 : 0;
713         ucom_rts(sc, onoff);
714
715         return (0);
716 }
717
718 Static void
719 ucom_break(struct ucom_softc *sc, int onoff)
720 {
721         DPRINTF(("ucom_break: onoff = %d\n", onoff));
722
723         if (sc->sc_callback->ucom_set == NULL)
724                 return;
725         sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
726                                   UCOM_SET_BREAK, onoff);
727 }
728
729 Static void
730 ucom_dtr(struct ucom_softc *sc, int onoff)
731 {
732         DPRINTF(("ucom_dtr: onoff = %d\n", onoff));
733
734         if (sc->sc_callback->ucom_set == NULL)
735                 return;
736         sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
737                                   UCOM_SET_DTR, onoff);
738 }
739
740 Static void
741 ucom_rts(struct ucom_softc *sc, int onoff)
742 {
743         DPRINTF(("ucom_rts: onoff = %d\n", onoff));
744
745         if (sc->sc_callback->ucom_set == NULL)
746                 return;
747         sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
748                                   UCOM_SET_RTS, onoff);
749 }
750
751 void
752 ucom_status_change(struct ucom_softc *sc)
753 {
754         struct tty *tp = sc->sc_tty;
755         u_char old_msr;
756         int onoff;
757
758         if (sc->sc_callback->ucom_get_status == NULL) {
759                 sc->sc_lsr = 0;
760                 sc->sc_msr = 0;
761                 return;
762         }
763
764         old_msr = sc->sc_msr;
765         sc->sc_callback->ucom_get_status(sc->sc_parent, sc->sc_portno,
766                                          &sc->sc_lsr, &sc->sc_msr);
767         if (ISSET((sc->sc_msr ^ old_msr), UMSR_DCD)) {
768                 if (sc->sc_poll == 0)
769                         return;
770                 onoff = ISSET(sc->sc_msr, UMSR_DCD) ? 1 : 0;
771                 DPRINTF(("ucom_status_change: DCD changed to %d\n", onoff));
772                 (*linesw[tp->t_line].l_modem)(tp, onoff);
773         }
774 }
775
776 Static int
777 ucomparam(struct tty *tp, struct termios *t)
778 {
779         struct ucom_softc *sc;
780         int error;
781         usbd_status uerr;
782
783         USB_GET_SC(ucom, UCOMUNIT(tp->t_dev), sc);
784
785         if (sc->sc_dying)
786                 return (EIO);
787
788         DPRINTF(("ucomparam: sc = %p\n", sc));
789
790         /* Check requested parameters. */
791         if (t->c_ospeed < 0) {
792                 DPRINTF(("ucomparam: negative ospeed\n"));
793                 return (EINVAL);
794         }
795         if (t->c_ispeed && t->c_ispeed != t->c_ospeed) {
796                 DPRINTF(("ucomparam: mismatch ispeed and ospeed\n"));
797                 return (EINVAL);
798         }
799
800         /*
801          * If there were no changes, don't do anything.  This avoids dropping
802          * input and improves performance when all we did was frob things like
803          * VMIN and VTIME.
804          */
805         if (tp->t_ospeed == t->c_ospeed &&
806             tp->t_cflag == t->c_cflag)
807                 return (0);
808
809         /* And copy to tty. */
810         tp->t_ispeed = 0;
811         tp->t_ospeed = t->c_ospeed;
812         tp->t_cflag = t->c_cflag;
813
814         if (sc->sc_callback->ucom_param == NULL)
815                 return (0);
816
817         ucomstopread(sc);
818
819         error = sc->sc_callback->ucom_param(sc->sc_parent, sc->sc_portno, t);
820         if (error) {
821                 DPRINTF(("ucomparam: callback: error = %d\n", error));
822                 return (error);
823         }
824
825         ttsetwater(tp);
826
827         if (t->c_cflag & CRTS_IFLOW) {
828                 sc->sc_state |= UCS_RTS_IFLOW;
829         } else if (sc->sc_state & UCS_RTS_IFLOW) {
830                 sc->sc_state &= ~UCS_RTS_IFLOW;
831                 (void)ucomctl(sc, UMCR_RTS, DMBIS);
832         }
833
834         disc_optim(tp, t, sc);
835
836         uerr = ucomstartread(sc);
837         if (uerr != USBD_NORMAL_COMPLETION)
838                 return (EIO);
839
840         return (0);
841 }
842
843 Static void
844 ucomstart(struct tty *tp)
845 {
846         struct ucom_softc *sc;
847         struct cblock *cbp;
848         usbd_status err;
849         u_char *data;
850         int cnt;
851
852         USB_GET_SC(ucom, UCOMUNIT(tp->t_dev), sc);
853         DPRINTF(("ucomstart: sc = %p\n", sc));
854
855         if (sc->sc_dying)
856                 return;
857
858         crit_enter();
859
860         if (tp->t_state & TS_TBLOCK) {
861                 if (ISSET(sc->sc_mcr, UMCR_RTS) &&
862                     ISSET(sc->sc_state, UCS_RTS_IFLOW)) {
863                         DPRINTF(("ucomstart: clear RTS\n"));
864                         (void)ucomctl(sc, UMCR_RTS, DMBIC);
865                 }
866         } else {
867                 if (!ISSET(sc->sc_mcr, UMCR_RTS) &&
868                     tp->t_rawq.c_cc <= tp->t_ilowat &&
869                     ISSET(sc->sc_state, UCS_RTS_IFLOW)) {
870                         DPRINTF(("ucomstart: set RTS\n"));
871                         (void)ucomctl(sc, UMCR_RTS, DMBIS);
872                 }
873         }
874
875         if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) {
876                 ttwwakeup(tp);
877                 DPRINTF(("ucomstart: stopped\n"));
878                 goto out;
879         }
880
881         if (tp->t_outq.c_cc <= tp->t_olowat) {
882                 if (ISSET(tp->t_state, TS_SO_OLOWAT)) {
883                         CLR(tp->t_state, TS_SO_OLOWAT);
884                         wakeup(TSA_OLOWAT(tp));
885                 }
886                 selwakeuppri(&tp->t_wsel, TTIPRI);
887                 if (tp->t_outq.c_cc == 0) {
888                         if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) ==
889                             TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) {
890                                 CLR(tp->t_state, TS_SO_OCOMPLETE);
891                                 wakeup(TSA_OCOMPLETE(tp));
892                         }
893                         goto out;
894                 }
895         }
896
897         /* Grab the first contiguous region of buffer space. */
898         data = tp->t_outq.c_cf;
899         cbp = (struct cblock *) ((intptr_t) tp->t_outq.c_cf & ~CROUND);
900         cnt = min((char *) (cbp+1) - tp->t_outq.c_cf, tp->t_outq.c_cc);
901
902         if (cnt == 0) {
903                 DPRINTF(("ucomstart: cnt == 0\n"));
904                 goto out;
905         }
906
907         SET(tp->t_state, TS_BUSY);
908
909         if (cnt > sc->sc_obufsize) {
910                 DPRINTF(("ucomstart: big buffer %d chars\n", cnt));
911                 cnt = sc->sc_obufsize;
912         }
913         if (sc->sc_callback->ucom_write != NULL)
914                 sc->sc_callback->ucom_write(sc->sc_parent, sc->sc_portno,
915                                             sc->sc_obuf, data, &cnt);
916         else
917                 memcpy(sc->sc_obuf, data, cnt);
918
919         DPRINTF(("ucomstart: %d chars\n", cnt));
920         usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe,
921                         (usbd_private_handle)sc, sc->sc_obuf, cnt,
922                         USBD_NO_COPY, USBD_NO_TIMEOUT, ucomwritecb);
923         /* What can we do on error? */
924         err = usbd_transfer(sc->sc_oxfer);
925         if (err != USBD_IN_PROGRESS)
926                 printf("ucomstart: err=%s\n", usbd_errstr(err));
927
928         ttwwakeup(tp);
929
930     out:
931         crit_exit();
932 }
933
934 Static void
935 ucomstop(struct tty *tp, int flag)
936 {
937         struct ucom_softc *sc;
938
939         USB_GET_SC(ucom, UCOMUNIT(tp->t_dev), sc);
940
941         DPRINTF(("ucomstop: %d\n", flag));
942
943         if (flag & FREAD) {
944                 /*
945                  * This is just supposed to flush pending receive data,
946                  * not stop the reception of data entirely!
947                  */
948                 DPRINTF(("ucomstop: read\n"));
949                 ucomstopread(sc);
950                 ucomstartread(sc);
951         }
952
953         if (flag & FWRITE) {
954                 DPRINTF(("ucomstop: write\n"));
955                 crit_enter();
956                 if (ISSET(tp->t_state, TS_BUSY)) {
957                         /* XXX do what? */
958                         if (!ISSET(tp->t_state, TS_TTSTOP))
959                                 SET(tp->t_state, TS_FLUSH);
960                 }
961                 crit_exit();
962         }
963
964         DPRINTF(("ucomstop: done\n"));
965 }
966
967 Static void
968 ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
969 {
970         struct ucom_softc *sc = (struct ucom_softc *)p;
971         struct tty *tp = sc->sc_tty;
972         u_int32_t cc;
973
974         DPRINTF(("ucomwritecb: status = %d\n", status));
975
976         if (status == USBD_CANCELLED || sc->sc_dying)
977                 goto error;
978
979         if (status != USBD_NORMAL_COMPLETION) {
980                 printf("%s: ucomwritecb: %s\n",
981                        USBDEVNAME(sc->sc_dev), usbd_errstr(status));
982                 if (status == USBD_STALLED)
983                         usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
984                 /*
985                  * XXX.  We may need a flag to sequence ucomstopread() and
986                  * ucomstartread() to handle the case where ucomstartread()
987                  * is called after ucomstopread() but before the request has
988                  * been properly canceled?
989                  */
990                 goto error;
991         }
992
993         usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
994         DPRINTF(("ucomwritecb: cc = %d\n", cc));
995         if (cc <= sc->sc_opkthdrlen) {
996                 printf("%s: sent size too small, cc = %d\n",
997                         USBDEVNAME(sc->sc_dev), cc);
998                 goto error;
999         }
1000
1001         /* convert from USB bytes to tty bytes */
1002         cc -= sc->sc_opkthdrlen;
1003
1004         crit_enter();
1005         CLR(tp->t_state, TS_BUSY);
1006         if (ISSET(tp->t_state, TS_FLUSH))
1007                 CLR(tp->t_state, TS_FLUSH);
1008         else
1009                 ndflush(&tp->t_outq, cc);
1010         (*linesw[tp->t_line].l_start)(tp);
1011         crit_exit();
1012
1013         return;
1014
1015   error:
1016         crit_enter();
1017         CLR(tp->t_state, TS_BUSY);
1018         crit_exit();
1019         return;
1020 }
1021
1022 Static usbd_status
1023 ucomstartread(struct ucom_softc *sc)
1024 {
1025         usbd_status err;
1026
1027         DPRINTF(("ucomstartread: start\n"));
1028
1029         sc->sc_state &= ~UCS_RXSTOP;
1030
1031         if (sc->sc_bulkin_pipe == NULL)
1032                 return (USBD_NORMAL_COMPLETION);
1033
1034         usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe,
1035                         (usbd_private_handle)sc,
1036                         sc->sc_ibuf, sc->sc_ibufsize,
1037                         USBD_SHORT_XFER_OK | USBD_NO_COPY,
1038                         USBD_NO_TIMEOUT, ucomreadcb);
1039
1040         err = usbd_transfer(sc->sc_ixfer);
1041         if (err != USBD_IN_PROGRESS) {
1042                 DPRINTF(("ucomstartread: err = %s\n", usbd_errstr(err)));
1043                 return (err);
1044         }
1045
1046         return (USBD_NORMAL_COMPLETION);
1047 }
1048
1049 Static void
1050 ucomreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
1051 {
1052         struct ucom_softc *sc = (struct ucom_softc *)p;
1053         struct tty *tp = sc->sc_tty;
1054         int (*rint) (int c, struct tty *tp) = linesw[tp->t_line].l_rint;
1055         usbd_status err;
1056         u_int32_t cc;
1057         u_char *cp;
1058         int lostcc;
1059
1060         DPRINTF(("ucomreadcb: status = %d\n", status));
1061
1062         if (status != USBD_NORMAL_COMPLETION) {
1063                 if (!(sc->sc_state & UCS_RXSTOP))
1064                         printf("%s: ucomreadcb: %s\n",
1065                                USBDEVNAME(sc->sc_dev), usbd_errstr(status));
1066                 if (status == USBD_STALLED)
1067                         usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
1068                 /* XXX we should restart after some delay. */
1069                 return;
1070         }
1071
1072         usbd_get_xfer_status(xfer, NULL, (void **)&cp, &cc, NULL);
1073         DPRINTF(("ucomreadcb: got %d chars, tp = %p\n", cc, tp));
1074         if (cc == 0)
1075                 goto resubmit;
1076
1077         if (sc->sc_callback->ucom_read != NULL) {
1078                 sc->sc_callback->ucom_read(sc->sc_parent, sc->sc_portno,
1079                                            &cp, &cc);
1080         }
1081
1082         if (cc > sc->sc_ibufsize) {
1083                 printf("%s: invalid receive data size, %d chars\n",
1084                         USBDEVNAME(sc->sc_dev), cc);
1085                 goto resubmit;
1086         }
1087         if (cc < 1)
1088                 goto resubmit;
1089
1090         crit_enter();
1091         if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
1092                 if (tp->t_rawq.c_cc + cc > tp->t_ihiwat
1093                     && (sc->sc_state & UCS_RTS_IFLOW
1094                         || tp->t_iflag & IXOFF)
1095                     && !(tp->t_state & TS_TBLOCK))
1096                         ttyblock(tp);
1097                 lostcc = b_to_q((char *)cp, cc, &tp->t_rawq);
1098                 tp->t_rawcc += cc;
1099                 if (sc->hotchar) {
1100                         while (cc) {
1101                                 if (*cp == sc->hotchar)
1102                                         break;
1103                                 --cc;
1104                                 ++cp;
1105                         }
1106                         if (cc)
1107                                 setsofttty();
1108                 }
1109                 ttwakeup(tp);
1110                 if (tp->t_state & TS_TTSTOP
1111                     && (tp->t_iflag & IXANY
1112                         || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
1113                         tp->t_state &= ~TS_TTSTOP;
1114                         tp->t_lflag &= ~FLUSHO;
1115                         ucomstart(tp);
1116                 }
1117                 if (lostcc > 0)
1118                         printf("%s: lost %d chars\n", USBDEVNAME(sc->sc_dev),
1119                                lostcc);
1120         } else {
1121                 /* Give characters to tty layer. */
1122                 while (cc > 0) {
1123                         DPRINTFN(7, ("ucomreadcb: char = 0x%02x\n", *cp));
1124                         if ((*rint)(*cp, tp) == -1) {
1125                                 /* XXX what should we do? */
1126                                 printf("%s: lost %d chars\n",
1127                                        USBDEVNAME(sc->sc_dev), cc);
1128                                 break;
1129                         }
1130                         cc--;
1131                         cp++;
1132                 }
1133         }
1134         crit_exit();
1135
1136 resubmit:
1137         err = ucomstartread(sc);
1138         if (err) {
1139                 printf("%s: read start failed\n", USBDEVNAME(sc->sc_dev));
1140                 /* XXX what should we dow now? */
1141         }
1142
1143         if ((sc->sc_state & UCS_RTS_IFLOW) && !ISSET(sc->sc_mcr, UMCR_RTS)
1144             && !(tp->t_state & TS_TBLOCK))
1145                 ucomctl(sc, UMCR_RTS, DMBIS);
1146 }
1147
1148 Static void
1149 ucom_cleanup(struct ucom_softc *sc)
1150 {
1151         DPRINTF(("ucom_cleanup: closing pipes\n"));
1152
1153         ucom_shutdown(sc);
1154         if (sc->sc_bulkin_pipe != NULL) {
1155                 usbd_abort_pipe(sc->sc_bulkin_pipe);
1156                 usbd_close_pipe(sc->sc_bulkin_pipe);
1157                 sc->sc_bulkin_pipe = NULL;
1158         }
1159         if (sc->sc_bulkout_pipe != NULL) {
1160                 usbd_abort_pipe(sc->sc_bulkout_pipe);
1161                 usbd_close_pipe(sc->sc_bulkout_pipe);
1162                 sc->sc_bulkout_pipe = NULL;
1163         }
1164         if (sc->sc_ixfer != NULL) {
1165                 usbd_free_xfer(sc->sc_ixfer);
1166                 sc->sc_ixfer = NULL;
1167         }
1168         if (sc->sc_oxfer != NULL) {
1169                 usbd_free_xfer(sc->sc_oxfer);
1170                 sc->sc_oxfer = NULL;
1171         }
1172 }
1173
1174 Static void
1175 ucomstopread(struct ucom_softc *sc)
1176 {
1177         usbd_status err;
1178
1179         DPRINTF(("ucomstopread: enter\n"));
1180
1181         if (!(sc->sc_state & UCS_RXSTOP)) {
1182                 sc->sc_state |= UCS_RXSTOP;
1183                 if (sc->sc_bulkin_pipe == NULL) {
1184                         DPRINTF(("ucomstopread: bulkin pipe NULL\n"));
1185                         return;
1186                 }
1187                 err = usbd_abort_pipe(sc->sc_bulkin_pipe);
1188                 if (err) {
1189                         DPRINTF(("ucomstopread: err = %s\n",
1190                                  usbd_errstr(err)));
1191                 }
1192         }
1193
1194         DPRINTF(("ucomstopread: leave\n"));
1195 }
1196
1197 static void
1198 disc_optim(struct tty *tp, struct termios *t, struct ucom_softc *sc)
1199 {
1200         if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
1201             && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
1202             && (!(t->c_iflag & PARMRK)
1203                 || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
1204             && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
1205             && linesw[tp->t_line].l_rint == ttyinput) {
1206                 DPRINTF(("disc_optim: bypass l_rint\n"));
1207                 tp->t_state |= TS_CAN_BYPASS_L_RINT;
1208         } else {
1209                 DPRINTF(("disc_optim: can't bypass l_rint\n"));
1210                 tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
1211         }
1212         sc->hotchar = linesw[tp->t_line].l_hotchar;
1213 }