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