了解UART串口驱动
来源:互联网 发布:淘宝曝光量是什么意思 编辑:程序博客网 时间:2024/05/02 02:18
在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_ttty_write(structfile *file,const char __user*buf,size_tcount,loff_t *ppos)
{
structinode *inode= file- >f_path.dentry- >d_inode;
structtty_struct *tty= file_tty(file);
structtty_ldisc *ld;
ssize_tret;
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);
returnret;
}
那么,又是谁充当线路规程的ops呢? 就是tty_ldisc_N_TTY (PATH:drivers/tty/n_tty.c),那么tty_write 将调用到n_tty_write
structtty_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_tn_tty_write(structtty_struct *tty,struct file*file,const unsigned char *buf,size_t nr)
{
const unsigned char *b= buf;
DECLARE_WAITQUEUE(wait,current);
intc;
ssize_tretval =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)
returnretval;
}
/ * 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_tnum =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 conststruct tty_operationsuart_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,
#ifdefCONFIG_PROC_FS
.proc_fops = &uart_proc_fops,
#endif
.tiocmget =uart_tiocmget,
.tiocmset =uart_tiocmset,
.get_icount =uart_get_icount,
#ifdefCONFIG_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(structtty_struct *tty,
const unsigned char *buf,int count)
{
structuart_state *state= tty- >driver_data;
structuart_port *port;
structcirc_buf *circ;
unsigned longflags;
intc,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)
return0;
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,分析见下*/
returnret;
}? end uart_write ?
static voiduart_start(structtty_struct *tty)
{
structuart_state *state= tty- >driver_data;
structuart_port *port= state- >uart_port;
unsigned longflags;
spin_lock_irqsave(&port- >lock, flags);
__uart_start(tty);
spin_unlock_irqrestore(&port- >lock, flags);
}
static void __uart_start(structtty_struct *tty)
{
structuart_state *state= tty- >driver_data;
structuart_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)(structuart_port *);
void(*set_mctrl)(structuart_port *,unsigned int mctrl);
unsigned int(*get_mctrl)(structuart_port *);
void(*stop_tx)(structuart_port *);
void(*start_tx)(structuart_port *);
void(*throttle)(structuart_port *);
void(*unthrottle)(structuart_port *);
void(*send_xchar)(structuart_port *,char ch);
void(*stop_rx)(structuart_port *);
void(*enable_ms)(structuart_port *);
void(*break_ctl)(structuart_port *,int ctl);
int(*startup)(structuart_port *);
void(*shutdown)(structuart_port *);
void(*flush_buffer)(structuart_port *);
void(*set_termios)(structuart_port *,struct ktermios *new,
structktermios *old);
void(*set_ldisc)(structuart_port *,int new);
void(*pm)(structuart_port *,unsigned int state,
unsigned intoldstate);
int(*set_wake)(structuart_port *,unsigned int state);
/ *
* Return a string describing the type of the port
*/
const char *(*type)(structuart_port *);
/ *
* Release IO and memory resources used by the port.
* This includes iounmap if necessary.
*/
void(*release_port)(structuart_port *);
/ *
* Request IO and memory resources used by the port.
* This includes iomapping the port if necessary.
*/
int (*request_port)(structuart_port *);
void (*config_port)(structuart_port *,int);
int (*verify_port)(structuart_port *,struct serial_struct *);
int (*ioctl)(structuart_port *,unsigned int,unsigned long);
#ifdef CONFIG_CONSOLE_POLL
int (*poll_init)(structuart_port *);
void (*poll_put_char)(structuart_port *,unsigned char);
int (*poll_get_char)(structuart_port *);
#endif
} ? end uart_ops ?;
- 了解UART串口驱动
- linux UART串口驱动
- S3C2440 UART串口驱动
- S3C2440 UART串口驱动
- S3C2440 UART串口驱动
- S3C2440 UART串口驱动
- uart串口驱动
- S3C2440 UART串口驱动
- S3C2440 UART串口驱动
- S3C2440 UART串口驱动
- linux UART串口驱动开发
- uart 串口控制器 驱动 s3c2410
- linux UART串口驱动开发文档
- linux UART串口驱动开发文档
- S3C2440 Linux UART 串口驱动-----1
- S3C2440 UART串口驱动(裸机)
- 如何编写串口(uart)驱动-基于linux310
- STM32F4XX串口高效驱动篇1-UART
- Deep Learning源代码收集
- 菜狗的C++ primer读书笔记:第一章 开始
- 简单的冒泡排序06
- uri个url的区别
- 长链剖分随想
- 了解UART串口驱动
- 素数回文
- 10分钟精通SharePoint - SharePoint升级
- 递归--九度1458.汉诺塔3
- 第28天(就业班) 自定义mvc框架、Struts入门及执行流程、环境搭建
- LCD显示时钟
- Deep Learning(深度学习)学习笔记整理系列之LeNet-5卷积参数个人理解
- 寒假作业
- MMAP使用