u4b - Fix panic on certain cell phone connections
[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                 return (error);
867         }
868         ucom_shutdown(sc);
869
870         ucom_queue_command(sc, ucom_cfg_close, NULL,
871             &sc->sc_close_task[0].hdr,
872             &sc->sc_close_task[1].hdr);
873
874         sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW);
875
876         if (sc->sc_callback->ucom_stop_read) {
877                 (sc->sc_callback->ucom_stop_read) (sc);
878         }
879
880         return (error);
881 }
882
883 #if 0 /* XXX */
884 static void
885 ucom_inwakeup(struct tty *tp)
886 {
887         struct ucom_softc *sc = tty_softc(tp);
888         uint16_t pos;
889
890         if (sc == NULL)
891                 return;
892
893         UCOM_MTX_ASSERT(sc, MA_OWNED);
894
895         DPRINTF("tp=%p\n", tp);
896
897         if (ttydisc_can_bypass(tp) != 0 || 
898             (sc->sc_flag & UCOM_FLAG_HL_READY) == 0 ||
899             (sc->sc_flag & UCOM_FLAG_INWAKEUP) != 0) {
900                 return;
901         }
902
903         /* prevent recursion */
904         sc->sc_flag |= UCOM_FLAG_INWAKEUP;
905
906         pos = sc->sc_jitterbuf_out;
907
908         while (sc->sc_jitterbuf_in != pos) {
909                 int c;
910
911                 c = (char)sc->sc_jitterbuf[pos];
912
913                 if (ttydisc_rint(tp, c, 0) == -1)
914                         break;
915                 pos++;
916                 if (pos >= UCOM_JITTERBUF_SIZE)
917                         pos -= UCOM_JITTERBUF_SIZE;
918         }
919
920         sc->sc_jitterbuf_out = pos;
921
922         /* clear RTS in async fashion */
923         if ((sc->sc_jitterbuf_in == pos) && 
924             (sc->sc_flag & UCOM_FLAG_RTS_IFLOW))
925                 ucom_rts(sc, 0);
926
927         sc->sc_flag &= ~UCOM_FLAG_INWAKEUP;
928 }
929 #endif
930
931 static int
932 ucom_dev_read(struct dev_read_args *ap)
933 {
934         cdev_t dev = ap->a_head.a_dev;
935         struct ucom_softc *sc;
936         struct tty *tp;
937         int error;
938
939         sc = devclass_get_softc(ucom_devclass, minor(dev));
940         lwkt_gettoken(&tty_token);
941         tp = sc->sc_tty;
942
943         DPRINTF("ucomread: tp = %p, flag = 0x%x\n", tp, ap->a_ioflag);
944
945 #if 0 /* XXXDF */
946         if (sc->sc_dying) {
947                 lwkt_reltoken(&tty_token);
948                 return (EIO);
949         }
950 #endif
951         error = (*linesw[tp->t_line].l_read)(tp, ap->a_uio, ap->a_ioflag);
952
953         DPRINTF("ucomread: error = %d\n", error);
954
955         lwkt_reltoken(&tty_token);
956         return (error);
957 }
958
959 static int
960 ucom_dev_write(struct dev_write_args *ap)
961 {
962         cdev_t dev = ap->a_head.a_dev;
963         struct ucom_softc *sc;
964         struct tty *tp;
965         int error;
966
967         sc = devclass_get_softc(ucom_devclass, minor(dev));
968         lwkt_gettoken(&tty_token);
969         tp = sc->sc_tty;
970
971         DPRINTF("ucomwrite: tp = %p, flag = 0x%x\n", tp, ap->a_ioflag);
972
973 #if 0 /* XXXDF */
974         if (sc->sc_dying) {
975                 lwkt_reltoken(&tty_token);
976                 return (EIO);
977         }
978 #endif
979
980         error = (*linesw[tp->t_line].l_write)(tp, ap->a_uio, ap->a_ioflag);
981
982         DPRINTF("ucomwrite: error = %d\n", error);
983
984         lwkt_reltoken(&tty_token);
985         return (error);
986 }
987
988 static int
989 ucom_dev_ioctl(struct dev_ioctl_args *ap)
990 {
991         cdev_t dev = ap->a_head.a_dev;
992         struct ucom_softc *sc = (struct ucom_softc *)dev->si_drv1;
993         u_long cmd = ap->a_cmd;
994         caddr_t data = ap->a_data;
995         struct tty *tp = sc->sc_tty;
996         int error;
997
998         UCOM_MTX_LOCK(sc);
999         lwkt_gettoken(&tty_token);
1000
1001         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1002                 lwkt_reltoken(&tty_token);
1003                 return (EIO);
1004         }
1005         DPRINTF("cmd = 0x%08lx\n", cmd);
1006
1007         error = (*linesw[tp->t_line].l_ioctl)(tp, ap->a_cmd, ap->a_data,
1008                                               ap->a_fflag, ap->a_cred);
1009
1010         if (error != ENOIOCTL) {
1011                 DPRINTF("ucomioctl: l_ioctl: error = %d\n", error);
1012                 lwkt_reltoken(&tty_token);
1013                 UCOM_MTX_UNLOCK(sc);
1014                 return (error);
1015         }
1016
1017         crit_enter();
1018
1019         error = ttioctl(tp, ap->a_cmd, ap->a_data, ap->a_fflag);
1020         /*disc_optim(tp, &tp->t_termios, sc); */
1021         if (error != ENOIOCTL) {
1022                 crit_exit();
1023                 DPRINTF("ucomioctl: ttioctl: error = %d\n", error);
1024
1025                 lwkt_reltoken(&tty_token);
1026                 UCOM_MTX_UNLOCK(sc);
1027
1028                 return (error);
1029         }
1030
1031
1032         switch (cmd) {
1033 #if 0 /* XXXDF */
1034         case TIOCSRING:
1035                 ucom_ring(sc, 1);
1036                 error = 0;
1037                 break;
1038         case TIOCCRING:
1039                 ucom_ring(sc, 0);
1040                 error = 0;
1041                 break;
1042 #endif
1043         case TIOCSBRK:
1044                 ucom_break(sc, 1);
1045                 error = 0;
1046                 break;
1047         case TIOCCBRK:
1048                 ucom_break(sc, 0);
1049                 error = 0;
1050                 break;
1051         default:
1052                 if (sc->sc_callback->ucom_ioctl) {
1053                         error = (sc->sc_callback->ucom_ioctl)
1054                             (sc, cmd, data, 0, curthread);
1055                         if (error>=0) {
1056                                 crit_exit();
1057
1058                                 lwkt_reltoken(&tty_token);
1059                                 UCOM_MTX_UNLOCK(sc);
1060
1061                                 return(error);  
1062                         }
1063                 } else {
1064                         error = ENOIOCTL;
1065                 }
1066                 break;
1067         }
1068         crit_exit();
1069
1070         lwkt_reltoken(&tty_token);
1071         UCOM_MTX_UNLOCK(sc);
1072
1073         return (error);
1074 }
1075
1076 static int
1077 ucom_modem(struct tty *tp, int sigon, int sigoff)
1078 {
1079         struct ucom_softc *sc;
1080         uint8_t onoff;
1081
1082         sc = devclass_get_softc(ucom_devclass, minor(tp->t_dev));
1083
1084         UCOM_MTX_ASSERT(sc, MA_OWNED);
1085
1086         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1087                 return (0);
1088         }
1089         if ((sigon == 0) && (sigoff == 0)) {
1090
1091                 if (sc->sc_mcr & SER_DTR) {
1092                         sigon |= SER_DTR;
1093                 }
1094                 if (sc->sc_mcr & SER_RTS) {
1095                         sigon |= SER_RTS;
1096                 }
1097                 if (sc->sc_msr & SER_CTS) {
1098                         sigon |= SER_CTS;
1099                 }
1100                 if (sc->sc_msr & SER_DCD) {
1101                         sigon |= SER_DCD;
1102                 }
1103                 if (sc->sc_msr & SER_DSR) {
1104                         sigon |= SER_DSR;
1105                 }
1106                 if (sc->sc_msr & SER_RI) {
1107                         sigon |= SER_RI;
1108                 }
1109                 return (sigon);
1110         }
1111         if (sigon & SER_DTR) {
1112                 sc->sc_mcr |= SER_DTR;
1113         }
1114         if (sigoff & SER_DTR) {
1115                 sc->sc_mcr &= ~SER_DTR;
1116         }
1117         if (sigon & SER_RTS) {
1118                 sc->sc_mcr |= SER_RTS;
1119         }
1120         if (sigoff & SER_RTS) {
1121                 sc->sc_mcr &= ~SER_RTS;
1122         }
1123         onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
1124         ucom_dtr(sc, onoff);
1125
1126         onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
1127         ucom_rts(sc, onoff);
1128
1129         return (0);
1130 }
1131
1132 static void
1133 ucom_cfg_line_state(struct usb_proc_msg *_task)
1134 {
1135         struct ucom_cfg_task *task = 
1136             (struct ucom_cfg_task *)_task;
1137         struct ucom_softc *sc = task->sc;
1138         uint8_t notch_bits;
1139         uint8_t any_bits;
1140         uint8_t prev_value;
1141         uint8_t last_value;
1142         uint8_t mask;
1143
1144         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
1145                 return;
1146         }
1147
1148         mask = 0;
1149         /* compute callback mask */
1150         if (sc->sc_callback->ucom_cfg_set_dtr)
1151                 mask |= UCOM_LS_DTR;
1152         if (sc->sc_callback->ucom_cfg_set_rts)
1153                 mask |= UCOM_LS_RTS;
1154         if (sc->sc_callback->ucom_cfg_set_break)
1155                 mask |= UCOM_LS_BREAK;
1156         if (sc->sc_callback->ucom_cfg_set_ring)
1157                 mask |= UCOM_LS_RING;
1158
1159         /* compute the bits we are to program */
1160         notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
1161         any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
1162         prev_value = sc->sc_pls_curr ^ notch_bits;
1163         last_value = sc->sc_pls_curr;
1164
1165         /* reset programmed line state */
1166         sc->sc_pls_curr = 0;
1167         sc->sc_pls_set = 0;
1168         sc->sc_pls_clr = 0;
1169
1170         /* ensure that we don't lose any levels */
1171         if (notch_bits & UCOM_LS_DTR)
1172                 sc->sc_callback->ucom_cfg_set_dtr(sc,
1173                     (prev_value & UCOM_LS_DTR) ? 1 : 0);
1174         if (notch_bits & UCOM_LS_RTS)
1175                 sc->sc_callback->ucom_cfg_set_rts(sc,
1176                     (prev_value & UCOM_LS_RTS) ? 1 : 0);
1177         if (notch_bits & UCOM_LS_BREAK)
1178                 sc->sc_callback->ucom_cfg_set_break(sc,
1179                     (prev_value & UCOM_LS_BREAK) ? 1 : 0);
1180         if (notch_bits & UCOM_LS_RING)
1181                 sc->sc_callback->ucom_cfg_set_ring(sc,
1182                     (prev_value & UCOM_LS_RING) ? 1 : 0);
1183
1184         /* set last value */
1185         if (any_bits & UCOM_LS_DTR)
1186                 sc->sc_callback->ucom_cfg_set_dtr(sc,
1187                     (last_value & UCOM_LS_DTR) ? 1 : 0);
1188         if (any_bits & UCOM_LS_RTS)
1189                 sc->sc_callback->ucom_cfg_set_rts(sc,
1190                     (last_value & UCOM_LS_RTS) ? 1 : 0);
1191         if (any_bits & UCOM_LS_BREAK)
1192                 sc->sc_callback->ucom_cfg_set_break(sc,
1193                     (last_value & UCOM_LS_BREAK) ? 1 : 0);
1194         if (any_bits & UCOM_LS_RING)
1195                 sc->sc_callback->ucom_cfg_set_ring(sc,
1196                     (last_value & UCOM_LS_RING) ? 1 : 0);
1197 }
1198
1199 static void
1200 ucom_line_state(struct ucom_softc *sc,
1201     uint8_t set_bits, uint8_t clear_bits)
1202 {
1203         UCOM_MTX_ASSERT(sc, MA_OWNED);
1204
1205         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1206                 return;
1207         }
1208
1209         DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
1210
1211         /* update current programmed line state */
1212         sc->sc_pls_curr |= set_bits;
1213         sc->sc_pls_curr &= ~clear_bits;
1214         sc->sc_pls_set |= set_bits;
1215         sc->sc_pls_clr |= clear_bits;
1216
1217         /* defer driver programming */
1218         ucom_queue_command(sc, ucom_cfg_line_state, NULL,
1219             &sc->sc_line_state_task[0].hdr, 
1220             &sc->sc_line_state_task[1].hdr);
1221 }
1222
1223 static void
1224 ucom_ring(struct ucom_softc *sc, uint8_t onoff)
1225 {
1226         DPRINTF("onoff = %d\n", onoff);
1227
1228         if (onoff)
1229                 ucom_line_state(sc, UCOM_LS_RING, 0);
1230         else
1231                 ucom_line_state(sc, 0, UCOM_LS_RING);
1232 }
1233
1234 static void
1235 ucom_break(struct ucom_softc *sc, uint8_t onoff)
1236 {
1237         DPRINTF("onoff = %d\n", onoff);
1238
1239         if (onoff)
1240                 ucom_line_state(sc, UCOM_LS_BREAK, 0);
1241         else
1242                 ucom_line_state(sc, 0, UCOM_LS_BREAK);
1243 }
1244
1245 static void
1246 ucom_dtr(struct ucom_softc *sc, uint8_t onoff)
1247 {
1248         DPRINTF("onoff = %d\n", onoff);
1249
1250         if (onoff)
1251                 ucom_line_state(sc, UCOM_LS_DTR, 0);
1252         else
1253                 ucom_line_state(sc, 0, UCOM_LS_DTR);
1254 }
1255
1256 static void
1257 ucom_rts(struct ucom_softc *sc, uint8_t onoff)
1258 {
1259         DPRINTF("onoff = %d\n", onoff);
1260
1261         if (onoff)
1262                 ucom_line_state(sc, UCOM_LS_RTS, 0);
1263         else
1264                 ucom_line_state(sc, 0, UCOM_LS_RTS);
1265 }
1266
1267 static void
1268 ucom_cfg_status_change(struct usb_proc_msg *_task)
1269 {
1270         struct ucom_cfg_task *task = 
1271             (struct ucom_cfg_task *)_task;
1272         struct ucom_softc *sc = task->sc;
1273         struct tty *tp;
1274         uint8_t new_msr;
1275         uint8_t new_lsr;
1276         uint8_t onoff;
1277         uint8_t lsr_delta;
1278
1279         tp = sc->sc_tty;
1280
1281         UCOM_MTX_ASSERT(sc, MA_OWNED);
1282
1283         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
1284                 return;
1285         }
1286         if (sc->sc_callback->ucom_cfg_get_status == NULL) {
1287                 return;
1288         }
1289         /* get status */
1290
1291         new_msr = 0;
1292         new_lsr = 0;
1293
1294         (sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr);
1295
1296         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1297                 /* TTY device closed */
1298                 return;
1299         }
1300         onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
1301         lsr_delta = (sc->sc_lsr ^ new_lsr);
1302
1303         sc->sc_msr = new_msr;
1304         sc->sc_lsr = new_lsr;
1305
1306         if (onoff) {
1307
1308                 onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
1309
1310                 DPRINTF("DCD changed to %d\n", onoff);
1311
1312                 /*
1313                 ttydisc_modem(tp, onoff);
1314                 */
1315         }
1316
1317         if ((lsr_delta & ULSR_BI) && (sc->sc_lsr & ULSR_BI)) {
1318
1319                 DPRINTF("BREAK detected\n");
1320
1321                 /*
1322                 ttydisc_rint(tp, 0, TRE_BREAK);
1323                 ttydisc_rint_done(tp);
1324                 */
1325         }
1326
1327         if ((lsr_delta & ULSR_FE) && (sc->sc_lsr & ULSR_FE)) {
1328
1329                 DPRINTF("Frame error detected\n");
1330
1331                 /*
1332                 ttydisc_rint(tp, 0, TRE_FRAMING);
1333                 ttydisc_rint_done(tp);
1334                 */
1335         }
1336
1337         if ((lsr_delta & ULSR_PE) && (sc->sc_lsr & ULSR_PE)) {
1338
1339                 DPRINTF("Parity error detected\n");
1340
1341                 /*
1342                 ttydisc_rint(tp, 0, TRE_PARITY);
1343                 ttydisc_rint_done(tp);
1344                 */
1345         }
1346 }
1347
1348 void
1349 ucom_status_change(struct ucom_softc *sc)
1350 {
1351         UCOM_MTX_ASSERT(sc, MA_OWNED);
1352
1353         if (sc->sc_flag & UCOM_FLAG_CONSOLE)
1354                 return;         /* not supported */
1355
1356         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1357                 return;
1358         }
1359         DPRINTF("\n");
1360
1361         ucom_queue_command(sc, ucom_cfg_status_change, NULL,
1362             &sc->sc_status_task[0].hdr,
1363             &sc->sc_status_task[1].hdr);
1364 }
1365
1366 static void
1367 ucom_cfg_param(struct usb_proc_msg *_task)
1368 {
1369         struct ucom_param_task *task = 
1370             (struct ucom_param_task *)_task;
1371         struct ucom_softc *sc = task->sc;
1372
1373         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
1374                 return;
1375         }
1376         if (sc->sc_callback->ucom_cfg_param == NULL) {
1377                 return;
1378         }
1379
1380         (sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy);
1381
1382         /* wait a little */
1383         usb_pause_mtx(sc->sc_lock, hz / 10);
1384 }
1385
1386 static int
1387 ucom_param(struct tty *tp, struct termios *t)
1388 {
1389         struct ucom_softc *sc;
1390         uint8_t opened;
1391         int error;
1392
1393         sc = devclass_get_softc(ucom_devclass, minor(tp->t_dev));
1394
1395         lwkt_gettoken(&tty_token);
1396         UCOM_MTX_ASSERT(sc, MA_OWNED);
1397
1398         opened = 0;
1399         error = 0;
1400
1401         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1402
1403                 /* XXX the TTY layer should call "open()" first! */
1404                 /*
1405                  * Not quite: Its ordering is partly backwards, but
1406                  * some parameters must be set early in ttydev_open(),
1407                  * possibly before calling ttydevsw_open().
1408                  */
1409                 error = ucom_open(sc);
1410
1411                 if (error) {
1412                         goto done;
1413                 }
1414                 opened = 1;
1415         }
1416         DPRINTF("sc = %p\n", sc);
1417
1418         /* Check requested parameters. */
1419         if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
1420                 /* XXX c_ospeed == 0 is perfectly valid. */
1421                 DPRINTF("mismatch ispeed and ospeed\n");
1422                 error = EINVAL;
1423                 goto done;
1424         }
1425         t->c_ispeed = t->c_ospeed;
1426
1427         if (sc->sc_callback->ucom_pre_param) {
1428                 /* Let the lower layer verify the parameters */
1429                 error = (sc->sc_callback->ucom_pre_param) (sc, t);
1430                 if (error) {
1431                         DPRINTF("callback error = %d\n", error);
1432                         goto done;
1433                 }
1434         }
1435
1436         /* Disable transfers */
1437         sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
1438
1439         /* Queue baud rate programming command first */
1440         ucom_queue_command(sc, ucom_cfg_param, t,
1441             &sc->sc_param_task[0].hdr,
1442             &sc->sc_param_task[1].hdr);
1443
1444         /* Queue transfer enable command last */
1445         ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
1446             &sc->sc_start_task[0].hdr, 
1447             &sc->sc_start_task[1].hdr);
1448
1449         if (t->c_cflag & CRTS_IFLOW) {
1450                 sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
1451         } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
1452                 sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
1453                 ucom_modem(tp, SER_RTS, 0);
1454         }
1455 done:
1456         if (error) {
1457                 if (opened) {
1458                         ucom_close(sc);
1459                 }
1460         }
1461
1462         lwkt_reltoken(&tty_token);      
1463         return (error);
1464 }
1465
1466 static void
1467 ucom_start(struct tty *tp)
1468 {
1469         struct ucom_softc *sc;
1470
1471         sc = devclass_get_softc(ucom_devclass, minor(tp->t_dev));
1472
1473         UCOM_MTX_ASSERT(sc, MA_OWNED);
1474
1475         DPRINTF("sc = %p\n", sc);
1476
1477         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1478                 /* The higher layer is not ready */
1479                 return;
1480         }
1481         ucom_start_transfers(sc);
1482 }
1483
1484 static void
1485 ucom_stop(struct tty *tp, int x)
1486 {
1487         return;
1488 }
1489
1490 /*------------------------------------------------------------------------*
1491  *      ucom_get_data
1492  *
1493  * Return values:
1494  * 0: No data is available.
1495  * Else: Data is available.
1496  *------------------------------------------------------------------------*/
1497 uint8_t
1498 ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1499     uint32_t offset, uint32_t len, uint32_t *actlen)
1500 {
1501 #if 0 /* XXX */
1502         struct usb_page_search res;
1503         struct tty *tp = sc->sc_tty;
1504         uint32_t cnt;
1505         uint32_t offset_orig;
1506
1507         UCOM_MTX_ASSERT(sc, MA_OWNED);
1508
1509         if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1510                 unsigned int temp;
1511
1512                 /* get total TX length */
1513
1514                 temp = ucom_cons_tx_high - ucom_cons_tx_low;
1515                 temp %= UCOM_CONS_BUFSIZE;
1516
1517                 /* limit TX length */
1518
1519                 if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_tx_low))
1520                         temp = (UCOM_CONS_BUFSIZE - ucom_cons_tx_low);
1521
1522                 if (temp > len)
1523                         temp = len;
1524
1525                 /* copy in data */
1526
1527                 usbd_copy_in(pc, offset, ucom_cons_tx_buf + ucom_cons_tx_low, temp);
1528
1529                 /* update counters */
1530
1531                 ucom_cons_tx_low += temp;
1532                 ucom_cons_tx_low %= UCOM_CONS_BUFSIZE;
1533
1534                 /* store actual length */
1535
1536                 *actlen = temp;
1537
1538                 return (temp ? 1 : 0);
1539         }
1540
1541         if (tty_gone(tp) ||
1542             !(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
1543                 actlen[0] = 0;
1544                 return (0);             /* multiport device polling */
1545         }
1546         offset_orig = offset;
1547
1548         while (len != 0) {
1549
1550                 usbd_get_page(pc, offset, &res);
1551
1552                 if (res.length > len) {
1553                         res.length = len;
1554                 }
1555                 /* copy data directly into USB buffer */
1556                 /*
1557                 cnt = ttydisc_getc(tp, res.buffer, res.length);
1558                 */
1559                 offset += cnt;
1560                 len -= cnt;
1561
1562                 if (cnt < res.length) {
1563                         /* end of buffer */
1564                         break;
1565                 }
1566         }
1567
1568         actlen[0] = offset - offset_orig;
1569
1570         DPRINTF("cnt=%d\n", actlen[0]);
1571
1572         if (actlen[0] == 0) {
1573                 return (0);
1574         }
1575 #endif
1576         return (1);
1577 }
1578
1579 void
1580 ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1581     uint32_t offset, uint32_t len)
1582 {
1583 #if 0 /* XXX */
1584         struct usb_page_search res;
1585         struct tty *tp = sc->sc_tty;
1586         char *buf;
1587         uint32_t cnt;
1588
1589         UCOM_MTX_ASSERT(sc, MA_OWNED);
1590
1591         if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1592                 unsigned int temp;
1593
1594                 /* get maximum RX length */
1595
1596                 temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_rx_high + ucom_cons_rx_low;
1597                 temp %= UCOM_CONS_BUFSIZE;
1598
1599                 /* limit RX length */
1600
1601                 if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_rx_high))
1602                         temp = (UCOM_CONS_BUFSIZE - ucom_cons_rx_high);
1603
1604                 if (temp > len)
1605                         temp = len;
1606
1607                 /* copy out data */
1608
1609                 usbd_copy_out(pc, offset, ucom_cons_rx_buf + ucom_cons_rx_high, temp);
1610
1611                 /* update counters */
1612
1613                 ucom_cons_rx_high += temp;
1614                 ucom_cons_rx_high %= UCOM_CONS_BUFSIZE;
1615
1616                 return;
1617         }
1618
1619         if (tty_gone(tp))
1620                 return;                 /* multiport device polling */
1621
1622         if (len == 0)
1623                 return;                 /* no data */
1624
1625         /* set a flag to prevent recursation ? */
1626
1627         while (len > 0) {
1628
1629                 usbd_get_page(pc, offset, &res);
1630
1631                 if (res.length > len) {
1632                         res.length = len;
1633                 }
1634                 len -= res.length;
1635                 offset += res.length;
1636
1637                 /* pass characters to tty layer */
1638
1639                 buf = res.buffer;
1640                 cnt = res.length;
1641
1642                 /* first check if we can pass the buffer directly */
1643
1644                 if (ttydisc_can_bypass(tp)) {
1645
1646                         /* clear any jitter buffer */
1647                         sc->sc_jitterbuf_in = 0;
1648                         sc->sc_jitterbuf_out = 0;
1649
1650                         if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
1651                                 DPRINTF("tp=%p, data lost\n", tp);
1652                         }
1653                         continue;
1654                 }
1655                 /* need to loop */
1656
1657                 for (cnt = 0; cnt != res.length; cnt++) {
1658                         if (sc->sc_jitterbuf_in != sc->sc_jitterbuf_out ||
1659                             ttydisc_rint(tp, buf[cnt], 0) == -1) {
1660                                 uint16_t end;
1661                                 uint16_t pos;
1662
1663                                 pos = sc->sc_jitterbuf_in;
1664                                 end = sc->sc_jitterbuf_out +
1665                                     UCOM_JITTERBUF_SIZE - 1;
1666                                 if (end >= UCOM_JITTERBUF_SIZE)
1667                                         end -= UCOM_JITTERBUF_SIZE;
1668
1669                                 for (; cnt != res.length; cnt++) {
1670                                         if (pos == end)
1671                                                 break;
1672                                         sc->sc_jitterbuf[pos] = buf[cnt];
1673                                         pos++;
1674                                         if (pos >= UCOM_JITTERBUF_SIZE)
1675                                                 pos -= UCOM_JITTERBUF_SIZE;
1676                                 }
1677
1678                                 sc->sc_jitterbuf_in = pos;
1679
1680                                 /* set RTS in async fashion */
1681                                 if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW)
1682                                         ucom_rts(sc, 1);
1683
1684                                 DPRINTF("tp=%p, lost %d "
1685                                     "chars\n", tp, res.length - cnt);
1686                                 break;
1687                         }
1688                 }
1689         }
1690         ttydisc_rint_done(tp);
1691 #endif
1692 }
1693
1694 #if 0 /* XXX */
1695 static void
1696 ucom_free(void *xsc)
1697 {
1698         struct ucom_softc *sc = xsc;
1699
1700         if (sc->sc_callback->ucom_free != NULL)
1701                 sc->sc_callback->ucom_free(sc);
1702         else
1703                 ucom_unref(sc->sc_super);
1704
1705         lockmgr(&ucom_lock, LK_EXCLUSIVE);
1706         ucom_close_refs--;
1707         lockmgr(&ucom_lock, LK_RELEASE);
1708 }
1709
1710 static cn_probe_t ucom_cnprobe;
1711 static cn_init_t ucom_cninit;
1712 static cn_term_t ucom_cnterm;
1713 static cn_getc_t ucom_cngetc;
1714 static cn_putc_t ucom_cnputc;
1715
1716 /*
1717 static cn_grab_t ucom_cngrab;
1718 static cn_ungrab_t ucom_cnungrab;
1719 CONSOLE_DRIVER(ucom);
1720 */
1721
1722 static void
1723 ucom_cnprobe(struct consdev  *cp)
1724 {
1725         if (ucom_cons_unit != -1)
1726                 cp->cn_pri = CN_NORMAL;
1727         else
1728                 cp->cn_pri = CN_DEAD;
1729
1730         /*
1731         strlcpy(cp->cn_name, "ucom", sizeof(cp->cn_name));
1732         */
1733 }
1734
1735 static void
1736 ucom_cninit(struct consdev  *cp)
1737 {
1738 }
1739
1740 static void
1741 ucom_cnterm(struct consdev  *cp)
1742 {
1743 }
1744
1745 static void
1746 ucom_cngrab(struct consdev *cp)
1747 {
1748 }
1749
1750 static void
1751 ucom_cnungrab(struct consdev *cp)
1752 {
1753 }
1754
1755 static int
1756 ucom_cngetc(struct consdev *cd)
1757 {
1758         struct ucom_softc *sc = ucom_cons_softc;
1759         int c;
1760
1761         if (sc == NULL)
1762                 return (-1);
1763
1764         UCOM_MTX_LOCK(sc);
1765
1766         if (ucom_cons_rx_low != ucom_cons_rx_high) {
1767                 c = ucom_cons_rx_buf[ucom_cons_rx_low];
1768                 ucom_cons_rx_low ++;
1769                 ucom_cons_rx_low %= UCOM_CONS_BUFSIZE;
1770         } else {
1771                 c = -1;
1772         }
1773
1774         /* start USB transfers */
1775         ucom_outwakeup(sc->sc_tty);
1776
1777         UCOM_MTX_UNLOCK(sc);
1778
1779         /* poll if necessary */
1780         /*
1781         if (kdb_active && sc->sc_callback->ucom_poll)
1782                 (sc->sc_callback->ucom_poll) (sc);
1783         */
1784         return (c);
1785 }
1786
1787 static void
1788 ucom_cnputc(void *cd, int c)
1789         /*
1790 ucom_cnputc(struct consdev *cd, int c)
1791         */
1792
1793 {
1794         struct ucom_softc *sc = ucom_cons_softc;
1795         unsigned int temp;
1796
1797         if (sc == NULL)
1798                 return;
1799
1800  repeat:
1801
1802         UCOM_MTX_LOCK(sc);
1803
1804         /* compute maximum TX length */
1805
1806         temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_tx_high + ucom_cons_tx_low;
1807         temp %= UCOM_CONS_BUFSIZE;
1808
1809         if (temp) {
1810                 ucom_cons_tx_buf[ucom_cons_tx_high] = c;
1811                 ucom_cons_tx_high ++;
1812                 ucom_cons_tx_high %= UCOM_CONS_BUFSIZE;
1813         }
1814
1815         /* start USB transfers */
1816         ucom_outwakeup(sc->sc_tty);
1817
1818         UCOM_MTX_UNLOCK(sc);
1819
1820         /* poll if necessary */
1821 #if 0 /* XXX */
1822         if (kdb_active && sc->sc_callback->ucom_poll) {
1823                 (sc->sc_callback->ucom_poll) (sc);
1824                 /* simple flow control */
1825                 if (temp == 0)
1826                         goto repeat;
1827         }
1828 #endif
1829 }
1830 #endif
1831 /*------------------------------------------------------------------------*
1832  *      ucom_ref
1833  *
1834  * This function will increment the super UCOM reference count.
1835  *------------------------------------------------------------------------*/
1836 void
1837 ucom_ref(struct ucom_super_softc *ssc)
1838 {
1839         lockmgr(&ucom_lock, LK_EXCLUSIVE);
1840         ssc->sc_refs++;
1841         lockmgr(&ucom_lock, LK_RELEASE);
1842 }
1843
1844 /*------------------------------------------------------------------------*
1845  *      ucom_free_unit
1846  *
1847  * This function will free the super UCOM's allocated unit
1848  * number. This function can be called on a zero-initialized
1849  * structure. This function can be called multiple times.
1850  *------------------------------------------------------------------------*/
1851 static void
1852 ucom_free_unit(struct ucom_super_softc *ssc)
1853 {
1854         if (!(ssc->sc_flag & UCOM_FLAG_FREE_UNIT))
1855                 return;
1856
1857         ucom_unit_free(ssc->sc_unit);
1858
1859         ssc->sc_flag &= ~UCOM_FLAG_FREE_UNIT;
1860 }
1861
1862 /*------------------------------------------------------------------------*
1863  *      ucom_unref
1864  *
1865  * This function will decrement the super UCOM reference count.
1866  *
1867  * Return values:
1868  * 0: UCOM structures are still referenced.
1869  * Else: UCOM structures are no longer referenced.
1870  *------------------------------------------------------------------------*/
1871 int
1872 ucom_unref(struct ucom_super_softc *ssc)
1873 {
1874         int retval;
1875
1876         lockmgr(&ucom_lock, LK_EXCLUSIVE);
1877         retval = (ssc->sc_refs < 2);
1878         ssc->sc_refs--;
1879         lockmgr(&ucom_lock, LK_RELEASE);
1880
1881         if (retval)
1882                 ucom_free_unit(ssc);
1883
1884         return (retval);
1885 }
1886
1887 #if defined(GDB)
1888
1889 #include <gdb/gdb.h>
1890
1891 static gdb_probe_f ucom_gdbprobe;
1892 static gdb_init_f ucom_gdbinit;
1893 static gdb_term_f ucom_gdbterm;
1894 static gdb_getc_f ucom_gdbgetc;
1895 static gdb_putc_f ucom_gdbputc;
1896
1897 GDB_DBGPORT(sio, ucom_gdbprobe, ucom_gdbinit, ucom_gdbterm, ucom_gdbgetc, ucom_gdbputc);
1898
1899 static int
1900 ucom_gdbprobe(void)
1901 {
1902         return ((ucom_cons_softc != NULL) ? 0 : -1);
1903 }
1904
1905 static void
1906 ucom_gdbinit(void)
1907 {
1908 }
1909
1910 static void
1911 ucom_gdbterm(void)
1912 {
1913 }
1914
1915 static void
1916 ucom_gdbputc(int c)
1917 {
1918         ucom_cnputc(NULL, c);
1919 }
1920
1921 static int
1922 ucom_gdbgetc(void)
1923 {
1924         return (ucom_cngetc(NULL));
1925 }
1926
1927 #endif