Compare commits

...

10 Commits

Author SHA1 Message Date
zmr961006
2c65133c0e add 2017-04-30 11:20:39 +08:00
zmr961006
11582cf8dd Add 2017-04-30 10:26:37 +08:00
zmr961006
c24d370fae usb1 2017-04-26 11:26:47 +08:00
zmr961006
80385d7e9e add 2017-04-26 10:08:12 +08:00
zmr961006
6987b94dac add 2017-04-26 10:05:34 +08:00
zmr961006
dc7735183a update 2017-04-26 10:02:25 +08:00
zmr961006
fd0cd58a77 interpci 2017-04-26 09:23:11 +08:00
zmr961006
7607150f36 inter 2017-04-25 09:55:19 +08:00
zmr961006
7f8fa1fc81 port_pci_interr 2017-04-24 09:21:43 +08:00
root
b7c95265f5 ad 2017-04-24 09:14:02 +08:00
26 changed files with 1900 additions and 7 deletions

133
PCI_driver/README.md Normal file
View File

@@ -0,0 +1,133 @@
## PCI驱动程序
一.理论
1. PCI总线的特点
1速度上快时钟频率提高到33M而且还为进一步把时钟频率提高到66MHZ、总线带宽提高到64位留下了余地。2对于地址的分配和设置系统软件课自动设置每块外设通过某种途径告诉系统该外设有几个存储区间和I/O地址区间每个区间的大小以及本地地址。系统软件知道了总共有多少外设以及各种的存储空间后就会统一为外设分配物理地址。3对于总线的竞争PCI总线上配备了一个仲裁器遇到冲突仲裁器会选择其中之一暂时成为当前的主设备而其他只能等待。同时考虑到这样的效率问题PCI总线为写提纲了缓冲故写比读会快。4对于总线扩充问题PCI总线引入HOST-PCI桥北桥,PCI-PCI桥,PCI-ISA桥南桥。CPU与内存通过系统总线连接北桥连接内存控制器与主PCI总线南桥连接主PCI总线和ISA总线PCI-PCI桥连接主PCI总和次层PCI总线。
2. PCI设备概述
每个PCI设备有许多地址配置的寄存器初始化时要通过这些寄存器来配置该设备的总线地址一旦完成配置以后CPU就可以访问该设备的各项资源了。PCI标准规定每个设备的配置寄存器组最多可以有256个连续的字节空间开头64个字节叫头部分为0型PCI设备和1型PCI桥头部头部开头16个字节是设备的类型、型号和厂商等。这些头部寄存器除了地址配置的作用还能使CPU能够探测到相应设备的存在这样就不需要用户告诉系统都有哪些设备了而是改由CPU通过一个号称枚举的过程自动扫描探测所有挂接在PCI总线上的设备。
设备的配置寄存器组采用相同的地址由所在总线的PCI桥在访问时附加上其他条件区分对于I386处理器有两个32位寄存器0XCF8为地址寄存器0XCFC为数据寄存器。地址寄存器写入的内容包括总线号设备号功能号。逻辑地址XX:YY.ZXX表示PCI总线号最多256个总线。YY表示PCI设备号最多32个设备。Z表示PCI设备功能号最多8个功能。
3. 查询PCI总线和设备的命令
查看PCI总线和PCI设备组成的树状图 lspci t
查看配置区的情况 lspci x注意PCI寄存器是小端格式
4. PCI总线架构
所有的根总线都链接在pci_root_buses链表中。Pci_bus ->device链表链接着该总线下的所有设备。而pci_bus->children链表链接着它的下层总线对于pci_dev来说pci_dev->bus指向它所属的pci_bus. Pci_dev->bus_list链接在它所属bus的device链表上。此外所有pci设备都链接在pci_device链表中。
5. ********
PCI驱动
1. PCI寻找空间
PCI设备包括杀个寻址空间配置空间I/O端口空间内存空间。
1.1 PCI配置空间
内核为驱动提供的函数:
pci_read_config_byte/word/dword(struct pci_dev *pdev, int offset, int *value)
pci_write_config_byte/word/dword(struct pci_dev *pdev, int offset, int *value)
配置空间的偏移定义在include/Linux/pci_regs.h
1.2 PCI的I/O和内存空间
从配置区相应寄存器得到I/O区域的基址
pci_resource_start(struct pci_dev *dev, int bar) Bar值的范围为0-5。
从配置区相应寄存器得到I/O区域的内存区域长度
pci_resource_length(struct pci_dev *dev, int bar) Bar值的范围为0-5。
从配置区相应寄存器得到I/O区域的内存的相关标志
pci_resource_flags(struct pci_dev *dev, int bar) Bar值的范围为0-5。
申请I/O端口:
request_mem_region(io_base, length, name)
读写:
inb() inw() inl() outb() outw() outl()
### 注册一个PCI 设备
```
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
static struct pci_device_id ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_3), },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, ids);
static unsigned char skel_get_revision(struct pci_dev *dev)
{
u8 revision;
pci_read_config_byte(dev, PCI_REVISION_ID, &revision);
return revision;
}
static int probe(struct pci_dev *dev, const struct pci_device_id *id)
{
/* Do probing type stuff here.
* Like calling request_region();
*/
pci_enable_device(dev);
if (skel_get_revision(dev) == 0x42)
return -ENODEV;
return 0;
}
static void remove(struct pci_dev *dev)
{
/* clean up any allocated resources and stuff here.
* like call release_region();
*/
}
static struct pci_driver pci_driver = {
.name = "pci_skel",
.id_table = ids,
.probe = probe,
.remove = remove,
};
static int __init pci_skel_init(void)
{
return pci_register_driver(&pci_driver);
}
static void __exit pci_skel_exit(void)
{
pci_unregister_driver(&pci_driver);
}
MODULE_LICENSE("GPL");
module_init(pci_skel_init);
module_exit(pci_skel_exit);
```
参考http://blog.csdn.net/weiqing1981127/article/details/8031541

31
PCI_driver/code/Makefile Executable file
View File

@@ -0,0 +1,31 @@
# To build modules outside of the kernel tree, we run "make"
# in the kernel source tree; the Makefile these then includes this
# Makefile once again.
# This conditional selects whether we are being included from the
# kernel Makefile or not.
ifeq ($(KERNELRELEASE),)
# Assume the source tree is where the running kernel was built
# You should set KERNELDIR in the environment if it's elsewhere
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
# The current directory is passed to sub-makes as argument
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* module*
.PHONY: modules modules_install clean
else
# called from kernel build system: just declare what our modules are
obj-m := pci_skel.o
endif

View File

@@ -0,0 +1,63 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
static struct pci_device_id ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_3), },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, ids);
static unsigned char skel_get_revision(struct pci_dev *dev)
{
u8 revision;
pci_read_config_byte(dev, PCI_REVISION_ID, &revision);
return revision;
}
static int probe(struct pci_dev *dev, const struct pci_device_id *id)
{
/* Do probing type stuff here.
* Like calling request_region();
*/
pci_enable_device(dev);
if (skel_get_revision(dev) == 0x42)
return -ENODEV;
return 0;
}
static void remove(struct pci_dev *dev)
{
/* clean up any allocated resources and stuff here.
* like call release_region();
*/
}
static struct pci_driver pci_driver = {
.name = "pci_skel",
.id_table = ids,
.probe = probe,
.remove = remove,
};
static int __init pci_skel_init(void)
{
return pci_register_driver(&pci_driver);
}
static void __exit pci_skel_exit(void)
{
pci_unregister_driver(&pci_driver);
}
MODULE_LICENSE("GPL");
module_init(pci_skel_init);
module_exit(pci_skel_exit);

View File

