linux驱动教程?linux系统的电脑

很多朋友对于linux驱动教程和linux系统的电脑不太懂,今天就由小编来为大家分享,希望可以帮助到大家,下面一起来看看吧!

Linux系统安装驱动的简易步骤linux系统安装驱动

Linux系统:安装驱动的简易步骤

Linux是世界上使用最广的开源操作系统之一,它拥有众多的独特功能,能够实现增强用户计算机网络体验的维护效果,同时也带来更大的可操作性。尽管Linux的可扩展性和稳定性是世界闻名的,但要实现它的全部功能,有时也需要添加一些驱动程序。在本文中,我们将介绍安装Linux系统驱动的简易步骤。

首先,根据个人的需要查找驱动程序。可以在Linux官方网站上下载合适自己的Linux系统版本,也可以在互联网上搜索相应的驱动程序,但请注意搜索时避免下载过旧或恶意的驱动程序。下载完成后将此文件解压或拷贝至指定的Linux路径目录。

接下来要打开终端窗口,进入到项目目录。要进入当前项目目录,需要使用cd命令,然后继续使用make命令来运行项目文件。在运行make命令时,编译器会处理加载设备驱动所需的所有扩展文件。编译完毕后,使用sudo make install命令将定义文件及相应的文件安装进Linux系统中。安装完成后,记得使用rm-rf命令来删除不需要的文件及资源。

最后,使用modprobe命令进行驱动加载。Modprobe命令是一种内核模块,用于加载和卸载内核模块。输入相关的模块名称及参数,即可将新的驱动程序安装进Linux系统中,从而实现与设备之间的更好兼容性及更加稳定的运行。

以上就是Linux系统驱动程序安装的简易步骤,在安装过程中要谨慎选择,确保下载的文件可信可靠,并注意语法及操作之间的正确性,以保证软件的正常安装及调试运行。

Linux系统中USB驱动程序的工作流程详解

1.USB主机

在Linux驱动中,USB驱动处于最底层的是USB主机控制器硬件,在其之上运行的是USB主机控制器驱动,主机控制器之上为USB核心层,再上层为USB设备驱动层(插入主机上的U盘、鼠标、USB转串口等设备驱动)。

因此,在主机侧的层次结构中,要实现的USB驱动包括两类:USB主机控制器驱动和USB设备驱动,前者控制插入其中的USB设备,后者控制USB设备如何与主机通信。Linux内核USB核心负责USB驱动管理和协议处理的主要工作。主机控制器驱动和设备驱动之间的USB核心非常重要,其功能包括:通过定义一些数据结构、宏和功能函数,向上为设备驱动提供编程接口,向下为USB主机控制器驱动提供编程接口;通过全局变量维护整个系统的USB设备信息;完成设备热插拔控制、总线数据传输控制等。

2.USB设备

Linux内核中USB设备侧驱动程序分为3个层次:UDC驱动程序、Gadget API和Gadget驱动程序。UDC驱动程序直接访问硬件,控制USB设备和主机间的底层通信,向上层提供与硬件相关操作的回调函数。当前Gadget API是UDC驱动程序回调函数的简单包装。Gadget驱动程序具体控制USB设备功能的实现,使设备表现出网络连接、打印机或USB Mass Storage等特性,它使用Gadget API控制UDC实现上述功能。Gadget API把下层的UDC驱动程序和上层的Gadget驱动程序隔离开,使得在Linux系统中编写USB设备侧驱动程序时能够把功能的实现和底层通信分离。

3.层次

在USB设备组织结构中,从上到下分为设备(device)、配置(config)、接口(interface)和端点(endpoint)四个层次。USB设备程序绑定到接口上。

对于这四个层次的简单描述如下:

(1)设备通常具有一个或多个的配置

(2)配置经常具有一个或多个的接口

(3)接口没有或具有一个以上的端点

4.端点

USB通信最基本的形式是通过端点(USB端点分中断(Interrupt)、批量(Bulk)、等时(ISO)、控制(Control)四种,每种用途不同),USB端点只能往一个方向传送数据,从主机到设备或者从设备到主机,端点可以看作是单向的管道(pipe)。驱动程序把驱动程序对象注册到USB子系统中,稍后再使用制造商和设备标识来判断是否已经安装了硬件。USB核心使用一个列表(是一个包含制造商ID和设备号ID的一个结构体)来判断对于一个设备该使用哪一个驱动程序,热插拨脚本使用它来确定当一个特定的设备插入到系统时该自动执行哪一个驱动程序的Probe。

5.数据结构

(1)USB设备:对应数据结构struct usb_device

(2)配置:struct usb_host_config(任一时刻,只能有一个配置生效)

