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