博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
PCI总线设备遍历
阅读量:4094 次
发布时间:2019-05-25

本文共 9535 字,大约阅读时间需要 31 分钟。

pcibios_scanbus()

//传参为PCI控制器,PCI控制器包含了指向PCI设备信息的地址空间static void pcibios_scanbus(struct pci_controller *hose){
static int next_busno; static int need_domain_info; LIST_HEAD(resources); struct pci_bus *bus; struct pci_host_bridge *bridge; int ret; bridge = pci_alloc_host_bridge(0); //申请PCI主桥struct pci_host_bridge结构体对象 if (!bridge) return; if (hose->get_busno && pci_has_flag(PCI_PROBE_ONLY)) next_busno = (*hose->get_busno)(); pci_add_resource_offset(&resources, hose->mem_resource, hose->mem_offset); pci_add_resource_offset(&resources, hose->io_resource, hose->io_offset); pci_add_resource(&resources, hose->busn_resource); list_splice_init(&resources, &bridge->windows); //将PCI总线可用的I/O资源添加到resources链表中。并将该resources链表与bridge中的windows属性关联。 bridge->dev.parent = NULL; bridge->sysdata = hose; //该属性用来存储PCI控制器 bridge->busnr = next_busno; bridge->ops = hose->pci_ops; bridge->swizzle_irq = pci_common_swizzle; bridge->map_irq = pcibios_map_irq; //对桥片进行相关的初始配置 ret = pci_scan_root_bus_bridge(bridge); //该函数对桥片进行注册 if (ret) {
pci_free_host_bridge(bridge); return; } hose->bus = bus = bridge->bus; //指定PCI控制器所连接的总线 ... pci_bus_add_devices(bus);}
int pci_scan_root_bus_bridge(struct pci_host_bridge *bridge){
struct resource_entry *window; bool found = false; struct pci_bus *b; int max, bus, ret; if (!bridge) return -EINVAL; resource_list_for_each_entry(window, &bridge->windows) if (window->res->flags & IORESOURCE_BUS) {
found = true; break; } ret = pci_register_host_bridge(bridge); //注册桥片 if (ret < 0) return ret; b = bridge->bus; bus = bridge->busnr; ... max = pci_scan_child_bus(b); //当桥片与总线注册完成之后,遍历所有子线下的设备,该函数实际执行为pci_scan_child_bus_extend()。 ... return 0;}
static int pci_register_host_bridge(struct pci_host_bridge *bridge){
struct device *parent = bridge->dev.parent; struct resource_entry *window, *n; struct pci_bus *bus, *b; ... bus = pci_alloc_bus(NULL); //根据PCI设备与桥片,以及总线的结构,需要为桥片创建总线对象 if (!bus) return -ENOMEM; bridge->bus = bus; list_splice_init(&bridge->windows, &resources); bus->sysdata = bridge->sysdata; bus->msi = bridge->msi; bus->ops = bridge->ops; bus->number = bus->busn_res.start = bridge->busnr;#ifdef CONFIG_PCI_DOMAINS_GENERIC bus->domain_nr = pci_bus_find_domain_nr(bus, parent); //根据总线结构的特点,计算机系统中可能存在多种不同的总线,为了区分不同种类的总线,内核按照域号来对其进行划分。#endif //总线对象进行初始化赋值,总线与桥片的部分属性是一致的 b = pci_find_bus(pci_domain_nr(bus), bridge->busnr); //该函数主要检测当前的总线是否已存在 if (b) {
dev_dbg(&b->dev, "bus already known\n"); err = -EEXIST; goto free; } dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(bus), bridge->busnr); //设置桥片的名称 err = pcibios_root_bridge_prepare(bridge); if (err) goto free; err = device_register(&bridge->dev); //注册桥片 if (err) {
put_device(&bridge->dev); goto free; } bus->bridge = get_device(&bridge->dev); //总线设置桥片 ... err = device_register(&bus->dev); //注册总线设备 if (err) goto unregister; pcibios_add_bus(bus); //添加总线,当前为空操作 pci_create_legacy_files(bus); //为总线创建I/O和内存文件 ... resource_list_for_each_entry_safe(window, n, &resources) {
//添加总线所能使用的资源 list_move_tail(&window->node, &bridge->windows); offset = window->offset; res = window->res; if (res->flags & IORESOURCE_BUS) pci_bus_insert_busn_res(bus, bus->number, res->end); else pci_bus_add_resource(bus, res, 0); ... } down_write(&pci_bus_sem); list_add_tail(&bus->node, &pci_root_buses); //添加总线节点 up_write(&pci_bus_sem); return 0; ...}

