当前位置: 首页 > news >正文

一般做公司网站需要哪几点国外网站做网上生意哪个好

一般做公司网站需要哪几点,国外网站做网上生意哪个好,学手机网站建设,磁器口网站建设写在前面#xff1a;我们没有讲UART驱动#xff0c;不过我们认为#xff0c;只要系统学习了第2期#xff0c;应该具备分析UART驱动的能力#xff0c;小编做答疑几年以来#xff0c;陆陆续续有不少人问到UART驱动怎么写#xff0c;所以今天就分享一篇深度长文(17000字我们没有讲UART驱动不过我们认为只要系统学习了第2期应该具备分析UART驱动的能力小编做答疑几年以来陆陆续续有不少人问到UART驱动怎么写所以今天就分享一篇深度长文(17000字阅读时间43分钟)作者是我们的答疑助手lizuobin涉及很多数据结构为了看懂本文特意打开source insight 跟踪了代码你也应该这样如果你的代码不一样那或许linux版本不一样。作者lizuobin原文(有些许修正)https://blog.csdn.net/lizuobin2/article/details/51773305本文参考了大量牛人的博客对大神的分享表示由衷的感谢。主要参考Linux TTY驱动--Uart_driver底层http://blog.csdn.net/sharecode/article/details/9196591Linux TTY驱动--Serial Core层 http://blog.csdn.net/sharecode/article/details/9197567前面学习过了 i2c、spi这俩都是基于设备总线驱动模型分析起来相对比较简单今天打算迎难而上学习一下 Uart 驱动因为它涉及了tty 、线路规程确实有些难度幸好有万能的互联网让我可以学习大神们的博客。一天下来总算有些收获下面总结一下主要是框架。整个uart 框架大概如上图所示简单来分的话可以说成两层一层是下层我们的串口驱动层它直接与硬件接触我们需要填充一个 struct uart_ops 的结构体另一层是上层 tty 层包括 tty 核心以及线路规程它们各自都有一个 Ops 结构用户空间通过 tty 注册的字符设备节点来访问这么说来如上图所示涉及到了4个 ops 结构了层层跳转。下面就来分析分析它们的层次结构。在 s3c2440平台它是这样来注册串口驱动的分配一个struct uart_driver 简单填充并调用uart_register_driver 注册到内核中去。static struct uart_driver s3c24xx_uart_drv {.owner THIS_MODULE,.dev_name s3c2410_serial,.nr CONFIG_SERIAL_SAMSUNG_UARTS,.cons S3C24XX_SERIAL_CONSOLE,.driver_name S3C24XX_SERIAL_NAME,.major S3C24XX_SERIAL_MAJOR,.minor S3C24XX_SERIAL_MINOR, }; static int __init s3c24xx_serial_modinit(void) {int ret;ret uart_register_driver(s3c24xx_uart_drv);if (ret 0) {printk(KERN_ERR failed to register UART drivern);return -1;}return 0; } uart_driver 中我们只是填充了一些名字、设备号等信息这些都是不涉及底层硬件访问的到底怎么回事呢来看一下完整的 uart_driver 结构或许就明白了。struct uart_driver {struct module *owner; /* 拥有该uart_driver的模块,一般为THIS_MODULE */const char *driver_name; /* 串口驱动名串口设备文件名以驱动名为基础 */const char *dev_name; /* 串口设备名 */int major; /* 主设备号 */int minor; /* 次设备号 */int nr; /* 该uart_driver支持的串口个数(最大) */struct console *cons; /* 其对应的console.若该uart_driver支持serial console,否则为NULL *//* 下面这俩它们应该被初始化为NULL */struct uart_state *state; span stylewhite-space:pre /span/* 下层串口驱动层 */struct tty_driver *tty_driver; /* tty相关 */ }; 在我们上边填充的结构体中有两个成员未被赋值对于tty_driver 代表的是上层它会在 uart_register_driver中的过程中赋值而uart_state 则代表下层uart_state 也会在uart_register_driver的过程中分配空间但是它里面真正设置硬件相关的东西是 uart_state-uart_port 这个uart_port 是需要我们从其它地方调用 uart_add_one_port 来添加的。 1、下层串口驱动层首先我们需要认识这几个结构体struct uart_state {struct tty_port port;int pm_state;struct circ_buf xmit;struct tasklet_struct tlet;struct uart_port *uart_port; // 对应于一个串口设备 }; 在注册 driver 时会根据 uart_driver-nr 来申请 nr 个 uart_state 空间用来存放驱动所支持的串口端口的物理信息。 struct uart_port {spinlock_t lock; /* port lock */unsigned long iobase; /* io端口基地址物理 */unsigned char __iomem *membase; /* io内存基地址虚拟 */unsigned int (*serial_in)(struct uart_port *, int);void (*serial_out)(struct uart_port *, int, int);unsigned int irq; /* 中断号 */unsigned long irqflags; /* 中断标志 */unsigned int uartclk; /* 串口时钟 */unsigned int fifosize; /* 串口缓冲区大小 */unsigned char x_char; /* xon/xoff char */unsigned char regshift; /* 寄存器位移 */unsigned char iotype; /* IO访问方式 */unsigned char unused1;unsigned int read_status_mask; /* 关心 Rx error status */unsigned int ignore_status_mask; /* 忽略 Rx error status */struct uart_state *state; /* pointer to parent state */struct uart_icount icount; /* 串口信息计数器 */struct console *cons; /* struct console, if any */ #if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)unsigned long sysrq; /* sysrq timeout */ #endifupf_t flags;unsigned int mctrl; /* 当前的Moden 设置 */unsigned int timeout; /* character-based timeout */unsigned int type; /* 端口类型 */const struct uart_ops *ops; /* 串口端口操作函数 */unsigned int custom_divisor;unsigned int line; /* 端口索引 */resource_size_t mapbase; /* io内存物理基地址 */struct device *dev; /* 父设备 */unsigned char hub6; /* this should be in the 8250 driver */unsigned char suspended;unsigned char unused[2];void *private_data; /* generic platform data pointer */ }; 这个结构体是需要我们自己来填充的比如s3c2440 有3个串口那么就需要填充3个 uart_port 并且通过 uart_add_one_port 添加到 uart_driver-uart_state-uart_port 中去。当然 uart_driver 有多个 uart_state 每个 uart_state 有一个 uart_port 。在 uart_port 里还有一个非常重要的成员 struct uart_ops *ops 这个也是需要我们自己来实现的一般芯片厂家都写好了或者只需要稍作修改。 struct uart_ops {unsigned int (*tx_empty)(struct uart_port *); /* 串口的Tx FIFO缓存是否为空 */void (*set_mctrl)(struct uart_port *, unsigned int mctrl); /* 设置串口modem控制 */unsigned int (*get_mctrl)(struct uart_port *); /* 获取串口modem控制 */void (*stop_tx)(struct uart_port *); /* 禁止串口发送数据 */void (*start_tx)(struct uart_port *); /* 使能串口发送数据 */ void (*send_xchar)(struct uart_port *, char ch); /* 发送xChar */void (*stop_rx)(struct uart_port *); /* 禁止串口接收数据 */void (*enable_ms)(struct uart_port *); /* 使能modem的状态信号 */void (*break_ctl)(struct uart_port *, int ctl); /* 设置break信号 */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 *);/* 设置线路规程 */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 *); /* 申请必要的IO端口/IO内存资源,必要时还可以重新映射串口端口 */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_POLLvoid (*poll_put_char)(struct uart_port *, unsigned char);int (*poll_get_char)(struct uart_port *); #endif }; 实在是太复杂了。但这一层就跟裸机程序一样用来操作硬件寄存器只不过内核把“格式”给我们规定死了。2、上层tty 核心层tty 层要从 uart_register_driver来看起了因为tty_driver是在注册过程中构建的我们也顺便了解注册过程。int uart_register_driver(struct uart_driver *drv) {struct tty_driver *normal NULL;int i, retval;/* 根据driver支持的最大设备数申请n个 uart_state 空间每一个 uart_state 都有一个uart_port */drv-state kzalloc(sizeof(struct uart_state) * drv-nr, GFP_KERNEL);/* tty层分配一个 tty_driver 并将drv-tty_driver 指向它 */normal alloc_tty_driver(drv-nr);drv-tty_driver normal;/* 对 tty_driver 进行设置 */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;struct tty_port *port state-port; /* driver-state-tty_port */tty_port_init(port);port-close_delay 500; /* .5 seconds */port-closing_wait 30000; /* 30 seconds *//* 初始化 tasklet */tasklet_init(state-tlet, uart_tasklet_action,(unsigned long)state);}/* tty层注册 driver-tty_driver */retval tty_register_driver(normal);} 注册过程干了哪些事1、根据driver支持的最大设备数申请n个 uart_state 空间每一个 uart_state 都有一个 uart_port 。2、分配一个 tty_driver 并将drv-tty_driver 指向它。3、对 tty_driver 进行设置其中包括默认波特率、校验方式等还有一个重要的 Ops uart_ops 它是tty核心与我们串口驱动通信的接口。4、初始化每一个 uart_state 的 tasklet 。5、注册 tty_driver 。注册 uart_driver 实际上是注册 tty_driver因此与用户空间打交道的工作完全交给了 tty_driver 而且这一部分都是内核实现好的我们不需要修改了解一下工作原理即可。 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, // 当termios设置被改变时又tty核心调用.set_ldisc uart_set_ldisc, // 设置线路规程函数.stop uart_stop, .start uart_start,.hangup uart_hangup, // 挂起函数当驱动挂起tty设备时调用.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, // 获得当前tty的线路规程的设置.tiocmset uart_tiocmset, // 设置当前tty线路规程的设置 #ifdef CONFIG_CONSOLE_POLL.poll_init uart_poll_init,.poll_get_char uart_poll_get_char,.poll_put_char uart_poll_put_char, #endif };这个是 tty 核心的 Ops 简单看看等后面分析调用关系时在来细看下面来看 tty_driver 的注册。 int tty_register_driver(struct tty_driver *driver) {int error;int i;dev_t dev;void **p NULL;if (!(driver-flags TTY_DRIVER_DEVPTS_MEM) driver-num) {p kzalloc(driver-num * 2 * sizeof(void *), GFP_KERNEL);}/* 如果没有主设备号则申请 */if (!driver-major) {error alloc_chrdev_region(dev, driver-minor_start,driver-num, driver-name);} else {dev MKDEV(driver-major, driver-minor_start);error register_chrdev_region(dev, driver-num, driver-name);}if (p) { /* 为线路规程和termios分配空间 */driver-ttys (struct tty_struct **)p;driver-termios (struct ktermios **)(p driver-num);} else {driver-ttys NULL;driver-termios NULL;}/* 创建字符设备使用 tty_fops */cdev_init(driver-cdev, tty_fops);driver-cdev.owner driver-owner;error cdev_add(driver-cdev, dev, driver-num);mutex_lock(tty_mutex);/* 将该 driver-tty_drivers 添加到全局链表 tty_drivers */list_add(driver-tty_drivers, tty_drivers);mutex_unlock(tty_mutex);if (!(driver-flags TTY_DRIVER_DYNAMIC_DEV)) {for (i 0; i driver-num; i)tty_register_device(driver, i, NULL);}/* proc 文件系统注册driver */proc_tty_register_driver(driver);driver-flags | TTY_DRIVER_INSTALLED;return 0; }tty_driver 注册过程干了哪些事1、为线路规程和termios分配空间并使 tty_driver 相应的成员指向它们。2、注册字符设备名字是 uart_driver-name 我们这里是“ttySAC”,文件操作函数集是 tty_fops。3、将该 uart_driver-tty_drivers 添加到全局链表 tty_drivers 。4、向 proc 文件系统添加 driver 这个暂时不了解。至此文章起初的结构图中的4个ops已经出现了3个另一个关于线路规程的在哪继续往下看。3、调用关系分析tty_driver 不是注册了一个字符设备么那我们就以它的 tty_fops 入手以 open、read、write 为例看看用户空间是如何访问到最底层的硬件操作函数的。3.1 tty_openstatic int tty_open(struct inode *inode, struct file *filp) {int ret;lock_kernel();ret __tty_open(inode, filp);unlock_kernel();return ret; }为了方便分析我把看不懂的代码都删掉了。 static int __tty_open(struct inode *inode, struct file *filp) {struct tty_struct *tty NULL;int noctty, retval;struct tty_driver *driver;int index;dev_t device inode-i_rdev;unsigned saved_flags filp-f_flags; //在全局tty_drivers链表中获取Core注册的tty_driverdriver get_tty_driver(device, index);tty tty_init_dev(driver, index, 0); // tty-ops driver-ops;filp-private_data tty;if (tty-ops-open)/* 调用tty_driver-tty_foperation-open */retval tty-ops-open(tty, filp);return 0; }从 tty_drivers 全局链表获取到前边我们注册进去的 tty_driver 然后分配设置一个 struct tty_struct 的东西最后调用 tty_struct-ops-open 函数其实 tty_struct-ops tty_driver-ops 。struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx, int first_ok) {struct tty_struct *tty;int retval;/* 分配一个 tty_struct */tty alloc_tty_struct();/* 初始化 tty 设置线路规程 Ops 等 */initialize_tty_struct(tty, driver, idx);//tty_ldisc_open(tty, ld)- return ld-ops-open(tty) - n_tty_openretval tty_ldisc_setup(tty, tty-link); return tty; }void initialize_tty_struct(struct tty_struct *tty,struct tty_driver *driver, int idx) {memset(tty, 0, sizeof(struct tty_struct));/* 设置线路规程为 N_TTY */tty_ldisc_init(tty);//struct tty_ldisc *ld tty_ldisc_get(N_TTY);tty_ldisc_assign(tty, ld);...tty_buffer_init(tty);tty-driver driver;/* 初始化等待队列头 */init_waitqueue_head(tty-write_wait);init_waitqueue_head(tty-read_wait);/* 将driver-ops 拷贝到 tty-ops */tty-ops driver-ops;tty-index idx; }void tty_buffer_init(struct tty_struct *tty) {spin_lock_init(tty-buf.lock);tty-buf.head NULL;tty-buf.tail NULL;tty-buf.free NULL;tty-buf.memory_used 0;/* 初始化延时工作队列 */INIT_DELAYED_WORK(tty-buf.work, flush_to_ldisc); } 整个 tty_open 的工作1、获取 tty_driver2、根据 tty_driver 初始化一个 tty_struct2.1 设置 tty_struct 的线路规程为 N_TTY (不同类型的线路规程有不同的 ops)2.2 初始化一个延时工作队列唤醒时调用flush_to_ldisc 读函数时我们需要分析它。2.3 初始化 tty_struct 里的两个等待队列头。2.4 设置 tty_struct-ops tty_driver-ops 。3、在 tty_ldisc_setup 函数中调用到线路规程的open函数对于 N_TTY 来说是 n_tty_open 。4、如果 tty_struct-ops 也就是 tty_driver-ops 定义了 open 函数则调用显然是有的 uart_open 。对于 n_tty_open 它应该是对线路规程如何“格式化数据”进行设置太复杂了忽略掉吧跟我们没多大关系。对于 uart_open 还是有必要贴下代码。static int uart_open(struct tty_struct *tty, struct file *filp) {struct uart_driver *drv (struct uart_driver *)tty-driver-driver_state;struct uart_state *state;struct tty_port *port;int retval, line tty-index;state uart_get(drv, line);port state-port; tty-driver_data state; state-uart_port-state state;/* uport-ops-startup(uport) 调用到最底层的ops里的startup 函数*/retval uart_startup(state, 0);}根据 tty_struct 获取到 uart_driver 再由 uart_driver 获取到里面 的uart_state-uart_port-ops-startup 并调用它。至此open函数分析完毕它不是简单的 “打开”还有大量的初始化工作最终调用到最底层的 startup 函数。3.2 tty_writestatic ssize_t tty_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {struct tty_struct *tty;struct inode *inode file-f_path.dentry-d_inode;ssize_t ret;struct tty_ldisc *ld;tty (struct tty_struct *)file-private_data;ld tty_ldisc_ref_wait(tty);if (!ld-ops-write)ret -EIO;else/* 调用 线路规程 n_tty_write 函数 */ret do_tty_write(ld-ops-write, tty, file, buf, count);tty_ldisc_deref(ld);return ret; } 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;// 将当前进程添加到等待队列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))) {....} else {while (nr 0) {/* 调用到 uart_write */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();} } n_tty_write 调用 tty-ops-write 也就是 uart_write。static int uart_write(struct tty_struct *tty, const unsigned char *buf, int count) {uart_start(tty);return ret; }static void uart_start(struct tty_struct *tty) {__uart_start(tty); }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)/* 调用到最底层的 start_tx */port-ops-start_tx(port); } uart_write 又调用到了最底层的 uart_port-ops-start_tx 函数。猜测一下大概“写”的思路1、将当前进程加入到等待队列2、设置当前进程为可打断的3、层层调用最终调用到底层的 start_tx 函数将要发送的数据存入 DATA 寄存器由硬件自动发送。4、进程调度当前进程进入休眠。5、硬件发送完成进入中断处理函数唤醒对面队列。当然这只是我自己的猜测到底是不是这样具体分析底层操作函数的时候应该会明白。3.3 tty_readstatic ssize_t tty_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) {int i;struct tty_struct *tty;struct inode *inode;struct tty_ldisc *ld;tty (struct tty_struct *)file-private_data;inode file-f_path.dentry-d_inode;ld tty_ldisc_ref_wait(tty);/* 调用线路规程 n_tty_read */if (ld-ops-read)i (ld-ops-read)(tty, file, buf, count);elsei -EIO;tty_ldisc_deref(ld);if (i 0)inode-i_atime current_fs_time(inode-i_sb);return i; } 调用线路规程的 read 函数对于 N_TTY 来说是 n_tty_read (删掉了一堆看不懂的代码还是有很多)static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,unsigned char __user *buf, size_t nr) {unsigned char __user *b buf;DECLARE_WAITQUEUE(wait, current);int c;int minimum, time;ssize_t retval 0;ssize_t size;long timeout;unsigned long flags;int packet;do_it_again:BUG_ON(!tty-read_buf);c job_control(tty, file);minimum time 0;timeout MAX_SCHEDULE_TIMEOUT;/* 如果是非标准模式 */if (!tty-icanon) {...}packet tty-packet;add_wait_queue(tty-read_wait, wait);while (nr) {/* First test for status change. */if (packet tty-link-ctrl_status) {/* 看不懂的都删掉 */}/* This statement must be first before checking for inputso that any interrupt will set the state back toTASK_RUNNING. */set_current_state(TASK_INTERRUPTIBLE);if (((minimum - (b - buf)) tty-minimum_to_wake) ((minimum - (b - buf)) 1))tty-minimum_to_wake (minimum - (b - buf));if (!input_available_p(tty, 0)) {/* 看不懂的都删掉 *//* FIXME: does n_tty_set_room need locking ? */n_tty_set_room(tty);/* 进程调度 休眠 */timeout schedule_timeout(timeout);continue;}__set_current_state(TASK_RUNNING);/* Deal with packet mode. */if (packet b buf) {/* 看不懂的都删掉 */}/* 如果是标准模式 */if (tty-icanon) {/* N.B. avoid overrun if nr 0 */while (nr tty-read_cnt) {int eol;eol test_and_clear_bit(tty-read_tail,tty-read_flags);/* 从tty-read_buf 获取数据 */c tty-read_buf[tty-read_tail];spin_lock_irqsave(tty-read_lock, flags);tty-read_tail ((tty-read_tail1) (N_TTY_BUF_SIZE-1));tty-read_cnt--;if (eol) {/* this test should be redundant:* we shouldnt be reading data if* canon_data is 0*/if (--tty-canon_data 0)tty-canon_data 0;}spin_unlock_irqrestore(tty-read_lock, flags);if (!eol || (c ! __DISABLED_CHAR)) {/* 将数据拷贝到用户空间 */if (tty_put_user(tty, c, b)) {retval -EFAULT;b--;break;}nr--;}if (eol) {tty_audit_push(tty);break;}}if (retval)break;} else {/* 非标准模式不关心删掉 */}....}mutex_unlock(tty-atomic_read_lock);remove_wait_queue(tty-read_wait, wait);if (!waitqueue_active(tty-read_wait))tty-minimum_to_wake minimum;__set_current_state(TASK_RUNNING);...n_tty_set_room(tty);return retval; } “读”过程干了哪些事1、将当前进程加入等待队列2、设置当前进程可中断3、进程调度当前进程进入休眠4、在某处被唤醒5、从 tty-read_buf 取出数据通过 tty_put_user 拷贝到用户空间。那么在何处唤醒猜测应该是在中断处理函数中当DATA寄存器满触发中断中断处理函数中调用 tty_flip_buffer_push 。void tty_flip_buffer_push(struct tty_struct *tty) {unsigned long flags;spin_lock_irqsave(tty-buf.lock, flags);if (tty-buf.tail ! NULL)tty-buf.tail-commit tty-buf.tail-used;spin_unlock_irqrestore(tty-buf.lock, flags);if (tty-low_latency)flush_to_ldisc(tty-buf.work.work);elseschedule_delayed_work(tty-buf.work, 1); } tty_flip_buffer_push 有两种方式调用到 flush_to_ldisc 一种直接调用另一种使用延时工作队列在很久很久以前我们初始化了这么一个工作队列~tty_open 初始化 tty_struct 时前面有提到。在flush_to_ldisc 会调用到 disc-ops-receive_buf 对于 N_TTY 来说是 n_tty_receive_buf 在 n_tty_receive_buf 中将数据拷贝到 tty-read_buf ,然后 wake_up_interruptible(tty-read_wait) 唤醒休眠队列。然后就是前面提到的在n_tty_read 函数中 从 tty-read_buf 里取出数据拷贝到用户空间了。至此关于 uart 的框架分析基本就结束了, 对于 tty 以及线路规程是什么东西大概了解是个什么东西。虽然大部分东西都不需要我们自己实现但是了解它们有益无害。下一篇文章--以 s3c2440 为例分析底层的操作函数以及 s3c2440 是如何初始化 uart_port 结构的这些是在移植驱动过程中需要做的工作~ --END--关注公众号百问科技(IDbaiwenkeji第一时间学习嵌入式干货。技术交流加个人威信13266630429验证: 知乎
http://www.yutouwan.com/news/117401/

