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