关于PCI设备的挂载,是从遍历子线开始的。即从pci_scan_child_bus函数调用pci_scan_child_bus_extend函数开始的。

static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus, unsigned int avaliable_buses){
unsigned int used_buses, normal_bridges = 0, hotplug_bridges = 0; unsigned int start = bus->busn_res.start; unsigned int devfn, fn, cmax, max = start; struct pci_dev *dev; int nr_devs; dev_dbg(&bus->dev, "scanning bus\n"); for (devfn = 0; devfn < 256; devfn += 8) {
//该循环是根据PCI设备来执行,目前每条PCI总线可挂载256个PCI设备,而每8个设备可位于同一个PCI槽 //因此,这里的跨度为8,每次处理的对象为一个PCI槽中的设备 nr_devs = pci_scan_slot(bus, devfn); //该函数中会创建PCI设备,并将该设备添加到bus的devices属性中 if (jailhouse_paravirt() && nr_devs == 0) {
for (fn = 1; fn < 8; fn++) {
dev = pci_scan_single_device(bus, devfn + fn); if (dev) dev->multifunction = 1; } } } ... for_each_pci_bridge(dev, bus) {
//区分普通桥片和热插拔桥片的个数 if (dev->is_hotplug_bridge) hotplug_bridges++; else normal_bridges++; } for_each_pci_bridge(dev, bus) {
//当前总线中的设备有可能是桥片,因此需要遍历该子桥片上的设备 cmax = max; max = pci_scan_bridge_extend(bus, dev, max, 0, 0); //在该函数中,会有可能再次调用pci_scan_child_bus函数 used_buses++; if (cmax - max > 1) used_buses += cmax - max - 1; } for_each_pci_bridge(dev, bus) {
unsigned int buses = 0; if (!hotplug_bridges && normal_bridges == 1) {
buses = avaliable_buses; } else if (dev->is_hotplug_bridge) {
buses = avaliable_buses / hotplug_bridges; buses = min(buses, avaliable_buses - used_buses + 1); } cmax = max; max = pci_scan_bridge_extend(bus, dev, cmax, buses, 1); if (max - cmax > 1) used_buses += max - cmax - 1; }}
int pci_scan_slot(struct pci_bus *bus, int devfn){
unsigned fn, nr = 0; struct pci_dev *dev; if (only_one_child(bus) && (devfn > 0)) return 0; dev = pci_scan_single_device(bus, devfn); //根据devfn,遍历单个设备 if (!dev) return 0; if (!pci_dev_is_added(dev)) nr++; for (fn = next_fn(bus, dev, 0); fn > 0; fn = next_fn(bus, dev, fn)) {
dev = pci_scan_single_device(bus, devfn + fn); if (dev) {
if (!pci_dev_is_added(dev)) nr++; dev->multifunction = 1; } } if (bus->self && nr) pcie_aspm_init_link_state(bus->self); return nr;}
struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn){
struct pci_dev *dev; dev = pci_get_slot(bus, devfn); //根据编号,从PCI槽中获取对应的PCI设备 if (dev) {
pci_dev_put(dev); return dev; } dev = pci_scan_device(bus, devfn); //假设从PCI槽中没有获取到PCI设备,则利用该方法来获取 //该函数通过bus找到pci_host_bridge结构体,随后通过桥片的PCI控制器来获取设备的vendor_id以及device_id。执行这一过程的函数为pci_bus_read_dev_vendor_id()。 if (!dev) return NULL; pci_device_add(dev, bus); return dev;}
static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn){
struct pci_dev *dev; u32 l; if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000)) return NULL; dev = pci_alloc_dev(bus); //创建struct pci_dev结构体对象,用来描述找到的PCI设备,同时将该设备与该总线进行关联 if (!dev) return NULL; dev->devfn = devfn; dev->vendor = l & 0xffff; dev->device = (l >> 16) & 0xffff; pci_set_of_node(dev); //该函数用来将PCI设备与对应的设备节点进行关联 if (pci_setup_device(dev)) {
//该函数主要用来读取PCI控制器中的地址空间,从而来设置当前的PCI设备对象,该函数为关键的一步操作。 //在该函数基本对pci_dev结构体进行大部分的关键设置。 pci_bus_put(dev->bus); kfree(dev); return NULL; } return dev;}
void pci_bus_add_devices(const struct pci_bus *bus){
struct pci_dev *dev; struct pci_bus *child; list_for_each_entry(dev, &bus->devices, bus_list) {
if (pci_dev_is_added(dev)) //如果pci设备已经被添加,则遍历下一个pci设备。 continue; pci_bus_add_device(dev); //如果没有,则将pci设备添加到pci总线当中。 } list_for_each_entry() {
if (!pci_dev_is_added(dev)) continue; child = dev->subordinate; if (child) pci_bus_add_devices(child); }}