@@ -2,19 +2,23 @@
### 前言
学习内核相关的东西完整算起来一年半了,不得不说内核的学习是一个学习成本很高的东西,慢慢摸索也逐渐的有了感悟,简单来说,需要如下的几个阶段:
为了降低后续同学学习内核的难度我把《LDD3》中的代码和内容做了整理。
1.基础阶段 操作系统C、asm 编程语言LINUX系统编程
#### 我做了那些事:
2.中级阶段 了解内核架构API学习编写内核模块)
1.代码升级将2.6.7 的代码 调整到了 4.0.4 。方便近两年学习内核的同学实验。
3.中高级阶段 (开发独立功能点,驱动程序的学习与编写)
2.删繁就简,将书中复杂的示例替换为简单示例,便于理解。
4.高级阶段 (参与内核的开发)
3.总结概括,记录了每一章的主要内容,实验截图。
ps:我只做了总结框架,还需要进一步完善,改正错误的地方。(考研不易,时间有限)
我现在将《Linux设备驱动程序第3版》简称LDD3 中内容整理抽取。由于这本书的内核版本有点老。我就把代码都移植到了我4.0.4的内核上,调通运行,既是自己的
学习也是为了后续同学们学习内核提供点参考资料。考研不易,已是见缝插针,难免有错误疏漏之处还请指正。
#### 后续同学需要做那些事:
1.进一步完善我总结的文档。
2.增加实验总结。
### 章节
@@ -39,4 +43,15 @@
*[IO端口通信](./IO_port/README.md)
*[内核数据类型](./kernel_DS/README.md)
*[内核中断处理](./interrupt/README.md)
*[PCI驱动程序](./PCI_driver/README.md)
*[USB驱动程序](./USB_driver/README.md)

446
USB_driver/README.md Normal file
View File

