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