关于tty这部分请参考:
《Linux设备驱动开发详解 第二版》第14章 Linux终端设备驱动
《精通Linux设备驱动程序开发》第6章 串行设备驱动程序
《Linux设备驱动程序 第三版》第18章 TTY驱动程序
下面是一些串口相关的文档:
http://pan.baidu.com/s/1mg20Umc
Makefile:
# Comment/uncomment the following line to disable/enable debugging
#DEBUG = y # Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
else
DEBFLAGS = -O2
endif EXTRA_CFLAGS += $(DEBFLAGS)
EXTRA_CFLAGS += -I.. ifneq ($(KERNELRELEASE),)
# call from kernel build system obj-m := tiny_tty.o tiny_serial.o else #KERNELDIR ?= /lib/modules/$(shell uname -r)/build
KERNELDIR ?= /root/Tiny4412_android_4_1_2/linux-3.0.
PWD := $(shell pwd) default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules endif clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions depend .depend dep:
$(CC) $(EXTRA_CFLAGS) -M *.c > .depend ifeq (.depend,$(wildcard .depend))
include .depend
endif
tiny_tty.c
/*
* 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/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 <linux/sched.h>
#include <linux/seq_file.h>
#include <asm/uaccess.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 4 /* 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[] = {TINY_DATA_CHARACTER};
int data_size = ; 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 = ; i < data_size; ++i) {
if (!tty_buffer_request_room(tty, ))
tty_flip_buffer_push(tty);
tty_insert_flip_char(tty, data[i], TTY_NORMAL);
}
tty_flip_buffer_push(tty); /* 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; /* initialize the pointer in case something fails */
tty->driver_data = NULL; /* get the serial object associated with this tty pointer */
index = tty->index;
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; sema_init(&tiny->sem, );
tiny->open_count = ;
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 == ) {
/* this is the first time this port is opened */
/* do any hardware initialization needed here */ /* create our timer and submit it */
if (!tiny->timer) {
timer = kmalloc(sizeof(*timer), GFP_KERNEL);
if (!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;
add_timer(tiny->timer);
} up(&tiny->sem);
return ;
} 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 <= ) {
/* 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; down(&tiny->sem); 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 = ; i < count; ++i)
printk("%02x ", buffer[i]);
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; if (!tiny)
return -ENODEV; down(&tiny->sem); if (!tiny->open_count) {
/* port was not opened */
goto exit;
} /* calculate how much room is left in the device */
room = ; 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 ktermios *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 static int tiny_tiocmget(struct tty_struct *tty)
{
struct tiny_serial *tiny = tty->driver_data; unsigned int result = ;
unsigned int msr = tiny->msr;
unsigned int mcr = tiny->mcr; result = ((mcr & MCR_DTR) ? TIOCM_DTR : ) | /* DTR is set */
((mcr & MCR_RTS) ? TIOCM_RTS : ) | /* RTS is set */
((mcr & MCR_LOOP) ? TIOCM_LOOP : ) | /* LOOP is set */
((msr & MSR_CTS) ? TIOCM_CTS : ) | /* CTS is set */
((msr & MSR_CD) ? TIOCM_CAR : ) | /* Carrier detect is set*/
((msr & MSR_RI) ? TIOCM_RI : ) | /* Ring Indicator is set */
((msr & MSR_DSR) ? TIOCM_DSR : ); /* DSR is set */ return result;
} static int tiny_tiocmset(struct tty_struct *tty, unsigned int set,
unsigned int clear)
{
struct tiny_serial *tiny = tty->driver_data;
unsigned int mcr = tiny->mcr; if (set & TIOCM_RTS)
mcr |= MCR_RTS;
if (set & TIOCM_DTR)
mcr |= MCR_RTS; if (clear & TIOCM_RTS)
mcr &= ~MCR_RTS;
if (clear & TIOCM_DTR)
mcr &= ~MCR_RTS; /* set the new MCR value in the device */
tiny->mcr = mcr;
return ;
} static int tiny_proc_show(struct seq_file *m, void *v)
{
struct tiny_serial *tiny;
int i; seq_printf(m, "tinyserinfo:1.0 driver:%s\n", DRIVER_VERSION);
for (i = ; i < TINY_TTY_MINORS; ++i) {
tiny = tiny_table[i];
if (tiny == NULL)
continue; seq_printf(m, "%d\n", i);
} return ;
} #define tiny_ioctl tiny_ioctl_tiocgserial
static int tiny_ioctl(struct tty_struct *tty, unsigned int cmd,
unsigned long arg)
{
struct tiny_serial *tiny = tty->driver_data; if (cmd == TIOCGSERIAL) {
struct serial_struct tmp; if (!arg)
return -EFAULT; memset(&tmp, , sizeof(tmp)); tmp.type = tiny->serial.type;
tmp.line = tiny->serial.line;
tmp.port = tiny->serial.port;
tmp.irq = tiny->serial.irq;
tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
tmp.xmit_fifo_size = tiny->serial.xmit_fifo_size;
tmp.baud_base = tiny->serial.baud_base;
tmp.close_delay = *HZ;
tmp.closing_wait = *HZ;
tmp.custom_divisor = tiny->serial.custom_divisor;
tmp.hub6 = tiny->serial.hub6;
tmp.io_type = tiny->serial.io_type; if (copy_to_user((void __user *)arg, &tmp, sizeof(struct serial_struct)))
return -EFAULT;
return ;
}
return -ENOIOCTLCMD;
}
#undef tiny_ioctl #define tiny_ioctl tiny_ioctl_tiocmiwait
static int tiny_ioctl(struct tty_struct *tty, unsigned int cmd,
unsigned long arg)
{
struct tiny_serial *tiny = tty->driver_data; if (cmd == TIOCMIWAIT) {
DECLARE_WAITQUEUE(wait, current);
struct async_icount cnow;
struct async_icount cprev; cprev = tiny->icount;
while () {
add_wait_queue(&tiny->wait, &wait);
set_current_state(TASK_INTERRUPTIBLE);
schedule();
remove_wait_queue(&tiny->wait, &wait); /* see if a signal woke us up */
if (signal_pending(current))
return -ERESTARTSYS; cnow = tiny->icount;
if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
return -EIO; /* no change => error */
if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
return ;
}
cprev = cnow;
} }
return -ENOIOCTLCMD;
}
#undef tiny_ioctl #define tiny_ioctl tiny_ioctl_tiocgicount
static int tiny_ioctl(struct tty_struct *tty, unsigned int cmd,
unsigned long arg)
{
struct tiny_serial *tiny = tty->driver_data; if (cmd == TIOCGICOUNT) {
struct async_icount cnow = tiny->icount;
struct serial_icounter_struct icount; icount.cts = cnow.cts;
icount.dsr = cnow.dsr;
icount.rng = cnow.rng;
icount.dcd = cnow.dcd;
icount.rx = cnow.rx;
icount.tx = cnow.tx;
icount.frame = cnow.frame;
icount.overrun = cnow.overrun;
icount.parity = cnow.parity;
icount.brk = cnow.brk;
icount.buf_overrun = cnow.buf_overrun; if (copy_to_user((void __user *)arg, &icount, sizeof(icount)))
return -EFAULT;
return ;
}
return -ENOIOCTLCMD;
}
#undef tiny_ioctl /* the real tiny_ioctl function. The above is done to get the small functions in the book */
static int tiny_ioctl(struct tty_struct *tty, unsigned int cmd,
unsigned long arg)
{
switch (cmd) {
case TIOCGSERIAL:
return tiny_ioctl_tiocgserial(tty, cmd, arg);
case TIOCMIWAIT:
return tiny_ioctl_tiocmiwait(tty, cmd, arg);
case TIOCGICOUNT:
return tiny_ioctl_tiocgicount(tty, cmd, arg);
} return -ENOIOCTLCMD;
} static int tiny_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, tiny_proc_show, NULL);
} static const struct file_operations serial_proc_fops = {
.owner = THIS_MODULE,
.open = tiny_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
}; static struct tty_operations serial_ops = {
.open = tiny_open,
.close = tiny_close,
.write = tiny_write,
.write_room = tiny_write_room,
.set_termios = tiny_set_termios,
.proc_fops = &serial_proc_fops,
.tiocmget = tiny_tiocmget,
.tiocmset = tiny_tiocmset,
.ioctl = tiny_ioctl,
}; static struct tty_driver *tiny_tty_driver; static int __init tiny_init(void)
{
int retval;
int i; /* allocate the tty driver */
tiny_tty_driver = alloc_tty_driver(TINY_TTY_MINORS);
if (!tiny_tty_driver)
return -ENOMEM; /* initialize the tty driver */
tiny_tty_driver->owner = THIS_MODULE;
tiny_tty_driver->driver_name = "tiny_tty";
tiny_tty_driver->name = "ttty";
tiny_tty_driver->major = TINY_TTY_MAJOR,
tiny_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
tiny_tty_driver->subtype = SERIAL_TYPE_NORMAL,
tiny_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV,
tiny_tty_driver->init_termios = tty_std_termios;
tiny_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
tty_set_operations(tiny_tty_driver, &serial_ops); /* register the tty driver */
retval = tty_register_driver(tiny_tty_driver);
if (retval) {
printk(KERN_ERR "failed to register tiny tty driver");
put_tty_driver(tiny_tty_driver);
return retval;
} for (i = ; i < TINY_TTY_MINORS; ++i)
tty_register_device(tiny_tty_driver, i, NULL); printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION);
return retval;
} static void __exit tiny_exit(void)
{
struct tiny_serial *tiny;
int i; for (i = ; i < TINY_TTY_MINORS; ++i)
tty_unregister_device(tiny_tty_driver, i);
tty_unregister_driver(tiny_tty_driver); /* shut down all of the timers and free the memory */
for (i = ; i < TINY_TTY_MINORS; ++i) {
tiny = tiny_table[i];
if (tiny) {
/* close the port */
while (tiny->open_count)
do_close(tiny); /* shut down our timer and free the memory */
del_timer(tiny->timer);
kfree(tiny->timer);
kfree(tiny);
tiny_table[i] = NULL;
}
}
} module_init(tiny_init);
module_exit(tiny_exit);
tiny_serial.c
/*
* Tiny Serial 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 serial 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/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/module.h> #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
#define DRIVER_DESC "Tiny serial 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_SERIAL_MAJOR 240 /* experimental range */
#define TINY_SERIAL_MINORS 1 /* only have one minor */
#define UART_NR 1 /* only use one port */ #define TINY_SERIAL_NAME "ttytiny" #define MY_NAME TINY_SERIAL_NAME static struct timer_list *timer; static void tiny_stop_tx(struct uart_port *port)
{
} static void tiny_stop_rx(struct uart_port *port)
{
} static void tiny_enable_ms(struct uart_port *port)
{
} static void tiny_tx_chars(struct uart_port *port)
{
struct circ_buf *xmit = &port->state->xmit;
int count; if (port->x_char) {
pr_debug("wrote %2x", port->x_char);
port->icount.tx++;
port->x_char = ;
return;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
tiny_stop_tx(port);
return;
} count = port->fifosize >> ;
do {
pr_debug("wrote %2x", xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + ) & (UART_XMIT_SIZE - );
port->icount.tx++;
if (uart_circ_empty(xmit))
break;
} while (--count > ); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port); if (uart_circ_empty(xmit))
tiny_stop_tx(port);
} static void tiny_start_tx(struct uart_port *port)
{
} static void tiny_timer(unsigned long data)
{
struct uart_port *port;
struct tty_struct *tty; port = (struct uart_port *)data;
if (!port)
return;
if (!port->state)
return;
tty = port->state->port.tty;
if (!tty)
return; /* add one character to the tty port */
/* this doesn't actually push the data through unless tty->low_latency is set */
tty_insert_flip_char(tty, TINY_DATA_CHARACTER, ); tty_flip_buffer_push(tty); /* resubmit the timer again */
timer->expires = jiffies + DELAY_TIME;
add_timer(timer); /* see if we have any data to transmit */
tiny_tx_chars(port);
} static unsigned int tiny_tx_empty(struct uart_port *port)
{
return ;
} static unsigned int tiny_get_mctrl(struct uart_port *port)
{
return ;
} static void tiny_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
} static void tiny_break_ctl(struct uart_port *port, int break_state)
{
} static void tiny_set_termios(struct uart_port *port,
struct ktermios *new, struct ktermios *old)
{
int baud, quot, cflag = new->c_cflag;
/* 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: // CS8
printk(KERN_DEBUG " - data bits = 8\n");
break;
} /* determine the parity */
if (cflag & PARENB)
if (cflag & PARODD)
pr_debug(" - parity = odd\n");
else
pr_debug(" - parity = even\n");
else
pr_debug(" - parity = none\n"); /* figure out the stop bits requested */
if (cflag & CSTOPB)
pr_debug(" - stop bits = 2\n");
else
pr_debug(" - stop bits = 1\n"); /* figure out the flow control settings */
if (cflag & CRTSCTS)
pr_debug(" - RTS/CTS is enabled\n");
else
pr_debug(" - RTS/CTS is disabled\n"); /* Set baud rate */
baud = uart_get_baud_rate(port, new, old, , port->uartclk/);
quot = uart_get_divisor(port, baud); //UART_PUT_DIV_LO(port, (quot & 0xff));
//UART_PUT_DIV_HI(port, ((quot & 0xf00) >> 8));
} static int tiny_startup(struct uart_port *port)
{
/* this is the first time this port is opened */
/* do any hardware initialization needed here */ /* create our timer and submit it */
if (!timer) {
timer = kmalloc(sizeof(*timer), GFP_KERNEL);
if (!timer)
return -ENOMEM;
}
init_timer(timer);
timer->data = (unsigned long)port;
timer->expires = jiffies + DELAY_TIME;
timer->function = tiny_timer;
add_timer(timer);
return ;
} static void tiny_shutdown(struct uart_port *port)
{
/* The port is being closed by the last user. */
/* Do any hardware specific stuff here */ /* shut down our timer */
del_timer(timer);
} static const char *tiny_type(struct uart_port *port)
{
return "tinytty";
} static void tiny_release_port(struct uart_port *port)
{ } static int tiny_request_port(struct uart_port *port)
{
return ;
} static void tiny_config_port(struct uart_port *port, int flags)
{
} static int tiny_verify_port(struct uart_port *port, struct serial_struct *ser)
{
return ;
} static struct uart_ops tiny_ops = {
.tx_empty = tiny_tx_empty,
.set_mctrl = tiny_set_mctrl,
.get_mctrl = tiny_get_mctrl,
.stop_tx = tiny_stop_tx,
.start_tx = tiny_start_tx,
.stop_rx = tiny_stop_rx,
.enable_ms = tiny_enable_ms,
.break_ctl = tiny_break_ctl,
.startup = tiny_startup,
.shutdown = tiny_shutdown,
.set_termios = tiny_set_termios,
.type = tiny_type,
.release_port = tiny_release_port,
.request_port = tiny_request_port,
.config_port = tiny_config_port,
.verify_port = tiny_verify_port,
}; static struct uart_port tiny_port = {
.ops = &tiny_ops,
}; static struct uart_driver tiny_reg = {
.owner = THIS_MODULE,
.driver_name = TINY_SERIAL_NAME,
.dev_name = TINY_SERIAL_NAME,
.major = TINY_SERIAL_MAJOR,
.minor = TINY_SERIAL_MINORS,
.nr = UART_NR,
}; static int __init tiny_init(void)
{
int result; printk(KERN_INFO "Tiny serial driver loaded\n"); result = uart_register_driver(&tiny_reg);
if (result)
return result; result = uart_add_one_port(&tiny_reg, &tiny_port);
if (result)
uart_unregister_driver(&tiny_reg); return result;
} module_init(tiny_init);