@@ -0,0 +1,446 @@
## USB 驱动程序
### USB的一般化定义
(概念来源于互联网资料)
从1994年11月11日发表了USB V0.7版本以后USB版本经历了多年的发展已经发展为3.1版本成为二十一世纪电脑中的标准扩展接口。当前2016年主板中主要是采用USB2.0和USB3.0接口各USB版本间能很好的兼容。USB用一个4针USB3.0标准为9针插头作为标准插头采用菊花链形式可以把所有的外设连接起来最多可以连接127个外部设备并且不会损失带宽。USB需要主机硬件、操作系统和外设三个方面的支持才能工作。二十一世纪的主板一般都采用支持USB功能的控制芯片组主板上也安装有USB接口插座而且除了背板的插座之外主板上还预留有USB插针可以通过连线接到机箱前面作为前置USB接口以方便使用注意在接线时要仔细阅读主板说明书并按图连接千万不可接错而使设备损坏。而且USB接口还可以通过专门的USB连机线实现双机互连并可以通过Hub扩展出更多的接口。USB具有传输速度快使用方便支持热插拔连接灵活独立供电等优点可以连接鼠标、键盘、打印机、扫描仪、摄像头、充电器、闪存盘、MP3机、手机、数码相机、移动硬盘、外置光驱/软驱、USB网卡、ADSL Modem、Cable Modem等几乎所有的外部设备。
理论上USB接口可用于连接多达127个外设如鼠标、调制解调器和键盘等。USB自从1996年推出后已成功替代串口和并口并成为二十一世纪个人电脑和大量智能设备的必配的接口之一。
### 软件结构
每个USB只有一个主机它包括以下几层
总线接口
USB总线接口处理电气层与协议层的互连。从互连的角度来看相似的总线接口由设备及主机同时给出例如串行接口机SIE。USB总线接口由主控制器实现。
USB系统用主控制器管理主机与USB设备间的数据传输。它与主控制器间的接口依赖于主控制器的硬件定义。同时USB系统也负责管理USB资源例如带宽和总线能量这使客户访问USB成为可能。
USB系统还有三个基本组件
1.主控制器驱动程序HCD这可把不同主控制器设备映射到USB系统中。HCD与USB之间的接口叫HCDI特定的HCDI由支持不同主控制器的操作系统定义通用主控制器驱动器UHCD处于软结构的最底层由它来管理和控制主控制器。UHCD实现了与USB主控制器通信和控制。
2.USB主控制器并且它对系统软件的其他部分是隐蔽的。系统软件中的最高层通过UHCD的软件接口与主控制器通信。
3.USB驱动程序USBD它在UHCD驱动器之上它提供驱动器级的接口满足现有设备驱动器设计的要求。USBD以I/O请求包IRPs的形式提供数据传输架构它由通过特定管道Pipe传输数据的需求组成。此外USBD使客户端出现设备的一个抽象以便于抽象和管理。作为抽象的一部分USBD拥有缺省的管道。通过它可以访问所有的USB设备以进行标准的USB控制。该缺省管道描述了一条USBD和USB设备间通信的逻辑通道。
主机软件
在某些操作系统中没有提供USB系统软件。这些软件本来是用于向设备驱动程序提供配置信息和装载结构的。在这些操作系统中设备驱动程序将应用提供的接口而不是直接访问USBDIUSB驱动程序接口结构。
USB客户软件
它是位于软件结构的最高层负责处理特定USB设备驱动器。客户程序层描述所有直接作用于设备的软件入口。当设备被系统检测到后这些客户程序将直接作用于外围硬件。这个共享的特性将USB系统软件置于客户和它的设备之间这就要根据USBD在客户端形成的设备映像由客户程序对它进行处理。
主机各层有以下功能:
1.检测连接和移去的USB设备。
2.管理主机和USB设备间的数据流。
3.连接USB状态和活动统计。
4.控制主控制器和USB设备间的电气接口包括限量能量供应。
5.HCD提供了主控制器的抽象和通过USB传输的数据的主控制器视角的一个抽象。USBD提供了USB设备的抽象和USBD客户与USB功能间数据
6.传输的一个抽象。USB系统促进客户和功能间的数据传输并作为USB设备的规范接口的一个控制点。USB系统提供缓冲区管理能力并允许数据传输同步于客户和功能的需求。
### 硬件结构
USB采用四线电缆其中两根是用来传送数据的串行通道另两根为下游Downstream设备提供电源对于任何已经成功连接且相互识别的外设将以双方设备均能够支持的最高速率传输数据。USB总线会根据外设情况在所兼容的传输模式中自动地由高速向低速动态转换且匹配锁定在合适的速率。
USB是基于令牌的总线。类似于令牌环网络或FDDI基于令牌的总线。USB主控制器广播令牌总线上设备检测令牌中的地址是否与自身相符通过接收或发送数据给主机来响应。USB通过支持悬挂/恢复操作来管理USB总线电源。USB系统采用级联星型拓扑该拓扑由三个基本部分组成主机Host集线器Hub和功能设备。
主机也称为根根结或根Hub它做在主板上或作为适配卡安装在计算机上主机包含有主控制器和根集线器Root Hub控制着USB
总线上的数据和控制信息的流动每个USB系统只能有一个根集线器它连接在主控制器上一台计算机可能有多个根集线器。
集线器是USB结构中的特定成分它提供叫做端口Port的点将设备连接到USB总线上同时检测连接在总线上的设备并为这些设备提供
电源管理,负责总线的故障检测和恢复。集线可为总线提供能源,亦可为自身提供能源(从外部得到电源)。
功能设备通过端口与总线连接。USB同时可做Hub使用。
### 数据传输
主控制器负责主机和USB设备间数据流的传输。这些传输数据被当作连续的比特流。每个设备提供了一个或多个可以与客户程序通信的接口每个接口由0个或多个管道组成它们分别独立地在客户程序和设备的特定终端间传输数据。USBD为主机软件的现实需求建立了接口和管道当提出配置请求时主控制器根据主机软件提供的参数提供服务。
USB支持四种基本的数据传输模式控制传输等时传输中断传输及数据块传输。每种传输模式应用到具有相同名字的终端则具有不同的性质。
控制传输类型
支持外设与主机之间的控制,状态,配置等信息的传输,为外设与主机之间提供一个控制通道。每种外设都支持控制传输类型,这样主机与外设之间就可以传送配置和命令/状态信息。
等时lsochronous传输类型或称同步传输
支持有周期性有限的时延和带宽且数据传输速率不变的外设与主机间的数据传输。该类型无差错校验故不能保证正确的数据传输支持像计算机电话集成系统CTI和音频系统与主机的数据传输。
中断传输类型
支持像游戏手柄,鼠标和键盘等输入设备,这些设备与主机间数据传输量小,无周期性,但对响应时间敏感,要求马上响应。
数据块Bulk传输类型
支持打印机扫描仪数码相机等外设这些外设与主机间传输的数据量大USB在满足带宽的情况下才进行该类型的数据传输。
USB采用分块带宽分配方案若外设超过当前带宽分配或潜在的要求则不能进入该设备。同步和中断传输类型的终端保留带宽并保证数据按一定的速率传送。集中和控制终端按可用的最佳带宽来传输传输数据。
### USB 驱动程序架构
![s](./image/usb.png)
USB 核心为驱动程序提供了一个用于访问控制的统一USB硬件接口。
### USB 端点
USB通信的最基本形式通过端点来只能向一个方向传送数据从主机到设备或者从设备到主机。共有4种类型。
控制控制对于USB不同部分的访问。
中断每当USB宿主要求传输时以一个固定的速率传输少量数据。
批量:传输大批量的数据。
等时:传输大批量数据,到达没有保正。
代码结构:
```
/**
* struct usb_host_endpoint - host-side endpoint descriptor and queue
* @desc: descriptor for this endpoint, wMaxPacketSize in native byteorder
* @ss_ep_comp: SuperSpeed companion descriptor for this endpoint
* @urb_list: urbs queued to this endpoint; maintained by usbcore
* @hcpriv: for use by HCD; typically holds hardware dma queue head (QH)
* with one or more transfer descriptors (TDs) per urb
* @ep_dev: ep_device for sysfs info
* @extra: descriptors following this endpoint in the configuration
* @extralen: how many bytes of "extra" are valid
* @enabled: URBs may be submitted to this endpoint
* @streams: number of USB-3 streams allocated on the endpoint
*
* USB requests are always queued to a given endpoint, identified by a
* descriptor within an active interface in a given USB configuration.
*/
struct usb_host_endpoint { //usb 节点
struct usb_endpoint_descriptor desc;
struct usb_ss_ep_comp_descriptor ss_ep_comp;
struct list_head urb_list;
void *hcpriv;
struct ep_device *ep_dev; /* For sysfs info */
unsigned char *extra; /* Extra descriptors */
int extralen;
int enabled;
int streams;
};
```
```
/* USB_DT_ENDPOINT: Endpoint descriptor */ usb真正端点信息
struct usb_endpoint_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bEndpointAddress; /*特定端点USB地址*/
__u8 bmAttributes; /*端点类型*/
__le16 wMaxPacketSize;
__u8 bInterval;
/* NOTE: these two are _only_ in audio endpoints. */
/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
__u8 bRefresh;
__u8 bSynchAddress;
} __attribute__ ((packed));
```
### 接口
USB端点被捆绑为接口。内核使用struct usb_interface 结构来描述USB接口。
```
/**
* struct usb_interface - what usb device drivers talk to
* @altsetting: array of interface structures, one for each alternate
* setting that may be selected. Each one includes a set of
* endpoint configurations. They will be in no particular order.
* @cur_altsetting: the current altsetting.
* @num_altsetting: number of altsettings defined.
* @intf_assoc: interface association descriptor
* @minor: the minor number assigned to this interface, if this
* interface is bound to a driver that uses the USB major number.
* If this interface does not use the USB major, this field should
* be unused. The driver should set this value in the probe()
* function of the driver, after it has been assigned a minor
* number from the USB core by calling usb_register_dev().
* @condition: binding state of the interface: not bound, binding
* (in probe()), bound to a driver, or unbinding (in disconnect())
* @sysfs_files_created: sysfs attributes exist
* @ep_devs_created: endpoint child pseudo-devices exist
* @unregistering: flag set when the interface is being unregistered
* @needs_remote_wakeup: flag set when the driver requires remote-wakeup
* capability during autosuspend.
* @needs_altsetting0: flag set when a set-interface request for altsetting 0
* has been deferred.
* @needs_binding: flag set when the driver should be re-probed or unbound
* following a reset or suspend operation it doesn't support.
* @authorized: This allows to (de)authorize individual interfaces instead
* a whole device in contrast to the device authorization.
* @dev: driver model's view of this device
* @usb_dev: if an interface is bound to the USB major, this will point
* to the sysfs representation for that device.
* @pm_usage_cnt: PM usage counter for this interface
* @reset_ws: Used for scheduling resets from atomic context.
* @resetting_device: USB core reset the device, so use alt setting 0 as
* current; needs bandwidth alloc after reset.
*
* USB device drivers attach to interfaces on a physical device. Each
* interface encapsulates a single high level function, such as feeding
* an audio stream to a speaker or reporting a change in a volume control.
* Many USB devices only have one interface. The protocol used to talk to
* an interface's endpoints can be defined in a usb "class" specification,
* or by a product's vendor. The (default) control endpoint is part of
* every interface, but is never listed among the interface's descriptors.
*
* The driver that is bound to the interface can use standard driver model
* calls such as dev_get_drvdata() on the dev member of this structure.
*
* Each interface may have alternate settings. The initial configuration
* of a device sets altsetting 0, but the device driver can change
* that setting using usb_set_interface(). Alternate settings are often
* used to control the use of periodic endpoints, such as by having
* different endpoints use different amounts of reserved USB bandwidth.
* All standards-conformant USB devices that use isochronous endpoints
* will use them in non-default settings.
*
* The USB specification says that alternate setting numbers must run from
* 0 to one less than the total number of alternate settings. But some
* devices manage to mess this up, and the structures aren't necessarily
* stored in numerical order anyhow. Use usb_altnum_to_altsetting() to
* look up an alternate setting in the altsetting array based on its number.
*/
struct usb_interface {
/* array of alternate settings for this interface,
* stored in no particular order */
struct usb_host_interface *altsetting;
struct usb_host_interface *cur_altsetting; /* the currently
* active alternate setting */
unsigned num_altsetting; /* number of alternate settings */
/* If there is an interface association descriptor then it will list
* the associated interfaces */
struct usb_interface_assoc_descriptor *intf_assoc;
int minor; /* minor number this interface is
* bound to */
enum usb_interface_condition condition; /* state of binding */
unsigned sysfs_files_created:1; /* the sysfs attributes exist */
unsigned ep_devs_created:1; /* endpoint "devices" exist */
unsigned unregistering:1; /* unregistration is in progress */
unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */
unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */
unsigned needs_binding:1; /* needs delayed unbind/rebind */
unsigned resetting_device:1; /* true: bandwidth alloc after reset */
unsigned authorized:1; /* used for interface authorization */
struct device dev; /* interface specific device info */
struct device *usb_dev;
atomic_t pm_usage_cnt; /* usage counter for autosuspend */
struct work_struct reset_ws; /* for resets in atomic context */
};
```
### 配置
USB 接口本身被捆绑为配置。
内核使用usb_host_config 描述USB配置使用struct usb_device 结构体描述整个USB 设备。
### 概述USB
设备通常有一个或多个配置
配置经常具有一个或者多个更多的接口
接口通常具有一个或多个更多配置
接口没有或者具有一个以上的端点
### URB
usb请求块urbusb request block,urb是usb设备驱动中用来描述与usb设备通信所用的基本载体和核心数据结构。
是usb主机和设备通信的“电波”。
一个 urb 用来发送或接受数据到或者从一个特定 USB 设备上的特定的 USB 端点, 以一种异步的方式. 它用起来非常象一个 kiocb 结构被用在文件系统异步 I/O 代码, 或者如同一个 struct skbuff 用在网络代码中. 一个 USB 设备驱动可能分配许多 urb 给一个端点或者可能重用单个 urb 给多个不同的端点, 根据驱动的需要. 设备中的每个端点都处理一个 urb 队列, 以至于多个 urb 可被发送到相同的端点, 在队列清空之前. 一个 urb 的典型生命循环如下:
1.由一个 USB 设备驱动创建.
2.分配给一个特定 USB 设备的特定端点.
3.由USB设备驱动程序提交到USB核心。
4.提交给特定设备的被 USB 核心指定的 USB 主机控制器驱动, .
5.由USB 主机控制器处理, 它从设备进行USB传送。
6.当 urb 完成, USB 主机控制器驱动通知 USB 设备驱动程序.
#### struct urb
```
struct urb {
/* private: usb core and host controller only fields in the urb */
struct kref kref; /* reference count of the URB */
void *hcpriv; /* private data for host controller */
atomic_t use_count; /* concurrent submissions counter */
atomic_t reject; /* submissions will fail */
int unlinked; /* unlink error code */
/* public: documented fields in the urb that can be used by drivers */
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; /* (in) pointer to associated device */
struct usb_host_endpoint *ep; /* (internal) pointer to endpoint */
unsigned int pipe; /* (in) pipe information */
unsigned int stream_id; /* (in) stream ID */
int status; /* (return) non-ISO status */
unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/
void *transfer_buffer; /* (in) associated data buffer */
dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */
struct scatterlist *sg; /* (in) scatter gather buffer list */
int num_mapped_sgs; /* (internal) mapped sg entries */
int num_sgs; /* (in) number of entries in the sg list */
u32 transfer_buffer_length; /* (in) data buffer length */
u32 actual_length; /* (return) actual transfer length */
unsigned char *setup_packet; /* (in) setup packet (control only) */
dma_addr_t setup_dma; /* (in) dma addr for setup_packet */
int start_frame; /* (modify) start frame (ISO) */
int number_of_packets; /* (in) number of ISO packets */
int interval; /* (modify) transfer interval
* (INT/ISO) */
int error_count; /* (return) number of ISO errors */
void *context; /* (in) context for completion */
usb_complete_t complete; /* (in) completion routine */
struct usb_iso_packet_descriptor iso_frame_desc[0];
/* (in) ISO ONLY */
};
```
这个结构体在"linux/usb.h" 中,在源码中每一项内容都有详细的说明,我们不再此讨论。
#### usb的操作
创建和销毁一个URB 注意此处内部使用引用计数机制,我们必须使用创建函数创建。
```
struct urb * usb_alloc(int iso_packets,int mem_flags);
```
@iso_packets 等时数据包量
@mem_flags 内核内存分配标志
放弃一个URB
```
void usb_free(struct urb *urb);
```
中断 urb
函数 usb_fill_int_urb 是一个帮忙函数, 来正确初始化一个urb 来发送给 USB 设备的一个中断端点:
```
void usb_fill_int_urb(struct urb *urb, struct usb_device *dev,
unsigned int pipe, void *transfer_buffer,
int buffer_length, usb_complete_t complete,void *context, int interval);
```
这个函数包含许多参数:
struct urb *urb 指向要被初始化的 urb 的指针.
struct usb_device *dev这个 urb 要发送到的 USB 设备.
unsigned int pipe这个 urb 要被发送到的 USB 设备的特定端点. 这个值被创建, 使用前面提过的 usb_sndintpipe 或者
usb_rcvintpipe 函数.
void *transfer_buffer指向缓冲的指针, 从那里外出的数据被获取或者进入数据被接受. 注意这不能是一个静态的缓冲并且必须使用 kmalloc 调用来创建.
int buffer_length缓冲的长度, 被 transfer_buffer 指针指向.
usb_complete_t complete指针, 指向当这个 urb 完成时被调用的完成处理者.
void *context指向数据块的指针, 它被添加到这个 urb 结构为以后被完成处理者函数获取.
int interval这个 urb 应当被调度的间隔. 见之前的 struct urb 结构的描述, 来找到这个值的正确单位.
批量 urb
块 urb 被初始化非常象中断 urb. 做这个的函数是 usb_fill_bulk_urb, 它看来如此:
```
void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev,
unsigned int pipe, void *transfer_buffer,
int buffer_length, usb_complete_t complete,void *context);
```
这个函数参数和 usb_fill_int_urb 函数的都相同. 但是, 没有 interval 参数因为 bulk urb 没有间隔值. 请注意这个 unsiged int pipe 变量必须被初始化用对 usb_sndbulkpipe 或者 usb_rcvbulkpipe 函数的调用.
usb_fill_int_urb 函数不设置 urb 中的 transfer_flags 变量, 因此任何对这个成员的修改不得不由这个驱动自己完成.
控制 urb
控制 urb 被初始化几乎和 块 urb 相同的方式, 使用对函数 usb_fill_control_urb 的调用:
```
void usb_fill_control_urb(struct urb *urb, struct usb_device *dev,
unsigned int pipe, unsigned char *setup_packet,
void *transfer_buffer, int buffer_length,
usb_complete_t complete, void *context);
```
函数参数和 usb_fill_bulk_urb 函数都相同, 除了有个新参数, unsigned char *setup_packet, 它必须指向要发送给端点的 setup 报文数据. 还有, unsigned int pipe 变量必须被初始化, 使用对 usb_sndctrlpipe 或者 usb_rcvictrlpipe 函数的调用.
usb_fill_control_urb 函数不设置 transfer_flags 变量在 urb 中, 因此任何对这个成员的修改必须游驱动自己完成. 大部分驱动不使用这个函数, 因为使用在"USB 传送不用 urb"一节中介绍的同步 API 调用更简单.
等时urb
不幸的是, 同步 urb 没有一个象中断, 控制, 和块 urb 的初始化函数. 因此它们必须在驱动中"手动"初始化, 在它们可被提交给 USB 核心之前. 下面是一个如何正确初始化这类 urb 的例子. 它是从 konicawc.c 内核驱动中取得的, 它位于主内核源码树的 drivers/usb/media 目录.
```
urb->dev = dev;
urb->context = uvd;
urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp-1);
urb->interval = 1;
urb->transfer_flags = URB_ISO_ASAP;
urb->transfer_buffer = cam->sts_buf[i];
urb->complete = konicawc_isoc_irq;
urb->number_of_packets = FRAMES_PER_DESC;
urb->transfer_buffer_length = FRAMES_PER_DESC;
for (j=0; j < FRAMES_PER_DESC; j++) {
urb->iso_frame_desc[j].offset = j;
urb->iso_frame_desc[j].length = 1;
}
```
提交 urb
一旦 urb 被正确地创建,并且被 USB 驱动初始化, 它已准备好被提交给 USB 核心来发送出到 USB 设备. 这通过调用函数 usb_submit_urb 实现:
int usb_submit_urb(struct urb *urb, int mem_flags);
urb 参数是一个指向 urb 的指针, 它要被发送到设备. mem_flags 参数等同于传递给 kmalloc 调用的同样的参数, 并且用来告诉 USB 核心如何及时分配任何内存缓冲在这个时间.
在 urb 被成功提交给 USB 核心之后, 应当从不试图存取 urb 结构的任何成员直到完成函数被调用.
因为函数 usb_submit_urb 可被在任何时候被调用(包括从一个中断上下文), mem_flags 变量的指定必须正确. 真正只有 3 个有效值可用, 根据何时 usb_submit_urb 被调用:
GFP_ATOMIC这个值应当被使用无论何时下面的是真:
调用者处于一个 urb 完成处理者, 一个中断, 一个后半部, 一个 tasklet, 或者一个时钟回调.
调用者持有一个自旋锁或者读写锁. 注意如果正持有一个旗标, 这个值不必要.
current->state 不是 TASK_RUNNING. 状态一直是 TASK_RUNNING 除非驱动已自己改变 current 状态.
GFP_NOIO这个值应当被使用, 如果驱动在块 I/O 补丁中. 它还应当用在所有的存储类型的错误处理补丁中.
GFP_KERNEL这应当用在所有其他的情况中, 不属于之前提到的类别.
urb主处理流程
![d](./image/urbc.png)
### 内核变化
usb_buffer_alloc/free 修改
http://www.360doc.com/content/13/0403/23/11400509_275841401.shtml
### 创建一个新的USB 驱动程序
在./code/ 代码
结果:
![](./image/dd.png)
*代码流程图

