Linux的pci设备
首先要了解pci是什么,外围设备互联, Peripheral Component Interconnect,PCI外围设备互联外设的内核函数,PCI总线是当今广泛使用在桌面以及更大型的计算机上的外设总线,该总线是内核支持最好的总线。
PCI接口
PCI是替代ISA的,尽可能做到三个目标:1.性能好 2.跨平台 3.简化从平台的添加和删除
PCI寻址
PCI允许单个 系统有256个总线,每个 总线可支持32个设备,当然我们无需记住他的二进制地址,我们可以通过pci_dev来访问
尽管16位的硬件地址隐藏在struct pci_Dev对象中,但是有时候仍然可见
lspci
zhanglei@zhanglei-virtual-machine:~$ lspci
00:00.0 Host bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX Host bridge (rev 01)
00:01.0 PCI bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX AGP bridge (rev 01)
00:07.0 ISA bridge: Intel Corporation 82371AB/EB/MB PIIX4 ISA (rev 08)
00:07.1 IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)
00:07.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 08)
00:07.7 System peripheral: VMware Virtual Machine Communication Interface (rev 10)
00:0f.0 VGA compatible controller: VMware SVGA II Adapter
00:10.0 SCSI storage controller: Broadcom / LSI 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (rev 01)
00:11.0 PCI bridge: VMware PCI bridge (rev 02)
00:15.0 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.1 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.2 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.3 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.4 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.5 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.6 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.7 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.0 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.1 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.2 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.3 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.4 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.5 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.6 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.7 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.0 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.1 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.2 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.3 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.4 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.5 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.6 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.7 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.0 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.1 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.2 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.3 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.4 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.5 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.6 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.7 PCI bridge: VMware PCI Express Root Port (rev 01)
02:00.0 USB controller: VMware USB1.1 UHCI Controller
02:01.0 Ethernet controller: Advanced Micro Devices, Inc. [AMD] 79c970 [PCnet32 LANCE] (rev 10)
02:02.0 Multimedia audio controller: Ensoniq ES1371/ES1373 / Creative Labs CT2518 (rev 02)
02:03.0 USB controller: VMware USB2 EHCI Controller
02:05.0 SATA controller: VMware SATA AHCI controller
zhanglei@zhanglei-virtual-machine:~$
还可以通过/proc/pci 和 /proc/bus去查询,当然/proc就是做查询用的目录/proc/bus/pci/devices,具体如下
zhanglei@zhanglei-virtual-machine:~$ cat /proc/bus/pci/devices
0000 80867190 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 agpgart-intel
0008 80867191 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0038 80867110 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0039 80867111 0 1f0 3f6 170 376 1061 0 0 8 0 8 0 10 0 0 ata_piix
003b 80867113 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0
003f 15ad0740 10 1081 febfe004 0 0 0 0 0 40 2000 0 0 0 0 0 vmw_vmci
0078 15ad0405 10 1071 e8000008 fe000000 0 0 0 c0002 10 8000000 800000 0 0 0 20000 vmwgfx
0080 10000030 11 1401 feba0004 0 febc0004 0 0 c0008000 100 20000 0 20000 0 0 4000 mptspi
0088 15ad0790 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
00a8 15ad07a0 18 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00a9 15ad07a0 19 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00aa 15ad07a0 1a 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00ab 15ad07a0 1b 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00ac 15ad07a0 1c 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00ad 15ad07a0 1d 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00ae 15ad07a0 1e 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00af 15ad07a0 1f 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00b0 15ad07a0 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00b1 15ad07a0 21 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00b2 15ad07a0 22 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00b3 15ad07a0 23 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00b4 15ad07a0 24 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00b5 15ad07a0 25 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00b6 15ad07a0 26 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00b7 15ad07a0 27 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00b8 15ad07a0 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00b9 15ad07a0 29 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00ba 15ad07a0 2a 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00bb 15ad07a0 2b 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00bc 15ad07a0 2c 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00bd 15ad07a0 2d 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00be 15ad07a0 2e 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00bf 15ad07a0 2f 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00c0 15ad07a0 30 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00c1 15ad07a0 31 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00c2 15ad07a0 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00c3 15ad07a0 33 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00c4 15ad07a0 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00c5 15ad07a0 35 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00c6 15ad07a0 36 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
00c7 15ad07a0 37 0 0 0 0 0 0 0 0 0 0 0 0 0 0 pcieport
0200 15ad0774 12 0 0 0 0 20c1 0 0 0 0 0 0 20 0 0 uhci_hcd
0208 10222000 13 2001 0 0 0 0 0 fd500000 80 0 0 0 0 0 10000 pcnet32
0210 12741371 10 2081 0 0 0 0 0 0 40 0 0 0 0 0 0 snd_ens1371
0218 15ad0770 11 fd5ff000 0 0 0 0 0 0 1000 0 0 0 0 0 0 ehci-pci
0228 15ad07e0 38 0 0 0 0 0 fd5fe000 fd510000 0 0 0 0 0 1000 10000 ahci
zhanglei@zhanglei-virtual-machine:~$
lspci是总依据
每个外设板的硬件电路对如下三种地址空间的查询进行应答:内存位置、IO端口和配置寄存器。
对驱动程序而言内存和IO是惯常的方式,我们可以通过readb和inb去进行读写。另一方面配置十五是调用特定的内核函数访问配置寄存器来执行的。关于中断,每个PCI槽有四个中断引脚,每个设备功能可使用其中一个。这种路由是计算机平台负责,在总线之外的。因为PCI中断规范要求中断是可共享的 pci区域所映射的地址可以从配置空间仲读取,因此linux驱动程序不需要探测就能访问设备。在读取寄存器之后,驱动程序就可以访问硬件了。
设备一般是指一组域编号、总线编号、设备编号和功能编号
引导阶段
PCI设备上电的时候处在未激活状态,当PCI设备上电后,进行系统引导,在每个PCI设备上执行配置事务,以便为它提供的每个地址区域提供一个安全的位置。当驱动程序访问它的内存和IO区域已经被映射到了处理器的地址空间。驱动程序可以修改默认配置,不过我们不需要这么做。
配置寄存器和初始化
PCI寄存器是小头的要注意大小端问题
PCI驱动程序可以使用不同的标识符来告诉内核它支持什么样子的设备。struct pci
device
id结构定义驱动程序支持不同的pci设备列表
看头文件 /usr/src/linux-headers-5.8.0-63-generic/include/linux/mod_devicetable.h
struct pci_device_id {
__u32 vendor, device; /* Vendor and device ID or PCI_ANY_ID*/
__u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */
__u32 class, class_mask; /* (class,subclass,prog-if) triplet */
kernel_ulong_t driver_data; /* Data private to the driver */
};
我们也可以通过两个辅助宏来初始化结构体
#define PCI_DEVICE(vend,dev) \
.vendor = (vend), .device = (dev), \
.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
#define PCI_DEVICE_CLASS(dev_class,dev_class_mask) \
.class = (dev_class), .class_mask = (dev_class_mask), \
.vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \
.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
MODULE_DEVICE_TABLE
pci_device_id需要被导出到用户空间,是热插拔和模块装载系统知道什么模块针对什么硬件设备。
MODULE_DEVICE_TABLE(pci, i810_ids)
这个语句会创建一个名字为
mod_pci_device_table的局部变量,指向struct pci_device_id数组。如果找到了指责个符号,这个数据从模块中抽出,添加到/lib/modules/KERNEL
VERSION/modules.pcimap仲。当depmod结束后,内核模块支持所有的pci设备链同他们的模块名字都在文件仲被列出。热插拔系统使用modules.pcimap来寻找新的恰当的驱动程序。
注册PCI驱动程序
pci驱动要创建的主要结构体是pci_driver结构体。
我们看一下结构体内容
struct pci_driver {
struct list_head node;
const char *name;
const struct pci_device_id *id_table; /* Must be non-NULL for probe to be called */
int (*probe)(struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */
void (*remove)(struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
int (*suspend)(struct pci_dev *dev, pm_message_t state); /* Device suspended */
int (*resume)(struct pci_dev *dev); /* Device woken up */
void (*shutdown)(struct pci_dev *dev);
int (*sriov_configure)(struct pci_dev *dev, int num_vfs); /* On PF */
const struct pci_error_handlers *err_handler;
const struct attribute_group **groups;
struct device_driver driver;
struct pci_dynids dynids;
};
name 是驱动名字
probe函数地址,当pci核心有一个认为驱动程序要控制的pci
dev的时候会调用这个函数,PCI程序要做判断pci
dev中的pci_device
id也被传递给这个函数。如果确认传递给他的struct
dev,就应该确认他,返回0,否则返回负数。
remove函数地址,当pci程序从系统中一处或从内核中卸载。
suspend函数,当struct pci_Dev被挂起的时候调用这个函数。挂起状态用state传递。
resume函数,struct pci_dev被挂起后恢复触发这个函数
初始化一个pci_driver 需要初始化四个字段
struct pci_driver pci_driver = {
.name = "11",
.id_table = ids,
.probe = probe,
.close = close
}
为了把pci_driver成功注册到内核需要调用
pci_register_driver(&pci_driver);
卸载需要 pci
unregister
driver(&pci_driver);
老式的pci探测
pci_register_driver 存在危险,如果内核中的设备被移出,驱动程序还是在操作设备那么内核极有可能崩溃,所以我们应该使用下面面的函数查找,
struct pci_dev* pci_get_device(unsigned int vendor, unsigned int device, struct pci_dev *from);
这个函数扫描当前系统中存在的PCI设备列表,如果输入参数和指定的厂商设备的ID匹配的话,增加了发现struct pci
dev变量的引用技术,然后把它返回给调用者,这避免了struct pci
dev无声无息的小时,struct pic_dev返回后必须调用pci
dev
put来吧使用计数器减小,如果没有则返回null
from 开找到最近一个设备,传null 就是第一个
上面的函数不能用在中断上下文中
激活pci 设备
在驱动程序可以访问PCI设备的任何资源之前,驱动程序必须调用pci
enable
device函数
int pci_enable_device(struct pci_dev *dev);
访问配置空间
通常需要读取写入三个地址空间:内存 端口和配置,对驱动程序而言访问配置空间十分重要,因为这是找到设备映射到内存和IO空间位置的唯一途径
相关函数在 <linux/pci.h> 中
int pci_read_config_byte(const struct pci_dev *dev, int where, u8 *val);
int pci_read_config_word(const struct pci_dev *dev, int where, u16 *val);
int pci_read_config_dword(const struct pci_dev *dev, int where, u32 *val);
int pci_write_config_byte(const struct pci_dev *dev, int where, u8 val);
int pci_write_config_word(const struct pci_dev *dev, int where, u16 val);
int pci_write_config_dword(const struct pci_dev *dev, int where, u32 val);
在驱动程序struct pci_dev不能访问的时候可以用下面的函数访问怀疑是总线
int pci_bus_read_config_byte(struct pci_bus *bus, unsigned int devfn,
int where, u8 *val);
int pci_bus_read_config_word(struct pci_bus *bus, unsigned int devfn,
int where, u16 *val);
int pci_bus_read_config_dword(struct pci_bus *bus, unsigned int devfn,
int where, u32 *val);
int pci_bus_write_config_byte(struct pci_bus *bus, unsigned int devfn,
int where, u8 val);
int pci_bus_write_config_word(struct pci_bus *bus, unsigned int devfn,
int where, u16 val);
int pci_bus_write_config_dword(struct pci_bus *bus, unsigned int devfn,
int where, u32 val);
访问IO和内存空间
一个PCI设备可实现6个IO地址区域,每个区域可以是内存也可以是IO地址,大多数设备在内存区域实现IO寄存器。但是不像常规内存,IO寄存器不应该让CPU缓存。
在内核中,PCI设备的IO区域已经被集成到通用资源管理。
//6个IO区域首地址
#define pci_resource_start(dev, bar) ((dev)->resource[(bar)].start)
//6个IO区域尾部地址
#define pci_resource_end(dev, bar) ((dev)->resource[(bar)].end)
//返回和获取资源标志位
#define pci_resource_flags(dev, bar) ((dev)->resource[(bar)].flags)
标志位:
IORESOURCE
IO IORESOURCE
MEM 相关IO区域存在,设置这些标志之一
IORESOURCE
PREFETCH IORESOURCE
READONLY 相关IO区域存在,设置这些标志表明内存区域是否是可预取的和/或是写保护的,从而不会设置后面那个标志。
PCI中断
PCI的终端号在引导阶段就确定好的了,我们驱动程序只需要用这个中断号就可以了,中断号就保存在寄存器60中,从PCI
INTERRUPT
LINE去获取就行了,这个寄存器有1个字节那么宽,意味着有256个中断线。
result = pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &myirq);
关于总线
总线是计算机各种功能部件之间传送信息的公共通信干线,它是由导线组成的传输线束, 按照计算机所传输的信息种类,计算机的总线可以划分为数据总线、地址总线和控制总线,分别用来传输数据、数据地址和控制信号。
比如说没有数据传输的时候是高电平,有数据来了是低电平,然后去读取相应的 起始位 + 数据位+ 校验位 + 结束位 就可以了
例子(从网上找到的)
参考地址:https://blog.csdn.net/wq897387/article/details/108697610
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/signal.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/device.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("pcie device driver");
#define DEV_NAME "hello_pci"
#define DEBUG
#ifdef DEBUG
#define DEBUG_ERR(format,args...)\
do{ \
printk("[%s:%d] ",__FUNCTION__,__LINE__); \
printk(format,##args); \
}while(0)
#else
#define DEBUG_PRINT(format,args...)
#endif
#define DMA_BUFFER_SIZE 1*1024*1024
#define FASYNC_MINOR 1
#define FASYNC_MAJOR 244
#define DEVICE_NUMBER 1
static struct class * hello_class;
static struct device * hello_class_dev;
struct hello_device
{
struct pci_dev *pci_dev;
struct cdev cdev;
dev_t devno;
}my_device;
unsigned long bar0_phy;
unsigned long bar0_vir;
unsigned long bar0_length;
unsigned long bar1_phy;
unsigned long bar1_vir;
unsigned long bar1_length;
dma_addr_t dma_src_phy;
dma_addr_t dma_src_vir;
dma_addr_t dma_dst_phy;
dma_addr_t dma_dst_vir;
#define HELLO_VENDOR_ID 0x666
#define HELLO_DEVICE_ID 0x999
static struct pci_device_id hello_ids[] = {
{HELLO_VENDOR_ID,HELLO_DEVICE_ID,PCI_ANY_ID,PCI_ANY_ID,0,0,0},
{0,}
};
MODULE_DEVICE_TABLE(pci,hello_ids);
static int hello_probe(struct pci_dev *pdev,const struct pci_device_id *id);
static void hello_remove(struct pci_dev *pdev);
static irqreturn_t hello_interrupt(int irq, void *dev);
void iATU_write_config_dword(struct pci_dev *pdev, int offset, int value)
{
}
static void iATU_bar0(void)
{
}
int dma_read_config_dword(struct pci_dev *pdev, int offset)
{
int value = 0;
return value;
}
void dma_write_config_dword(struct pci_dev *pdev, int offset, int value)
{
}
void dma_init(void)
{
int pos;
u16 msi_control;
u32 msi_addr_l;
u32 msi_addr_h;
u32 msi_data;
pos = pci_find_capability(my_device.pci_dev, PCI_CAP_ID_MSI);
pci_read_config_word(my_device.pci_dev, pos+2, &msi_control);
pci_read_config_dword(my_device.pci_dev, pos+4, &msi_addr_l);
if(msi_control&0x80)
{
pci_read_config_dword(my_device.pci_dev,pos+0x8,&msi_addr_h);
pci_read_config_dword(my_device.pci_dev,pos+0xc,&msi_data);
}else{
pci_read_config_dword(my_device.pci_dev,pos+0x8,&msi_data);
}
}
static int hello_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
int i;
int result;
if (pci_enable_device(pdev))
{
result = -EIO;
goto end;
}
pci_set_master(pdev);
my_device.pci_dev = pdev;
if(unlikely(pci_request_regions(pdev,DEV_NAME)))
{
DEBUG_ERR("failed: pci_request_regions\n");
result = -EIO;
goto enable_device_err;
}
bar0_phy = pci_resource_start(pdev,0);
if(bar0_phy<0)
{
DEBUG_ERR("failed: pci_resource_start\n");
result = -EIO;
goto request_regions_err;
}
bar0_length = pci_resource_len(pdev,0);
if(bar0_length!=0)
{
bar0_vir = (unsigned long)ioremap(bar0_phy,bar0_length);
}
dma_src_vir = (dma_addr_t)pci_alloc_consistent(pdev,DMA_BUFFER_SIZE,&dma_src_phy);
if(dma_src_vir != 0)
{
for(i=0;i<DMA_BUFFER_SIZE/PAGE_SIZE;i++)
{
SetPageReserved(virt_to_page(dma_dst_phy+i*PAGE_SIZE));
}
}else{
goto free_bar0;
}
dma_dst_vir = (dma_addr_t)pci_alloc_consistent(pdev,DMA_BUFFER_SIZE,&dma_src_phy);
if(dma_dst_vir != 0)
{
for(i=0;i<DMA_BUFFER_SIZE/PAGE_SIZE;i++)
{
SetPageReserved(virt_to_page(dma_dst_phy+i*PAGE_SIZE));
}
}else{
goto enable_msi_error;
}
dma_init();
enable_msi_error:
pci_disable_msi(pdev);
alloc_dma_dst_err:
for(i=0;i<DMA_BUFFER_SIZE/PAGE_SIZE;i++)
{
ClearPageReserved(virt_to_page(dma_dst_phy+i*PAGE_SIZE));
}
pci_free_consistent(pdev,DMA_BUFFER_SIZE,(void *)dma_dst_vir,dma_dst_phy);
alloc_dma_src_err:
for(i=0;i<DMA_BUFFER_SIZE/PAGE_SIZE;i++)
{
ClearPageReserved(virt_to_page(dma_src_phy+i*PAGE_SIZE));
}
pci_free_consistent(pdev,DMA_BUFFER_SIZE,(void *)dma_src_vir,dma_src_phy);
free_bar0:
iounmap((void *)bar0_vir);
request_regions_err:
pci_release_regions(pdev);
enable_device_err:
pci_disable_device(pdev);
end:
return result;
}
static void hello_remove(struct pci_dev *pdev)
{
int i;
free_irq(pdev->irq,my_device.pci_dev);
pci_disable_msi(pdev);
for(i=0;i<DMA_BUFFER_SIZE/PAGE_SIZE;i++)
{
ClearPageReserved(virt_to_page(dma_dst_phy+i*PAGE_SIZE));
}
pci_free_consistent(pdev,DMA_BUFFER_SIZE,(void *)dma_dst_vir,dma_dst_phy);
for(i=0;i<DMA_BUFFER_SIZE/PAGE_SIZE;i++)
{
ClearPageReserved(virt_to_page(dma_src_phy+i*PAGE_SIZE));
}
pci_free_consistent(pdev,DMA_BUFFER_SIZE,(void *)dma_src_vir,dma_src_phy);
iounmap((void *)bar0_vir);
pci_release_regions(pdev);
pci_disable_device(pdev);
}
static irqreturn_t hello_interrupt(int irq, void *dev)
{
return 0;
}
static struct pci_driver hello_driver = {
.name = DEV_NAME,
.id_table = hello_ids,
.probe = hello_probe,
.remove = hello_remove,
};
static int hello_open(struct inode *inode, struct file *file)
{
printk("driver: hello_open");
return 0;
}
int hello_close(struct inode *inode, struct file *file)
{
printk("driver: hello_close");
return 0;
}
long hello_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
iATU_bar0();
return 0;
}
static struct file_operations hello_fops = {
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_close,
.unlocked_ioctl = hello_unlocked_ioctl,
};
static int hello_drv_init(void)
{
int ret;
ret = pci_register_driver(&hello_driver);
if(ret<0)
{
printk("failed: pci_register_driver\n");
return ret;
}
ret = alloc_chrdev_region(&my_device.devno,0,DEVICE_NUMBER,"hello");
if(ret<0)
{
printk("failed: register_chrdev_region\n");
return ret;
}
cdev_init(&my_device.cdev,&hello_fops);
ret = cdev_add(&my_device.cdev, my_device.devno,DEVICE_NUMBER);
if(ret<0)
{
printk("failed: cdev_add\n");
return ret;
}
hello_class = class_create(THIS_MODULE, "hello_class");
hello_class_dev = device_create(hello_class,NULL,my_device.devno,NULL,"hello_device");
return 0;
}
static void hello_drv_exit(void)
{
device_destroy(hello_class,my_device.devno);
class_destroy(hello_class);
cdev_del(&(my_device.cdev));
unregister_chrdev_region(my_device.devno,DEVICE_NUMBER);
pci_unregister_driver(&hello_driver);
}
module_init(hello_drv_init);
module_exit(hello_drv_exit);