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