View File

@@ -0,0 +1,11 @@
obj-m := usb-skeleton.o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD)
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

View File

@@ -0,0 +1,359 @@
/*
* USB Skeleton driver - 2.0
*
* Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
* This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c
* but has been rewritten to be easy to read and use, as no locks are now
* needed anymore.
*
*/
//#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kref.h>
//#include <linux/smp_lock.h>
#include <linux/smp.h>
#include <linux/usb/ch9.h>
#include <linux/usb.h>
#include <asm/uaccess.h>
#include <linux/hardirq.h>
/* Define these values to match your devices */
#define USB_SKEL_VENDOR_ID 0xfff0
#define USB_SKEL_PRODUCT_ID 0xfff0
/* table of devices that work with this driver */
static struct usb_device_id skel_table [] = {
{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, skel_table);
/* Get a minor range for your devices from the usb maintainer */
#define USB_SKEL_MINOR_BASE 192
/* Structure to hold all of our device specific stuff */
struct usb_skel {
struct usb_device * udev; /* the usb device for this device */
struct usb_interface * interface; /* the interface for this device */
unsigned char * bulk_in_buffer; /* the buffer to receive data */
size_t bulk_in_size; /* the size of the receive buffer */
__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
struct kref kref;
};
#define to_skel_dev(d) container_of(d, struct usb_skel, kref)
static struct usb_driver skel_driver;
static void skel_delete(struct kref *kref)
{
struct usb_skel *dev = to_skel_dev(kref);
usb_put_dev(dev->udev);
kfree (dev->bulk_in_buffer);
kfree (dev);
}
static int skel_open(struct inode *inode, struct file *file)
{
struct usb_skel *dev;
struct usb_interface *interface;
int subminor;
int retval = 0;
subminor = iminor(inode);
interface = usb_find_interface(&skel_driver, subminor);
if (!interface) {
printk("%s - error, can't find device for minor %d",
__FUNCTION__, subminor);
retval = -ENODEV;
goto exit;
}
dev = usb_get_intfdata(interface);
if (!dev) {
retval = -ENODEV;
goto exit;
}
/* increment our usage count for the device */
kref_get(&dev->kref);
/* save our object in the file's private structure */
file->private_data = dev;
exit:
return retval;
}
static int skel_release(struct inode *inode, struct file *file)
{
struct usb_skel *dev;
dev = (struct usb_skel *)file->private_data;
if (dev == NULL)
return -ENODEV;
/* decrement the count on our device */
kref_put(&dev->kref, skel_delete);
return 0;
}
static ssize_t skel_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
struct usb_skel *dev;
int retval = 0;
dev = (struct usb_skel *)file->private_data;
/* do a blocking bulk read to get data from the device */
retval = usb_bulk_msg(dev->udev,
usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
dev->bulk_in_buffer,
min(dev->bulk_in_size, count),
&count, HZ*10);
/* if the read was successful, copy the data to userspace */
if (!retval) {
if (copy_to_user(buffer, dev->bulk_in_buffer, count))
retval = -EFAULT;
else
retval = count;
}
return retval;
}
static void skel_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
{
/* sync/async unlink faults aren't errors */
if (urb->status &&
!(urb->status == -ENOENT ||
urb->status == -ECONNRESET ||
urb->status == -ESHUTDOWN)) {
printk("%s - nonzero write bulk status received: %d",
__FUNCTION__, urb->status);
}
/* free up our allocated buffer */
usb_free_coherent(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
}
static ssize_t skel_write(struct file *file, const char __user *user_buffer, size_t count, loff_t *ppos)
{
struct usb_skel *dev;
int retval = 0;
struct urb *urb = NULL;
char *buf = NULL;
dev = (struct usb_skel *)file->private_data;
/* verify that we actually have some data to write */
if (count == 0)
goto exit;
/* create a urb, and a buffer for it, and copy the data to the urb */
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
retval = -ENOMEM;
goto error;
}
buf = usb_alloc_coherent(dev->udev, count, GFP_KERNEL, &urb->transfer_dma);
if (!buf) {
retval = -ENOMEM;
goto error;
}
if (copy_from_user(buf, user_buffer, count)) {
retval = -EFAULT;
goto error;
}
/* initialize the urb properly */
usb_fill_bulk_urb(urb, dev->udev,
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
buf, count, skel_write_bulk_callback, dev);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* send the data out the bulk port */
retval = usb_submit_urb(urb, GFP_KERNEL);
if (retval) {
printk("%s - failed submitting write urb, error %d", __FUNCTION__, retval);
goto error;
}
/* release our reference to this urb, the USB core will eventually free it entirely */
usb_free_urb(urb);
exit:
return count;
error:
usb_free_coherent(dev->udev, count, buf, urb->transfer_dma);
usb_free_urb(urb);
kfree(buf);
return retval;
}
static struct file_operations skel_fops = {
.owner = THIS_MODULE,
.read = skel_read,
.write = skel_write,
.open = skel_open,
.release = skel_release,
};
/*
* usb class driver info in order to get a minor number from the usb core,
* and to have the device registered with devfs and the driver core
*/
static struct usb_class_driver skel_class = {
.name = "usb/skel%d",
.fops = &skel_fops,
//.mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
.minor_base = USB_SKEL_MINOR_BASE,
};
static int skel_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
struct usb_skel *dev = NULL;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
size_t buffer_size;
int i;
int retval = -ENOMEM;
/* allocate memory for our device state and initialize it */
dev = kmalloc(sizeof(struct usb_skel), GFP_KERNEL);
if (dev == NULL) {
printk("Out of memory");
goto error;
}
memset(dev, 0x00, sizeof (*dev));
kref_init(&dev->kref);
dev->udev = usb_get_dev(interface_to_usbdev(interface));
dev->interface = interface;
/* set up the endpoint information */
/* use only the first bulk-in and bulk-out endpoints */
iface_desc = interface->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
if (!dev->bulk_in_endpointAddr &&
(endpoint->bEndpointAddress & USB_DIR_IN) &&
((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_BULK)) {
/* we found a bulk in endpoint */
buffer_size = endpoint->wMaxPacketSize;
dev->bulk_in_size = buffer_size;
dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
if (!dev->bulk_in_buffer) {
printk("Could not allocate bulk_in_buffer");
goto error;
}
}
if (!dev->bulk_out_endpointAddr &&
!(endpoint->bEndpointAddress & USB_DIR_IN) &&
((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_BULK)) {
/* we found a bulk out endpoint */
dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
}
}
if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
printk("Could not find both bulk-in and bulk-out endpoints");
goto error;
}
/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);
/* we can register the device now, as it is ready */
retval = usb_register_dev(interface, &skel_class);
if (retval) {
/* something prevented us from registering this driver */
printk("Not able to get a minor for this device.");
usb_set_intfdata(interface, NULL);
goto error;
}
/* let the user know what node this device is now attached to */
printk("USB Skeleton device now attached to USBSkel-%d", interface->minor);
return 0;
error:
if (dev)
kref_put(&dev->kref, skel_delete);
return retval;
}
static void skel_disconnect(struct usb_interface *interface)
{
struct usb_skel *dev;
int minor = interface->minor;
/* prevent skel_open() from racing skel_disconnect() */
//lock_kernel();
dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
/* give back our minor */
usb_deregister_dev(interface, &skel_class);
//unlock_kernel();
/* decrement our usage count */
kref_put(&dev->kref, skel_delete);
printk("USB Skeleton #%d now disconnected", minor);
}
static struct usb_driver skel_driver = {
//.owner = THIS_MODULE,
.name = "skeleton",
.id_table = skel_table,
.probe = skel_probe,
.disconnect = skel_disconnect,
};
static int __init usb_skel_init(void)
{
int result;
/* register this driver with the USB subsystem */
result = usb_register(&skel_driver);
if (result)
printk("usb_register failed. Error number %d", result);
return result;
}
static void __exit usb_skel_exit(void)
{
/* deregister this driver with the USB subsystem */
usb_deregister(&skel_driver);
}
module_init (usb_skel_init);
module_exit (usb_skel_exit);
MODULE_LICENSE("GPL");

