linux回调函数 java回调函数写法

嵌入式Linux上的C语言编程实践的目录

第一部分基础知识

第1章 Linux环境下C语言的开发 2

1.1 Linux下的C语言开发环境 2

1.2在Linux中使用C语言开发 3

1.2.1开发流程和开发工具 3

1.2.2 Linux中程序的运行原理 4

第2章嵌入式环境中的C语言开发 7

2.1嵌入式C语言的开发环境 7

2.2嵌入式开发中C语言编程要点 9

第二部分 Linux环境中

C语言的开发环境和工具

第3章 Linux的文本编辑工具VI 12

3.1 VI编辑器概述 12

3.1.1 VI简介 12

3.1.2 VI的工作模式和使用

3.1.2之前的准备 12

3.1.3进入和退出VI 13

3.2 VI的增强版VIM 16

3.3 VI编辑器的基本使用方法 17

3.3.1在屏幕上移动光标 17

3.3.2插入文本 20

3.3.3删除文本 22

3.3.4修改文本内容 25

3.3.5替换文本内容 27

3.3.6合并文本内容 30

3.3.7移动文本内容 30

3.4 VI编辑器的命令和高级操作 32

3.4.1 VI常用命令的列表 32

3.4.2 VI的一些高级的操作和

3.1.2使用技巧 35

第4章 GCC程序开发工具 39

4.1 GNU工具综述 39

4.2 GCC的编译和连接 43

4.2.1工程示例 43

4.2.2编译、汇编和连接 46

4.2.3动态库 48

4.3 GCC的二进制工具 49

4.3.1 ar(归档工具) 49

4.3.2 readelf(读取ELF格式

3.1.2文件信息) 51

4.3.3 strings(查看字符串) 54

4.3.4 nm(显示符号信息) 55

4.3.5 strip(删除符号) 57

4.3.6 objdump(显示目标

3.1.2文件信息) 58

4.3.7 objcopy(复制目标文件) 63

第5章 make工程管理工具 67

5.1 make和Makefile 67

5.1.1 make机制概述 67

5.1.2 make和Makefile的使用 68

5.2 Makefile使用示例 69

5.2.1简单的Makefile 69

5.2.2 Makefile中的依赖关系 71

5.2.3 Makefile中使用隐含规则

3.1.2来编译程序 73

5.2.4 Makefile中指定依赖关系的

3.1.2编译 76

5.3自动生成Makefile 78

5.3.1自动生成Makefile的意义和

3.1.2相关工具 78

5.3.2自动生成Makefile的流程 79

第6章 GDB调试工具 85

6.1 GDB简介 85

6.2使用GDB调试程序 86

6.2.1基本操作 88

6.2.2查看命令 90

6.2.3高级命令 92

6.2.4 attach命令的使用 94

6.3远程GDB调试 95

6.3.1本地GDB调试和远程GDB

3.1.2调试的比较 95

6.3.2远程GDB调试流程 97

6.3.3远程GDB调试示例 98

第三部分库函数

第7章 C语言标准库函数 106

7.1 ISO的C语言标准库函数

7.1分类 106

7.2标准格式化输入/输出类函数 107

7.2.1 scanf函数:格式化输入

3.1.2字符串 107

7.2.2 printf函数:格式化输出

3.1.2字符串 109

7.2.3 putchar函数:输出字符到

3.1.2标准输出 111

7.2.4 getchar函数:从标准输入

3.1.2获取字符 111

7.2.5 putc函数:向文件输出字符 112

7.2.6 getc函数:从文件输入字符 112

7.2.7 gets函数:获得字符串 112

7.2.8 puts函数:输出指定字符串 113

7.2.9 ungetc函数:把字符

3.1.2写回流中 113

7.3字符处理类函数 114

7.4字符串处理及转换函数 116

7.4.1 sprintf函数:格式化输出

3.1.2字符串到一个缓冲区 116

7.4.2 strcat和strncat函数:

3.1.2字符串连接 119

7.4.3 strcpy和strncpy函数:

3.1.2字符串复制 120

7.4.4 strcmp和strncmp函数:

3.1.2字符串比较 121

7.4.5 strlen函数:获取字符串

3.1.2长度 122

7.4.6 strchr和strrchr函数:字符/

3.1.2字符串定位 122

7.4.7 strstr函数:字符串查找 123

7.4.8 strrev函数:字符串逆序 124

7.4.9 strupr和strlwr函数:字母

3.1.2形式转换 125

7.4.10 strdup和strndup函数:

3.1.2字符串复制 125

7.4.11 memset函数:内存设置 126

7.4.12 memmove函数:内存移动 126

7.4.13 memcmp函数:内存比较 127

7.4.14 memcpy函数:内存复制 128

7.5数学计算类函数 128

