Merge branch 'vendor/OPENSSL'
[dragonfly.git] / sys / bus / u4b / serial / usb_serial.c
1 /*      $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $  */
2
3 /*-
4  * Copyright (c) 2001-2003, 2005, 2008
5  *      Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 /*-
31  * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
32  * All rights reserved.
33  *
34  * This code is derived from software contributed to The NetBSD Foundation
35  * by Lennart Augustsson (lennart@augustsson.net) at
36  * Carlstedt Research & Technology.
37  *
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions
40  * are met:
41  * 1. Redistributions of source code must retain the above copyright
42  *    notice, this list of conditions and the following disclaimer.
43  * 2. Redistributions in binary form must reproduce the above copyright
44  *    notice, this list of conditions and the following disclaimer in the
45  *    documentation and/or other materials provided with the distribution.
46  * 3. All advertising materials mentioning features or use of this software
47  *    must display the following acknowledgement:
48  *        This product includes software developed by the NetBSD
49  *        Foundation, Inc. and its contributors.
50  * 4. Neither the name of The NetBSD Foundation nor the names of its
51  *    contributors may be used to endorse or promote products derived
52  *    from this software without specific prior written permission.
53  *
54  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
55  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
56  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
57  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
58  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
59  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
60  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
61  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
62  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
63  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
64  * POSSIBILITY OF SUCH DAMAGE.
65  */
66
67 #include <sys/stdint.h>
68 #include <sys/param.h>
69 #include <sys/queue.h>
70 #include <sys/types.h>
71 #include <sys/systm.h>
72 #include <sys/kernel.h>
73 #include <sys/bus.h>
74 #include <sys/module.h>
75 #include <sys/lock.h>
76 #include <sys/condvar.h>
77 #include <sys/sysctl.h>
78 #include <sys/unistd.h>
79 #include <sys/callout.h>
80 #include <sys/malloc.h>
81 #include <sys/priv.h>
82 #include <sys/cons.h>
83 #include <sys/serial.h>
84 #include <sys/thread2.h>
85 #include <sys/conf.h>
86  
87 #include <bus/u4b/usb.h>
88 #include <bus/u4b/usbdi.h>
89 #include <bus/u4b/usbdi_util.h>
90
91 #define USB_DEBUG_VAR ucom_debug
92 #include <bus/u4b/usb_debug.h>
93 #include <bus/u4b/usb_busdma.h>
94 #include <bus/u4b/usb_process.h>
95
96 #include <bus/u4b/serial/usb_serial.h>
97
98 //#include "opt_gdb.h"
99
100 static SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
101
102 #ifdef USB_DEBUG
103 static int ucom_debug = 0;
104
105 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
106     &ucom_debug, 0, "ucom debug level");
107 #endif
108
109 #define UCOM_CONS_BUFSIZE 1024
110
111 #if 0
112 static uint8_t ucom_cons_rx_buf[UCOM_CONS_BUFSIZE];
113 static uint8_t ucom_cons_tx_buf[UCOM_CONS_BUFSIZE];
114 #endif
115
116 static unsigned int ucom_cons_rx_low = 0;
117 static unsigned int ucom_cons_rx_high = 0;
118
119 static unsigned int ucom_cons_tx_low = 0;
120 static unsigned int ucom_cons_tx_high = 0;
121
122 static int ucom_cons_unit = -1;
123 static int ucom_cons_subunit = 0;
124 static int ucom_cons_baud = 9600;
125 static struct ucom_softc *ucom_cons_softc = NULL;
126
127 TUNABLE_INT("hw.usb.ucom.cons_unit", &ucom_cons_unit);
128 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_unit, CTLFLAG_RW,
129     &ucom_cons_unit, 0, "console unit number");
130 TUNABLE_INT("hw.usb.ucom.cons_subunit", &ucom_cons_subunit);
131 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_subunit, CTLFLAG_RW,
132     &ucom_cons_subunit, 0, "console subunit number");
133 TUNABLE_INT("hw.usb.ucom.cons_baud", &ucom_cons_baud);
134 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_baud, CTLFLAG_RW,
135     &ucom_cons_baud, 0, "console baud rate");
136
137 static usb_proc_callback_t ucom_cfg_start_transfers;
138 static usb_proc_callback_t ucom_cfg_open;
139 static usb_proc_callback_t ucom_cfg_close;
140 static usb_proc_callback_t ucom_cfg_line_state;
141 static usb_proc_callback_t ucom_cfg_status_change;
142 static usb_proc_callback_t ucom_cfg_param;
143
144 static int      ucom_unit_alloc(void);
145 static void     ucom_unit_free(int);
146 static int      ucom_attach_tty(struct ucom_super_softc *, struct ucom_softc *);
147 static void     ucom_detach_tty(struct ucom_super_softc *, struct ucom_softc *);
148 static void     ucom_queue_command(struct ucom_softc *,
149                     usb_proc_callback_t *, struct termios *pt,
150                     struct usb_proc_msg *t0, struct usb_proc_msg *t1);
151 static void     ucom_shutdown(struct ucom_softc *);
152 static void     ucom_ring(struct ucom_softc *, uint8_t);
153 static void     ucom_break(struct ucom_softc *, uint8_t);
154 static void     ucom_dtr(struct ucom_softc *, uint8_t);
155 static void     ucom_rts(struct ucom_softc *, uint8_t);
156
157 static int ucom_open(struct ucom_softc *sc);
158 static int ucom_close(struct ucom_softc *sc);
159 static void ucom_start(struct tty *tp);
160 static void ucom_stop(struct tty *tp, int);
161 static int ucom_param(struct tty *tp, struct termios *t);
162 static int ucom_modem(struct tty *tp, int sigon, int sigoff);
163
164 static d_open_t ucom_dev_open;
165 static d_close_t ucom_dev_close;
166 static d_read_t ucom_dev_read;
167 static d_write_t ucom_dev_write;
168 static d_ioctl_t ucom_dev_ioctl;
169
170 static struct dev_ops ucom_ops = {
171   { "ucom", 0, D_MPSAFE | D_TTY },
172   .d_open =       ucom_dev_open,
173   .d_close =      ucom_dev_close,
174   .d_read =       ucom_dev_read,
175   .d_write =      ucom_dev_write,
176   .d_ioctl =      ucom_dev_ioctl,
177   .d_kqfilter =   ttykqfilter,
178   .d_revoke =     ttyrevoke
179 };
180
181 devclass_t ucom_devclass;
182
183 static moduledata_t ucom_mod = {
184         "ucom",
185         NULL,
186         NULL
187 };
188
189 DECLARE_MODULE(ucom, ucom_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
190 MODULE_DEPEND(ucom, usb, 1, 1, 1);
191 MODULE_VERSION(ucom, UCOM_MODVER);
192
193 #if 0 /* XXXDF */
194 static tsw_open_t ucom_open;
195 static tsw_close_t ucom_close;
196 static tsw_ioctl_t ucom_ioctl;
197 static tsw_modem_t ucom_modem;
198 static tsw_param_t ucom_param;
199 static tsw_outwakeup_t ucom_outwakeup;
200 static tsw_inwakeup_t ucom_inwakeup;
201 static tsw_free_t ucom_free;
202
203 static struct ttydevsw ucom_class = {
204         .tsw_flags = TF_INITLOCK | TF_CALLOUT,
205         .tsw_open = ucom_open,
206         .tsw_close = ucom_close,
207         .tsw_outwakeup = ucom_outwakeup,
208         .tsw_inwakeup = ucom_inwakeup,
209         .tsw_ioctl = ucom_ioctl,
210         .tsw_param = ucom_param,
211         .tsw_modem = ucom_modem,
212         .tsw_free = ucom_free,
213 };
214 #endif
215
216 #define UCOM_UNIT_MAX           128     /* maximum number of units */
217 #define UCOM_TTY_PREFIX         "ucom"
218
219 static struct unrhdr *ucom_unrhdr;
220 static struct lock ucom_lock;
221 static int ucom_close_refs;
222
223 static void
224 ucom_init(void *arg)
225 {
226         DPRINTF("\n");
227         kprintf("ucom init\n");
228         ucom_unrhdr = new_unrhdr(0, UCOM_UNIT_MAX - 1, NULL);
229         lockinit(&ucom_lock, "UCOM LOCK", 0, 0);
230 }
231 SYSINIT(ucom_init, SI_BOOT2_KLD - 1, SI_ORDER_ANY, ucom_init, NULL);
232
233 static void
234 ucom_uninit(void *arg)
235 {
236         struct unrhdr *hdr;
237         hdr = ucom_unrhdr;
238         ucom_unrhdr = NULL;
239
240         DPRINTF("\n");
241
242         if (hdr != NULL)
243                 delete_unrhdr(hdr);
244
245         lockuninit(&ucom_lock);
246 }
247 SYSUNINIT(ucom_uninit, SI_BOOT2_KLD - 2, SI_ORDER_ANY, ucom_uninit, NULL);
248
249 /*
250  * Mark a unit number (the X in cuaUX) as in use.
251  *
252  * Note that devices using a different naming scheme (see ucom_tty_name()
253  * callback) still use this unit allocation.
254  */
255 static int
256 ucom_unit_alloc(void)
257 {
258         int unit;
259
260         /* sanity checks */
261         if (ucom_unrhdr == NULL) {
262                 DPRINTF("ucom_unrhdr is NULL\n");
263                 return (-1);
264         }
265         unit = alloc_unr(ucom_unrhdr);
266         DPRINTF("unit %d is allocated\n", unit);
267         return (unit);
268 }
269
270 /*
271  * Mark the unit number as not in use.
272  */
273 static void
274 ucom_unit_free(int unit)
275 {
276         /* sanity checks */
277         if (unit < 0 || unit >= UCOM_UNIT_MAX || ucom_unrhdr == NULL) {
278                 DPRINTF("cannot free unit number\n");
279                 return;
280         }
281         DPRINTF("unit %d is freed\n", unit);
282         free_unr(ucom_unrhdr, unit);
283 }
284
285 /*
286  * Setup a group of one or more serial ports.
287  *
288  * The lock pointed to by "lock" is applied before all
289  * callbacks are called back. Also "lock" must be applied
290  * before calling into the ucom-layer!
291  */
292 int
293 ucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
294     int subunits, void *parent,
295     const struct ucom_callback *callback, struct lock *lock)
296 {
297         int subunit;
298         int error = 0;
299
300         if ((sc == NULL) ||
301             (subunits <= 0) ||
302             (callback == NULL) ||
303             (lock == NULL)) {
304                 return (EINVAL);
305         }
306
307         /* XXX Do we want our own lock here maybe */
308         sc->sc_lock = lock;
309
310         /* allocate a uniq unit number */
311         ssc->sc_unit = ucom_unit_alloc();
312         if (ssc->sc_unit == -1)
313                 return (ENOMEM);
314
315         /* generate TTY name string */
316         ksnprintf(ssc->sc_ttyname, sizeof(ssc->sc_ttyname),
317             UCOM_TTY_PREFIX "%d", ssc->sc_unit);
318
319         /* create USB request handling process */
320         error = usb_proc_create(&ssc->sc_tq, lock, "ucom", USB_PRI_MED);
321         if (error) {
322                 ucom_unit_free(ssc->sc_unit);
323                 return (error);
324         }
325         ssc->sc_subunits = subunits;
326         ssc->sc_flag = UCOM_FLAG_ATTACHED |
327             UCOM_FLAG_FREE_UNIT;
328
329         if (callback->ucom_free == NULL)
330                 ssc->sc_flag |= UCOM_FLAG_WAIT_REFS;
331
332         /* increment reference count */
333         ucom_ref(ssc);
334
335         for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
336                 sc[subunit].sc_subunit = subunit;
337                 sc[subunit].sc_super = ssc;
338                 sc[subunit].sc_lock = lock;
339                 sc[subunit].sc_parent = parent;
340                 sc[subunit].sc_callback = callback;
341
342                 error = ucom_attach_tty(ssc, &sc[subunit]);
343                 if (error) {
344                         ucom_detach(ssc, &sc[0]);
345                         return (error);
346                 }
347                 /* increment reference count */
348                 ucom_ref(ssc);
349
350                 /* set subunit attached */
351                 sc[subunit].sc_flag |= UCOM_FLAG_ATTACHED;
352         }
353
354         DPRINTF("tp = %p, unit = %d, subunits = %d\n",
355                 sc->sc_tty, ssc->sc_unit, ssc->sc_subunits);
356
357         return (0);
358 }
359
360 /*
361  * The following function will do nothing if the structure pointed to
362  * by "ssc" and "sc" is zero or has already been detached.
363  */
364 void
365 ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc)
366 {
367         int subunit;
368
369         if (!(ssc->sc_flag & UCOM_FLAG_ATTACHED))
370                 return;         /* not initialized */
371
372         if (ssc->sc_sysctl_ttyname != NULL) {
373                 sysctl_remove_oid(ssc->sc_sysctl_ttyname, 1, 0);
374                 ssc->sc_sysctl_ttyname = NULL;
375         }
376
377         if (ssc->sc_sysctl_ttyports != NULL) {
378                 sysctl_remove_oid(ssc->sc_sysctl_ttyports, 1, 0);
379                 ssc->sc_sysctl_ttyports = NULL;
380         }
381
382         usb_proc_drain(&ssc->sc_tq);
383
384         for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
385                 if (sc[subunit].sc_flag & UCOM_FLAG_ATTACHED) {
386
387                         ucom_detach_tty(ssc, &sc[subunit]);
388
389                         /* avoid duplicate detach */
390                         sc[subunit].sc_flag &= ~UCOM_FLAG_ATTACHED;
391                 }
392         }
393         usb_proc_free(&ssc->sc_tq);
394
395         ucom_unref(ssc);
396
397         if (ssc->sc_flag & UCOM_FLAG_WAIT_REFS)
398                 ucom_drain(ssc);
399
400         /* make sure we don't detach twice */
401         ssc->sc_flag &= ~UCOM_FLAG_ATTACHED;
402 }
403
404 void
405 ucom_drain(struct ucom_super_softc *ssc)
406 {
407         lockmgr(&ucom_lock, LK_EXCLUSIVE);
408         while (ssc->sc_refs > 0) {
409                 kprintf("ucom: Waiting for a TTY device to close.\n");
410                 usb_pause_mtx(&ucom_lock, hz);
411         }
412         lockmgr(&ucom_lock, LK_RELEASE);
413 }
414
415 void
416 ucom_drain_all(void *arg)
417 {
418         lockmgr(&ucom_lock, LK_EXCLUSIVE);
419         while (ucom_close_refs > 0) {
420                 kprintf("ucom: Waiting for all detached TTY "
421                     "devices to have open fds closed.\n");
422                 usb_pause_mtx(&ucom_lock, hz);
423         }
424         lockmgr(&ucom_lock, LK_RELEASE);
425 }
426
427 static int
428 ucom_attach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
429 {
430         struct tty *tp;
431         char buf[32];                   /* temporary TTY device name buffer */
432         cdev_t dev;
433
434         kprintf("attach tty\n");
435
436         lwkt_gettoken(&tty_token);
437         
438         kprintf("malloc: ");
439         sc->sc_tty = tp = ttymalloc(sc->sc_tty);
440
441         tp->t_oproc = ucom_start;
442         tp->t_param = ucom_param;
443         tp->t_stop = ucom_stop;
444         
445         if (tp == NULL) {
446                 lwkt_reltoken(&tty_token);
447                 return (ENOMEM);
448         }
449
450         /* Check if the client has a custom TTY name */
451         buf[0] = '\0';
452         if (sc->sc_callback->ucom_tty_name) {
453                 sc->sc_callback->ucom_tty_name(sc, buf,
454                     sizeof(buf), ssc->sc_unit, sc->sc_subunit);
455         }
456         if (buf[0] == 0) {
457                 /* Use default TTY name */
458                 if (ssc->sc_subunits > 1) {
459                         /* multiple modems in one */
460                         ksnprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u.%u",
461                             ssc->sc_unit, sc->sc_subunit);
462                 } else {
463                         /* single modem */
464                         ksnprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u",
465                             ssc->sc_unit);
466                 }
467         }
468
469         dev = make_dev(&ucom_ops, ssc->sc_unit | 0x80, // XXX UCOM_CALLOUT_MASK,
470                         UID_UUCP, GID_DIALER, 0660,
471                         buf, ssc->sc_unit);
472         dev->si_tty = tp;
473         sc->sc_tty = tp;
474         dev->si_drv1 = sc;
475         
476         DPRINTF("ttycreate: %s\n", buf);
477
478         /* Check if this device should be a console */
479         if ((ucom_cons_softc == NULL) && 
480             (ssc->sc_unit == ucom_cons_unit) &&
481             (sc->sc_subunit == ucom_cons_subunit)) {
482
483                 DPRINTF("unit %d subunit %d is console",
484                     ssc->sc_unit, sc->sc_subunit);
485
486                 ucom_cons_softc = sc;
487
488                 /* XXXDF
489                 tty_init_console(tp, ucom_cons_baud);
490                 */
491                 tp->t_termios.c_ispeed = ucom_cons_baud;
492                 tp->t_termios.c_ospeed = ucom_cons_baud;
493
494                 UCOM_MTX_LOCK(ucom_cons_softc);
495                 ucom_cons_rx_low = 0;
496                 ucom_cons_rx_high = 0;
497                 ucom_cons_tx_low = 0;
498                 ucom_cons_tx_high = 0;
499                 sc->sc_flag |= UCOM_FLAG_CONSOLE;
500                 
501                 ucom_open(ucom_cons_softc);
502                 ucom_param(tp, &tp->t_termios);
503                 UCOM_MTX_UNLOCK(ucom_cons_softc);
504         }
505
506         lwkt_reltoken(&tty_token);
507         return (0);
508 }
509
510 static void
511 ucom_detach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
512 {
513         struct tty *tp = sc->sc_tty;
514
515         DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
516
517         if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
518                 UCOM_MTX_LOCK(ucom_cons_softc);
519                 ucom_close(ucom_cons_softc);
520                 sc->sc_flag &= ~UCOM_FLAG_CONSOLE;
521                 UCOM_MTX_UNLOCK(ucom_cons_softc);
522                 ucom_cons_softc = NULL;
523         }
524
525         /* the config thread has been stopped when we get here */
526
527         UCOM_MTX_LOCK(sc);
528         sc->sc_flag |= UCOM_FLAG_GONE;
529         sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_LL_READY);
530         UCOM_MTX_UNLOCK(sc);
531
532         if (tp) {
533                 lockmgr(&ucom_lock, LK_EXCLUSIVE);
534                 ucom_close_refs++;
535                 lockmgr(&ucom_lock, LK_RELEASE);
536
537                 /*
538                 tty_lock(tp);
539                 */
540
541                 ucom_close(sc); /* close, if any */
542
543                 /*tty_rel_gone(tp);
544                  */
545
546                 UCOM_MTX_LOCK(sc);
547                 /*
548                  * make sure that read and write transfers are stopped
549                  */
550                 if (sc->sc_callback->ucom_stop_read) {
551                         (sc->sc_callback->ucom_stop_read) (sc);
552                 }
553                 if (sc->sc_callback->ucom_stop_write) {
554                         (sc->sc_callback->ucom_stop_write) (sc);
555                 }
556                 UCOM_MTX_UNLOCK(sc);
557         }
558 }
559
560 void
561 ucom_set_pnpinfo_usb(struct ucom_super_softc *ssc, device_t dev)
562 {
563         char buf[64];
564         uint8_t iface_index;
565         struct usb_attach_arg *uaa;
566
567         ksnprintf(buf, sizeof(buf), "ttyname=" UCOM_TTY_PREFIX
568             "%d ttyports=%d", ssc->sc_unit, ssc->sc_subunits);
569
570         /* Store the PNP info in the first interface for the device */
571         uaa = device_get_ivars(dev);
572         iface_index = uaa->info.bIfaceIndex;
573     
574         if (usbd_set_pnpinfo(uaa->device, iface_index, buf) != 0)
575                 device_printf(dev, "Could not set PNP info\n");
576
577         /*
578          * The following information is also replicated in the PNP-info
579          * string which is registered above:
580          */
581         if (ssc->sc_sysctl_ttyname == NULL) {
582                 /*
583                 ssc->sc_sysctl_ttyname = SYSCTL_ADD_STRING(NULL,
584                     SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
585                     OID_AUTO, "ttyname", CTLFLAG_RD, ssc->sc_ttyname, 0,
586                     "TTY device basename");
587                 */
588         }
589         if (ssc->sc_sysctl_ttyports == NULL) {
590                 /*
591                 ssc->sc_sysctl_ttyports = SYSCTL_ADD_INT(NULL,
592                     SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
593                     OID_AUTO, "ttyports", CTLFLAG_RD,
594                     NULL, ssc->sc_subunits, "Number of ports");
595                 */
596         }
597 }
598
599 static void
600 ucom_queue_command(struct ucom_softc *sc,
601     usb_proc_callback_t *fn, struct termios *pt,
602     struct usb_proc_msg *t0, struct usb_proc_msg *t1)
603 {
604         struct ucom_super_softc *ssc = sc->sc_super;
605         struct ucom_param_task *task;
606
607         UCOM_MTX_ASSERT(sc, MA_OWNED);
608
609         if (usb_proc_is_gone(&ssc->sc_tq)) {
610                 DPRINTF("proc is gone\n");
611                 return;         /* nothing to do */
612         }
613         /* 
614          * NOTE: The task cannot get executed before we drop the
615          * "sc_lock" lock. It is safe to update fields in the message
616          * structure after that the message got queued.
617          */
618         task = (struct ucom_param_task *)
619           usb_proc_msignal(&ssc->sc_tq, t0, t1);
620
621         /* Setup callback and softc pointers */
622         task->hdr.pm_callback = fn;
623         task->sc = sc;
624
625         /* 
626          * Make a copy of the termios. This field is only present if
627          * the "pt" field is not NULL.
628          */
629         if (pt != NULL)
630                 task->termios_copy = *pt;
631
632         /*
633          * Closing the device should be synchronous.
634          */
635         if (fn == ucom_cfg_close)
636                 usb_proc_mwait(&ssc->sc_tq, t0, t1);
637
638         /*
639          * In case of multiple configure requests,
640          * keep track of the last one!
641          */
642         if (fn == ucom_cfg_start_transfers)
643                 sc->sc_last_start_xfer = &task->hdr;
644 }
645
646 static void
647 ucom_shutdown(struct ucom_softc *sc)
648 {
649         struct tty *tp = sc->sc_tty;
650
651         UCOM_MTX_ASSERT(sc, MA_OWNED);
652
653         DPRINTF("\n");
654
655         /*
656          * Hang up if necessary:
657          */
658         if (tp->t_termios.c_cflag & HUPCL) {
659                 ucom_modem(tp, 0, SER_DTR);
660         }
661 }
662
663 /*
664  * Return values:
665  *    0: normal
666  * else: taskqueue is draining or gone
667  */
668 uint8_t
669 ucom_cfg_is_gone(struct ucom_softc *sc)
670 {
671         struct ucom_super_softc *ssc = sc->sc_super;
672
673         return (usb_proc_is_gone(&ssc->sc_tq));
674 }
675
676 static void
677 ucom_cfg_start_transfers(struct usb_proc_msg *_task)
678 {
679         struct ucom_cfg_task *task = 
680             (struct ucom_cfg_task *)_task;
681         struct ucom_softc *sc = task->sc;
682
683         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
684                 return;
685         }
686         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
687                 /* TTY device closed */
688                 return;
689         }
690
691         if (_task == sc->sc_last_start_xfer)
692                 sc->sc_flag |= UCOM_FLAG_GP_DATA;
693
694         if (sc->sc_callback->ucom_start_read) {
695                 (sc->sc_callback->ucom_start_read) (sc);
696         }
697         if (sc->sc_callback->ucom_start_write) {
698                 (sc->sc_callback->ucom_start_write) (sc);
699         }
700 }
701
702 static void
703 ucom_start_transfers(struct ucom_softc *sc)
704 {
705         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
706                 return;
707         }
708         /*
709          * Make sure that data transfers are started in both
710          * directions:
711          */
712         if (sc->sc_callback->ucom_start_read) {
713                 (sc->sc_callback->ucom_start_read) (sc);
714         }
715         if (sc->sc_callback->ucom_start_write) {
716                 (sc->sc_callback->ucom_start_write) (sc);
717         }
718 }
719
720 static void
721 ucom_cfg_open(struct usb_proc_msg *_task)
722 {
723         struct ucom_cfg_task *task = 
724             (struct ucom_cfg_task *)_task;
725         struct ucom_softc *sc = task->sc;
726
727         DPRINTF("\n");
728
729         if (sc->sc_flag & UCOM_FLAG_LL_READY) {
730
731                 /* already opened */
732
733         } else {
734
735                 sc->sc_flag |= UCOM_FLAG_LL_READY;
736
737                 if (sc->sc_callback->ucom_cfg_open) {
738                         (sc->sc_callback->ucom_cfg_open) (sc);
739
740                         /* wait a little */
741                         usb_pause_mtx(sc->sc_lock, hz / 10);
742                 }
743         }
744 }
745
746 static int
747 ucom_dev_open(struct dev_open_args *ap)
748 {
749         cdev_t dev = ap->a_head.a_dev;
750         struct ucom_softc *sc = (struct ucom_softc *)dev->si_drv1;
751         int error;
752
753         UCOM_MTX_LOCK(sc);
754         error = ucom_open(sc);
755         UCOM_MTX_UNLOCK(sc);
756
757         return error;   
758 }
759
760 static int
761 ucom_open(struct ucom_softc *sc)
762 {
763         int error;
764
765         if (sc->sc_flag & UCOM_FLAG_GONE) {
766                 return (ENXIO);
767         }
768         if (sc->sc_flag & UCOM_FLAG_HL_READY) {
769                 /* already opened */
770                 return (0);
771         }
772         DPRINTF("tp = %p\n", sc->sc_tty);
773
774         if (sc->sc_callback->ucom_pre_open) {
775                 /*
776                  * give the lower layer a chance to disallow TTY open, for
777                  * example if the device is not present:
778                  */
779                 error = (sc->sc_callback->ucom_pre_open) (sc);
780                 if (error) {
781                         return (error);
782                 }
783         }
784         sc->sc_flag |= UCOM_FLAG_HL_READY;
785
786         /* Disable transfers */
787         sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
788
789         sc->sc_lsr = 0;
790         sc->sc_msr = 0;
791         sc->sc_mcr = 0;
792
793         /* reset programmed line state */
794         sc->sc_pls_curr = 0;
795         sc->sc_pls_set = 0;
796         sc->sc_pls_clr = 0;
797
798         /* reset jitter buffer */
799         sc->sc_jitterbuf_in = 0;
800         sc->sc_jitterbuf_out = 0;
801
802         ucom_queue_command(sc, ucom_cfg_open, NULL,
803             &sc->sc_open_task[0].hdr,
804             &sc->sc_open_task[1].hdr);
805
806         /* Queue transfer enable command last */
807         ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
808             &sc->sc_start_task[0].hdr, 
809             &sc->sc_start_task[1].hdr);
810
811         ucom_modem(sc->sc_tty, SER_DTR | SER_RTS, 0);
812
813         ucom_ring(sc, 0);
814
815         ucom_break(sc, 0);
816
817         ucom_status_change(sc);
818
819         return (0);
820 }
821
822 static void
823 ucom_cfg_close(struct usb_proc_msg *_task)
824 {
825         struct ucom_cfg_task *task = 
826             (struct ucom_cfg_task *)_task;
827         struct ucom_softc *sc = task->sc;
828
829         DPRINTF("\n");
830
831         if (sc->sc_flag & UCOM_FLAG_LL_READY) {
832                 sc->sc_flag &= ~UCOM_FLAG_LL_READY;
833                 if (sc->sc_callback->ucom_cfg_close)
834                         (sc->sc_callback->ucom_cfg_close) (sc);
835         } else {
836                 /* already closed */
837         }
838 }
839
840 static int
841 ucom_dev_close(struct dev_close_args *ap)
842 {
843         cdev_t dev = ap->a_head.a_dev;
844         struct ucom_softc *sc = (struct ucom_softc *)dev->si_drv1;
845         int error;
846
847         UCOM_MTX_LOCK(sc);
848         error = ucom_close(sc);
849         UCOM_MTX_UNLOCK(sc);
850
851         return error;
852 }
853
854 static int
855 ucom_close(struct ucom_softc *sc)
856 {
857 #ifdef USB_DEBUG
858         struct tty *tp = sc->sc_tty;
859 #endif
860         int error = 0;
861
862         DPRINTF("tp=%p\n", tp);
863
864         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
865                 DPRINTF("tp=%p already closed\n", tp);
866                 UCOM_MTX_UNLOCK(sc);
867                 return (error);
868         }
869         ucom_shutdown(sc);
870
871         ucom_queue_command(sc, ucom_cfg_close, NULL,
872             &sc->sc_close_task[0].hdr,
873             &sc->sc_close_task[1].hdr);
874
875         sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW);
876
877         if (sc->sc_callback->ucom_stop_read) {
878                 (sc->sc_callback->ucom_stop_read) (sc);
879         }
880
881         return (error);
882 }
883
884 #if 0 /* XXX */
885 static void
886 ucom_inwakeup(struct tty *tp)
887 {
888         struct ucom_softc *sc = tty_softc(tp);
889         uint16_t pos;
890
891         if (sc == NULL)
892                 return;
893
894         UCOM_MTX_ASSERT(sc, MA_OWNED);
895
896         DPRINTF("tp=%p\n", tp);
897
898         if (ttydisc_can_bypass(tp) != 0 || 
899             (sc->sc_flag & UCOM_FLAG_HL_READY) == 0 ||
900             (sc->sc_flag & UCOM_FLAG_INWAKEUP) != 0) {
901                 return;
902         }
903
904         /* prevent recursion */
905         sc->sc_flag |= UCOM_FLAG_INWAKEUP;
906
907         pos = sc->sc_jitterbuf_out;
908
909         while (sc->sc_jitterbuf_in != pos) {
910                 int c;
911
912                 c = (char)sc->sc_jitterbuf[pos];
913
914                 if (ttydisc_rint(tp, c, 0) == -1)
915                         break;
916                 pos++;
917                 if (pos >= UCOM_JITTERBUF_SIZE)
918                         pos -= UCOM_JITTERBUF_SIZE;
919         }
920
921         sc->sc_jitterbuf_out = pos;
922
923         /* clear RTS in async fashion */
924         if ((sc->sc_jitterbuf_in == pos) && 
925             (sc->sc_flag & UCOM_FLAG_RTS_IFLOW))
926                 ucom_rts(sc, 0);
927
928         sc->sc_flag &= ~UCOM_FLAG_INWAKEUP;
929 }
930 #endif
931
932 static int
933 ucom_dev_read(struct dev_read_args *ap)
934 {
935         cdev_t dev = ap->a_head.a_dev;
936         struct ucom_softc *sc;
937         struct tty *tp;
938         int error;
939
940         sc = devclass_get_softc(ucom_devclass, minor(dev));
941         lwkt_gettoken(&tty_token);
942         tp = sc->sc_tty;
943
944         DPRINTF("ucomread: tp = %p, flag = 0x%x\n", tp, ap->a_ioflag);
945
946 #if 0 /* XXXDF */
947         if (sc->sc_dying) {
948                 lwkt_reltoken(&tty_token);
949                 return (EIO);
950         }
951 #endif
952         error = (*linesw[tp->t_line].l_read)(tp, ap->a_uio, ap->a_ioflag);
953
954         DPRINTF("ucomread: error = %d\n", error);
955
956         lwkt_reltoken(&tty_token);
957         return (error);
958 }
959
960 static int
961 ucom_dev_write(struct dev_write_args *ap)
962 {
963         cdev_t dev = ap->a_head.a_dev;
964         struct ucom_softc *sc;
965         struct tty *tp;
966         int error;
967
968         sc = devclass_get_softc(ucom_devclass, minor(dev));
969         lwkt_gettoken(&tty_token);
970         tp = sc->sc_tty;
971
972         DPRINTF("ucomwrite: tp = %p, flag = 0x%x\n", tp, ap->a_ioflag);
973
974 #if 0 /* XXXDF */
975         if (sc->sc_dying) {
976                 lwkt_reltoken(&tty_token);
977                 return (EIO);
978         }
979 #endif
980
981         error = (*linesw[tp->t_line].l_write)(tp, ap->a_uio, ap->a_ioflag);
982
983         DPRINTF("ucomwrite: error = %d\n", error);
984
985         lwkt_reltoken(&tty_token);
986         return (error);
987 }
988
989 static int
990 ucom_dev_ioctl(struct dev_ioctl_args *ap)
991 {
992         cdev_t dev = ap->a_head.a_dev;
993         struct ucom_softc *sc = (struct ucom_softc *)dev->si_drv1;
994         u_long cmd = ap->a_cmd;
995         caddr_t data = ap->a_data;
996         struct tty *tp = sc->sc_tty;
997         int error;
998
999         UCOM_MTX_LOCK(sc);
1000         lwkt_gettoken(&tty_token);
1001
1002         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1003                 lwkt_reltoken(&tty_token);
1004                 return (EIO);
1005         }
1006         DPRINTF("cmd = 0x%08lx\n", cmd);
1007
1008         error = (*linesw[tp->t_line].l_ioctl)(tp, ap->a_cmd, ap->a_data,
1009                                               ap->a_fflag, ap->a_cred);
1010
1011         if (error != ENOIOCTL) {
1012                 DPRINTF("ucomioctl: l_ioctl: error = %d\n", error);
1013                 lwkt_reltoken(&tty_token);
1014                 UCOM_MTX_UNLOCK(sc);
1015                 return (error);
1016         }
1017
1018         crit_enter();
1019
1020         error = ttioctl(tp, ap->a_cmd, ap->a_data, ap->a_fflag);
1021         /*disc_optim(tp, &tp->t_termios, sc); */
1022         if (error != ENOIOCTL) {
1023                 crit_exit();
1024                 DPRINTF("ucomioctl: ttioctl: error = %d\n", error);
1025
1026                 lwkt_reltoken(&tty_token);
1027                 UCOM_MTX_UNLOCK(sc);
1028
1029                 return (error);
1030         }
1031
1032
1033         switch (cmd) {
1034 #if 0 /* XXXDF */
1035         case TIOCSRING:
1036                 ucom_ring(sc, 1);
1037                 error = 0;
1038                 break;
1039         case TIOCCRING:
1040                 ucom_ring(sc, 0);
1041                 error = 0;
1042                 break;
1043 #endif
1044         case TIOCSBRK:
1045                 ucom_break(sc, 1);
1046                 error = 0;
1047                 break;
1048         case TIOCCBRK:
1049                 ucom_break(sc, 0);
1050                 error = 0;
1051                 break;
1052         default:
1053                 if (sc->sc_callback->ucom_ioctl) {
1054                         error = (sc->sc_callback->ucom_ioctl)
1055                             (sc, cmd, data, 0, curthread);
1056                         if (error>=0) {
1057                                 crit_exit();
1058
1059                                 lwkt_reltoken(&tty_token);
1060                                 UCOM_MTX_UNLOCK(sc);
1061
1062                                 return(error);  
1063                         }
1064                 } else {
1065                         error = ENOIOCTL;
1066                 }
1067                 break;
1068         }
1069         crit_exit();
1070
1071         lwkt_reltoken(&tty_token);
1072         UCOM_MTX_UNLOCK(sc);
1073
1074         return (error);
1075 }
1076
1077 static int
1078 ucom_modem(struct tty *tp, int sigon, int sigoff)
1079 {
1080         struct ucom_softc *sc;
1081         uint8_t onoff;
1082
1083         sc = devclass_get_softc(ucom_devclass, minor(tp->t_dev));
1084
1085         UCOM_MTX_ASSERT(sc, MA_OWNED);
1086
1087         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1088                 return (0);
1089         }
1090         if ((sigon == 0) && (sigoff == 0)) {
1091
1092                 if (sc->sc_mcr & SER_DTR) {
1093                         sigon |= SER_DTR;
1094                 }
1095                 if (sc->sc_mcr & SER_RTS) {
1096                         sigon |= SER_RTS;
1097                 }
1098                 if (sc->sc_msr & SER_CTS) {
1099                         sigon |= SER_CTS;
1100                 }
1101                 if (sc->sc_msr & SER_DCD) {
1102                         sigon |= SER_DCD;
1103                 }
1104                 if (sc->sc_msr & SER_DSR) {
1105                         sigon |= SER_DSR;
1106                 }
1107                 if (sc->sc_msr & SER_RI) {
1108                         sigon |= SER_RI;
1109                 }
1110                 return (sigon);
1111         }
1112         if (sigon & SER_DTR) {
1113                 sc->sc_mcr |= SER_DTR;
1114         }
1115         if (sigoff & SER_DTR) {
1116                 sc->sc_mcr &= ~SER_DTR;
1117         }
1118         if (sigon & SER_RTS) {
1119                 sc->sc_mcr |= SER_RTS;
1120         }
1121         if (sigoff & SER_RTS) {
1122                 sc->sc_mcr &= ~SER_RTS;
1123         }
1124         onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
1125         ucom_dtr(sc, onoff);
1126
1127         onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
1128         ucom_rts(sc, onoff);
1129
1130         return (0);
1131 }
1132
1133 static void
1134 ucom_cfg_line_state(struct usb_proc_msg *_task)
1135 {
1136         struct ucom_cfg_task *task = 
1137             (struct ucom_cfg_task *)_task;
1138         struct ucom_softc *sc = task->sc;
1139         uint8_t notch_bits;
1140         uint8_t any_bits;
1141         uint8_t prev_value;
1142         uint8_t last_value;
1143         uint8_t mask;
1144
1145         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
1146                 return;
1147         }
1148
1149         mask = 0;
1150         /* compute callback mask */
1151         if (sc->sc_callback->ucom_cfg_set_dtr)
1152                 mask |= UCOM_LS_DTR;
1153         if (sc->sc_callback->ucom_cfg_set_rts)
1154                 mask |= UCOM_LS_RTS;
1155         if (sc->sc_callback->ucom_cfg_set_break)
1156                 mask |= UCOM_LS_BREAK;
1157         if (sc->sc_callback->ucom_cfg_set_ring)
1158                 mask |= UCOM_LS_RING;
1159
1160         /* compute the bits we are to program */
1161         notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
1162         any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
1163         prev_value = sc->sc_pls_curr ^ notch_bits;
1164         last_value = sc->sc_pls_curr;
1165
1166         /* reset programmed line state */
1167         sc->sc_pls_curr = 0;
1168         sc->sc_pls_set = 0;
1169         sc->sc_pls_clr = 0;
1170
1171         /* ensure that we don't lose any levels */
1172         if (notch_bits & UCOM_LS_DTR)
1173                 sc->sc_callback->ucom_cfg_set_dtr(sc,
1174                     (prev_value & UCOM_LS_DTR) ? 1 : 0);
1175         if (notch_bits & UCOM_LS_RTS)
1176                 sc->sc_callback->ucom_cfg_set_rts(sc,
1177                     (prev_value & UCOM_LS_RTS) ? 1 : 0);
1178         if (notch_bits & UCOM_LS_BREAK)
1179                 sc->sc_callback->ucom_cfg_set_break(sc,
1180                     (prev_value & UCOM_LS_BREAK) ? 1 : 0);
1181         if (notch_bits & UCOM_LS_RING)
1182                 sc->sc_callback->ucom_cfg_set_ring(sc,
1183                     (prev_value & UCOM_LS_RING) ? 1 : 0);
1184
1185         /* set last value */
1186         if (any_bits & UCOM_LS_DTR)
1187                 sc->sc_callback->ucom_cfg_set_dtr(sc,
1188                     (last_value & UCOM_LS_DTR) ? 1 : 0);
1189         if (any_bits & UCOM_LS_RTS)
1190                 sc->sc_callback->ucom_cfg_set_rts(sc,
1191                     (last_value & UCOM_LS_RTS) ? 1 : 0);
1192         if (any_bits & UCOM_LS_BREAK)
1193                 sc->sc_callback->ucom_cfg_set_break(sc,
1194                     (last_value & UCOM_LS_BREAK) ? 1 : 0);
1195         if (any_bits & UCOM_LS_RING)
1196                 sc->sc_callback->ucom_cfg_set_ring(sc,
1197                     (last_value & UCOM_LS_RING) ? 1 : 0);
1198 }
1199
1200 static void
1201 ucom_line_state(struct ucom_softc *sc,
1202     uint8_t set_bits, uint8_t clear_bits)
1203 {
1204         UCOM_MTX_ASSERT(sc, MA_OWNED);
1205
1206         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1207                 return;
1208         }
1209
1210         DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
1211
1212         /* update current programmed line state */
1213         sc->sc_pls_curr |= set_bits;
1214         sc->sc_pls_curr &= ~clear_bits;
1215         sc->sc_pls_set |= set_bits;
1216         sc->sc_pls_clr |= clear_bits;
1217
1218         /* defer driver programming */
1219         ucom_queue_command(sc, ucom_cfg_line_state, NULL,
1220             &sc->sc_line_state_task[0].hdr, 
1221             &sc->sc_line_state_task[1].hdr);
1222 }
1223
1224 static void
1225 ucom_ring(struct ucom_softc *sc, uint8_t onoff)
1226 {
1227         DPRINTF("onoff = %d\n", onoff);
1228
1229         if (onoff)
1230                 ucom_line_state(sc, UCOM_LS_RING, 0);
1231         else
1232                 ucom_line_state(sc, 0, UCOM_LS_RING);
1233 }
1234
1235 static void
1236 ucom_break(struct ucom_softc *sc, uint8_t onoff)
1237 {
1238         DPRINTF("onoff = %d\n", onoff);
1239
1240         if (onoff)
1241                 ucom_line_state(sc, UCOM_LS_BREAK, 0);
1242         else
1243                 ucom_line_state(sc, 0, UCOM_LS_BREAK);
1244 }
1245
1246 static void
1247 ucom_dtr(struct ucom_softc *sc, uint8_t onoff)
1248 {
1249         DPRINTF("onoff = %d\n", onoff);
1250
1251         if (onoff)
1252                 ucom_line_state(sc, UCOM_LS_DTR, 0);
1253         else
1254                 ucom_line_state(sc, 0, UCOM_LS_DTR);
1255 }
1256
1257 static void
1258 ucom_rts(struct ucom_softc *sc, uint8_t onoff)
1259 {
1260         DPRINTF("onoff = %d\n", onoff);
1261
1262         if (onoff)
1263                 ucom_line_state(sc, UCOM_LS_RTS, 0);
1264         else
1265                 ucom_line_state(sc, 0, UCOM_LS_RTS);
1266 }
1267
1268 static void
1269 ucom_cfg_status_change(struct usb_proc_msg *_task)
1270 {
1271         struct ucom_cfg_task *task = 
1272             (struct ucom_cfg_task *)_task;
1273         struct ucom_softc *sc = task->sc;
1274         struct tty *tp;
1275         uint8_t new_msr;
1276         uint8_t new_lsr;
1277         uint8_t onoff;
1278         uint8_t lsr_delta;
1279
1280         tp = sc->sc_tty;
1281
1282         UCOM_MTX_ASSERT(sc, MA_OWNED);
1283
1284         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
1285                 return;
1286         }
1287         if (sc->sc_callback->ucom_cfg_get_status == NULL) {
1288                 return;
1289         }
1290         /* get status */
1291
1292         new_msr = 0;
1293         new_lsr = 0;
1294
1295         (sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr);
1296
1297         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1298                 /* TTY device closed */
1299                 return;
1300         }
1301         onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
1302         lsr_delta = (sc->sc_lsr ^ new_lsr);
1303
1304         sc->sc_msr = new_msr;
1305         sc->sc_lsr = new_lsr;
1306
1307         if (onoff) {
1308
1309                 onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
1310
1311                 DPRINTF("DCD changed to %d\n", onoff);
1312
1313                 /*
1314                 ttydisc_modem(tp, onoff);
1315                 */
1316         }
1317
1318         if ((lsr_delta & ULSR_BI) && (sc->sc_lsr & ULSR_BI)) {
1319
1320                 DPRINTF("BREAK detected\n");
1321
1322                 /*
1323                 ttydisc_rint(tp, 0, TRE_BREAK);
1324                 ttydisc_rint_done(tp);
1325                 */
1326         }
1327
1328         if ((lsr_delta & ULSR_FE) && (sc->sc_lsr & ULSR_FE)) {
1329
1330                 DPRINTF("Frame error detected\n");
1331
1332                 /*
1333                 ttydisc_rint(tp, 0, TRE_FRAMING);
1334                 ttydisc_rint_done(tp);
1335                 */
1336         }
1337
1338         if ((lsr_delta & ULSR_PE) && (sc->sc_lsr & ULSR_PE)) {
1339
1340                 DPRINTF("Parity error detected\n");
1341
1342                 /*
1343                 ttydisc_rint(tp, 0, TRE_PARITY);
1344                 ttydisc_rint_done(tp);
1345                 */
1346         }
1347 }
1348
1349 void
1350 ucom_status_change(struct ucom_softc *sc)
1351 {
1352         UCOM_MTX_ASSERT(sc, MA_OWNED);
1353
1354         if (sc->sc_flag & UCOM_FLAG_CONSOLE)
1355                 return;         /* not supported */
1356
1357         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1358                 return;
1359         }
1360         DPRINTF("\n");
1361
1362         ucom_queue_command(sc, ucom_cfg_status_change, NULL,
1363             &sc->sc_status_task[0].hdr,
1364             &sc->sc_status_task[1].hdr);
1365 }
1366
1367 static void
1368 ucom_cfg_param(struct usb_proc_msg *_task)
1369 {
1370         struct ucom_param_task *task = 
1371             (struct ucom_param_task *)_task;
1372         struct ucom_softc *sc = task->sc;
1373
1374         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
1375                 return;
1376         }
1377         if (sc->sc_callback->ucom_cfg_param == NULL) {
1378                 return;
1379         }
1380
1381         (sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy);
1382
1383         /* wait a little */
1384         usb_pause_mtx(sc->sc_lock, hz / 10);
1385 }
1386
1387 static int
1388 ucom_param(struct tty *tp, struct termios *t)
1389 {
1390         struct ucom_softc *sc;
1391         uint8_t opened;
1392         int error;
1393
1394         sc = devclass_get_softc(ucom_devclass, minor(tp->t_dev));
1395
1396         lwkt_gettoken(&tty_token);
1397         UCOM_MTX_ASSERT(sc, MA_OWNED);
1398
1399         opened = 0;
1400         error = 0;
1401
1402         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1403
1404                 /* XXX the TTY layer should call "open()" first! */
1405                 /*
1406                  * Not quite: Its ordering is partly backwards, but
1407                  * some parameters must be set early in ttydev_open(),
1408                  * possibly before calling ttydevsw_open().
1409                  */
1410                 error = ucom_open(sc);
1411
1412                 if (error) {
1413                         goto done;
1414                 }
1415                 opened = 1;
1416         }
1417         DPRINTF("sc = %p\n", sc);
1418
1419         /* Check requested parameters. */
1420         if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
1421                 /* XXX c_ospeed == 0 is perfectly valid. */
1422                 DPRINTF("mismatch ispeed and ospeed\n");
1423                 error = EINVAL;
1424                 goto done;
1425         }
1426         t->c_ispeed = t->c_ospeed;
1427
1428         if (sc->sc_callback->ucom_pre_param) {
1429                 /* Let the lower layer verify the parameters */
1430                 error = (sc->sc_callback->ucom_pre_param) (sc, t);
1431                 if (error) {
1432                         DPRINTF("callback error = %d\n", error);
1433                         goto done;
1434                 }
1435         }
1436
1437         /* Disable transfers */
1438         sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
1439
1440         /* Queue baud rate programming command first */
1441         ucom_queue_command(sc, ucom_cfg_param, t,
1442             &sc->sc_param_task[0].hdr,
1443             &sc->sc_param_task[1].hdr);
1444
1445         /* Queue transfer enable command last */
1446         ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
1447             &sc->sc_start_task[0].hdr, 
1448             &sc->sc_start_task[1].hdr);
1449
1450         if (t->c_cflag & CRTS_IFLOW) {
1451                 sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
1452         } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
1453                 sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
1454                 ucom_modem(tp, SER_RTS, 0);
1455         }
1456 done:
1457         if (error) {
1458                 if (opened) {
1459                         ucom_close(sc);
1460                 }
1461         }
1462
1463         lwkt_reltoken(&tty_token);      
1464         return (error);
1465 }
1466
1467 static void
1468 ucom_start(struct tty *tp)
1469 {
1470         struct ucom_softc *sc;
1471
1472         sc = devclass_get_softc(ucom_devclass, minor(tp->t_dev));
1473
1474         UCOM_MTX_ASSERT(sc, MA_OWNED);
1475
1476         DPRINTF("sc = %p\n", sc);
1477
1478         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1479                 /* The higher layer is not ready */
1480                 return;
1481         }
1482         ucom_start_transfers(sc);
1483 }
1484
1485 static void
1486 ucom_stop(struct tty *tp, int x)
1487 {
1488         return;
1489 }
1490
1491 /*------------------------------------------------------------------------*
1492  *      ucom_get_data
1493  *
1494  * Return values:
1495  * 0: No data is available.
1496  * Else: Data is available.
1497  *------------------------------------------------------------------------*/
1498 uint8_t
1499 ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1500     uint32_t offset, uint32_t len, uint32_t *actlen)
1501 {
1502 #if 0 /* XXX */
1503         struct usb_page_search res;
1504         struct tty *tp = sc->sc_tty;
1505         uint32_t cnt;
1506         uint32_t offset_orig;
1507
1508         UCOM_MTX_ASSERT(sc, MA_OWNED);
1509
1510         if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1511                 unsigned int temp;
1512
1513                 /* get total TX length */
1514
1515                 temp = ucom_cons_tx_high - ucom_cons_tx_low;
1516                 temp %= UCOM_CONS_BUFSIZE;
1517
1518                 /* limit TX length */
1519
1520                 if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_tx_low))
1521                         temp = (UCOM_CONS_BUFSIZE - ucom_cons_tx_low);
1522
1523                 if (temp > len)
1524                         temp = len;
1525
1526                 /* copy in data */
1527
1528                 usbd_copy_in(pc, offset, ucom_cons_tx_buf + ucom_cons_tx_low, temp);
1529
1530                 /* update counters */
1531
1532                 ucom_cons_tx_low += temp;
1533                 ucom_cons_tx_low %= UCOM_CONS_BUFSIZE;
1534
1535                 /* store actual length */
1536
1537                 *actlen = temp;
1538
1539                 return (temp ? 1 : 0);
1540         }
1541
1542         if (tty_gone(tp) ||
1543             !(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
1544                 actlen[0] = 0;
1545                 return (0);             /* multiport device polling */
1546         }
1547         offset_orig = offset;
1548
1549         while (len != 0) {
1550
1551                 usbd_get_page(pc, offset, &res);
1552
1553                 if (res.length > len) {
1554                         res.length = len;
1555                 }
1556                 /* copy data directly into USB buffer */
1557                 /*
1558                 cnt = ttydisc_getc(tp, res.buffer, res.length);
1559                 */
1560                 offset += cnt;
1561                 len -= cnt;
1562
1563                 if (cnt < res.length) {
1564                         /* end of buffer */
1565                         break;
1566                 }
1567         }
1568
1569         actlen[0] = offset - offset_orig;
1570
1571         DPRINTF("cnt=%d\n", actlen[0]);
1572
1573         if (actlen[0] == 0) {
1574                 return (0);
1575         }
1576 #endif
1577         return (1);
1578 }
1579
1580 void
1581 ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1582     uint32_t offset, uint32_t len)
1583 {
1584 #if 0 /* XXX */
1585         struct usb_page_search res;
1586         struct tty *tp = sc->sc_tty;
1587         char *buf;
1588         uint32_t cnt;
1589
1590         UCOM_MTX_ASSERT(sc, MA_OWNED);
1591
1592         if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1593                 unsigned int temp;
1594
1595                 /* get maximum RX length */
1596
1597                 temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_rx_high + ucom_cons_rx_low;
1598                 temp %= UCOM_CONS_BUFSIZE;
1599
1600                 /* limit RX length */
1601
1602                 if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_rx_high))
1603                         temp = (UCOM_CONS_BUFSIZE - ucom_cons_rx_high);
1604
1605                 if (temp > len)
1606                         temp = len;
1607
1608                 /* copy out data */
1609
1610                 usbd_copy_out(pc, offset, ucom_cons_rx_buf + ucom_cons_rx_high, temp);
1611
1612                 /* update counters */
1613
1614                 ucom_cons_rx_high += temp;
1615                 ucom_cons_rx_high %= UCOM_CONS_BUFSIZE;
1616
1617                 return;
1618         }
1619
1620         if (tty_gone(tp))
1621                 return;                 /* multiport device polling */
1622
1623         if (len == 0)
1624                 return;                 /* no data */
1625
1626         /* set a flag to prevent recursation ? */
1627
1628         while (len > 0) {
1629
1630                 usbd_get_page(pc, offset, &res);
1631
1632                 if (res.length > len) {
1633                         res.length = len;
1634                 }
1635                 len -= res.length;
1636                 offset += res.length;
1637
1638                 /* pass characters to tty layer */
1639
1640                 buf = res.buffer;
1641                 cnt = res.length;
1642
1643                 /* first check if we can pass the buffer directly */
1644
1645                 if (ttydisc_can_bypass(tp)) {
1646
1647                         /* clear any jitter buffer */
1648                         sc->sc_jitterbuf_in = 0;
1649                         sc->sc_jitterbuf_out = 0;
1650
1651                         if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
1652                                 DPRINTF("tp=%p, data lost\n", tp);
1653                         }
1654                         continue;
1655                 }
1656                 /* need to loop */
1657
1658                 for (cnt = 0; cnt != res.length; cnt++) {
1659                         if (sc->sc_jitterbuf_in != sc->sc_jitterbuf_out ||
1660                             ttydisc_rint(tp, buf[cnt], 0) == -1) {
1661                                 uint16_t end;
1662                                 uint16_t pos;
1663
1664                                 pos = sc->sc_jitterbuf_in;
1665                                 end = sc->sc_jitterbuf_out +
1666                                     UCOM_JITTERBUF_SIZE - 1;
1667                                 if (end >= UCOM_JITTERBUF_SIZE)
1668                                         end -= UCOM_JITTERBUF_SIZE;
1669
1670                                 for (; cnt != res.length; cnt++) {
1671                                         if (pos == end)
1672                                                 break;
1673                                         sc->sc_jitterbuf[pos] = buf[cnt];
1674                                         pos++;
1675                                         if (pos >= UCOM_JITTERBUF_SIZE)
1676                                                 pos -= UCOM_JITTERBUF_SIZE;
1677                                 }
1678
1679                                 sc->sc_jitterbuf_in = pos;
1680
1681                                 /* set RTS in async fashion */
1682                                 if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW)
1683                                         ucom_rts(sc, 1);
1684
1685                                 DPRINTF("tp=%p, lost %d "
1686                                     "chars\n", tp, res.length - cnt);
1687                                 break;
1688                         }
1689                 }
1690         }
1691         ttydisc_rint_done(tp);
1692 #endif
1693 }
1694
1695 #if 0 /* XXX */
1696 static void
1697 ucom_free(void *xsc)
1698 {
1699         struct ucom_softc *sc = xsc;
1700
1701         if (sc->sc_callback->ucom_free != NULL)
1702                 sc->sc_callback->ucom_free(sc);
1703         else
1704                 ucom_unref(sc->sc_super);
1705
1706         lockmgr(&ucom_lock, LK_EXCLUSIVE);
1707         ucom_close_refs--;
1708         lockmgr(&ucom_lock, LK_RELEASE);
1709 }
1710
1711 static cn_probe_t ucom_cnprobe;
1712 static cn_init_t ucom_cninit;
1713 static cn_term_t ucom_cnterm;
1714 static cn_getc_t ucom_cngetc;
1715 static cn_putc_t ucom_cnputc;
1716
1717 /*
1718 static cn_grab_t ucom_cngrab;
1719 static cn_ungrab_t ucom_cnungrab;
1720 CONSOLE_DRIVER(ucom);
1721 */
1722
1723 static void
1724 ucom_cnprobe(struct consdev  *cp)
1725 {
1726         if (ucom_cons_unit != -1)
1727                 cp->cn_pri = CN_NORMAL;
1728         else
1729                 cp->cn_pri = CN_DEAD;
1730
1731         /*
1732         strlcpy(cp->cn_name, "ucom", sizeof(cp->cn_name));
1733         */
1734 }
1735
1736 static void
1737 ucom_cninit(struct consdev  *cp)
1738 {
1739 }
1740
1741 static void
1742 ucom_cnterm(struct consdev  *cp)
1743 {
1744 }
1745
1746 static void
1747 ucom_cngrab(struct consdev *cp)
1748 {
1749 }
1750
1751 static void
1752 ucom_cnungrab(struct consdev *cp)
1753 {
1754 }
1755
1756 static int
1757 ucom_cngetc(struct consdev *cd)
1758 {
1759         struct ucom_softc *sc = ucom_cons_softc;
1760         int c;
1761
1762         if (sc == NULL)
1763                 return (-1);
1764
1765         UCOM_MTX_LOCK(sc);
1766
1767         if (ucom_cons_rx_low != ucom_cons_rx_high) {
1768                 c = ucom_cons_rx_buf[ucom_cons_rx_low];
1769                 ucom_cons_rx_low ++;
1770                 ucom_cons_rx_low %= UCOM_CONS_BUFSIZE;
1771         } else {
1772                 c = -1;
1773         }
1774
1775         /* start USB transfers */
1776         ucom_outwakeup(sc->sc_tty);
1777
1778         UCOM_MTX_UNLOCK(sc);
1779
1780         /* poll if necessary */
1781         /*
1782         if (kdb_active && sc->sc_callback->ucom_poll)
1783                 (sc->sc_callback->ucom_poll) (sc);
1784         */
1785         return (c);
1786 }
1787
1788 static void
1789 ucom_cnputc(void *cd, int c)
1790         /*
1791 ucom_cnputc(struct consdev *cd, int c)
1792         */
1793
1794 {
1795         struct ucom_softc *sc = ucom_cons_softc;
1796         unsigned int temp;
1797
1798         if (sc == NULL)
1799                 return;
1800
1801  repeat:
1802
1803         UCOM_MTX_LOCK(sc);
1804
1805         /* compute maximum TX length */
1806
1807         temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_tx_high + ucom_cons_tx_low;
1808         temp %= UCOM_CONS_BUFSIZE;
1809
1810         if (temp) {
1811                 ucom_cons_tx_buf[ucom_cons_tx_high] = c;
1812                 ucom_cons_tx_high ++;
1813                 ucom_cons_tx_high %= UCOM_CONS_BUFSIZE;
1814         }
1815
1816         /* start USB transfers */
1817         ucom_outwakeup(sc->sc_tty);
1818
1819         UCOM_MTX_UNLOCK(sc);
1820
1821         /* poll if necessary */
1822 #if 0 /* XXX */
1823         if (kdb_active && sc->sc_callback->ucom_poll) {
1824                 (sc->sc_callback->ucom_poll) (sc);
1825                 /* simple flow control */
1826                 if (temp == 0)
1827                         goto repeat;
1828         }
1829 #endif
1830 }
1831 #endif
1832 /*------------------------------------------------------------------------*
1833  *      ucom_ref
1834  *
1835  * This function will increment the super UCOM reference count.
1836  *------------------------------------------------------------------------*/
1837 void
1838 ucom_ref(struct ucom_super_softc *ssc)
1839 {
1840         lockmgr(&ucom_lock, LK_EXCLUSIVE);
1841         ssc->sc_refs++;
1842         lockmgr(&ucom_lock, LK_RELEASE);
1843 }
1844
1845 /*------------------------------------------------------------------------*
1846  *      ucom_free_unit
1847  *
1848  * This function will free the super UCOM's allocated unit
1849  * number. This function can be called on a zero-initialized
1850  * structure. This function can be called multiple times.
1851  *------------------------------------------------------------------------*/
1852 static void
1853 ucom_free_unit(struct ucom_super_softc *ssc)
1854 {
1855         if (!(ssc->sc_flag & UCOM_FLAG_FREE_UNIT))
1856                 return;
1857
1858         ucom_unit_free(ssc->sc_unit);
1859
1860         ssc->sc_flag &= ~UCOM_FLAG_FREE_UNIT;
1861 }
1862
1863 /*------------------------------------------------------------------------*
1864  *      ucom_unref
1865  *
1866  * This function will decrement the super UCOM reference count.
1867  *
1868  * Return values:
1869  * 0: UCOM structures are still referenced.
1870  * Else: UCOM structures are no longer referenced.
1871  *------------------------------------------------------------------------*/
1872 int
1873 ucom_unref(struct ucom_super_softc *ssc)
1874 {
1875         int retval;
1876
1877         lockmgr(&ucom_lock, LK_EXCLUSIVE);
1878         retval = (ssc->sc_refs < 2);
1879         ssc->sc_refs--;
1880         lockmgr(&ucom_lock, LK_RELEASE);
1881
1882         if (retval)
1883                 ucom_free_unit(ssc);
1884
1885         return (retval);
1886 }
1887
1888 #if defined(GDB)
1889
1890 #include <gdb/gdb.h>
1891
1892 static gdb_probe_f ucom_gdbprobe;
1893 static gdb_init_f ucom_gdbinit;
1894 static gdb_term_f ucom_gdbterm;
1895 static gdb_getc_f ucom_gdbgetc;
1896 static gdb_putc_f ucom_gdbputc;
1897
1898 GDB_DBGPORT(sio, ucom_gdbprobe, ucom_gdbinit, ucom_gdbterm, ucom_gdbgetc, ucom_gdbputc);
1899
1900 static int
1901 ucom_gdbprobe(void)
1902 {
1903         return ((ucom_cons_softc != NULL) ? 0 : -1);
1904 }
1905
1906 static void
1907 ucom_gdbinit(void)
1908 {
1909 }
1910
1911 static void
1912 ucom_gdbterm(void)
1913 {
1914 }
1915
1916 static void
1917 ucom_gdbputc(int c)
1918 {
1919         ucom_cnputc(NULL, c);
1920 }
1921
1922 static int
1923 ucom_gdbgetc(void)
1924 {
1925         return (ucom_cngetc(NULL));
1926 }
1927
1928 #endif