BIN
USB_driver/image/dd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
USB_driver/image/urbc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
USB_driver/image/usb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

189
interrupt/README.md Normal file
View File

@@ -0,0 +1,189 @@
## 中断处理
首先,关于中断和异常的概念,可以参考我的博客,或者等后边的同学进行补充,我们不再这里赘述。我们尽可能的讨论一些进阶的东西。
### /proc 接口
如图:我们可以在/proc/interrupts 中看到我们系统中安装的中断。
![d](./image/res.png)
可以看到我的电脑是有4个CPU 其实是双核4线程看来内核是以执行流的来作为CPU的计数标准。
第一列是IRQ中断号最后一列是中断的名称中间是每个CPU处理中断的计数。
我们可以发现即使我的个人PC并没有运行太多的服务和程序但是CPU0明显还是处理的中断更多这是LINUX内核为了最大化缓存的本地性质。
接着我们来看看/proc/stat 文件。
这个文件中记录的是系统活动的底层统计信息,包括从系统启动到现在系统接受的中断数量。
![ss](./image/ddd.png)
可以看到这是一个使用位图的表达方式。
### 中断接口
请求中断线 && 中断描述符:
```
/**
* struct irqaction - per interrupt action descriptor
* @handler: interrupt handler function
* @name: name of the device
* @dev_id: cookie to identify the device
* @percpu_dev_id: cookie to identify the device
* @next: pointer to the next irqaction for shared interrupts
* @irq: interrupt number
* @flags: flags (see IRQF_* above)
* @thread_fn: interrupt handler function for threaded interrupts
* @thread: thread pointer for threaded interrupts
* @secondary: pointer to secondary irqaction (force threading)
* @thread_flags: flags related to @thread
* @thread_mask: bitmask for keeping track of @thread activity
* @dir: pointer to the proc/irq/NN/name entry
*/
struct irqaction {
irq_handler_t handler;
void *dev_id;
void __percpu *percpu_dev_id;
struct irqaction *next;
irq_handler_t thread_fn;
struct task_struct *thread;
struct irqaction *secondary;
unsigned int irq;
unsigned int flags;
unsigned long thread_flags;
unsigned long thread_mask;
const char *name;
struct proc_dir_entry *dir;
} ____cacheline_internodealigned_in_smp;
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}
@irq 要申请的中断号
@handler_t 安装处理中断的函数指针
@flags 中断掩码
@name 中断拥有者
@dev 中断信号线
```
#### 实现中断程序的几个简单要求
1.处理例程不能向用户空间发送或者接受数据,因为它不能再进程上下文中执行,处理过程也不能休眠。
2.不能调用schdule函数
#### 典型应用
如果中断通知进程所等待的事件已经发生,比如新数据到达,就会唤醒在该设备上休眠的进程。我们最好编写执行时间尽可能短的处理例程。
如果需要长时间的计算任务最好的使用方法是tasklet 或者 工作队列在更加安全的时间计算。
### 注册中断函数小测试
我们以共享形式在27号中断线设置一个中断处理函数27号是PCI上的一个周期中断。代码在./code 中。
测试效果:
![s](./image/inter.png)
随着我们系统上27号中断线收到中断请求我们注册的中断处理信息也被打印。此处是共享了中断线。
### 中断线探测
此处暂时不作为主要讨论内容代后续补充内核已经为我们提供了API。
以下一些材料来自内核源码,供读者参考。
```
/*
* Autoprobing for irqs:
*
* probe_irq_on() and probe_irq_off() provide robust primitives
* for accurate IRQ probing during kernel initialization. They are
* reasonably simple to use, are not "fooled" by spurious interrupts,
* and, unlike other attempts at IRQ probing, they do not get hung on
* stuck interrupts (such as unused PS2 mouse interfaces on ASUS boards).
*
* For reasonably foolproof probing, use them as follows:
*
* 1. clear and/or mask the device's internal interrupt.
* 2. sti();
* 3. irqs = probe_irq_on(); // "take over" all unassigned idle IRQs
* 4. enable the device and cause it to trigger an interrupt.
* 5. wait for the device to interrupt, using non-intrusive polling or a delay.
* 6. irq = probe_irq_off(irqs); // get IRQ number, 0=none, negative=multiple
* 7. service the device to clear its pending interrupt.
* 8. loop again if paranoia is required.
*
* probe_irq_on() returns a mask of allocated irq's.
*
* probe_irq_off() takes the mask as a parameter,
* and returns the irq number which occurred,
* or zero if none occurred, or a negative irq number
* if more than one irq occurred.
*/
#if !defined(CONFIG_GENERIC_IRQ_PROBE)
static inline unsigned long probe_irq_on(void)
{
return 0;
}
static inline int probe_irq_off(unsigned long val)
{
return 0;
}
static inline unsigned int probe_irq_mask(unsigned long val)
{
return 0;
}
```
### 下半部处理机制
中断处理过程必须快速,但是我们很多时候需要一些执行时间较长的流程,为了解决这个问题,内核提供了下半部机制。
下半部机制唯一的不同在于处理历程在执行的过程中中断是打开的可以继续响应中断一共有两种实现方式一个是tasklet 一个是工作队列。
#### tasklet
tasklet是中断处理下半部分最常用的一种方法驱动程序一般先申请中断在中断处理函数内完成中断上半部分的工作后调用tasklet。tasklet有如下特点
1.tasklet只可以在一个CPU上同步地执行不同的tasklet可以在不同地CPU上同步地执行。
2.tasklet的实现是建立在两个软件中断的基础之上的即HI_SOFTIRQ和TASKLET_SOFTIRQ本质上没有什么区别只不过HI_SOFTIRQ的优先级更高一些
3.由于tasklet是在软中断上实现的所以像软中断一样不能睡眠、不能阻塞处理函数内不能含有导致睡眠的动作如减少信号量、从用户空间拷贝数据或手工分配内存等。
4.一个 tasklet 能够被禁止并且之后被重新使能; 它不会执行直到它被使能的次数与被禁止的次数相同.
5.tasklet的串行化使tasklet函数不必是可重入的因此简化了设备驱动程序开发者的工作。
6.每个cpu拥有一个tasklet_vec链表具体是哪个cpu的tasklet_vec链表是根据当前线程是运行在哪个cpu来决定的。
在CODE目录中我们的tasklet 代码中实现了一个简单的tasklet 程序。
效果如下:
![ss](./image/ds.png)
可以看到中断被触发但是并没有每次都执行我们的100次打印循环进一步的你可以观察到在执行100此打印的过程中也会被打断。
小心你在实验的时候不要设置100次循环找一个中断少点的中断线不然容易挂哈哈哈。
#### 工作队列
linux的工作队列workqueue是另外一种将工作推后执行的形式它和软中断、tasklet 这两种下半部机制都有不同。工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。这样,通过工作队列执行的代码能占尽进程上下文的所有优势。最重要的就是工作队列允许被重新调度甚至是睡眠。
![ds](./image/dd.png)