7.6数据结构和算法类函数 133

7.6.1 bsearch函数:二元搜索 133

7.6.2 lfind函数:线性搜索 134

7.6.3 lsearch函数:线性搜索 135

7.6.4 qsort函数:利用快速排序法

3.1.2排列数组 136

7.6.5 rand函数:产生随机数 136

7.6.6 srand函数:设置随机

3.1.2数种子 137

7.7文件I/O操作类相关函数 137

7.7.1 fopen函数:打开文件 138

7.7.2 fclose函数:关闭文件 139

7.7.3 fgetc函数:从文件中读取

3.1.2一个字符 139

7.7.4 fputc函数:将一指定字符

3.1.2写入文件流中 139

7.7.5 fgets函数:从文件中读取

3.1.2一字符串 140

7.7.6 fputs函数:将一指定的

3.1.2字符串写入文件内 140

7.7.7 rewind函数:重设文件流的

3.1.2读写位置为文件开头 141

7.7.8 ftell函数:取得文件流的

3.1.2读取位置 141

7.7.9 fseek函数:移动文件流的

3.1.2读写位置 141

7.7.10 fwrite函数:将数据写至

7.7.10文件流 142

7.7.11 fread函数:从文件流读取

7.7.10数据 142

7.7.12 remove函数:删除文件 143

7.7.13 rename函数:更改文件

7.7.10名称或位置 143

7.7.14 freopen函数:重新打开

7.7.10文件 144

7.7.15 fflush函数:同步缓冲区 144

7.7.16 fgetpos函数:获得文件

7.7.10位置 145

7.7.17 fsetpos函数:设置文件

7.7.10位置 145

7.7.18 mktemp函数:建立临时

7.7.10文件 146

7.7.19 tmpfile函数:临时文件 146

7.7.20 tmpnam:得到临时文件名 147

7.8日期时间类函数 147

7.8.1 clock函数:获得CPU时间 148

7.8.2 time函数:获得当前日历

7.8.2时间 148

7.8.3 difftime函数:获得时间

7.8.2差值 148

7.8.4 gmtime函数:将日历时间

7.8.2转换成UTC时间 149

7.8.5 mktime函数:将UTC时间

7.8.2转换成日历时间 149

7.8.6 asctime函数:将UTC时间

7.8.2转换成字符串 149

7.8.7 ctime函数:将日历时间转换

7.8.2成当地时间的字符串 150

7.8.8 localtime函数:将日历时间

7.8.2转换成本地时间 150

7.8.9 strftime函数:转换日期和

7.8.2时间格式 151

7.9国际化和本地化函数 152

7.9.1 setlocale函数:本地化控制

7.8.2函数 153

7.9.2 localeconv函数:本地化

7.8.2转换 154

7.10错误处理类函数 155

7.10.1 clearerr函数:清除流中的

7.10.1结束指示符和错误指示符 155

7.10.2 feof函数:指示文件结束 155

7.10.3 ferror函数:指示文件出错 156

7.10.4 perror函数:输出出错信息 156

7.10.5 errno函数:错误编号记录 156

7.11其他一些工具函数 157

7.11.1 assert函数:程序诊断 157

7.11.2长跳转函数 157

7.11.3可变长的参数控制函数 160

7.11.4获取结构体成员函数

7.10.1(宏) 161

7.12一些标准库中有用的宏 161

第8章 Linux中C语言的扩展库

函数 163

8.1文件I/O操作函数 163

8.1.1 open函数:打开文件 163

8.1.2 close函数:关闭文件 164

8.1.3 read函数:读文件 165

8.1.4 write函数:写文件 165

8.1.5 lseek函数:文件定位 167

8.1.6 ioctl函数:文件控制 167

8.1.7 flock函数:锁定文件 167

8.1.8 mmap函数和munmap函数:

8.1.8内存映射 168

8.1.9 create函数:创建新文件 170

8.1.10 dup函数和dup2函数:

8.1.10复制文件描述符 171

8.1.11 fcntl函数:改变已打开的

8.1.10文件的属性 171

8.2文件权限相关的操作函数 172

8.2.1 access函数:判断是否

8.2.1具有存取文件的权限 172

8.2.2 chown函数和fchown函数:

8.2.1改变文件的所有者 173

8.2.3 chmod函数和fchmod函数:

8.2.1改变权限 173

8.2.4 unlink函数:删除文件 173

8.2.5 utime函数和utimes函数:

8.2.1改变文件时间 174

8.2.6 umask函数:设置建立

8.2.1新文件时的权限掩码 175

8.2.7 link函数:建立文件连接 175

8.2.8 stat函数、fstat函数和lstat

8.2.1函数:获取文件信息 175

8.3用户组操作函数 176

