1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
#include <stdbool.h>
#include <stdio.h>
#include <sys/io.h>
#include "serial.h"
enum {
THR = 0,
RBR = 0,
DLL = 0,
DLM = 1,
IER = 1,
IIR = 2,
FCR = 2,
LCR = 3,
MCR = 4,
LSR = 5,
MSR = 6,
SCR = 7,
};
int serial_init(struct serial_if *sif, const char *argv[])
{
uint16_t port;
unsigned int speed, divisor;
uint8_t dll, dlm, lcr;
port = strtoul(argv[0], NULL, 0);
if (port <= 3)
port = *(uint16_t *)(0x400 + port*2);
if (argv[1])
speed = strtoul(argv[1], NULL, 0);
else
speed = 115200;
divisor = 115200/speed;
/* Save old register settings */
sif->old.lcr = inb(port + LCR);
sif->old.mcr = inb(port + MCR);
sif->old.iir = inb(port + IIR);
/* Set 115200n81 */
outb(0x83, port + LCR); /* Enable divisor access */
sif->old.dll = inb(port + DLL);
sif->old.dlm = inb(port + DLM);
outb(divisor, port + DLL);
outb(divisor >> 8, port + DLM);
(void)inb(port + IER); /* Synchronize */
dll = inb(port + DLL);
dlm = inb(port + DLM);
lcr = inb(port + LCR);
outb(0x03, port + LCR);
(void)inb(port + IER); /* Synchronize */
sif->old.ier = inb(port + IER);
if (dll != (uint8_t)divisor ||
dlm != (uint8_t)(divisor >> 8) ||
lcr != 0x83)
return -1; /* This doesn't look like a serial port */
/* Disable interrupts */
outb(0, port + IER);
/* Enable 16550A FIFOs if available */
outb(0x41, port + FCR); /* Enable FIFO */
(void)inb(port + IER); /* Synchronize */
if (inb(port + IIR) < 0xc0)
outb(0x00, port + FCR); /* Disable FIFOs if non-functional */
(void)inb(port + IER); /* Synchronize */
return 0;
}
void serial_write(struct serial_if *sif, const void *data, size_t n)
{
uint16_t port = sif->port;
const char *p = data;
uint8_t lsr;
while (n--) {
do {
lsr = inb(port + LSR);
} while (!(lsr & 0x20));
outb(*p++, port + THR);
}
}
void serial_read(struct serial_if *sif, void *data, size_t n)
{
uint16_t port = sif->port;
char *p = data;
uint8_t lsr;
while (n--) {
do {
lsr = inb(port + LSR);
} while (!(lsr & 0x01));
*p++ = inb(port + RBR);
}
}
void serial_cleanup(struct serial_if *sif)
{
uint16_t port = sif->port;
outb(0x83, port + LCR);
(void)inb(port + IER);
outb(sif->old.dll, port + DLL);
outb(sif->old.dlm, port + DLM);
(void)inb(port + IER);
outb(sif->old.lcr & 0x7f, port + LCR);
(void)inb(port + IER);
outb(sif->old.mcr, port + MCR);
outb(sif->old.ier, port + IER);
if ((sif->old.iir & 0xc0) != 0xc0)
outb(0x00, port + FCR); /* Disable FIFOs */
}
|