31
interrupt/code/Makefile Executable file
View File

@@ -0,0 +1,31 @@
# To build modules outside of the kernel tree, we run "make"
# in the kernel source tree; the Makefile these then includes this
# Makefile once again.
# This conditional selects whether we are being included from the
# kernel Makefile or not.
ifeq ($(KERNELRELEASE),)
# Assume the source tree is where the running kernel was built
# You should set KERNELDIR in the environment if it's elsewhere
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
# The current directory is passed to sub-makes as argument
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* module*
.PHONY: modules modules_install clean
else
# called from kernel build system: just declare what our modules are
obj-m := irq_qu.o tasklet.o workqueue_my.o
endif

45
interrupt/code/irq_qu.c Normal file
View File

@@ -0,0 +1,45 @@
/*************************************************************************
> File Name: irq_qu.c
> Author:
> Mail:
> Created Time: 2017年04月25日 星期二 09时18分44秒
************************************************************************/
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/interrupt.h>
MODULE_LICENSE("GPL");
static int irq = 27;
const char *interface = "my_irq";
static irqreturn_t myirq_handler(int irq,void *dev);
static int __init myirq_init(void) {
if(request_irq(irq,myirq_handler,IRQF_SHARED,interface,&irq)){
printk(KERN_ERR "%s interrrupt can't register %d IRQ \n",interface,irq);
return -EIO;
}
printk("%s request %d IRQ\n",interface,irq);
return 0;
}
static irqreturn_t myirq_handler(int irq,void *dev){
printk("my_handle %d IRQ is working\n",irq);
return 0;
}
static void __exit myirq_exit(void){
free_irq(irq,&irq);
printk("%s interrupt free %d IRQ\n",interface,irq);
}
module_init(myirq_init);
module_exit(myirq_exit);

