1 #include <stdbool.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <console.h>
5 #include <sys/io.h>
6 #include <sys/cpu.h>
7 #include <syslinux/config.h>
8
9 #include "serial.h"
10
11 enum {
12 THR = 0,
13 RBR = 0,
14 DLL = 0,
15 DLM = 1,
16 IER = 1,
17 IIR = 2,
18 FCR = 2,
19 LCR = 3,
20 MCR = 4,
21 LSR = 5,
22 MSR = 6,
23 SCR = 7,
24 };
25
26
serial_init(struct serial_if * sif,const char * argv[])27 int serial_init(struct serial_if *sif, const char *argv[])
28 {
29 const struct syslinux_serial_console_info *sci
30 = syslinux_serial_console_info();
31 uint16_t port;
32 unsigned int divisor;
33 uint8_t dll, dlm, lcr;
34
35 if (!argv[0]) {
36 if (sci->iobase) {
37 port = sci->iobase;
38 } else {
39 printf("No port number specified and not using serial console!\n");
40 return -1;
41 }
42 } else {
43 port = strtoul(argv[0], NULL, 0);
44 if (port <= 3) {
45 uint16_t addr = ((uint16_t *)0x400)[port];
46 if (!addr) {
47 printf("No serial port address found!\n");
48 return -1;
49 }
50 printf("Serial port %u is at 0x%04x\n", port, addr);
51 port = addr;
52 }
53 }
54
55 sif->port = port;
56 sif->console = false;
57
58 divisor = 1; /* Default speed = 115200 bps */
59
60 /* Check to see if this is the same as the serial console */
61 if (port == sci->iobase) {
62 /* Overlaying the console... */
63 sif->console = true;
64
65 /* Default to already configured speed */
66 divisor = sci->divisor;
67
68 /* Shut down I/O to the console for the time being */
69 openconsole(&dev_null_r, &dev_null_w);
70 }
71
72 if (argv[0] && argv[1])
73 divisor = 115200/strtoul(argv[1], NULL, 0);
74
75 cli(); /* Just in case... */
76
77 /* Save old register settings */
78 sif->old.lcr = inb(port + LCR);
79 sif->old.mcr = inb(port + MCR);
80 sif->old.iir = inb(port + IIR);
81
82 /* Set speed */
83 outb(0x83, port + LCR); /* Enable divisor access */
84 sif->old.dll = inb(port + DLL);
85 sif->old.dlm = inb(port + DLM);
86 outb(divisor, port + DLL);
87 outb(divisor >> 8, port + DLM);
88 (void)inb(port + IER); /* Synchronize */
89
90 dll = inb(port + DLL);
91 dlm = inb(port + DLM);
92 lcr = inb(port + LCR);
93 outb(0x03, port + LCR); /* Enable data access, n81 */
94 (void)inb(port + IER); /* Synchronize */
95 sif->old.ier = inb(port + IER);
96
97 /* Disable interrupts */
98 outb(0, port + IER);
99
100 sti();
101
102 if (dll != (uint8_t)divisor ||
103 dlm != (uint8_t)(divisor >> 8) ||
104 lcr != 0x83) {
105 serial_cleanup(sif);
106 printf("No serial port detected!\n");
107 return -1; /* This doesn't look like a serial port */
108 }
109
110 /* Enable 16550A FIFOs if available */
111 outb(0x01, port + FCR); /* Enable FIFO */
112 (void)inb(port + IER); /* Synchronize */
113 if (inb(port + IIR) < 0xc0)
114 outb(0x00, port + FCR); /* Disable FIFOs if non-functional */
115 (void)inb(port + IER); /* Synchronize */
116
117 return 0;
118 }
119
serial_write(struct serial_if * sif,const void * data,size_t n)120 void serial_write(struct serial_if *sif, const void *data, size_t n)
121 {
122 uint16_t port = sif->port;
123 const char *p = data;
124 uint8_t lsr;
125
126 while (n--) {
127 do {
128 lsr = inb(port + LSR);
129 } while (!(lsr & 0x20));
130
131 outb(*p++, port + THR);
132 }
133 }
134
serial_read(struct serial_if * sif,void * data,size_t n)135 void serial_read(struct serial_if *sif, void *data, size_t n)
136 {
137 uint16_t port = sif->port;
138 char *p = data;
139 uint8_t lsr;
140
141 while (n--) {
142 do {
143 lsr = inb(port + LSR);
144 } while (!(lsr & 0x01));
145
146 *p++ = inb(port + RBR);
147 }
148 }
149
serial_cleanup(struct serial_if * sif)150 void serial_cleanup(struct serial_if *sif)
151 {
152 uint16_t port = sif->port;
153
154 outb(0x83, port + LCR);
155 (void)inb(port + IER);
156 outb(sif->old.dll, port + DLL);
157 outb(sif->old.dlm, port + DLM);
158 (void)inb(port + IER);
159 outb(sif->old.lcr & 0x7f, port + LCR);
160 (void)inb(port + IER);
161 outb(sif->old.mcr, port + MCR);
162 outb(sif->old.ier, port + IER);
163 if (sif->old.iir < 0xc0)
164 outb(0x00, port + FCR); /* Disable FIFOs */
165
166 /* Re-enable console messages, if we shut them down */
167 if (sif->console)
168 openconsole(&dev_null_r, &dev_stdcon_w);
169 }
170