8.3.1 getgid函数和setgid函数:

8.2.1获得/设置组识别码 176

8.3.2 getegid函数和setegid函数:

8.2.1获得/设置有效的组识别码 177

8.3.3 getuid函数和setuid函数:

8.2.1获得/设置真实的用户识别码 177

8.3.4 geteuid函数和seteuid函数:

8.2.1获得/设置有效的用户识别码 178

8.3.5 getgroups函数和setgroups

8.2.1函数:获得/设置组代码 178

8.4信号类函数 179

8.4.1 kill函数:传送信号给指定的

8.2.1进程 181

8.4.2 raise函数:信号发送函数 181

8.4.3 alarm函数:设置超时函数 182

8.4.4 signal函数:信号安装函数 182

8.5进程处理函数 183

8.5.1 getpid函数和getppid函数:

8.2.1获得进程ID和父进程ID 183

8.5.2 fork函数:建立子进程 183

8.5.3 sleep函数和usleep函数:

8.2.1让进程暂停执行一段时间 185

8.5.4 exec函数族:找到可执行

8.2.1文件 185

8.5.5 _ exit函数和_Exit函数:

8.2.1结束进程执行 188

第四部分 C语言高级编程

第9章动态内存的堆与栈 190

9.1程序内存区域的使用 190

9.1.1静态内存与动态内存 190

9.1.2 C语言中的动态内存 191

9.2 C程序中栈空间的使用 196

9.2.1参数使用栈空间 196

9.2.2自动变量使用栈空间 199

9.2.3程序中较大的栈 201

9.2.4栈空间的特性 202

9.3 C程序中的堆空间使用 203

9.3.1分配和释放堆内存的库函数 203

9.3.2库函数使用 204

9.3.3堆内存的特性 218

9.4堆内存和栈内存使用的比较 222

9.4.1利用返回值传递信息 222

9.4.2利用参数传递信息 226

9.4.3堆与栈内存管理的区别 231

第10章函数指针的使用 232

10.1函数指针的概念 232

10.1.1 C语言函数的本质 232

10.1.2函数指针在C语言中的

10.1.2意义 234

10.2函数指针的使用 237

10.2.1函数指针使用初步 237

10.2.2函数指针的类型定义 240

10.2.3函数指针作为结构体成员 242

10.2.4函数指针作为函数的参数 243

10.2.5函数指针作为函数的

10.2.5返回值 244

10.2.6函数指针数组 246

10.3函数指针使用示例 248

第11章回调函数的使用 252

11.1回调函数的概念与作用 252

11.1.1程序调用的方式 252

11.1.2回调函数的作用 254

11.2回调函数的语法 254

11.2.1简单的回调函数 254

11.2.2完全形式的回调函数 256

11.3回调函数的使用 259

11.3.1 qsort中的回调函数 259

11.3.2 atexit和on_exit函数的

10.2.5注册退出函数 263

第12章 C语言实现对象编程 268

12.1 C语言实现基于对象编程的

12.1概念与作用 268

12.2 C语言基于对象编程实现

12.1封装 269

12.2.1简单的程序示例 269

12.2.2 C语言基于对象编程的

10.2.5详解 272

12.2.3 C语言基于对象编程与

10.2.5 C++面向对象编程的对比 275

12.3 C语言基于对象编程实现

12.3部分继承功能 278

12.3.1利用数据结构的包含实现

10.2.5继承功能 279

12.3.2利用私有指针实现继承

10.2.5功能 282

12.3.3 C语言实现继承的总结 287

12.4 C语言基于对象编程实现

12.4部分多态功能 288

12.4.1利用操作指针组的包含

10.2.5实现多态功能 288

12.4.2 C语言实现多态功能的总结 292

12.5对C语言实现基于对象

12.5编程的思考 292

12.5.1 C语言基于对象编程的

10.2.5特性 292

12.5.2 C语言基于对象编程中接口、

10.2.5实现和调用者的关系 293

第五部分在嵌入式

环境下的C语言编程

第13章 C语言程序的内存布局 295

13.1 C语言程序的存储区域 295

13.2 C语言程序的段 297

13.2.1段的分类 297

13.2.2程序中段的使用 298

13.3可执行程序的连接 301

13.3.1可执行程序的组成 301

13.3.2各个目标文件的关系 303

13.3.3连接错误示例 304

13.4 C语言程序的运行 309

13.4.1 RAM调试运行 311

13.4.2固化程序的XIP运行 312

13.4.3固化程序的加载运行 313

13.4.4 C语言程序的运行总结 315

第14章嵌入式C语言常用语法 317

14.1内存指针操作 317

14.1.1内存操作的意义 317

14.1.2使用指针操作内存 319

14.1.3 volatile的使用 324

