]> CyberLeo.Net >> Repos - FreeBSD/releng/8.0.git/blob - sys/dev/usb/serial/usb_serial.c
Adjust to reflect 8.0-RELEASE.
[FreeBSD/releng/8.0.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/linker_set.h>
79 #include <sys/module.h>
80 #include <sys/lock.h>
81 #include <sys/mutex.h>
82 #include <sys/condvar.h>
83 #include <sys/sysctl.h>
84 #include <sys/sx.h>
85 #include <sys/unistd.h>
86 #include <sys/callout.h>
87 #include <sys/malloc.h>
88 #include <sys/priv.h>
89
90 #include <dev/usb/usb.h>
91 #include <dev/usb/usbdi.h>
92 #include <dev/usb/usbdi_util.h>
93
94 #define USB_DEBUG_VAR ucom_debug
95 #include <dev/usb/usb_debug.h>
96 #include <dev/usb/usb_busdma.h>
97 #include <dev/usb/usb_process.h>
98
99 #include <dev/usb/serial/usb_serial.h>
100
101 #if USB_DEBUG
102 static int ucom_debug = 0;
103
104 SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
105 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
106     &ucom_debug, 0, "ucom debug level");
107 #endif
108
109 static usb_proc_callback_t ucom_cfg_start_transfers;
110 static usb_proc_callback_t ucom_cfg_open;
111 static usb_proc_callback_t ucom_cfg_close;
112 static usb_proc_callback_t ucom_cfg_line_state;
113 static usb_proc_callback_t ucom_cfg_status_change;
114 static usb_proc_callback_t ucom_cfg_param;
115
116 static uint8_t  ucom_units_alloc(uint32_t, uint32_t *);
117 static void     ucom_units_free(uint32_t, uint32_t);
118 static int      ucom_attach_tty(struct ucom_softc *, uint32_t);
119 static void     ucom_detach_tty(struct ucom_softc *);
120 static void     ucom_queue_command(struct ucom_softc *,
121                     usb_proc_callback_t *, struct termios *pt,
122                     struct usb_proc_msg *t0, struct usb_proc_msg *t1);
123 static void     ucom_shutdown(struct ucom_softc *);
124 static void     ucom_break(struct ucom_softc *, uint8_t);
125 static void     ucom_dtr(struct ucom_softc *, uint8_t);
126 static void     ucom_rts(struct ucom_softc *, uint8_t);
127
128 static tsw_open_t ucom_open;
129 static tsw_close_t ucom_close;
130 static tsw_ioctl_t ucom_ioctl;
131 static tsw_modem_t ucom_modem;
132 static tsw_param_t ucom_param;
133 static tsw_outwakeup_t ucom_outwakeup;
134 static tsw_free_t ucom_free;
135
136 static struct ttydevsw ucom_class = {
137         .tsw_flags = TF_INITLOCK | TF_CALLOUT,
138         .tsw_open = ucom_open,
139         .tsw_close = ucom_close,
140         .tsw_outwakeup = ucom_outwakeup,
141         .tsw_ioctl = ucom_ioctl,
142         .tsw_param = ucom_param,
143         .tsw_modem = ucom_modem,
144         .tsw_free = ucom_free,
145 };
146
147 MODULE_DEPEND(ucom, usb, 1, 1, 1);
148 MODULE_VERSION(ucom, 1);
149
150 #define UCOM_UNIT_MAX 0x1000            /* exclusive */
151 #define UCOM_SUB_UNIT_MAX 0x100         /* exclusive */
152
153 static uint8_t ucom_bitmap[(UCOM_UNIT_MAX + 7) / 8];
154 static struct mtx ucom_bitmap_mtx;
155 MTX_SYSINIT(ucom_bitmap_mtx, &ucom_bitmap_mtx, "ucom bitmap", MTX_DEF);
156
157 static uint8_t
158 ucom_units_alloc(uint32_t sub_units, uint32_t *p_root_unit)
159 {
160         uint32_t n;
161         uint32_t o;
162         uint32_t x;
163         uint32_t max = UCOM_UNIT_MAX - (UCOM_UNIT_MAX % sub_units);
164         uint8_t error = 1;
165
166         mtx_lock(&ucom_bitmap_mtx);
167
168         for (n = 0; n < max; n += sub_units) {
169
170                 /* check for free consecutive bits */
171
172                 for (o = 0; o < sub_units; o++) {
173
174                         x = n + o;
175
176                         if (ucom_bitmap[x / 8] & (1 << (x % 8))) {
177                                 goto skip;
178                         }
179                 }
180
181                 /* allocate */
182
183                 for (o = 0; o < sub_units; o++) {
184
185                         x = n + o;
186
187                         ucom_bitmap[x / 8] |= (1 << (x % 8));
188                 }
189
190                 error = 0;
191
192                 break;
193
194 skip:           ;
195         }
196
197         mtx_unlock(&ucom_bitmap_mtx);
198
199         /*
200          * Always set the variable pointed to by "p_root_unit" so that
201          * the compiler does not think that it is used uninitialised:
202          */
203         *p_root_unit = n;
204
205         return (error);
206 }
207
208 static void
209 ucom_units_free(uint32_t root_unit, uint32_t sub_units)
210 {
211         uint32_t x;
212
213         mtx_lock(&ucom_bitmap_mtx);
214
215         while (sub_units--) {
216                 x = root_unit + sub_units;
217                 ucom_bitmap[x / 8] &= ~(1 << (x % 8));
218         }
219
220         mtx_unlock(&ucom_bitmap_mtx);
221 }
222
223 /*
224  * "N" sub_units are setup at a time. All sub-units will
225  * be given sequential unit numbers. The number of
226  * sub-units can be used to differentiate among
227  * different types of devices.
228  *
229  * The mutex pointed to by "mtx" is applied before all
230  * callbacks are called back. Also "mtx" must be applied
231  * before calling into the ucom-layer!
232  */
233 int
234 ucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
235     uint32_t sub_units, void *parent,
236     const struct ucom_callback *callback, struct mtx *mtx)
237 {
238         uint32_t n;
239         uint32_t root_unit;
240         int error = 0;
241
242         if ((sc == NULL) ||
243             (sub_units == 0) ||
244             (sub_units > UCOM_SUB_UNIT_MAX) ||
245             (callback == NULL)) {
246                 return (EINVAL);
247         }
248
249         /* XXX unit management does not really belong here */
250         if (ucom_units_alloc(sub_units, &root_unit)) {
251                 return (ENOMEM);
252         }
253
254         error = usb_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED);
255         if (error) {
256                 ucom_units_free(root_unit, sub_units);
257                 return (error);
258         }
259
260         for (n = 0; n != sub_units; n++, sc++) {
261                 sc->sc_unit = root_unit + n;
262                 sc->sc_local_unit = n;
263                 sc->sc_super = ssc;
264                 sc->sc_mtx = mtx;
265                 sc->sc_parent = parent;
266                 sc->sc_callback = callback;
267
268                 error = ucom_attach_tty(sc, sub_units);
269                 if (error) {
270                         ucom_detach(ssc, sc - n, n);
271                         ucom_units_free(root_unit + n, sub_units - n);
272                         return (error);
273                 }
274                 sc->sc_flag |= UCOM_FLAG_ATTACHED;
275         }
276         return (0);
277 }
278
279 /*
280  * NOTE: the following function will do nothing if
281  * the structure pointed to by "ssc" and "sc" is zero.
282  */
283 void
284 ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
285     uint32_t sub_units)
286 {
287         uint32_t n;
288
289         usb_proc_drain(&ssc->sc_tq);
290
291         for (n = 0; n != sub_units; n++, sc++) {
292                 if (sc->sc_flag & UCOM_FLAG_ATTACHED) {
293
294                         ucom_detach_tty(sc);
295
296                         ucom_units_free(sc->sc_unit, 1);
297
298                         /* avoid duplicate detach: */
299                         sc->sc_flag &= ~UCOM_FLAG_ATTACHED;
300                 }
301         }
302         usb_proc_free(&ssc->sc_tq);
303 }
304
305 static int
306 ucom_attach_tty(struct ucom_softc *sc, uint32_t sub_units)
307 {
308         struct tty *tp;
309         int error = 0;
310         char buf[32];                   /* temporary TTY device name buffer */
311
312         tp = tty_alloc_mutex(&ucom_class, sc, sc->sc_mtx);
313         if (tp == NULL) {
314                 error = ENOMEM;
315                 goto done;
316         }
317         DPRINTF("tp = %p, unit = %d\n", tp, sc->sc_unit);
318
319         buf[0] = 0;                     /* set some default value */
320
321         /* Check if the client has a custom TTY name */
322         if (sc->sc_callback->ucom_tty_name) {
323                 sc->sc_callback->ucom_tty_name(sc, buf,
324                     sizeof(buf), sc->sc_local_unit);
325         }
326         if (buf[0] == 0) {
327                 /* Use default TTY name */
328                 if (sub_units > 1) {
329                         /* multiple modems in one */
330                         if (snprintf(buf, sizeof(buf), "U%u.%u",
331                             sc->sc_unit - sc->sc_local_unit,
332                             sc->sc_local_unit)) {
333                                 /* ignore */
334                         }
335                 } else {
336                         /* single modem */
337                         if (snprintf(buf, sizeof(buf), "U%u", sc->sc_unit)) {
338                                 /* ignore */
339                         }
340                 }
341         }
342         tty_makedev(tp, NULL, "%s", buf);
343
344         sc->sc_tty = tp;
345
346         DPRINTF("ttycreate: %s\n", buf);
347         cv_init(&sc->sc_cv, "ucom");
348
349 done:
350         return (error);
351 }
352
353 static void
354 ucom_detach_tty(struct ucom_softc *sc)
355 {
356         struct tty *tp = sc->sc_tty;
357
358         DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
359
360         /* the config thread has been stopped when we get here */
361
362         mtx_lock(sc->sc_mtx);
363         sc->sc_flag |= UCOM_FLAG_GONE;
364         sc->sc_flag &= ~(UCOM_FLAG_HL_READY |
365             UCOM_FLAG_LL_READY);
366         mtx_unlock(sc->sc_mtx);
367         if (tp) {
368                 tty_lock(tp);
369
370                 ucom_close(tp); /* close, if any */
371
372                 tty_rel_gone(tp);
373
374                 mtx_lock(sc->sc_mtx);
375                 /* Wait for the callback after the TTY is torn down */
376                 while (sc->sc_ttyfreed == 0)
377                         cv_wait(&sc->sc_cv, sc->sc_mtx);
378                 /*
379                  * make sure that read and write transfers are stopped
380                  */
381                 if (sc->sc_callback->ucom_stop_read) {
382                         (sc->sc_callback->ucom_stop_read) (sc);
383                 }
384                 if (sc->sc_callback->ucom_stop_write) {
385                         (sc->sc_callback->ucom_stop_write) (sc);
386                 }
387                 mtx_unlock(sc->sc_mtx);
388         }
389         cv_destroy(&sc->sc_cv);
390 }
391
392 static void
393 ucom_queue_command(struct ucom_softc *sc,
394     usb_proc_callback_t *fn, struct termios *pt,
395     struct usb_proc_msg *t0, struct usb_proc_msg *t1)
396 {
397         struct ucom_super_softc *ssc = sc->sc_super;
398         struct ucom_param_task *task;
399
400         mtx_assert(sc->sc_mtx, MA_OWNED);
401
402         if (usb_proc_is_gone(&ssc->sc_tq)) {
403                 DPRINTF("proc is gone\n");
404                 return;         /* nothing to do */
405         }
406         /* 
407          * NOTE: The task cannot get executed before we drop the
408          * "sc_mtx" mutex. It is safe to update fields in the message
409          * structure after that the message got queued.
410          */
411         task = (struct ucom_param_task *)
412           usb_proc_msignal(&ssc->sc_tq, t0, t1);
413
414         /* Setup callback and softc pointers */
415         task->hdr.pm_callback = fn;
416         task->sc = sc;
417
418         /* 
419          * Make a copy of the termios. This field is only present if
420          * the "pt" field is not NULL.
421          */
422         if (pt != NULL)
423                 task->termios_copy = *pt;
424
425         /*
426          * Closing the device should be synchronous.
427          */
428         if (fn == ucom_cfg_close)
429                 usb_proc_mwait(&ssc->sc_tq, t0, t1);
430
431         /*
432          * In case of multiple configure requests,
433          * keep track of the last one!
434          */
435         if (fn == ucom_cfg_start_transfers)
436                 sc->sc_last_start_xfer = &task->hdr;
437 }
438
439 static void
440 ucom_shutdown(struct ucom_softc *sc)
441 {
442         struct tty *tp = sc->sc_tty;
443
444         mtx_assert(sc->sc_mtx, MA_OWNED);
445
446         DPRINTF("\n");
447
448         /*
449          * Hang up if necessary:
450          */
451         if (tp->t_termios.c_cflag & HUPCL) {
452                 ucom_modem(tp, 0, SER_DTR);
453         }
454 }
455
456 /*
457  * Return values:
458  *    0: normal
459  * else: taskqueue is draining or gone
460  */
461 uint8_t
462 ucom_cfg_is_gone(struct ucom_softc *sc)
463 {
464         struct ucom_super_softc *ssc = sc->sc_super;
465
466         return (usb_proc_is_gone(&ssc->sc_tq));
467 }
468
469 static void
470 ucom_cfg_start_transfers(struct usb_proc_msg *_task)
471 {
472         struct ucom_cfg_task *task = 
473             (struct ucom_cfg_task *)_task;
474         struct ucom_softc *sc = task->sc;
475
476         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
477                 return;
478         }
479         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
480                 /* TTY device closed */
481                 return;
482         }
483
484         if (_task == sc->sc_last_start_xfer)
485                 sc->sc_flag |= UCOM_FLAG_GP_DATA;
486
487         if (sc->sc_callback->ucom_start_read) {
488                 (sc->sc_callback->ucom_start_read) (sc);
489         }
490         if (sc->sc_callback->ucom_start_write) {
491                 (sc->sc_callback->ucom_start_write) (sc);
492         }
493 }
494
495 static void
496 ucom_start_transfers(struct ucom_softc *sc)
497 {
498         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
499                 return;
500         }
501         /*
502          * Make sure that data transfers are started in both
503          * directions:
504          */
505         if (sc->sc_callback->ucom_start_read) {
506                 (sc->sc_callback->ucom_start_read) (sc);
507         }
508         if (sc->sc_callback->ucom_start_write) {
509                 (sc->sc_callback->ucom_start_write) (sc);
510         }
511 }
512
513 static void
514 ucom_cfg_open(struct usb_proc_msg *_task)
515 {
516         struct ucom_cfg_task *task = 
517             (struct ucom_cfg_task *)_task;
518         struct ucom_softc *sc = task->sc;
519
520         DPRINTF("\n");
521
522         if (sc->sc_flag & UCOM_FLAG_LL_READY) {
523
524                 /* already opened */
525
526         } else {
527
528                 sc->sc_flag |= UCOM_FLAG_LL_READY;
529
530                 if (sc->sc_callback->ucom_cfg_open) {
531                         (sc->sc_callback->ucom_cfg_open) (sc);
532
533                         /* wait a little */
534                         usb_pause_mtx(sc->sc_mtx, hz / 10);
535                 }
536         }
537 }
538
539 static int
540 ucom_open(struct tty *tp)
541 {
542         struct ucom_softc *sc = tty_softc(tp);
543         int error;
544
545         mtx_assert(sc->sc_mtx, MA_OWNED);
546
547         if (sc->sc_flag & UCOM_FLAG_GONE) {
548                 return (ENXIO);
549         }
550         if (sc->sc_flag & UCOM_FLAG_HL_READY) {
551                 /* already opened */
552                 return (0);
553         }
554         DPRINTF("tp = %p\n", tp);
555
556         if (sc->sc_callback->ucom_pre_open) {
557                 /*
558                  * give the lower layer a chance to disallow TTY open, for
559                  * example if the device is not present:
560                  */
561                 error = (sc->sc_callback->ucom_pre_open) (sc);
562                 if (error) {
563                         return (error);
564                 }
565         }
566         sc->sc_flag |= UCOM_FLAG_HL_READY;
567
568         /* Disable transfers */
569         sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
570
571         sc->sc_lsr = 0;
572         sc->sc_msr = 0;
573         sc->sc_mcr = 0;
574
575         /* reset programmed line state */
576         sc->sc_pls_curr = 0;
577         sc->sc_pls_set = 0;
578         sc->sc_pls_clr = 0;
579
580         ucom_queue_command(sc, ucom_cfg_open, NULL,
581             &sc->sc_open_task[0].hdr,
582             &sc->sc_open_task[1].hdr);
583
584         /* Queue transfer enable command last */
585         ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
586             &sc->sc_start_task[0].hdr, 
587             &sc->sc_start_task[1].hdr);
588
589         ucom_modem(tp, SER_DTR | SER_RTS, 0);
590
591         ucom_break(sc, 0);
592
593         ucom_status_change(sc);
594
595         return (0);
596 }
597
598 static void
599 ucom_cfg_close(struct usb_proc_msg *_task)
600 {
601         struct ucom_cfg_task *task = 
602             (struct ucom_cfg_task *)_task;
603         struct ucom_softc *sc = task->sc;
604
605         DPRINTF("\n");
606
607         if (sc->sc_flag & UCOM_FLAG_LL_READY) {
608                 sc->sc_flag &= ~UCOM_FLAG_LL_READY;
609                 if (sc->sc_callback->ucom_cfg_close)
610                         (sc->sc_callback->ucom_cfg_close) (sc);
611         } else {
612                 /* already closed */
613         }
614 }
615
616 static void
617 ucom_close(struct tty *tp)
618 {
619         struct ucom_softc *sc = tty_softc(tp);
620
621         mtx_assert(sc->sc_mtx, MA_OWNED);
622
623         DPRINTF("tp=%p\n", tp);
624
625         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
626                 DPRINTF("tp=%p already closed\n", tp);
627                 return;
628         }
629         ucom_shutdown(sc);
630
631         ucom_queue_command(sc, ucom_cfg_close, NULL,
632             &sc->sc_close_task[0].hdr,
633             &sc->sc_close_task[1].hdr);
634
635         sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW);
636
637         if (sc->sc_callback->ucom_stop_read) {
638                 (sc->sc_callback->ucom_stop_read) (sc);
639         }
640 }
641
642 static int
643 ucom_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
644 {
645         struct ucom_softc *sc = tty_softc(tp);
646         int error;
647
648         mtx_assert(sc->sc_mtx, MA_OWNED);
649
650         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
651                 return (EIO);
652         }
653         DPRINTF("cmd = 0x%08lx\n", cmd);
654
655         switch (cmd) {
656         case TIOCSBRK:
657                 ucom_break(sc, 1);
658                 error = 0;
659                 break;
660         case TIOCCBRK:
661                 ucom_break(sc, 0);
662                 error = 0;
663                 break;
664         default:
665                 if (sc->sc_callback->ucom_ioctl) {
666                         error = (sc->sc_callback->ucom_ioctl)
667                             (sc, cmd, data, 0, td);
668                 } else {
669                         error = ENOIOCTL;
670                 }
671                 break;
672         }
673         return (error);
674 }
675
676 static int
677 ucom_modem(struct tty *tp, int sigon, int sigoff)
678 {
679         struct ucom_softc *sc = tty_softc(tp);
680         uint8_t onoff;
681
682         mtx_assert(sc->sc_mtx, MA_OWNED);
683
684         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
685                 return (0);
686         }
687         if ((sigon == 0) && (sigoff == 0)) {
688
689                 if (sc->sc_mcr & SER_DTR) {
690                         sigon |= SER_DTR;
691                 }
692                 if (sc->sc_mcr & SER_RTS) {
693                         sigon |= SER_RTS;
694                 }
695                 if (sc->sc_msr & SER_CTS) {
696                         sigon |= SER_CTS;
697                 }
698                 if (sc->sc_msr & SER_DCD) {
699                         sigon |= SER_DCD;
700                 }
701                 if (sc->sc_msr & SER_DSR) {
702                         sigon |= SER_DSR;
703                 }
704                 if (sc->sc_msr & SER_RI) {
705                         sigon |= SER_RI;
706                 }
707                 return (sigon);
708         }
709         if (sigon & SER_DTR) {
710                 sc->sc_mcr |= SER_DTR;
711         }
712         if (sigoff & SER_DTR) {
713                 sc->sc_mcr &= ~SER_DTR;
714         }
715         if (sigon & SER_RTS) {
716                 sc->sc_mcr |= SER_RTS;
717         }
718         if (sigoff & SER_RTS) {
719                 sc->sc_mcr &= ~SER_RTS;
720         }
721         onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
722         ucom_dtr(sc, onoff);
723
724         onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
725         ucom_rts(sc, onoff);
726
727         return (0);
728 }
729
730 static void
731 ucom_cfg_line_state(struct usb_proc_msg *_task)
732 {
733         struct ucom_cfg_task *task = 
734             (struct ucom_cfg_task *)_task;
735         struct ucom_softc *sc = task->sc;
736         uint8_t notch_bits;
737         uint8_t any_bits;
738         uint8_t prev_value;
739         uint8_t last_value;
740         uint8_t mask;
741
742         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
743                 return;
744         }
745
746         mask = 0;
747         /* compute callback mask */
748         if (sc->sc_callback->ucom_cfg_set_dtr)
749                 mask |= UCOM_LS_DTR;
750         if (sc->sc_callback->ucom_cfg_set_rts)
751                 mask |= UCOM_LS_RTS;
752         if (sc->sc_callback->ucom_cfg_set_break)
753                 mask |= UCOM_LS_BREAK;
754
755         /* compute the bits we are to program */
756         notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
757         any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
758         prev_value = sc->sc_pls_curr ^ notch_bits;
759         last_value = sc->sc_pls_curr;
760
761         /* reset programmed line state */
762         sc->sc_pls_curr = 0;
763         sc->sc_pls_set = 0;
764         sc->sc_pls_clr = 0;
765
766         /* ensure that we don't loose any levels */
767         if (notch_bits & UCOM_LS_DTR)
768                 sc->sc_callback->ucom_cfg_set_dtr(sc,
769                     (prev_value & UCOM_LS_DTR) ? 1 : 0);
770         if (notch_bits & UCOM_LS_RTS)
771                 sc->sc_callback->ucom_cfg_set_rts(sc,
772                     (prev_value & UCOM_LS_RTS) ? 1 : 0);
773         if (notch_bits & UCOM_LS_BREAK)
774                 sc->sc_callback->ucom_cfg_set_break(sc,
775                     (prev_value & UCOM_LS_BREAK) ? 1 : 0);
776
777         /* set last value */
778         if (any_bits & UCOM_LS_DTR)
779                 sc->sc_callback->ucom_cfg_set_dtr(sc,
780                     (last_value & UCOM_LS_DTR) ? 1 : 0);
781         if (any_bits & UCOM_LS_RTS)
782                 sc->sc_callback->ucom_cfg_set_rts(sc,
783                     (last_value & UCOM_LS_RTS) ? 1 : 0);
784         if (any_bits & UCOM_LS_BREAK)
785                 sc->sc_callback->ucom_cfg_set_break(sc,
786                     (last_value & UCOM_LS_BREAK) ? 1 : 0);
787 }
788
789 static void
790 ucom_line_state(struct ucom_softc *sc,
791     uint8_t set_bits, uint8_t clear_bits)
792 {
793         mtx_assert(sc->sc_mtx, MA_OWNED);
794
795         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
796                 return;
797         }
798
799         DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
800
801         /* update current programmed line state */
802         sc->sc_pls_curr |= set_bits;
803         sc->sc_pls_curr &= ~clear_bits;
804         sc->sc_pls_set |= set_bits;
805         sc->sc_pls_clr |= clear_bits;
806
807         /* defer driver programming */
808         ucom_queue_command(sc, ucom_cfg_line_state, NULL,
809             &sc->sc_line_state_task[0].hdr, 
810             &sc->sc_line_state_task[1].hdr);
811 }
812
813 static void
814 ucom_break(struct ucom_softc *sc, uint8_t onoff)
815 {
816         DPRINTF("onoff = %d\n", onoff);
817
818         if (onoff)
819                 ucom_line_state(sc, UCOM_LS_BREAK, 0);
820         else
821                 ucom_line_state(sc, 0, UCOM_LS_BREAK);
822 }
823
824 static void
825 ucom_dtr(struct ucom_softc *sc, uint8_t onoff)
826 {
827         DPRINTF("onoff = %d\n", onoff);
828
829         if (onoff)
830                 ucom_line_state(sc, UCOM_LS_DTR, 0);
831         else
832                 ucom_line_state(sc, 0, UCOM_LS_DTR);
833 }
834
835 static void
836 ucom_rts(struct ucom_softc *sc, uint8_t onoff)
837 {
838         DPRINTF("onoff = %d\n", onoff);
839
840         if (onoff)
841                 ucom_line_state(sc, UCOM_LS_RTS, 0);
842         else
843                 ucom_line_state(sc, 0, UCOM_LS_RTS);
844 }
845
846 static void
847 ucom_cfg_status_change(struct usb_proc_msg *_task)
848 {
849         struct ucom_cfg_task *task = 
850             (struct ucom_cfg_task *)_task;
851         struct ucom_softc *sc = task->sc;
852         struct tty *tp;
853         uint8_t new_msr;
854         uint8_t new_lsr;
855         uint8_t onoff;
856
857         tp = sc->sc_tty;
858
859         mtx_assert(sc->sc_mtx, MA_OWNED);
860
861         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
862                 return;
863         }
864         if (sc->sc_callback->ucom_cfg_get_status == NULL) {
865                 return;
866         }
867         /* get status */
868
869         new_msr = 0;
870         new_lsr = 0;
871
872         (sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr);
873
874         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
875                 /* TTY device closed */
876                 return;
877         }
878         onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
879
880         sc->sc_msr = new_msr;
881         sc->sc_lsr = new_lsr;
882
883         if (onoff) {
884
885                 onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
886
887                 DPRINTF("DCD changed to %d\n", onoff);
888
889                 ttydisc_modem(tp, onoff);
890         }
891 }
892
893 void
894 ucom_status_change(struct ucom_softc *sc)
895 {
896         mtx_assert(sc->sc_mtx, MA_OWNED);
897
898         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
899                 return;
900         }
901         DPRINTF("\n");
902
903         ucom_queue_command(sc, ucom_cfg_status_change, NULL,
904             &sc->sc_status_task[0].hdr,
905             &sc->sc_status_task[1].hdr);
906 }
907
908 static void
909 ucom_cfg_param(struct usb_proc_msg *_task)
910 {
911         struct ucom_param_task *task = 
912             (struct ucom_param_task *)_task;
913         struct ucom_softc *sc = task->sc;
914
915         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
916                 return;
917         }
918         if (sc->sc_callback->ucom_cfg_param == NULL) {
919                 return;
920         }
921
922         (sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy);
923
924         /* wait a little */
925         usb_pause_mtx(sc->sc_mtx, hz / 10);
926 }
927
928 static int
929 ucom_param(struct tty *tp, struct termios *t)
930 {
931         struct ucom_softc *sc = tty_softc(tp);
932         uint8_t opened;
933         int error;
934
935         mtx_assert(sc->sc_mtx, MA_OWNED);
936
937         opened = 0;
938         error = 0;
939
940         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
941
942                 /* XXX the TTY layer should call "open()" first! */
943
944                 error = ucom_open(tp);
945                 if (error) {
946                         goto done;
947                 }
948                 opened = 1;
949         }
950         DPRINTF("sc = %p\n", sc);
951
952         /* Check requested parameters. */
953         if (t->c_ospeed < 0) {
954                 DPRINTF("negative ospeed\n");
955                 error = EINVAL;
956                 goto done;
957         }
958         if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
959                 DPRINTF("mismatch ispeed and ospeed\n");
960                 error = EINVAL;
961                 goto done;
962         }
963         t->c_ispeed = t->c_ospeed;
964
965         if (sc->sc_callback->ucom_pre_param) {
966                 /* Let the lower layer verify the parameters */
967                 error = (sc->sc_callback->ucom_pre_param) (sc, t);
968                 if (error) {
969                         DPRINTF("callback error = %d\n", error);
970                         goto done;
971                 }
972         }
973
974         /* Disable transfers */
975         sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
976
977         /* Queue baud rate programming command first */
978         ucom_queue_command(sc, ucom_cfg_param, t,
979             &sc->sc_param_task[0].hdr,
980             &sc->sc_param_task[1].hdr);
981
982         /* Queue transfer enable command last */
983         ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
984             &sc->sc_start_task[0].hdr, 
985             &sc->sc_start_task[1].hdr);
986
987         if (t->c_cflag & CRTS_IFLOW) {
988                 sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
989         } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
990                 sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
991                 ucom_modem(tp, SER_RTS, 0);
992         }
993 done:
994         if (error) {
995                 if (opened) {
996                         ucom_close(tp);
997                 }
998         }
999         return (error);
1000 }
1001
1002 static void
1003 ucom_outwakeup(struct tty *tp)
1004 {
1005         struct ucom_softc *sc = tty_softc(tp);
1006
1007         mtx_assert(sc->sc_mtx, MA_OWNED);
1008
1009         DPRINTF("sc = %p\n", sc);
1010
1011         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1012                 /* The higher layer is not ready */
1013                 return;
1014         }
1015         ucom_start_transfers(sc);
1016 }
1017
1018 /*------------------------------------------------------------------------*
1019  *      ucom_get_data
1020  *
1021  * Return values:
1022  * 0: No data is available.
1023  * Else: Data is available.
1024  *------------------------------------------------------------------------*/
1025 uint8_t
1026 ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1027     uint32_t offset, uint32_t len, uint32_t *actlen)
1028 {
1029         struct usb_page_search res;
1030         struct tty *tp = sc->sc_tty;
1031         uint32_t cnt;
1032         uint32_t offset_orig;
1033
1034         mtx_assert(sc->sc_mtx, MA_OWNED);
1035
1036         if (tty_gone(tp) ||
1037             !(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
1038                 actlen[0] = 0;
1039                 return (0);             /* multiport device polling */
1040         }
1041         offset_orig = offset;
1042
1043         while (len != 0) {
1044
1045                 usbd_get_page(pc, offset, &res);
1046
1047                 if (res.length > len) {
1048                         res.length = len;
1049                 }
1050                 /* copy data directly into USB buffer */
1051                 cnt = ttydisc_getc(tp, res.buffer, res.length);
1052
1053                 offset += cnt;
1054                 len -= cnt;
1055
1056                 if (cnt < res.length) {
1057                         /* end of buffer */
1058                         break;
1059                 }
1060         }
1061
1062         actlen[0] = offset - offset_orig;
1063
1064         DPRINTF("cnt=%d\n", actlen[0]);
1065
1066         if (actlen[0] == 0) {
1067                 return (0);
1068         }
1069         return (1);
1070 }
1071
1072 void
1073 ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1074     uint32_t offset, uint32_t len)
1075 {
1076         struct usb_page_search res;
1077         struct tty *tp = sc->sc_tty;
1078         char *buf;
1079         uint32_t cnt;
1080
1081         mtx_assert(sc->sc_mtx, MA_OWNED);
1082
1083         if (tty_gone(tp))
1084                 return;                 /* multiport device polling */
1085
1086         if (len == 0)
1087                 return;                 /* no data */
1088
1089         /* set a flag to prevent recursation ? */
1090
1091         while (len > 0) {
1092
1093                 usbd_get_page(pc, offset, &res);
1094
1095                 if (res.length > len) {
1096                         res.length = len;
1097                 }
1098                 len -= res.length;
1099                 offset += res.length;
1100
1101                 /* pass characters to tty layer */
1102
1103                 buf = res.buffer;
1104                 cnt = res.length;
1105
1106                 /* first check if we can pass the buffer directly */
1107
1108                 if (ttydisc_can_bypass(tp)) {
1109                         if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
1110                                 DPRINTF("tp=%p, data lost\n", tp);
1111                         }
1112                         continue;
1113                 }
1114                 /* need to loop */
1115
1116                 for (cnt = 0; cnt != res.length; cnt++) {
1117                         if (ttydisc_rint(tp, buf[cnt], 0) == -1) {
1118                                 /* XXX what should we do? */
1119
1120                                 DPRINTF("tp=%p, lost %d "
1121                                     "chars\n", tp, res.length - cnt);
1122                                 break;
1123                         }
1124                 }
1125         }
1126         ttydisc_rint_done(tp);
1127 }
1128
1129 static void
1130 ucom_free(void *xsc)
1131 {
1132         struct ucom_softc *sc = xsc;
1133
1134         mtx_lock(sc->sc_mtx);
1135         sc->sc_ttyfreed = 1;
1136         cv_signal(&sc->sc_cv);
1137         mtx_unlock(sc->sc_mtx);
1138 }