Introduce old redir program
[lcapit-junk-code.git] / linux-kernel / fake-serial / tty / fake-serial-device.c
blobd5fbcf377a1bbcb992afcdabad348222f0dcc6d4
1 /*
2 * fake-serial-device: A fake modem-like device and device driver implementation.
4 * This driver was written just to teach me a little about the Linux's kernel
5 * tty layer.
7 * FIXME:
9 * 1. User-level programs read()s lots of '\0' before the actual response
10 * 2. fake_device_handler() doesn't handle the case in which the buffer
11 * become its size +1, but end-of-command wasn't issued yet
13 * TODO:
15 * 1. Documents the design
16 * 2. Documents every function
17 * 3. Implements a true MCR and MSR emulation support (the current
18 * implementation wasn't even tested)
19 * 4. ioctl() is bogus
20 * 5. Change the driver to act as a real modem
21 * 6. Moves some configurable values (like debug and number of devices) as
22 * module's parameters
23 * 7. Cleanup some uneeded and/or brain-damaged code (like open count at
24 * module's initialization)
26 #include <linux/init.h>
27 #include <linux/kernel.h>
28 #include <linux/sched.h>
29 #include <linux/timer.h>
30 #include <linux/tty.h>
31 #include <linux/tty_driver.h>
32 #include <linux/tty_flip.h>
33 #include <linux/serial_reg.h>
34 #include <linux/module.h>
35 #include <asm/semaphore.h>
37 #define FAKE_DEVICE_MAJOR 188
38 #define FAKE_DEVICE_MINOR 0
39 #define FAKE_DEVICE_NR_DEVS 5
40 #define FAKE_DEVICE_IBUF_LEN 12
41 #define FAKE_DEVICE_DELAY (HZ * 1)
42 #define FAKE_DEVICE_END_CMD_CHAR '%'
44 struct fake_device {
45 char *ibuf;
46 int idx;
47 size_t len;
48 int open_count;
49 struct tty_struct *tty;
50 struct timer_list timer;
51 unsigned int msr;
52 unsigned int mcr;
55 #define for_each_ibuf_char(p, fake) \
56 for (p = fake->ibuf; (p - fake->ibuf) < fake->idx; p++)
58 static struct fake_device *fake_devices[FAKE_DEVICE_NR_DEVS];
59 static struct tty_driver *fake_device_tty;
60 static int fake_device_pr_debug = 1;
62 #define fake_device_pr_debug(format, a...) \
63 do { if (fake_device_pr_debug) \
64 printk(KERN_DEBUG "%s (%d): " format, __FUNCTION__, \
65 (int) current->pid, ##a); \
66 } while (0)
68 static char *fake_device_commands[] = {
69 "AT+A%",
70 "AT+AB%",
71 "AT+FOO%",
72 NULL
75 static char *fake_device_responses[] = {
76 "AT+A: aaaaaa%",
77 "AT+AB: seu puto%",
78 "AT+FOO: canalha!%",
79 NULL
82 static char *fake_device_error_resp = "ERROR: Sua mula%";
84 static DECLARE_MUTEX(fake_device_mutex);
86 static void fill_tty_buffer(struct tty_struct *tty, const char *buf,
87 int buf_len)
89 int room;
91 room = tty_buffer_request_room(tty, buf_len);
92 if (!room) {
93 printk(KERN_ALERT "%s: No memory available!\n", __FUNCTION__);
94 return;
96 tty_insert_flip_string(tty, buf, buf_len);
97 tty_flip_buffer_push(tty);
100 static void fake_device_ibuf_dump(struct fake_device *fake)
102 char *p;
104 for_each_ibuf_char(p, fake)
105 fake_device_pr_debug("--> (%lu) 0x%X %c\n", p - fake->ibuf,
106 *p, *p);
108 fake_device_pr_debug("---> len: %lu | idx: %d\n", fake->len,
109 fake->idx);
112 static void fake_device_handler(unsigned long data)
114 int i, found = 0;
115 char *p, *resp = NULL;
116 struct fake_device *fake = (struct fake_device *) data;
118 fake_device_pr_debug("%s: Running\n", __FUNCTION__);
120 fake_device_ibuf_dump(fake);
122 for_each_ibuf_char(p, fake)
123 if (*p == FAKE_DEVICE_END_CMD_CHAR) {
124 found = 1;
125 break;
128 if (!found) {
129 /* No end of command has been found */
130 if (!fake->len)
131 goto out_reset;
132 return;
136 * End of command has been found
139 for (i = 0; fake_device_commands[i]; i++) {
140 if (!memcmp(fake->ibuf, fake_device_commands[i],
141 (p - fake->ibuf) + 1)) {
142 resp = fake_device_responses[i];
143 fake_device_pr_debug("--> result: %s\n", resp);
144 goto out_reset;
148 resp = fake_device_error_resp;
149 fake_device_pr_debug("---> result: %s\n", resp);
151 out_reset:
152 if (resp)
153 fill_tty_buffer(fake->tty, resp, strlen(resp));
154 fake->idx = 0;
155 fake->len = FAKE_DEVICE_IBUF_LEN;
158 static int fake_device_open(struct tty_struct *tty, struct file *filp)
160 int idx;
161 int ret = 0;
162 struct fake_device *fake;
164 down(&fake_device_mutex);
166 idx = tty->index;
168 fake = fake_devices[idx];
169 if (!fake) {
170 fake = kmalloc(sizeof(struct fake_device), GFP_KERNEL);
171 if (!fake) {
172 printk(KERN_ALERT "Couldn't alloc a fake_device\n");
173 ret = -ENOMEM;
174 goto out;
176 fake->idx = 0;
177 fake->len = FAKE_DEVICE_IBUF_LEN;
178 fake->ibuf = kmalloc(fake->len, GFP_KERNEL);
179 if (!fake->ibuf) {
180 printk(KERN_ALERT "Couldn't alloc ibuf\n");
181 kfree(fake);
182 ret = -ENOMEM;
183 goto out;
185 fake->open_count = 0;
186 init_timer(&fake->timer);
187 fake->timer.data = (unsigned long) fake;
188 fake->timer.function = fake_device_handler;
189 fake->tty = tty;
190 fake->msr = fake->mcr = 0;
191 fake_devices[idx] = fake;
192 tty->driver_data = fake;
195 fake->open_count++;
196 if (fake->open_count == 1)
197 fake_device_pr_debug("First time opening %d\n", idx);
199 fake_device_pr_debug("Device %d opened\n", tty->index);
200 out:
201 up(&fake_device_mutex);
202 return ret;
205 static void fake_device_close(struct tty_struct *tty, struct file *filp)
207 int idx;
208 struct fake_device *fake;
210 down(&fake_device_mutex);
212 idx = tty->index;
213 fake = tty->driver_data;
215 fake->open_count--;
216 BUG_ON(fake->open_count < 0);
217 if (!fake->open_count) {
218 del_timer(&fake->timer);
219 kfree(fake->ibuf);
220 kfree(fake);
221 fake_devices[idx] = NULL;
222 tty->driver_data = NULL;
223 fake_device_pr_debug("Device %d closed forever\n", idx);
224 } else {
225 fake_device_pr_debug("Device %d closed\n", idx);
228 up(&fake_device_mutex);
231 static int fake_device_write(struct tty_struct *tty, const unsigned char *buf,
232 int count)
234 int ret = 0;
235 struct fake_device *fake;
237 fake = tty->driver_data;
238 if (!fake)
239 return -ENODEV;
241 del_timer_sync(&fake->timer);
243 fake_device_pr_debug("%d device %d bytes written\n", tty->index, count);
244 memcpy(fake->ibuf + fake->idx, buf, fake->len);
245 if (count > fake->len) {
246 ret = fake->len;
247 fake->idx += fake->len;
248 fake->len = 0;
249 } else {
250 ret = count;
251 fake->idx += count;
252 fake->len -= count;
255 fake->timer.expires = jiffies + FAKE_DEVICE_DELAY;
256 add_timer(&fake->timer);
258 return ret;
261 static int fake_device_write_room(struct tty_struct *tty)
263 struct fake_device *fake;
265 fake = tty->driver_data;
266 if (!fake)
267 return -ENODEV;
269 return fake->len;
272 static int fake_device_ioctl(struct tty_struct *tty, struct file *file,
273 unsigned int cmd, unsigned long arg)
275 return -ENOIOCTLCMD;
278 static void fake_device_set_termios(struct tty_struct *tty, struct termios *old)
280 fake_device_pr_debug("");
281 return;
284 static void fake_device_throttle(struct tty_struct *tty)
286 struct fake_device *fake;
288 fake = tty->driver_data;
289 if (!fake)
290 return;
292 printk(KERN_ALERT "%s: called for minor %d\n", __FUNCTION__,
293 tty->index);
294 return;
297 static void fake_device_unthrottle(struct tty_struct *tty)
299 struct fake_device *fake;
301 fake = tty->driver_data;
302 if (!fake)
303 return;
305 printk(KERN_ALERT "%s: called for minor %d\n", __FUNCTION__,
306 tty->index);
307 return;
310 static void fake_device_break_ctl(struct tty_struct *tty, int break_state)
312 struct fake_device *fake;
314 fake = tty->driver_data;
315 if (!fake)
316 return;
318 printk(KERN_ALERT "%s: sending BREAK to device %d\n", __FUNCTION__,
319 tty->index);
320 return;
323 static int fake_device_chars_in_buffer(struct tty_struct *tty)
326 * That's our bogus implementation, we never have queued data
328 return 0;
331 static int fake_device_tiocmget(struct tty_struct *tty, struct file *file)
333 unsigned int msr, mcr;
334 unsigned int result = 0;
335 struct fake_device *fake;
337 fake = tty->driver_data;
338 if (!fake)
339 return -ENODEV;
341 msr = fake->msr;
342 mcr = fake->mcr;
344 result = ((mcr & UART_MCR_DTR) ? TIOCM_DTR : 0) | /* DTR is set */
345 ((mcr & UART_MCR_RTS) ? TIOCM_RTS : 0) | /* RTS is set */
346 ((mcr & UART_MCR_LOOP) ? TIOCM_LOOP : 0) | /* LOOP is set */
347 ((msr & UART_MSR_CTS) ? TIOCM_CTS : 0) | /* CTS is set */
348 ((msr & UART_MSR_DCD) ? TIOCM_CAR : 0) | /* Carrier detect is set*/
349 ((msr & UART_MSR_RI) ? TIOCM_RI : 0) | /* Ring Indicator is set */
350 ((msr & UART_MSR_DSR) ? TIOCM_DSR : 0); /* DSR is set */
352 return result;
355 static int fake_device_tiocmset(struct tty_struct *tty, struct file *file,
356 unsigned int set, unsigned int clear)
358 unsigned int mcr;
359 struct fake_device *fake;
361 fake = tty->driver_data;
362 if (!fake)
363 return -ENODEV;
365 mcr = fake->mcr;
367 if (set & TIOCM_RTS)
368 mcr |= UART_MCR_RTS;
369 if (set & TIOCM_DTR)
370 mcr |= UART_MCR_RTS;
372 if (clear & TIOCM_RTS)
373 mcr &= ~UART_MCR_RTS;
374 if (clear & TIOCM_DTR)
375 mcr &= ~UART_MCR_RTS;
377 fake->mcr = mcr;
379 return 0;
382 static struct tty_operations fake_device_ops = {
383 .open = fake_device_open,
384 .close = fake_device_close,
385 .write = fake_device_write,
386 .write_room = fake_device_write_room,
387 .ioctl = fake_device_ioctl,
388 .set_termios = fake_device_set_termios,
389 .throttle = fake_device_throttle,
390 .unthrottle = fake_device_unthrottle,
391 .break_ctl = fake_device_break_ctl,
392 .chars_in_buffer = fake_device_chars_in_buffer,
393 .tiocmget = fake_device_tiocmget,
394 .tiocmset = fake_device_tiocmset,
397 static int __init fake_device_init(void)
399 int i, err;
401 fake_device_pr_debug("Module is loading...\n");
403 fake_device_tty = alloc_tty_driver(FAKE_DEVICE_NR_DEVS);
404 if (!fake_device_tty)
405 return -ENOMEM;
407 fake_device_tty->owner = THIS_MODULE;
408 fake_device_tty->driver_name = "fake_device";
409 fake_device_tty->name = "ttyFAKEDEVICE";
410 fake_device_tty->major = FAKE_DEVICE_MAJOR;
411 fake_device_tty->minor_start = 0;
412 fake_device_tty->type = TTY_DRIVER_TYPE_SERIAL;
413 fake_device_tty->subtype = SERIAL_TYPE_NORMAL;
414 fake_device_tty->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
415 fake_device_tty->init_termios = tty_std_termios;
416 fake_device_tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
417 tty_set_operations(fake_device_tty, &fake_device_ops);
419 err = tty_register_driver(fake_device_tty);
420 if (err) {
421 printk(KERN_ALERT "Register failed\n");
422 put_tty_driver(fake_device_tty);
423 return err;
426 for (i = 0; i < FAKE_DEVICE_NR_DEVS; i++) {
427 fake_devices[i] = NULL;
428 tty_register_device(fake_device_tty, i, NULL);
431 fake_device_pr_debug("Loaded!!\n");
432 return 0;
435 static void __exit fake_device_exit(void)
437 int i;
439 for (i = 0; i < FAKE_DEVICE_NR_DEVS; i++)
440 tty_unregister_device(fake_device_tty, i);
441 tty_unregister_driver(fake_device_tty);
442 put_tty_driver(fake_device_tty);
443 fake_device_pr_debug("Exiting...\n");
446 module_init(fake_device_init);
447 module_exit(fake_device_exit);
449 MODULE_LICENSE("GPL");
450 MODULE_AUTHOR("Luiz Capitulino <lcapitulino@gmail.com>");
451 MODULE_DESCRIPTION("A fake serial device driver implementation");