了解UART串口驱动
在Linux中,UART属于TTY设备三大类别中的串口,所以UART驱动完全遵循TTY驱动的架构。但事实上,Linux内核在TTY驱动框架下又封装一层UART驱动。内核中用uart_driver来表示一个UART驱动。因此在学习串口驱动前需要了解两个基本的框架结构,tty框架和uart框架。 首先看看tty框架: 最上面的用户空间会有很多对底层硬件的操作,像read,write等。
在Linux中,UART属于TTY设备三大类别中的串口,所以UART驱动完全遵循TTY驱动的架构。但事实上,Linux内核在TTY驱动框架下又封装一层UART驱动。内核中用uart_driver来表示一个UART驱动。因此在学习串口驱动前需要了解两个基本的框架结构,tty框架和uart框架。
首先看看tty框架:
最上面的用户空间会有很多对底层硬件的操作,像read,write等。用户空间主要是通过设备文件同tty_core交互,tty_core根据用空间操作的类型再选择跟line discipline和tty_driver也就是serial_core交互,例如设置硬件的ioctl指令就直接交给serial_core处理。Read和write操作就会交给line discipline处理。Line discipline是线路规程的意思。正如它的名字一样,它表示的是这条终端”线程”的输入与输出规范设置,主要用来进行输入/输出数据的预处理。处理之后,就会将数据交给serial_core,最后serial_core会调用底层的操作。
下面来看看uart框架图:
uart_driver用来表示串口设备驱动(统称),uart_state用来表示一个具体的串口设备,例如:/dev/ttyS0 /dev/ttyS1 每个设备节点是对应一个具体硬件的(uart_state), uart_port用来表示设备的具体信息,包括设备的控制和状态信息。tty(tty_port)是从tty核心层的角度来表示一个串口设备,其中就封装了tty核心层要用到的数据结构tty_struct 。xmit是数据发送缓冲区,用来接收上层用户传递过来的数据。
在uart驱动注册时(uart_register_driver),会将uart设备(uart_driver)注册进tty层(后面再来分析具体代码),同时将tty_driver的操作集设为了uart_ops,使得从用户空间下来的操作可以找到正确的serial_core的操作函数。
接下来简单介绍下串口的一次工作:
当用户打开串口设备,向串口写入数据时调用write(),write()通过系统调用找到file_operations 里的 .write 函数指针,充当我们 .write 的函数是 tty_write() ,(PATH:/driver/tty/tty_io.c)
static const struct file_operations tty_fops = {
.llseek= no_llseek,
.read= tty_read,
.write= tty_write,
.poll= tty_poll,
.unlocked_ioctl= tty_ioctl,
.compat_ioctl= tty_compat_ioctl,
.open= tty_open,
.release= tty_release,
.fasync= tty_fasync,
};
2. 由tty_write找到线路规程的ops(tty_ldisc_ops),并且调用线路规程ops的 (.write ) do_tty_write(ld- >ops- >write, tty, file, buf, count)
static ssize_t tty_write(struct file *file, const char __user *buf,size_t count, loff_t *ppos)
{
struct inode *inode = file- >f_path.dentry- >d_inode;
struct tty_struct *tty = file_tty(file);
struct tty_ldisc *ld;
ssize_t ret;
if (tty_paranoia_check(tty, inode, "tty_write"))
return - EIO;
if (! tty | | ! tty- >ops- >write | |
(test_bit(TTY_IO_ERROR, &tty- >flags)))
return - EIO;
/ * Short term debug to catch buggy drivers */
if (tty- >ops- >write_room == NULL)
printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
tty- >driver- >name);
ld = tty_ldisc_ref_wait(tty);
if (! ld- >ops- >write)
ret = - EIO;
else
ret = do_tty_write(ld- >ops- >write, tty, file, buf, count);
tty_ldisc_deref(ld);
return ret;
}
那么,又是谁充当线路规程的ops呢? 就是tty_ldisc_N_TTY (PATH:drivers/tty/n_tty.c),那么tty_write 将调用到n_tty_write
struct tty_ldisc_ops tty_ldisc_N_TTY = {
.magic = TTY_LDISC_MAGIC,
.name = "n_tty",
.open = n_tty_open,
.close = n_tty_close,
.flush_buffer = n_tty_flush_buffer,
.chars_in_buffer = n_tty_chars_in_buffer,
.read = n_tty_read,
.write = n_tty_write,
.ioctl = n_tty_ioctl,
.set_termios = n_tty_set_termios,
.poll = n_tty_poll,
.receive_buf = n_tty_receive_buf,
.write_wakeup = n_tty_write_wakeup
};
由 n_tty_write 调用到tty_operatios里的write函数:
static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,const unsigned char *buf, size_t nr)
{
const unsigned char *b = buf;
DECLARE_WAITQUEUE(wait, current);
int c;
ssize_t retval = 0;
/ * Job control check - - must be done at start (POSIX.1 7.1.1.4). */
if (L_TOSTOP(tty) && file- >f_op- >write ! = redirected_tty_write)
{
retval = tty_check_change(tty);
if (retval)
return retval;
}
/ * Write out any echoed characters that are still pending */
process_echoes(tty);
add_wait_queue(&tty- >write_wait, &wait);
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
if (signal_pending(current)) {
retval = - ERESTARTSYS;
break;
}
if (tty_hung_up_p(file) | | (tty- >link && ! tty- >link- >count)) {
retval = - EIO;
break;
}
if (O_OPOST(tty) && ! (test_bit(TTY_HW_COOK_OUT, &tty- >
flags))) {
while (nr > 0) {
ssize_t num = process_output_block(tty, b, nr);
if (num < 0) {
if (num == - EAGAIN)
break;
retval = num;
goto ¯break_out;
}
b += num;
nr - = num;
if (nr == 0)
break;
c = *b;
if (process_output(c, tty) < 0)
break;
b++; nr- - ;
}
if (tty- >ops- >flush_chars)
tty- >ops- >flush_chars(tty);
} else {
while (nr > 0) {
c = tty- >ops- >write(tty, b, nr);
if (c < 0) {
retval = c;
goto ¯break_out;
}
if (! c)
break;
b += c;
nr - = c;
}
}
if (! nr)
break;
if (file- >f_flags & O_NONBLOCK) {
retval = - EAGAIN;
break;
}
schedule();
} ? end while 1 ?
break_out:
__set_current_state(TASK_RUNNING);
remove_wait_queue(&tty- >write_wait, &wait);
if (b - buf ! = nr && tty- >fasync)
set_bit(TTY_DO_WRITE_WAKEUP, &tty- >flags);
return (b - buf) ? b - buf : retval;
}
那么,又是谁充当tty_operations的ops呢? uart_ops,此操作集在串口驱动注册时指定,注册进tty框架中。,由uart_ops找到uart_write函数。
(PATH:/driver/tty/serial/serial_core.c)
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,
.get_icount = uart_get_icount,
#ifdef CONFIG_CONSOLE_POLL
.poll_init = uart_poll_init,
.poll_get_char = uart_poll_get_char,
.poll_put_char = uart_poll_put_char,
#endif
};
下面分析下uart_write函数做了什么。首先从tty- >driver_data中取得uart_state *state,然后从*state中取得uart_port *port,然后从*port中拿到了ops,实现了对硬件层的驱动。
static int uart_write(struct tty_struct *tty,
const unsigned char *buf, int count)
{
struct uart_state *state = tty- >driver_data;
struct uart_port *port;
struct circ_buf *circ;
unsigned long flags;
int c, ret = 0;
/ *
* This means you called this function _after_ the port was
* closed. No cookie for you.
*/
if (! state) {
WARN_ON(1);
return - EL3HLT;
}
port = state- >uart_port;
circ = &state- >xmit;
if (! circ- >buf)
return 0;
spin_lock_irqsave(&port- >lock, flags);
while (1) {
c = CIRC_SPACE_TO_END(circ- >head, circ- >tail,
UART_XMIT_SIZE);
if (count < c)
c = count;
if (c <= 0)
break;
memcpy(circ- >buf + circ- >head, buf, c);
circ- >head = (circ- >head + c) & (UART_XMIT_SIZE - 1);
buf += c;
count - = c;
ret += c;
}
spin_unlock_irqrestore(&port- >lock, flags);
uart_start(tty);/*->由此处拿到port中的ops,分析见下*/
return ret;
} ? end uart_write ?
static void uart_start(struct tty_struct *tty)
{
struct uart_state *state = tty- >driver_data;
struct uart_port *port = state- >uart_port;
unsigned long flags;
spin_lock_irqsave(&port- >lock, flags);
__uart_start(tty);
spin_unlock_irqrestore(&port- >lock, flags);
}
static void __uart_start(struct tty_struct *tty)
{
struct uart_state *state = tty- >driver_data;
struct uart_port *port = state- >uart_port;
if (! uart_circ_empty(&state- >xmit) && state- >xmit.buf &&
! tty- >stopped && ! tty- >hw_stopped)
port- >ops- >start_tx(port); /*到此便完成了一次串口写入*/
}
那么,此处的ops类型:
struct uart_ops {
unsigned int (*tx_empty)(struct uart_port *);
void (*set_mctrl)(struct uart_port *, unsigned int mctrl);
unsigned int (*get_mctrl)(struct uart_port *);
void (*stop_tx)(struct uart_port *);
void (*start_tx)(struct uart_port *);
void (*throttle)(struct uart_port *);
void (*unthrottle)(struct uart_port *);
void (*send_xchar)(struct uart_port *, char ch);
void (*stop_rx)(struct uart_port *);
void (*enable_ms)(struct uart_port *);
void (*break_ctl)(struct uart_port *, int ctl);
int (*startup)(struct uart_port *);
void (*shutdown)(struct uart_port *);
void (*flush_buffer)(struct uart_port *);
void (*set_termios)(struct uart_port *, struct ktermios *new,
struct ktermios *old);
void (*set_ldisc)(struct uart_port *, int new);
void (*pm)(struct uart_port *, unsigned int state,
unsigned int oldstate);
int (*set_wake)(struct uart_port *, unsigned int state);
/ *
* Return a string describing the type of the port
*/
const char *(*type)(struct uart_port *);
/ *
* Release IO and memory resources used by the port.
* This includes iounmap if necessary.
*/
void (*release_port)(struct uart_port *);
/ *
* Request IO and memory resources used by the port.
* This includes iomapping the port if necessary.
*/
int (*request_port)(struct uart_port *);
void (*config_port)(struct uart_port *, int);
int (*verify_port)(struct uart_port *, struct serial_struct *);
int (*ioctl)(struct uart_port *, unsigned int, unsigned long);
#ifdef CONFIG_CONSOLE_POLL
int (*poll_init)(struct uart_port *);
void (*poll_put_char)(struct uart_port *, unsigned char);
int (*poll_get_char)(struct uart_port *);
#endif
} ? end uart_ops ? ;
更多推荐
所有评论(0)