1.驅動程序定義:
struct uart_driver {
struct module *owner;/* 擁有該uart_driver的模塊,一般爲THIS_MODULE
*/
constchar*driver_name;/* 串口驅動名,串口設備文件名以驅動名爲基礎 */
constchar*dev_name;/* 串口設備名 */
int major;/* 主設備號 */
int minor;/* 次設備號 */
int nr;/* 該uart_driver支持的串口個數(最大)
*/
struct console *cons;/* 其對應的console.若該uart_driver支持serial
console,否則爲NULL */
/*
* these are private; the low level driver should not
* touch these; they should be initialised to NULL
*/
struct uart_state *state;
struct tty_driver *tty_driver;
};
static struct uart_driver sci_uart_driver = {
.owner = THIS_MODULE,
.driver_name = "sci",
.dev_name = "ttySC",
.major = SCI_MAJOR,
.minor = SCI_MINOR_START,
.nr = SCI_NPORTS,
.cons = SCI_CONSOLE,
};
2. 註冊驅動程序
int uart_register_driver(struct uart_driver *drv)
{
struct tty_driver *normal = NULL;
int i, retval;
BUG_ON(drv->state);
/*
* Maybe we should be using a slab cache for this, especially if
* we have a large number of ports to handle.
*/
drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
retval = -ENOMEM;
if (!drv->state)
goto out;
normal = alloc_tty_driver(drv->nr);
if (!normal)
goto out;
drv->tty_driver = normal;
normal->owner
= drv->owner;
normal->driver_name= drv->driver_name;
normal->name
= drv->dev_name;
normal->major
= drv->major;
normal->minor_start= drv->minor;
normal->type
= TTY_DRIVER_TYPE_SERIAL;
normal->subtype= SERIAL_TYPE_NORMAL;
normal->init_termios= tty_std_termios;
normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
normal->flags
= TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
normal->driver_state = drv;
tty_set_operations(normal, &uart_ops);
/*
* Initialise the UART state(s).
*/
for (i = 0; i < drv->nr; i++) {
struct uart_state *state = drv->state + i;
state->close_delay = 500;/* .5 seconds */
state->closing_wait = 30000;/* 30 seconds */
mutex_init(&state->mutex);
tty_port_init(&state->info.port);
init_waitqueue_head(&state->info.delta_msr_wait);
tasklet_init(&state->info.tlet, uart_tasklet_action,
(unsigned long)state);
}
retval = tty_register_driver(normal);
out:
if (retval < 0) {
put_tty_driver(normal);
kfree(drv->state);
}
return retval;
}
3.1 描述1個串口
struct uart_port {
spinlock_t
lock; /* port lock */
unsigned long
iobase; /* in/out[bwl] 基地址 */
unsigned char __iomem*membase;/* read/write[bwl] */
unsigned int
(*serial_in)(struct uart_port *, int);
void (*serial_out)(struct uart_port *, int, int);
unsigned int
irq; /* irq number */
unsigned int
uartclk; /* base uart clock */
unsigned int
fifosize; /* tx fifo size */
unsigned char
x_char; /* xon/xoff char */
unsigned char
regshift; /* reg offset shift */
unsigned char
iotype; /* io access style */
unsigned char
unused1;
#define UPIO_PORT (0)
#define UPIO_HUB6 (1)
#define UPIO_MEM (2)
#define UPIO_MEM32 (3)
#define UPIO_AU (4)/* Au1x00 type IO */
#define UPIO_TSI (5)/* Tsi108/109 type IO */
#define UPIO_DWAPB (6)/* DesignWare APB UART */
#define UPIO_RM9000 (7)/* RM9000 type IO */
unsigned int
read_status_mask; /* driver specific */
unsigned int
ignore_status_mask; /* driver specific */
struct uart_info*info;/* pointer to parent info */
struct uart_icounticount;/* statistics */
struct console*cons;/* struct console, if any */
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)
unsigned long
sysrq; /* sysrq timeout */
#endif
upf_t flags;
#define UPF_FOURPORT ((__force upf_t) (1 << 1))
#define UPF_SAK ((__force upf_t) (1 << 2))
#define UPF_SPD_MASK ((__force upf_t) (0x1030))
#define UPF_SPD_HI ((__force upf_t) (0x0010))
#define UPF_SPD_VHI ((__force upf_t) (0x0020))
#define UPF_SPD_CUST ((__force upf_t) (0x0030))
#define UPF_SPD_SHI ((__force upf_t) (0x1000))
#define UPF_SPD_WARP ((__force upf_t) (0x1010))
#define UPF_SKIP_TEST ((__force upf_t) (1 << 6))
#define UPF_AUTO_IRQ ((__force upf_t) (1 << 7))
#define UPF_HARDPPS_CD ((__force upf_t) (1 << 11))
#define UPF_LOW_LATENCY ((__force upf_t) (1 << 13))
#define UPF_BUGGY_UART ((__force upf_t) (1 << 14))
#define UPF_NO_TXEN_TEST ((__force upf_t) (1 << 15))
#define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16))
#define UPF_CONS_FLOW ((__force upf_t) (1 << 23))
#define UPF_SHARE_IRQ ((__force upf_t) (1 << 24))
/* The exact UART type is known and should not be probed. */
#define UPF_FIXED_TYPE ((__force upf_t) (1 << 27))
#define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28))
#define UPF_FIXED_PORT ((__force upf_t) (1 << 29))
#define UPF_DEAD ((__force upf_t) (1 << 30))
#define UPF_IOREMAP ((__force upf_t) (1 << 31))
#define UPF_CHANGE_MASK ((__force upf_t) (0x17fff))
#define UPF_USR_MASK ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))
unsigned int
mctrl; /* current modem ctrl settings */
unsigned int
timeout; /* character-based timeout */
unsigned int
type; /* port type */
const struct uart_ops*ops;
unsigned int
custom_divisor;
unsigned int
line; /* port index */
resource_size_tmapbase;/* for ioremap */
struct device
*dev; /* parent device */
unsigned char
hub6; /* this should be in the 8250 driver */
unsigned char
suspended;
unsigned char
unused[2];
void *private_data;/* generic platform data pointer */
};
3.2實現在uart_port 中的 uart_ops 對應的函數
static const struct tty_operations uart_ops = {
.open = uart_open,
.close
= uart_close,
.write
= uart_write,
.put_char
= uart_put_char,
.flush_chars
= uart_flush_chars,
.write_room
= uart_write_room,
.chars_in_buffer= uart_chars_in_buffer,
.flush_buffer
= uart_flush_buffer,
.ioctl
= uart_ioctl,
.throttle
= uart_throttle,
.unthrottle
= uart_unthrottle,
.send_xchar
= uart_send_xchar,
.set_termios
= uart_set_termios,
.set_ldisc
= uart_set_ldisc,
.stop = uart_stop,
.start
= uart_start,
.hangup
= uart_hangup,
.break_ctl
= uart_break_ctl,
.wait_until_sent= uart_wait_until_sent,
#ifdef CONFIG_PROC_FS
.proc_fops
= &uart_proc_fops,
#endif
.tiocmget
= uart_tiocmget,
.tiocmset
= uart_tiocmset,
#ifdef CONFIG_CONSOLE_POLL
.poll_init
= uart_poll_init,
.poll_get_char= uart_poll_get_char,
.poll_put_char= uart_poll_put_char,
#endif
};
4.添加串口到OS 輸入參數1 驅動程序 參數2 端口描述
int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
{
struct uart_state *state;
int ret = 0;
struct device *tty_dev;
BUG_ON(in_interrupt());
if (port->line >= drv->nr)
return -EINVAL;
state = drv->state + port->line;
mutex_lock(&port_mutex);
mutex_lock(&state->mutex);
if (state->port) {
ret = -EINVAL;
goto out;
}
state->port = port;
state->pm_state = -1;
port->cons = drv->cons;
port->info = &state->info;
/*
* If this port is a console, then the spinlock is already
* initialised.
*/
if (!(uart_console(port) && (port->cons->flags & CON_ENABLED))) {
spin_lock_init(&port->lock);
lockdep_set_class(&port->lock, &port_lock_key);
}
uart_configure_port(drv, state, port);
/*
* Register the port whether it's detected or not. This allows
* setserial to be used to alter this ports parameters.
*/
tty_dev = tty_register_device(drv->tty_driver, port->line, port->dev);
if (likely(!IS_ERR(tty_dev))) {
device_init_wakeup(tty_dev, 1);
device_set_wakeup_enable(tty_dev, 0);
} else
printk(KERN_ERR "Cannot register tty device on line %d\n",
port->line);
/*
* Ensure UPF_DEAD is not set.
*/
port->flags &= ~UPF_DEAD;
out:
mutex_unlock(&state->mutex);
mutex_unlock(&port_mutex);
return ret;
}