Unifying External Interrupt System (#51)
[lunaix-os.git] / lunaix-os / hal / char / uart / 16x50_base.c
1 #include <lunaix/mm/valloc.h>
2 #include <lunaix/status.h>
3
4 #include <usr/lunaix/serial.h>
5 #include <usr/lunaix/term.h>
6
7 #include "16x50.h"
8
9 struct uart16550*
10 uart_alloc(ptr_t base_addr)
11 {
12     struct uart16550* uart = valloc(sizeof(struct uart16550));
13
14     // load registers default value
15     uart->cntl_save.rmc = UART_rMC_DTR | UART_rMC_RTS | UART_rMC_IEN;
16     uart->cntl_save.rie = 0;
17
18     uart->base_addr = base_addr;
19     uart->base_clk  = 115200U;
20     return uart;
21 }
22
23 void
24 uart_free(struct uart16550* uart)
25 {
26     vfree(uart);
27 }
28
29 int
30 uart_general_tx(struct serial_dev* sdev, u8_t* data, size_t len)
31 {
32     struct uart16550* uart = UART16550(sdev);
33
34     size_t i = 0;
35     while (i < len) {
36         while (!uart_can_transmit(uart))
37             ;
38         uart_write_byte(uart, data[i++]);
39     }
40
41     serial_end_xmit(sdev, len);
42
43     return RXTX_DONE;
44 }
45
46 static void
47 uart_set_control_mode(struct uart16550* uart, tcflag_t cflags)
48 {
49     uart->cntl_save.rie &= ~(UART_rIE_ERBFI | UART_rIE_EDSSI);
50     uart->cntl_save.rie |= (!(cflags & _CLOCAL)) * UART_rIE_EDSSI;
51     uart->cntl_save.rie |= (!!(cflags & _CREAD)) * UART_rIE_ERBFI;
52     uart_setie(uart);
53
54     uart->cntl_save.rlc &= ~UART_LCR_RESET;
55     uart->cntl_save.rlc |= (!!(cflags & _CSTOPB)) * UART_rLC_STOPB;
56     uart->cntl_save.rlc |= (!!(cflags & _CPARENB)) * UART_rLC_PAREN;
57     uart->cntl_save.rlc |= (!(cflags & _CPARODD)) * UART_rLC_PAREVN;
58     uart->cntl_save.rlc |= (cflags & _CSZ_MASK) >> 2;
59     uart_setlc(uart);
60 }
61
62 int
63 uart_general_exec_cmd(struct serial_dev* sdev, u32_t req, va_list args)
64 {
65     struct uart16550* uart = UART16550(sdev);
66     switch (req) {
67         case SERIO_RXEN:
68             uart_setie(uart);
69             break;
70         case SERIO_RXDA:
71             uart_clrie(uart);
72             break;
73         case SERIO_SETBRDRATE:
74             {
75                 unsigned int div, rate;
76
77                 rate = va_arg(args, speed_t);
78                 if (!rate) {
79                     return EINVAL;
80                 }
81
82                 div  = uart->base_clk / va_arg(args, speed_t);
83                 uart_baud_divisor(uart, div);
84                 break;
85             }
86         case SERIO_SETBRDBASE:
87             {
88                 int clk = va_arg(args, unsigned int);
89                 if (!clk) {
90                     return EINVAL;
91                 }
92                 
93                 uart->base_clk = clk;
94                 break;
95             }
96         case SERIO_SETCNTRLMODE:
97             uart_set_control_mode(uart, va_arg(args, tcflag_t));
98             break;
99         default:
100             return ENOTSUP;
101     }
102     return 0;
103 }
104
105 void
106 uart_handle_irq_overlap(irq_t irq, struct llist_header* ports)
107 {
108     struct uart16550 *pos, *n;
109     llist_for_each(pos, n, ports, local_ports)
110     {
111         int is = uart_intr_identify(pos);
112         if (irq == pos->irq && (is == UART_CHR_TIMEOUT)) {
113             goto done;
114         }
115     }
116
117     return;
118
119 done:
120     uart_handle_irq(irq, pos);
121 }
122
123 void
124 uart_handle_irq(irq_t irq, struct uart16550 *uart)
125 {
126     char tmpbuf[32];
127     char recv;
128     int i = 0;
129     while ((recv = uart_read_byte(uart))) {
130         tmpbuf[i++] = recv;
131         if (likely(i < 32)) {
132             continue;
133         }
134
135         if (!serial_accept_buffer(uart->sdev, tmpbuf, i)) {
136             uart_clear_rxfifo(uart);
137             return;
138         }
139
140         i = 0;
141     }
142
143     serial_accept_buffer(uart->sdev, tmpbuf, i);
144     serial_accept_one(uart->sdev, 0);
145
146     serial_end_recv(uart->sdev);
147 }