xref: /netbsd-src/sys/arch/epoc32/windermere/wmcom.c (revision 8ecbf5f02b752fcb7debe1a8fab1dc82602bc760)
1 /*      $NetBSD: wmcom.c,v 1.8 2019/11/10 21:16:25 chs Exp $      */
2 /*
3  * Copyright (c) 2012 KIYOHARA Takashi
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
19  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  */
27 #include <sys/cdefs.h>
28 __KERNEL_RCSID(0, "$NetBSD: wmcom.c,v 1.8 2019/11/10 21:16:25 chs Exp $");
29 
30 #include "rnd.h"
31 
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/conf.h>
35 #include <sys/device.h>
36 #include <sys/errno.h>
37 #include <sys/fcntl.h>
38 #include <sys/intr.h>
39 #include <sys/kauth.h>
40 #include <sys/lwp.h>
41 #include <sys/malloc.h>
42 #include <sys/systm.h>
43 #include <sys/termios.h>
44 #include <sys/tty.h>
45 #include <sys/types.h>
46 
47 #include <epoc32/windermere/windermerereg.h>
48 #include <epoc32/windermere/windermerevar.h>
49 
50 #include <dev/cons.h>
51 
52 #ifdef RND_COM
53 #include <sys/rndsource.h>
54 #endif
55 
56 #include "ioconf.h"
57 #include "locators.h"
58 
59 #define COMUNIT(x)	TTUNIT(x)
60 #define COMDIALOUT(x)	TTDIALOUT(x)
61 
62 #define WMCOM_RING_SIZE	2048
63 
64 struct wmcom_softc {
65 	device_t sc_dev;
66 	bus_space_tag_t sc_iot;
67 	bus_space_handle_t sc_ioh;
68 
69 	void *sc_si;
70 
71 	struct tty *sc_tty;
72 
73 	u_char *sc_tba;
74 	u_int sc_tbc;
75 	u_char *sc_rbuf;
76 	char *volatile sc_rbget;
77 	char *volatile sc_rbput;
78 	volatile int sc_rbavail;
79 
80 	int sc_tx_done;
81 	int sc_rx_ready;
82 
83 	int sc_hwflags;
84 #define COM_HW_CONSOLE	(1 << 0)
85 #define COM_HW_DEV_OK	(1 << 1)
86 #define COM_HW_KGDB	(1 << 2)
87 	int sc_swflags;
88 
89 	int sc_flags;
90 #define WMCOM_IRDA	(1 << 0)
91 
92 #ifdef RND_COM
93 	krandsource_t rnd_source;
94 #endif
95 };
96 
97 static int wmcom_match(device_t, cfdata_t, void *);
98 static void wmcom_attach(device_t, device_t, void *);
99 
100 static int wmcom_intr(void *);
101 static void wmcom_soft(void *);
102 
103 static void wmcom_start(struct tty *);
104 static int wmcom_param(struct tty *, struct termios *);
105 static int wmcom_hwiflow(struct tty *, int);
106 
107 dev_type_open(wmcomopen);
108 dev_type_close(wmcomclose);
109 dev_type_read(wmcomread);
110 dev_type_write(wmcomwrite);
111 dev_type_ioctl(wmcomioctl);
112 dev_type_stop(wmcomstop);
113 dev_type_tty(wmcomtty);
114 dev_type_poll(wmcompoll);
115 
116 static void wmcom_iflush(struct wmcom_softc *);
117 static void wmcom_shutdown(struct wmcom_softc *);
118 static void wmcom_break(struct wmcom_softc *, int);
119 
120 static void wmcom_rxsoft(struct wmcom_softc *, struct tty *);
121 
122 static inline uint32_t wmcom_rate2lcr(int);
123 static uint8_t wmcom_cflag2fcr(tcflag_t);
124 
125 static int wmcom_cngetc(dev_t);
126 static void wmcom_cnputc(dev_t, int);
127 static void wmcom_cnpollc(dev_t, int);
128 
129 CFATTACH_DECL_NEW(wmcom, sizeof(struct wmcom_softc),
130     wmcom_match, wmcom_attach, NULL, NULL);
131 
132 const struct cdevsw wmcom_cdevsw = {
133 	.d_open = wmcomopen,
134 	.d_close = wmcomclose,
135 	.d_read = wmcomread,
136 	.d_write = wmcomwrite,
137 	.d_ioctl = wmcomioctl,
138 	.d_stop = wmcomstop,
139 	.d_tty = wmcomtty,
140 	.d_poll = wmcompoll,
141 	.d_mmap = nommap,
142 	.d_kqfilter = ttykqfilter,
143 	.d_discard = nodiscard,
144 	.d_flag = D_TTY
145 };
146 
147 static struct cnm_state wmcom_cnm_state;
148 static vaddr_t wmcom_cnaddr;
149 static int wmcom_cnrate;
150 static tcflag_t wmcom_cncflag;
151 
152 
153 /* ARGSUSED */
154 static int
155 wmcom_match(device_t parent, cfdata_t match, void *aux)
156 {
157 	struct windermere_attach_args *aa = aux;
158 
159 	/* Wildcard not accept */
160 	if (aa->aa_offset == WINDERMERECF_OFFSET_DEFAULT ||
161 	    aa->aa_irq == WINDERMERECF_IRQ_DEFAULT)
162 		return 0;
163 
164 	aa->aa_size = UART_SIZE;
165 	return 1;
166 }
167 
168 /* ARGSUSED */
169 static void
170 wmcom_attach(device_t parent, device_t self, void *aux)
171 {
172 	struct wmcom_softc *sc = device_private(self);
173 	struct windermere_attach_args *aa = aux;
174 
175 	aprint_naive("\n");
176 	aprint_normal("\n");
177 
178 	sc->sc_dev = self;
179 	if (windermere_bus_space_subregion(aa->aa_iot, *aa->aa_ioh,
180 				aa->aa_offset, aa->aa_size, &sc->sc_ioh) != 0) {
181 		aprint_error_dev(self, "can't map registers\n");
182 		return;
183 	}
184 	sc->sc_iot = aa->aa_iot;
185 	if (intr_establish(aa->aa_irq, IPL_SERIAL, 0, wmcom_intr, sc) == NULL) {
186 		aprint_error_dev(self, "can't establish interrupt\n");
187 		return;
188 	}
189 
190 	if (aa->aa_offset == (wmcom_cnaddr & 0xfff))
191 		SET(sc->sc_hwflags, COM_HW_CONSOLE);
192 
193 	if (aa->aa_offset == WINDERMERE_COM0_OFFSET)
194 		SET(sc->sc_flags, WMCOM_IRDA);
195 
196 	sc->sc_tty = tty_alloc();
197 	sc->sc_tty->t_oproc = wmcom_start;
198 	sc->sc_tty->t_param = wmcom_param;
199 	sc->sc_tty->t_hwiflow = wmcom_hwiflow;
200 
201 	sc->sc_tbc = 0;
202 	sc->sc_rbuf = malloc(WMCOM_RING_SIZE << 1, M_DEVBUF, M_WAITOK);
203 	sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
204 	sc->sc_rbavail = WMCOM_RING_SIZE;
205 
206 	tty_attach(sc->sc_tty);
207 
208 	if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
209 		int maj = cdevsw_lookup_major(&wmcom_cdevsw);
210 
211 		sc->sc_tty->t_dev = makedev(maj, device_unit(sc->sc_dev));
212 		cn_tab->cn_dev = sc->sc_tty->t_dev;
213 
214 		aprint_normal_dev(self, "console\n");
215 	}
216 
217 	sc->sc_si = softint_establish(SOFTINT_SERIAL, wmcom_soft, sc);
218 
219 #ifdef RND_COM
220 	rnd_attach_source(&sc->rnd_source, device_xname(sc->sc_dev),
221 	    RND_TYPE_TTY, RND_FLAG_DEFAULT);
222 #endif
223 
224 	SET(sc->sc_hwflags, COM_HW_DEV_OK);
225 }
226 
227 static int
228 wmcom_intr(void *arg)
229 {
230 	struct wmcom_softc *sc = arg;
231 	bus_space_tag_t iot = sc->sc_iot;
232 	bus_space_handle_t ioh = sc->sc_ioh;
233 	int cc;
234 	uint32_t data;
235 	uint8_t fr, intm;
236 	u_char *put;
237 
238 	if (!device_is_active(sc->sc_dev))
239 		return 0;
240 
241 	fr = bus_space_read_1(iot, ioh, UARTFR);
242 	intm = bus_space_read_1(iot, ioh, UARTINTM);
243 	if (bus_space_read_1(iot, ioh, UARTINT) & INT_RXINT) {
244 		put = sc->sc_rbput;
245 		cc = sc->sc_rbavail;
246 		while (cc > 0) {
247 			if (ISSET(fr, FR_RXFE))
248 				break;
249 			data = bus_space_read_4(iot, ioh, UARTDR);
250 			cn_check_magic(sc->sc_tty->t_dev, data & 0xff,
251 			    wmcom_cnm_state);
252 
253 			put[0] = data & 0xff;
254 			put[1] = (data >> 8) & 0xff;
255 			put += 2;
256 			if (put >= sc->sc_rbuf + (WMCOM_RING_SIZE << 1))
257 				put = sc->sc_rbuf;
258 			cc--;
259 			sc->sc_rx_ready = 1;
260 
261 			fr = bus_space_read_1(iot, ioh, UARTFR);
262 		}
263 
264 		/*
265 		 * Current string of incoming characters ended because
266 		 * no more data was available or we ran out of space.
267 		 * Schedule a receive event if any data was received.
268 		 * If we're out of space, turn off receive interrupts.
269 		 */
270 		sc->sc_rbput = put;
271 		sc->sc_rbavail = cc;
272 
273 		/*
274 		 * See if we are in danger of overflowing a buffer. If
275 		 * so, use hardware flow control to ease the pressure.
276 		 */
277 
278 		/* but wmcom cannot. X-( */
279 
280 		/*
281 		 * If we're out of space, disable receive interrupts
282 		 * until the queue has drained a bit.
283 		 */
284 		if (cc <= 0)
285 			CLR(intm, INT_RXINT);
286 	}
287 
288 	/*
289 	 * Done handling any receive interrupts. See if data can be
290 	 * transmitted as well. Schedule tx done event if no data left
291 	 * and tty was marked busy.
292 	 */
293 
294 	if (!ISSET(fr, FR_TXFF)) {
295 		/* Output the next chunk of the contiguous buffer, if any. */
296 		if (sc->sc_tbc > 0) {
297 			while (sc->sc_tbc > 0 && !ISSET(fr, FR_TXFF)) {
298 				bus_space_write_1(iot, ioh, UARTDR,
299 				    *sc->sc_tba);
300 				sc->sc_tba++;
301 				sc->sc_tbc--;
302 				fr = bus_space_read_1(iot, ioh, UARTFR);
303 			}
304 		} else if (!ISSET(fr, FR_BUSY) && ISSET(intm, INT_TXINT)) {
305 			CLR(intm, INT_TXINT);
306 			sc->sc_tx_done = 1;
307 		}
308 	}
309 
310 	bus_space_write_1(iot, ioh, UARTINTM, intm);
311 
312 	/* Wake up the poller. */
313 	softint_schedule(sc->sc_si);
314 
315 	return 1;
316 }
317 
318 static void
319 wmcom_soft(void *arg)
320 {
321 	struct wmcom_softc *sc = arg;
322 	struct tty *tp = sc->sc_tty;
323 
324 	if (!device_is_active(sc->sc_dev))
325 		return;
326 
327 	if (sc->sc_rx_ready) {
328 		sc->sc_rx_ready = 0;
329 		wmcom_rxsoft(sc, tp);
330 	}
331 	if (sc->sc_tx_done) {
332 		sc->sc_tx_done = 0;
333 		CLR(tp->t_state, TS_BUSY);
334 		if (ISSET(tp->t_state, TS_FLUSH))
335 			CLR(tp->t_state, TS_FLUSH);
336 		else
337 			ndflush(&tp->t_outq,
338 			    (int)(sc->sc_tba - tp->t_outq.c_cf));
339 		(*tp->t_linesw->l_start)(tp);
340 	}
341 }
342 
343 static void
344 wmcom_start(struct tty *tp)
345 {
346 	struct wmcom_softc *sc
347 		= device_lookup_private(&wmcom_cd, COMUNIT(tp->t_dev));
348 	bus_space_tag_t iot = sc->sc_iot;
349 	bus_space_handle_t ioh = sc->sc_ioh;
350 	int s, n;
351 	uint8_t intm;
352 
353 	if (!device_is_active(sc->sc_dev))
354 		return;
355 
356 	s = spltty();
357 	if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP))
358 		goto out;
359 	if (!ttypull(tp))
360 		goto out;
361 
362 	/* Grab the first contiguous region of buffer space. */
363 	{
364 		u_char *tba;
365 		int tbc;
366 
367 		tba = tp->t_outq.c_cf;
368 		tbc = ndqb(&tp->t_outq, 0);
369 
370 		(void)splserial();
371 
372 		sc->sc_tba = tba;
373 		sc->sc_tbc = tbc;
374 	}
375 
376 	SET(tp->t_state, TS_BUSY);
377 
378 	intm = bus_space_read_1(iot, ioh, UARTINTM);
379 	if (!ISSET(intm, INT_TXINT)) {
380 		bus_space_write_1(iot, ioh, UARTINTM, intm | INT_TXINT);
381 
382 		/* Output the first chunk of the contiguous buffer. */
383 		n = uimin(sc->sc_tbc, UART_FIFO_SIZE);
384 		bus_space_write_multi_1(iot, ioh, UARTDR, sc->sc_tba, n);
385 		sc->sc_tba += n;
386 		sc->sc_tbc -= n;
387 	}
388 out:
389 	splx(s);
390 	return;
391 }
392 
393 static int
394 wmcom_param(struct tty *tp, struct termios *t)
395 {
396 	struct wmcom_softc *sc =
397 	    device_lookup_private(&wmcom_cd, COMUNIT(tp->t_dev));
398 	bus_space_tag_t iot = sc->sc_iot;
399 	bus_space_handle_t ioh = sc->sc_ioh;
400 	int s;
401 
402 	if (!device_is_active(sc->sc_dev))
403 		return ENXIO;
404 	if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
405 		return EINVAL;
406 
407 	/*
408 	 * For the console, always force CLOCAL and !HUPCL, so that the port
409 	 * is always active.
410 	 */
411 	if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR) ||
412 	    ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
413 		SET(t->c_cflag, CLOCAL);
414 		CLR(t->c_cflag, HUPCL);
415 	}
416 
417 	/*
418 	 * If there were no changes, don't do anything.  This avoids dropping
419 	 * input and improves performance when all we did was frob things like
420 	 * VMIN and VTIME.
421 	 */
422 	if (tp->t_ospeed == t->c_ospeed &&
423 	    tp->t_cflag == t->c_cflag)
424 		return 0;
425 
426 	s = splserial();
427 	bus_space_write_4(iot, ioh, UARTLCR, wmcom_rate2lcr(t->c_ospeed));
428 	bus_space_write_1(iot, ioh, UARTFCR, wmcom_cflag2fcr(t->c_cflag));
429 
430 	/* And copy to tty. */
431 	tp->t_ispeed = 0;
432 	tp->t_ospeed = t->c_ospeed;
433 	tp->t_cflag = t->c_cflag;
434 	splx(s);
435 
436 	/*
437 	 * Update the tty layer's idea of the carrier bit.
438 	 * We tell tty the carrier is always on.
439 	 */
440 	(*tp->t_linesw->l_modem)(tp, 1);
441 
442 	return 0;
443 }
444 
445 static int
446 wmcom_hwiflow(struct tty *tp, int block)
447 {
448 	/* Nothing */
449 	return 0;
450 }
451 
452 /* ARGSUSED */
453 int
454 wmcomopen(dev_t dev, int flag, int mode, struct lwp *l)
455 {
456 	struct wmcom_softc *sc;
457 	struct tty *tp;
458 	int error, s, s2;
459 	uint8_t con;
460 
461 	sc = device_lookup_private(&wmcom_cd, COMUNIT(dev));
462 	if (sc == NULL || !ISSET(sc->sc_hwflags, COM_HW_DEV_OK))
463 		return ENXIO;
464 	if (!device_is_active(sc->sc_dev))
465 		return ENXIO;
466 
467 #ifdef KGDB
468 	/*
469 	 * If this is the kgdb port, no other use is permitted.
470 	 */
471 	if (ISSET(sc->sc_hwflags, COM_HW_KGDB))
472 		return EBUSY;
473 #endif
474 
475 	tp = sc->sc_tty;
476 
477 	if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
478 		return EBUSY;
479 
480 	s = spltty();
481 
482 	/*
483 	 * Do the following iff this is a first open.
484 	 */
485 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
486 		struct termios t;
487 
488 		tp->t_dev = dev;
489 
490 		/* Enable and turn on interrupt */
491 		con = CON_UARTEN;
492 		if (ISSET(sc->sc_flags, WMCOM_IRDA))
493 			con |= CON_IRTXM;
494 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, UARTCON, con);
495 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, UARTINTM, INT_RXINT);
496 
497 		/*
498 		 * Initialize the termios status to the defaults.  Add in the
499 		 * sticky bits from TIOCSFLAGS.
500 		 */
501 		t.c_ispeed = 0;
502 		if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
503 			t.c_ospeed = wmcom_cnrate;
504 			t.c_cflag = wmcom_cncflag;
505 		} else {
506 			t.c_ospeed = TTYDEF_SPEED;
507 			t.c_cflag = TTYDEF_CFLAG;
508 		}
509 		if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL))
510 			SET(t.c_cflag, CLOCAL);
511 		if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS))
512 			SET(t.c_cflag, CRTSCTS);
513 		if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF))
514 			SET(t.c_cflag, MDMBUF);
515 		/* Make sure wmcom_param() we do something */
516 		tp->t_ospeed = 0;
517 		wmcom_param(tp, &t);
518 		tp->t_iflag = TTYDEF_IFLAG;
519 		tp->t_oflag = TTYDEF_OFLAG;
520 		tp->t_lflag = TTYDEF_LFLAG;
521 		ttychars(tp);
522 		ttsetwater(tp);
523 
524 		s2 = splserial();
525 
526 		/* Clear the input ring. */
527 		sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
528 		sc->sc_rbavail = WMCOM_RING_SIZE;
529 		wmcom_iflush(sc);
530 
531 		splx(s2);
532 	}
533 
534 	splx(s);
535 
536 	error = ttyopen(tp, COMDIALOUT(dev), ISSET(flag, O_NONBLOCK));
537 	if (error)
538 		goto bad;
539 
540 	error = (*tp->t_linesw->l_open)(dev, tp);
541 	if (error)
542 		goto bad;
543 	return 0;
544 
545 bad:
546 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
547 		/*
548 		 * We failed to open the device, and nobody else had it opened.
549 		 * Clean up the state as appropriate.
550 		 */
551 		wmcom_shutdown(sc);
552 
553 		/* Disable UART */
554 		if (!ISSET(sc->sc_hwflags, COM_HW_CONSOLE))
555 			bus_space_write_1(sc->sc_iot, sc->sc_ioh, UARTCON, 0);
556 	}
557 
558 	return error;
559 }
560 
561 /* ARGSUSED */
562 int
563 wmcomclose(dev_t dev, int flag, int mode, struct lwp *l)
564 {
565 	struct wmcom_softc *sc = device_lookup_private(&wmcom_cd, COMUNIT(dev));
566 	struct tty *tp = sc->sc_tty;
567 
568 	/* XXXX This is for cons.c. */
569 	if (!ISSET(tp->t_state, TS_ISOPEN))
570 		return 0;
571 
572 	(*tp->t_linesw->l_close)(tp, flag);
573 	ttyclose(tp);
574 
575 	if (!device_is_active(sc->sc_dev))
576 		return 0;
577 
578 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
579 		/*
580 		 * Although we got a last close, the device may still be in
581 		 * use; e.g. if this was the dialout node, and there are still
582 		 * processes waiting for carrier on the non-dialout node.
583 		 */
584 		wmcom_shutdown(sc);
585 
586 		/* Disable UART */
587 		if (!ISSET(sc->sc_hwflags, COM_HW_CONSOLE))
588 			bus_space_write_1(sc->sc_iot, sc->sc_ioh, UARTCON, 0);
589 	}
590 
591 	return 0;
592 }
593 
594 int
595 wmcomread(dev_t dev, struct uio *uio, int flag)
596 {
597 	struct wmcom_softc *sc = device_lookup_private(&wmcom_cd, COMUNIT(dev));
598 	struct tty *tp = sc->sc_tty;
599 
600 	if (!device_is_active(sc->sc_dev))
601 		return EIO;
602 
603 	return (*tp->t_linesw->l_read)(tp, uio, flag);
604 }
605 
606 int
607 wmcomwrite(dev_t dev, struct uio *uio, int flag)
608 {
609 	struct wmcom_softc *sc = device_lookup_private(&wmcom_cd, COMUNIT(dev));
610 	struct tty *tp = sc->sc_tty;
611 
612 	if (!device_is_active(sc->sc_dev))
613 		return EIO;
614 
615 	return (*tp->t_linesw->l_write)(tp, uio, flag);
616 }
617 
618 int
619 wmcomioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
620 {
621 	struct wmcom_softc *sc = device_lookup_private(&wmcom_cd, COMUNIT(dev));
622 	struct tty *tp = sc->sc_tty;
623 	int error, s;
624 
625 	if (!device_is_active(sc->sc_dev))
626 		return EIO;
627 
628 	error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
629 	if (error != EPASSTHROUGH)
630 		return error;
631 
632 	error = ttioctl(tp, cmd, data, flag, l);
633 	if (error != EPASSTHROUGH)
634 		return error;
635 
636 	switch (cmd) {
637 	case TIOCSFLAGS:
638 		error = kauth_authorize_device_tty(l->l_cred,
639 		    KAUTH_DEVICE_TTY_PRIVSET, tp);
640 		break;
641 	default:
642 		break;
643 	}
644 	if (error)
645 		return error;
646 
647 	s = splserial();
648 	error = 0;
649 	switch (cmd) {
650 	case TIOCSBRK:
651 		wmcom_break(sc, 1);
652 		break;
653 
654 	case TIOCCBRK:
655 		wmcom_break(sc, 0);
656 		break;
657 
658 	case TIOCGFLAGS:
659 		*(int *)data = sc->sc_swflags;
660 		break;
661 
662 	case TIOCSFLAGS:
663 		sc->sc_swflags = *(int *)data;
664 		break;
665 
666 	default:
667 		error = EPASSTHROUGH;
668 		break;
669 	}
670 	splx(s);
671 	return error;
672 }
673 
674 int
675 wmcompoll(dev_t dev, int events, struct lwp *l)
676 {
677 	struct wmcom_softc *sc = device_lookup_private(&wmcom_cd, COMUNIT(dev));
678 	struct tty *tp = sc->sc_tty;
679 
680 	if (!device_is_active(sc->sc_dev))
681 		return EIO;
682 
683 	return (*tp->t_linesw->l_poll)(tp, events, l);
684 }
685 
686 struct tty *
687 wmcomtty(dev_t dev)
688 {
689 	struct wmcom_softc *sc = device_lookup_private(&wmcom_cd, COMUNIT(dev));
690 
691 	return sc->sc_tty;
692 }
693 
694 void
695 wmcomstop(struct tty *tp, int flag)
696 {
697 	int s;
698 
699 	s = splserial();
700 	if (ISSET(tp->t_state, TS_BUSY)) {
701 		/* Stop transmitting at the next chunk. */
702 		if (!ISSET(tp->t_state, TS_TTSTOP))
703 			SET(tp->t_state, TS_FLUSH);
704 	}
705 	splx(s);
706 }
707 
708 
709 static void
710 wmcom_iflush(struct wmcom_softc *sc)
711 {
712 	bus_space_tag_t iot = sc->sc_iot;
713 	bus_space_handle_t ioh = sc->sc_ioh;
714 	int timo;
715 
716 	timo = 50000;
717 	while ((bus_space_read_1(iot, ioh, UARTFR) & FR_RXFE) == 0 &&
718 	    timo--)
719 		bus_space_read_1(iot, ioh, UARTDR);
720 	if (timo == 0)
721 		printf("%s: iflush timeout\n", device_xname(sc->sc_dev));
722 }
723 
724 static void
725 wmcom_shutdown(struct wmcom_softc *sc)
726 {
727 	int s;
728 
729 	s = splserial();
730 
731 	/* Turn off interrupt */
732 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, UARTINTM, 0);
733 
734 	/* Clear any break condition set with TIOCSBRK. */
735 	wmcom_break(sc, 0);
736 
737 	splx(s);
738 }
739 
740 static void
741 wmcom_break(struct wmcom_softc *sc, int onoff)
742 {
743 	int s;
744 	uint8_t fcr;
745 
746 	s = splserial();
747 	fcr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, UARTFCR);
748 	if (onoff)
749 		SET(fcr, FCR_BREAK);
750 	else
751 		CLR(fcr, FCR_BREAK);
752 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, UARTFCR, fcr);
753 	splx(s);
754 }
755 
756 static void
757 wmcom_rxsoft(struct wmcom_softc *sc, struct tty *tp)
758 {
759 	bus_space_tag_t iot = sc->sc_iot;
760 	bus_space_handle_t ioh = sc->sc_ioh;
761 	int code, s;
762 	u_int cc, scc;
763 	uint8_t intm;
764 	u_char sts, *get;
765 
766 	get = sc->sc_rbget;
767 	scc = cc = WMCOM_RING_SIZE - sc->sc_rbavail;
768 	while (cc) {
769 		code = get[0];
770 		sts = get[1];
771 		if (ISSET(sts, RSR_FE | RSR_PE | RSR_OE)) {
772 			if (ISSET(sts, (RSR_FE)))
773 				SET(code, TTY_FE);
774 			if (ISSET(sts, RSR_PE))
775 				SET(code, TTY_PE);
776 			if (ISSET(sts, RSR_OE))
777 				;		/* XXXXX: Overrun */
778 		}
779 		if ((*tp->t_linesw->l_rint)(code, tp) == -1) {
780 			/*
781 			 * The line discipline's buffer is out of space.
782 			 */
783 			/*
784 			 * We're either not using flow control, or the
785 			 * line discipline didn't tell us to block for
786 			 * some reason.  Either way, we have no way to
787 			 * know when there's more space available, so
788 			 * just drop the rest of the data.
789 			 */
790 			get += cc << 1;
791 			if (get >= sc->sc_rbuf + (WMCOM_RING_SIZE << 1))
792 				get -= (WMCOM_RING_SIZE << 1);
793 			cc = 0;
794 			break;
795 		}
796 		get += 2;
797 		if (get >= sc->sc_rbuf + (WMCOM_RING_SIZE << 1))
798 			get = sc->sc_rbuf;
799 		cc--;
800 	}
801 
802 	if (cc != scc) {
803 		sc->sc_rbget = get;
804 		s = splserial();
805 
806 		cc = sc->sc_rbavail += scc - cc;
807 		/* Buffers should be ok again, release possible block. */
808 		if (cc >= 1) {
809 			intm = bus_space_read_1(iot, ioh, UARTINTM);
810 			SET(intm, INT_RXINT);
811 			bus_space_write_1(iot, ioh, UARTINTM, intm);
812 		}
813 		splx(s);
814 	}
815 }
816 
817 static inline uint32_t
818 wmcom_rate2lcr(int rate)
819 {
820 
821 	return 7372800 / (16 * rate) - 1;
822 }
823 
824 static uint8_t
825 wmcom_cflag2fcr(tcflag_t cflag)
826 {
827 	int8_t fcr = FCR_UFIFOEN;
828 
829 	switch (cflag & CSIZE) {
830 	case CS5: SET(fcr, FCR_WLEN_5); break;
831 	case CS6: SET(fcr, FCR_WLEN_6); break;
832 	case CS7: SET(fcr, FCR_WLEN_7); break;
833 	case CS8: SET(fcr, FCR_WLEN_8); break;
834 	default:  SET(fcr, FCR_WLEN_8); break;
835 	}
836 	if (cflag & CSTOPB)
837 		SET(fcr, FCR_XSTOP);
838 	if (cflag & PARENB) {
839 		SET(fcr, (FCR_PRTEN | FCR_EVENPRT));
840 		if (cflag & PARODD)
841 			CLR(fcr, FCR_EVENPRT);
842 	}
843 	return fcr;
844 }
845 
846 #define WMCOM_CNREAD_1(offset) \
847 	(*(volatile uint8_t *)(wmcom_cnaddr + ((offset) << 2)))
848 #define WMCOM_CNREAD_4(offset) \
849 	(*(volatile uint32_t *)(wmcom_cnaddr + ((offset) << 2)))
850 #define WMCOM_CNWRITE_1(offset, val) \
851 	(*(volatile uint8_t *)(wmcom_cnaddr + ((offset) << 2)) = val)
852 #define WMCOM_CNWRITE_4(offset, val) \
853 	(*(volatile uint32_t *)(wmcom_cnaddr + ((offset) << 2)) = val)
854 
855 static struct consdev wmcomcons = {
856 	NULL, NULL, wmcom_cngetc, wmcom_cnputc, wmcom_cnpollc, NULL, NULL, NULL,
857 	NODEV, CN_NORMAL
858 };
859 
860 int
861 wmcom_cnattach(vaddr_t addr, int rate, tcflag_t cflag, int irda)
862 {
863 
864 	wmcom_cnaddr = addr;
865 	wmcom_cnrate = rate;
866 	wmcom_cncflag = cflag;
867 	WMCOM_CNWRITE_4(UARTLCR, wmcom_rate2lcr(rate));
868 	WMCOM_CNWRITE_1(UARTFCR, wmcom_cflag2fcr(cflag));
869 	if (irda)
870 		WMCOM_CNWRITE_1(UARTCON, CON_UARTEN | CON_IRTXM);
871 	else
872 		WMCOM_CNWRITE_1(UARTCON, CON_UARTEN);
873 
874 	cn_tab = &wmcomcons;
875 	cn_init_magic(&wmcom_cnm_state);
876 	cn_set_magic("\047\001");	/* default magic is BREAK */
877 
878 	return 0;
879 }
880 
881 /* ARGSUSED */
882 static int
883 wmcom_cngetc(dev_t dev)
884 {
885 	int s = splserial();
886 	char ch;
887 
888 	while (WMCOM_CNREAD_1(UARTFR) & FR_RXFE);
889 
890 	ch = WMCOM_CNREAD_4(UARTDR);
891 
892 	{
893 #ifdef DDB
894 		extern int db_active;
895 		if (!db_active)
896 #endif
897 			cn_check_magic(dev, ch, wmcom_cnm_state);
898 	}
899 
900 	splx(s);
901 	return ch;
902 }
903 
904 /* ARGSUSED */
905 static void
906 wmcom_cnputc(dev_t dev, int c)
907 {
908 	int s = splserial();
909 
910 	while (WMCOM_CNREAD_1(UARTFR) & FR_TXFF);
911 
912 	WMCOM_CNWRITE_1(UARTDR, c);
913 
914 	/* Make sure output. */
915 	while (WMCOM_CNREAD_1(UARTFR) & FR_BUSY);
916 
917 	splx(s);
918 }
919 
920 /* ARGSUSED */
921 static void
922 wmcom_cnpollc(dev_t dev, int on)
923 {
924 	/* Nothing */
925 }
926