/* * Tiny TTY driver * * Copyright (C) 2002-2004 Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2 of the License. * * This driver shows how to create a minimal tty driver. It does not rely on * any backing hardware, but creates a timer that emulates data being received * from some kind of hardware. */
//#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/wait.h> #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/serial.h> #include <asm/uaccess.h> #include <linux/timer.h>
#define DRIVER_VERSION "v2.0" #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>" #define DRIVER_DESC "Tiny TTY driver"
/* Module information */ MODULE_AUTHOR( DRIVER_AUTHOR ); MODULE_DESCRIPTION( DRIVER_DESC ); MODULE_LICENSE("GPL");
#define DELAY_TIME (HZ*2) /* 2 seconds per character */ #define TINY_DATA_CHARACTER 't'
#define TINY_TTY_MAJOR 240 /* experimental range */ #define TINY_TTY_MINORS 1 /* only have 4 devices */
struct tiny_serial { struct tty_struct *tty; /* pointer to the tty for this device */ int open_count; /* number of times this port has been opened */ struct semaphore sem; /* locks this structure */ struct timer_list *timer;
/* for tiocmget and tiocmset functions */ int msr; /* MSR shadow */ int mcr; /* MCR shadow */
/* for ioctl fun */ struct serial_struct serial; wait_queue_head_t wait; struct async_icount icount; };
static struct tiny_serial *tiny_table[TINY_TTY_MINORS]; /* initially all NULL */
static void tiny_timer(unsigned long timer_data) {
struct tiny_serial *tiny = (struct tiny_serial *)timer_data; struct tty_struct *tty; int i; char data[14] = "hello,world "; char c_enter = '\n'; int data_size = 12;
if (!tiny) return;
tty = tiny->tty;
// send the data to the tty layer for users to read. This doesn't // actually push the data through unless tty->low_latency is set for (i = 0; i < data_size; ++i) { // if (tty->flip.count >= TTY_FLIPBUF_SIZE) // tty_flip_buffer_push(tty); tty_insert_flip_char(tty, data, TTY_NORMAL); } tty_insert_flip_char(tty, '\n', TTY_NORMAL); // printk ("tty push buffer \n"); tty_flip_buffer_push(tty); // printk ("tty end buffer \n");
// resubmit the timer again tiny->timer->expires = jiffies + DELAY_TIME; add_timer(tiny->timer); /* //test timer static int i; struct tiny_serial *tiny = (struct tiny_serial *)timer_data;
++i; printk ("tiny timer i=%d\n",i); // resubmit the timer again tiny->timer->expires = jiffies + DELAY_TIME; add_timer(tiny->timer); */ }
static int tiny_open(struct tty_struct *tty, struct file *file) { struct tiny_serial *tiny; struct timer_list *timer; int index; if (NULL == tty) { printk ("tty == NULL \n"); return -1; }
/* initialize the pointer in case something fails */ tty->driver_data = NULL;
/* get the serial object associated with this tty pointer */ index = 0; tiny = tiny_table[index]; if (tiny == NULL) { /* first time accessing this device, let's create it */ tiny = kmalloc(sizeof(*tiny), GFP_KERNEL); if (!tiny) return -ENOMEM;
init_MUTEX(&tiny->sem); tiny->open_count = 0; tiny->timer = NULL;
tiny_table[index] = tiny; }
down(&tiny->sem);
/* save our structure within the tty structure */ tty->driver_data = tiny; tiny->tty = tty;
++tiny->open_count; if (tiny->open_count == 1) { /* this is the first time this port is opened */ /* do any hardware initialization needed here */
/* create our timer and submit it */ printk ("open 3 \n"); if (NULL == tiny->timer) { timer = kmalloc(sizeof(*timer), GFP_KERNEL); if (NULL == timer) { up(&tiny->sem); return -ENOMEM; } tiny->timer = timer; } init_timer(tiny->timer); //擦,需要添加这个才行。 tiny->timer->data = (unsigned long )tiny; tiny->timer->expires = jiffies + DELAY_TIME; tiny->timer->function = tiny_timer; printk ("open 4 \n"); add_timer(tiny->timer); printk ("open 5 \n");
}
up(&tiny->sem); return 0; }
static void do_close(struct tiny_serial *tiny) { down(&tiny->sem);
if (!tiny->open_count) { /* port was never opened */ goto exit; }
--tiny->open_count; if (tiny->open_count <= 0) { /* The port is being closed by the last user. */ /* Do any hardware specific stuff here */
/* shut down our timer */ del_timer(tiny->timer); } exit: up(&tiny->sem); }
static void tiny_close(struct tty_struct *tty, struct file *file) { struct tiny_serial *tiny = tty->driver_data;
if (tiny) do_close(tiny); }
static int tiny_write(struct tty_struct *tty, const unsigned char *buffer, int count) { struct tiny_serial *tiny = tty->driver_data; int i; int retval = -EINVAL;
if (!tiny) return -ENODEV;
if (!tiny->open_count) /* port was not opened */ goto exit;
/* fake sending the data out a hardware port by * writing it to the kernel debug log. */ printk(KERN_DEBUG "%s - ", __FUNCTION__); for (i = 0; i < count; ++i) printk("%02x ", buffer); printk("\n"); exit: up(&tiny->sem); return retval; }
static int tiny_write_room(struct tty_struct *tty) { struct tiny_serial *tiny = tty->driver_data; int room = -EINVAL;
down(&tiny->sem); if (!tiny->open_count) { /* port was not opened */ goto exit; }
/* calculate how much room is left in the device */ room = 255;
exit: up(&tiny->sem); return room; }
#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
static void tiny_set_termios(struct tty_struct *tty, struct termios *old_termios) { unsigned int cflag;
cflag = tty->termios->c_cflag;
/* check that they really want us to change something */ if (old_termios) { if ((cflag == old_termios->c_cflag) && (RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) { printk(KERN_DEBUG " - nothing to change...\n"); return; } }
/* get the byte size */ switch (cflag & CSIZE) { case CS5: printk(KERN_DEBUG " - data bits = 5\n"); break; case CS6: printk(KERN_DEBUG " - data bits = 6\n"); break; case CS7: printk(KERN_DEBUG " - data bits = 7\n"); break; default: case CS8: printk(KERN_DEBUG " - data bits = 8\n"); break; } /* determine the parity */ if (cflag & PARENB) if (cflag & PARODD) printk(KERN_DEBUG " - parity = odd\n"); else printk(KERN_DEBUG " - parity = even\n"); else printk(KERN_DEBUG " - parity = none\n");
/* figure out the stop bits requested */ if (cflag & CSTOPB) printk(KERN_DEBUG " - stop bits = 2\n"); else printk(KERN_DEBUG " - stop bits = 1\n");
/* figure out the hardware flow control settings */ if (cflag & CRTSCTS) printk(KERN_DEBUG " - RTS/CTS is enabled\n"); else printk(KERN_DEBUG " - RTS/CTS is disabled\n"); /* determine software flow control */ /* if we are implementing XON/XOFF, set the start and * stop character in the device */ if (I_IXOFF(tty) || I_IXON(tty)) { unsigned char stop_char = STOP_CHAR(tty); unsigned char start_char = START_CHAR(tty);
/* if we are implementing INBOUND XON/XOFF */ if (I_IXOFF(tty)) printk(KERN_DEBUG " - INBOUND XON/XOFF is enabled, " "XON = %2x, XOFF = %2x", start_char, stop_char); else printk(KERN_DEBUG" - INBOUND XON/XOFF is disabled");
/* if we are implementing OUTBOUND XON/XOFF */ if (I_IXON(tty)) printk(KERN_DEBUG" - OUTBOUND XON/XOFF is enabled, " "XON = %2x, XOFF = %2x", start_char, stop_char); else printk(KERN_DEBUG" - OUTBOUND XON/XOFF is disabled"); }
/* get the baud rate wanted */ printk(KERN_DEBUG " - baud rate = %d", tty_get_baud_rate(tty)); }
/* Our fake UART values */ #define MCR_DTR 0x01 #define MCR_RTS 0x02 #define MCR_LOOP 0x04 #define MSR_CTS 0x08 #define MSR_CD 0x10 #define MSR_RI 0x20 #define MSR_DSR 0x40
module_exit(tiny_exit);
发布
文章评论(0条评论)
登录后参与讨论