xref: /openbsd-src/sys/dev/usb/ucom.c (revision 8500990981f885cbe5e6a4958549cacc238b5ae6)
1 /*	$OpenBSD: ucom.c,v 1.20 2003/10/03 16:44:51 miod Exp $ */
2 /*	$NetBSD: ucom.c,v 1.49 2003/01/01 00:10:25 thorpej Exp $	*/
3 
4 /*
5  * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Lennart Augustsson (lennart@augustsson.net) at
10  * Carlstedt Research & Technology.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *        This product includes software developed by the NetBSD
23  *        Foundation, Inc. and its contributors.
24  * 4. Neither the name of The NetBSD Foundation nor the names of its
25  *    contributors may be used to endorse or promote products derived
26  *    from this software without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
29  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
32  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38  * POSSIBILITY OF SUCH DAMAGE.
39  */
40 /*
41  * This code is very heavily based on the 16550 driver, com.c.
42  */
43 
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/ioctl.h>
48 #include <sys/conf.h>
49 #include <sys/tty.h>
50 #include <sys/file.h>
51 #include <sys/select.h>
52 #include <sys/proc.h>
53 #include <sys/vnode.h>
54 #include <sys/device.h>
55 #include <sys/poll.h>
56 #if defined(__NetBSD__)
57 #include "rnd.h"
58 #if NRND > 0
59 #include <sys/rnd.h>
60 #endif
61 #endif
62 
63 #include <dev/usb/usb.h>
64 
65 #include <dev/usb/usbdi.h>
66 #include <dev/usb/usbdi_util.h>
67 #include <dev/usb/usbdevs.h>
68 #include <dev/usb/usb_quirks.h>
69 
70 #include <dev/usb/ucomvar.h>
71 
72 #include "ucom.h"
73 
74 #if NUCOM > 0
75 
76 #ifdef UCOM_DEBUG
77 #define DPRINTFN(n, x)	if (ucomdebug > (n)) logprintf x
78 int ucomdebug = 0;
79 #else
80 #define DPRINTFN(n, x)
81 #endif
82 #define DPRINTF(x) DPRINTFN(0, x)
83 
84 #if defined(__NetBSD__)
85 #define	UCOMUNIT_MASK		0x3ffff
86 #define	UCOMDIALOUT_MASK	0x80000
87 #define	UCOMCALLUNIT_MASK	0x40000
88 
89 #define LINESW(tp, func)	((tp)->t_linesw->func)
90 #endif
91 
92 #if defined(__OpenBSD__)
93 #define	UCOMUNIT_MASK		0x3f
94 #define	UCOMDIALOUT_MASK	0x80
95 #define	UCOMCALLUNIT_MASK	0x40
96 
97 #define LINESW(tp, func)	(linesw[(tp)->t_line].func)
98 #endif
99 
100 #define	UCOMUNIT(x)		(minor(x) & UCOMUNIT_MASK)
101 #define	UCOMDIALOUT(x)		(minor(x) & UCOMDIALOUT_MASK)
102 #define	UCOMCALLUNIT(x)		(minor(x) & UCOMCALLUNIT_MASK)
103 
104 struct ucom_softc {
105 	USBBASEDEVICE		sc_dev;		/* base device */
106 
107 	usbd_device_handle	sc_udev;	/* USB device */
108 
109 	usbd_interface_handle	sc_iface;	/* data interface */
110 
111 	int			sc_bulkin_no;	/* bulk in endpoint address */
112 	usbd_pipe_handle	sc_bulkin_pipe;	/* bulk in pipe */
113 	usbd_xfer_handle	sc_ixfer;	/* read request */
114 	u_char			*sc_ibuf;	/* read buffer */
115 	u_int			sc_ibufsize;	/* read buffer size */
116 	u_int			sc_ibufsizepad;	/* read buffer size padded */
117 
118 	int			sc_bulkout_no;	/* bulk out endpoint address */
119 	usbd_pipe_handle	sc_bulkout_pipe;/* bulk out pipe */
120 	usbd_xfer_handle	sc_oxfer;	/* write request */
121 	u_char			*sc_obuf;	/* write buffer */
122 	u_int			sc_obufsize;	/* write buffer size */
123 	u_int			sc_opkthdrlen;	/* header length of
124 						 * output packet */
125 
126 	struct ucom_methods     *sc_methods;
127 	void                    *sc_parent;
128 	int			sc_portno;
129 
130 	struct tty		*sc_tty;	/* our tty */
131 	u_char			sc_lsr;
132 	u_char			sc_msr;
133 	u_char			sc_mcr;
134 	u_char			sc_tx_stopped;
135 	int			sc_swflags;
136 
137 	u_char			sc_opening;	/* lock during open */
138 	int			sc_refcnt;
139 	u_char			sc_dying;	/* disconnecting */
140 
141 #if defined(__NetBSD__) && NRND > 0
142 	rndsource_element_t	sc_rndsource;	/* random source */
143 #endif
144 };
145 
146 #if defined(__NetBSD__)
147 dev_type_open(ucomopen);
148 dev_type_close(ucomclose);
149 dev_type_read(ucomread);
150 dev_type_write(ucomwrite);
151 dev_type_ioctl(ucomioctl);
152 dev_type_stop(ucomstop);
153 dev_type_tty(ucomtty);
154 dev_type_poll(ucompoll);
155 
156 const struct cdevsw ucom_cdevsw = {
157 	ucomopen, ucomclose, ucomread, ucomwrite, ucomioctl,
158 	ucomstop, ucomtty, ucompoll, nommap, ttykqfilter, D_TTY
159 };
160 #endif
161 
162 Static void	ucom_cleanup(struct ucom_softc *);
163 Static void	ucom_hwiflow(struct ucom_softc *);
164 Static int	ucomparam(struct tty *, struct termios *);
165 Static void	ucomstart(struct tty *);
166 Static void	ucom_shutdown(struct ucom_softc *);
167 Static int	ucom_do_ioctl(struct ucom_softc *, u_long, caddr_t,
168 			      int, usb_proc_ptr);
169 Static void	ucom_dtr(struct ucom_softc *, int);
170 Static void	ucom_rts(struct ucom_softc *, int);
171 Static void	ucom_break(struct ucom_softc *, int);
172 Static usbd_status ucomstartread(struct ucom_softc *);
173 Static void	ucomreadcb(usbd_xfer_handle, usbd_private_handle, usbd_status);
174 Static void	ucomwritecb(usbd_xfer_handle, usbd_private_handle, usbd_status);
175 Static void	tiocm_to_ucom(struct ucom_softc *, u_long, int);
176 Static int	ucom_to_tiocm(struct ucom_softc *);
177 
178 USB_DECLARE_DRIVER(ucom);
179 
180 USB_MATCH(ucom)
181 {
182 	return (1);
183 }
184 
185 USB_ATTACH(ucom)
186 {
187 	struct ucom_softc *sc = (struct ucom_softc *)self;
188 	struct ucom_attach_args *uca = aux;
189 	struct tty *tp;
190 
191 	if (uca->portno != UCOM_UNK_PORTNO)
192 		printf(": portno %d", uca->portno);
193 	if (uca->info != NULL)
194 		printf(", %s", uca->info);
195 	printf("\n");
196 
197 	sc->sc_udev = uca->device;
198 	sc->sc_iface = uca->iface;
199 	sc->sc_bulkout_no = uca->bulkout;
200 	sc->sc_bulkin_no = uca->bulkin;
201 	sc->sc_ibufsize = uca->ibufsize;
202 	sc->sc_ibufsizepad = uca->ibufsizepad;
203 	sc->sc_obufsize = uca->obufsize;
204 	sc->sc_opkthdrlen = uca->opkthdrlen;
205 	sc->sc_methods = uca->methods;
206 	sc->sc_parent = uca->arg;
207 	sc->sc_portno = uca->portno;
208 
209 	tp = ttymalloc();
210 	tp->t_oproc = ucomstart;
211 	tp->t_param = ucomparam;
212 	sc->sc_tty = tp;
213 
214 #ifndef __OpenBSD__
215 	DPRINTF(("ucom_attach: tty_attach %p\n", tp));
216 	tty_attach(tp);
217 #endif
218 
219 #if defined(__NetBSD__) && NRND > 0
220 	rnd_attach_source(&sc->sc_rndsource, USBDEVNAME(sc->sc_dev),
221 			  RND_TYPE_TTY, 0);
222 #endif
223 
224 	USB_ATTACH_SUCCESS_RETURN;
225 }
226 
227 USB_DETACH(ucom)
228 {
229 	struct ucom_softc *sc = (struct ucom_softc *)self;
230 	struct tty *tp = sc->sc_tty;
231 	int maj, mn;
232 	int s;
233 
234 	DPRINTF(("ucom_detach: sc=%p flags=%d tp=%p, pipe=%d,%d\n",
235 		 sc, flags, tp, sc->sc_bulkin_no, sc->sc_bulkout_no));
236 
237 	sc->sc_dying = 1;
238 
239 	if (sc->sc_bulkin_pipe != NULL)
240 		usbd_abort_pipe(sc->sc_bulkin_pipe);
241 	if (sc->sc_bulkout_pipe != NULL)
242 		usbd_abort_pipe(sc->sc_bulkout_pipe);
243 
244 	s = splusb();
245 	if (--sc->sc_refcnt >= 0) {
246 		/* Wake up anyone waiting */
247 		if (tp != NULL) {
248 			CLR(tp->t_state, TS_CARR_ON);
249 			CLR(tp->t_cflag, CLOCAL | MDMBUF);
250 			ttyflush(tp, FREAD|FWRITE);
251 		}
252 		/* Wait for processes to go away. */
253 		usb_detach_wait(USBDEV(sc->sc_dev));
254 	}
255 	splx(s);
256 
257 #if defined(__NetBSD__)
258 	/* locate the major number */
259 	maj = cdevsw_lookup_major(&ucom_cdevsw);
260 #else
261 	/* locate the major number */
262 	for (maj = 0; maj < nchrdev; maj++)
263 		if (cdevsw[maj].d_open == ucomopen)
264 			break;
265 #endif
266 
267 	/* Nuke the vnodes for any open instances. */
268 	mn = self->dv_unit;
269 	DPRINTF(("ucom_detach: maj=%d mn=%d\n", maj, mn));
270 	vdevgone(maj, mn, mn, VCHR);
271 	vdevgone(maj, mn | UCOMDIALOUT_MASK, mn | UCOMDIALOUT_MASK, VCHR);
272 	vdevgone(maj, mn | UCOMCALLUNIT_MASK, mn | UCOMCALLUNIT_MASK, VCHR);
273 
274 	/* Detach and free the tty. */
275 	if (tp != NULL) {
276 #ifndef __OpenBSD__
277 		tty_detach(tp);
278 #endif
279 		ttyfree(tp);
280 		sc->sc_tty = NULL;
281 	}
282 
283 	/* Detach the random source */
284 #if defined(__NetBSD__) && NRND > 0
285 	rnd_detach_source(&sc->sc_rndsource);
286 #endif
287 
288 	return (0);
289 }
290 
291 int
292 ucom_activate(device_ptr_t self, enum devact act)
293 {
294 	struct ucom_softc *sc = (struct ucom_softc *)self;
295 
296 	DPRINTFN(5,("ucom_activate: %d\n", act));
297 
298 	switch (act) {
299 	case DVACT_ACTIVATE:
300 		return (EOPNOTSUPP);
301 
302 	case DVACT_DEACTIVATE:
303 		sc->sc_dying = 1;
304 		break;
305 	}
306 	return (0);
307 }
308 
309 void
310 ucom_shutdown(struct ucom_softc *sc)
311 {
312 	struct tty *tp = sc->sc_tty;
313 
314 	DPRINTF(("ucom_shutdown\n"));
315 	/*
316 	 * Hang up if necessary.  Wait a bit, so the other side has time to
317 	 * notice even if we immediately open the port again.
318 	 */
319 	if (ISSET(tp->t_cflag, HUPCL)) {
320 		ucom_dtr(sc, 0);
321 		(void)tsleep(sc, TTIPRI, ttclos, hz);
322 	}
323 }
324 
325 int
326 ucomopen(dev_t dev, int flag, int mode, usb_proc_ptr p)
327 {
328 	int unit = UCOMUNIT(dev);
329 	usbd_status err;
330 	struct ucom_softc *sc;
331 	struct tty *tp;
332 	int s;
333 	int error;
334 
335 	if (unit >= ucom_cd.cd_ndevs)
336 		return (ENXIO);
337 	sc = ucom_cd.cd_devs[unit];
338 	if (sc == NULL)
339 		return (ENXIO);
340 
341 	if (sc->sc_dying)
342 		return (EIO);
343 
344 	if (ISSET(sc->sc_dev.dv_flags, DVF_ACTIVE) == 0)
345 		return (ENXIO);
346 
347 	tp = sc->sc_tty;
348 
349 	DPRINTF(("ucomopen: unit=%d, tp=%p\n", unit, tp));
350 
351 	if (ISSET(tp->t_state, TS_ISOPEN) &&
352 	    ISSET(tp->t_state, TS_XCLUDE) &&
353 	    p->p_ucred->cr_uid != 0)
354 		return (EBUSY);
355 
356 	s = spltty();
357 
358 	/*
359 	 * Do the following iff this is a first open.
360 	 */
361 	while (sc->sc_opening)
362 		tsleep(&sc->sc_opening, PRIBIO, "ucomop", 0);
363 
364 	if (sc->sc_dying) {
365 		splx(s);
366 		return (EIO);
367 	}
368 	sc->sc_opening = 1;
369 
370 #if defined(__NetBSD__)
371 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
372 #else
373 	if (!ISSET(tp->t_state, TS_ISOPEN)) {
374 #endif
375 		struct termios t;
376 
377 		tp->t_dev = dev;
378 
379 		if (sc->sc_methods->ucom_open != NULL) {
380 			error = sc->sc_methods->ucom_open(sc->sc_parent,
381 							  sc->sc_portno);
382 			if (error) {
383 				ucom_cleanup(sc);
384 				sc->sc_opening = 0;
385 				wakeup(&sc->sc_opening);
386 				splx(s);
387 				return (error);
388 			}
389 		}
390 
391 		ucom_status_change(sc);
392 
393 		/*
394 		 * Initialize the termios status to the defaults.  Add in the
395 		 * sticky bits from TIOCSFLAGS.
396 		 */
397 		t.c_ispeed = 0;
398 		t.c_ospeed = TTYDEF_SPEED;
399 		t.c_cflag = TTYDEF_CFLAG;
400 		if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL))
401 			SET(t.c_cflag, CLOCAL);
402 		if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS))
403 			SET(t.c_cflag, CRTSCTS);
404 		if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF))
405 			SET(t.c_cflag, MDMBUF);
406 		/* Make sure ucomparam() will do something. */
407 		tp->t_ospeed = 0;
408 		(void) ucomparam(tp, &t);
409 		tp->t_iflag = TTYDEF_IFLAG;
410 		tp->t_oflag = TTYDEF_OFLAG;
411 		tp->t_lflag = TTYDEF_LFLAG;
412 		ttychars(tp);
413 		ttsetwater(tp);
414 
415 		/*
416 		 * Turn on DTR.  We must always do this, even if carrier is not
417 		 * present, because otherwise we'd have to use TIOCSDTR
418 		 * immediately after setting CLOCAL, which applications do not
419 		 * expect.  We always assert DTR while the device is open
420 		 * unless explicitly requested to deassert it.
421 		 */
422 		ucom_dtr(sc, 1);
423 
424 		/* XXX CLR(sc->sc_rx_flags, RX_ANY_BLOCK);*/
425 		ucom_hwiflow(sc);
426 
427 		DPRINTF(("ucomopen: open pipes in=%d out=%d\n",
428 			 sc->sc_bulkin_no, sc->sc_bulkout_no));
429 
430 		/* Open the bulk pipes */
431 		err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_no, 0,
432 				     &sc->sc_bulkin_pipe);
433 		if (err) {
434 			DPRINTF(("%s: open bulk out error (addr %d), err=%s\n",
435 				 USBDEVNAME(sc->sc_dev), sc->sc_bulkin_no,
436 				 usbd_errstr(err)));
437 			error = EIO;
438 			goto fail_0;
439 		}
440 		err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_no,
441 				     USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
442 		if (err) {
443 			DPRINTF(("%s: open bulk in error (addr %d), err=%s\n",
444 				 USBDEVNAME(sc->sc_dev), sc->sc_bulkout_no,
445 				 usbd_errstr(err)));
446 			error = EIO;
447 			goto fail_1;
448 		}
449 
450 		/* Allocate a request and an input buffer and start reading. */
451 		sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev);
452 		if (sc->sc_ixfer == NULL) {
453 			error = ENOMEM;
454 			goto fail_2;
455 		}
456 
457 		sc->sc_ibuf = usbd_alloc_buffer(sc->sc_ixfer,
458 						sc->sc_ibufsizepad);
459 		if (sc->sc_ibuf == NULL) {
460 			error = ENOMEM;
461 			goto fail_3;
462 		}
463 
464 		sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev);
465 		if (sc->sc_oxfer == NULL) {
466 			error = ENOMEM;
467 			goto fail_3;
468 		}
469 
470 		sc->sc_obuf = usbd_alloc_buffer(sc->sc_oxfer,
471 						sc->sc_obufsize +
472 						sc->sc_opkthdrlen);
473 		if (sc->sc_obuf == NULL) {
474 			error = ENOMEM;
475 			goto fail_4;
476 		}
477 
478 		ucomstartread(sc);
479 	}
480 	sc->sc_opening = 0;
481 	wakeup(&sc->sc_opening);
482 	splx(s);
483 
484 #if defined(__NetBSD__)
485 	error = ttyopen(tp, UCOMDIALOUT(dev), ISSET(flag, O_NONBLOCK));
486 #else
487 	error = ttyopen(UCOMDIALOUT(dev), tp);
488 #endif
489 	if (error)
490 		goto bad;
491 
492 	error = (*LINESW(tp, l_open))(dev, tp);
493 	if (error)
494 		goto bad;
495 
496 	return (0);
497 
498 fail_4:
499 	usbd_free_xfer(sc->sc_oxfer);
500 	sc->sc_oxfer = NULL;
501 fail_3:
502 	usbd_free_xfer(sc->sc_ixfer);
503 	sc->sc_ixfer = NULL;
504 fail_2:
505 	usbd_close_pipe(sc->sc_bulkout_pipe);
506 	sc->sc_bulkout_pipe = NULL;
507 fail_1:
508 	usbd_close_pipe(sc->sc_bulkin_pipe);
509 	sc->sc_bulkin_pipe = NULL;
510 fail_0:
511 	sc->sc_opening = 0;
512 	wakeup(&sc->sc_opening);
513 	splx(s);
514 	return (error);
515 
516 bad:
517 #if defined(__NetBSD__)
518 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
519 #else
520 	if (!ISSET(tp->t_state, TS_ISOPEN)) {
521 #endif
522 		/*
523 		 * We failed to open the device, and nobody else had it opened.
524 		 * Clean up the state as appropriate.
525 		 */
526 		ucom_cleanup(sc);
527 	}
528 
529 	return (error);
530 }
531 
532 int
533 ucomclose(dev_t dev, int flag, int mode, usb_proc_ptr p)
534 {
535 	struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
536 	struct tty *tp = sc->sc_tty;
537 
538 	DPRINTF(("ucomclose: unit=%d\n", UCOMUNIT(dev)));
539 	if (!ISSET(tp->t_state, TS_ISOPEN))
540 		return (0);
541 
542 	sc->sc_refcnt++;
543 
544 	(*LINESW(tp, l_close))(tp, flag);
545 	ttyclose(tp);
546 
547 #if defined(__NetBSD__)
548 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
549 #else
550 	if (!ISSET(tp->t_state, TS_ISOPEN)) {
551 #endif
552 		/*
553 		 * Although we got a last close, the device may still be in
554 		 * use; e.g. if this was the dialout node, and there are still
555 		 * processes waiting for carrier on the non-dialout node.
556 		 */
557 		ucom_cleanup(sc);
558 	}
559 
560 	if (sc->sc_methods->ucom_close != NULL)
561 		sc->sc_methods->ucom_close(sc->sc_parent, sc->sc_portno);
562 
563 	if (--sc->sc_refcnt < 0)
564 		usb_detach_wakeup(USBDEV(sc->sc_dev));
565 
566 	return (0);
567 }
568 
569 int
570 ucomread(dev_t dev, struct uio *uio, int flag)
571 {
572 	struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
573 	struct tty *tp = sc->sc_tty;
574 	int error;
575 
576 	if (sc->sc_dying)
577 		return (EIO);
578 
579 	sc->sc_refcnt++;
580 	error = (*LINESW(tp, l_read))(tp, uio, flag);
581 	if (--sc->sc_refcnt < 0)
582 		usb_detach_wakeup(USBDEV(sc->sc_dev));
583 	return (error);
584 }
585 
586 int
587 ucomwrite(dev_t dev, struct uio *uio, int flag)
588 {
589 	struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
590 	struct tty *tp = sc->sc_tty;
591 	int error;
592 
593 	if (sc->sc_dying)
594 		return (EIO);
595 
596 	sc->sc_refcnt++;
597 	error = (*LINESW(tp, l_write))(tp, uio, flag);
598 	if (--sc->sc_refcnt < 0)
599 		usb_detach_wakeup(USBDEV(sc->sc_dev));
600 	return (error);
601 }
602 
603 #if defined(__NetBSD__)
604 int
605 ucompoll(dev_t dev, int events, usb_proc_ptr p)
606 {
607 	struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
608 	struct tty *tp = sc->sc_tty;
609 	int error;
610 
611 	if (sc->sc_dying)
612 		return (EIO);
613 
614 	sc->sc_refcnt++;
615 	error = (*LINESW(tp, l_poll))(tp, events, p);
616 	if (--sc->sc_refcnt < 0)
617 		usb_detach_wakeup(USBDEV(sc->sc_dev));
618 	return (error);
619 }
620 #endif
621 
622 struct tty *
623 ucomtty(dev_t dev)
624 {
625 	struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
626 	struct tty *tp = sc->sc_tty;
627 
628 	return (tp);
629 }
630 
631 int
632 ucomioctl(dev_t dev, u_long cmd, caddr_t data, int flag, usb_proc_ptr p)
633 {
634 	struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
635 	int error;
636 
637 	sc->sc_refcnt++;
638 	error = ucom_do_ioctl(sc, cmd, data, flag, p);
639 	if (--sc->sc_refcnt < 0)
640 		usb_detach_wakeup(USBDEV(sc->sc_dev));
641 	return (error);
642 }
643 
644 Static int
645 ucom_do_ioctl(struct ucom_softc *sc, u_long cmd, caddr_t data,
646 	      int flag, usb_proc_ptr p)
647 {
648 	struct tty *tp = sc->sc_tty;
649 	int error;
650 	int s;
651 
652 	if (sc->sc_dying)
653 		return (EIO);
654 
655 	DPRINTF(("ucomioctl: cmd=0x%08lx\n", cmd));
656 
657 	error = (*LINESW(tp, l_ioctl))(tp, cmd, data, flag, p);
658 	if (error >= 0)
659 		return (error);
660 
661 	error = ttioctl(tp, cmd, data, flag, p);
662 	if (error >= 0)
663 		return (error);
664 
665 	if (sc->sc_methods->ucom_ioctl != NULL) {
666 		error = sc->sc_methods->ucom_ioctl(sc->sc_parent,
667 			    sc->sc_portno, cmd, data, flag, p);
668 		if (error >= 0)
669 			return (error);
670 	}
671 
672 	error = 0;
673 
674 	DPRINTF(("ucomioctl: our cmd=0x%08lx\n", cmd));
675 	s = spltty();
676 
677 	switch (cmd) {
678 	case TIOCSBRK:
679 		ucom_break(sc, 1);
680 		break;
681 
682 	case TIOCCBRK:
683 		ucom_break(sc, 0);
684 		break;
685 
686 	case TIOCSDTR:
687 		ucom_dtr(sc, 1);
688 		break;
689 
690 	case TIOCCDTR:
691 		ucom_dtr(sc, 0);
692 		break;
693 
694 	case TIOCGFLAGS:
695 		*(int *)data = sc->sc_swflags;
696 		break;
697 
698 	case TIOCSFLAGS:
699 		error = suser(p, 0);
700 		if (error)
701 			break;
702 		sc->sc_swflags = *(int *)data;
703 		break;
704 
705 	case TIOCMSET:
706 	case TIOCMBIS:
707 	case TIOCMBIC:
708 		tiocm_to_ucom(sc, cmd, *(int *)data);
709 		break;
710 
711 	case TIOCMGET:
712 		*(int *)data = ucom_to_tiocm(sc);
713 		break;
714 
715 	default:
716 		error = ENOTTY;
717 		break;
718 	}
719 
720 	splx(s);
721 
722 	return (error);
723 }
724 
725 Static void
726 tiocm_to_ucom(struct ucom_softc *sc, u_long how, int ttybits)
727 {
728 	u_char combits;
729 
730 	combits = 0;
731 	if (ISSET(ttybits, TIOCM_DTR))
732 		SET(combits, UMCR_DTR);
733 	if (ISSET(ttybits, TIOCM_RTS))
734 		SET(combits, UMCR_RTS);
735 
736 	switch (how) {
737 	case TIOCMBIC:
738 		CLR(sc->sc_mcr, combits);
739 		break;
740 
741 	case TIOCMBIS:
742 		SET(sc->sc_mcr, combits);
743 		break;
744 
745 	case TIOCMSET:
746 		CLR(sc->sc_mcr, UMCR_DTR | UMCR_RTS);
747 		SET(sc->sc_mcr, combits);
748 		break;
749 	}
750 
751 	if (how == TIOCMSET || ISSET(combits, UMCR_DTR))
752 		ucom_dtr(sc, (sc->sc_mcr & UMCR_DTR) != 0);
753 	if (how == TIOCMSET || ISSET(combits, UMCR_RTS))
754 		ucom_rts(sc, (sc->sc_mcr & UMCR_RTS) != 0);
755 }
756 
757 Static int
758 ucom_to_tiocm(struct ucom_softc *sc)
759 {
760 	u_char combits;
761 	int ttybits = 0;
762 
763 	combits = sc->sc_mcr;
764 	if (ISSET(combits, UMCR_DTR))
765 		SET(ttybits, TIOCM_DTR);
766 	if (ISSET(combits, UMCR_RTS))
767 		SET(ttybits, TIOCM_RTS);
768 
769 	combits = sc->sc_msr;
770 	if (ISSET(combits, UMSR_DCD))
771 		SET(ttybits, TIOCM_CD);
772 	if (ISSET(combits, UMSR_CTS))
773 		SET(ttybits, TIOCM_CTS);
774 	if (ISSET(combits, UMSR_DSR))
775 		SET(ttybits, TIOCM_DSR);
776 	if (ISSET(combits, UMSR_RI | UMSR_TERI))
777 		SET(ttybits, TIOCM_RI);
778 
779 #if 0
780 XXX;
781 	if (sc->sc_ier != 0)
782 		SET(ttybits, TIOCM_LE);
783 #endif
784 
785 	return (ttybits);
786 }
787 
788 Static void
789 ucom_break(sc, onoff)
790 	struct ucom_softc *sc;
791 	int onoff;
792 {
793 	DPRINTF(("ucom_break: onoff=%d\n", onoff));
794 
795 	if (sc->sc_methods->ucom_set != NULL)
796 		sc->sc_methods->ucom_set(sc->sc_parent, sc->sc_portno,
797 		    UCOM_SET_BREAK, onoff);
798 }
799 
800 Static void
801 ucom_dtr(struct ucom_softc *sc, int onoff)
802 {
803 	DPRINTF(("ucom_dtr: onoff=%d\n", onoff));
804 
805 	if (sc->sc_methods->ucom_set != NULL)
806 		sc->sc_methods->ucom_set(sc->sc_parent, sc->sc_portno,
807 		    UCOM_SET_DTR, onoff);
808 }
809 
810 Static void
811 ucom_rts(struct ucom_softc *sc, int onoff)
812 {
813 	DPRINTF(("ucom_rts: onoff=%d\n", onoff));
814 
815 	if (sc->sc_methods->ucom_set != NULL)
816 		sc->sc_methods->ucom_set(sc->sc_parent, sc->sc_portno,
817 		    UCOM_SET_RTS, onoff);
818 }
819 
820 void
821 ucom_status_change(struct ucom_softc *sc)
822 {
823 	struct tty *tp = sc->sc_tty;
824 	u_char old_msr;
825 
826 	if (sc->sc_methods->ucom_get_status != NULL) {
827 		old_msr = sc->sc_msr;
828 		sc->sc_methods->ucom_get_status(sc->sc_parent, sc->sc_portno,
829 		    &sc->sc_lsr, &sc->sc_msr);
830 		if (ISSET((sc->sc_msr ^ old_msr), UMSR_DCD))
831 			(*LINESW(tp, l_modem))(tp,
832 			    ISSET(sc->sc_msr, UMSR_DCD));
833 	} else {
834 		sc->sc_lsr = 0;
835 		sc->sc_msr = 0;
836 	}
837 }
838 
839 Static int
840 ucomparam(struct tty *tp, struct termios *t)
841 {
842 	struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(tp->t_dev)];
843 	int error;
844 
845 	if (sc->sc_dying)
846 		return (EIO);
847 
848 	/* Check requested parameters. */
849 	if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
850 		return (EINVAL);
851 
852 	/*
853 	 * For the console, always force CLOCAL and !HUPCL, so that the port
854 	 * is always active.
855 	 */
856 	if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR)) {
857 		SET(t->c_cflag, CLOCAL);
858 		CLR(t->c_cflag, HUPCL);
859 	}
860 
861 	/*
862 	 * If there were no changes, don't do anything.  This avoids dropping
863 	 * input and improves performance when all we did was frob things like
864 	 * VMIN and VTIME.
865 	 */
866 	if (tp->t_ospeed == t->c_ospeed &&
867 	    tp->t_cflag == t->c_cflag)
868 		return (0);
869 
870 	/* XXX lcr = ISSET(sc->sc_lcr, LCR_SBREAK) | cflag2lcr(t->c_cflag); */
871 
872 	/* And copy to tty. */
873 	tp->t_ispeed = 0;
874 	tp->t_ospeed = t->c_ospeed;
875 	tp->t_cflag = t->c_cflag;
876 
877 	if (sc->sc_methods->ucom_param != NULL) {
878 		error = sc->sc_methods->ucom_param(sc->sc_parent, sc->sc_portno,
879 			    t);
880 		if (error)
881 			return (error);
882 	}
883 
884 	/* XXX worry about CHWFLOW */
885 
886 	/*
887 	 * Update the tty layer's idea of the carrier bit, in case we changed
888 	 * CLOCAL or MDMBUF.  We don't hang up here; we only do that by
889 	 * explicit request.
890 	 */
891 	DPRINTF(("ucomparam: l_modem\n"));
892 	(void) (*LINESW(tp, l_modem))(tp, 1 /* XXX carrier */ );
893 
894 #if 0
895 XXX what if the hardware is not open
896 	if (!ISSET(t->c_cflag, CHWFLOW)) {
897 		if (sc->sc_tx_stopped) {
898 			sc->sc_tx_stopped = 0;
899 			ucomstart(tp);
900 		}
901 	}
902 #endif
903 
904 	return (0);
905 }
906 
907 /*
908  * (un)block input via hw flowcontrol
909  */
910 Static void
911 ucom_hwiflow(struct ucom_softc *sc)
912 {
913 	DPRINTF(("ucom_hwiflow:\n"));
914 #if 0
915 XXX
916 	bus_space_tag_t iot = sc->sc_iot;
917 	bus_space_handle_t ioh = sc->sc_ioh;
918 
919 	if (sc->sc_mcr_rts == 0)
920 		return;
921 
922 	if (ISSET(sc->sc_rx_flags, RX_ANY_BLOCK)) {
923 		CLR(sc->sc_mcr, sc->sc_mcr_rts);
924 		CLR(sc->sc_mcr_active, sc->sc_mcr_rts);
925 	} else {
926 		SET(sc->sc_mcr, sc->sc_mcr_rts);
927 		SET(sc->sc_mcr_active, sc->sc_mcr_rts);
928 	}
929 	bus_space_write_1(iot, ioh, com_mcr, sc->sc_mcr_active);
930 #endif
931 }
932 
933 Static void
934 ucomstart(struct tty *tp)
935 {
936 	struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(tp->t_dev)];
937 	usbd_status err;
938 	int s;
939 	u_char *data;
940 	int cnt;
941 
942 	if (sc->sc_dying)
943 		return;
944 
945 	s = spltty();
946 	if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) {
947 		DPRINTFN(4,("ucomstart: no go, state=0x%x\n", tp->t_state));
948 		goto out;
949 	}
950 	if (sc->sc_tx_stopped)
951 		goto out;
952 
953 	if (tp->t_outq.c_cc <= tp->t_lowat) {
954 		if (ISSET(tp->t_state, TS_ASLEEP)) {
955 			CLR(tp->t_state, TS_ASLEEP);
956 			wakeup(&tp->t_outq);
957 		}
958 		selwakeup(&tp->t_wsel);
959 		if (tp->t_outq.c_cc == 0)
960 			goto out;
961 	}
962 
963 	/* Grab the first contiguous region of buffer space. */
964 	data = tp->t_outq.c_cf;
965 	cnt = ndqb(&tp->t_outq, 0);
966 
967 	if (cnt == 0) {
968 		DPRINTF(("ucomstart: cnt==0\n"));
969 		goto out;
970 	}
971 
972 	SET(tp->t_state, TS_BUSY);
973 
974 	if (cnt > sc->sc_obufsize) {
975 		DPRINTF(("ucomstart: big buffer %d chars\n", cnt));
976 		cnt = sc->sc_obufsize;
977 	}
978 	if (sc->sc_methods->ucom_write != NULL)
979 		sc->sc_methods->ucom_write(sc->sc_parent, sc->sc_portno,
980 					   sc->sc_obuf, data, &cnt);
981 	else
982 		memcpy(sc->sc_obuf, data, cnt);
983 
984 	DPRINTFN(4,("ucomstart: %d chars\n", cnt));
985 	usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe,
986 			(usbd_private_handle)sc, sc->sc_obuf, cnt,
987 			USBD_NO_COPY, USBD_NO_TIMEOUT, ucomwritecb);
988 	/* What can we do on error? */
989 	err = usbd_transfer(sc->sc_oxfer);
990 #ifdef DIAGNOSTIC
991 	if (err != USBD_IN_PROGRESS)
992 		printf("ucomstart: err=%s\n", usbd_errstr(err));
993 #endif
994 
995 out:
996 	splx(s);
997 }
998 
999 #if defined(__NetBSD__)
1000 void
1001 #else
1002 int
1003 #endif
1004 ucomstop(struct tty *tp, int flag)
1005 {
1006 	DPRINTF(("ucomstop: flag=%d\n", flag));
1007 #if 0
1008 	/*struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(tp->t_dev)];*/
1009 	int s;
1010 
1011 	s = spltty();
1012 	if (ISSET(tp->t_state, TS_BUSY)) {
1013 		DPRINTF(("ucomstop: XXX\n"));
1014 		/* sc->sc_tx_stopped = 1; */
1015 		if (!ISSET(tp->t_state, TS_TTSTOP))
1016 			SET(tp->t_state, TS_FLUSH);
1017 	}
1018 	splx(s);
1019 #endif
1020 #if !defined(__NetBSD__)
1021 	return (0);
1022 #endif
1023 }
1024 
1025 Static void
1026 ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
1027 {
1028 	struct ucom_softc *sc = (struct ucom_softc *)p;
1029 	struct tty *tp = sc->sc_tty;
1030 	u_int32_t cc;
1031 	int s;
1032 
1033 	DPRINTFN(5,("ucomwritecb: status=%d\n", status));
1034 
1035 	if (status == USBD_CANCELLED || sc->sc_dying)
1036 		goto error;
1037 
1038 	if (status) {
1039 		DPRINTF(("ucomwritecb: status=%d\n", status));
1040 		usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
1041 		/* XXX we should restart after some delay. */
1042 		goto error;
1043 	}
1044 
1045 	usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
1046 #if defined(__NetBSD__) && NRND > 0
1047 	rnd_add_uint32(&sc->sc_rndsource, cc);
1048 #endif
1049 	DPRINTFN(5,("ucomwritecb: cc=%d\n", cc));
1050 	/* convert from USB bytes to tty bytes */
1051 	cc -= sc->sc_opkthdrlen;
1052 
1053 	s = spltty();
1054 	CLR(tp->t_state, TS_BUSY);
1055 	if (ISSET(tp->t_state, TS_FLUSH))
1056 		CLR(tp->t_state, TS_FLUSH);
1057 	else
1058 		ndflush(&tp->t_outq, cc);
1059 	(*LINESW(tp, l_start))(tp);
1060 	splx(s);
1061 	return;
1062 
1063 error:
1064 	s = spltty();
1065 	CLR(tp->t_state, TS_BUSY);
1066 	splx(s);
1067 }
1068 
1069 Static usbd_status
1070 ucomstartread(struct ucom_softc *sc)
1071 {
1072 	usbd_status err;
1073 
1074 	DPRINTFN(5,("ucomstartread: start\n"));
1075 	usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe,
1076 			(usbd_private_handle)sc,
1077 			sc->sc_ibuf, sc->sc_ibufsize,
1078 			USBD_SHORT_XFER_OK | USBD_NO_COPY,
1079 			USBD_NO_TIMEOUT, ucomreadcb);
1080 	err = usbd_transfer(sc->sc_ixfer);
1081 	if (err != USBD_IN_PROGRESS) {
1082 		DPRINTF(("ucomstartread: err=%s\n", usbd_errstr(err)));
1083 		return (err);
1084 	}
1085 	return (USBD_NORMAL_COMPLETION);
1086 }
1087 
1088 Static void
1089 ucomreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
1090 {
1091 	struct ucom_softc *sc = (struct ucom_softc *)p;
1092 	struct tty *tp = sc->sc_tty;
1093 	int (*rint)(int c, struct tty *tp) = LINESW(tp, l_rint);
1094 	usbd_status err;
1095 	u_int32_t cc;
1096 	u_char *cp;
1097 	int s;
1098 
1099 	DPRINTFN(5,("ucomreadcb: status=%d\n", status));
1100 
1101 	if (status == USBD_CANCELLED || status == USBD_IOERROR ||
1102 	    sc->sc_dying) {
1103 		DPRINTF(("ucomreadcb: dying\n"));
1104 		/* Send something to wake upper layer */
1105 		s = spltty();
1106 		(*rint)('\n', tp);
1107 		ttwakeup(tp);
1108 		splx(s);
1109 		return;
1110 	}
1111 
1112 	if (status) {
1113 		usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
1114 		/* XXX we should restart after some delay. */
1115 		return;
1116 	}
1117 
1118 	usbd_get_xfer_status(xfer, NULL, (void *)&cp, &cc, NULL);
1119 #if defined(__NetBSD__) && NRND > 0
1120 	rnd_add_uint32(&sc->sc_rndsource, cc);
1121 #endif
1122 	DPRINTFN(5,("ucomreadcb: got %d chars, tp=%p\n", cc, tp));
1123 	if (sc->sc_methods->ucom_read != NULL)
1124 		sc->sc_methods->ucom_read(sc->sc_parent, sc->sc_portno,
1125 					  &cp, &cc);
1126 
1127 	s = spltty();
1128 	/* Give characters to tty layer. */
1129 	while (cc-- > 0) {
1130 		DPRINTFN(7,("ucomreadcb: char=0x%02x\n", *cp));
1131 		if ((*rint)(*cp++, tp) == -1) {
1132 			/* XXX what should we do? */
1133 			printf("%s: lost %d chars\n", USBDEVNAME(sc->sc_dev),
1134 			       cc);
1135 			break;
1136 		}
1137 	}
1138 	splx(s);
1139 
1140 	err = ucomstartread(sc);
1141 	if (err) {
1142 		printf("%s: read start failed\n", USBDEVNAME(sc->sc_dev));
1143 		/* XXX what should we dow now? */
1144 	}
1145 }
1146 
1147 Static void
1148 ucom_cleanup(struct ucom_softc *sc)
1149 {
1150 	DPRINTF(("ucom_cleanup: closing pipes\n"));
1151 
1152 	ucom_shutdown(sc);
1153 	if (sc->sc_bulkin_pipe != NULL) {
1154 		usbd_abort_pipe(sc->sc_bulkin_pipe);
1155 		usbd_close_pipe(sc->sc_bulkin_pipe);
1156 		sc->sc_bulkin_pipe = NULL;
1157 	}
1158 	if (sc->sc_bulkout_pipe != NULL) {
1159 		usbd_abort_pipe(sc->sc_bulkout_pipe);
1160 		usbd_close_pipe(sc->sc_bulkout_pipe);
1161 		sc->sc_bulkout_pipe = NULL;
1162 	}
1163 	if (sc->sc_ixfer != NULL) {
1164 		usbd_free_xfer(sc->sc_ixfer);
1165 		sc->sc_ixfer = NULL;
1166 	}
1167 	if (sc->sc_oxfer != NULL) {
1168 		usbd_free_xfer(sc->sc_oxfer);
1169 		sc->sc_oxfer = NULL;
1170 	}
1171 }
1172 
1173 #endif /* NUCOM > 0 */
1174 
1175 int
1176 ucomprint(void *aux, const char *pnp)
1177 {
1178 	struct ucom_attach_args *uca = aux;
1179 
1180 	if (pnp)
1181 		printf("ucom at %s", pnp);
1182 	if (uca->portno != UCOM_UNK_PORTNO)
1183 		printf(" portno %d", uca->portno);
1184 	return (UNCONF);
1185 }
1186 
1187 int
1188 #if defined(__OpenBSD__)
1189 ucomsubmatch(struct device *parent, void *match, void *aux)
1190 #else
1191 ucomsubmatch(struct device *parent, struct cfdata *cf, void *aux)
1192 #endif
1193 {
1194         struct ucom_attach_args *uca = aux;
1195 #if defined(__OpenBSD__)
1196         struct cfdata *cf = match;
1197 #endif
1198 
1199 	if (uca->portno != UCOM_UNK_PORTNO &&
1200 	    cf->ucomcf_portno != UCOM_UNK_PORTNO &&
1201 	    cf->ucomcf_portno != uca->portno)
1202 		return (0);
1203 	return ((*cf->cf_attach->ca_match)(parent, cf, aux));
1204 }
1205