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