(3)USB接口:struct usb_interface(USB核心将其传递给USB设备驱动,并由USB设备驱动负责后续的控制。一个USB接口代表一个基本功能,每个USB驱动控制一个接口。所以一个物理上的硬件设备可能需要一个以上的驱动程序。)

(4)端点: struct usb_host_endpoint,它所包含的真实端点信息在另一个结构中:struct usb_endpoint_descriptor(端点描述符,包含所有的USB特定数据)。

6. USB端点分类

USB通讯的最基本形式是通过一个称为端点的东西。一个USB端点只能向一个方向传输数据(从主机到设备(称为输出端点)或者从设备到主机(称为输入端点))。端点可被看作一个单向的管道。

USB端点有 4种不同类型,分别具有不同的数据传送方式:

(1)控制CONTROL

控制端点被用来控制对USB设备的不同部分访问.通常用作配置设备、获取设备信息、发送命令到设备或获取设备状态报告。这些端点通常较小。每个 USB设备都有一个控制端点称为端点 0,被 USB核心用来在插入时配置设备。USB协议保证总有足够的带宽留给控制端点传送数据到设备.

(2)中断INTERRUPT

每当 USB主机向设备请求数据时,中断端点以固定的速率传送小量的数据。此为USB键盘和鼠标的主要的数据传送方法。它还用以传送数据到USB设备来控制设备。通常不用来传送大量数据。USB协议保证总有足够的带宽留给中断端点传送数据到设备.

(3)批量BULK

批量端点用以传送大量数据。这些端点通常比中断端点大得多.它们普遍用于不能有任何数据丢失的情况。USB协议不保证传输在特定时间范围内完成。如果总线上没有足够的空间来发送整个BULK包,它被分为多个包进行传输。这些端点普遍用于打印机、USB Mass Storage和USB网络设备上。

(4)等时ISOCHRONOUS

等时端点也批量传送大量数据,但是这个数据不被保证能送达。这些端点用在可以处理数据丢失的设备中,并且更多依赖于保持持续的数据流。如音频和视频设备等等。

控制和批量端点用于异步数据传送,而中断和等时端点是周期性的。这意味着这些端点被设置来在固定的时间连续传送数据,USB核心为它们保留了相应的带宽。

7. endpoint

C/C++ Code复制内容到剪贴板structusb_host_endpoint{structusb_endpoint_descriptordesc;//端点描述符structlist_headurb_list;//此端点的URB对列,由USB核心维护void*hcpriv;structep_device*ep_dev;/*Forsysfsinfo*/unsignedchar*extra;/*Extradescriptors*/intextralen;intenabled;};

当调用USB设备驱动调用usb_submit_urb提交urb请求时,将调用int usb_hcd_link_urb_to_ep(struct usb_hcd*hcd, struct urb*urb)把此urb增加到urb_list的尾巴上。(hcd: Host Controller Driver,对应数据结构struct usb_hcd)

8. urb

所有USB通讯均为请求--响应模式,USB设备不会主动向Host发送数据。写数据:USB设备驱动发送urb请求给USB设备,USB设备不需要回数据。读数据:USB设备驱动发送urb请求给USB设备,USB设备需要回数据。

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被动态创建并包含一个内部引用计数,使它们可以在最后一个用户释放它们时被自动释放。

8.1提交 urb

一旦 urb被正确地创建并初始化,它就可以提交给 USB核心以发送出到 USB设备.这通过调用函数sb_submit_urb实现.

int usb_submit_urb(struct urb*urb, gfp_t mem_flags);

参数:

struct urb*urb:指向被提交的 urb的指针

gfp_t mem_flags:使用传递给 kmalloc调用同样的参数,用来告诉 USB核心如何及时分配内存缓冲

因为函数 usb_submit_urb可被在任何时候被调用(包括从一个中断上下文), mem_flags变量必须正确设置.根据 usb_submit_urb被调用的时间,只有 3个有效值可用:

GFP_ATOMIC

只要满足以下条件,就应当使用此值:

1)调用者处于一个 urb结束处理例程,中断处理例程,底半部,tasklet或者一个定时器回调函数.

2)调用者持有自旋锁或者读写锁.注意如果正持有一个信号量,这个值不必要.

3) current-state不是 TASK_RUNNING.除非驱动已自己改变 current状态,否则状态应该一直是TASK_RUNNING.

GFP_NOIO

驱动处于块 I/O处理过程中.它还应当用在所有的存储类型的错误处理过程中.

GFP_KERNEL

所有不属于之前提到的其他情况

在 urb被成功提交给 USB核心之后,直到结束处理例程函数被调用前,都不能访问 urb结构的任何成员

8.2 urb结束处理例程