71
interrupt/code/tasklet.c Normal file
View File

@@ -0,0 +1,71 @@
/*************************************************************************
> File Name: tasklet.c
> Author:
> Mail:
> Created Time: 2017年04月26日 星期三 08时20分14秒
************************************************************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
static int irq = 27;
const char *interface = "my_irq";
static irqreturn_t myirq_handler(int irq,void *dev);
static void tasklet_func(unsigned long value)
{
int i = 0;
for(i = 0;i < 100 ;i++){
mdelay(100);
printk("dely : %d \n",i);
}
}
DECLARE_TASKLET(my_tasklet,&tasklet_func,0);
static int __init myirq_init(void) {
if(request_irq(irq,myirq_handler,IRQF_SHARED,interface,&irq)){
printk(KERN_ERR "%s interrrupt can't register %d IRQ \n",interface,irq);
return -EIO;
}
printk("%s request %d IRQ\n",interface,irq);
return 0;
}
static irqreturn_t myirq_handler(int irq,void *dev){
printk("my_handle %d IRQ is working\n",irq);
tasklet_schedule(&my_tasklet);
return 0;
}
static void __exit myirq_exit(void){
free_irq(irq,&irq);
printk("%s interrupt free %d IRQ\n",interface,irq);
}
module_init(myirq_init);
module_exit(myirq_exit);

View File

@@ -0,0 +1,43 @@
/*************************************************************************
> File Name: workqueue.c
> Author:
> Mail:
> Created Time: 2017年04月26日 星期三 08时54分35秒
************************************************************************/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/workqueue.h>
static struct workqueue_struct *queue = NULL;
static struct work_struct work;
static void work_handler(struct work_struct *data)
{
printk(KERN_ALERT "work handler function.\n");
}
static int __init test_init(void)
{
queue = create_singlethread_workqueue("helloworld");
if (!queue)
goto err;
INIT_WORK(&work, work_handler);
schedule_work(&work);
return 0;
err:
return -1;
}
static void __exit test_exit(void)
{
destroy_workqueue(queue);
}
MODULE_LICENSE("GPL");
module_init(test_init);
module_exit(test_exit);

BIN
interrupt/image/dd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
interrupt/image/ddd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
interrupt/image/ds.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
interrupt/image/inter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
interrupt/image/res.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

310
kernel_DS/README.md Normal file
View File

@@ -0,0 +1,310 @@
## 内核的数据类型
### 基本数据类型
我们写了两个小的模块来测试实际数据类型和内存对齐的长度。
内核基本数据类型
C语言类型int
char、short、int、long long在不同的平台上大小不变。
long、ptr(指针)平台不同其大小不同,但二者的大小始终相同。
char的符号问题
大多数平台上char默认是signed但有些平台上默认是 unsigned。
char i = -1; 大部分平台上i是-1有些平台上是255。
应该使用signed char i = -1; unsigned char i = 255;
确定大小的类型u32
u8、u16、u32、u64、 s8、s16、s32、s64是Linux内核确定大小的类型。
__u8等式linux用户态确定大小的类型。头文件linux/types.h
uint8_t、uint32_t是新编译器支持的C99标准确定大小的类型可以跨平台。
特定内核对象的类型pid_t
进程标识符使用pid_t类型而不使用int屏蔽了实际的数据类型中任何可能的差异。
特定内核对象的类型打印时不太好选择printk或printf的输出格式
1. 一些平台上排除的警告在另一平台上可能会出现size_t在一些平台上是unsigned long在一些平台上是unsigned int
2. 将其强制转换成可能的最大类型,然后用响应的格式打印输出。
字节序
大端、小端
数值0x01020304内存从低到高依次存储04 03 02 01 为小端。 存储顺序反过来为大端)
数值0x00000001内存从低到高依次存储01 00 00 00 为小端。
转换函数
u32 __cpu_to_be32(u32); /* 把cpu字节序转为大端字节序 */
u32 __be32_to_cpu(u32); /* 把大端字节序转为cpu字节序 */
u32 __cpu_to_le32(u32); /* 把cpu字节序转为小端字节序 */
u32 __le32_to_cpu(u32); /* 把小端字节序转为cpu字节序 */
在头文件<linux/byteorder.h>
时间间隔
使用HZ代表一秒。
不能假定每秒就1000个jiffies。
与msec毫秒对应的jiffies数目总是msec*HZ/1000。
页大小
页大小为PAGE_SIZE个字节而不是4KB。
分配16KB的空间临时存储数据如下
以上,只是基本数据类型中最简单的一部分,绝大多数都是细节问题,要注意。
#### 基本数据类型的长度
![df](./image/ssd.png)
#### 对齐数据类型长度
![fs](./image/dfd.png)
#### types.h 中的重要数据类型
```
typedef __u32 __kernel_dev_t;
typedef __kernel_fd_set fd_set;
typedef __kernel_dev_t dev_t;
typedef __kernel_ino_t ino_t;
typedef __kernel_mode_t mode_t;
typedef unsigned short umode_t;
typedef __u32 nlink_t;
typedef __kernel_off_t off_t;
typedef __kernel_pid_t pid_t;
typedef __kernel_daddr_t daddr_t;
typedef __kernel_key_t key_t;
typedef __kernel_suseconds_t suseconds_t;
typedef __kernel_timer_t timer_t;
typedef __kernel_clockid_t clockid_t;
typedef __kernel_mqd_t mqd_t;
typedef _Bool bool;
typedef __kernel_uid32_t uid_t;
typedef __kernel_gid32_t gid_t;
typedef __kernel_uid16_t uid16_t;
typedef __kernel_gid16_t gid16_t;
typedef unsigned long uintptr_t;
#ifdef CONFIG_HAVE_UID16
/* This is defined by include/asm-{arch}/posix_types.h */
typedef __kernel_old_uid_t old_uid_t;
typedef __kernel_old_gid_t old_gid_t;
#endif /* CONFIG_UID16 */
#if defined(__GNUC__)
typedef __kernel_loff_t loff_t;
#endif
/*
* The following typedefs are also protected by individual ifdefs for
* historical reasons:
*/
#ifndef _SIZE_T
#define _SIZE_T
typedef __kernel_size_t size_t;
#endif
#ifndef _SSIZE_T
#define _SSIZE_T
typedef __kernel_ssize_t ssize_t;
#endif
#ifndef _PTRDIFF_T
#define _PTRDIFF_T
typedef __kernel_ptrdiff_t ptrdiff_t;
#endif
#ifndef _TIME_T
#define _TIME_T
typedef __kernel_time_t time_t;
#endif
#ifndef _CLOCK_T
#define _CLOCK_T
typedef __kernel_clock_t clock_t;
#endif
#ifndef _CADDR_T
#define _CADDR_T
typedef __kernel_caddr_t caddr_t;
#endif
/* bsd */
typedef unsigned char u_char;
typedef unsigned short u_short;
typedef unsigned int u_int;
typedef unsigned long u_long;
/* sysv */
typedef unsigned char unchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long ulong;
#ifndef __BIT_TYPES_DEFINED__
#define __BIT_TYPES_DEFINED__
typedef __u8 u_int8_t;
typedef __s8 int8_t;
typedef __u16 u_int16_t;
typedef __s16 int16_t;
typedef __u32 u_int32_t;
typedef __s32 int32_t;
#endif /* !(__BIT_TYPES_DEFINED__) */
typedef __u8 uint8_t;
typedef __u16 uint16_t;
typedef __u32 uint32_t;
#if defined(__GNUC__)
typedef __u64 uint64_t;
typedef __u64 u_int64_t;
typedef __s64 int64_t;
#endif
/* this is a special 64bit data type that is 8-byte aligned */
#define aligned_u64 __u64 __attribute__((aligned(8)))
#define aligned_be64 __be64 __attribute__((aligned(8)))
#define aligned_le64 __le64 __attribute__((aligned(8)))
/**
* The type used for indexing onto a disc or disc partition.
*
* Linux always considers sectors to be 512 bytes long independently
* of the devices real block size.
*
* blkcnt_t is the type of the inode's block count.
*/
#ifdef CONFIG_LBDAF
typedef u64 sector_t;
typedef u64 blkcnt_t;
#else
typedef unsigned long sector_t;
typedef unsigned long blkcnt_t;
#endif
/*
* The type of an index into the pagecache.
*/
#define pgoff_t unsigned long
/*
* A dma_addr_t can hold any valid DMA address, i.e., any address returned
* by the DMA API.
*
* If the DMA API only uses 32-bit addresses, dma_addr_t need only be 32
* bits wide. Bus addresses, e.g., PCI BARs, may be wider than 32 bits,
* but drivers do memory-mapped I/O to ioremapped kernel virtual addresses,
* so they don't care about the size of the actual bus addresses.
*/
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
typedef u64 dma_addr_t;
#else
typedef u32 dma_addr_t;
#endif
typedef unsigned __bitwise__ gfp_t;
typedef unsigned __bitwise__ fmode_t;
typedef unsigned __bitwise__ oom_flags_t;
#ifdef CONFIG_PHYS_ADDR_T_64BIT
typedef u64 phys_addr_t;
#else
typedef u32 phys_addr_t;
#endif
typedef phys_addr_t resource_size_t;
/*
* This type is the placeholder for a hardware interrupt number. It has to be
* big enough to enclose whatever representation is used by a given platform.
*/
typedef unsigned long irq_hw_number_t;
typedef struct {
int counter;
} atomic_t;
#ifdef CONFIG_64BIT
typedef struct {
long counter;
} atomic64_t;
#endif
struct list_head {
struct list_head *next, *prev;
};
struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node *next, **pprev;
};
struct ustat {
__kernel_daddr_t f_tfree;
__kernel_ino_t f_tinode;
char f_fname[6];
char f_fpack[6];
};
/**
* struct callback_head - callback structure for use with RCU and task_work
* @next: next update requests in a list
* @func: actual update function to call after the grace period.
*
* The struct is aligned to size of pointer. On most architectures it happens
* naturally due ABI requirements, but some architectures (like CRIS) have
* weird ABI and we need to ask it explicitly.
*
* The alignment is required to guarantee that bits 0 and 1 of @next will be
* clear under normal conditions -- as long as we use call_rcu(),
* call_rcu_bh(), call_rcu_sched(), or call_srcu() to queue callback.
*
* This guarantee is important for few reasons:
* - future call_rcu_lazy() will make use of lower bits in the pointer;
* - the structure shares storage spacer in struct page with @compound_head,
* which encode PageTail() in bit 0. The guarantee is needed to avoid
* false-positive PageTail().
*/
struct callback_head {
struct callback_head *next;
void (*func)(struct callback_head *head);
} __attribute__((aligned(sizeof(void *))));
#define rcu_head callback_head
typedef void (*rcu_callback_t)(struct rcu_head *head);
typedef void (*call_rcu_func_t)(struct rcu_head *head, rcu_callback_t func);
/* clocksource cycle base type */
typedef u64 cycle_t;
```
#### list.h

