]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - sys/dev/usb/serial/usb_serial.c
MFC r230204 and r230209:
[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     uint32_t subunits, void *parent,
240     const struct ucom_callback *callback, struct mtx *mtx)
241 {
242         uint32_t 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         uint32_t 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_ospeed < 0) {
1093                 DPRINTF("negative ospeed\n");
1094                 error = EINVAL;
1095                 goto done;
1096         }
1097         if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
1098                 DPRINTF("mismatch ispeed and ospeed\n");
1099                 error = EINVAL;
1100                 goto done;
1101         }
1102         t->c_ispeed = t->c_ospeed;
1103
1104         if (sc->sc_callback->ucom_pre_param) {
1105                 /* Let the lower layer verify the parameters */
1106                 error = (sc->sc_callback->ucom_pre_param) (sc, t);
1107                 if (error) {
1108                         DPRINTF("callback error = %d\n", error);
1109                         goto done;
1110                 }
1111         }
1112
1113         /* Disable transfers */
1114         sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
1115
1116         /* Queue baud rate programming command first */
1117         ucom_queue_command(sc, ucom_cfg_param, t,
1118             &sc->sc_param_task[0].hdr,
1119             &sc->sc_param_task[1].hdr);
1120
1121         /* Queue transfer enable command last */
1122         ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
1123             &sc->sc_start_task[0].hdr, 
1124             &sc->sc_start_task[1].hdr);
1125
1126         if (t->c_cflag & CRTS_IFLOW) {
1127                 sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
1128         } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
1129                 sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
1130                 ucom_modem(tp, SER_RTS, 0);
1131         }
1132 done:
1133         if (error) {
1134                 if (opened) {
1135                         ucom_close(tp);
1136                 }
1137         }
1138         return (error);
1139 }
1140
1141 static void
1142 ucom_outwakeup(struct tty *tp)
1143 {
1144         struct ucom_softc *sc = tty_softc(tp);
1145
1146         mtx_assert(sc->sc_mtx, MA_OWNED);
1147
1148         DPRINTF("sc = %p\n", sc);
1149
1150         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1151                 /* The higher layer is not ready */
1152                 return;
1153         }
1154         ucom_start_transfers(sc);
1155 }
1156
1157 /*------------------------------------------------------------------------*
1158  *      ucom_get_data
1159  *
1160  * Return values:
1161  * 0: No data is available.
1162  * Else: Data is available.
1163  *------------------------------------------------------------------------*/
1164 uint8_t
1165 ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1166     uint32_t offset, uint32_t len, uint32_t *actlen)
1167 {
1168         struct usb_page_search res;
1169         struct tty *tp = sc->sc_tty;
1170         uint32_t cnt;
1171         uint32_t offset_orig;
1172
1173         mtx_assert(sc->sc_mtx, MA_OWNED);
1174
1175         if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1176                 unsigned int temp;
1177
1178                 /* get total TX length */
1179
1180                 temp = ucom_cons_tx_high - ucom_cons_tx_low;
1181                 temp %= UCOM_CONS_BUFSIZE;
1182
1183                 /* limit TX length */
1184
1185                 if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_tx_low))
1186                         temp = (UCOM_CONS_BUFSIZE - ucom_cons_tx_low);
1187
1188                 if (temp > len)
1189                         temp = len;
1190
1191                 /* copy in data */
1192
1193                 usbd_copy_in(pc, offset, ucom_cons_tx_buf + ucom_cons_tx_low, temp);
1194
1195                 /* update counters */
1196
1197                 ucom_cons_tx_low += temp;
1198                 ucom_cons_tx_low %= UCOM_CONS_BUFSIZE;
1199
1200                 /* store actual length */
1201
1202                 *actlen = temp;
1203
1204                 return (temp ? 1 : 0);
1205         }
1206
1207         if (tty_gone(tp) ||
1208             !(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
1209                 actlen[0] = 0;
1210                 return (0);             /* multiport device polling */
1211         }
1212         offset_orig = offset;
1213
1214         while (len != 0) {
1215
1216                 usbd_get_page(pc, offset, &res);
1217
1218                 if (res.length > len) {
1219                         res.length = len;
1220                 }
1221                 /* copy data directly into USB buffer */
1222                 cnt = ttydisc_getc(tp, res.buffer, res.length);
1223
1224                 offset += cnt;
1225                 len -= cnt;
1226
1227                 if (cnt < res.length) {
1228                         /* end of buffer */
1229                         break;
1230                 }
1231         }
1232
1233         actlen[0] = offset - offset_orig;
1234
1235         DPRINTF("cnt=%d\n", actlen[0]);
1236
1237         if (actlen[0] == 0) {
1238                 return (0);
1239         }
1240         return (1);
1241 }
1242
1243 void
1244 ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1245     uint32_t offset, uint32_t len)
1246 {
1247         struct usb_page_search res;
1248         struct tty *tp = sc->sc_tty;
1249         char *buf;
1250         uint32_t cnt;
1251
1252         mtx_assert(sc->sc_mtx, MA_OWNED);
1253
1254         if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1255                 unsigned int temp;
1256
1257                 /* get maximum RX length */
1258
1259                 temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_rx_high + ucom_cons_rx_low;
1260                 temp %= UCOM_CONS_BUFSIZE;
1261
1262                 /* limit RX length */
1263
1264                 if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_rx_high))
1265                         temp = (UCOM_CONS_BUFSIZE - ucom_cons_rx_high);
1266
1267                 if (temp > len)
1268                         temp = len;
1269
1270                 /* copy out data */
1271
1272                 usbd_copy_out(pc, offset, ucom_cons_rx_buf + ucom_cons_rx_high, temp);
1273
1274                 /* update counters */
1275
1276                 ucom_cons_rx_high += temp;
1277                 ucom_cons_rx_high %= UCOM_CONS_BUFSIZE;
1278
1279                 return;
1280         }
1281
1282         if (tty_gone(tp))
1283                 return;                 /* multiport device polling */
1284
1285         if (len == 0)
1286                 return;                 /* no data */
1287
1288         /* set a flag to prevent recursation ? */
1289
1290         while (len > 0) {
1291
1292                 usbd_get_page(pc, offset, &res);
1293
1294                 if (res.length > len) {
1295                         res.length = len;
1296                 }
1297                 len -= res.length;
1298                 offset += res.length;
1299
1300                 /* pass characters to tty layer */
1301
1302                 buf = res.buffer;
1303                 cnt = res.length;
1304
1305                 /* first check if we can pass the buffer directly */
1306
1307                 if (ttydisc_can_bypass(tp)) {
1308                         if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
1309                                 DPRINTF("tp=%p, data lost\n", tp);
1310                         }
1311                         continue;
1312                 }
1313                 /* need to loop */
1314
1315                 for (cnt = 0; cnt != res.length; cnt++) {
1316                         if (ttydisc_rint(tp, buf[cnt], 0) == -1) {
1317                                 /* XXX what should we do? */
1318
1319                                 DPRINTF("tp=%p, lost %d "
1320                                     "chars\n", tp, res.length - cnt);
1321                                 break;
1322                         }
1323                 }
1324         }
1325         ttydisc_rint_done(tp);
1326 }
1327
1328 static void
1329 ucom_free(void *xsc)
1330 {
1331         struct ucom_softc *sc = xsc;
1332
1333         mtx_lock(sc->sc_mtx);
1334         sc->sc_ttyfreed = 1;
1335         cv_signal(&sc->sc_cv);
1336         mtx_unlock(sc->sc_mtx);
1337 }
1338
1339 static cn_probe_t ucom_cnprobe;
1340 static cn_init_t ucom_cninit;
1341 static cn_term_t ucom_cnterm;
1342 static cn_getc_t ucom_cngetc;
1343 static cn_putc_t ucom_cnputc;
1344
1345 CONSOLE_DRIVER(ucom);
1346
1347 static void
1348 ucom_cnprobe(struct consdev  *cp)
1349 {
1350         if (ucom_cons_unit != -1)
1351                 cp->cn_pri = CN_NORMAL;
1352         else
1353                 cp->cn_pri = CN_DEAD;
1354
1355         strlcpy(cp->cn_name, "ucom", sizeof(cp->cn_name));
1356 }
1357
1358 static void
1359 ucom_cninit(struct consdev  *cp)
1360 {
1361 }
1362
1363 static void
1364 ucom_cnterm(struct consdev  *cp)
1365 {
1366 }
1367
1368 static int
1369 ucom_cngetc(struct consdev *cd)
1370 {
1371         struct ucom_softc *sc = ucom_cons_softc;
1372         int c;
1373
1374         if (sc == NULL)
1375                 return (-1);
1376
1377         mtx_lock(sc->sc_mtx);
1378
1379         if (ucom_cons_rx_low != ucom_cons_rx_high) {
1380                 c = ucom_cons_rx_buf[ucom_cons_rx_low];
1381                 ucom_cons_rx_low ++;
1382                 ucom_cons_rx_low %= UCOM_CONS_BUFSIZE;
1383         } else {
1384                 c = -1;
1385         }
1386
1387         /* start USB transfers */
1388         ucom_outwakeup(sc->sc_tty);
1389
1390         mtx_unlock(sc->sc_mtx);
1391
1392         /* poll if necessary */
1393         if (kdb_active && sc->sc_callback->ucom_poll)
1394                 (sc->sc_callback->ucom_poll) (sc);
1395
1396         return (c);
1397 }
1398
1399 static void
1400 ucom_cnputc(struct consdev *cd, int c)
1401 {
1402         struct ucom_softc *sc = ucom_cons_softc;
1403         unsigned int temp;
1404
1405         if (sc == NULL)
1406                 return;
1407
1408  repeat:
1409
1410         mtx_lock(sc->sc_mtx);
1411
1412         /* compute maximum TX length */
1413
1414         temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_tx_high + ucom_cons_tx_low;
1415         temp %= UCOM_CONS_BUFSIZE;
1416
1417         if (temp) {
1418                 ucom_cons_tx_buf[ucom_cons_tx_high] = c;
1419                 ucom_cons_tx_high ++;
1420                 ucom_cons_tx_high %= UCOM_CONS_BUFSIZE;
1421         }
1422
1423         /* start USB transfers */
1424         ucom_outwakeup(sc->sc_tty);
1425
1426         mtx_unlock(sc->sc_mtx);
1427
1428         /* poll if necessary */
1429         if (kdb_active && sc->sc_callback->ucom_poll) {
1430                 (sc->sc_callback->ucom_poll) (sc);
1431                 /* simple flow control */
1432                 if (temp == 0)
1433                         goto repeat;
1434         }
1435 }
1436
1437 #if defined(GDB)
1438
1439 #include <gdb/gdb.h>
1440
1441 static gdb_probe_f ucom_gdbprobe;
1442 static gdb_init_f ucom_gdbinit;
1443 static gdb_term_f ucom_gdbterm;
1444 static gdb_getc_f ucom_gdbgetc;
1445 static gdb_putc_f ucom_gdbputc;
1446
1447 GDB_DBGPORT(sio, ucom_gdbprobe, ucom_gdbinit, ucom_gdbterm, ucom_gdbgetc, ucom_gdbputc);
1448
1449 static int
1450 ucom_gdbprobe(void)
1451 {
1452         return ((ucom_cons_softc != NULL) ? 0 : -1);
1453 }
1454
1455 static void
1456 ucom_gdbinit(void)
1457 {
1458 }
1459
1460 static void
1461 ucom_gdbterm(void)
1462 {
1463 }
1464
1465 static void
1466 ucom_gdbputc(int c)
1467 {
1468         ucom_cnputc(NULL, c);
1469 }
1470
1471 static int
1472 ucom_gdbgetc(void)
1473 {
1474         return (ucom_cngetc(NULL));
1475 }
1476
1477 #endif