Linux内核TTY驱动架构

Linux内核TTY驱动架构
Linux内核TTY驱动架构

基于MX51的TTY驱动架构

2012-12-28 一、任务目标

以uart为实例分析整理TTY驱动架构。

二、层次架构

图1

在linux内核中,串行设备多种多样,内核开发者为了让驱动开发者尽量简单(可复用的东西多),构建了如图1所示的串行设备层次架构。

tty core里是最基本的字符驱动,经过线路规程处理的数据,通过tty core提供的数据交互机制与用户空间通信。?

图1中线路规程表示对上层或下层传过来数据的处理,不同的处理方式对应不同的协议,如红外、ppp拨号等等。

serial core是对tty驱动进行了封装,与硬件无关,给底层uart驱动开发提供API,针对具体硬件的操作全在uart驱动中实现。

因为uart同时涉及到以上三层,所以本文档以uart为例,阐述TTY。

三、数据结构设计

1. 数据结构

uart驱动中定义了2个重要的数据结构,struct uart_driver mxc_reg,和struct uart_mxc_port mxc_port[8]。

a. struct uart_driver mxc_reg

uart_driver是serial_core提供的重要结构体,下面是代码定义的uart_driver 实例,变量的解释见对应注释。具体结构体见数据结构关系图。

b. struct uart_mxr_port mxc_ports[8]

mxc_ports[]数组是自定义的uart_mxc_port数组,数组大小为8,在mx51中只用到前3个。

mxc_ports[]中包含的最重要的结构体是struct uart_port{},它们之间的关系详见数据结构关系图。

2. 数据结构关系图

四、uart注册及初始化

1. uart注册

uart驱动程序入口:

在mxcuart_init()函数中,我们重点分析uart_register_driver(struct uart_driver *),对于platform_driver_register(),详见平台设备注册文档分析。

下面重点分析uart_register_driver(struct uart_driver *)

在uart_register_driver()中,构建了tty_driver,并将uart_driver中的相关成员赋值给tty_driver,最终调用到tty_register_driver(struct tty_driver *)。

在tty_register_driver(struct tty_driver *)中,最终注册了8个字符设备驱动,其操作函数为tty_fops。

下面分析不同层次间几个operations函数的关系,如图:

在上面前3层中,每一层都给自己的下一层提供了操作函数的接口,tty_core 是一个字符设备驱动,实现了字符设备提供的struct file_operations{},同时给自己的下层提供了struct tty_operations {}操作接口。

serial_core使用了tty_core提供的struct tty_operations {}操作接口,同时给自己的下层提供了struct uart_ops {}操作接口。

在后面的打开、数据读写过程中我们将会看到具体的层层调用关系。

到这里uart注册告一段落。

2. uart初始化

uart平台设备在/arch/arm/mach-mx5/serial.c中arch_initcall(mxc_init_uart)注册,注册了3个平台设备。

uart平台驱动和uart平台设备通过名字“mxcintuart”匹配,匹配成功调用uart平台驱动中的mxcuart_probe()。

在mxcuart_probe 中主要是将平台资源给mxc_ports中的uart_port ,然后调用uart_add_one_port(),它根据uart_port{}中的line拿到uart_driver{}中对应的

uart_state{},然后配置好这个port并调用tty_register_device()注册该port。

匹配成功后将调用到mxcuart_probe()。

五、uart打开

在介绍uart打开、读写数据时,首先需要先回顾下不同层次间几个operations函数的关系图,接下来先从总体上分析其大致流程,然后具体分析细节。

下面分5个步骤介绍uart打开的具体细节:

●用户空间

●字符驱动

●tty_core

●serial_core

●uart驱动

1. 用户空间

用户空间通过具体的设备节点拿到主次设备号,通过VFS找到具体的字符驱动设备。这一过程非常复杂,详细VFS文档分析。

2. 字符驱动

字符驱动会调用tty_core向它注册的file_operations中的tty_open()。

在上述流程中tty_init_dev需要重点概述下:

3. tty_core

在执行到tty_open()时,会调用serial_core向它注册的tty_operations中的uart_open()。

4. serial_core

如上图代码,在tty_core中调用到了serial_core向它注册的uart_open(),同时uart_open()中也用到了uart驱动向它注册的uart_ops中的startup()。

上面注册了中断处理函数,这个函数极为重要,数据接收和发送都要用到它。

此中断是接收发送共用的,当收到中断信号时通过判断相应的寄存器位来区分是接收中断还是发送中断。

5. uart驱动

uart驱动做为tty的一个实例,就是按照serial_core提供的注册接口注册,并实现其uart_ops中的回调函数,这些函数大多是与具体硬件操作相关的。

六、uart写数据

uart写数据是由用户空间发起的,同样我们先从总体上了解它的流程,再具体讲其细节。

下面分6个步骤介绍uart打开的具体细节:

●用户空间

●字符驱动

●线路规程

●tty_core

●serial_core

●uart驱动

1. 用户空间

用户空间调用write(),通过打开设备节点返回的文件描述符调到tty_core注册的tty_write()。

2. 字符驱动

字符驱动中的tty_write()会从用户空间拷贝数据,然后调用线路规程的write()函数。

3. 线路规程

线路规程就是对写入或者读到的数据按照指定的协议进行处理。不同的线路规程处理数据的方式不一样,这里我们用到的是N_TTY,后续单独分析N_TTY,这里我们只要知道数据经过了它,它对输入的数据进行了处理。