14.1.4嵌入式系统指针的实际

10.2.5应用 325

14.2位操作 327

14.2.1位操作的意义 327

14.2.2位操作的语法 328

14.3大小端与对齐问题 330

14.3.1大小端问题 331

14.3.2内存对齐问题 335

14.3.3结构体成员的对齐问题 338

14.4程序的跳转 344

14.4.1嵌入式系统程序跳转的

10.2.5类型 344

14.4.2 C语言中实现程序的跳转 345

第15章嵌入式C语言编程的技巧 348

15.1程序的优化技巧 348

15.1.1循环缓冲区 348

15.1.2查表法 350

15.1.3针对循环执行效率的

10.2.5优化 353

15.2关于小数运算 355

15.3函数参数和返回值的传递 357

15.4变量的初始化技巧 360

15.4.1数组的初始化 360

15.4.2结构体的初始化 362

15.4.3变量的初始化总结 362

15.5程序的调试和宏使用的技巧 363

15.5.1打印文件、函数和程序行 363

15.5.2#:字符串化操作符 364

15.5.3##:连接操作符 366

15.5.4调试宏的第一种定义方式 367

15.5.5调试宏的第二种定义方式 368

15.5.6对调试语句进行分级审查 369

15.5.7条件编译调试语句 370

15.5.8使用do…while的宏定义 372

15.6代码剖析 373

参考文献 378

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异步IO

Linux中最常用的IO模型是同步IO,在这个模型中,当请求发出之后,应用程序就会阻塞,直到请求满足条件为止。这是一种很好的解决方案,调用应用程序在等待IO完成的时候不需要占用CPU,但是在很多场景中,IO请求可能需要和CPU消耗交叠,以充分利用CPU和IO提高吞吐率。

下图描绘了异步IO的时序,应用程序发起IO操作后,直接开始执行,并不等待IO结束,它要么过一段时间来查询之前的IO请求完成情况,要么IO请求完成了会自动被调用与IO完成绑定的回调函数。

Linux的AIO有多种实现,其中一种实现是在用户空间的glibc库中实现的,本质上是借用了多线程模型,用开启的新的线程以同步的方式做IO,新的AIO辅助线程与发起AIO的线程以pthread_cond_signal()的形式进行线程间的同步,glibc的AIO主要包含以下函数:

1、aio_read()

aio_read()函数请求对一个有效的文件描述符进行异步读操作。这个文件描述符可以代表一个文件、套接字,甚至管道,aio_read()函数原型如下:

aio_read()函数在请求进行排队之后就会立即返回(尽管读操作并未完成),如果执行成功就返回0,如果出现错误就返回-1。参数aiocb(AIO I/O Control Block)结构体包含了传输的所有信息,以及为AIO操作准备的用户空间缓冲区。在产生IO完成通知时,aiocb结构就被用来唯一标识所完成的IO操作。

2.aio_write()

aio_write()函数用来请求一个异步写操作。函数原型如下:

aio_write()函数会立即返回,并且它的请求以及被排队(成功时返回值为0,失败时返回值为-1)

3.aio_error()

aio_error()函数被用来确定请求的状态,其原型如下:

该函数的返回:

4.aio_return()

异步IO和同步阻塞IO方式之间有一个区别就是不能立即访问函数的返回状态,因为异步IO没有阻塞在read()调用上。在标准的同步阻塞read()调用中,返回状态是在该函数返回时提供的。

但是在异步IO中,我们要用aio_return()函数,原型如下:

只有在aio_error()调用确定请求已经完成(可能成功、也可能发生了错误)之后,才会调用这个函数,aio_return()的返回值就等价于同步情况中read()或者write系统调用的返回值。

5.aio_suspend()

用户可以用该函数阻塞调用进程,直到异步请求完成为止,调用者提供了一个aiocb引用列表,其中任何一个完成都会导致aio_suspend()返回。函数原型如下:

6.aio_cancel()

该函数允许用户取消对某个文件描述符执行的一个或所以IO请求。

要取消一个请求,用户需要提供文件描述符和aiocb指针,如果这个请求被成功取消了,那么这个函数就会返回AIO_CANCELED。如果请求完成了,就会返回AIO_NOTCANCELED。

7.lio_listio()

lio_listio()函数可用于同时发起多个传输。这个函数非常重要,它使得用户可以在一个系统调用中启动大量的IO操作,原型如下:

mode参数可以是LIO_WAIT或者是LIO_NOWAIT。LIO_WAIT会阻塞这个调用,直到所有的IO都返回为止,若是LIO_NOWAIT模型,在IO操作完成排队之后,该函数就会返回。list是一个aiocb的列表,最大元素的个数是由nent定义的。如果list的元素为null,lio_listio()会将其忽略。

阅读剩余
THE END