]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/usb/ucom.c
This commit was generated by cvs2svn to compensate for changes in r177576,
[FreeBSD/FreeBSD.git] / sys / dev / usb / ucom.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/param.h>
71 #include <sys/systm.h>
72 #include <sys/kernel.h>
73 #include <sys/malloc.h>
74 #include <sys/module.h>
75 #include <sys/bus.h>
76 #include <sys/ioccom.h>
77 #include <sys/fcntl.h>
78 #include <sys/conf.h>
79 #include <sys/serial.h>
80 #include <sys/tty.h>
81 #include <sys/clist.h>
82 #include <sys/file.h>
83 #include <sys/selinfo.h>
84 #include <sys/proc.h>
85 #include <sys/poll.h>
86 #include <sys/sysctl.h>
87
88 #include <dev/usb/usb.h>
89 #include <dev/usb/usbcdc.h>
90
91 #include <dev/usb/usbdi.h>
92 #include <dev/usb/usbdi_util.h>
93 #include "usbdevs.h"
94 #include <dev/usb/usb_quirks.h>
95
96 #include <dev/usb/ucomvar.h>
97
98 #ifdef USB_DEBUG
99 static int      ucomdebug = 0;
100 SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
101 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
102            &ucomdebug, 0, "ucom debug level");
103 #define DPRINTF(x)      do { \
104                                 if (ucomdebug) \
105                                         printf x; \
106                         } while (0)
107
108 #define DPRINTFN(n, x)  do { \
109                                 if (ucomdebug > (n)) \
110                                         printf x; \
111                         } while (0)
112 #else
113 #define DPRINTF(x)
114 #define DPRINTFN(n, x)
115 #endif
116
117 static int ucom_modevent(module_t, int, void *);
118 static void ucom_cleanup(struct ucom_softc *);
119 static int ucomparam(struct tty *, struct termios *);
120 static void ucomstart(struct tty *);
121 static void ucomstop(struct tty *, int);
122 static void ucom_shutdown(struct ucom_softc *);
123 static void ucom_dtr(struct ucom_softc *, int);
124 static void ucom_rts(struct ucom_softc *, int);
125 static void ucombreak(struct tty *, int);
126 static usbd_status ucomstartread(struct ucom_softc *);
127 static void ucomreadcb(usbd_xfer_handle, usbd_private_handle, usbd_status);
128 static void ucomwritecb(usbd_xfer_handle, usbd_private_handle, usbd_status);
129 static void ucomstopread(struct ucom_softc *);
130
131 static t_open_t  ucomopen;
132 static t_close_t ucomclose;
133 static t_modem_t ucommodem;
134 static t_ioctl_t ucomioctl;
135
136 devclass_t ucom_devclass;
137
138 static moduledata_t ucom_mod = {
139         "ucom",
140         ucom_modevent,
141         NULL
142 };
143
144 DECLARE_MODULE(ucom, ucom_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
145 MODULE_DEPEND(ucom, usb, 1, 1, 1);
146 MODULE_VERSION(ucom, UCOM_MODVER);
147
148 static int
149 ucom_modevent(module_t mod, int type, void *data)
150 {
151         switch (type) {
152         case MOD_LOAD:
153                 break;
154         case MOD_UNLOAD:
155                 break;
156         default:
157                 return (EOPNOTSUPP);
158                 break;
159         }
160         return (0);
161 }
162
163 int
164 ucom_attach(struct ucom_softc *sc)
165 {
166         struct tty *tp;
167         int unit;
168
169         unit = device_get_unit(sc->sc_dev);
170
171         sc->sc_tty = tp = ttyalloc();
172         tp->t_sc = sc;
173         tp->t_oproc = ucomstart;
174         tp->t_param = ucomparam;
175         tp->t_stop = ucomstop;
176         tp->t_break = ucombreak;
177         tp->t_open = ucomopen;
178         tp->t_close = ucomclose;
179         tp->t_modem = ucommodem;
180         tp->t_ioctl = ucomioctl;
181
182         DPRINTF(("ucom_attach: tty_attach tp = %p\n", tp));
183
184         ttycreate(tp, TS_CALLOUT, "U%d", unit);
185         DPRINTF(("ucom_attach: ttycreate: ttyU%d\n", unit));
186
187         return (0);
188 }
189
190 int
191 ucom_detach(struct ucom_softc *sc)
192 {
193         int s;
194
195         DPRINTF(("ucom_detach: sc = %p, tp = %p\n", sc, sc->sc_tty));
196
197         sc->sc_dying = 1;
198         ttygone(sc->sc_tty);
199         if (sc->sc_tty->t_state & TS_ISOPEN)
200                 ucomclose(sc->sc_tty);
201
202         if (sc->sc_bulkin_pipe != NULL)
203                 usbd_abort_pipe(sc->sc_bulkin_pipe);
204         if (sc->sc_bulkout_pipe != NULL)
205                 usbd_abort_pipe(sc->sc_bulkout_pipe);
206
207         ttyfree(sc->sc_tty);
208
209         s = splusb();
210         splx(s);
211
212         return (0);
213 }
214
215 static void
216 ucom_shutdown(struct ucom_softc *sc)
217 {
218         struct tty *tp = sc->sc_tty;
219
220         DPRINTF(("ucom_shutdown\n"));
221         /*
222          * Hang up if necessary.  Wait a bit, so the other side has time to
223          * notice even if we immediately open the port again.
224          */
225         if (ISSET(tp->t_cflag, HUPCL)) {
226                 (void)ucommodem(tp, 0, SER_DTR);
227                 (void)tsleep(sc, TTIPRI, "ucomsd", hz);
228         }
229 }
230
231 static int
232 ucomopen(struct tty *tp, struct cdev *dev)
233 {
234         struct ucom_softc *sc;
235         usbd_status err;
236         int error;
237
238         sc = tp->t_sc;
239
240         if (sc->sc_dying)
241                 return (ENXIO);
242
243         DPRINTF(("%s: ucomopen: tp = %p\n", device_get_nameunit(sc->sc_dev), tp));
244
245         sc->sc_poll = 0;
246         sc->sc_lsr = sc->sc_msr = sc->sc_mcr = 0;
247
248         (void)ucommodem(tp, SER_DTR | SER_RTS, 0);
249
250         /* Device specific open */
251         if (sc->sc_callback->ucom_open != NULL) {
252                 error = sc->sc_callback->ucom_open(sc->sc_parent,
253                                                    sc->sc_portno);
254                 if (error) {
255                         ucom_cleanup(sc);
256                         return (error);
257                 }
258         }
259
260         DPRINTF(("ucomopen: open pipes in = %d out = %d\n",
261                  sc->sc_bulkin_no, sc->sc_bulkout_no));
262
263         /* Open the bulk pipes */
264         /* Bulk-in pipe */
265         err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_no, 0,
266                              &sc->sc_bulkin_pipe);
267         if (err) {
268                 printf("%s: open bulk in error (addr %d): %s\n",
269                        device_get_nameunit(sc->sc_dev), sc->sc_bulkin_no,
270                        usbd_errstr(err));
271                 error = EIO;
272                 goto fail;
273         }
274         /* Bulk-out pipe */
275         err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_no,
276                              USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
277         if (err) {
278                 printf("%s: open bulk out error (addr %d): %s\n",
279                        device_get_nameunit(sc->sc_dev), sc->sc_bulkout_no,
280                        usbd_errstr(err));
281                 error = EIO;
282                 goto fail;
283         }
284
285         /* Allocate a request and an input buffer and start reading. */
286         sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev);
287         if (sc->sc_ixfer == NULL) {
288                 error = ENOMEM;
289                 goto fail;
290         }
291
292         sc->sc_ibuf = usbd_alloc_buffer(sc->sc_ixfer,
293                                         sc->sc_ibufsizepad);
294         if (sc->sc_ibuf == NULL) {
295                 error = ENOMEM;
296                 goto fail;
297         }
298
299         sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev);
300         if (sc->sc_oxfer == NULL) {
301                 error = ENOMEM;
302                 goto fail;
303         }
304
305         sc->sc_obuf = usbd_alloc_buffer(sc->sc_oxfer,
306                                         sc->sc_obufsize +
307                                         sc->sc_opkthdrlen);
308         if (sc->sc_obuf == NULL) {
309                 error = ENOMEM;
310                 goto fail;
311         }
312
313         sc->sc_state |= UCS_RXSTOP;
314         ucomstartread(sc);
315
316         sc->sc_poll = 1;
317
318         return (0);
319
320 fail:
321         ucom_cleanup(sc);
322         return (error);
323 }
324
325 static void
326 ucomclose(struct tty *tp)
327 {
328         struct ucom_softc *sc;
329
330         sc = tp->t_sc;
331
332         DPRINTF(("%s: ucomclose \n", device_get_nameunit(sc->sc_dev)));
333
334         ucom_cleanup(sc);
335
336         if (sc->sc_callback->ucom_close != NULL)
337                 sc->sc_callback->ucom_close(sc->sc_parent, sc->sc_portno);
338 }
339
340 static int
341 ucomioctl(struct tty *tp, u_long cmd, void *data, int flag, struct thread *p)
342 {
343         struct ucom_softc *sc;
344         int error;
345
346         sc = tp->t_sc;;
347         if (sc->sc_dying)
348                 return (EIO);
349
350         DPRINTF(("ucomioctl: cmd = 0x%08lx\n", cmd));
351
352         error = ENOTTY;
353         if (sc->sc_callback->ucom_ioctl != NULL)
354                 error = sc->sc_callback->ucom_ioctl(sc->sc_parent,
355                                                     sc->sc_portno,
356                                                     cmd, data, flag, p);
357         return (error);
358 }
359
360 static int
361 ucommodem(struct tty *tp, int sigon, int sigoff)
362 {
363         struct ucom_softc *sc;
364         int     mcr;
365         int     msr;
366         int     onoff;
367
368         sc = tp->t_sc;
369
370         if (sigon == 0 && sigoff == 0) {
371                 mcr = sc->sc_mcr;
372                 if (ISSET(mcr, SER_DTR))
373                         sigon |= SER_DTR;
374                 if (ISSET(mcr, SER_RTS))
375                         sigon |= SER_RTS;
376
377                 msr = sc->sc_msr;
378                 if (ISSET(msr, SER_CTS))
379                         sigon |= SER_CTS;
380                 if (ISSET(msr, SER_DCD))
381                         sigon |= SER_DCD;
382                 if (ISSET(msr, SER_DSR))
383                         sigon |= SER_DSR;
384                 if (ISSET(msr, SER_RI))
385                         sigon |= SER_RI;
386                 return (sigon);
387         }
388
389         mcr = sc->sc_mcr;
390         if (ISSET(sigon, SER_DTR))
391                 mcr |= SER_DTR;
392         if (ISSET(sigoff, SER_DTR))
393                 mcr &= ~SER_DTR;
394         if (ISSET(sigon, SER_RTS))
395                 mcr |= SER_RTS;
396         if (ISSET(sigoff, SER_RTS))
397                 mcr &= ~SER_RTS;
398         sc->sc_mcr = mcr;
399
400         onoff = ISSET(sc->sc_mcr, SER_DTR) ? 1 : 0;
401         ucom_dtr(sc, onoff);
402
403         onoff = ISSET(sc->sc_mcr, SER_RTS) ? 1 : 0;
404         ucom_rts(sc, onoff);
405
406         return (0);
407 }
408
409 static void
410 ucombreak(struct tty *tp, int onoff)
411 {
412         struct ucom_softc *sc;
413
414         sc = tp->t_sc;
415         DPRINTF(("ucombreak: onoff = %d\n", onoff));
416         if (sc->sc_callback->ucom_set == NULL)
417                 return;
418         sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
419                                   UCOM_SET_BREAK, onoff);
420 }
421
422 static void
423 ucom_dtr(struct ucom_softc *sc, int onoff)
424 {
425         DPRINTF(("ucom_dtr: onoff = %d\n", onoff));
426
427         if (sc->sc_callback->ucom_set == NULL)
428                 return;
429         sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
430                                   UCOM_SET_DTR, onoff);
431 }
432
433 static void
434 ucom_rts(struct ucom_softc *sc, int onoff)
435 {
436         DPRINTF(("ucom_rts: onoff = %d\n", onoff));
437
438         if (sc->sc_callback->ucom_set == NULL)
439                 return;
440         sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
441                                   UCOM_SET_RTS, onoff);
442 }
443
444 void
445 ucom_status_change(struct ucom_softc *sc)
446 {
447         struct tty *tp = sc->sc_tty;
448         u_char old_msr;
449         int onoff;
450
451         if (sc->sc_callback->ucom_get_status == NULL) {
452                 sc->sc_lsr = 0;
453                 sc->sc_msr = 0;
454                 return;
455         }
456
457         old_msr = sc->sc_msr;
458         sc->sc_callback->ucom_get_status(sc->sc_parent, sc->sc_portno,
459                                          &sc->sc_lsr, &sc->sc_msr);
460         if (ISSET((sc->sc_msr ^ old_msr), SER_DCD)) {
461                 if (sc->sc_poll == 0)
462                         return;
463                 onoff = ISSET(sc->sc_msr, SER_DCD) ? 1 : 0;
464                 DPRINTF(("ucom_status_change: DCD changed to %d\n", onoff));
465                 ttyld_modem(tp, onoff);
466         }
467 }
468
469 static int
470 ucomparam(struct tty *tp, struct termios *t)
471 {
472         struct ucom_softc *sc;
473         int error;
474         usbd_status uerr;
475
476         sc = tp->t_sc;
477
478         if (sc->sc_dying)
479                 return (EIO);
480
481         DPRINTF(("ucomparam: sc = %p\n", sc));
482
483         /* Check requested parameters. */
484         if (t->c_ospeed < 0) {
485                 DPRINTF(("ucomparam: negative ospeed\n"));
486                 return (EINVAL);
487         }
488         if (t->c_ispeed && t->c_ispeed != t->c_ospeed) {
489                 DPRINTF(("ucomparam: mismatch ispeed and ospeed\n"));
490                 return (EINVAL);
491         }
492
493         /*
494          * If there were no changes, don't do anything.  This avoids dropping
495          * input and improves performance when all we did was frob things like
496          * VMIN and VTIME.
497          */
498         if (tp->t_ospeed == t->c_ospeed &&
499             tp->t_cflag == t->c_cflag)
500                 return (0);
501
502         /* And copy to tty. */
503         tp->t_ispeed = 0;
504         tp->t_ospeed = t->c_ospeed;
505         tp->t_cflag = t->c_cflag;
506
507         if (sc->sc_callback->ucom_param == NULL)
508                 return (0);
509
510         ucomstopread(sc);
511
512         error = sc->sc_callback->ucom_param(sc->sc_parent, sc->sc_portno, t);
513         if (error) {
514                 DPRINTF(("ucomparam: callback: error = %d\n", error));
515                 return (error);
516         }
517
518         ttsetwater(tp);
519
520         if (t->c_cflag & CRTS_IFLOW) {
521                 sc->sc_state |= UCS_RTS_IFLOW;
522         } else if (sc->sc_state & UCS_RTS_IFLOW) {
523                 sc->sc_state &= ~UCS_RTS_IFLOW;
524                 (void)ucommodem(tp, SER_RTS, 0);
525         }
526
527         ttyldoptim(tp);
528
529         uerr = ucomstartread(sc);
530         if (uerr != USBD_NORMAL_COMPLETION)
531                 return (EIO);
532
533         return (0);
534 }
535
536 static void
537 ucomstart(struct tty *tp)
538 {
539         struct ucom_softc *sc;
540         struct cblock *cbp;
541         usbd_status err;
542         int s;
543         u_char *data;
544         int cnt;
545
546         sc = tp->t_sc;
547         DPRINTF(("ucomstart: sc = %p\n", sc));
548
549         if (sc->sc_dying)
550                 return;
551
552         /*
553          * If there's no sc_oxfer, then ucomclose has removed it.  The buffer
554          * has just been flushed in the ttyflush() in ttyclose().  ttyflush()
555          * then calls tt_stop().  ucomstop calls ucomstart, so the right thing
556          * to do here is just abort if sc_oxfer is NULL, as everything else
557          * is cleaned up elsewhere.
558          */
559         if (sc->sc_oxfer == NULL)
560                 return;
561
562         s = spltty();
563
564         if (tp->t_state & TS_TBLOCK) {
565                 if (ISSET(sc->sc_mcr, SER_RTS) &&
566                     ISSET(sc->sc_state, UCS_RTS_IFLOW)) {
567                         DPRINTF(("ucomstart: clear RTS\n"));
568                         (void)ucommodem(tp, 0, SER_RTS);
569                 }
570         } else {
571                 if (!ISSET(sc->sc_mcr, SER_RTS) &&
572                     tp->t_rawq.c_cc <= tp->t_ilowat &&
573                     ISSET(sc->sc_state, UCS_RTS_IFLOW)) {
574                         DPRINTF(("ucomstart: set RTS\n"));
575                         (void)ucommodem(tp, SER_RTS, 0);
576                 }
577         }
578
579         if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) {
580                 ttwwakeup(tp);
581                 DPRINTF(("ucomstart: stopped\n"));
582                 goto out;
583         }
584
585         if (tp->t_outq.c_cc <= tp->t_olowat) {
586                 if (ISSET(tp->t_state, TS_SO_OLOWAT)) {
587                         CLR(tp->t_state, TS_SO_OLOWAT);
588                         wakeup(TSA_OLOWAT(tp));
589                 }
590                 selwakeuppri(&tp->t_wsel, TTIPRI);
591                 if (tp->t_outq.c_cc == 0) {
592                         if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) ==
593                             TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) {
594                                 CLR(tp->t_state, TS_SO_OCOMPLETE);
595                                 wakeup(TSA_OCOMPLETE(tp));
596                         }
597                         goto out;
598                 }
599         }
600
601         /* Grab the first contiguous region of buffer space. */
602         data = tp->t_outq.c_cf;
603         cbp = (struct cblock *) ((intptr_t) tp->t_outq.c_cf & ~CROUND);
604         cnt = min((char *) (cbp+1) - tp->t_outq.c_cf, tp->t_outq.c_cc);
605
606         if (cnt == 0) {
607                 DPRINTF(("ucomstart: cnt == 0\n"));
608                 goto out;
609         }
610
611         SET(tp->t_state, TS_BUSY);
612
613         if (cnt > sc->sc_obufsize) {
614                 DPRINTF(("ucomstart: big buffer %d chars\n", cnt));
615                 cnt = sc->sc_obufsize;
616         }
617         if (sc->sc_callback->ucom_write != NULL)
618                 sc->sc_callback->ucom_write(sc->sc_parent, sc->sc_portno,
619                                             sc->sc_obuf, data, &cnt);
620         else
621                 memcpy(sc->sc_obuf, data, cnt);
622
623         DPRINTF(("ucomstart: %d chars\n", cnt));
624         usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe,
625                         (usbd_private_handle)sc, sc->sc_obuf, cnt,
626                         USBD_NO_COPY, USBD_NO_TIMEOUT, ucomwritecb);
627         /* What can we do on error? */
628         err = usbd_transfer(sc->sc_oxfer);
629         if (err != USBD_IN_PROGRESS)
630                 printf("ucomstart: err=%s\n", usbd_errstr(err));
631
632         ttwwakeup(tp);
633
634     out:
635         splx(s);
636 }
637
638 static void
639 ucomstop(struct tty *tp, int flag)
640 {
641         struct ucom_softc *sc;
642         int s;
643
644         sc = tp->t_sc;
645
646         DPRINTF(("ucomstop: %d\n", flag));
647
648         if ((flag & FREAD) && (sc->sc_state & UCS_RXSTOP) == 0) {
649                 DPRINTF(("ucomstop: read\n"));
650                 ucomstopread(sc);
651                 ucomstartread(sc);
652         }
653
654         if (flag & FWRITE) {
655                 DPRINTF(("ucomstop: write\n"));
656                 s = spltty();
657                 if (ISSET(tp->t_state, TS_BUSY)) {
658                         /* XXX do what? */
659                         if (!ISSET(tp->t_state, TS_TTSTOP))
660                                 SET(tp->t_state, TS_FLUSH);
661                 }
662                 splx(s);
663         }
664
665         ucomstart(tp);
666
667         DPRINTF(("ucomstop: done\n"));
668 }
669
670 static void
671 ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
672 {
673         struct ucom_softc *sc = (struct ucom_softc *)p;
674         struct tty *tp = sc->sc_tty;
675         u_int32_t cc;
676         int s;
677
678         DPRINTF(("ucomwritecb: status = %d\n", status));
679
680         if (status == USBD_CANCELLED || sc->sc_dying)
681                 goto error;
682
683         if (status != USBD_NORMAL_COMPLETION) {
684                 printf("%s: ucomwritecb: %s\n",
685                        device_get_nameunit(sc->sc_dev), usbd_errstr(status));
686                 if (status == USBD_STALLED)
687                         usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
688                 /* XXX we should restart after some delay. */
689                 goto error;
690         }
691
692         usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
693         DPRINTF(("ucomwritecb: cc = %d\n", cc));
694         if (cc <= sc->sc_opkthdrlen) {
695                 printf("%s: sent size too small, cc = %d\n",
696                        device_get_nameunit(sc->sc_dev), cc);
697                 goto error;
698         }
699
700         /* convert from USB bytes to tty bytes */
701         cc -= sc->sc_opkthdrlen;
702
703         s = spltty();
704         CLR(tp->t_state, TS_BUSY);
705         if (ISSET(tp->t_state, TS_FLUSH))
706                 CLR(tp->t_state, TS_FLUSH);
707         else
708                 ndflush(&tp->t_outq, cc);
709         ttyld_start(tp);
710         splx(s);
711
712         return;
713
714   error:
715         s = spltty();
716         CLR(tp->t_state, TS_BUSY);
717         splx(s);
718         return;
719 }
720
721 static usbd_status
722 ucomstartread(struct ucom_softc *sc)
723 {
724         usbd_status err;
725
726         DPRINTF(("ucomstartread: start\n"));
727
728         if (sc->sc_bulkin_pipe == NULL || (sc->sc_state & UCS_RXSTOP) == 0)
729                 return (USBD_NORMAL_COMPLETION);
730         sc->sc_state &= ~UCS_RXSTOP;
731
732         usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe,
733                         (usbd_private_handle)sc,
734                         sc->sc_ibuf, sc->sc_ibufsize,
735                         USBD_SHORT_XFER_OK | USBD_NO_COPY,
736                         USBD_NO_TIMEOUT, ucomreadcb);
737
738         err = usbd_transfer(sc->sc_ixfer);
739         if (err && err != USBD_IN_PROGRESS) {
740                 sc->sc_state |= UCS_RXSTOP;
741                 DPRINTF(("ucomstartread: err = %s\n", usbd_errstr(err)));
742                 return (err);
743         }
744
745         return (USBD_NORMAL_COMPLETION);
746 }
747
748 static void
749 ucomreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
750 {
751         struct ucom_softc *sc = (struct ucom_softc *)p;
752         struct tty *tp = sc->sc_tty;
753         usbd_status err;
754         u_int32_t cc;
755         u_char *cp;
756         int lostcc;
757         int s;
758
759         DPRINTF(("ucomreadcb: status = %d\n", status));
760
761         if (status != USBD_NORMAL_COMPLETION) {
762                 if (!(sc->sc_state & UCS_RXSTOP))
763                         printf("%s: ucomreadcb: %s\n",
764                                device_get_nameunit(sc->sc_dev), usbd_errstr(status));
765                 sc->sc_state |= UCS_RXSTOP;
766                 if (status == USBD_STALLED)
767                         usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
768                 /* XXX we should restart after some delay. */
769                 return;
770         }
771         sc->sc_state |= UCS_RXSTOP;
772
773         usbd_get_xfer_status(xfer, NULL, (void **)&cp, &cc, NULL);
774         DPRINTF(("ucomreadcb: got %d chars, tp = %p\n", cc, tp));
775         if (cc == 0)
776                 goto resubmit;
777
778         if (sc->sc_callback->ucom_read != NULL)
779                 sc->sc_callback->ucom_read(sc->sc_parent, sc->sc_portno,
780                                            &cp, &cc);
781
782         if (cc > sc->sc_ibufsize) {
783                 printf("%s: invalid receive data size, %d chars\n",
784                        device_get_nameunit(sc->sc_dev), cc);
785                 goto resubmit;
786         }
787         if (cc < 1)
788                 goto resubmit;
789
790         s = spltty();
791         if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
792                 if (tp->t_rawq.c_cc + cc > tp->t_ihiwat
793                     && (sc->sc_state & UCS_RTS_IFLOW
794                         || tp->t_iflag & IXOFF)
795                     && !(tp->t_state & TS_TBLOCK))
796                         ttyblock(tp);
797                 lostcc = b_to_q((char *)cp, cc, &tp->t_rawq);
798                 tp->t_rawcc += cc;
799                 ttwakeup(tp);
800                 if (tp->t_state & TS_TTSTOP
801                     && (tp->t_iflag & IXANY
802                         || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
803                         tp->t_state &= ~TS_TTSTOP;
804                         tp->t_lflag &= ~FLUSHO;
805                         ucomstart(tp);
806                 }
807                 if (lostcc > 0)
808                         printf("%s: lost %d chars\n", device_get_nameunit(sc->sc_dev),
809                                lostcc);
810         } else {
811                 /* Give characters to tty layer. */
812                 while (cc > 0) {
813                         DPRINTFN(7, ("ucomreadcb: char = 0x%02x\n", *cp));
814                         if (ttyld_rint(tp, *cp) == -1) {
815                                 /* XXX what should we do? */
816                                 printf("%s: lost %d chars\n",
817                                        device_get_nameunit(sc->sc_dev), cc);
818                                 break;
819                         }
820                         cc--;
821                         cp++;
822                 }
823         }
824         splx(s);
825
826     resubmit:
827         err = ucomstartread(sc);
828         if (err) {
829                 printf("%s: read start failed\n", device_get_nameunit(sc->sc_dev));
830                 /* XXX what should we dow now? */
831         }
832
833         if ((sc->sc_state & UCS_RTS_IFLOW) && !ISSET(sc->sc_mcr, SER_RTS)
834             && !(tp->t_state & TS_TBLOCK))
835                 ucommodem(tp, SER_RTS, 0);
836 }
837
838 static void
839 ucom_cleanup(struct ucom_softc *sc)
840 {
841         DPRINTF(("ucom_cleanup: closing pipes\n"));
842
843         ucom_shutdown(sc);
844         if (sc->sc_bulkin_pipe != NULL) {
845                 sc->sc_state |= UCS_RXSTOP;
846                 usbd_abort_pipe(sc->sc_bulkin_pipe);
847                 usbd_close_pipe(sc->sc_bulkin_pipe);
848                 sc->sc_bulkin_pipe = NULL;
849         }
850         if (sc->sc_bulkout_pipe != NULL) {
851                 usbd_abort_pipe(sc->sc_bulkout_pipe);
852                 usbd_close_pipe(sc->sc_bulkout_pipe);
853                 sc->sc_bulkout_pipe = NULL;
854         }
855         if (sc->sc_ixfer != NULL) {
856                 usbd_free_xfer(sc->sc_ixfer);
857                 sc->sc_ixfer = NULL;
858         }
859         if (sc->sc_oxfer != NULL) {
860                 usbd_free_xfer(sc->sc_oxfer);
861                 sc->sc_oxfer = NULL;
862         }
863 }
864
865 static void
866 ucomstopread(struct ucom_softc *sc)
867 {
868         usbd_status err;
869
870         DPRINTF(("ucomstopread: enter\n"));
871
872         if (!(sc->sc_state & UCS_RXSTOP)) {
873                 sc->sc_state |= UCS_RXSTOP;
874                 if (sc->sc_bulkin_pipe == NULL) {
875                         DPRINTF(("ucomstopread: bulkin pipe NULL\n"));
876                         return;
877                 }
878                 err = usbd_abort_pipe(sc->sc_bulkin_pipe);
879                 if (err) {
880                         DPRINTF(("ucomstopread: err = %s\n",
881                                  usbd_errstr(err)));
882                 }
883         }
884
885         DPRINTF(("ucomstopread: leave\n"));
886 }