4. tty_core

线路规程会把处理后的数据给到tty_core层,通过调用serial_core注册的uart_write()。

5. serial_core

在上图中可以看到,最后面调用到了uart驱动向serial_core注册的start_tx(),具体的实现是mxcuart_start_tx()函数,它触发了中断,程序将进入中断处理流程。

6. uart驱动

uart中实现了中断处理函数,这里面会将数据一个一个写入寄存器。

下面通过图示的方法,结合数据结构关系图,说明具体数据的走向。

七、uart读数据

uart读数据流程

1.mxcuart_rx_chars(umxc);

uart_insert_char(&umxc->port, status, MXC_UARTURXD_OVRRUN, ch, flag);

tty_insert_flip_char(tty, ch, flag);

Linux设备驱动程序举例

Linux设备驱动程序设计实例2007-03-03 23:09 Linux系统中,设备驱动程序是操作系统内核的重要组成部分,在与硬件设备之间 建立了标准的抽象接口。通过这个接口,用户可以像处理普通文件一样,对硬件设 备进行打开(open)、关闭(close)、读写(read/write)等操作。通过分析和设计设 备驱动程序,可以深入理解Linux系统和进行系统开发。本文通过一个简单的例子 来说明设备驱动程序的设计。 1、程序清单 //MyDev.c 2000年2月7日编写 #ifndef __KERNEL__ #define __KERNEL__//按内核模块编译 #endif #ifndef MODULE #define MODULE//设备驱动程序模块编译 #endif #define DEVICE_NAME "MyDev" #define OPENSPK 1 #define CLOSESPK 2 //必要的头文件 #include //同kernel.h,最基本的内核模块头文件 #include //同module.h,最基本的内核模块头文件 #include //这里包含了进行正确性检查的宏 #include //文件系统所必需的头文件 #include //这里包含了内核空间与用户空间进行数据交换时的函数宏 #include //I/O访问 int my_major=0; //主设备号 static int Device_Open=0; static char Message[]="This is from device driver"; char *Message_Ptr; int my_open(struct inode *inode, struct file *file) {//每当应用程序用open打开设备时,此函数被调用 printk ("\ndevice_open(%p,%p)\n", inode, file); if (Device_Open) return -EBUSY;//同时只能由一个应用程序打开 Device_Open++; MOD_INC_USE_COUNT;//设备打开期间禁止卸载 return 0; } static void my_release(struct inode *inode, struct file *file)

设备驱动加到Linux内核中

7.2.3 设备驱动加到Linux内核中 设备驱动程序编写完后将该驱动程序加到内核中。这需要修改Linux 的源代码,然后重新编译内核。 ①将设备驱动程序文件(比如mydriver.c)复制到/Linux/drivers/char目录下。该目录保存了Linux下字符设备的设备驱动程序。修改该目录下mem.c 文件,在int chr_dev_init()函数中增加如下代码: #ifdef CONFIG_MYDRIVER device_init(); #endif 其中CONFIG_MYDRIVER是在配置Linux内核时赋值。 ②在/linux/drivers/char目录下Makefile中增加如下代码: ifeq ($(CONFIG_MYDRIVER),y) L_OBJ + = mydriver.o endif 如果在配置Linux内核时选择了支持新定义的设备,则在编译内核时会编译mydriver.c生成mydriver.o文件。 ③修改/linux/drivers/char目录下config.in文件,在 comment Character devices 语句下面加上 bool suppot for mydriver CONFIG_MYDRIVER 这样,若编译内核,运行make config,make menuconfig或make xconfig,那么在配置字符设备时就会有选项: Support for mydriver 当选中这个设备时,设备驱动就加到了内核中了。 重新编译内核,在shell中将当前目录cd 到Linux目录下,然后执行以下代码: # make menuconfig # make dep # make 在配置选项时要注意选择支持用户添加的设备。这样得到的内核就包含用户的设备驱动程序。 Linux通过设备文件来提供应用程序和设备驱动的接口,应用程序通过标准的文件操作函数来打开、关闭、读取和控制设备。查看Linux文件系统下的/proc/devices,可以看到当前的设备信息。如果设备驱动程序已被成功加进,这里应该由该设备对应的项。/proc/interrupts纪录了当时中断情况,可以用来查看中断申请是否正常;对于DMA和I/O口的使用,在/proc下都有相应的文件进行记录;还可以在设备驱动程序中申请在/proc 文件系统下创建一个文件,该文件用来存放设备相关信息。这样通过查看该文件就可以了解设备的使用情况。总之,/proc文件系统为用户提供了查

一个简单的演示用的Linux字符设备驱动程序.

