b73016ee836fc8aac5af190f4605b947235a0d37
[feed/packages.git] / utils / gl-mifi-mcu / src / module.c
1 #include <linux/module.h>
2 #include <linux/gpio.h>
3 #include <linux/hrtimer.h>
4 #include <linux/interrupt.h>
5 #include <linux/ktime.h>
6 #include <linux/proc_fs.h>
7 #include <linux/seq_file.h>
8 #include <linux/version.h>
9
10 MODULE_LICENSE("GPL");
11 MODULE_AUTHOR("Nuno Goncalves");
12 MODULE_DESCRIPTION("GL-MiFi power monitoring MCU interface");
13 MODULE_VERSION("0.1");
14
15 static int gpio_tx = 19;
16 static int gpio_rx = 8;
17 static int baudrate = 1200;
18 static int query_interval_sec = 4;
19
20 static struct hrtimer timer_tx;
21 static struct hrtimer timer_rx;
22 static ktime_t period;
23 static int rx_bit_index = -1;
24
25 static unsigned read_buf_ready = 0;
26 static unsigned read_buf_size = 0;
27 static char read_buf[2][64] = {{0},{0}};
28
29 static int proc_show(struct seq_file *m, void *v)
30 {
31 seq_printf(m, "%s\n", read_buf[read_buf_ready]);
32 return 0;
33 }
34
35 static int proc_open(struct inode *inode, struct file *file)
36 {
37 return single_open(file, proc_show, NULL);
38 }
39
40 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
41 static const struct proc_ops hello_proc_ops = {
42 .proc_open = proc_open,
43 .proc_read = seq_read,
44 .proc_lseek = seq_lseek,
45 .proc_release = single_release,
46 };
47 #else
48 static const struct file_operations hello_proc_ops = {
49 .owner = THIS_MODULE,
50 .open = proc_open,
51 .read = seq_read,
52 .llseek = seq_lseek,
53 .release = single_release,
54 };
55 #endif
56
57 static irqreturn_t handle_rx_start(int irq, void* device)
58 {
59 if (rx_bit_index == -1)
60 {
61 hrtimer_start(&timer_rx, ktime_set(0, period / 2), HRTIMER_MODE_REL);
62 }
63 return IRQ_HANDLED;
64 }
65
66 static enum hrtimer_restart handle_tx(struct hrtimer* timer)
67 {
68 ktime_t current_time = ktime_get();
69 const unsigned char character = 'g';
70 static int bit_index = -1;
71
72 // Start bit.
73 if (bit_index == -1)
74 {
75 gpio_set_value(gpio_tx, 0);
76 bit_index++;
77 }
78
79 // Data bits.
80 else if (0 <= bit_index && bit_index < 8)
81 {
82 gpio_set_value(gpio_tx, 1 & (character >> bit_index));
83 bit_index++;
84 }
85
86 // Stop bit.
87 else if (bit_index == 8)
88 {
89 gpio_set_value(gpio_tx, 1);
90 bit_index = -1;
91 }
92
93 hrtimer_forward(&timer_tx, current_time, bit_index == 8
94 ? ktime_set(query_interval_sec, 0) //wait for next query cycle
95 : period); //wait for next bit period
96
97 return HRTIMER_RESTART;
98 }
99
100 void receive_character(unsigned char character)
101 {
102 if(character == '{')
103 read_buf_size = 0;
104
105 if(read_buf_size < (sizeof(read_buf[0])-1) || character == '}')
106 {
107 read_buf[!read_buf_ready][read_buf_size++] = character;
108 if(character == '}')
109 {
110 read_buf[!read_buf_ready][read_buf_size] = '\0';
111 read_buf_ready = !read_buf_ready;
112 read_buf_size = 0;
113 }
114 }
115 }
116
117 static enum hrtimer_restart handle_rx(struct hrtimer* timer)
118 {
119 ktime_t current_time = ktime_get();
120 static unsigned int character = 0;
121 int bit_value = gpio_get_value(gpio_rx);
122 enum hrtimer_restart result = HRTIMER_NORESTART;
123 bool must_restart_timer = false;
124
125 // Start bit.
126 if (rx_bit_index == -1)
127 {
128 rx_bit_index++;
129 character = 0;
130 must_restart_timer = true;
131 }
132
133 // Data bits.
134 else if (0 <= rx_bit_index && rx_bit_index < 8)
135 {
136 if (bit_value == 0)
137 {
138 character &= 0xfeff;
139 }
140 else
141 {
142 character |= 0x0100;
143 }
144
145 rx_bit_index++;
146 character >>= 1;
147 must_restart_timer = true;
148 }
149
150 // Stop bit.
151 else if (rx_bit_index == 8)
152 {
153 receive_character(character);
154 rx_bit_index = -1;
155 }
156
157 // Restarts the RX timer.
158 if (must_restart_timer)
159 {
160 hrtimer_forward(&timer_rx, current_time, period);
161 result = HRTIMER_RESTART;
162 }
163
164 return result;
165 }
166
167 static int __init gl_mifi_mcu_init(void)
168 {
169 bool success = true;
170
171 proc_create("gl_mifi_mcu", 0, NULL, &hello_proc_ops);
172
173 success &= gpio_request(gpio_tx, "soft_uart_tx") == 0;
174 success &= gpio_direction_output(gpio_tx, 1) == 0;
175 success &= gpio_request(gpio_rx, "soft_uart_rx") == 0;
176 success &= gpio_direction_input(gpio_rx) == 0;
177 success &= gpio_set_debounce(gpio_rx, 1000/baudrate/2);
178
179 success &= request_irq(
180 gpio_to_irq(gpio_rx),
181 handle_rx_start,
182 IRQF_TRIGGER_FALLING,
183 "gl_mifi_mcu_irq_handler",
184 NULL) == 0;
185
186 hrtimer_init(&timer_tx, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
187 timer_tx.function = &handle_tx;
188 hrtimer_init(&timer_rx, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
189 timer_rx.function = &handle_rx;
190 period = ktime_set(0, 1000000000/baudrate);
191 hrtimer_start(&timer_tx, period, HRTIMER_MODE_REL);
192
193 return success;
194 }
195
196 static void __exit gl_mifi_mcu_exit(void)
197 {
198 disable_irq(gpio_to_irq(gpio_rx));
199 hrtimer_cancel(&timer_tx);
200 hrtimer_cancel(&timer_rx);
201 free_irq(gpio_to_irq(gpio_rx), NULL);
202 gpio_set_value(gpio_tx, 0);
203 gpio_free(gpio_tx);
204 gpio_free(gpio_rx);
205 remove_proc_entry("gl_mifi_mcu", NULL);
206 }
207
208 module_init(gl_mifi_mcu_init);
209 module_exit(gl_mifi_mcu_exit);
210