相关文章:

  • 信阳网站建设汉狮怎么样华为云和wordpress
  • 桂林相关网站深圳宝安区深圳网站建设 骏域网络
  • 网站建设与制作实训报告局域网内实现域名访问
  • 中文小说网站建设与维护广州网络营销公司
  • 优秀网站建设公司响应式网站好么
  • 当雄网站建设辉县网站建设
  • 杭州网站建设技术支持重庆造价工程建设信息网站
  • 可以做装修效果图的网站有哪些app界面生成器
  • 沧州网站建设公司jsp网站开发的教材
  • 舟山普陀区建设局网站受欢迎的扬中网站建设
  • 网站备案背景成都制作网页
  • 在线做视频的网站wordpress图片显示在文章
  • 私人小工厂做网站价格福建省建设环卫协会网站
  • 深圳西乡网站建设公司汕头网站建设推广厂家
  • 有没有做家纺类的网站如何创作个人网站
  • 网站建设怎样做好网站首页布局分析
  • 深圳专业网站建设网站建设及推广好学习吗
  • 自己建网站收费吗学前端好找工作吗
  • 网站页面设计效果图怎么写代码做网站
  • 电子商务网站建设考卷徐州网站建设哪家专业
  • 中文域名的网站百度数据平台
  • 做seo网站公司哪家好网易企业邮箱手机端设置
  • 常州网站搭建找哪家网站建设银行
  • 食用油 网站 模板ui设计尺寸规范
  • 响应式网站设计图怎么做网站软文推广网站
  • 网站开发前景与趋势如何wordpress交易网站
  • 唐河网站制作公司制作网站多少钱
  • 网站建设个人建设wordpress的注册文件在哪儿
  • 网站开发类论文搜索引擎关键词怎么优化
  • 有偷菜餐厅城市建设的网站天津工程建设招标网站