ec1d7307a012d8d3feab3f1946950915b7f0ffc4
[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 /*
68  * XXX profmakx
69  * This is a Frankenstein of FreeBSD's usb4bsd ucom and Dragonfly's old ucom
70  * module. There might be bugs lurking everywhere still
71  *
72  * In particular serial console on ucom is completely untested and likely broken
73  * as well as anyting that requires the modem control lines.
74  */
75
76 #include <sys/stdint.h>
77 #include <sys/param.h>
78 #include <sys/queue.h>
79 #include <sys/types.h>
80 #include <sys/systm.h>
81 #include <sys/kernel.h>
82 #include <sys/bus.h>
83 #include <sys/module.h>
84 #include <sys/lock.h>
85 #include <sys/condvar.h>
86 #include <sys/sysctl.h>
87 #include <sys/unistd.h>
88 #include <sys/callout.h>
89 #include <sys/malloc.h>
90 #include <sys/priv.h>
91 #include <sys/cons.h>
92 #include <sys/serial.h>
93 #include <sys/thread2.h>
94 #include <sys/conf.h>
95 #include <sys/clist.h>
96  
97 #include <bus/u4b/usb.h>
98 #include <bus/u4b/usbdi.h>
99 #include <bus/u4b/usbdi_util.h>
100
101 #define USB_DEBUG_VAR ucom_debug
102 #include <bus/u4b/usb_debug.h>
103 #include <bus/u4b/usb_busdma.h>
104 #include <bus/u4b/usb_process.h>
105
106 #include <bus/u4b/serial/usb_serial.h>
107
108 //#include "opt_gdb.h"
109
110 static SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
111
112 static int ucom_pps_mode;
113
114 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, pps_mode, CTLFLAG_RW,
115     &ucom_pps_mode, 0, "pulse capturing mode - 0/1/2 - disabled/CTS/DCD");
116 TUNABLE_INT("hw.usb.ucom.pss_mode", &ucom_pps_mode);
117
118
119 #ifdef USB_DEBUG
120 static int ucom_debug = 0;
121
122 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
123     &ucom_debug, 0, "ucom debug level");
124 #endif
125
126 #define UCOM_CONS_BUFSIZE 1024
127
128 static uint8_t ucom_cons_rx_buf[UCOM_CONS_BUFSIZE];
129 static uint8_t ucom_cons_tx_buf[UCOM_CONS_BUFSIZE];
130
131 static unsigned int ucom_cons_rx_low = 0;
132 static unsigned int ucom_cons_rx_high = 0;
133
134 static unsigned int ucom_cons_tx_low = 0;
135 static unsigned int ucom_cons_tx_high = 0;
136
137 static int ucom_cons_unit = -1;
138 static int ucom_cons_subunit = 0;
139 static int ucom_cons_baud = 9600;
140 static struct ucom_softc *ucom_cons_softc = NULL;
141
142 TUNABLE_INT("hw.usb.ucom.cons_unit", &ucom_cons_unit);
143 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_unit, CTLFLAG_RW,
144     &ucom_cons_unit, 0, "console unit number");
145 TUNABLE_INT("hw.usb.ucom.cons_subunit", &ucom_cons_subunit);
146 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_subunit, CTLFLAG_RW,
147     &ucom_cons_subunit, 0, "console subunit number");
148 TUNABLE_INT("hw.usb.ucom.cons_baud", &ucom_cons_baud);
149 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_baud, CTLFLAG_RW,
150     &ucom_cons_baud, 0, "console baud rate");
151
152 static usb_proc_callback_t ucom_cfg_start_transfers;
153 static usb_proc_callback_t ucom_cfg_open;
154 static usb_proc_callback_t ucom_cfg_close;
155 static usb_proc_callback_t ucom_cfg_line_state;
156 static usb_proc_callback_t ucom_cfg_status_change;
157 static usb_proc_callback_t ucom_cfg_param;
158
159 static int      ucom_unit_alloc(void);
160 static void     ucom_unit_free(int);
161 static int      ucom_attach_tty(struct ucom_super_softc *, struct ucom_softc *);
162 static void     ucom_detach_tty(struct ucom_super_softc *, struct ucom_softc *);
163 static void     ucom_queue_command(struct ucom_softc *,
164                     usb_proc_callback_t *, struct termios *pt,
165                     struct usb_proc_msg *t0, struct usb_proc_msg *t1);
166 static void     ucom_shutdown(struct ucom_softc *);
167 static void     ucom_ring(struct ucom_softc *, uint8_t);
168 static void     ucom_break(struct ucom_softc *, uint8_t);
169 static void     ucom_dtr(struct ucom_softc *, uint8_t);
170 static void     ucom_rts(struct ucom_softc *, uint8_t);
171
172 static int ucom_open(struct ucom_softc *sc);
173 static int ucom_close(struct ucom_softc *sc);
174 static void ucom_start(struct tty *tp);
175 static void ucom_stop(struct tty *tp, int);
176 static int ucom_param(struct tty *tp, struct termios *t);
177 static int ucom_modem(struct tty *tp, int sigon, int sigoff);
178
179 static int ucom_fromtio(int);
180 static int ucom_totio(int);
181
182 static void disc_optim(struct tty *, struct termios *, struct ucom_softc *);
183
184 static d_open_t ucom_dev_open;
185 static d_close_t ucom_dev_close;
186 static d_read_t ucom_dev_read;
187 static d_write_t ucom_dev_write;
188 static d_ioctl_t ucom_dev_ioctl;
189
190 static struct dev_ops ucom_ops = {
191   { "ucom", 0, D_MPSAFE | D_TTY },
192   .d_open =       ucom_dev_open,
193   .d_close =      ucom_dev_close,
194   .d_read =       ucom_dev_read,
195   .d_write =      ucom_dev_write,
196   .d_ioctl =      ucom_dev_ioctl,
197   .d_kqfilter =   ttykqfilter,
198   .d_revoke =     ttyrevoke
199 };
200
201 static moduledata_t ucom_mod = {
202         "ucom",
203         NULL,
204         NULL
205 };
206
207 DECLARE_MODULE(ucom, ucom_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
208 MODULE_DEPEND(ucom, usb, 1, 1, 1);
209 MODULE_VERSION(ucom, UCOM_MODVER);
210
211 /* XXXDF */
212 #define tty_gone(tp) ((tp->t_state) & (TS_ZOMBIE))
213
214 #define UCOM_UNIT_MAX           128     /* maximum number of units */
215 #define UCOM_TTY_PREFIX         "ucom"
216
217
218 #define CALLOUT_MASK            0x80
219 #define CONTROL_MASK            0x60
220 #define CONTROL_INIT_STATE      0x20
221 #define CONTROL_LOCK_STATE      0x40
222
223 static struct unrhdr *ucom_unrhdr;
224 static struct lock ucom_lock;
225 static int ucom_close_refs;
226
227 static void
228 ucom_init(void *arg)
229 {
230         DPRINTF("\n");
231         ucom_unrhdr = new_unrhdr(0, UCOM_UNIT_MAX - 1, NULL);
232         lockinit(&ucom_lock, "UCOM LOCK", 0, 0);
233 }
234 SYSINIT(ucom_init, SI_BOOT2_KLD - 1, SI_ORDER_ANY, ucom_init, NULL);
235
236 static void
237 ucom_uninit(void *arg)
238 {
239         struct unrhdr *hdr;
240         hdr = ucom_unrhdr;
241         ucom_unrhdr = NULL;
242
243         DPRINTF("\n");
244
245         if (hdr != NULL)
246                 delete_unrhdr(hdr);
247
248         lockuninit(&ucom_lock);
249 }
250 SYSUNINIT(ucom_uninit, SI_BOOT2_KLD - 2, SI_ORDER_ANY, ucom_uninit, NULL);
251
252 /*
253  * Mark a unit number (the X in cuaUX) as in use.
254  *
255  * Note that devices using a different naming scheme (see ucom_tty_name()
256  * callback) still use this unit allocation.
257  */
258 static int
259 ucom_unit_alloc(void)
260 {
261         int unit;
262
263         /* sanity checks */
264         if (ucom_unrhdr == NULL) {
265                 DPRINTF("ucom_unrhdr is NULL\n");
266                 return (-1);
267         }
268         unit = alloc_unr(ucom_unrhdr);
269         DPRINTF("unit %d is allocated\n", unit);
270         return (unit);
271 }
272
273 /*
274  * Mark the unit number as not in use.
275  */
276 static void
277 ucom_unit_free(int unit)
278 {
279         /* sanity checks */
280         if (unit < 0 || unit >= UCOM_UNIT_MAX || ucom_unrhdr == NULL) {
281                 DPRINTF("cannot free unit number\n");
282                 return;
283         }
284         DPRINTF("unit %d is freed\n", unit);
285         free_unr(ucom_unrhdr, unit);
286 }
287
288 /*
289  * Setup a group of one or more serial ports.
290  *
291  * The lock pointed to by "lock" is applied before all
292  * callbacks are called back. Also "lock" must be applied
293  * before calling into the ucom-layer!
294  */
295 int
296 ucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
297     int subunits, void *parent,
298     const struct ucom_callback *callback, struct lock *lock)
299 {
300         int subunit;
301         int error = 0;
302
303         if ((sc == NULL) ||
304             (subunits <= 0) ||
305             (callback == NULL) ||
306             (lock == NULL)) {
307                 return (EINVAL);
308         }
309
310         /* XXX Do we want our own lock here maybe */
311         sc->sc_lock = lock;
312
313         /* allocate a uniq unit number */
314         ssc->sc_unit = ucom_unit_alloc();
315         if (ssc->sc_unit == -1)
316                 return (ENOMEM);
317
318         /* generate TTY name string */
319         ksnprintf(ssc->sc_ttyname, sizeof(ssc->sc_ttyname),
320             UCOM_TTY_PREFIX "%d", ssc->sc_unit);
321
322         /* create USB request handling process */
323         error = usb_proc_create(&ssc->sc_tq, lock, "ucom", USB_PRI_MED);
324         if (error) {
325                 ucom_unit_free(ssc->sc_unit);
326                 return (error);
327         }
328         ssc->sc_subunits = subunits;
329         ssc->sc_flag = UCOM_FLAG_ATTACHED |
330             UCOM_FLAG_FREE_UNIT;
331
332         if (callback->ucom_free == NULL)
333                 ssc->sc_flag |= UCOM_FLAG_WAIT_REFS;
334
335         /* increment reference count */
336         ucom_ref(ssc);
337
338         for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
339                 sc[subunit].sc_subunit = subunit;
340                 sc[subunit].sc_super = ssc;
341                 sc[subunit].sc_lock = lock;
342                 sc[subunit].sc_parent = parent;
343                 sc[subunit].sc_callback = callback;
344
345                 error = ucom_attach_tty(ssc, &sc[subunit]);
346                 if (error) {
347                         ucom_detach(ssc, &sc[0]);
348                         return (error);
349                 }
350                 /* increment reference count */
351                 ucom_ref(ssc);
352
353                 /* set subunit attached */
354                 sc[subunit].sc_flag |= UCOM_FLAG_ATTACHED;
355         }
356
357         DPRINTF("tp = %p, unit = %d, subunits = %d\n",
358                 sc->sc_tty, ssc->sc_unit, ssc->sc_subunits);
359
360         return (0);
361 }
362
363 /*
364  * The following function will do nothing if the structure pointed to
365  * by "ssc" and "sc" is zero or has already been detached.
366  */
367 void
368 ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc)
369 {
370         int subunit;
371
372         if (!(ssc->sc_flag & UCOM_FLAG_ATTACHED))
373                 return;         /* not initialized */
374
375         destroy_dev(sc->sc_cdev);
376         destroy_dev(sc->sc_cdev_init);
377         destroy_dev(sc->sc_cdev_lock);
378         destroy_dev(sc->sc_cdev2);
379         destroy_dev(sc->sc_cdev2_init);
380         destroy_dev(sc->sc_cdev2_lock);
381
382         lwkt_gettoken(&tty_token);
383
384         if (ssc->sc_sysctl_ttyname != NULL) {
385                 sysctl_remove_oid(ssc->sc_sysctl_ttyname, 1, 0);
386                 ssc->sc_sysctl_ttyname = NULL;
387         }
388
389         if (ssc->sc_sysctl_ttyports != NULL) {
390                 sysctl_remove_oid(ssc->sc_sysctl_ttyports, 1, 0);
391                 ssc->sc_sysctl_ttyports = NULL;
392         }
393
394         usb_proc_drain(&ssc->sc_tq);
395
396         for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
397                 if (sc[subunit].sc_flag & UCOM_FLAG_ATTACHED) {
398                         ucom_detach_tty(ssc, &sc[subunit]);
399
400                         /* avoid duplicate detach */
401                         sc[subunit].sc_flag &= ~UCOM_FLAG_ATTACHED;
402                 }
403         }
404         usb_proc_free(&ssc->sc_tq);
405
406         ucom_unref(ssc);
407
408         if (ssc->sc_flag & UCOM_FLAG_WAIT_REFS)
409                 ucom_drain(ssc);
410
411         /* make sure we don't detach twice */
412         ssc->sc_flag &= ~UCOM_FLAG_ATTACHED;
413
414         lwkt_reltoken(&tty_token);
415 }
416
417 void
418 ucom_drain(struct ucom_super_softc *ssc)
419 {
420         lockmgr(&ucom_lock, LK_EXCLUSIVE);
421         while (ssc->sc_refs > 0) {
422                 kprintf("ucom: Waiting for a TTY device to close.\n");
423                 usb_pause_mtx(&ucom_lock, hz);
424         }
425         lockmgr(&ucom_lock, LK_RELEASE);
426 }
427
428 void
429 ucom_drain_all(void *arg)
430 {
431         lockmgr(&ucom_lock, LK_EXCLUSIVE);
432         while (ucom_close_refs > 0) {
433                 kprintf("ucom: Waiting for all detached TTY "
434                     "devices to have open fds closed.\n");
435                 usb_pause_mtx(&ucom_lock, hz);
436         }
437         lockmgr(&ucom_lock, LK_RELEASE);
438 }
439
440 static int
441 ucom_attach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
442 {
443         struct tty *tp;
444         char buf[32];                   /* temporary TTY device name buffer */
445
446         lwkt_gettoken(&tty_token);
447         
448         sc->sc_tty = tp = ttymalloc(sc->sc_tty);
449
450         if (tp == NULL) {
451                 lwkt_reltoken(&tty_token);
452                 return (ENOMEM);
453         }
454
455         tp->t_sc = (void *)sc;
456
457         tp->t_oproc = ucom_start;
458         tp->t_param = ucom_param;
459         tp->t_stop = ucom_stop;
460
461         /* Check if the client has a custom TTY name */
462         buf[0] = '\0';
463         if (sc->sc_callback->ucom_tty_name) {
464                 sc->sc_callback->ucom_tty_name(sc, buf,
465                     sizeof(buf), ssc->sc_unit, sc->sc_subunit);
466         }
467         if (buf[0] == 0) {
468                 /* Use default TTY name */
469                 if (ssc->sc_subunits > 1) {
470                         /* multiple modems in one */
471                         ksnprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u.%u",
472                             ssc->sc_unit, sc->sc_subunit);
473                 } else {
474                         /* single modem */
475                         ksnprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u",
476                             ssc->sc_unit);
477                 }
478         }
479
480         sc->sc_cdev = make_dev(&ucom_ops, ssc->sc_unit,
481                         UID_ROOT, GID_WHEEL, 0600, "ttyU%r", ssc->sc_unit);
482         sc->sc_cdev_init = make_dev(&ucom_ops, ssc->sc_unit | CONTROL_INIT_STATE,
483                         UID_ROOT, GID_WHEEL, 0600, "ttyiU%r", ssc->sc_unit);
484         sc->sc_cdev_lock = make_dev(&ucom_ops, ssc->sc_unit | CONTROL_LOCK_STATE,
485                         UID_ROOT, GID_WHEEL, 0600, "ttylU%r", ssc->sc_unit);
486         sc->sc_cdev2 = make_dev(&ucom_ops, ssc->sc_unit | CALLOUT_MASK,
487                         UID_UUCP, GID_DIALER, 0660, "cuaU%r", ssc->sc_unit);
488         sc->sc_cdev2_init = make_dev(&ucom_ops, ssc->sc_unit | CALLOUT_MASK | CONTROL_INIT_STATE,
489                         UID_UUCP, GID_DIALER, 0660, "cuaiU%r", ssc->sc_unit);
490         sc->sc_cdev2_lock = make_dev(&ucom_ops, ssc->sc_unit | CALLOUT_MASK | CONTROL_LOCK_STATE,
491                         UID_UUCP, GID_DIALER, 0660, "cualU%r", ssc->sc_unit);
492
493         sc->sc_cdev->si_tty = tp;
494         sc->sc_cdev_init->si_tty = tp;
495         sc->sc_cdev_lock->si_tty = tp;
496
497         sc->sc_cdev->si_drv1 = sc;
498         sc->sc_cdev_init->si_drv1 = sc;
499         sc->sc_cdev_lock->si_drv1 = sc;
500
501         sc->sc_cdev2->si_tty = tp;
502         sc->sc_cdev2_init->si_tty = tp;
503         sc->sc_cdev2_lock->si_tty = tp;
504
505         sc->sc_cdev2->si_drv1 = sc;
506         sc->sc_cdev2_init->si_drv1 = sc;
507         sc->sc_cdev2_lock->si_drv1 = sc;
508
509         sc->sc_tty = tp;
510         
511         DPRINTF("ttycreate: %s\n", buf);
512
513         /* Check if this device should be a console */
514         if ((ucom_cons_softc == NULL) && 
515             (ssc->sc_unit == ucom_cons_unit) &&
516             (sc->sc_subunit == ucom_cons_subunit)) {
517
518                 DPRINTF("unit %d subunit %d is console",
519                     ssc->sc_unit, sc->sc_subunit);
520
521                 ucom_cons_softc = sc;
522
523                 /* XXXDF
524                 tty_init_console(tp, ucom_cons_baud);
525                 */
526                 tp->t_termios.c_ispeed = ucom_cons_baud;
527                 tp->t_termios.c_ospeed = ucom_cons_baud;
528
529                 UCOM_MTX_LOCK(ucom_cons_softc);
530                 ucom_cons_rx_low = 0;
531                 ucom_cons_rx_high = 0;
532                 ucom_cons_tx_low = 0;
533                 ucom_cons_tx_high = 0;
534                 sc->sc_flag |= UCOM_FLAG_CONSOLE;
535                 
536                 ucom_open(ucom_cons_softc);
537                 ucom_param(tp, &tp->t_termios);
538                 UCOM_MTX_UNLOCK(ucom_cons_softc);
539         }
540
541         lwkt_reltoken(&tty_token);
542         return (0);
543 }
544
545 static void
546 ucom_detach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
547 {
548         struct tty *tp = sc->sc_tty;
549
550         DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
551
552         if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
553                 UCOM_MTX_LOCK(ucom_cons_softc);
554                 ucom_close(ucom_cons_softc);
555                 sc->sc_flag &= ~UCOM_FLAG_CONSOLE;
556                 UCOM_MTX_UNLOCK(ucom_cons_softc);
557                 ucom_cons_softc = NULL;
558         }
559
560         /* the config thread has been stopped when we get here */
561
562         UCOM_MTX_LOCK(sc);
563         sc->sc_flag |= UCOM_FLAG_GONE;
564         sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_LL_READY);
565         UCOM_MTX_UNLOCK(sc);
566
567         lwkt_gettoken(&tty_token);
568         if (tp != NULL) {
569                 ucom_close_refs++;
570
571                 UCOM_MTX_LOCK(sc);
572                 if (tp->t_state & TS_ISOPEN) {
573                         kprintf("device still open, forcing close\n");
574                         (*linesw[tp->t_line].l_close)(tp, 0);
575                         ttyclose(tp);
576                 }
577                 ucom_close(sc); /* close, if any */
578
579                 /*
580                  * make sure that read and write transfers are stopped
581                  */
582                 if (sc->sc_callback->ucom_stop_read) {
583                         (sc->sc_callback->ucom_stop_read) (sc);
584                 }
585                 if (sc->sc_callback->ucom_stop_write) {
586                         (sc->sc_callback->ucom_stop_write) (sc);
587                 }
588                 UCOM_MTX_UNLOCK(sc);
589         } else {
590                 DPRINTF("no tty\n");
591         }
592
593         dev_ops_remove_minor(&ucom_ops,ssc->sc_unit);
594
595         lwkt_reltoken(&tty_token);
596         ucom_unref(ssc);
597 }
598
599 void
600 ucom_set_pnpinfo_usb(struct ucom_super_softc *ssc, device_t dev)
601 {
602         char buf[64];
603         uint8_t iface_index;
604         struct usb_attach_arg *uaa;
605
606         ksnprintf(buf, sizeof(buf), "ttyname=" UCOM_TTY_PREFIX
607             "%d ttyports=%d", ssc->sc_unit, ssc->sc_subunits);
608
609         /* Store the PNP info in the first interface for the device */
610         uaa = device_get_ivars(dev);
611         iface_index = uaa->info.bIfaceIndex;
612     
613         if (usbd_set_pnpinfo(uaa->device, iface_index, buf) != 0)
614                 device_printf(dev, "Could not set PNP info\n");
615
616         /*
617          * The following information is also replicated in the PNP-info
618          * string which is registered above:
619          */
620         if (ssc->sc_sysctl_ttyname == NULL) {
621                 /*
622                 ssc->sc_sysctl_ttyname = SYSCTL_ADD_STRING(NULL,
623                     SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
624                     OID_AUTO, "ttyname", CTLFLAG_RD, ssc->sc_ttyname, 0,
625                     "TTY device basename");
626                 */
627         }
628         if (ssc->sc_sysctl_ttyports == NULL) {
629                 /*
630                 ssc->sc_sysctl_ttyports = SYSCTL_ADD_INT(NULL,
631                     SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
632                     OID_AUTO, "ttyports", CTLFLAG_RD,
633                     NULL, ssc->sc_subunits, "Number of ports");
634                 */
635         }
636 }
637
638 static void
639 ucom_queue_command(struct ucom_softc *sc,
640     usb_proc_callback_t *fn, struct termios *pt,
641     struct usb_proc_msg *t0, struct usb_proc_msg *t1)
642 {
643         struct ucom_super_softc *ssc = sc->sc_super;
644         struct ucom_param_task *task;
645
646         UCOM_MTX_ASSERT(sc, MA_OWNED);
647
648         if (usb_proc_is_gone(&ssc->sc_tq)) {
649                 DPRINTF("proc is gone\n");
650                 return;         /* nothing to do */
651         }
652         /* 
653          * NOTE: The task cannot get executed before we drop the
654          * "sc_lock" lock. It is safe to update fields in the message
655          * structure after that the message got queued.
656          */
657         task = (struct ucom_param_task *)
658           usb_proc_msignal(&ssc->sc_tq, t0, t1);
659
660         /* Setup callback and softc pointers */
661         task->hdr.pm_callback = fn;
662         task->sc = sc;
663
664         /* 
665          * Make a copy of the termios. This field is only present if
666          * the "pt" field is not NULL.
667          */
668         if (pt != NULL)
669                 task->termios_copy = *pt;
670
671         /*
672          * Closing the device should be synchronous.
673          */
674         if (fn == ucom_cfg_close)
675                 usb_proc_mwait(&ssc->sc_tq, t0, t1);
676
677         /*
678          * In case of multiple configure requests,
679          * keep track of the last one!
680          */
681         if (fn == ucom_cfg_start_transfers)
682                 sc->sc_last_start_xfer = &task->hdr;
683 }
684
685 static void
686 ucom_shutdown(struct ucom_softc *sc)
687 {
688         struct tty *tp = sc->sc_tty;
689
690         UCOM_MTX_ASSERT(sc, MA_OWNED);
691
692         DPRINTF("\n");
693
694         /*
695          * Hang up if necessary:
696          */
697         if (tp->t_termios.c_cflag & HUPCL) {
698                 ucom_modem(tp, 0, SER_DTR);
699         }
700 }
701
702 /*
703  * Return values:
704  *    0: normal
705  * else: taskqueue is draining or gone
706  */
707 uint8_t
708 ucom_cfg_is_gone(struct ucom_softc *sc)
709 {
710         struct ucom_super_softc *ssc = sc->sc_super;
711
712         return (usb_proc_is_gone(&ssc->sc_tq));
713 }
714
715 static void
716 ucom_cfg_start_transfers(struct usb_proc_msg *_task)
717 {
718         struct ucom_cfg_task *task = 
719             (struct ucom_cfg_task *)_task;
720         struct ucom_softc *sc = task->sc;
721
722         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
723                 return;
724         }
725         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
726                 /* TTY device closed */
727                 return;
728         }
729
730         if (_task == sc->sc_last_start_xfer)
731                 sc->sc_flag |= UCOM_FLAG_GP_DATA;
732
733         if (sc->sc_callback->ucom_start_read) {
734                 (sc->sc_callback->ucom_start_read) (sc);
735         }
736         if (sc->sc_callback->ucom_start_write) {
737                 (sc->sc_callback->ucom_start_write) (sc);
738         }
739 }
740
741 static void
742 ucom_start_transfers(struct ucom_softc *sc)
743 {
744         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
745                 return;
746         }
747         /*
748          * Make sure that data transfers are started in both
749          * directions:
750          */
751         if (sc->sc_callback->ucom_start_read) {
752                 (sc->sc_callback->ucom_start_read) (sc);
753         }
754         if (sc->sc_callback->ucom_start_write) {
755                 (sc->sc_callback->ucom_start_write) (sc);
756         }
757 }
758
759 static void
760 ucom_cfg_open(struct usb_proc_msg *_task)
761 {
762         struct ucom_cfg_task *task = 
763             (struct ucom_cfg_task *)_task;
764         struct ucom_softc *sc = task->sc;
765
766         DPRINTF("\n");
767
768         if (sc->sc_flag & UCOM_FLAG_LL_READY) {
769
770                 /* already opened */
771
772         } else {
773
774                 sc->sc_flag |= UCOM_FLAG_LL_READY;
775
776                 if (sc->sc_callback->ucom_cfg_open) {
777                         (sc->sc_callback->ucom_cfg_open) (sc);
778
779                         /* wait a little */
780                         usb_pause_mtx(sc->sc_lock, hz / 10);
781                 }
782         }
783 }
784
785 static int
786 ucom_dev_open(struct dev_open_args *ap)
787 {
788         cdev_t dev = ap->a_head.a_dev;
789         struct ucom_softc *sc = (struct ucom_softc *)dev->si_drv1;
790         int error;
791         int mynor;
792
793         error = 0;
794         mynor = minor(dev);
795
796         UCOM_MTX_LOCK(sc);
797         error = ucom_open(sc);
798         UCOM_MTX_UNLOCK(sc);
799
800         return error;   
801 }
802
803 static int
804 ucom_open(struct ucom_softc *sc)
805 {
806         int error;
807         struct tty *tp;
808
809         int mynor;
810
811         mynor = minor(sc->sc_cdev);
812
813         if (sc->sc_flag & UCOM_FLAG_GONE) {
814                 return (ENXIO);
815         }
816         if (sc->sc_flag & UCOM_FLAG_HL_READY) {
817                 /* already opened */
818                 return (0);
819         }
820         DPRINTF("tp = %p\n", sc->sc_tty);
821
822         if (sc->sc_callback->ucom_pre_open) {
823                 /*
824                  * give the lower layer a chance to disallow TTY open, for
825                  * example if the device is not present:
826                  */
827                 error = (sc->sc_callback->ucom_pre_open) (sc);
828                 if (error) {
829                         return (error);
830                 }
831         }
832         sc->sc_flag |= UCOM_FLAG_HL_READY;
833
834         lwkt_gettoken(&tty_token);
835         tp = sc->sc_tty;
836
837         crit_enter();
838
839         if (!ISSET(tp->t_state, TS_ISOPEN)) {
840                 struct termios t;
841
842                 tp->t_dev = reference_dev(sc->sc_cdev);
843         
844                 t = mynor & CALLOUT_MASK ? sc->sc_it_out : sc->sc_it_in;
845
846                 tp->t_ospeed = 0;
847                 ucom_param(tp, &t);
848                 tp->t_iflag = TTYDEF_IFLAG;
849                 tp->t_oflag = TTYDEF_OFLAG;
850                 tp->t_lflag = TTYDEF_LFLAG;
851                 ttychars(tp);
852                 ttsetwater(tp);
853
854                 /* Disable transfers */
855                 sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
856
857                 sc->sc_lsr = 0;
858                 sc->sc_msr = 0;
859                 sc->sc_mcr = 0;
860
861                 /* reset programmed line state */
862                 sc->sc_pls_curr = 0;
863                 sc->sc_pls_set = 0;
864                 sc->sc_pls_clr = 0;
865
866                 /* reset jitter buffer */
867                 sc->sc_jitterbuf_in = 0;
868                 sc->sc_jitterbuf_out = 0;
869
870                 ucom_queue_command(sc, ucom_cfg_open, NULL,
871                                 &sc->sc_open_task[0].hdr,
872                                 &sc->sc_open_task[1].hdr);
873
874                 /* Queue transfer enable command last */
875                 ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
876                                 &sc->sc_start_task[0].hdr, 
877                                 &sc->sc_start_task[1].hdr);
878
879                 ucom_modem(sc->sc_tty, SER_DTR | SER_RTS, 0);
880
881                 ucom_ring(sc, 0);
882
883                 ucom_break(sc, 0);
884                 
885                 ucom_status_change(sc);
886
887                 if (ISSET(sc->sc_msr, SER_DCD)) {
888                         (*linesw[tp->t_line].l_modem)(tp, 1);
889                 }
890         }
891         crit_exit();
892
893         error = ttyopen(sc->sc_cdev, tp);
894         if (error) {
895                 lwkt_reltoken(&tty_token);
896                 return (error);
897         }
898
899         error = (*linesw[tp->t_line].l_open)(sc->sc_cdev, tp);
900         if (error) {
901                 lwkt_reltoken(&tty_token);
902                 return (error);
903         }
904
905         disc_optim(tp, &tp->t_termios, sc);
906
907         lwkt_reltoken(&tty_token);
908         
909         return (0);
910 }
911
912 static void
913 ucom_cfg_close(struct usb_proc_msg *_task)
914 {
915         struct ucom_cfg_task *task = 
916             (struct ucom_cfg_task *)_task;
917         struct ucom_softc *sc = task->sc;
918
919         DPRINTF("\n");
920
921         if (sc->sc_flag & UCOM_FLAG_LL_READY) {
922                 sc->sc_flag &= ~UCOM_FLAG_LL_READY;
923                 if (sc->sc_callback->ucom_cfg_close)
924                         (sc->sc_callback->ucom_cfg_close) (sc);
925         } else {
926                 /* already closed */
927         }
928 }
929
930 static int
931 ucom_dev_close(struct dev_close_args *ap)
932 {
933         cdev_t dev = ap->a_head.a_dev;
934         struct ucom_softc *sc = (struct ucom_softc *)dev->si_drv1;
935         int error;
936
937         UCOM_MTX_LOCK(sc);
938         error = ucom_close(sc);
939         UCOM_MTX_UNLOCK(sc);
940         
941         return error;
942 }
943
944 static int
945 ucom_close(struct ucom_softc *sc)
946 {
947         struct tty *tp = sc->sc_tty;
948         int error = 0;
949
950         DPRINTF("tp=%p\n", tp);
951
952         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
953                 DPRINTF("tp=%p already closed\n", tp);
954                 return (error);
955         }
956         if (!ISSET(tp->t_state, TS_ISOPEN)) {
957                 return(error);
958         }
959         ucom_shutdown(sc);
960
961         ucom_queue_command(sc, ucom_cfg_close, NULL,
962             &sc->sc_close_task[0].hdr,
963             &sc->sc_close_task[1].hdr);
964
965         sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW);
966
967         if (sc->sc_callback->ucom_stop_read) {
968                 (sc->sc_callback->ucom_stop_read) (sc);
969         }
970
971         lwkt_gettoken(&tty_token);
972         crit_enter();
973         (*linesw[tp->t_line].l_close)(tp, 0); /* XXX: flags */
974         disc_optim(tp, &tp->t_termios, sc);
975         ttyclose(tp);
976         crit_exit();
977
978         if (tp->t_dev) {
979                 release_dev(tp->t_dev);
980                 tp->t_dev = NULL;
981         }
982         /* XXX: Detach wakeup */
983         lwkt_reltoken(&tty_token);
984
985         return (error);
986 }
987
988 #if 0 /* XXX */
989 static void
990 ucom_inwakeup(struct tty *tp)
991 {
992         struct ucom_softc *sc = tty_softc(tp);
993         uint16_t pos;
994
995         if (sc == NULL)
996                 return;
997
998         UCOM_MTX_ASSERT(sc, MA_OWNED);
999
1000         DPRINTF("tp=%p\n", tp);
1001
1002         if (ttydisc_can_bypass(tp) != 0 || 
1003             (sc->sc_flag & UCOM_FLAG_HL_READY) == 0 ||
1004             (sc->sc_flag & UCOM_FLAG_INWAKEUP) != 0) {
1005                 return;
1006         }
1007
1008         /* prevent recursion */
1009         sc->sc_flag |= UCOM_FLAG_INWAKEUP;
1010
1011         pos = sc->sc_jitterbuf_out;
1012
1013         while (sc->sc_jitterbuf_in != pos) {
1014                 int c;
1015
1016                 c = (char)sc->sc_jitterbuf[pos];
1017
1018                 if (ttydisc_rint(tp, c, 0) == -1)
1019                         break;
1020                 pos++;
1021                 if (pos >= UCOM_JITTERBUF_SIZE)
1022                         pos -= UCOM_JITTERBUF_SIZE;
1023         }
1024
1025         sc->sc_jitterbuf_out = pos;
1026
1027         /* clear RTS in async fashion */
1028         if ((sc->sc_jitterbuf_in == pos) && 
1029             (sc->sc_flag & UCOM_FLAG_RTS_IFLOW))
1030                 ucom_rts(sc, 0);
1031
1032         sc->sc_flag &= ~UCOM_FLAG_INWAKEUP;
1033 }
1034 #endif
1035
1036 static int
1037 ucom_dev_read(struct dev_read_args *ap)
1038 {
1039         cdev_t dev = ap->a_head.a_dev;
1040         struct ucom_softc *sc;
1041         struct tty *tp;
1042         int error;
1043
1044         sc = 0;
1045
1046         lwkt_gettoken(&tty_token);
1047
1048         tp = dev->si_tty;
1049         KKASSERT(tp!=NULL);
1050         sc = tp->t_sc;
1051         KKASSERT(sc!=NULL);
1052
1053         DPRINTF("tp = %p, flag = 0x%x\n", tp, ap->a_ioflag);
1054
1055         /* must not be locked in case it blocks */
1056         /*UCOM_MTX_LOCK(sc);*/
1057         error = (*linesw[tp->t_line].l_read)(tp, ap->a_uio, ap->a_ioflag);
1058         /*UCOM_MTX_UNLOCK(sc);*/
1059
1060         DPRINTF("error = %d\n", error);
1061
1062         lwkt_reltoken(&tty_token);
1063         return (error);
1064 }
1065
1066 static int
1067 ucom_dev_write(struct dev_write_args *ap)
1068 {
1069         cdev_t dev = ap->a_head.a_dev;
1070         struct ucom_softc *sc;
1071         struct tty *tp;
1072         int error;
1073
1074         lwkt_gettoken(&tty_token);
1075         tp = dev->si_tty;
1076         KKASSERT(tp!=NULL);
1077         sc = tp->t_sc;
1078         KKASSERT(sc!=NULL);
1079
1080         DPRINTF("tp = %p, flag = 0x%x\n", tp, ap->a_ioflag);
1081
1082         /* must not be locked in case it blocks */
1083         /*UCOM_MTX_LOCK(sc);*/
1084         error = (*linesw[tp->t_line].l_write)(tp, ap->a_uio, ap->a_ioflag);
1085         /*UCOM_MTX_UNLOCK(sc);*/
1086
1087         DPRINTF("ucomwrite: error = %d\n", error);
1088
1089         lwkt_reltoken(&tty_token);
1090         return (error);
1091 }
1092
1093 static int
1094 ucom_dev_ioctl(struct dev_ioctl_args *ap)
1095 {
1096         cdev_t dev = ap->a_head.a_dev;
1097         struct ucom_softc *sc = (struct ucom_softc *)dev->si_drv1;
1098         u_long cmd = ap->a_cmd;
1099         caddr_t data = ap->a_data;
1100         struct tty *tp = sc->sc_tty;
1101         int d;
1102         int error;
1103         int mynor;
1104
1105         UCOM_MTX_LOCK(sc);
1106         lwkt_gettoken(&tty_token);
1107
1108         mynor = minor(dev);
1109
1110         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1111                 lwkt_reltoken(&tty_token);
1112                 UCOM_MTX_UNLOCK(sc);
1113                 return (EIO);
1114         }
1115         DPRINTF("cmd = 0x%08lx\n", cmd);
1116         if (mynor & CONTROL_MASK) {
1117                 struct termios *ct;
1118
1119                 switch (mynor & CONTROL_MASK) {
1120                 case CONTROL_INIT_STATE:
1121                         ct = mynor & CALLOUT_MASK ? &sc->sc_it_out : &sc->sc_it_in;
1122                         break;
1123                 case CONTROL_LOCK_STATE:
1124                         ct = mynor & CALLOUT_MASK ? &sc->sc_lt_out : &sc->sc_lt_in;
1125                         break;
1126                 default:
1127                         lwkt_reltoken(&tty_token);
1128                         UCOM_MTX_UNLOCK(sc);
1129                         return (ENODEV);        /* /dev/nodev */
1130                 }
1131                 switch (ap->a_cmd) {
1132                 case TIOCSETA:
1133                         error = priv_check_cred(ap->a_cred, PRIV_ROOT, 0);
1134                         if (error != 0) {
1135                                 lwkt_reltoken(&tty_token);
1136                                 UCOM_MTX_UNLOCK(sc);
1137                                 return (error);
1138                         }
1139                         *ct = *(struct termios *)data;
1140                         lwkt_reltoken(&tty_token);
1141                         UCOM_MTX_UNLOCK(sc);
1142                         return (0);
1143                 case TIOCGETA:
1144                         *(struct termios *)data = *ct;
1145                         lwkt_reltoken(&tty_token);
1146                         UCOM_MTX_UNLOCK(sc);
1147                         return (0);
1148                 case TIOCGETD:
1149                         *(int *)data = TTYDISC;
1150                         lwkt_reltoken(&tty_token);
1151                         UCOM_MTX_UNLOCK(sc);
1152                         return (0);
1153                 case TIOCGWINSZ:
1154                         bzero(data, sizeof(struct winsize));
1155                         lwkt_reltoken(&tty_token);
1156                         UCOM_MTX_UNLOCK(sc);
1157                         return (0);
1158                 default:
1159                         lwkt_reltoken(&tty_token);
1160                         UCOM_MTX_UNLOCK(sc);
1161                         return (ENOTTY);
1162                 }
1163         }
1164
1165         error = (*linesw[tp->t_line].l_ioctl)(tp, ap->a_cmd, ap->a_data,
1166                                               ap->a_fflag, ap->a_cred);
1167
1168         if (error != ENOIOCTL) {
1169                 DPRINTF("ucomioctl: l_ioctl: error = %d\n", error);
1170                 lwkt_reltoken(&tty_token);
1171                 UCOM_MTX_UNLOCK(sc);
1172                 return (error);
1173         }
1174
1175         crit_enter();
1176
1177         error = ttioctl(tp, ap->a_cmd, ap->a_data, ap->a_fflag);
1178         disc_optim(tp, &tp->t_termios, sc);
1179         if (error != ENOIOCTL) {
1180                 crit_exit();
1181                 DPRINTF("ucomioctl: ttioctl: error = %d\n", error);
1182                 lwkt_reltoken(&tty_token);
1183                 UCOM_MTX_UNLOCK(sc);
1184
1185                 return (error);
1186         }
1187
1188         error = 0;
1189
1190         switch (cmd) {
1191 #if 0 /* XXXDF */
1192         case TIOCSRING:
1193                 ucom_ring(sc, 1);
1194                 error = 0;
1195                 break;
1196         case TIOCCRING:
1197                 ucom_ring(sc, 0);
1198                 error = 0;
1199                 break;
1200 #endif
1201         case TIOCSBRK:
1202                 ucom_break(sc, 1);
1203                 error = 0;
1204                 break;
1205         case TIOCCBRK:
1206                 ucom_break(sc, 0);
1207                 error = 0;
1208                 break;
1209         case TIOCSDTR:
1210                 ucom_dtr(sc, 1);
1211                 break;
1212         case TIOCCDTR:
1213                 ucom_dtr(sc, 0);
1214                 break;
1215         case TIOCMSET:
1216                 d = *(int *)ap->a_data;
1217                 DPRINTF("ucomioctl: TIOCMSET, 0x%x\n", d);
1218                 ucom_modem(tp, ucom_fromtio(d), 0);
1219                 break;
1220         case TIOCMGET:
1221                 d = ucom_modem(tp, 0, 0);
1222                 DPRINTF("ucomioctl: TIOCMGET, 0x%x\n", d);
1223                 *(int *)ap->a_data = ucom_totio(d);
1224                 ucom_status_change(sc);
1225                 break;
1226         case TIOCMBIS:
1227                 d = *(int *)ap->a_data;
1228                 ucom_modem(tp, ucom_fromtio(d), 0);
1229                 break;
1230         case TIOCMBIC:
1231                 d = *(int *)ap->a_data;
1232                 ucom_modem(tp, 0, ucom_fromtio(d));
1233                 break;
1234         default:
1235                 if (sc->sc_callback->ucom_ioctl) {
1236                         error = (sc->sc_callback->ucom_ioctl)
1237                             (sc, cmd, data, 0, curthread);
1238                         if (error>=0) {
1239                                 crit_exit();
1240
1241                                 lwkt_reltoken(&tty_token);
1242                                 UCOM_MTX_UNLOCK(sc);
1243
1244                                 return(error);  
1245                         }
1246                 } else {
1247                         error = ENOIOCTL;
1248                 }
1249                 if (error == ENOIOCTL)
1250                         error = pps_ioctl(cmd, data, &sc->sc_pps);
1251                 break;
1252         }
1253         crit_exit();
1254
1255         lwkt_reltoken(&tty_token);
1256         UCOM_MTX_UNLOCK(sc);
1257
1258         return (error);
1259 }
1260
1261 static int
1262 ucom_totio(int bits)
1263 {
1264         int rbits = 0;
1265
1266         SET(bits, TIOCM_LE);
1267
1268         if (ISSET(bits, SER_DTR)) {
1269                 SET(rbits, TIOCM_DTR);
1270         }
1271         if (ISSET(bits, SER_RTS)) {
1272                 SET(rbits, TIOCM_RTS);
1273         }
1274         if (ISSET(bits, SER_CTS)) {
1275                 SET(rbits, TIOCM_CTS);
1276         }
1277         if (ISSET(bits, SER_DCD)) {
1278                 SET(rbits, TIOCM_CD);
1279         }
1280         if (ISSET(bits, SER_DSR)) {
1281                 SET(rbits, TIOCM_DSR);
1282         }
1283         if (ISSET(bits, SER_RI)) {
1284                 SET(rbits, TIOCM_RI);
1285         }
1286
1287         return (rbits);
1288 }
1289
1290 static int
1291 ucom_fromtio(int bits)
1292 {
1293         int rbits = 0;
1294
1295         if (ISSET(bits, TIOCM_DTR)) {
1296                 SET(rbits, SER_DTR);
1297         }
1298         if (ISSET(bits, TIOCM_RTS)) {
1299                 SET(rbits, SER_RTS);
1300         }
1301         if (ISSET(bits, TIOCM_CTS)) {
1302                 SET(rbits, SER_CTS);
1303         }
1304         if (ISSET(bits, TIOCM_CD)) {
1305                 SET(rbits, SER_DCD);
1306         }
1307         if (ISSET(bits, TIOCM_DSR)) {
1308                 SET(rbits, SER_DSR);
1309         }
1310         if (ISSET(bits, TIOCM_RI)) {
1311                 SET(rbits, SER_RI);
1312         }
1313
1314         return (rbits);
1315 }
1316
1317 static int
1318 ucom_modem(struct tty *tp, int sigon, int sigoff)
1319 {
1320         struct ucom_softc *sc = (struct ucom_softc *)tp->t_sc;
1321         uint8_t onoff;
1322
1323         UCOM_MTX_ASSERT(sc, MA_OWNED);
1324
1325         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1326                 return (0);
1327         }
1328         if ((sigon == 0) && (sigoff == 0)) {
1329
1330                 if (sc->sc_mcr & SER_DTR) {
1331                         sigon |= SER_DTR;
1332                 }
1333                 if (sc->sc_mcr & SER_RTS) {
1334                         sigon |= SER_RTS;
1335                 }
1336                 if (sc->sc_msr & SER_CTS) {
1337                         sigon |= SER_CTS;
1338                 }
1339                 if (sc->sc_msr & SER_DCD) {
1340                         sigon |= SER_DCD;
1341                 }
1342                 if (sc->sc_msr & SER_DSR) {
1343                         sigon |= SER_DSR;
1344                 }
1345                 if (sc->sc_msr & SER_RI) {
1346                         sigon |= SER_RI;
1347                 }
1348                 return (sigon);
1349         }
1350         if (sigon & SER_DTR) {
1351                 sc->sc_mcr |= SER_DTR;
1352         }
1353         if (sigoff & SER_DTR) {
1354                 sc->sc_mcr &= ~SER_DTR;
1355         }
1356         if (sigon & SER_RTS) {
1357                 sc->sc_mcr |= SER_RTS;
1358         }
1359         if (sigoff & SER_RTS) {
1360                 sc->sc_mcr &= ~SER_RTS;
1361         }
1362         onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
1363         ucom_dtr(sc, onoff);
1364
1365         onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
1366         ucom_rts(sc, onoff);
1367
1368         return (0);
1369 }
1370
1371 static void
1372 ucom_cfg_line_state(struct usb_proc_msg *_task)
1373 {
1374         struct ucom_cfg_task *task = 
1375             (struct ucom_cfg_task *)_task;
1376         struct ucom_softc *sc = task->sc;
1377         uint8_t notch_bits;
1378         uint8_t any_bits;
1379         uint8_t prev_value;
1380         uint8_t last_value;
1381         uint8_t mask;
1382
1383         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
1384                 return;
1385         }
1386
1387         mask = 0;
1388         /* compute callback mask */
1389         if (sc->sc_callback->ucom_cfg_set_dtr)
1390                 mask |= UCOM_LS_DTR;
1391         if (sc->sc_callback->ucom_cfg_set_rts)
1392                 mask |= UCOM_LS_RTS;
1393         if (sc->sc_callback->ucom_cfg_set_break)
1394                 mask |= UCOM_LS_BREAK;
1395         if (sc->sc_callback->ucom_cfg_set_ring)
1396                 mask |= UCOM_LS_RING;
1397
1398         /* compute the bits we are to program */
1399         notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
1400         any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
1401         prev_value = sc->sc_pls_curr ^ notch_bits;
1402         last_value = sc->sc_pls_curr;
1403
1404         /* reset programmed line state */
1405         sc->sc_pls_curr = 0;
1406         sc->sc_pls_set = 0;
1407         sc->sc_pls_clr = 0;
1408
1409         /* ensure that we don't lose any levels */
1410         if (notch_bits & UCOM_LS_DTR)
1411                 sc->sc_callback->ucom_cfg_set_dtr(sc,
1412                     (prev_value & UCOM_LS_DTR) ? 1 : 0);
1413         if (notch_bits & UCOM_LS_RTS)
1414                 sc->sc_callback->ucom_cfg_set_rts(sc,
1415                     (prev_value & UCOM_LS_RTS) ? 1 : 0);
1416         if (notch_bits & UCOM_LS_BREAK)
1417                 sc->sc_callback->ucom_cfg_set_break(sc,
1418                     (prev_value & UCOM_LS_BREAK) ? 1 : 0);
1419         if (notch_bits & UCOM_LS_RING)
1420                 sc->sc_callback->ucom_cfg_set_ring(sc,
1421                     (prev_value & UCOM_LS_RING) ? 1 : 0);
1422
1423         /* set last value */
1424         if (any_bits & UCOM_LS_DTR)
1425                 sc->sc_callback->ucom_cfg_set_dtr(sc,
1426                     (last_value & UCOM_LS_DTR) ? 1 : 0);
1427         if (any_bits & UCOM_LS_RTS)
1428                 sc->sc_callback->ucom_cfg_set_rts(sc,
1429                     (last_value & UCOM_LS_RTS) ? 1 : 0);
1430         if (any_bits & UCOM_LS_BREAK)
1431                 sc->sc_callback->ucom_cfg_set_break(sc,
1432                     (last_value & UCOM_LS_BREAK) ? 1 : 0);
1433         if (any_bits & UCOM_LS_RING)
1434                 sc->sc_callback->ucom_cfg_set_ring(sc,
1435                     (last_value & UCOM_LS_RING) ? 1 : 0);
1436 }
1437
1438 static void
1439 ucom_line_state(struct ucom_softc *sc,
1440     uint8_t set_bits, uint8_t clear_bits)
1441 {
1442         UCOM_MTX_ASSERT(sc, MA_OWNED);
1443
1444         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1445                 return;
1446         }
1447
1448         DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
1449
1450         /* update current programmed line state */
1451         sc->sc_pls_curr |= set_bits;
1452         sc->sc_pls_curr &= ~clear_bits;
1453         sc->sc_pls_set |= set_bits;
1454         sc->sc_pls_clr |= clear_bits;
1455
1456         /* defer driver programming */
1457         ucom_queue_command(sc, ucom_cfg_line_state, NULL,
1458             &sc->sc_line_state_task[0].hdr, 
1459             &sc->sc_line_state_task[1].hdr);
1460 }
1461
1462 static void
1463 ucom_ring(struct ucom_softc *sc, uint8_t onoff)
1464 {
1465         DPRINTF("onoff = %d\n", onoff);
1466
1467         if (onoff)
1468                 ucom_line_state(sc, UCOM_LS_RING, 0);
1469         else
1470                 ucom_line_state(sc, 0, UCOM_LS_RING);
1471 }
1472
1473 static void
1474 ucom_break(struct ucom_softc *sc, uint8_t onoff)
1475 {
1476         DPRINTF("onoff = %d\n", onoff);
1477
1478         if (onoff)
1479                 ucom_line_state(sc, UCOM_LS_BREAK, 0);
1480         else
1481                 ucom_line_state(sc, 0, UCOM_LS_BREAK);
1482 }
1483
1484 static void
1485 ucom_dtr(struct ucom_softc *sc, uint8_t onoff)
1486 {
1487         DPRINTF("onoff = %d\n", onoff);
1488
1489         if (onoff)
1490                 ucom_line_state(sc, UCOM_LS_DTR, 0);
1491         else
1492                 ucom_line_state(sc, 0, UCOM_LS_DTR);
1493 }
1494
1495 static void
1496 ucom_rts(struct ucom_softc *sc, uint8_t onoff)
1497 {
1498         DPRINTF("onoff = %d\n", onoff);
1499
1500         if (onoff)
1501                 ucom_line_state(sc, UCOM_LS_RTS, 0);
1502         else
1503                 ucom_line_state(sc, 0, UCOM_LS_RTS);
1504 }
1505
1506 static void
1507 ucom_cfg_status_change(struct usb_proc_msg *_task)
1508 {
1509         struct ucom_cfg_task *task = 
1510             (struct ucom_cfg_task *)_task;
1511         struct ucom_softc *sc = task->sc;
1512         struct tty *tp;
1513         uint8_t new_msr;
1514         uint8_t new_lsr;
1515         uint8_t msr_delta;
1516         uint8_t lsr_delta;
1517
1518         tp = sc->sc_tty;
1519
1520         UCOM_MTX_ASSERT(sc, MA_OWNED);
1521
1522         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
1523                 return;
1524         }
1525         if (sc->sc_callback->ucom_cfg_get_status == NULL) {
1526                 return;
1527         }
1528         /* get status */
1529
1530         new_msr = 0;
1531         new_lsr = 0;
1532
1533         (sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr);
1534
1535         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1536                 /* TTY device closed */
1537                 return;
1538         }
1539         msr_delta = (sc->sc_msr ^ new_msr);
1540         lsr_delta = (sc->sc_lsr ^ new_lsr);
1541
1542         sc->sc_msr = new_msr;
1543         sc->sc_lsr = new_lsr;
1544
1545 #if 0   /* missing pps_capture */
1546         /*
1547          * Time pulse counting support. Note that both CTS and DCD are
1548          * active-low signals. The status bit is high to indicate that
1549          * the signal on the line is low, which corresponds to a PPS
1550          * clear event.
1551          */
1552         switch(ucom_pps_mode) {
1553         case 1:
1554                 if ((sc->sc_pps.ppsparam.mode & PPS_CAPTUREBOTH) &&
1555                     (msr_delta & SER_CTS)) {
1556                         pps_capture(&sc->sc_pps);
1557                         pps_event(&sc->sc_pps, (sc->sc_msr & SER_CTS) ?
1558                             PPS_CAPTURECLEAR : PPS_CAPTUREASSERT);
1559                 }
1560                 break;
1561         case 2:
1562                 if ((sc->sc_pps.ppsparam.mode & PPS_CAPTUREBOTH) &&
1563                     (msr_delta & SER_DCD)) {
1564                         pps_capture(&sc->sc_pps);
1565                         pps_event(&sc->sc_pps, (sc->sc_msr & SER_DCD) ?
1566                             PPS_CAPTURECLEAR : PPS_CAPTUREASSERT);
1567                 }
1568                 break;
1569         default:
1570                 break;
1571         }
1572 #endif
1573
1574         if (msr_delta & SER_DCD) {
1575
1576                 int onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
1577
1578                 DPRINTF("DCD changed to %d\n", onoff);
1579
1580                 (*linesw[tp->t_line].l_modem)(tp, onoff);
1581         }
1582
1583         if ((lsr_delta & ULSR_BI) && (sc->sc_lsr & ULSR_BI)) {
1584
1585                 DPRINTF("BREAK detected\n");
1586                 (*linesw[tp->t_line].l_rint)(0, tp);
1587
1588                 /*
1589                 ttydisc_rint(tp, 0, TRE_BREAK);
1590                 ttydisc_rint_done(tp);
1591                 */
1592         }
1593
1594         if ((lsr_delta & ULSR_FE) && (sc->sc_lsr & ULSR_FE)) {
1595
1596                 DPRINTF("Frame error detected\n");
1597                 (*linesw[tp->t_line].l_rint)(0, tp);
1598
1599                 /*
1600                 ttydisc_rint(tp, 0, TRE_FRAMING);
1601                 ttydisc_rint_done(tp);
1602                 */
1603         }
1604
1605         if ((lsr_delta & ULSR_PE) && (sc->sc_lsr & ULSR_PE)) {
1606
1607                 DPRINTF("Parity error detected\n");
1608                 (*linesw[tp->t_line].l_rint)(0, tp);
1609                 /*
1610                 ttydisc_rint(tp, 0, TRE_PARITY);
1611                 ttydisc_rint_done(tp);
1612                 */
1613         }
1614 }
1615
1616 void
1617 ucom_status_change(struct ucom_softc *sc)
1618 {
1619         UCOM_MTX_ASSERT(sc, MA_OWNED);
1620
1621         if (sc->sc_flag & UCOM_FLAG_CONSOLE)
1622                 return;         /* not supported */
1623
1624         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1625                 return;
1626         }
1627         DPRINTF("\n");
1628
1629         ucom_queue_command(sc, ucom_cfg_status_change, NULL,
1630             &sc->sc_status_task[0].hdr,
1631             &sc->sc_status_task[1].hdr);
1632 }
1633
1634 static void
1635 ucom_cfg_param(struct usb_proc_msg *_task)
1636 {
1637         struct ucom_param_task *task = 
1638             (struct ucom_param_task *)_task;
1639         struct ucom_softc *sc = task->sc;
1640
1641         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
1642                 return;
1643         }
1644         if (sc->sc_callback->ucom_cfg_param == NULL) {
1645                 return;
1646         }
1647
1648         (sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy);
1649
1650         /* wait a little */
1651         usb_pause_mtx(sc->sc_lock, hz / 10);
1652 }
1653
1654 static int
1655 ucom_param(struct tty *tp, struct termios *t)
1656 {
1657         struct ucom_softc *sc = (struct ucom_softc *)tp->t_sc;
1658         uint8_t opened;
1659         int error;
1660
1661         lwkt_gettoken(&tty_token);
1662         UCOM_MTX_ASSERT(sc, MA_OWNED);
1663
1664         opened = 0;
1665         error = 0;
1666
1667         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1668
1669                 /* XXX the TTY layer should call "open()" first! */
1670                 /*
1671                  * Not quite: Its ordering is partly backwards, but
1672                  * some parameters must be set early in ttydev_open(),
1673                  * possibly before calling ttydevsw_open().
1674                  */
1675                 error = ucom_open(sc);
1676
1677                 if (error) {
1678                         goto done;
1679                 }
1680                 opened = 1;
1681         }
1682         DPRINTF("sc = %p\n", sc);
1683
1684         /* Check requested parameters. */
1685         if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
1686                 /* XXX c_ospeed == 0 is perfectly valid. */
1687                 DPRINTF("mismatch ispeed and ospeed\n");
1688                 error = EINVAL;
1689                 goto done;
1690         }
1691         t->c_ispeed = t->c_ospeed;
1692
1693         if (sc->sc_callback->ucom_pre_param) {
1694                 /* Let the lower layer verify the parameters */
1695                 error = (sc->sc_callback->ucom_pre_param) (sc, t);
1696                 if (error) {
1697                         DPRINTF("callback error = %d\n", error);
1698                         goto done;
1699                 }
1700         }
1701
1702         /* Disable transfers */
1703         sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
1704
1705         /* Queue baud rate programming command first */
1706         ucom_queue_command(sc, ucom_cfg_param, t,
1707             &sc->sc_param_task[0].hdr,
1708             &sc->sc_param_task[1].hdr);
1709
1710         /* Queue transfer enable command last */
1711         ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
1712             &sc->sc_start_task[0].hdr, 
1713             &sc->sc_start_task[1].hdr);
1714
1715         if (t->c_cflag & CRTS_IFLOW) {
1716                 sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
1717         } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
1718                 sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
1719                 ucom_modem(tp, SER_RTS, 0);
1720         }
1721 done:
1722         if (error) {
1723                 if (opened) {
1724                         ucom_close(sc);
1725                 }
1726         }
1727
1728         lwkt_reltoken(&tty_token);      
1729         return (error);
1730 }
1731
1732 static void
1733 ucom_start(struct tty *tp)
1734 {
1735         struct ucom_softc *sc = (struct ucom_softc *)tp->t_sc;
1736         int didlock;
1737
1738         /* may be called locked or unlocked */
1739         if (lockowned(sc->sc_lock)) {
1740                 didlock = 0;
1741         } else {
1742                 UCOM_MTX_LOCK(sc);
1743                 didlock = 1;
1744         }
1745         /* UCOM_MTX_ASSERT(sc, MA_OWNED); */
1746
1747         DPRINTF("sc = %p\n", sc);
1748
1749         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1750                 /* The higher layer is not ready */
1751                 if (didlock)
1752                         UCOM_MTX_UNLOCK(sc);
1753                 return;
1754         }
1755
1756         lwkt_gettoken(&tty_token);
1757         crit_enter();
1758
1759         if (tp->t_state & TS_TBLOCK) {
1760                 if (ISSET(sc->sc_mcr, SER_RTS) &&
1761                     ISSET(sc->sc_flag, UCOM_FLAG_RTS_IFLOW)) {
1762                         DPRINTF("ucom_start: clear RTS\n");
1763                         (void)ucom_modem(tp, 0, SER_RTS);
1764                 }
1765         } else {
1766                 if (!ISSET(sc->sc_mcr, SER_RTS) &&
1767                     tp->t_rawq.c_cc <= tp->t_ilowat &&
1768                     ISSET(sc->sc_flag, UCOM_FLAG_RTS_IFLOW)) {
1769                         DPRINTF("ucom_start: set RTS\n");
1770                         (void)ucom_modem(tp, SER_RTS, 0);
1771                 }
1772         }
1773
1774         if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) {
1775                 ttwwakeup(tp);
1776                 DPRINTF("ucom_start: stopped\n");
1777                 goto out;
1778         }
1779
1780         if (tp->t_outq.c_cc <= tp->t_olowat) {
1781                 if (ISSET(tp->t_state, TS_SO_OLOWAT)) {
1782                         CLR(tp->t_state, TS_SO_OLOWAT);
1783                         wakeup(TSA_OLOWAT(tp));
1784                 }
1785                 KNOTE(&tp->t_wkq.ki_note, 0);
1786                 if (tp->t_outq.c_cc == 0) {
1787                         if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) ==
1788                                         TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) {
1789                                 CLR(tp->t_state, TS_SO_OCOMPLETE);
1790                                 wakeup(TSA_OCOMPLETE(tp));
1791                         }
1792                         goto out;
1793                 }
1794         }
1795
1796         DPRINTF("about to start write?\n");
1797         ucom_start_transfers(sc);
1798
1799         ttwwakeup(tp);
1800
1801 out:
1802         crit_exit();
1803         lwkt_reltoken(&tty_token);
1804         if (didlock)
1805                 UCOM_MTX_UNLOCK(sc);
1806 }
1807
1808 static void
1809 ucom_stop(struct tty *tp, int flag)
1810 {
1811         struct ucom_softc *sc = (struct ucom_softc *)tp->t_sc;
1812
1813         DPRINTF("sc = %p, x = 0x%x\n", sc, flag);
1814
1815         lwkt_gettoken(&tty_token);
1816         if (flag & FREAD) {
1817                 /*   
1818                  * This is just supposed to flush pending receive data,
1819                  * not stop the reception of data entirely!
1820                  */
1821                 DPRINTF("read\n");
1822                 if (sc->sc_callback->ucom_stop_read) {
1823                         (sc->sc_callback->ucom_stop_read) (sc);
1824                 }
1825                 if (sc->sc_callback->ucom_start_read) {
1826                         (sc->sc_callback->ucom_start_read) (sc);
1827                 }
1828 /*              ucomstopread(sc);
1829                 ucomstartread(sc);
1830 */      }    
1831
1832         if (flag & FWRITE) {
1833                 DPRINTF("write\n");
1834                 crit_enter();
1835                 if (ISSET(tp->t_state, TS_BUSY)) {
1836                         /* XXX do what? */
1837                         if (!ISSET(tp->t_state, TS_TTSTOP))
1838                                 SET(tp->t_state, TS_FLUSH);
1839                 }    
1840                 crit_exit();
1841         }    
1842
1843         DPRINTF("done\n");
1844         lwkt_reltoken(&tty_token);
1845 }
1846
1847 /*------------------------------------------------------------------------*
1848  *      ucom_get_data
1849  * Input values:
1850  * len: maximum length of data to get
1851  * 
1852  * Get data from the TTY layer
1853  *
1854  * Return values:
1855  * 0: No data is available.
1856  * Else: Data is available.
1857  *------------------------------------------------------------------------*/
1858
1859 /* Copy data from the tty layer to usb */
1860 uint8_t
1861 ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1862     uint32_t offset, uint32_t len, uint32_t *actlen)
1863 {
1864         struct usb_page_search res;
1865         struct tty *tp = sc->sc_tty;
1866         uint32_t cnt;
1867         uint32_t offset_orig;
1868         
1869         DPRINTF("\n");
1870
1871         UCOM_MTX_ASSERT(sc, MA_OWNED);
1872         if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1873                 unsigned int temp;
1874
1875                 /* get total TX length */
1876
1877                 temp = ucom_cons_tx_high - ucom_cons_tx_low;
1878                 temp %= UCOM_CONS_BUFSIZE;
1879
1880                 /* limit TX length */
1881
1882                 if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_tx_low))
1883                         temp = (UCOM_CONS_BUFSIZE - ucom_cons_tx_low);
1884
1885                 if (temp > len)
1886                         temp = len;
1887
1888                 /* copy in data */
1889
1890                 usbd_copy_in(pc, offset, ucom_cons_tx_buf + ucom_cons_tx_low, temp);
1891
1892                 /* update counters */
1893
1894                 ucom_cons_tx_low += temp;
1895                 ucom_cons_tx_low %= UCOM_CONS_BUFSIZE;
1896
1897                 /* store actual length */
1898
1899                 *actlen = temp;
1900
1901                 return (temp ? 1 : 0);
1902         }
1903
1904         if (tty_gone(tp) ||
1905             !(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
1906                 actlen[0] = 0;
1907                 return (0);             /* multiport device polling */
1908         }
1909
1910         offset_orig = offset;
1911
1912         lwkt_gettoken(&tty_token);
1913         crit_enter();
1914         while (len != 0) {
1915                 usbd_get_page(pc, offset, &res);
1916
1917                 /* Buffer bigger than max requested data */
1918                 if (res.length > len) {
1919                         res.length = len;
1920                 }
1921                 /* copy data directly into USB buffer */
1922                 SET(tp->t_state, TS_BUSY);
1923                 cnt = q_to_b(&tp->t_outq, res.buffer, len); 
1924                 if (cnt == 0) {
1925                         DPRINTF("ucom_get_data: cnt == 0\n");
1926                         CLR(tp->t_state, TS_BUSY);
1927                         break;
1928                 }
1929
1930                 CLR(tp->t_state, TS_BUSY);
1931
1932                 /* XXX mp: This breaks avrdude,
1933                            does the flush need to happen
1934                            elsewhere?
1935                 if (ISSET(tp->t_state, TS_FLUSH))
1936                         CLR(tp->t_state, TS_FLUSH);
1937                 else
1938                         ndflush(&tp->t_outq,cnt);
1939                 */
1940                
1941                 offset += cnt;
1942                 len -= cnt;
1943
1944                 if (cnt < res.length) {
1945                         /* end of buffer */
1946                         break;
1947                 }
1948         }
1949         crit_exit();
1950         lwkt_reltoken(&tty_token);
1951
1952         actlen[0] = offset - offset_orig;
1953
1954         DPRINTF("cnt=%d\n", actlen[0]);
1955
1956         if (actlen[0] == 0) {
1957                 return (0);
1958         }
1959         return (1);
1960 }
1961
1962 /*
1963  * Write data to the tty layer
1964  */
1965
1966 void
1967 ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1968     uint32_t offset, uint32_t len)
1969 {
1970         struct usb_page_search res;
1971         struct tty *tp = sc->sc_tty;
1972         char *buf;
1973         uint32_t cnt;
1974         int lostcc;
1975
1976         DPRINTF("\n");
1977
1978         UCOM_MTX_ASSERT(sc, MA_OWNED);
1979         lwkt_gettoken(&tty_token);
1980
1981         if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1982                 unsigned int temp;
1983
1984                 /* get maximum RX length */
1985
1986                 temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_rx_high + ucom_cons_rx_low;
1987                 temp %= UCOM_CONS_BUFSIZE;
1988
1989                 /* limit RX length */
1990
1991                 if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_rx_high))
1992                         temp = (UCOM_CONS_BUFSIZE - ucom_cons_rx_high);
1993
1994                 if (temp > len)
1995                         temp = len;
1996
1997                 /* copy out data */
1998
1999                 usbd_copy_out(pc, offset, ucom_cons_rx_buf + ucom_cons_rx_high, temp);
2000
2001                 /* update counters */
2002
2003                 ucom_cons_rx_high += temp;
2004                 ucom_cons_rx_high %= UCOM_CONS_BUFSIZE;
2005
2006                 lwkt_reltoken(&tty_token);
2007                 return;
2008         }
2009
2010         if (tty_gone(tp)) {
2011                 lwkt_reltoken(&tty_token);
2012                 return;                 /* multiport device polling */
2013         }
2014         if (len == 0) {
2015                 lwkt_reltoken(&tty_token);
2016                 return;                 /* no data */
2017         }
2018
2019         /* set a flag to prevent recursation ? */
2020
2021         crit_enter();
2022         while (len > 0) {
2023                 usbd_get_page(pc, offset, &res);
2024
2025                 if (res.length > len) {
2026                         res.length = len;
2027                 }
2028                 len -= res.length;
2029                 offset += res.length;
2030
2031                 /* pass characters to tty layer */
2032
2033                 buf = res.buffer;
2034                 cnt = res.length;
2035
2036                 /* first check if we can pass the buffer directly */
2037
2038                 if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
2039                         /* clear any jitter buffer */
2040                         sc->sc_jitterbuf_in = 0;
2041                         sc->sc_jitterbuf_out = 0;
2042
2043                         if (tp->t_rawq.c_cc + cnt > tp->t_ihiwat
2044                             && (sc->sc_flag & UCOM_FLAG_RTS_IFLOW
2045                                 || tp->t_iflag & IXOFF)
2046                             && !(tp->t_state & TS_TBLOCK))
2047                                ttyblock(tp);
2048                         lostcc = b_to_q((char *)buf, cnt, &tp->t_rawq);
2049                         tp->t_rawcc += cnt;
2050                         if (sc->hotchar) {
2051                                 while (cnt) {
2052                                         if (*buf == sc->hotchar)
2053                                                 break;
2054                                         --cnt;
2055                                         ++buf;
2056                                 }
2057                                 if (cnt) 
2058                                         setsofttty();
2059                         }
2060                         ttwakeup(tp);
2061                         if (tp->t_state & TS_TTSTOP
2062                             && (tp->t_iflag & IXANY
2063                                 || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
2064                                 tp->t_state &= ~TS_TTSTOP;
2065                                 tp->t_lflag &= ~FLUSHO;
2066                                 ucom_start(tp);
2067                         }       
2068                         if (lostcc > 0)
2069                                 kprintf("lost %d chars\n", lostcc);
2070
2071                         /*
2072                         if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
2073                                 DPRINTF("tp=%p, data lost\n", tp);
2074                         }
2075                         */
2076                         continue;
2077                 } else {
2078                 /* need to loop */
2079                         for (cnt = 0; cnt != res.length; cnt++) {
2080                                 if (sc->sc_jitterbuf_in != sc->sc_jitterbuf_out ||
2081                                     (*linesw[tp->t_line].l_rint)((unsigned char)buf[cnt], tp) == -1) {
2082                                         uint16_t end;
2083                                         uint16_t pos;
2084
2085                                         pos = sc->sc_jitterbuf_in;
2086                                         end = sc->sc_jitterbuf_out +
2087                                             UCOM_JITTERBUF_SIZE - 1;
2088                                         
2089                                         if (end >= UCOM_JITTERBUF_SIZE)
2090                                                 end -= UCOM_JITTERBUF_SIZE;
2091
2092                                         for (; cnt != res.length; cnt++) {
2093                                                 if (pos == end)
2094                                                         break;
2095                                                 sc->sc_jitterbuf[pos] = buf[cnt];
2096                                                 pos++;
2097                                                 if (pos >= UCOM_JITTERBUF_SIZE)
2098                                                         pos -= UCOM_JITTERBUF_SIZE;
2099                                         }
2100
2101                                         sc->sc_jitterbuf_in = pos;
2102
2103                                         /* set RTS in async fashion */
2104                                         if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW)
2105                                                 ucom_rts(sc, 1);
2106
2107                                         DPRINTF("tp=%p, lost %d "
2108                                             "chars\n", tp, res.length - cnt);
2109                                         break;
2110                                 }
2111                         }
2112                 }
2113         }
2114         crit_exit();
2115         lwkt_reltoken(&tty_token);
2116         /*
2117         ttydisc_rint_done(tp);
2118         */
2119 }
2120
2121 #if 0 /* XXX */
2122 static void
2123 ucom_free(void *xsc)
2124 {
2125         struct ucom_softc *sc = xsc;
2126
2127         if (sc->sc_callback->ucom_free != NULL)
2128                 sc->sc_callback->ucom_free(sc);
2129         else
2130                 /*ucom_unref(sc->sc_super) XXX hack, see end of ucom_detach_tty() */;
2131
2132         lockmgr(&ucom_lock, LK_EXCLUSIVE);
2133         ucom_close_refs--;
2134         lockmgr(&ucom_lock, LK_RELEASE);
2135 }
2136
2137 static cn_probe_t ucom_cnprobe;
2138 static cn_init_t ucom_cninit;
2139 static cn_term_t ucom_cnterm;
2140 static cn_getc_t ucom_cngetc;
2141 static cn_putc_t ucom_cnputc;
2142
2143 /*
2144 static cn_grab_t ucom_cngrab;
2145 static cn_ungrab_t ucom_cnungrab;
2146 CONSOLE_DRIVER(ucom);
2147 */
2148
2149 static void
2150 ucom_cnprobe(struct consdev  *cp)
2151 {
2152         if (ucom_cons_unit != -1)
2153                 cp->cn_pri = CN_NORMAL;
2154         else
2155                 cp->cn_pri = CN_DEAD;
2156
2157         /*
2158         strlcpy(cp->cn_name, "ucom", sizeof(cp->cn_name));
2159         */
2160 }
2161
2162 static void
2163 ucom_cninit(struct consdev  *cp)
2164 {
2165 }
2166
2167 static void
2168 ucom_cnterm(struct consdev  *cp)
2169 {
2170 }
2171
2172 static void
2173 ucom_cngrab(struct consdev *cp)
2174 {
2175 }
2176
2177 static void
2178 ucom_cnungrab(struct consdev *cp)
2179 {
2180 }
2181
2182 static int
2183 ucom_cngetc(struct consdev *cd)
2184 {
2185         struct ucom_softc *sc = ucom_cons_softc;
2186         int c;
2187
2188         if (sc == NULL)
2189                 return (-1);
2190
2191         UCOM_MTX_LOCK(sc);
2192
2193         if (ucom_cons_rx_low != ucom_cons_rx_high) {
2194                 c = ucom_cons_rx_buf[ucom_cons_rx_low];
2195                 ucom_cons_rx_low ++;
2196                 ucom_cons_rx_low %= UCOM_CONS_BUFSIZE;
2197         } else {
2198                 c = -1;
2199         }
2200
2201         /* start USB transfers */
2202         ucom_outwakeup(sc->sc_tty);
2203
2204         UCOM_MTX_UNLOCK(sc);
2205
2206         /* poll if necessary */
2207         /*
2208         if (kdb_active && sc->sc_callback->ucom_poll)
2209                 (sc->sc_callback->ucom_poll) (sc);
2210         */
2211         return (c);
2212 }
2213
2214 static void
2215 ucom_cnputc(void *cd, int c)
2216         /*
2217 ucom_cnputc(struct consdev *cd, int c)
2218         */
2219
2220 {
2221         struct ucom_softc *sc = ucom_cons_softc;
2222         unsigned int temp;
2223
2224         if (sc == NULL)
2225                 return;
2226
2227  repeat:
2228
2229         UCOM_MTX_LOCK(sc);
2230
2231         /* compute maximum TX length */
2232
2233         temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_tx_high + ucom_cons_tx_low;
2234         temp %= UCOM_CONS_BUFSIZE;
2235
2236         if (temp) {
2237                 ucom_cons_tx_buf[ucom_cons_tx_high] = c;
2238                 ucom_cons_tx_high ++;
2239                 ucom_cons_tx_high %= UCOM_CONS_BUFSIZE;
2240         }
2241
2242         /* start USB transfers */
2243         ucom_outwakeup(sc->sc_tty);
2244
2245         UCOM_MTX_UNLOCK(sc);
2246
2247         /* poll if necessary */
2248 #if 0 /* XXX */
2249         if (kdb_active && sc->sc_callback->ucom_poll) {
2250                 (sc->sc_callback->ucom_poll) (sc);
2251                 /* simple flow control */
2252                 if (temp == 0)
2253                         goto repeat;
2254         }
2255 #endif
2256 }
2257 #endif
2258 /*------------------------------------------------------------------------*
2259  *      ucom_ref
2260  *
2261  * This function will increment the super UCOM reference count.
2262  *------------------------------------------------------------------------*/
2263 void
2264 ucom_ref(struct ucom_super_softc *ssc)
2265 {
2266         lockmgr(&ucom_lock, LK_EXCLUSIVE);
2267         ssc->sc_refs++;
2268         lockmgr(&ucom_lock, LK_RELEASE);
2269 }
2270
2271 /*------------------------------------------------------------------------*
2272  *      ucom_free_unit
2273  *
2274  * This function will free the super UCOM's allocated unit
2275  * number. This function can be called on a zero-initialized
2276  * structure. This function can be called multiple times.
2277  *------------------------------------------------------------------------*/
2278 static void
2279 ucom_free_unit(struct ucom_super_softc *ssc)
2280 {
2281         if (!(ssc->sc_flag & UCOM_FLAG_FREE_UNIT))
2282                 return;
2283
2284         ucom_unit_free(ssc->sc_unit);
2285
2286         ssc->sc_flag &= ~UCOM_FLAG_FREE_UNIT;
2287 }
2288
2289 /*------------------------------------------------------------------------*
2290  *      ucom_unref
2291  *
2292  * This function will decrement the super UCOM reference count.
2293  *
2294  * Return values:
2295  * 0: UCOM structures are still referenced.
2296  * Else: UCOM structures are no longer referenced.
2297  *------------------------------------------------------------------------*/
2298 int
2299 ucom_unref(struct ucom_super_softc *ssc)
2300 {
2301         int retval;
2302
2303         lockmgr(&ucom_lock, LK_EXCLUSIVE);
2304         retval = (ssc->sc_refs < 2);
2305         ssc->sc_refs--;
2306         lockmgr(&ucom_lock, LK_RELEASE);
2307
2308         if (retval)
2309                 ucom_free_unit(ssc);
2310
2311         return (retval);
2312 }
2313
2314 /*
2315  * NOTE: Must be called with tty_token held.
2316  */
2317 static void
2318 disc_optim(struct tty *tp, struct termios *t, struct ucom_softc *sc)
2319 {
2320         ASSERT_LWKT_TOKEN_HELD(&tty_token);
2321         if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
2322             && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
2323             && (!(t->c_iflag & PARMRK)
2324                 || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
2325             && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
2326             && linesw[tp->t_line].l_rint == ttyinput) {
2327                 DPRINTF("disc_optim: bypass l_rint\n");
2328                 tp->t_state |= TS_CAN_BYPASS_L_RINT;
2329         } else {
2330                 DPRINTF("disc_optim: can't bypass l_rint\n");
2331                 tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
2332         }
2333         sc->hotchar = linesw[tp->t_line].l_hotchar;
2334 }
2335
2336 #if defined(GDB)
2337
2338 #include <gdb/gdb.h>
2339
2340 static gdb_probe_f ucom_gdbprobe;
2341 static gdb_init_f ucom_gdbinit;
2342 static gdb_term_f ucom_gdbterm;
2343 static gdb_getc_f ucom_gdbgetc;
2344 static gdb_putc_f ucom_gdbputc;
2345
2346 GDB_DBGPORT(sio, ucom_gdbprobe, ucom_gdbinit, ucom_gdbterm, ucom_gdbgetc, ucom_gdbputc);
2347
2348 static int
2349 ucom_gdbprobe(void)
2350 {
2351         return ((ucom_cons_softc != NULL) ? 0 : -1);
2352 }
2353
2354 static void
2355 ucom_gdbinit(void)
2356 {
2357 }
2358
2359 static void
2360 ucom_gdbterm(void)
2361 {
2362 }
2363
2364 static void
2365 ucom_gdbputc(int c)
2366 {
2367         ucom_cnputc(NULL, c);
2368 }
2369
2370 static int
2371 ucom_gdbgetc(void)
2372 {
2373         return (ucom_cngetc(NULL));
2374 }
2375
2376 #endif
2377
2378