如果 usb_submit_urb被成功调用,并把对 urb的控制权传递给 USB核心,函数返回 0;否则返回一个负的错误代码.如果函数调用成功,当 urb被结束的时候结束处理例程会被调用一次.当这个函数被调用时, USB核心就完成了这个urb,并将它的控制权返回给设备驱动.

只有3种结束urb并调用结束处理例程的情况:

(1)urb被成功发送给设备,且设备返回正确的确认.如果这样, urb中的status变量被设置为 0.

(2)发生错误,错误值记录在 urb结构中的 status变量.

(3)urb从 USB核心unlink.这发生在要么当驱动通过调用 usb_unlink_urb或者 usb_kill_urb告知 USB核心取消一个已提交的 urb,或者在一个 urb已经被提交给它时设备从系统中去除.

9.探测和断开

在 struct usb_driver结构中,有 2个 USB核心在适当的时候调用的函数:

(1)当设备插入时,如果 USB核心认为这个驱动可以处理(USB核心使用一个列表(是一个包含制造商ID和设备号ID的一个结构体)来判断对于一个设备该使用哪一个驱动程序),则调用探测(probe)函数,探测函数检查传递给它的设备信息,并判断驱动是否真正合适这个设备.

(2)由于某些原因,设备被移除或驱动不再控制设备时,调用断开(disconnect)函数,做适当清理.

探测和断开回调函数都在USB集线器内核线程上下文中被调用,因此它们休眠是合法的.为了缩短 USB探测时间,大部分工作尽可能在设备打开时完成.这是因为 USB核心是在一个线程中处理 USB设备的添加和移除,因此任何慢设备驱动都可能使 USB设备探测时间变长。

9.1探测函数分析

在探测回调函数中, USB设备驱动应当初始化它可能用来管理 USB设备的所有本地结构并保存所有需要的设备信息到本地结构,因为在此时做这些通常更容易.为了和设备通讯,USB驱动通常要探测设备的端点地址和缓冲大小.

PS:Linux USB驱动相关细节知识补充

1.在usb_fill_bulk_urb,usb_fill_int_urb,usb_fill_control_urb都需要指定回调函数,当此URB请求完成时,usb core回调用此函数。

注意:urb回调函数是在中断上下文运行,因此它不应做任何内存分配,持有任何信号量,或任何可导致进程休眠的事情.如果从回调中提交 urb并需要分配新内存块,需使用 GFP_ATOMIC标志来告知 USB核心不要休眠.

2. urb封装函数:

(1)int usb_bulk_msg(struct usb_device*usb_dev,unsigned int pipe,void*data, int len, int*actual_length,int timeout)

功能:创建批量 urb并发送到指定的设备,接着在返回之前等待完成.

参数:

struct usb_device*usb_dev:目标 USB设备指针

unsigned int pipe:目标 USB设备的特定端点.必须使用特定的宏创建.

void*data:如果是 OUT端点,指向要发送到设备的数据的指针.如果是 IN端点,这是从设备读取的数据的缓冲区指针.

int len: data参数指向的缓冲的长度

int*actual_length:指向函数放置真实字节数的指针,根据端点方向,这些字节要么是被发送到设备的,要么是从设备中读取的.

int timeout:时钟嘀哒数,应等待的时间.如果为 0,函数永远等待操作完成.

返回值:成功返回0,actual_length参数包含被传送或从设备中读取的字节数.否则返回负的错误值.

(2)int usb_control_msg(struct usb_device*dev, unsigned int pipe, __u8 request,__u8 requesttype, __u16 value, __u16 index,void*data, __u16 size,int timeout)

功能:创建控制 urb并发送到指定的设备,接着在返回之前等待完成.

参数:

struct usb_device*usb_dev:目标 USB设备指针

unsigned int pipe:目标 USB设备的特定端点.必须使用特定的宏创建.

__u8 request:控制消息的 USB请求值.

__u8 requesttype:控制消息的 USB请求类型.

__u16 value:控制消息的 USB消息值.

__u16 index:控制消息的 USB消息索引值.

void*data:如果是 OUT端点,指向要发送到设备的数据的指针.如果是 IN端点,这是从设备读取的数据的缓冲区指针.

__u16 size: data参数指向的缓冲的长度

int timeout:时钟嘀哒数,应等待的时间.如果为 0,函数永远等待操作完成.

返回值:成功返回被传送到或从设备读取的字节数.否则返回负的错误值.

(3)int usb_interrupt_msg(struct usb_device*usb_dev, unsigned int pipe,void*data,int len, int*actual_length,int timeout)

功能:创建中断 urb并发送到指定的设备,接着在返回之前等待完成.其实就是usb_bulk_msg的包装,所有参数和usb_bulk_msg一样使用

如何玩转linux驱动

