-
Notifications
You must be signed in to change notification settings - Fork 0
/
uart.c
155 lines (133 loc) · 3.12 KB
/
uart.c
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#define UART_BAUD_RATE 115200
struct BCM2711_AUX {
unsigned IRQ;
unsigned ENABLES;
unsigned MU_IO_REG;
unsigned MU_IER_REG;
unsigned MU_IIR_REG;
unsigned MU_LCR_REG;
unsigned MU_MCR_REG;
unsigned MU_LSR_REG;
unsigned MU_MSR_REG;
unsigned MU_SCRATCH;
unsigned MU_CNTL_REG;
unsigned MU_STAT_REG;
unsigned MU_BAUD_REG;
unsigned SPI1_CNTL0_REG;
unsigned SPI1_CNTL1_REG;
unsigned SPI1_STAT_REG;
unsigned SPI1_PEEK_REG;
unsigned SPI1_IO_REGa;
unsigned SPI1_IO_REGb;
unsigned SPI1_IO_REGc;
unsigned SPI1_IO_REGd;
unsigned SPI1_TXHOLD_REGa;
unsigned SPI1_TXHOLD_REGb;
unsigned SPI1_TXHOLD_REGc;
unsigned SPI1_TXHOLD_REGd;
unsigned SPI2_CNTL0_REG;
unsigned SPI2_CNTL1_REG;
unsigned SPI2_STAT_REG;
unsigned SPI2_PEEK_REG;
unsigned SPI2_IO_REGa;
unsigned SPI2_IO_REGb;
unsigned SPI2_IO_REGc;
unsigned SPI2_IO_REGd;
unsigned SPI2_TXHOLD_REGa;
unsigned SPI2_TXHOLD_REGb;
unsigned SPI2_TXHOLD_REGc;
unsigned SPI2_TXHOLD_REGd;
};
static struct BCM2711_AUX *p_AUX = (struct BCM2711_AUX *)0x7e2150000;
static int DeviceUseCount = 0;
static ssize_t uart_read(struct file *fp, char *user_buf, size_t nbytes, loff_t *off)
{
char buf[8];
int n;
int len = 0;
int err;
while(len < nbytes) {
n = 0;
while(!(p_AUX->MU_IIR_REG & 1)); //polling
while(n < sizeof(buf) && n < nbytes - len && p_AUX->MU_IIR_REG & 2) { // copy to buf
buf[n++] = (char)p_AUX->MU_IO_REG;
}
err = copy_to_user(user_buf + len, buf, n); // copy to user buffer
if(err < 0) {
return -1;
}
len += n;
}
return len;
}
static ssize_t uart_write(struct file *fp, const char *user_str, size_t nbytes, loff_t *off)
{
char buf[8];
int i;
int n;
int len = 0;
int err;
while(len < nbytes) {
while(!(p_AUX->MU_IIR_REG & 1)); //polling
n = nbytes - len > sizeof(buf) ? sizeof(buf) : nbytes - len; //cut n into buf size
err = copy_from_user(buf, user_str + len, n); //copy to kernel
if(err < 0) {
return -1;
}
for(i = 0; i < n && p_AUX->MU_STAT_REG & 32; i++) { // send data
p_AUX->MU_IO_REG = buf[i];
}
n = i;
len += n;
}
return len;
}
static int uart_open(struct inode *node, struct file *fp)
{
if(DeviceUseCount != 0) {
return -EBUSY;
}
DeviceUseCount++;
return 0;
}
static int uart_release(struct inode *node, struct file *fp)
{
if(DeviceUseCount == 0) {
return -EBADF;
}
DeviceUseCount--;
return 0;
}
struct file_operations uart_fops = {
.owner = THIS_MODULE,
.read = uart_read,
.write = uart_write,
.open = uart_open,
.release = uart_release,
};
static int __init register_dev(void)
{
int err;
err = register_chrdev(221, "my_uart", &uart_fops);
if(err < 0) {
printk("Unable to register driver\n");
}
else {
p_AUX->ENABLES = 1; //enable mini UART
p_AUX->MU_IER_REG = 3; //enable TX, RX interrupts
p_AUX->MU_BAUD_REG = 250000000 / (8 * UART_BAUD_RATE) - 1;
printk("UART driver has been installed\n");
}
return err;
}
static void __exit unregister_dev(void)
{
p_AUX->ENABLES = 0; //disable entire peripherals
unregister_chrdev(221, "my_uart");
}
module_init(register_dev);
module_exit(unregister_dev);