实现如下的功能: --字符设备驱动程序的结构及驱动程序需要实现的系统调用 --可以使用cat命令或者自编的readtest命令读出"设备"里的内容 --以8139网卡为例,演示了I/O端口和I/O内存的使用 本文中的大部分内容在Linux Device Driver这本书中都可以找到, 这本书是Linux驱动开发者的唯一圣经。 ================================================== ===== 先来看看整个驱动程序的入口,是char8139_init(这个函数 如果不指定MODULE_LICENSE("GPL", 在模块插入内核的 时候会出错,因为将非"GPL"的模块插入内核就沾污了内核的 "GPL"属性。 module_init(char8139_init; module_exit(char8139_exit; MODULE_LICENSE("GPL"; MODULE_AUTHOR("ypixunil"; MODULE_DESCRIPTION("Wierd char device driver for Realtek 8139 NIC"; 接着往下看char8139_init( static int __init char8139_init(void {

int result; PDBG("hello. init.\n"; /* register our char device */ result=register_chrdev(char8139_major, "char8139", &char8139_fops; if(result<0 { PDBG("Cannot allocate major device number!\n"; return result; } /* register_chrdev( will assign a major device number and return if it called * with "major" parameter set to 0 */ if(char8139_major == 0 char8139_major=result; /* allocate some kernel memory we need */ buffer=(unsigned char*(kmalloc(CHAR8139_BUFFER_SIZE, GFP_KERNEL; if(!buffer { PDBG("Cannot allocate memory!\n"; result= -ENOMEM;

linux驱动开发的经典书籍

linux驱动开发的经典书籍 结构、操作系统、体系结构、编译原理、计算机网络你全修过 我想大概可以分为4个阶段,水平从低到高 从安装使用=>linux常用命令=>linux系统编程=>内核开发阅读内核源码 其中学习linux常用命令时就要学会自己编译内核,优化系统,调整参数 安装和常用命令书太多了,找本稍微详细点的就ok,其间需要学会正则表达式 系统编程推荐《高级unix环境编程》,黑话叫APUE 还有《unix网络编程》 这时候大概还需要看资料理解elf文件格式,连接器和加载器,cmu的一本教材中文名为《深入理解计算机系统》比较好 内核开发阅读内核源码阶段,从写驱动入手逐渐深入linux内核开发 参考书如下《linux device drivers》,黑话叫ldd 《linux kernel development》,黑话叫lkd 《understading the linux kernel》,黑话叫utlk 《linux源码情景分析》 这四本书为搞内核的必读书籍 最后,第三阶段和第四阶段最重动手,空言无益,光看书也不罩,不动手那些东西理解不了 学习linux/unix编程方法的建议 建议学习路径: 首先先学学编辑器,vim, emacs什么的都行。 然后学make file文件,只要知道一点就行,这样就可以准备编程序了。 然后看看《C程序设计语言》K&R,这样呢,基本上就可以进行一般的编程了,顺便找本数据结构的书来看。 如果想学习UNIX/LINUX的编程,《APUE》绝对经典的教材,加深一下功底,学习《UNP》的第二卷。这样基本上系统方面的就可以掌握了。 然后再看Douglus E. Comer的《用TCP/IP进行网际互连》第一卷,学习一下网络的知识,再看《UNP》的第一卷,不仅学习网络编程,而且对系统编程的一些常用的技巧就很熟悉了,如果继续网络编程,建议看《TCP/IP进行网际互连》的第三卷,里面有很多关于应用

Linux内核驱动加载顺序

Linux内核驱动加载顺序 【问题】 背光驱动初始化先于LCD驱动初始化,导致LCD驱动初始化时出现闪屏的现象。 【解决过程】 1 mach-xxx.c中platform devices列表如下 /* platform devices */ static struct platform_device *athena_evt_platform_devices[] __initdata = { //&xxx_led_device, &xxx_rtc_device, &xxx_uart0_device, &xxx_uart1_device, &xxx_uart2_device, &xxx_uart3_device, &xxx_nand_device, &xxx_i2c_device, &xxx_lcd_device, &xxxpwm_backlight_device, ... }; LCD(xxx_lcd_device)设备先于PWM(xxxpwm_backlight_device)设备。 可见驱动的初始化顺序并不是和这个表定义的顺序始终保持一致的。(记得PM操作 - resume/suspend 的顺序 是和这个表的顺序保持一致的) 2 怀疑和编译顺序有关 Z:\kernel\drivers\video\Makefile:背光驱动(backlight/)的编译限于LCD驱动(xxxfb.o)的编译 obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ obj-y += backlight/ display/ ... obj-$(CONFIG_FB_xxx) += xxxfb.o ak_logo.o obj-$(CONFIG_FB_AK88) += ak88-fb/ 这样编译生成的System.map中的顺序为: 906 c001f540 t __initcall_pwm_backlight_init6 907 c001f544 t __initcall_display_class_init6 908 c001f548 t __initcall_xxxfb_init6 Makefile更改为: obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ obj-y += display/

Linux设备驱动程序学习(18)-USB 驱动程序(三)

Linux设备驱动程序学习(18)-USB 驱动程序(三) (2009-07-14 11:45) 分类:Linux设备驱动程序 USB urb (USB request block) 内核使用2.6.29.4 USB 设备驱动代码通过urb和所有的 USB 设备通讯。urb用 struct urb 结构描述(include/linux/usb.h )。 urb以一种异步的方式同一个特定USB设备的特定端点发送或接受数据。一个USB 设备驱动可根据驱动的需要,分配多个 urb 给一个端点或重用单个 urb 给多个不同的端点。设备中的每个端点都处理一个 urb 队列, 所以多个 urb 可在队列清空之前被发送到相同的端点。 一个 urb 的典型生命循环如下: (1)被创建; (2)被分配给一个特定 USB 设备的特定端点; (3)被提交给 USB 核心; (4)被 USB 核心提交给特定设备的特定 USB 主机控制器驱动; (5)被 USB 主机控制器驱动处理, 并传送到设备; (6)以上操作完成后,USB主机控制器驱动通知 USB 设备驱动。 urb 也可被提交它的驱动在任何时间取消;如果设备被移除,urb 可以被USB 核心取消。urb 被动态创建并包含一个内部引用计数,使它们可以在最后一个用户释放它们时被自动释放。 struct urb

struct list_head urb_list;/* list head for use by the urb's * current owner */ struct list_head anchor_list;/* the URB may be anchored */ struct usb_anchor *anchor; struct usb_device *dev;/* 指向这个 urb 要发送的目标 struct usb_device 的指针,这个变量必须在这个 urb 被发送到 USB 核心之前被USB 驱动初始化.*/ struct usb_host_endpoint *ep;/* (internal) pointer to endpoint */ unsigned int pipe;/* 这个 urb 所要发送到的特定struct usb_device 的端点消息,这个变量必须在这个 urb 被发送到 USB 核心之前被 USB 驱动初始化.必须由下面的函数生成*/ int status;/*当 urb开始由 USB 核心处理或处理结束, 这个变量被设置为 urb 的当前状态. USB 驱动可安全访问这个变量的唯一时间是在 urb 结束处理例程函数中. 这个限制是为防止竞态. 对于等时 urb, 在这个变量中成功值(0)只表示这个 urb 是否已被去链. 为获得等时 urb 的详细状态, 应当检查 iso_frame_desc 变量. */ unsigned int transfer_flags;/* 传输设置*/ void*transfer_buffer;/* 指向用于发送数据到设备(OUT urb)或者从设备接收数据(IN urb)的缓冲区指针。为了主机控制器驱动正确访问这个缓冲, 它必须使用 kmalloc 调用来创建, 不是在堆栈或者静态内存中。对控制端点, 这个缓冲区用于数据中转*/ dma_addr_t transfer_dma;/* 用于以 DMA 方式传送数据到 USB 设备的缓冲区*/ int transfer_buffer_length;/* transfer_buffer 或者 transfer_dma 变量指向的缓冲区大小。如果这是 0, 传送缓冲没有被 USB 核心所使用。对于一个 OUT 端点, 如果这个端点大小比这个变量指定的值小, 对这个USB 设备的传输将被分成更小的块,以正确地传送数据。这种大的传送以连续的 USB 帧进行。在一个 urb 中提交一个大块数据, 并且使 USB 主机控制器去划分为更小的块, 比以连续地顺序发送小缓冲的速度快得多*/

linux设备驱动中常用函数

Linux2.6设备驱动常用的接口函数(一) ----字符设备 刚开始,学习linux驱动,觉得linux驱动很难,有字符设备,块设备,网络设备,针对每一种设备其接口函数,驱动的架构都不一样。这么多函数,要每一个的熟悉,那可多难啦!可后来发现linux驱动有很多规律可循,驱动的基本框架都差不多,再就是一些通用的模块。 基本的架构里包括:加载,卸载,常用的读写,打开,关闭,这是那种那基本的咯。利用这些基本的功能,当然无法实现一个系统。比方说:当多个执行单元对资源进行访问时,会引发竞态;当执行单元获取不到资源时,它是阻塞还是非阻塞?当突然间来了中断,该怎么办?还有内存管理,异步通知。而linux 针对这些问题提供了一系列的接口函数和模板框架。这样,在实际驱动设计中,根据具体的要求,选择不同的模块来实现其功能需求。 觉得能熟练理解,运用这些函数,是写号linux设备驱动的第一步。因为是设备驱动,是与最底层的设备打交道,就必须要熟悉底层设备的一些特性,例如字符设备,块设备等。系统提供的接口函数,功能模块就像是工具,能够根据不同的底层设备的的一些特性,选择不同的工具,方能在linux驱动中游刃有余。 最后就是调试,这可是最头疼的事。在调试过程中,总会遇到这样,那样的问题。怎样能更快,更好的发现并解决这些问题,就是一个人的道行咯!我个人觉得: 发现问题比解决问题更难! 时好时坏的东西,最纠结! 看得见的错误比看不见的错误好解决! 一:Fops结构体中函数: ①ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); 用来从设备中获取数据. 在这个位置的一个空指针导致 read 系统调用以-EINVAL("Invalid argument") 失败. 一个非负返回值代表了成功读取的字节数( 返回值是一个 "signed size" 类型, 常常是目标平台本地的整数类型). ②ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); 发送数据给设备. 如果 NULL, -EINVAL 返回给调用 write 系统调用的程序. 如果非负, 返回值代表成功写的字节数 ③loff_t (*llseek) (struct file *, loff_t, int); llseek 方法用作改变文件中的当前读/写位置, 并且新位置作为(正的)返回值. loff_t 参数是一个"long offset", 并且就算在 32位平台上也至少 64 位宽. 错误由一个负返回值指示. 如果这个函数指针是 NULL, seek 调用会以潜在地无法预知的方式修改 file 结构中的位置计数器( 在"file 结构" 一节中描述). ④int (*open) (struct inode *, struct file *);

linux驱动程序的编写

linux驱动程序的编写 一、实验目的 1.掌握linux驱动程序的编写方法 2.掌握驱动程序动态模块的调试方法 3.掌握驱动程序填加到内核的方法 二、实验内容 1. 学习linux驱动程序的编写流程 2. 学习驱动程序动态模块的调试方法 3. 学习驱动程序填加到内核的流程 三、实验设备 PentiumII以上的PC机,LINUX操作系统,EL-ARM860实验箱 四、linux的驱动程序的编写 嵌入式应用对成本和实时性比较敏感,而对linux的应用主要体现在对硬件的驱动程序的编写和上层应用程序的开发上。 嵌入式linux驱动程序的基本结构和标准Linux的结构基本一致,也支持模块化模式,所以,大部分驱动程序编成模块化形式,而且,要求可以在不同的体系结构上安装。linux是可以支持模块化模式的,但由于嵌入式应用是针对具体的应用,所以,一般不采用该模式,而是把驱动程序直接编译进内核之中。但是这种模式是调试驱动模块的极佳方法。 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。同时,设备驱动程序是内核的一部分,它完成以下的功能:对设备初始化和释放;把数据从内核传送到硬件和从硬件读取数据;读取应用程序传送给设备文件的数据和回送应用程序请求的数据;检测和处理设备出现的错误。在linux操作系统下有字符设备和块设备,网络设备三类主要的设备文件类型。 字符设备和块设备的主要区别是:在对字符设备发出读写请求时,实际的硬件I/O一般就紧接着发生了;块设备利用一块系统内存作为缓冲区,当用户进程对设备请求满足用户要求时,就返回请求的数据。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。 1 字符设备驱动结构 Linux字符设备驱动的关键数据结构是cdev和file_operations结构体。

史上最全linux内核配置详解

对于每一个配置选项,用户可以回答"y"、"m"或"n"。其中"y"表示将相应特性的支持或设备驱动程序编译进内核;"m"表示将相应特性的支持或设备驱动程序编译成可加载模块,在需要时,可由系统或用户自行加入到内核中去;"n"表示内核不提供相应特性或驱动程序的支持。只有<>才能选择M 1. General setup(通用选项) [*]Prompt for development and/or incomplete code/drivers,设置界面中显示还在开发或者还没有完成的代码与驱动,最好选上,许多设备都需要它才能配置。 [ ]Cross-compiler tool prefix,交叉编译工具前缀,如果你要使用交叉编译工具的话输入相关前缀。默认不使用。嵌入式linux更不需要。 [ ]Local version - append to kernel release,自定义版本,也就是uname -r可以看到的版本,可以自行修改,没多大意义。 [ ]Automatically append version information to the version string,自动生成版本信息。这个选项会自动探测你的内核并且生成相应的版本,使之不会和原先的重复。这需要Perl的支持。由于在编译的命令make-kpkg 中我们会加入- –append-to-version 选项来生成自定义版本,所以这里选N。 Kernel compression mode (LZMA),选择压缩方式。 [ ]Support for paging of anonymous memory (swap),交换分区支持,也就是虚拟内存支持,嵌入式不需要。 [*]System V IPC,为进程提供通信机制,这将使系统中各进程间有交换信息与保持同步的能力。有些程序只有在选Y的情况下才能运行,所以不用考虑,这里一定要选。 [*]POSIX Message Queues,这是POSIX的消息队列,它同样是一种IPC(进程间通讯)。建议你最好将它选上。 [*]BSD Process Accounting,允许进程访问内核,将账户信息写入文件中,主要包括进程的创建时间/创建者/内存占用等信息。可以选上,无所谓。 [*]BSD Process Accounting version 3 file format,选用的话统计信息将会以新的格式(V3)写入,注意这个格式和以前的v0/v1/v2 格式不兼容,选不选无所谓。 [ ]Export task/process statistics through netlink (EXPERIMENTAL),通过通用的网络输出工作/进程的相应数据,和BSD不同的是,这些数据在进程运行的时候就可以通过相关命令访问。和BSD类似,数据将在进程结束时送入用户空间。如果不清楚,选N(实验阶段功能,下同)。 [ ]Auditing support,审计功能,某些内核模块需要它(SELINUX),如果不知道,不用选。 [ ]RCU Subsystem,一个高性能的锁机制RCU 子系统,不懂不了解,按默认就行。 [ ]Kernel .config support,将.config配置信息保存在内核中,选上它及它的子项使得其它用户能从/proc/ config.gz中得到内核的配置,选上,重新配置内核时可以利用已有配置Enable access to .config through /proc/config.gz,上一项的子项,可以通过/proc/ config.gz访问.config配置,上一个选的话,建议选上。 (16)Kernel log buffer size (16 => 64KB, 17 => 128KB) ,内核日志缓存的大小,使用默认值即可。12 => 4 KB,13 => 8 KB,14 => 16 KB单处理器,15 => 32 KB多处理器,16 => 64 KB,17 => 128 KB。 [ ]Control Group support(有子项),使用默认即可,不清楚可以不选。 Example debug cgroup subsystem,cgroup子系统调试例子 Namespace cgroup subsystem,cgroup子系统命名空间 Device controller for cgroups,cgroups设备控制器

Linux驱动工程师成长之路

本人此刻还不是什么驱动工程师,连入门都谈不上,但我坚信在未来的3-5年我肯定能成为我想像中的人,因为我马上就要进入这一行工作了。写下这个日志来记录我是怎么最后成为我想像中的人才的,呵呵。 《Linux驱动工程师》这个东西是我在大二的时候看到有一篇讲如何学习嵌入式的,点击这里下载PDF,里面讲到嵌入式分为四层:硬件,驱动,系统,应用程序;还说linux驱动最难然后工资也最高就冲着他这句话我就决定我大学毕业的时候要去做这个linux驱动工程师,随后我就先后买了51单片机,ARM7,ARM9还有一大堆的视频教程准备来进行学习。我还跟我旁边那个哈工大哥们说:“我们学校像我这样的人很少,你们学校呢?”他说:“太少了,不过我们学校都是做这种板子卖的人比较多!”。行,你们牛!即使是买了这些东西,从大二到现在都快毕业了但感觉还是没有入门。回想一下我都学过什么啊:1:自己在ARM9上写bootloader(主要锻炼了三方面的知识:C语言应该写了有近万行的代码,ARM9的外设的基本操作方法如UART,LCD,TOUCH,SD,USB,ETHERNET...,makefile);2:移植和学习linux驱动。下面我说一下我学习Linux驱动的一个思路这也是我在面试的时候自我介绍中最重要的部分;1:硬件知识学习Linux驱动首先得了解这个驱动对应的硬件的一些基本原理和操作方法比如LCD你得了解它的场同步,行同步,像素时钟,一个像素的表示模式,还有就是这个LCD是怎么把图像显示在屏幕上的。如果是USB,SD卡就得了解相关协议。可以通过spec(协议)、datasheet来了解,这就是传说中的Linux驱动开发三件宝之二,还有一个就是linux相关源码。2:了解linux驱动框架linux下的每一类驱动差不多都是一个比较完善的子系统,比如FLASH的驱动它就属于MTD子系统从上到下分为四层:设备节点层,设备层,原始设备层,最下面的与具体硬件相关的硬件驱动层,通常要我们自己来实现就是最下面这个与具体硬件相关那部分代码。3:了解这个驱动的数据流。这个过程与第二个过程紧密相关,如果了解了驱动的框架差不多这个过程也算了解了。比如flash.在/dev/目录下有对应flash的字符设备文件和块设备文件,用户对这些文件进行读、写、ioctl操作,其间通过层层的函数调用最终将调用到最下面的硬件驱动层对硬件进行操作。了解这个过程我相信在调试驱动的时候是很有帮助。3:分析与硬件相关通常需要我们实现的那部分源代码。4:三板子上将驱动调试出来。每次调试都会出问题,但我买的板子提供的资料比较全调试过程中遇到的问题都比较浅显,即使是浅显的问题也要把它记录下来。(这个是我上次在华为面试的时候,那个人问我你调试驱动遇到过什么问题吗?你是如何解决的。当时我学习还没有到调试驱动这一步,所以那次面试也惨败收场)。 好像说了这么多,还没有进入正题《工作的选择》。在年前去了龙芯,实习2.8K,转正3.5k,环境还是不错,经理很好,头儿也很帅都是中科院的硕士。不过去了两周我就没去了身边的人都不太理解,我也一度有过后悔的时候,从龙芯出来应该是1月6号,也就是从那个时候开始我就没有再找工作,转而学习linux驱动。一直到上周日。上周日的晚上我就开始投简历一开始要找linux驱动,在智联里面输入linux驱动出来500来个职位,点开一看没有一个自己符合要求的,差不多都要3-5年经验本科,有时候好不容易有个实习的关键字在里面,一看要求硕士,严重打击了我的信心,哎不管了随便投,最后又投了一下嵌入式关键字的职位。最后就瞎申请,看看职位要求差不多就申请。周一来了,这周一共来了6个面试,创下了我求职以来的历史新高。周一下午面了一家感觉还不错不过到现在也没有给我一个通知,估计当时我要了4500把他给要跑了,这家是做测量的不是Linux驱动,差不多是把ARM当单片机用。周二上午一家也是要招linux驱动面了估计不到二分钟,他

Linux内核驱动模块编写概览-ioctl,class_create,device_create

如果你对内核驱动模块一无所知,请先学习内核驱动模块的基础知识。 如果你已经入门了内核驱动模块,但是仍感觉有些模糊,不能从整体来了解一个内核驱动模块的结构,请赏读一下这篇拙文。 如果你已经从事内核模块编程N年,并且道行高深,也请不吝赐教一下文中的疏漏错误。 本文中我将实现一个简单的Linux字符设备,旨在大致勾勒出linux内核模块的编写方法的轮廓。其中重点介绍ioctl的用途。 我把这个简单的Linux字符设备模块命名为hello_mod. 设备类型名为hello_cl ass 设备名为hello 该设备是一个虚拟设备,模块加载时会在/sys/class/中创建名为hello_class 的逻辑设备,在/dev/中创建hello的物理设备文件。模块名为hello_mod,可接受输入字符串数据(长度小于128),处理该输入字符串之后可向外输出字符串。并且可以接受ioctl()函数控制内部处理字符串的方式。 例如: a.通过write函数写入“Tom”,通过ioctl函数设置langtype=chinese,通过read函数读出的数据将会是“你好!Tom/n” b.通过write函数写入“Tom”,通过ioctl函数设置langtype=english,通过read函数读出的数据将会是“hello!Tom/n” c.通过write函数写入“Tom”,通过ioctl函数设置langtype=pinyin,通过read函数读出的数据将会是“ni hao!Tom/n” 一般的内核模块中不会负责设备类别和节点的创建,我们在编译完之后会得到.o或者.k o文件,然后insmod之后需要mk nod来创建相应文件,这个简单的例子 中我们让驱动模块加载时负责自动创建设备类别和设备文件。这个功能有两个步骤, 1)创建设备类别文件class_cr eate(); 2)创建设备文件dev ice_create(); 关于这两个函数的使用方法请参阅其他资料。 linux设备驱动的编写相对wi ndows编程来说更容易理解一点因为不需要处理IR P,应用层函数和内核函数的关联方式浅显易懂。 比如当应曾函数对我的设备调用了open()函数,而最终这个应用层函数会调用我的设备中的自定义open()函数,这个函数要怎么写呢, 我在我的设备中定义的函数名是hello_mod_open,注意函数名是可以随意定义,但是函数签名是要符合内核要求的,具体的定义是怎么样请看 static int hello_mod_open(struct inode *, struct file *); 这样就定义了内核中的open函数,这只是定义还需要与我们自己的模块关联起来,这就要用到一个结构 struct file_operations 这个结构里面的成员是对应于设备操作的各种函数的指针。 我在设备中用到了这些函数所以就如下定义,注意下面的写法不是标准ANSI C的语法,而是GNU扩展语法。 struct file_operations hello_mod_fops = { .owner = THIS_MODULE, .open = hello_mod_open,

Linux设备驱动程序学习(20)-内存映射和DMA-基本概念

Linux设备驱动程序学习(20)-内存映射和DMA-基本概念 (2011-09-25 15:47) 标签: 虚拟内存设备驱动程序Linux技术分类:Linux设备驱动程序 这部分主要研究 Linux 内存管理的基础知识, 重点在于对设备驱动有用的技术. 因为许多驱动编程需要一些对于虚拟内存(VM)子系统原理的理解。 而这些知识主要分为三个部分: 1、 mmap系统调用的实现原理:它允许设备内存直接映射到一个用户进程地址 空间. 这样做对一些设备来说可显著地提高性能. 2、与mmap的功能相反的应用原理:内核态代码如何跨过边界直接存取用户空间的内存页. 虽然较少驱动需要这个能力. 但是了解如何映射用户空间内存到内 核(使用 get_user_pages)会有用. 3、直接内存存取( DMA ) I/O 操作, 它提供给外设对系统内存的直接存取. 但所有这些技术需要理解 Linux 内存管理的基本原理, 因此我将先学习VM子 系统的基本原理. 一、Linux的内存管理 这里重点是 Linux 内存管理实现的主要特点,而不是描述操作系统的内存管理理论。Linux虚拟内存管理非常的复杂,要写可以写一本书:《深入理解Linux 虚拟内存管理》。学习驱动无须如此深入, 但是对它的工作原理的基本了解是必要的. 解了必要的背景知识后,才可以学习内核管理内存的数据结构. Linux是一个虚拟内存系统(但是在没有MMU的CPU中跑的ucLinux除外), 意味着在内核启动了MMU 之后所有使用的地址不直接对应于硬件使用的物理地址,这些地址(称之为虚拟地址)都经过了MMU转换为物理地址之后再从CPU的内存总线中发出,读取/写入数据. 这样 VM 就引入了一个间接层, 它是许多操作成为可能: 1、系统中运行的程序可以分配远多于物理内存的内存空间,即便单个进程都可拥有一个大于系统的物理内存的虚拟地址空间. 2、虚拟内存也允许程序对进程的地址空间运用多种技巧, 包括映射程序的内存到设备内存.等等~~~ 1、地址类型 Linux 系统处理几种类型的地址, 每个有它自己的含义: 用户虚拟地址:User virtual addresses,用户程序见到的常规地址. 用户地址在长度上是 32 位或者 64 位, 依赖底层的硬件结构, 并且每个进程有它自己 的虚拟地址空间.

从零开始搭建Linux驱动开发环境

参考: 韦东山视频第10课第一节内核启动流程分析之编译体验 第11课第三节构建根文件系统之busybox 第11课第四节构建根文件系统之构建根文件系统韦东山书籍《嵌入式linux应用开发完全手册》 其他《linux设备驱动程序》第三版 平台: JZ2440、mini2440或TQ2440 交叉网线和miniUSB PC机(windows系统和Vmware下的ubuntu12.04) 一、交叉编译环境的选型 具体的安装交叉编译工具,网上很多资料都有,我的那篇《arm-linux- gcc交叉环境相关知识》也有介绍,这里我只是想提示大家:构建跟文件系统中所用到的lib库一定要是本系统Ubuntu中的交叉编译环境arm-linux- gcc中的。即如果电脑ubuntu中的交叉编译环境为arm-linux-

二、主机、开发板和虚拟机要三者互通 w IP v2.0》一文中有详细的操作步骤,不再赘述。 linux 2.6.22.6_jz2440.patch组合而来,具体操作: 1. 解压缩内核和其补丁包 tar xjvf linux-2.6.22.6.tar.bz2 # 解压内核 tar xjvf linux-2.6.22.6_jz2440.tar.bz2 # 解压补丁

cd linux_2.6.22.6 patch –p1 < ../linux-2.6.22.6_jz2440.patch 3. 配置 在内核目录下执行make 2410_defconfig生成配置菜单,至于怎么配置,《嵌入式linux应用开发完全手册》有详细介绍。 4. 生成uImage make uImage 四、移植busybox 在我们的根文件系统中的/bin和/sbin目录下有各种命令的应用程序,而这些程序在嵌入式系统中都是通过busybox来构建的,每一个命令实际上都是一个指向bu sybox的链接,busybox通过传入的参数来决定进行何种命令操作。 1)配置busybox 解压busybox-1.7.0,然后进入该目录,使用make menuconfig进行配置。这里我们这配置两项 一是在编译选项选择动态库编译,当然你也可以选择静态,不过那样构建的根文件系统会比动态编译的的大。 ->Busybox Settings ->Build Options

Linux驱动框架及驱动加载

本讲主要概述Linux设备驱动框架、驱动程序的配置文件及常用的加载驱动程序的方法;并且介绍Red Hat Linux安装程序是如何加载驱动的,通过了解这个过程,我们可以自己将驱动程序放到引导盘中;安装完系统后,使用kudzu自动配置硬件程序。 Linux设备驱动概述 1. 内核和驱动模块 操作系统是通过各种驱动程序来驾驭硬件设备,它为用户屏蔽了各种各样的设备,驱动硬件是操作系统最基本的功能,并且提供统一的操作方式。正如我们查看屏幕上的文档时,不用去管到底使用nVIDIA芯片,还是ATI芯片的显示卡,只需知道输入命令后,需要的文字就显示在屏幕上。硬件驱动程序是操作系统最基本的组成部分,在Linux内核源程序中也占有较高的比例。 Linux内核中采用可加载的模块化设计(LKMs ,Loadable Kernel Modules),一般情况下编译的Linux内核是支持可插入式模块的,也就是将最基本的核心代码编译在内核中,其它的代码可以选择是在内核中,或者编译为内核的模块文件。 如果需要某种功能,比如需要访问一个NTFS分区,就加载相应的NTFS模块。这种设计可以使内核文件不至于太大,但是又可以支持很多的功能,必要时动态地加载。这是一种跟微内核设计不太一样,但却是切实可行的内核设计方案。 我们常见的驱动程序就是作为内核模块动态加载的,比如声卡驱动和网卡驱动等,而Linux最基础的驱动,如CPU、PCI总线、TCP/IP协议、APM(高级电源管理)、VFS等驱动程序则编译在内核文件中。有时也把内核模块就叫做驱动程序,只不过驱动的内容不一定是硬件罢了,比如ext3文件系统的驱动。 理解这一点很重要。因此,加载驱动时就是加载内核模块。下面来看一下有关模块的命令,在加载驱动程序要用到它们:lsmod、modprob、insmod、rmmod、modinfo。 lsmod

Linux设备驱动程序简介

第一章Linux设备驱动程序简介 Linux Kernel 系统架构图 一、驱动程序的特点 ?是应用和硬件设备之间的一个软件层。 ?这个软件层一般在内核中实现 ?设备驱动程序的作用在于提供机制,而不是提供策略,编写访问硬件的内核代码时不要给用户强加任何策略 o机制:驱动程序能实现什么功能。 o策略:用户如何使用这些功能。 二、设备驱动分类和内核模块 ?设备驱动类型。Linux 系统将设备驱动分成三种类型 o字符设备 o块设备 o网络设备 ?内核模块:内核模块是内核提供的一种可以动态加载功能单元来扩展内核功能的机制,类似于软件中的插件机制。这种功能单元叫内核模块。 ?通常为每个驱动创建一个不同的模块,而不在一个模块中实现多个设备驱动,从而实现良好的伸缩性和扩展性。 三、字符设备 ?字符设备是个能够象字节流<比如文件)一样访问的设备,由字符设备驱动程序来实现这种特性。通过/dev下的字符设备文件来访问。字符设备驱动程序通常至少需要实现 open、close、read 和 write 等系统调用 所对应的对该硬件进行操作的功能函数。 ?应用程序调用system call<系统调用),例如:read、write,将会导致操作系统执行上层功能组件的代码,这些代码会处理内核的一些内部 事务,为操作硬件做好准备,然后就会调用驱动程序中实现的对硬件进 行物理操作的函数,从而完成对硬件的驱动,然后返回操作系统上层功 能组件的代码,做好内核内部的善后事务,最后返回应用程序。 ?由于应用程序必须使用/dev目录下的设备文件<参见open调用的第1个参数),所以该设备文件必须事先创建。谁创建设备文件呢? ?大多数字符设备是个只能顺序访问的数据通道,不能前后移动访问指针,这点和文件不同。比如串口驱动,只能顺序的读写设备。然而,也 存在和数据区或者文件特性类似的字符设备,访问它们时可前后移动访

linux驱动程序进入内核

ARM-uClinux下编写加载驱动程序详细过程 本文主要介绍在uClinux下,通过加载模块的方式调试IO控制蜂鸣器的驱动程序。实验过程与上篇文章所讲的过程基本相似,更多注重细节及注意事项。 本文适合学习ARM—Linux的初学者。 //================================================================== 硬件平台:MagicARM2200教学试验开发平台(LPC2290) Linux version 2.4.24,gcc version 2.95.3 电路连接:P0.7——蜂鸣器,低电平发声。 实验条件:uClinux内核已经下载到开发板上,能够正常运行;与宿主机相连的网络、串口连接正常。 //================================================================== 编写蜂鸣器的驱动程序相对来说容易实现,不需要处理中断等繁琐的过程,本文以蜂鸣器的驱动程序为例,详细说明模块化驱动程序设计的主要过程和注意事项。 一、编写驱动程序 驱动程序的编写与上文所说的编写过程基本相同,这里再详细说明一下。 //========================================== //蜂鸣器驱动程序:beep.c文件 //------------------------------------------------------------------- #include /*模块相关*/ #include /*内核相关*/ #include /*linux定义类型*/ #include /*文件系统 file_opertions 结构体定义*/ #include /*出错信息*/ /*PINSEL0 注意:低2位是UART0复用口,不要改动*/ #define PINSEL0 (*((volatile unsigned*) 0xE002C000)) /*P0口控制寄存器*/ #define IO0PIN (*((volatile unsigned*) 0xE0028000))

相关文档
最新文档