-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtuxctl-ld.c
323 lines (253 loc) · 8.01 KB
/
tuxctl-ld.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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
https://powcoder.com
代写代考加微信 powcoder
Assignment Project Exam Help
Add WeChat powcoder
/* tuxctl-ld.c
* The line discipline for the MP2 Tuxcontroller wrapper.
* Mark Murphy 2006
* Andrew Ofisher 2007
* Puskar Naha 2013
*/
#include <linux/tty.h>
#include <linux/tty_ldisc.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/init.h>
#include "tuxctl-ld.h"
#define uhoh(str, ...) printk(KERN_EMERG "%s " str, __FUNCTION__, ##__VA_ARGS__)
#define debug(str, ...) printk(KERN_DEBUG "%s " str, __FUNCTION__,\
## __VA_ARGS__)
/* Here's an interesting tidbit: tty_struct has no synchronization available
* to us to protect against races involving the tty->disc_data field. So,
* rather than do something tricky, we'll employ the 'one-big-freakin-lock'
* strategy for synchronizing the methods in this file. This can't be a
* semaphore because of the following chain of function calls:
*
* rs_interrupt() (serial.c)
* tty_flip_buffer_push() (tty_io.c)
* flush_to_ldisc() (tty_io.c)
* ldisc.receive_buf() (this file)
*
* Specifically, rs_interrupt is, well, an interrupt. Sleeping in an
* interrupt is a recipe for breaking things, so a spinlock it is.
*/
static spinlock_t tuxctl_ldisc_lock = SPIN_LOCK_UNLOCKED;
/* Line Discipline specific stuff */
#define TUXCTL_MAGIC 0x74757863
#define sanity(data) if((data)->magic != TUXCTL_MAGIC){\
uhoh("MAGIC MISMATCH!\n");\
BUG();}
static int tuxctl_ldisc_open(struct tty_struct*);
static void tuxctl_ldisc_close(struct tty_struct*);
static void tuxctl_ldisc_rcv_buf(struct tty_struct*, const unsigned char *,
char *, int);
static void tuxctl_ldisc_write_wakeup(struct tty_struct*);
static void tuxctl_ldisc_data_callback(struct tty_struct *tty);
#define TUXCTL_BUFSIZE 64
typedef struct tuxctl_ldisc_data {
unsigned long magic;
char rx_buf[TUXCTL_BUFSIZE];
int rx_start, rx_end;
char tx_buf[TUXCTL_BUFSIZE];
int tx_start, tx_end;
} tuxctl_ldisc_data_t;
static struct tty_ldisc tuxctl_ldisc = {
.magic = TUXCTL_MAGIC,
.name = "tuxcontroller",
.open = tuxctl_ldisc_open,
.close = tuxctl_ldisc_close,
.ioctl = tuxctl_ioctl,
.receive_buf = tuxctl_ldisc_rcv_buf,
.write_wakeup = tuxctl_ldisc_write_wakeup,
};
int __init
tuxctl_ldisc_init(void)
{
int err = 0;
if((err = tty_register_ldisc(N_MOUSE, &tuxctl_ldisc))){
debug("tuxctl line discipline register failed\n");
}else{
printk("tuxctl line discipline registered\n");
}
return err;
}
module_init(tuxctl_ldisc_init);
void __exit
tuxctl_ldisc_exit(void)
{
tty_unregister_ldisc(N_MOUSE);
printk("tuxctl line discipline removed\n");
}
module_exit(tuxctl_ldisc_exit);
#define buf_used(start,end) ((start) <= (end) ? \
((end) - (start)) \
: (TUXCTL_BUFSIZE + (end) - (start)))
#define buf_room(start,end) (TUXCTL_BUFSIZE - buf_used(start,end) - 1)
#define buf_empty(start, end) ((start) == (end))
#define buf_full(start, end) ((((end)+1)%TUXCTL_BUFSIZE) == (start))
#define buf_incidx(idx) ((idx) = ((idx)+1) % TUXCTL_BUFSIZE)
static int
tuxctl_ldisc_open(struct tty_struct *tty)
{
tuxctl_ldisc_data_t *data;
unsigned long flags;
if(!(data = kmalloc(sizeof(*data), GFP_KERNEL))){
uhoh("kmalloc failed!\n");
return -ENOMEM;
}
spin_lock_irqsave(&tuxctl_ldisc_lock, flags);
data->magic = TUXCTL_MAGIC;
data->rx_start = 0;
data->rx_end = 0;
data->tx_start = 0;
data->tx_end = 0;
tty->disc_data = data;
spin_unlock_irqrestore(&tuxctl_ldisc_lock, flags);
return 0;
}
static void
tuxctl_ldisc_close(struct tty_struct *tty)
{
tuxctl_ldisc_data_t *data;
unsigned long flags;
spin_lock_irqsave(&tuxctl_ldisc_lock, flags);
data = tty->disc_data;
tty->disc_data = 0;
spin_unlock_irqrestore(&tuxctl_ldisc_lock, flags);
kfree(data);
}
/* tuxctl_ldisc_rcv_buf
* The receive_buf() method of our line discipline. It receives count bytes
* from cp. fp points to some flag/error bytes which I conveniently ignore.
* This is called when there are bytes received from the serial driver, and
* is called from an interrupt handler.
*/
static void
tuxctl_ldisc_rcv_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
int call = 0, c=0;
unsigned long flags;
tuxctl_ldisc_data_t *data;
spin_lock_irqsave(&tuxctl_ldisc_lock, flags);
if(0 != (data = tty->disc_data)){
call = 1;
while(count-- > 0 && !buf_full(data->rx_start, data->rx_end)) {
data->rx_buf[data->rx_end] = *cp++;
buf_incidx(data->rx_end);
c++;
}
}
spin_unlock_irqrestore(&tuxctl_ldisc_lock, flags);
if(call){
tuxctl_ldisc_data_callback(tty);
}
}
/* tuxctl_ldisc_write_wakeup()
* Called by the lower level serial driver when it can accept more
*/
static void
tuxctl_ldisc_write_wakeup(struct tty_struct *tty)
{
tuxctl_ldisc_data_t *data;
int sent, n = 0, room;
char buf[TUXCTL_BUFSIZE];
unsigned long flags;
/* I hope that this doesn't need synchronization. */
room = tty->driver->write_room(tty);
spin_lock_irqsave(&tuxctl_ldisc_lock, flags);
data = tty->disc_data;
while(n <= room && !buf_empty(data->tx_start, data->tx_end)){
buf[n++] = data->tx_buf[data->tx_start];
buf_incidx(data->tx_start);
}
spin_unlock_irqrestore(&tuxctl_ldisc_lock, flags);
sent = tty->driver->write(tty, buf, n);
if(sent != n){
debug("driver lied to us? We lost some data");
}
}
/*********** Interface to the char driver ********************/
/* tuxctl_ldisc_get()
* Read bytes that the line-discipline has received from the controller.
* Returns the number of bytes actually read, or -1 on error (if, for
* example, the first argument is invalid.
*/
int
tuxctl_ldisc_get(struct tty_struct *tty, char *buf, int n)
{
tuxctl_ldisc_data_t *data;
unsigned long flags;
int r = 0;
spin_lock_irqsave(&tuxctl_ldisc_lock, flags);
data = tty->disc_data;
while(n-- > 0 && !buf_empty(data->rx_start, data->rx_end)){
*buf++ = data->rx_buf[data->rx_start];
buf_incidx(data->rx_start);
r++;
}
spin_unlock_irqrestore(&tuxctl_ldisc_lock, flags);
return r;
}
/* tuxctl_ldisc_put()
* Write bytes out to the device. Returns the number of bytes *not* written.
* This means, 0 on success and >0 if the line discipline's internal buffer
* is full.
*/
int
tuxctl_ldisc_put(struct tty_struct *tty, char const *buf, int n)
{
tuxctl_ldisc_data_t *data;
unsigned long flags;
spin_lock_irqsave(&tuxctl_ldisc_lock, flags);
data = tty->disc_data;
while (n > 0 && !buf_full(data->tx_start, data->tx_end)) {
data->tx_buf[data->tx_end] = *buf++;
buf_incidx(data->tx_end);
--n;
}
spin_unlock_irqrestore(&tuxctl_ldisc_lock, flags);
/* Potential race conditions here ... ?*/
tuxctl_ldisc_write_wakeup(tty);
return n;
}
/* tuxctl_ldisc_data_callback()
* This is the function called from the line-discipline when data is
* available from the device. This is how responses to polling the buttons
* and ACK's for setting the LEDs will be transmitted to the tuxctl driver.
* The tuxctl driver must implement this function. The 'tty' parameter
* can be passed back to the tuxctl_ldisc_read function to read
* data from the lower-level buffers.
*
* IMPORTANT: This function is called from an interrupt context, so it
* cannot acquire any semaphores or otherwise sleep, or access
* the 'current' pointer. It also must not take up too much time.
*/
static void tuxctl_ldisc_data_callback(struct tty_struct *tty)
{
/* Should probably synchronize these */
static unsigned char saved[2];
static int n_saved = 0;
unsigned char packet[12], *p;
int n, i = 0, j;
for(i=0; i< n_saved; i++)
packet[i] = saved[i];
n = n_saved + tuxctl_ldisc_get(tty, packet + n_saved, 12 - n_saved);
/* Look at all bytes as potential packet beginnings except for
* the last two bytes. Save them for next time. */
for(i = 0; i < n-2; ){
p = packet + i;
/* Check the framing bits to detect lost bytes */
if(!(p[0]&0x80) && p[1]&0x80 && p[2]&0x80){
tuxctl_handle_packet(tty, p);
i += 3;
}else{
i++;
}
}
/* Save the leftovers - extra bytes for breakfast */
n_saved = n - i;
for(j = 0; j < n_saved; j++)
saved[j] = packet[i+j];
}