]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - sys/dev/usb/serial/usb_serial.c
MFC r233774:
[FreeBSD/stable/9.git] / sys / dev / usb / 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 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 /*-
34  * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
35  * All rights reserved.
36  *
37  * This code is derived from software contributed to The NetBSD Foundation
38  * by Lennart Augustsson (lennart@augustsson.net) at
39  * Carlstedt Research & Technology.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. All advertising materials mentioning features or use of this software
50  *    must display the following acknowledgement:
51  *        This product includes software developed by the NetBSD
52  *        Foundation, Inc. and its contributors.
53  * 4. Neither the name of The NetBSD Foundation nor the names of its
54  *    contributors may be used to endorse or promote products derived
55  *    from this software without specific prior written permission.
56  *
57  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
58  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
59  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
60  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
61  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
62  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
63  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
64  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
65  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
66  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
67  * POSSIBILITY OF SUCH DAMAGE.
68  */
69
70 #include <sys/stdint.h>
71 #include <sys/stddef.h>
72 #include <sys/param.h>
73 #include <sys/queue.h>
74 #include <sys/types.h>
75 #include <sys/systm.h>
76 #include <sys/kernel.h>
77 #include <sys/bus.h>
78 #include <sys/module.h>
79 #include <sys/lock.h>
80 #include <sys/mutex.h>
81 #include <sys/condvar.h>
82 #include <sys/sysctl.h>
83 #include <sys/sx.h>
84 #include <sys/unistd.h>
85 #include <sys/callout.h>
86 #include <sys/malloc.h>
87 #include <sys/priv.h>
88 #include <sys/cons.h>
89 #include <sys/kdb.h>
90
91 #include <dev/usb/usb.h>
92 #include <dev/usb/usbdi.h>
93 #include <dev/usb/usbdi_util.h>
94
95 #define USB_DEBUG_VAR ucom_debug
96 #include <dev/usb/usb_debug.h>
97 #include <dev/usb/usb_busdma.h>
98 #include <dev/usb/usb_process.h>
99
100 #include <dev/usb/serial/usb_serial.h>
101
102 #include "opt_gdb.h"
103
104 SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
105
106 #ifdef USB_DEBUG
107 static int ucom_debug = 0;
108
109 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
110     &ucom_debug, 0, "ucom debug level");
111 #endif
112
113 #define UCOM_CONS_BUFSIZE 1024
114
115 static uint8_t ucom_cons_rx_buf[UCOM_CONS_BUFSIZE];
116 static uint8_t ucom_cons_tx_buf[UCOM_CONS_BUFSIZE];
117
118 static unsigned int ucom_cons_rx_low = 0;
119 static unsigned int ucom_cons_rx_high = 0;
120
121 static unsigned int ucom_cons_tx_low = 0;
122 static unsigned int ucom_cons_tx_high = 0;
123
124 static int ucom_cons_unit = -1;
125 static int ucom_cons_subunit = 0;
126 static int ucom_cons_baud = 9600;
127 static struct ucom_softc *ucom_cons_softc = NULL;
128
129 TUNABLE_INT("hw.usb.ucom.cons_unit", &ucom_cons_unit);
130 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_unit, CTLFLAG_RW,
131     &ucom_cons_unit, 0, "console unit number");
132 TUNABLE_INT("hw.usb.ucom.cons_subunit", &ucom_cons_subunit);
133 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_subunit, CTLFLAG_RW,
134     &ucom_cons_subunit, 0, "console subunit number");
135 TUNABLE_INT("hw.usb.ucom.cons_baud", &ucom_cons_baud);
136 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_baud, CTLFLAG_RW,
137     &ucom_cons_baud, 0, "console baud rate");
138
139 static usb_proc_callback_t ucom_cfg_start_transfers;
140 static usb_proc_callback_t ucom_cfg_open;
141 static usb_proc_callback_t ucom_cfg_close;
142 static usb_proc_callback_t ucom_cfg_line_state;
143 static usb_proc_callback_t ucom_cfg_status_change;
144 static usb_proc_callback_t ucom_cfg_param;
145
146 static int      ucom_unit_alloc(void);
147 static void     ucom_unit_free(int);
148 static int      ucom_attach_tty(struct ucom_super_softc *, struct ucom_softc *);
149 static void     ucom_detach_tty(struct ucom_softc *);
150 static void     ucom_queue_command(struct ucom_softc *,
151                     usb_proc_callback_t *, struct termios *pt,
152                     struct usb_proc_msg *t0, struct usb_proc_msg *t1);
153 static void     ucom_shutdown(struct ucom_softc *);
154 static void     ucom_ring(struct ucom_softc *, uint8_t);
155 static void     ucom_break(struct ucom_softc *, uint8_t);
156 static void     ucom_dtr(struct ucom_softc *, uint8_t);
157 static void     ucom_rts(struct ucom_softc *, uint8_t);
158
159 static tsw_open_t ucom_open;
160 static tsw_close_t ucom_close;
161 static tsw_ioctl_t ucom_ioctl;
162 static tsw_modem_t ucom_modem;
163 static tsw_param_t ucom_param;
164 static tsw_outwakeup_t ucom_outwakeup;
165 static tsw_free_t ucom_free;
166
167 static struct ttydevsw ucom_class = {
168         .tsw_flags = TF_INITLOCK | TF_CALLOUT,
169         .tsw_open = ucom_open,
170         .tsw_close = ucom_close,
171         .tsw_outwakeup = ucom_outwakeup,
172         .tsw_ioctl = ucom_ioctl,
173         .tsw_param = ucom_param,
174         .tsw_modem = ucom_modem,
175         .tsw_free = ucom_free,
176 };
177
178 MODULE_DEPEND(ucom, usb, 1, 1, 1);
179 MODULE_VERSION(ucom, 1);
180
181 #define UCOM_UNIT_MAX           128     /* limits size of ucom_bitmap */
182
183 static uint8_t ucom_bitmap[(UCOM_UNIT_MAX + 7) / 8];
184 static struct mtx ucom_bitmap_mtx;
185 MTX_SYSINIT(ucom_bitmap_mtx, &ucom_bitmap_mtx, "ucom bitmap", MTX_DEF);
186
187 #define UCOM_TTY_PREFIX         "U"
188
189 /*
190  * Mark a unit number (the X in cuaUX) as in use.
191  *
192  * Note that devices using a different naming scheme (see ucom_tty_name()
193  * callback) still use this unit allocation.
194  */
195 static int
196 ucom_unit_alloc(void)
197 {
198         int unit;
199
200         mtx_lock(&ucom_bitmap_mtx);
201
202         for (unit = 0; unit < UCOM_UNIT_MAX; unit++) {
203                 if ((ucom_bitmap[unit / 8] & (1 << (unit % 8))) == 0) {
204                         ucom_bitmap[unit / 8] |= (1 << (unit % 8));
205                         break;
206                 }
207         }
208
209         mtx_unlock(&ucom_bitmap_mtx);
210
211         if (unit == UCOM_UNIT_MAX)
212                 return -1;
213         else
214                 return unit;
215 }
216
217 /*
218  * Mark the unit number as not in use.
219  */
220 static void
221 ucom_unit_free(int unit)
222 {
223         mtx_lock(&ucom_bitmap_mtx);
224
225         ucom_bitmap[unit / 8] &= ~(1 << (unit % 8));
226
227         mtx_unlock(&ucom_bitmap_mtx);
228 }
229
230 /*
231  * Setup a group of one or more serial ports.
232  *
233  * The mutex pointed to by "mtx" is applied before all
234  * callbacks are called back. Also "mtx" must be applied
235  * before calling into the ucom-layer!
236  */
237 int
238 ucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
239     int subunits, void *parent,
240     const struct ucom_callback *callback, struct mtx *mtx)
241 {
242         int subunit;
243         int error = 0;
244
245         if ((sc == NULL) ||
246             (subunits <= 0) ||
247             (callback == NULL)) {
248                 return (EINVAL);
249         }
250
251         /* allocate a uniq unit number */
252         ssc->sc_unit = ucom_unit_alloc();
253         if (ssc->sc_unit == -1)
254                 return (ENOMEM);
255
256         /* generate TTY name string */
257         snprintf(ssc->sc_ttyname, sizeof(ssc->sc_ttyname),
258             UCOM_TTY_PREFIX "%d", ssc->sc_unit);
259
260         /* create USB request handling process */
261         error = usb_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED);
262         if (error) {
263                 ucom_unit_free(ssc->sc_unit);
264                 return (error);
265         }
266         ssc->sc_subunits = subunits;
267
268         for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
269                 sc[subunit].sc_subunit = subunit;
270                 sc[subunit].sc_super = ssc;
271                 sc[subunit].sc_mtx = mtx;
272                 sc[subunit].sc_parent = parent;
273                 sc[subunit].sc_callback = callback;
274
275                 error = ucom_attach_tty(ssc, &sc[subunit]);
276                 if (error) {
277                         ucom_detach(ssc, &sc[0]);
278                         return (error);
279                 }
280                 sc[subunit].sc_flag |= UCOM_FLAG_ATTACHED;
281         }
282
283         DPRINTF("tp = %p, unit = %d, subunits = %d\n",
284                 sc->sc_tty, ssc->sc_unit, ssc->sc_subunits);
285
286         return (0);
287 }
288
289 /*
290  * NOTE: the following function will do nothing if
291  * the structure pointed to by "ssc" and "sc" is zero.
292  */
293 void
294 ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc)
295 {
296         int subunit;
297
298         if (ssc->sc_subunits == 0)
299                 return;         /* not initialized */
300
301         if (ssc->sc_sysctl_ttyname != NULL) {
302                 sysctl_remove_oid(ssc->sc_sysctl_ttyname, 1, 0);
303                 ssc->sc_sysctl_ttyname = NULL;
304         }
305
306         if (ssc->sc_sysctl_ttyports != NULL) {
307                 sysctl_remove_oid(ssc->sc_sysctl_ttyports, 1, 0);
308                 ssc->sc_sysctl_ttyports = NULL;
309         }
310
311         usb_proc_drain(&ssc->sc_tq);
312
313         for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
314                 if (sc[subunit].sc_flag & UCOM_FLAG_ATTACHED) {
315
316                         ucom_detach_tty(&sc[subunit]);
317
318                         /* avoid duplicate detach */
319                         sc[subunit].sc_flag &= ~UCOM_FLAG_ATTACHED;
320                 }
321         }
322         ucom_unit_free(ssc->sc_unit);
323         usb_proc_free(&ssc->sc_tq);
324 }
325
326 static int
327 ucom_attach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
328 {
329         struct tty *tp;
330         char buf[32];                   /* temporary TTY device name buffer */
331
332         tp = tty_alloc_mutex(&ucom_class, sc, sc->sc_mtx);
333         if (tp == NULL)
334                 return (ENOMEM);
335
336         /* Check if the client has a custom TTY name */
337         buf[0] = '\0';
338         if (sc->sc_callback->ucom_tty_name) {
339                 sc->sc_callback->ucom_tty_name(sc, buf,
340                     sizeof(buf), ssc->sc_unit, sc->sc_subunit);
341         }
342         if (buf[0] == 0) {
343                 /* Use default TTY name */
344                 if (ssc->sc_subunits > 1) {
345                         /* multiple modems in one */
346                         snprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u.%u",
347                             ssc->sc_unit, sc->sc_subunit);
348                 } else {
349                         /* single modem */
350                         snprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u",
351                             ssc->sc_unit);
352                 }
353         }
354         tty_makedev(tp, NULL, "%s", buf);
355
356         sc->sc_tty = tp;
357
358         DPRINTF("ttycreate: %s\n", buf);
359         cv_init(&sc->sc_cv, "ucom");
360
361         /* Check if this device should be a console */
362         if ((ucom_cons_softc == NULL) && 
363             (ssc->sc_unit == ucom_cons_unit) &&
364             (sc->sc_subunit == ucom_cons_subunit)) {
365                 struct termios t;
366
367                 DPRINTF("unit %d subunit %d is console", ssc->sc_unit, sc->sc_subunit);
368
369                 ucom_cons_softc = sc;
370
371                 memset(&t, 0, sizeof(t));
372                 t.c_ispeed = ucom_cons_baud;
373                 t.c_ospeed = t.c_ispeed;
374                 t.c_cflag = CS8;
375
376                 mtx_lock(ucom_cons_softc->sc_mtx);
377                 ucom_cons_rx_low = 0;
378                 ucom_cons_rx_high = 0;
379                 ucom_cons_tx_low = 0;
380                 ucom_cons_tx_high = 0;
381                 sc->sc_flag |= UCOM_FLAG_CONSOLE;
382                 ucom_open(ucom_cons_softc->sc_tty);
383                 ucom_param(ucom_cons_softc->sc_tty, &t);
384                 mtx_unlock(ucom_cons_softc->sc_mtx);
385         }
386
387         return (0);
388 }
389
390 static void
391 ucom_detach_tty(struct ucom_softc *sc)
392 {
393         struct tty *tp = sc->sc_tty;
394
395         DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
396
397         if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
398                 mtx_lock(ucom_cons_softc->sc_mtx);
399                 ucom_close(ucom_cons_softc->sc_tty);
400                 sc->sc_flag &= ~UCOM_FLAG_CONSOLE;
401                 mtx_unlock(ucom_cons_softc->sc_mtx);
402                 ucom_cons_softc = NULL;
403         }
404
405         /* the config thread has been stopped when we get here */
406
407         mtx_lock(sc->sc_mtx);
408         sc->sc_flag |= UCOM_FLAG_GONE;
409         sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_LL_READY);
410         mtx_unlock(sc->sc_mtx);
411         if (tp) {
412                 tty_lock(tp);
413
414                 ucom_close(tp); /* close, if any */
415
416                 tty_rel_gone(tp);
417
418                 mtx_lock(sc->sc_mtx);
419                 /* Wait for the callback after the TTY is torn down */
420                 while (sc->sc_ttyfreed == 0)
421                         cv_wait(&sc->sc_cv, sc->sc_mtx);
422                 /*
423                  * make sure that read and write transfers are stopped
424                  */
425                 if (sc->sc_callback->ucom_stop_read) {
426                         (sc->sc_callback->ucom_stop_read) (sc);
427                 }
428                 if (sc->sc_callback->ucom_stop_write) {
429                         (sc->sc_callback->ucom_stop_write) (sc);
430                 }
431                 mtx_unlock(sc->sc_mtx);
432         }
433         cv_destroy(&sc->sc_cv);
434 }
435
436 void
437 ucom_set_pnpinfo_usb(struct ucom_super_softc *ssc, device_t dev)
438 {
439         char buf[64];
440         uint8_t iface_index;
441         struct usb_attach_arg *uaa;
442
443         snprintf(buf, sizeof(buf), "ttyname=" UCOM_TTY_PREFIX
444             "%d ttyports=%d", ssc->sc_unit, ssc->sc_subunits);
445
446         /* Store the PNP info in the first interface for the device */
447         uaa = device_get_ivars(dev);
448         iface_index = uaa->info.bIfaceIndex;
449     
450         if (usbd_set_pnpinfo(uaa->device, iface_index, buf) != 0)
451                 device_printf(dev, "Could not set PNP info\n");
452
453         /*
454          * The following information is also replicated in the PNP-info
455          * string which is registered above:
456          */
457         if (ssc->sc_sysctl_ttyname == NULL) {
458                 ssc->sc_sysctl_ttyname = SYSCTL_ADD_STRING(NULL,
459                     SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
460                     OID_AUTO, "ttyname", CTLFLAG_RD, ssc->sc_ttyname, 0,
461                     "TTY device basename");
462         }
463         if (ssc->sc_sysctl_ttyports == NULL) {
464                 ssc->sc_sysctl_ttyports = SYSCTL_ADD_INT(NULL,
465                     SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
466                     OID_AUTO, "ttyports", CTLFLAG_RD,
467                     NULL, ssc->sc_subunits, "Number of ports");
468         }
469 }
470
471 static void
472 ucom_queue_command(struct ucom_softc *sc,
473     usb_proc_callback_t *fn, struct termios *pt,
474     struct usb_proc_msg *t0, struct usb_proc_msg *t1)
475 {
476         struct ucom_super_softc *ssc = sc->sc_super;
477         struct ucom_param_task *task;
478
479         mtx_assert(sc->sc_mtx, MA_OWNED);
480
481         if (usb_proc_is_gone(&ssc->sc_tq)) {
482                 DPRINTF("proc is gone\n");
483                 return;         /* nothing to do */
484         }
485         /* 
486          * NOTE: The task cannot get executed before we drop the
487          * "sc_mtx" mutex. It is safe to update fields in the message
488          * structure after that the message got queued.
489          */
490         task = (struct ucom_param_task *)
491           usb_proc_msignal(&ssc->sc_tq, t0, t1);
492
493         /* Setup callback and softc pointers */
494         task->hdr.pm_callback = fn;
495         task->sc = sc;
496
497         /* 
498          * Make a copy of the termios. This field is only present if
499          * the "pt" field is not NULL.
500          */
501         if (pt != NULL)
502                 task->termios_copy = *pt;
503
504         /*
505          * Closing the device should be synchronous.
506          */
507         if (fn == ucom_cfg_close)
508                 usb_proc_mwait(&ssc->sc_tq, t0, t1);
509
510         /*
511          * In case of multiple configure requests,
512          * keep track of the last one!
513          */
514         if (fn == ucom_cfg_start_transfers)
515                 sc->sc_last_start_xfer = &task->hdr;
516 }
517
518 static void
519 ucom_shutdown(struct ucom_softc *sc)
520 {
521         struct tty *tp = sc->sc_tty;
522
523         mtx_assert(sc->sc_mtx, MA_OWNED);
524
525         DPRINTF("\n");
526
527         /*
528          * Hang up if necessary:
529          */
530         if (tp->t_termios.c_cflag & HUPCL) {
531                 ucom_modem(tp, 0, SER_DTR);
532         }
533 }
534
535 /*
536  * Return values:
537  *    0: normal
538  * else: taskqueue is draining or gone
539  */
540 uint8_t
541 ucom_cfg_is_gone(struct ucom_softc *sc)
542 {
543         struct ucom_super_softc *ssc = sc->sc_super;
544
545         return (usb_proc_is_gone(&ssc->sc_tq));
546 }
547
548 static void
549 ucom_cfg_start_transfers(struct usb_proc_msg *_task)
550 {
551         struct ucom_cfg_task *task = 
552             (struct ucom_cfg_task *)_task;
553         struct ucom_softc *sc = task->sc;
554
555         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
556                 return;
557         }
558         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
559                 /* TTY device closed */
560                 return;
561         }
562
563         if (_task == sc->sc_last_start_xfer)
564                 sc->sc_flag |= UCOM_FLAG_GP_DATA;
565
566         if (sc->sc_callback->ucom_start_read) {
567                 (sc->sc_callback->ucom_start_read) (sc);
568         }
569         if (sc->sc_callback->ucom_start_write) {
570                 (sc->sc_callback->ucom_start_write) (sc);
571         }
572 }
573
574 static void
575 ucom_start_transfers(struct ucom_softc *sc)
576 {
577         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
578                 return;
579         }
580         /*
581          * Make sure that data transfers are started in both
582          * directions:
583          */
584         if (sc->sc_callback->ucom_start_read) {
585                 (sc->sc_callback->ucom_start_read) (sc);
586         }
587         if (sc->sc_callback->ucom_start_write) {
588                 (sc->sc_callback->ucom_start_write) (sc);
589         }
590 }
591
592 static void
593 ucom_cfg_open(struct usb_proc_msg *_task)
594 {
595         struct ucom_cfg_task *task = 
596             (struct ucom_cfg_task *)_task;
597         struct ucom_softc *sc = task->sc;
598
599         DPRINTF("\n");
600
601         if (sc->sc_flag & UCOM_FLAG_LL_READY) {
602
603                 /* already opened */
604
605         } else {
606
607                 sc->sc_flag |= UCOM_FLAG_LL_READY;
608
609                 if (sc->sc_callback->ucom_cfg_open) {
610                         (sc->sc_callback->ucom_cfg_open) (sc);
611
612                         /* wait a little */
613                         usb_pause_mtx(sc->sc_mtx, hz / 10);
614                 }
615         }
616 }
617
618 static int
619 ucom_open(struct tty *tp)
620 {
621         struct ucom_softc *sc = tty_softc(tp);
622         int error;
623
624         mtx_assert(sc->sc_mtx, MA_OWNED);
625
626         if (sc->sc_flag & UCOM_FLAG_GONE) {
627                 return (ENXIO);
628         }
629         if (sc->sc_flag & UCOM_FLAG_HL_READY) {
630                 /* already opened */
631                 return (0);
632         }
633         DPRINTF("tp = %p\n", tp);
634
635         if (sc->sc_callback->ucom_pre_open) {
636                 /*
637                  * give the lower layer a chance to disallow TTY open, for
638                  * example if the device is not present:
639                  */
640                 error = (sc->sc_callback->ucom_pre_open) (sc);
641                 if (error) {
642                         return (error);
643                 }
644         }
645         sc->sc_flag |= UCOM_FLAG_HL_READY;
646
647         /* Disable transfers */
648         sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
649
650         sc->sc_lsr = 0;
651         sc->sc_msr = 0;
652         sc->sc_mcr = 0;
653
654         /* reset programmed line state */
655         sc->sc_pls_curr = 0;
656         sc->sc_pls_set = 0;
657         sc->sc_pls_clr = 0;
658
659         ucom_queue_command(sc, ucom_cfg_open, NULL,
660             &sc->sc_open_task[0].hdr,
661             &sc->sc_open_task[1].hdr);
662
663         /* Queue transfer enable command last */
664         ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
665             &sc->sc_start_task[0].hdr, 
666             &sc->sc_start_task[1].hdr);
667
668         ucom_modem(tp, SER_DTR | SER_RTS, 0);
669
670         ucom_ring(sc, 0);
671
672         ucom_break(sc, 0);
673
674         ucom_status_change(sc);
675
676         return (0);
677 }
678
679 static void
680 ucom_cfg_close(struct usb_proc_msg *_task)
681 {
682         struct ucom_cfg_task *task = 
683             (struct ucom_cfg_task *)_task;
684         struct ucom_softc *sc = task->sc;
685
686         DPRINTF("\n");
687
688         if (sc->sc_flag & UCOM_FLAG_LL_READY) {
689                 sc->sc_flag &= ~UCOM_FLAG_LL_READY;
690                 if (sc->sc_callback->ucom_cfg_close)
691                         (sc->sc_callback->ucom_cfg_close) (sc);
692         } else {
693                 /* already closed */
694         }
695 }
696
697 static void
698 ucom_close(struct tty *tp)
699 {
700         struct ucom_softc *sc = tty_softc(tp);
701
702         mtx_assert(sc->sc_mtx, MA_OWNED);
703
704         DPRINTF("tp=%p\n", tp);
705
706         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
707                 DPRINTF("tp=%p already closed\n", tp);
708                 return;
709         }
710         ucom_shutdown(sc);
711
712         ucom_queue_command(sc, ucom_cfg_close, NULL,
713             &sc->sc_close_task[0].hdr,
714             &sc->sc_close_task[1].hdr);
715
716         sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW);
717
718         if (sc->sc_callback->ucom_stop_read) {
719                 (sc->sc_callback->ucom_stop_read) (sc);
720         }
721 }
722
723 static int
724 ucom_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
725 {
726         struct ucom_softc *sc = tty_softc(tp);
727         int error;
728
729         mtx_assert(sc->sc_mtx, MA_OWNED);
730
731         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
732                 return (EIO);
733         }
734         DPRINTF("cmd = 0x%08lx\n", cmd);
735
736         switch (cmd) {
737 #if 0
738         case TIOCSRING:
739                 ucom_ring(sc, 1);
740                 error = 0;
741                 break;
742         case TIOCCRING:
743                 ucom_ring(sc, 0);
744                 error = 0;
745                 break;
746 #endif
747         case TIOCSBRK:
748                 ucom_break(sc, 1);
749                 error = 0;
750                 break;
751         case TIOCCBRK:
752                 ucom_break(sc, 0);
753                 error = 0;
754                 break;
755         default:
756                 if (sc->sc_callback->ucom_ioctl) {
757                         error = (sc->sc_callback->ucom_ioctl)
758                             (sc, cmd, data, 0, td);
759                 } else {
760                         error = ENOIOCTL;
761                 }
762                 break;
763         }
764         return (error);
765 }
766
767 static int
768 ucom_modem(struct tty *tp, int sigon, int sigoff)
769 {
770         struct ucom_softc *sc = tty_softc(tp);
771         uint8_t onoff;
772
773         mtx_assert(sc->sc_mtx, MA_OWNED);
774
775         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
776                 return (0);
777         }
778         if ((sigon == 0) && (sigoff == 0)) {
779
780                 if (sc->sc_mcr & SER_DTR) {
781                         sigon |= SER_DTR;
782                 }
783                 if (sc->sc_mcr & SER_RTS) {
784                         sigon |= SER_RTS;
785                 }
786                 if (sc->sc_msr & SER_CTS) {
787                         sigon |= SER_CTS;
788                 }
789                 if (sc->sc_msr & SER_DCD) {
790                         sigon |= SER_DCD;
791                 }
792                 if (sc->sc_msr & SER_DSR) {
793                         sigon |= SER_DSR;
794                 }
795                 if (sc->sc_msr & SER_RI) {
796                         sigon |= SER_RI;
797                 }
798                 return (sigon);
799         }
800         if (sigon & SER_DTR) {
801                 sc->sc_mcr |= SER_DTR;
802         }
803         if (sigoff & SER_DTR) {
804                 sc->sc_mcr &= ~SER_DTR;
805         }
806         if (sigon & SER_RTS) {
807                 sc->sc_mcr |= SER_RTS;
808         }
809         if (sigoff & SER_RTS) {
810                 sc->sc_mcr &= ~SER_RTS;
811         }
812         onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
813         ucom_dtr(sc, onoff);
814
815         onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
816         ucom_rts(sc, onoff);
817
818         return (0);
819 }
820
821 static void
822 ucom_cfg_line_state(struct usb_proc_msg *_task)
823 {
824         struct ucom_cfg_task *task = 
825             (struct ucom_cfg_task *)_task;
826         struct ucom_softc *sc = task->sc;
827         uint8_t notch_bits;
828         uint8_t any_bits;
829         uint8_t prev_value;
830         uint8_t last_value;
831         uint8_t mask;
832
833         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
834                 return;
835         }
836
837         mask = 0;
838         /* compute callback mask */
839         if (sc->sc_callback->ucom_cfg_set_dtr)
840                 mask |= UCOM_LS_DTR;
841         if (sc->sc_callback->ucom_cfg_set_rts)
842                 mask |= UCOM_LS_RTS;
843         if (sc->sc_callback->ucom_cfg_set_break)
844                 mask |= UCOM_LS_BREAK;
845         if (sc->sc_callback->ucom_cfg_set_ring)
846                 mask |= UCOM_LS_RING;
847
848         /* compute the bits we are to program */
849         notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
850         any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
851         prev_value = sc->sc_pls_curr ^ notch_bits;
852         last_value = sc->sc_pls_curr;
853
854         /* reset programmed line state */
855         sc->sc_pls_curr = 0;
856         sc->sc_pls_set = 0;
857         sc->sc_pls_clr = 0;
858
859         /* ensure that we don't loose any levels */
860         if (notch_bits & UCOM_LS_DTR)
861                 sc->sc_callback->ucom_cfg_set_dtr(sc,
862                     (prev_value & UCOM_LS_DTR) ? 1 : 0);
863         if (notch_bits & UCOM_LS_RTS)
864                 sc->sc_callback->ucom_cfg_set_rts(sc,
865                     (prev_value & UCOM_LS_RTS) ? 1 : 0);
866         if (notch_bits & UCOM_LS_BREAK)
867                 sc->sc_callback->ucom_cfg_set_break(sc,
868                     (prev_value & UCOM_LS_BREAK) ? 1 : 0);
869         if (notch_bits & UCOM_LS_RING)
870                 sc->sc_callback->ucom_cfg_set_ring(sc,
871                     (prev_value & UCOM_LS_RING) ? 1 : 0);
872
873         /* set last value */
874         if (any_bits & UCOM_LS_DTR)
875                 sc->sc_callback->ucom_cfg_set_dtr(sc,
876                     (last_value & UCOM_LS_DTR) ? 1 : 0);
877         if (any_bits & UCOM_LS_RTS)
878                 sc->sc_callback->ucom_cfg_set_rts(sc,
879                     (last_value & UCOM_LS_RTS) ? 1 : 0);
880         if (any_bits & UCOM_LS_BREAK)
881                 sc->sc_callback->ucom_cfg_set_break(sc,
882                     (last_value & UCOM_LS_BREAK) ? 1 : 0);
883         if (any_bits & UCOM_LS_RING)
884                 sc->sc_callback->ucom_cfg_set_ring(sc,
885                     (last_value & UCOM_LS_RING) ? 1 : 0);
886 }
887
888 static void
889 ucom_line_state(struct ucom_softc *sc,
890     uint8_t set_bits, uint8_t clear_bits)
891 {
892         mtx_assert(sc->sc_mtx, MA_OWNED);
893
894         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
895                 return;
896         }
897
898         DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
899
900         /* update current programmed line state */
901         sc->sc_pls_curr |= set_bits;
902         sc->sc_pls_curr &= ~clear_bits;
903         sc->sc_pls_set |= set_bits;
904         sc->sc_pls_clr |= clear_bits;
905
906         /* defer driver programming */
907         ucom_queue_command(sc, ucom_cfg_line_state, NULL,
908             &sc->sc_line_state_task[0].hdr, 
909             &sc->sc_line_state_task[1].hdr);
910 }
911
912 static void
913 ucom_ring(struct ucom_softc *sc, uint8_t onoff)
914 {
915         DPRINTF("onoff = %d\n", onoff);
916
917         if (onoff)
918                 ucom_line_state(sc, UCOM_LS_RING, 0);
919         else
920                 ucom_line_state(sc, 0, UCOM_LS_RING);
921 }
922
923 static void
924 ucom_break(struct ucom_softc *sc, uint8_t onoff)
925 {
926         DPRINTF("onoff = %d\n", onoff);
927
928         if (onoff)
929                 ucom_line_state(sc, UCOM_LS_BREAK, 0);
930         else
931                 ucom_line_state(sc, 0, UCOM_LS_BREAK);
932 }
933
934 static void
935 ucom_dtr(struct ucom_softc *sc, uint8_t onoff)
936 {
937         DPRINTF("onoff = %d\n", onoff);
938
939         if (onoff)
940                 ucom_line_state(sc, UCOM_LS_DTR, 0);
941         else
942                 ucom_line_state(sc, 0, UCOM_LS_DTR);
943 }
944
945 static void
946 ucom_rts(struct ucom_softc *sc, uint8_t onoff)
947 {
948         DPRINTF("onoff = %d\n", onoff);
949
950         if (onoff)
951                 ucom_line_state(sc, UCOM_LS_RTS, 0);
952         else
953                 ucom_line_state(sc, 0, UCOM_LS_RTS);
954 }
955
956 static void
957 ucom_cfg_status_change(struct usb_proc_msg *_task)
958 {
959         struct ucom_cfg_task *task = 
960             (struct ucom_cfg_task *)_task;
961         struct ucom_softc *sc = task->sc;
962         struct tty *tp;
963         uint8_t new_msr;
964         uint8_t new_lsr;
965         uint8_t onoff;
966         uint8_t lsr_delta;
967
968         tp = sc->sc_tty;
969
970         mtx_assert(sc->sc_mtx, MA_OWNED);
971
972         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
973                 return;
974         }
975         if (sc->sc_callback->ucom_cfg_get_status == NULL) {
976                 return;
977         }
978         /* get status */
979
980         new_msr = 0;
981         new_lsr = 0;
982
983         (sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr);
984
985         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
986                 /* TTY device closed */
987                 return;
988         }
989         onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
990         lsr_delta = (sc->sc_lsr ^ new_lsr);
991
992         sc->sc_msr = new_msr;
993         sc->sc_lsr = new_lsr;
994
995         if (onoff) {
996
997                 onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
998
999                 DPRINTF("DCD changed to %d\n", onoff);
1000
1001                 ttydisc_modem(tp, onoff);
1002         }
1003
1004         if ((lsr_delta & ULSR_BI) && (sc->sc_lsr & ULSR_BI)) {
1005
1006                 DPRINTF("BREAK detected\n");
1007
1008                 ttydisc_rint(tp, 0, TRE_BREAK);
1009                 ttydisc_rint_done(tp);
1010         }
1011
1012         if ((lsr_delta & ULSR_FE) && (sc->sc_lsr & ULSR_FE)) {
1013
1014                 DPRINTF("Frame error detected\n");
1015
1016                 ttydisc_rint(tp, 0, TRE_FRAMING);
1017                 ttydisc_rint_done(tp);
1018         }
1019
1020         if ((lsr_delta & ULSR_PE) && (sc->sc_lsr & ULSR_PE)) {
1021
1022                 DPRINTF("Parity error detected\n");
1023
1024                 ttydisc_rint(tp, 0, TRE_PARITY);
1025                 ttydisc_rint_done(tp);
1026         }
1027 }
1028
1029 void
1030 ucom_status_change(struct ucom_softc *sc)
1031 {
1032         mtx_assert(sc->sc_mtx, MA_OWNED);
1033
1034         if (sc->sc_flag & UCOM_FLAG_CONSOLE)
1035                 return;         /* not supported */
1036
1037         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1038                 return;
1039         }
1040         DPRINTF("\n");
1041
1042         ucom_queue_command(sc, ucom_cfg_status_change, NULL,
1043             &sc->sc_status_task[0].hdr,
1044             &sc->sc_status_task[1].hdr);
1045 }
1046
1047 static void
1048 ucom_cfg_param(struct usb_proc_msg *_task)
1049 {
1050         struct ucom_param_task *task = 
1051             (struct ucom_param_task *)_task;
1052         struct ucom_softc *sc = task->sc;
1053
1054         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
1055                 return;
1056         }
1057         if (sc->sc_callback->ucom_cfg_param == NULL) {
1058                 return;
1059         }
1060
1061         (sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy);
1062
1063         /* wait a little */
1064         usb_pause_mtx(sc->sc_mtx, hz / 10);
1065 }
1066
1067 static int
1068 ucom_param(struct tty *tp, struct termios *t)
1069 {
1070         struct ucom_softc *sc = tty_softc(tp);
1071         uint8_t opened;
1072         int error;
1073
1074         mtx_assert(sc->sc_mtx, MA_OWNED);
1075
1076         opened = 0;
1077         error = 0;
1078
1079         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1080
1081                 /* XXX the TTY layer should call "open()" first! */
1082
1083                 error = ucom_open(tp);
1084                 if (error) {
1085                         goto done;
1086                 }
1087                 opened = 1;
1088         }
1089         DPRINTF("sc = %p\n", sc);
1090
1091         /* Check requested parameters. */
1092         if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
1093                 DPRINTF("mismatch ispeed and ospeed\n");
1094                 error = EINVAL;
1095                 goto done;
1096         }
1097         t->c_ispeed = t->c_ospeed;
1098
1099         if (sc->sc_callback->ucom_pre_param) {
1100                 /* Let the lower layer verify the parameters */
1101                 error = (sc->sc_callback->ucom_pre_param) (sc, t);
1102                 if (error) {
1103                         DPRINTF("callback error = %d\n", error);
1104                         goto done;
1105                 }
1106         }
1107
1108         /* Disable transfers */
1109         sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
1110
1111         /* Queue baud rate programming command first */
1112         ucom_queue_command(sc, ucom_cfg_param, t,
1113             &sc->sc_param_task[0].hdr,
1114             &sc->sc_param_task[1].hdr);
1115
1116         /* Queue transfer enable command last */
1117         ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
1118             &sc->sc_start_task[0].hdr, 
1119             &sc->sc_start_task[1].hdr);
1120
1121         if (t->c_cflag & CRTS_IFLOW) {
1122                 sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
1123         } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
1124                 sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
1125                 ucom_modem(tp, SER_RTS, 0);
1126         }
1127 done:
1128         if (error) {
1129                 if (opened) {
1130                         ucom_close(tp);
1131                 }
1132         }
1133         return (error);
1134 }
1135
1136 static void
1137 ucom_outwakeup(struct tty *tp)
1138 {
1139         struct ucom_softc *sc = tty_softc(tp);
1140
1141         mtx_assert(sc->sc_mtx, MA_OWNED);
1142
1143         DPRINTF("sc = %p\n", sc);
1144
1145         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1146                 /* The higher layer is not ready */
1147                 return;
1148         }
1149         ucom_start_transfers(sc);
1150 }
1151
1152 /*------------------------------------------------------------------------*
1153  *      ucom_get_data
1154  *
1155  * Return values:
1156  * 0: No data is available.
1157  * Else: Data is available.
1158  *------------------------------------------------------------------------*/
1159 uint8_t
1160 ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1161     uint32_t offset, uint32_t len, uint32_t *actlen)
1162 {
1163         struct usb_page_search res;
1164         struct tty *tp = sc->sc_tty;
1165         uint32_t cnt;
1166         uint32_t offset_orig;
1167
1168         mtx_assert(sc->sc_mtx, MA_OWNED);
1169
1170         if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1171                 unsigned int temp;
1172
1173                 /* get total TX length */
1174
1175                 temp = ucom_cons_tx_high - ucom_cons_tx_low;
1176                 temp %= UCOM_CONS_BUFSIZE;
1177
1178                 /* limit TX length */
1179
1180                 if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_tx_low))
1181                         temp = (UCOM_CONS_BUFSIZE - ucom_cons_tx_low);
1182
1183                 if (temp > len)
1184                         temp = len;
1185
1186                 /* copy in data */
1187
1188                 usbd_copy_in(pc, offset, ucom_cons_tx_buf + ucom_cons_tx_low, temp);
1189
1190                 /* update counters */
1191
1192                 ucom_cons_tx_low += temp;
1193                 ucom_cons_tx_low %= UCOM_CONS_BUFSIZE;
1194
1195                 /* store actual length */
1196
1197                 *actlen = temp;
1198
1199                 return (temp ? 1 : 0);
1200         }
1201
1202         if (tty_gone(tp) ||
1203             !(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
1204                 actlen[0] = 0;
1205                 return (0);             /* multiport device polling */
1206         }
1207         offset_orig = offset;
1208
1209         while (len != 0) {
1210
1211                 usbd_get_page(pc, offset, &res);
1212
1213                 if (res.length > len) {
1214                         res.length = len;
1215                 }
1216                 /* copy data directly into USB buffer */
1217                 cnt = ttydisc_getc(tp, res.buffer, res.length);
1218
1219                 offset += cnt;
1220                 len -= cnt;
1221
1222                 if (cnt < res.length) {
1223                         /* end of buffer */
1224                         break;
1225                 }
1226         }
1227
1228         actlen[0] = offset - offset_orig;
1229
1230         DPRINTF("cnt=%d\n", actlen[0]);
1231
1232         if (actlen[0] == 0) {
1233                 return (0);
1234         }
1235         return (1);
1236 }
1237
1238 void
1239 ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1240     uint32_t offset, uint32_t len)
1241 {
1242         struct usb_page_search res;
1243         struct tty *tp = sc->sc_tty;
1244         char *buf;
1245         uint32_t cnt;
1246
1247         mtx_assert(sc->sc_mtx, MA_OWNED);
1248
1249         if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1250                 unsigned int temp;
1251
1252                 /* get maximum RX length */
1253
1254                 temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_rx_high + ucom_cons_rx_low;
1255                 temp %= UCOM_CONS_BUFSIZE;
1256
1257                 /* limit RX length */
1258
1259                 if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_rx_high))
1260                         temp = (UCOM_CONS_BUFSIZE - ucom_cons_rx_high);
1261
1262                 if (temp > len)
1263                         temp = len;
1264
1265                 /* copy out data */
1266
1267                 usbd_copy_out(pc, offset, ucom_cons_rx_buf + ucom_cons_rx_high, temp);
1268
1269                 /* update counters */
1270
1271                 ucom_cons_rx_high += temp;
1272                 ucom_cons_rx_high %= UCOM_CONS_BUFSIZE;
1273
1274                 return;
1275         }
1276
1277         if (tty_gone(tp))
1278                 return;                 /* multiport device polling */
1279
1280         if (len == 0)
1281                 return;                 /* no data */
1282
1283         /* set a flag to prevent recursation ? */
1284
1285         while (len > 0) {
1286
1287                 usbd_get_page(pc, offset, &res);
1288
1289                 if (res.length > len) {
1290                         res.length = len;
1291                 }
1292                 len -= res.length;
1293                 offset += res.length;
1294
1295                 /* pass characters to tty layer */
1296
1297                 buf = res.buffer;
1298                 cnt = res.length;
1299
1300                 /* first check if we can pass the buffer directly */
1301
1302                 if (ttydisc_can_bypass(tp)) {
1303                         if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
1304                                 DPRINTF("tp=%p, data lost\n", tp);
1305                         }
1306                         continue;
1307                 }
1308                 /* need to loop */
1309
1310                 for (cnt = 0; cnt != res.length; cnt++) {
1311                         if (ttydisc_rint(tp, buf[cnt], 0) == -1) {
1312                                 /* XXX what should we do? */
1313
1314                                 DPRINTF("tp=%p, lost %d "
1315                                     "chars\n", tp, res.length - cnt);
1316                                 break;
1317                         }
1318                 }
1319         }
1320         ttydisc_rint_done(tp);
1321 }
1322
1323 static void
1324 ucom_free(void *xsc)
1325 {
1326         struct ucom_softc *sc = xsc;
1327
1328         mtx_lock(sc->sc_mtx);
1329         sc->sc_ttyfreed = 1;
1330         cv_signal(&sc->sc_cv);
1331         mtx_unlock(sc->sc_mtx);
1332 }
1333
1334 static cn_probe_t ucom_cnprobe;
1335 static cn_init_t ucom_cninit;
1336 static cn_term_t ucom_cnterm;
1337 static cn_getc_t ucom_cngetc;
1338 static cn_putc_t ucom_cnputc;
1339
1340 CONSOLE_DRIVER(ucom);
1341
1342 static void
1343 ucom_cnprobe(struct consdev  *cp)
1344 {
1345         if (ucom_cons_unit != -1)
1346                 cp->cn_pri = CN_NORMAL;
1347         else
1348                 cp->cn_pri = CN_DEAD;
1349
1350         strlcpy(cp->cn_name, "ucom", sizeof(cp->cn_name));
1351 }
1352
1353 static void
1354 ucom_cninit(struct consdev  *cp)
1355 {
1356 }
1357
1358 static void
1359 ucom_cnterm(struct consdev  *cp)
1360 {
1361 }
1362
1363 static int
1364 ucom_cngetc(struct consdev *cd)
1365 {
1366         struct ucom_softc *sc = ucom_cons_softc;
1367         int c;
1368
1369         if (sc == NULL)
1370                 return (-1);
1371
1372         mtx_lock(sc->sc_mtx);
1373
1374         if (ucom_cons_rx_low != ucom_cons_rx_high) {
1375                 c = ucom_cons_rx_buf[ucom_cons_rx_low];
1376                 ucom_cons_rx_low ++;
1377                 ucom_cons_rx_low %= UCOM_CONS_BUFSIZE;
1378         } else {
1379                 c = -1;
1380         }
1381
1382         /* start USB transfers */
1383         ucom_outwakeup(sc->sc_tty);
1384
1385         mtx_unlock(sc->sc_mtx);
1386
1387         /* poll if necessary */
1388         if (kdb_active && sc->sc_callback->ucom_poll)
1389                 (sc->sc_callback->ucom_poll) (sc);
1390
1391         return (c);
1392 }
1393
1394 static void
1395 ucom_cnputc(struct consdev *cd, int c)
1396 {
1397         struct ucom_softc *sc = ucom_cons_softc;
1398         unsigned int temp;
1399
1400         if (sc == NULL)
1401                 return;
1402
1403  repeat:
1404
1405         mtx_lock(sc->sc_mtx);
1406
1407         /* compute maximum TX length */
1408
1409         temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_tx_high + ucom_cons_tx_low;
1410         temp %= UCOM_CONS_BUFSIZE;
1411
1412         if (temp) {
1413                 ucom_cons_tx_buf[ucom_cons_tx_high] = c;
1414                 ucom_cons_tx_high ++;
1415                 ucom_cons_tx_high %= UCOM_CONS_BUFSIZE;
1416         }
1417
1418         /* start USB transfers */
1419         ucom_outwakeup(sc->sc_tty);
1420
1421         mtx_unlock(sc->sc_mtx);
1422
1423         /* poll if necessary */
1424         if (kdb_active && sc->sc_callback->ucom_poll) {
1425                 (sc->sc_callback->ucom_poll) (sc);
1426                 /* simple flow control */
1427                 if (temp == 0)
1428                         goto repeat;
1429         }
1430 }
1431
1432 #if defined(GDB)
1433
1434 #include <gdb/gdb.h>
1435
1436 static gdb_probe_f ucom_gdbprobe;
1437 static gdb_init_f ucom_gdbinit;
1438 static gdb_term_f ucom_gdbterm;
1439 static gdb_getc_f ucom_gdbgetc;
1440 static gdb_putc_f ucom_gdbputc;
1441
1442 GDB_DBGPORT(sio, ucom_gdbprobe, ucom_gdbinit, ucom_gdbterm, ucom_gdbgetc, ucom_gdbputc);
1443
1444 static int
1445 ucom_gdbprobe(void)
1446 {
1447         return ((ucom_cons_softc != NULL) ? 0 : -1);
1448 }
1449
1450 static void
1451 ucom_gdbinit(void)
1452 {
1453 }
1454
1455 static void
1456 ucom_gdbterm(void)
1457 {
1458 }
1459
1460 static void
1461 ucom_gdbputc(int c)
1462 {
1463         ucom_cnputc(NULL, c);
1464 }
1465
1466 static int
1467 ucom_gdbgetc(void)
1468 {
1469         return (ucom_cngetc(NULL));
1470 }
1471
1472 #endif