俗话说的好,免费是最贵,闲暇是最累的,但是我自己选的路就要负责走完;
压力一天比一天重,当学习了理论却不知道该如何输出的时候,会有一种油然而生的挫败感;
看来必须得调整自己的心态还是要调整学习方法,如何才能用最好状态去接受新的知识;
文章目录
[0x100]概念与特征
[0x110] PCI接口特征
概述 | 描述 |
---|---|
更高传输性能 | 由于拥有 66MHz~133MHz 的时钟频率传输速率、32/64位内存地址空间 |
替代ISA设备 | 用于计算机与外设通讯的接口 |
平台无关性 | 接口板自动探测、无需初始化配置,多功能中断线明确不冲突 |
多设备支持 | 基于PCI域,最多支持256域 每个域支持32 个设备 |
存在配置空间 | 通过普通I/O 寻址访问配置空间, 每个PCI设备拥有256K大小配置空间 |
这里黑了一张书上的图图,描述 PCI到底处于总线架构什么位置,以及怎么连接到CPU的
[0x120] PCI接口寻址
- 8位总线编号 + 8 位设备/功能编号
- 8位总线编号 + 8 位设备编号 + 4位功能编号 [图示]
- 16位PCI域号 + 8 位总线编号 + 5 位设备编号 + 3位功能编号
- 总线共享空间 :PCI内存映射、PCI I/O端口
- 总线私有空间 :配置寄存器
[0x200] PCI设备相关信息
[0x210]配置空间
- PCI 设备引导阶段只响应配置事务,因为配置空间没有初始化成功时,不知道存在哪些设备;
- 所有设备中断为禁用状态;
前64字节的配置空间
- PCI 配置寄存器的字节序是小端字节序,如果使用大端的系统时,要注意访问问题;
- PCI 共有256k的配置空间,前64位标准的寄存器空间,后面空间为外设卡修改
- 16 位厂商ID [0x1 0x2]+ 16位设备ID[0x3 0x4]==设备标识符;
- 24 位类代号 高8位为所属主类;
- 16 位子系统厂商ID + 16位子系统设备ID == 子系统标识符,用于设备PCI设备具体型号区别;
#include <linux/mod_devicetable.h>
/*驱动程序支持设备标识列表,通常是以结构体数组的形式出现,结束需要显式指定"{}"*/
struct pci_device_id {
__u32 vendor, device; /* PCI设备标识符 Vendor and device ID or PCI_ANY_ID*/
__u32 subvendor, subdevice; /* PCI设备型号标识符 Subsystem ID's or PCI_ANY_ID */
__u32 class, class_mask; /* PCI设备所属设备组类 (class,subclass,prog-if) triplet */
kernel_ulong_t driver_data; /* PCI驱动私有数据*/
};
/*初始化 仅配置vendor和device,子系统型号被设置成了 PCI_ANY_ID*/
#define PCI_DEVICE(vend,dev) \
.vendor = (vend), .device = (dev), \
.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
/*设置PCI 设备主类标识,以及类掩码*/
#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
/*struct pci_device_id 信息导出到用户空间,type=pci name= 【struct pci_device_id的数组名称】*/
#define MODULE_DEVICE_TABLE(type,name) MODULE_GENERIC_TABLE(type##_device,name)
#if defined(MODULE) || !defined(CONFIG_SYSFS)
#define MODULE_VERSION(_version) MODULE_INFO(version, _version)
#else
#define MODULE_VERSION(_version) \
static struct module_version_attribute ___modver_attr = { \
.mattr = { \
.attr = { \
.name = "version", \
.mode = S_IRUGO, \
}, \
.show = __modver_version_show, \
}, \
.module_name = KBUILD_MODNAME, \
.version = _version, \
}; \
static const struct module_version_attribute \
__used __attribute__ ((__section__ ("__modver"))) \
* __moduleparam_const __modver_attr = &___modver_attr
#endif
[0x211]访问配置空间接口
- 配置空间用于查找设备映射位置与IO空间的映射位置;
#include <linux/pci.h>
struct pci_bus {
struct list_head node; /* 总线节点链表*/
struct pci_bus *parent; /* parent bus this bridge is on */
struct list_head children; /* 子总线列表*/
struct list_head devices; /* 总线设备列表*/
struct pci_dev *self; /* bridge device as seen by parent */
struct list_head slots; /* 总线槽列表 */
struct resource *resource[PCI_BRIDGE_RESOURCE_NUM]; /*获取配置空间 BASE_ADDR bar*/
struct list_head resources;
struct pci_ops *ops; /* 配置访问控制函数集*/
void *sysdata; /* hook for sys-specific extension */
struct proc_dir_entry *procdir; /* 进程目录pci总线目录 */
unsigned char number; /* 总线编号 */
unsigned char primary; /* 主桥编号 */
unsigned char secondary; /* 副桥编号*/
unsigned char subordinate; /* 次级总线最大数量 */
unsigned char max_bus_speed; /* 最大总线速率 */
unsigned char cur_bus_speed; /* 当前总线速率 */
char name[48]; /*总线名称*/
unsigned short bridge_ctl; /* manage NO_ISA/FBB/et al behaviors */
pci_bus_flags_t bus_flags; /* 子总线继承 */
struct device *bridge;
struct device dev;
struct bin_attribute *legacy_io; /* IO端口属性*/
struct bin_attribute *legacy_mem; /* IO内存属性*/
unsigned int is_added:1;
};
/*如果不能使用 struct pci_dev 任何时候都可以直接调用里面的函数
*实际使用的 struct pci_bus * 和 unsigned int devfn,来自于pci_dev 中,
*/
static inline int pci_read_config_byte(const struct pci_dev *dev, int where, u8 *val)
{
return pci_bus_read_config_byte(dev->bus, dev->devfn, where, val);
}
static inline int pci_read_config_word(const struct pci_dev *dev, int where, u16 *val)
{
return pci_bus_read_config_word(dev->bus, dev->devfn, where, val);
}
static inline int pci_read_config_dword(const struct pci_dev *dev, int where,u32 *val)
{
return pci_bus_read_config_dword(dev->bus, dev->devfn, where, val);
}
static inline int pci_write_config_byte(const struct pci_dev *dev, int where, u8 val)
{
return pci_bus_write_config_byte(dev->bus, dev->devfn, where, val);
}
static inline int pci_write_config_word(const struct pci_dev *dev, int where, u16 val)
{
return pci_bus_write_config_word(dev->bus, dev->devfn, where, val);
}
static inline int pci_write_config_dword(const struct pci_dev *dev, int where,u32 val)
{
return pci_bus_write_config_dword(dev->bus, dev->devfn, where, val);
}
[0x212]获取PCI设备IO空间位置
#include <linux/pci.h>
/*pci_dev 获取配置空间中 BASE_ADDR bar 的第一个可用地址,返回地址unsigned long*/
#define pci_resource_start(dev, bar) ((dev)->resource[(bar)].start)
/*pci_dev 获取配置空间中 BASE_ADDR bar 的最后一个可用地址,返回地址unsigned long*/
#define pci_resource_end(dev, bar) ((dev)->resource[(bar)].end)
[0x213]中断侦测
- PCI_INTERRUPT_LINE : 中断线 , 位于配置空间 0x3c 的位置;
- PCI_INTERRUPT_PIN : 中断侦测位,位于配置空间 0x3e的位置;
- 只需要通过IO空间读取函数从中断线中读取中断号,然后注册IRQ就好了;
[0x220]注册PCI驱动程序流程
[0x221] 填充结构pci_driver
#include <linux/pci.h>
/*1.定义并填充PCI设备信息结构,*/
struct pci_driver {
/*内核实现链表*/
struct list_head node;
/*[必选参数]设备名称 显示在 /proc/bus/pci/device 目录下*/
const char *name;
/*[必选参数]硬件PCI设备接入功能列表 数组首地址指定*/
const struct pci_device_id *id_table;
/*[必选参数]当新设备插入后,初始化 */
int (*probe) (struct pci_dev *dev, const struct pci_device_id *id);
/*[必选参数]设备移除清理函数 */
void (*remove) (struct pci_dev *dev);
/*[可选函数]当指向的pci_dev设备,被挂起时执行 */
int (*suspend) (struct pci_dev *dev, pm_message_t state);
int (*suspend_late) (struct pci_dev *dev, pm_message_t state);
/*恢复挂起的PCI设备 */
int (*resume) (struct pci_dev *dev);
int (*resume_early) (struct pci_dev *dev);
/*关机时安全的卸载设备*/
void (*shutdown) (struct pci_dev *dev);
struct pci_error_handlers *err_handler;
struct device_driver driver;
struct pci_dynids dynids;
};
[0x221] 注册pci驱动程序
/*注册PCI设备到内核中*/
#define pci_register_driver(driver) \
__pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
/*核心函数 */
int __pci_register_driver(struct pci_driver *drv, struct module *owner,const char *mod_name)
{
int error;
/* initialize common driver fields */
drv->driver.name = drv->name;
drv->driver.bus = &pci_bus_type;
drv->driver.owner = owner;
drv->driver.mod_name = mod_name;
spin_lock_init(&drv->dynids.lock);
INIT_LIST_HEAD(&drv->dynids.list);
/* register with core */
error = driver_register(&drv->driver);
if (error)
goto out;
/*动态分配PCI ID,写入文件为 new_id*/
error = pci_create_newid_files(drv);
if (error)
goto out_newid;
out:
return error;
out_newid:
driver_unregister(&drv->driver);
goto out;
}
Func : 将注册PCI设备到内核中
args1: 驱动程序信息,于第一步填充完成;
args2: 模块所有者,常见的THIS_MODULE;
args3: 模块名称,只能用这个“KBUILD_MODNAME”;
retval: 成功 0 失败错误码
[0x222] 注销pci驱动程序
void
pci_unregister_driver(struct pci_driver *drv)
{
pci_remove_newid_files(drv);
driver_unregister(&drv->driver);
pci_free_dynids(drv);
}
[0x222] 激活pci驱动程序
int pci_enable_device(struct pci_dev *dev)
{
return __pci_enable_device_flags(dev, IORESOURCE_MEM | IORESOURCE_IO);
}
/*核心函数*/
static int __pci_enable_device_flags(struct pci_dev *dev,resource_size_t flags)
{
int err;
int i, bars = 0;
if (dev->pm_cap) {
u16 pmcsr;
pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
}
if (atomic_add_return(1, &dev->enable_cnt) > 1)
return 0; /* already enabled */
/* only skip sriov related */
for (i = 0; i <= PCI_ROM_RESOURCE; i++)
if (dev->resource[i].flags & flags)
bars |= (1 << i);
for (i = PCI_BRIDGE_RESOURCES; i < DEVICE_COUNT_RESOURCE; i++)
if (dev->resource[i].flags & flags)
bars |= (1 << i);
err = do_pci_enable_device(dev, bars);
if (err < 0)
atomic_dec(&dev->enable_cnt);
return err;
}