31
kernel_DS/code/Makefile Executable file
View File

@@ -0,0 +1,31 @@
# To build modules outside of the kernel tree, we run "make"
# in the kernel source tree; the Makefile these then includes this
# Makefile once again.
# This conditional selects whether we are being included from the
# kernel Makefile or not.
ifeq ($(KERNELRELEASE),)
# Assume the source tree is where the running kernel was built
# You should set KERNELDIR in the environment if it's elsewhere
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
# The current directory is passed to sub-makes as argument
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
.PHONY: modules modules_install clean
else
# called from kernel build system: just declare what our modules are
obj-m := kdatasize.o kdataalign.o
endif

68
kernel_DS/code/kdataalign.c Executable file
View File

@@ -0,0 +1,68 @@
/*
* kdatasize.c -- print the size of common data items from kernel space
* This runs with any Linux kernel (not any Unix, because of <linux/types.h>)
*
* Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
* Copyright (C) 2001 O'Reilly & Associates
*
* The source code in this file can be freely used, adapted,
* and redistributed in source or binary form, so long as an
* acknowledgment appears in derived source files. The citation
* should list that the code comes from the book "Linux Device
* Drivers" by Alessandro Rubini and Jonathan Corbet, published
* by O'Reilly & Associates. No warranty is attached;
* we cannot take responsibility for errors or fitness for use.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/utsname.h>
#include <linux/errno.h>
/*
* Define several data structures, all of them start with a lone char
* in order to present an unaligned offset for the next field
*/
struct c {char c; char t;} c;
struct s {char c; short t;} s;
struct i {char c; int t;} i;
struct l {char c; long t;} l;
struct ll {char c; long long t;} ll;
struct p {char c; void * t;} p;
struct u1b {char c; __u8 t;} u1b;
struct u2b {char c; __u16 t;} u2b;
struct u4b {char c; __u32 t;} u4b;
struct u8b {char c; __u64 t;} u8b;
static void data_cleanup(void)
{
/* never called */
}
static int data_init(void)
{
/* print information and return an error */
printk("arch Align: char short int long ptr long-long "
" u8 u16 u32 u64\n");
printk("x86_64 %3i %3i %3i %3i %3i %3i "
"%3i %3i %3i %3i\n",
/* note that gcc can subtract void * values, but it's not ansi */
(int)((void *)(&c.t) - (void *)&c),
(int)((void *)(&s.t) - (void *)&s),
(int)((void *)(&i.t) - (void *)&i),
(int)((void *)(&l.t) - (void *)&l),
(int)((void *)(&p.t) - (void *)&p),
(int)((void *)(&ll.t) - (void *)&ll),
(int)((void *)(&u1b.t) - (void *)&u1b),
(int)((void *)(&u2b.t) - (void *)&u2b),
(int)((void *)(&u4b.t) - (void *)&u4b),
(int)((void *)(&u8b.t) - (void *)&u8b));
return -ENODEV;
}
module_init(data_init);
module_exit(data_cleanup);
MODULE_LICENSE("Dual BSD/GPL");

47
kernel_DS/code/kdatasize.c Executable file
View File

@@ -0,0 +1,47 @@
/*
* kdatasize.c -- print the size of common data items from kernel space
* This runs with any Linux kernel (not any Unix, because of <linux/types.h>)
*
* Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
* Copyright (C) 2001 O'Reilly & Associates
*
* The source code in this file can be freely used, adapted,
* and redistributed in source or binary form, so long as an
* acknowledgment appears in derived source files. The citation
* should list that the code comes from the book "Linux Device
* Drivers" by Alessandro Rubini and Jonathan Corbet, published
* by O'Reilly & Associates. No warranty is attached;
* we cannot take responsibility for errors or fitness for use.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/utsname.h>
#include <linux/errno.h>
static void data_cleanup(void)
{
/* never called */
}
int data_init(void)
{
/* print information and return an error */
printk("arch Size: char short int long ptr long-long "
" u8 u16 u32 u64\n");
printk("x86_64 %3i %3i %3i %3i %3i %3i "
"%3i %3i %3i %3i\n",
(int)sizeof(char), (int)sizeof(short), (int)sizeof(int),
(int)sizeof(long),
(int)sizeof(void *), (int)sizeof(long long), (int)sizeof(__u8),
(int)sizeof(__u16), (int)sizeof(__u32), (int)sizeof(__u64));
printk("u64: %3i\n",(int)sizeof(__u64));
return -ENODEV;
}
module_init(data_init);
module_exit(data_cleanup);
MODULE_LICENSE("Dual BSD/GPL");

BIN
kernel_DS/image/dfd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
kernel_DS/image/ssd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB