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