]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/usb/ucom.c
This commit was generated by cvs2svn to compensate for changes in r174993,
[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         /*
536          * If there's no sc_oxfer, then ucomclose has removed it.  The buffer
537          * has just been flushed in the ttyflush() in ttyclose().  ttyflush()
538          * then calls tt_stop().  ucomstop calls ucomstart, so the right thing
539          * to do here is just abort if sc_oxfer is NULL, as everything else
540          * is cleaned up elsewhere.
541          */
542         if (sc->sc_oxfer == NULL)
543                 return;
544
545         s = spltty();
546
547         if (tp->t_state & TS_TBLOCK) {
548                 if (ISSET(sc->sc_mcr, SER_RTS) &&
549                     ISSET(sc->sc_state, UCS_RTS_IFLOW)) {
550                         DPRINTF(("ucomstart: clear RTS\n"));
551                         (void)ucommodem(tp, 0, SER_RTS);
552                 }
553         } else {
554                 if (!ISSET(sc->sc_mcr, SER_RTS) &&
555                     tp->t_rawq.c_cc <= tp->t_ilowat &&
556                     ISSET(sc->sc_state, UCS_RTS_IFLOW)) {
557                         DPRINTF(("ucomstart: set RTS\n"));
558                         (void)ucommodem(tp, SER_RTS, 0);
559                 }
560         }
561
562         if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) {
563                 ttwwakeup(tp);
564                 DPRINTF(("ucomstart: stopped\n"));
565                 goto out;
566         }
567
568         if (tp->t_outq.c_cc <= tp->t_olowat) {
569                 if (ISSET(tp->t_state, TS_SO_OLOWAT)) {
570                         CLR(tp->t_state, TS_SO_OLOWAT);
571                         wakeup(TSA_OLOWAT(tp));
572                 }
573                 selwakeuppri(&tp->t_wsel, TTIPRI);
574                 if (tp->t_outq.c_cc == 0) {
575                         if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) ==
576                             TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) {
577                                 CLR(tp->t_state, TS_SO_OCOMPLETE);
578                                 wakeup(TSA_OCOMPLETE(tp));
579                         }
580                         goto out;
581                 }
582         }
583
584         /* Grab the first contiguous region of buffer space. */
585         data = tp->t_outq.c_cf;
586         cbp = (struct cblock *) ((intptr_t) tp->t_outq.c_cf & ~CROUND);
587         cnt = min((char *) (cbp+1) - tp->t_outq.c_cf, tp->t_outq.c_cc);
588
589         if (cnt == 0) {
590                 DPRINTF(("ucomstart: cnt == 0\n"));
591                 goto out;
592         }
593
594         SET(tp->t_state, TS_BUSY);
595
596         if (cnt > sc->sc_obufsize) {
597                 DPRINTF(("ucomstart: big buffer %d chars\n", cnt));
598                 cnt = sc->sc_obufsize;
599         }
600         if (sc->sc_callback->ucom_write != NULL)
601                 sc->sc_callback->ucom_write(sc->sc_parent, sc->sc_portno,
602                                             sc->sc_obuf, data, &cnt);
603         else
604                 memcpy(sc->sc_obuf, data, cnt);
605
606         DPRINTF(("ucomstart: %d chars\n", cnt));
607         usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe,
608                         (usbd_private_handle)sc, sc->sc_obuf, cnt,
609                         USBD_NO_COPY, USBD_NO_TIMEOUT, ucomwritecb);
610         /* What can we do on error? */
611         err = usbd_transfer(sc->sc_oxfer);
612         if (err != USBD_IN_PROGRESS)
613                 printf("ucomstart: err=%s\n", usbd_errstr(err));
614
615         ttwwakeup(tp);
616
617     out:
618         splx(s);
619 }
620
621 static void
622 ucomstop(struct tty *tp, int flag)
623 {
624         struct ucom_softc *sc;
625         int s;
626
627         sc = tp->t_sc;
628
629         DPRINTF(("ucomstop: %d\n", flag));
630
631         if ((flag & FREAD) && (sc->sc_state & UCS_RXSTOP) == 0) {
632                 DPRINTF(("ucomstop: read\n"));
633                 ucomstopread(sc);
634                 ucomstartread(sc);
635         }
636
637         if (flag & FWRITE) {
638                 DPRINTF(("ucomstop: write\n"));
639                 s = spltty();
640                 if (ISSET(tp->t_state, TS_BUSY)) {
641                         /* XXX do what? */
642                         if (!ISSET(tp->t_state, TS_TTSTOP))
643                                 SET(tp->t_state, TS_FLUSH);
644                 }
645                 splx(s);
646         }
647
648         ucomstart(tp);
649
650         DPRINTF(("ucomstop: done\n"));
651 }
652
653 static void
654 ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
655 {
656         struct ucom_softc *sc = (struct ucom_softc *)p;
657         struct tty *tp = sc->sc_tty;
658         u_int32_t cc;
659         int s;
660
661         DPRINTF(("ucomwritecb: status = %d\n", status));
662
663         if (status == USBD_CANCELLED || sc->sc_dying)
664                 goto error;
665
666         if (status != USBD_NORMAL_COMPLETION) {
667                 printf("%s: ucomwritecb: %s\n",
668                        device_get_nameunit(sc->sc_dev), usbd_errstr(status));
669                 if (status == USBD_STALLED)
670                         usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
671                 /* XXX we should restart after some delay. */
672                 goto error;
673         }
674
675         usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
676         DPRINTF(("ucomwritecb: cc = %d\n", cc));
677         if (cc <= sc->sc_opkthdrlen) {
678                 printf("%s: sent size too small, cc = %d\n",
679                        device_get_nameunit(sc->sc_dev), cc);
680                 goto error;
681         }
682
683         /* convert from USB bytes to tty bytes */
684         cc -= sc->sc_opkthdrlen;
685
686         s = spltty();
687         CLR(tp->t_state, TS_BUSY);
688         if (ISSET(tp->t_state, TS_FLUSH))
689                 CLR(tp->t_state, TS_FLUSH);
690         else
691                 ndflush(&tp->t_outq, cc);
692         ttyld_start(tp);
693         splx(s);
694
695         return;
696
697   error:
698         s = spltty();
699         CLR(tp->t_state, TS_BUSY);
700         splx(s);
701         return;
702 }
703
704 static usbd_status
705 ucomstartread(struct ucom_softc *sc)
706 {
707         usbd_status err;
708
709         DPRINTF(("ucomstartread: start\n"));
710
711         if (sc->sc_bulkin_pipe == NULL || (sc->sc_state & UCS_RXSTOP) == 0)
712                 return (USBD_NORMAL_COMPLETION);
713         sc->sc_state &= ~UCS_RXSTOP;
714
715         usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe,
716                         (usbd_private_handle)sc,
717                         sc->sc_ibuf, sc->sc_ibufsize,
718                         USBD_SHORT_XFER_OK | USBD_NO_COPY,
719                         USBD_NO_TIMEOUT, ucomreadcb);
720
721         err = usbd_transfer(sc->sc_ixfer);
722         if (err && err != USBD_IN_PROGRESS) {
723                 sc->sc_state |= UCS_RXSTOP;
724                 DPRINTF(("ucomstartread: err = %s\n", usbd_errstr(err)));
725                 return (err);
726         }
727
728         return (USBD_NORMAL_COMPLETION);
729 }
730
731 static void
732 ucomreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
733 {
734         struct ucom_softc *sc = (struct ucom_softc *)p;
735         struct tty *tp = sc->sc_tty;
736         usbd_status err;
737         u_int32_t cc;
738         u_char *cp;
739         int lostcc;
740         int s;
741
742         DPRINTF(("ucomreadcb: status = %d\n", status));
743
744         if (status != USBD_NORMAL_COMPLETION) {
745                 if (!(sc->sc_state & UCS_RXSTOP))
746                         printf("%s: ucomreadcb: %s\n",
747                                device_get_nameunit(sc->sc_dev), usbd_errstr(status));
748                 sc->sc_state |= UCS_RXSTOP;
749                 if (status == USBD_STALLED)
750                         usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
751                 /* XXX we should restart after some delay. */
752                 return;
753         }
754         sc->sc_state |= UCS_RXSTOP;
755
756         usbd_get_xfer_status(xfer, NULL, (void **)&cp, &cc, NULL);
757         DPRINTF(("ucomreadcb: got %d chars, tp = %p\n", cc, tp));
758         if (cc == 0)
759                 goto resubmit;
760
761         if (sc->sc_callback->ucom_read != NULL)
762                 sc->sc_callback->ucom_read(sc->sc_parent, sc->sc_portno,
763                                            &cp, &cc);
764
765         if (cc > sc->sc_ibufsize) {
766                 printf("%s: invalid receive data size, %d chars\n",
767                        device_get_nameunit(sc->sc_dev), cc);
768                 goto resubmit;
769         }
770         if (cc < 1)
771                 goto resubmit;
772
773         s = spltty();
774         if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
775                 if (tp->t_rawq.c_cc + cc > tp->t_ihiwat
776                     && (sc->sc_state & UCS_RTS_IFLOW
777                         || tp->t_iflag & IXOFF)
778                     && !(tp->t_state & TS_TBLOCK))
779                         ttyblock(tp);
780                 lostcc = b_to_q((char *)cp, cc, &tp->t_rawq);
781                 tp->t_rawcc += cc;
782                 ttwakeup(tp);
783                 if (tp->t_state & TS_TTSTOP
784                     && (tp->t_iflag & IXANY
785                         || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
786                         tp->t_state &= ~TS_TTSTOP;
787                         tp->t_lflag &= ~FLUSHO;
788                         ucomstart(tp);
789                 }
790                 if (lostcc > 0)
791                         printf("%s: lost %d chars\n", device_get_nameunit(sc->sc_dev),
792                                lostcc);
793         } else {
794                 /* Give characters to tty layer. */
795                 while (cc > 0) {
796                         DPRINTFN(7, ("ucomreadcb: char = 0x%02x\n", *cp));
797                         if (ttyld_rint(tp, *cp) == -1) {
798                                 /* XXX what should we do? */
799                                 printf("%s: lost %d chars\n",
800                                        device_get_nameunit(sc->sc_dev), cc);
801                                 break;
802                         }
803                         cc--;
804                         cp++;
805                 }
806         }
807         splx(s);
808
809     resubmit:
810         err = ucomstartread(sc);
811         if (err) {
812                 printf("%s: read start failed\n", device_get_nameunit(sc->sc_dev));
813                 /* XXX what should we dow now? */
814         }
815
816         if ((sc->sc_state & UCS_RTS_IFLOW) && !ISSET(sc->sc_mcr, SER_RTS)
817             && !(tp->t_state & TS_TBLOCK))
818                 ucommodem(tp, SER_RTS, 0);
819 }
820
821 static void
822 ucom_cleanup(struct ucom_softc *sc)
823 {
824         DPRINTF(("ucom_cleanup: closing pipes\n"));
825
826         ucom_shutdown(sc);
827         if (sc->sc_bulkin_pipe != NULL) {
828                 sc->sc_state |= UCS_RXSTOP;
829                 usbd_abort_pipe(sc->sc_bulkin_pipe);
830                 usbd_close_pipe(sc->sc_bulkin_pipe);
831                 sc->sc_bulkin_pipe = NULL;
832         }
833         if (sc->sc_bulkout_pipe != NULL) {
834                 usbd_abort_pipe(sc->sc_bulkout_pipe);
835                 usbd_close_pipe(sc->sc_bulkout_pipe);
836                 sc->sc_bulkout_pipe = NULL;
837         }
838         if (sc->sc_ixfer != NULL) {
839                 usbd_free_xfer(sc->sc_ixfer);
840                 sc->sc_ixfer = NULL;
841         }
842         if (sc->sc_oxfer != NULL) {
843                 usbd_free_xfer(sc->sc_oxfer);
844                 sc->sc_oxfer = NULL;
845         }
846 }
847
848 static void
849 ucomstopread(struct ucom_softc *sc)
850 {
851         usbd_status err;
852
853         DPRINTF(("ucomstopread: enter\n"));
854
855         if (!(sc->sc_state & UCS_RXSTOP)) {
856                 sc->sc_state |= UCS_RXSTOP;
857                 if (sc->sc_bulkin_pipe == NULL) {
858                         DPRINTF(("ucomstopread: bulkin pipe NULL\n"));
859                         return;
860                 }
861                 err = usbd_abort_pipe(sc->sc_bulkin_pipe);
862                 if (err) {
863                         DPRINTF(("ucomstopread: err = %s\n",
864                                  usbd_errstr(err)));
865                 }
866         }
867
868         DPRINTF(("ucomstopread: leave\n"));
869 }