struct pci_controller结构体:

struct pci_controller {
struct list_head list; struct pci_bus *bus; struct device_node *of_node; struct pic_ops *pci_ops; struct resource *mem_resource; unsigned long mem_offset; struct resource *io_resource; unsigned long io_offset; unsigned long io_map_base; struct resource *busn_resource;#ifndef CONFIG_PCI_DOMAINS_GENERIC unsigned int index; unsigned int need_domain_info;#endif int (*get_busno)(void); void (*set_busno)(int busno);};

struct pci_host_bridge结构体:

struct pci_host_bridge {
struct device dev; struct pci_bus *bus; struct pci_ops *ops; void *sysdata; int busnr; struct list_head windows; struct list_head dma_ranges; u8 (*swizzle_irq)(struct pci_dev *, u8 *); int (*map_irq)(const struct pci_dev *, u8, u8); void (*release_fn)(struct pci_host_bridge *); void *release_data; struct msi_controller *msi; ... unsigned long private[0] ____cacheline_aligned;};

struct pci_bus结构体:

struct pci_bus {
struct list_head node; struct pci_bus *parent; //pci总线的父节点,比如:PCI总线可以挂载在另一条PCI总线上。 struct list_head children; struct list_head devices; //pci总线上的设备链表,该链表中主要连接的结构体对象为struct pci_dev。 struct pci_dev *self; struct list_head slots; //pci总线上的PCI槽链表 struct resource *resource[PCI_BRIDGE_RESOURCE_NUM]; //pci总线可用的资源,即I/O地址空间 struct list_head resources; struct resource busn_res; struct pci_ops *ops; struct msi_controller *msi; void *sysdata; struct proc_dir_entry *procdir; unsigned char number; unsigned char primary; unsigned char max_bus_speed; unsigned char cur_bus_speed;#ifdef CONFIG_PCI_DOMAINS_GENERIC int domain_nr;#endif char name[48]; ... struct device *bridge; //pci总线所使用的桥片 struct device dev; struct bin_attribute *legacy_io; struct bin_attribute *legacy_mem; unsigned int is_added:1;};

struct pci_dev结构体:

struct pci_dev {
struct list_head bus_list; struct pci_bus *bus; struct pci_bus *subordinate; void *sysdata; struct proc_dir_entry *procent; struct pci_slot *slot; unsigned int devfn; unsigned short vendor; unsigned short device; unsigned short subsystem_vendor; unsigned short subsystem_device; unsigned int class; u8 revision; u8 hdr_type; ... u8 pin; ... struct pci_driver *driver; u64 dma_mask; ... struct device dev; int cfg_size; unsigned int irq; struct resource resource[DEVICE_COUNT_RESOURCE]; bool match_driver; ...}

关于PCI设备挂载的实现方式,目前有三种方式,分别为:

序号 PCI设备挂载方式
1 以上述代码为例,内核中创建并定义struct pci_controller控制器对象,随后通过该对象来挂载设备
2 将PCI控制器抽象为platform_device设备,随后通过注册该设备驱动来挂载设备
3 利用dts文件,声明PCI控制器节点,通过dts文件,内核可将其构建为platform_device设备,随后通过注册该设备驱动来挂载设备

上述的三种方式,最为方便的为第一种。关于后边两种,需要对设备树的相关操作进行分析。

转载地址:http://rsxii.baihongyu.com/

你可能感兴趣的文章
DirectX11 聚光灯
查看>>
DirectX11 HLSL打包(packing)格式和“pad”变量的必要性
查看>>
DirectX11 光照演示示例Demo
查看>>
漫谈一下前端的可视化技术
查看>>
VUe+webpack构建单页router应用(一)
查看>>
Vue+webpack构建单页router应用(二)
查看>>
从头开始讲Node.js——异步与事件驱动
查看>>
Node.js-模块和包
查看>>
Node.js核心模块
查看>>
express的应用
查看>>
NodeJS开发指南——mongoDB、Session
查看>>
Express: Can’t set headers after they are sent.
查看>>
2017年,这一次我们不聊技术
查看>>
实现接口创建线程
查看>>
Java对象序列化与反序列化(1)
查看>>
HTML5的表单验证实例
查看>>
JavaScript入门笔记:全选功能的实现
查看>>
程序设计方法概述:从面相对象到面向功能到面向对象
查看>>
数据库事务
查看>>
JavaScript基础1:JavaScript 错误 - Throw、Try 和 Catch
查看>>