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
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
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)
20 * 5. Change the driver to act as a real modem
21 * 6. Moves some configurable values (like debug and number of devices) as
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 '%'
49 struct tty_struct
*tty
;
50 struct timer_list timer
;
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); \
68 static char *fake_device_commands
[] = {
75 static char *fake_device_responses
[] = {
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
,
91 room
= tty_buffer_request_room(tty
, buf_len
);
93 printk(KERN_ALERT
"%s: No memory available!\n", __FUNCTION__
);
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
)
104 for_each_ibuf_char(p
, fake
)
105 fake_device_pr_debug("--> (%lu) 0x%X %c\n", p
- fake
->ibuf
,
108 fake_device_pr_debug("---> len: %lu | idx: %d\n", fake
->len
,
112 static void fake_device_handler(unsigned long data
)
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
) {
129 /* No end of command has been found */
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
);
148 resp
= fake_device_error_resp
;
149 fake_device_pr_debug("---> result: %s\n", resp
);
153 fill_tty_buffer(fake
->tty
, resp
, strlen(resp
));
155 fake
->len
= FAKE_DEVICE_IBUF_LEN
;
158 static int fake_device_open(struct tty_struct
*tty
, struct file
*filp
)
162 struct fake_device
*fake
;
164 down(&fake_device_mutex
);
168 fake
= fake_devices
[idx
];
170 fake
= kmalloc(sizeof(struct fake_device
), GFP_KERNEL
);
172 printk(KERN_ALERT
"Couldn't alloc a fake_device\n");
177 fake
->len
= FAKE_DEVICE_IBUF_LEN
;
178 fake
->ibuf
= kmalloc(fake
->len
, GFP_KERNEL
);
180 printk(KERN_ALERT
"Couldn't alloc ibuf\n");
185 fake
->open_count
= 0;
186 init_timer(&fake
->timer
);
187 fake
->timer
.data
= (unsigned long) fake
;
188 fake
->timer
.function
= fake_device_handler
;
190 fake
->msr
= fake
->mcr
= 0;
191 fake_devices
[idx
] = fake
;
192 tty
->driver_data
= fake
;
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
);
201 up(&fake_device_mutex
);
205 static void fake_device_close(struct tty_struct
*tty
, struct file
*filp
)
208 struct fake_device
*fake
;
210 down(&fake_device_mutex
);
213 fake
= tty
->driver_data
;
216 BUG_ON(fake
->open_count
< 0);
217 if (!fake
->open_count
) {
218 del_timer(&fake
->timer
);
221 fake_devices
[idx
] = NULL
;
222 tty
->driver_data
= NULL
;
223 fake_device_pr_debug("Device %d closed forever\n", idx
);
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
,
235 struct fake_device
*fake
;
237 fake
= tty
->driver_data
;
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
) {
247 fake
->idx
+= fake
->len
;
255 fake
->timer
.expires
= jiffies
+ FAKE_DEVICE_DELAY
;
256 add_timer(&fake
->timer
);
261 static int fake_device_write_room(struct tty_struct
*tty
)
263 struct fake_device
*fake
;
265 fake
= tty
->driver_data
;
272 static int fake_device_ioctl(struct tty_struct
*tty
, struct file
*file
,
273 unsigned int cmd
, unsigned long arg
)
278 static void fake_device_set_termios(struct tty_struct
*tty
, struct termios
*old
)
280 fake_device_pr_debug("");
284 static void fake_device_throttle(struct tty_struct
*tty
)
286 struct fake_device
*fake
;
288 fake
= tty
->driver_data
;
292 printk(KERN_ALERT
"%s: called for minor %d\n", __FUNCTION__
,
297 static void fake_device_unthrottle(struct tty_struct
*tty
)
299 struct fake_device
*fake
;
301 fake
= tty
->driver_data
;
305 printk(KERN_ALERT
"%s: called for minor %d\n", __FUNCTION__
,
310 static void fake_device_break_ctl(struct tty_struct
*tty
, int break_state
)
312 struct fake_device
*fake
;
314 fake
= tty
->driver_data
;
318 printk(KERN_ALERT
"%s: sending BREAK to device %d\n", __FUNCTION__
,
323 static int fake_device_chars_in_buffer(struct tty_struct
*tty
)
326 * That's our bogus implementation, we never have queued data
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
;
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 */
355 static int fake_device_tiocmset(struct tty_struct
*tty
, struct file
*file
,
356 unsigned int set
, unsigned int clear
)
359 struct fake_device
*fake
;
361 fake
= tty
->driver_data
;
372 if (clear
& TIOCM_RTS
)
373 mcr
&= ~UART_MCR_RTS
;
374 if (clear
& TIOCM_DTR
)
375 mcr
&= ~UART_MCR_RTS
;
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)
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
)
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
);
421 printk(KERN_ALERT
"Register failed\n");
422 put_tty_driver(fake_device_tty
);
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");
435 static void __exit
fake_device_exit(void)
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");