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