说玩转驱动这话,其实有点过头,玩驱动是个长期积累的过程,写出来是一回事,调试起来也是一种磨练。为了让大家明白玩驱动的乐趣和掌握编写驱动的捷径,我分享一些经验,算是抛砖引玉。不过正所谓一口吃不了个胖子,只有写够了足够多的代码,调试了足够多的模块,玩转驱动也不再话下。希望今天的唠叨对想踏入或者即将踏入驱动行业的你有些帮助。

我们很明白Linux设备驱动的学习是一项浩大的工程,正是由于这个原因,一些人不免望而生畏,其实,只要我们有足够的积累和全面的知识,玩转驱动,也是早晚的事。闲话少说,开始来干货。

对于驱动工程师来说,首先要明白驱动在整个系统中的作用,

大家从上图中可以看出,linux驱动②在这个构架中起到承上硬件①启下应用程序③的作用。在程序的编写中,我们常用高内聚低耦合的标准,因此,驱动的引入显得意义更加重大:一方面,使嵌入式应用工程师不用考虑过多的硬件差异,另一方面,通过将设备驱动融入内核,面向操作系统内核的接口,这样的接口由操作系统规定,对一类设备而言结构一致,独立于具体的设备。同时由于linux操作系统有内存管理和进程管理,因此对于多任务并发的要求时,操作系统和驱动的引入使得任务变得简单。但是对于不需要多任务调度、文件系统、内存管理等复杂功能时,在一个大while(1)循环中既可以完成相关的任务。

上面分析了驱动的意义,那么,玩转linux驱动需要那方面的知识呢,现在罗列下:

第一、Linux驱动工程师要有良好的硬件基础。

这个要求不言而喻,linux驱动工程师的主要任务就是隐藏硬件的差异,给应用工程师一个统一的接口,因此需要能看懂电路图,理解SRAM、Flash、SDRAM、磁盘等模块的读写方式,知道UART、I2C、USB等设备的接口以及常规操作,了解轮询、中断、DMA的原理,PCI总线的工作方式以及CPU的内存管理单元(MMU)等。不过对于这种常见的模块,linux内核中有相关的配置,因此需要有阅读linux内核的能力和修改linux内核的能力。

第二、Linux驱动工程师具有良好的C语言基础。

作为一个面向硬件底层和应用层的关键人物,C语言功底是必须要牢固的。在编写linux的字符设备和块设备驱动中常用的fopen()、fwrite()、fread()、fclose()以及内存分配中经常使用结构体和指针。因此能灵活地运用C语言的结构体、指针、函数指针及内存动态申请和释放显现的尤为重要。

例如字符设备驱动中的读函数函数的定义

/*读设备*/

ssize_t xxx_read(struct file*filp, char _ _user*buf, size_t count,loff_t*f_pos)

{

...

copy_to_user(buf,...,...);

...

}

从中看出C语言功底的重要性。

第三、 Linux驱动工程师具有一定的Linux内核基础,虽然并不要求工程师对内核各个部分有深入的研究,但至少要了解设备驱动与内核的接口,尤其是对于块设备、网络设备、Flash设备、串口设备等复杂设备。

现在工作起来,嵌入式驱动工程师的工作量相对会小一点,因为一般常见的硬件设备供应商都会提供相应的linux版本驱动,驱动工程师的任务就是调试这些驱动能正常运行在自己的系统中,同时保证系统的稳定。

第四、 Linux驱动工程师具有良好的操作系统知识。

这个要求对于没有学习过操作系统的人来说唯一的痛苦之处就是对于专有名词不是很理解,例如上半部,下半部,原子操作等。其实刚开始或许是个痛苦的过程,但是只要认真的分析了一个或者几个驱动程序后,你就会发现其中的规律。毕竟linux驱动大体分为字符设备驱动、块设备驱动和网络设备驱动三类,正所谓抓其纲要,举一反三,便可融会贯通。因此linux中多任务并发控制和同步等基础很重要,因为在设备驱动中会大量使用自旋锁、互斥、信号量、等待队列等并发与同步机制。

第五、动手能力。

纸上得来终觉浅,因此,看再多的书也没有真正的调试一个驱动来的认识深刻。这时你需要搭建宿主机平台,购买开发板。不要好大喜功,从简单的小驱动开始一步一步走,以蚂蚁啃骨头的精神进行学习,收获会很大。

经历了痛苦的折磨,现在看下嵌入式驱动工程师的甜蜜吧,工作个三五年,你已经是大师了,可以去招聘网站浏览下,这方面的待遇都是面议奖金都是大大的,红色票票也随心所愿了。想到这些,你还不下定决心来经受linux驱动的虐待,相信只要以“驱动虐我千百遍,我待驱动如初恋”的决心,相信你可以玩转linux驱动。

阅读剩余
THE END