Linux串口添加過程


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;
}

發佈了28 篇原創文章 · 獲贊 4 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章