device_id_check(mod->name, device_id, size, id_size, symval); /* Leave last one: it's the terminator. */ size -= id_size;
for (i = 0; i < size; i += id_size) { if (do_entry(mod->name, symval+i, alias)) { buf_printf(&mod->dev_table_buf, "MODULE_ALIAS(\"%s\");\n", alias); } } }
/* The pci_dev structure describes PCI devices */ structpci_dev { structlist_headbus_list;/* Node in per-bus list */ structpci_bus *bus;/* Bus this device is on */ structpci_bus *subordinate;/* Bus this device bridges to */
void *sysdata; /* Hook for sys-specific extension */ structproc_dir_entry *procent;/* Device entry in /proc/bus/pci */ structpci_slot *slot;/* Physical slot this device is in */
unsignedint devfn; /* Encoded device & function index */ unsignedshort vendor; unsignedshort device; unsignedshort subsystem_vendor; unsignedshort subsystem_device; unsignedintclass;/* 3 bytes: (base,sub,prog-if) */ u8 revision; /* PCI revision, low byte of class word */ u8 hdr_type; /* PCI header type (`multi' flag masked out) */
/* ... */
structpci_driver *driver;/* Driver bound to this device */
/* ... */
pci_channel_state_t error_state; /* Current connectivity state */ structdevicedev;/* Generic device interface */
int cfg_size; /* Size of config space */
/* * Instead of touching interrupt line and base address registers * directly, use the values stored here. They might be different! */ unsignedint irq; structresourceresource[DEVICE_COUNT_RESOURCE];/* I/O and memory regions + expansion ROMs */
bool match_driver; /* Skip attaching driver */
/* ... */
pci_dev_flags_t dev_flags; atomic_t enable_cnt; /* pci_enable_device has been called */
u32 saved_config_space[16]; /* Config space saved at suspend time */ structhlist_headsaved_cap_space; structbin_attribute *rom_attr;/* Attribute descriptor for sysfs ROM entry */ int rom_attr_enabled; /* Display of ROM attribute enabled? */ structbin_attribute *res_attr[DEVICE_COUNT_RESOURCE];/* sysfs file for resources */ structbin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE];/* sysfs file for WC mapping of resources */
/* ... */
phys_addr_t rom; /* Physical address if not from BAR */ size_t romlen; /* Length if not from BAR */ char *driver_override; /* Driver name to force a match */
unsignedlong priv_flags; /* Private flags for the PCI driver */ };
structpci_bus { structlist_headnode;/* Node in list of buses */ structpci_bus *parent;/* Parent bus this bridge is on */ structlist_headchildren;/* List of child buses */ structlist_headdevices;/* List of devices on this bus */ structpci_dev *self;/* Bridge device as seen by parent */ structlist_headslots;/* List of slots on this bus; protected by pci_slot_mutex */ structresource *resource[PCI_BRIDGE_RESOURCE_NUM]; structlist_headresources;/* Address space routed to this bus */ structresourcebusn_res;/* Bus numbers routed to this bus */
/* Do NOT directly access these two variables, unless you are arch-specific PCI * code, or PCI core code. */ externstructlist_headpci_root_buses;/* List of all known PCI buses */
/* Add initial resources to the bus */ resource_list_for_each_entry_safe(window, n, &resources) { list_move_tail(&window->node, &bridge->windows); offset = window->offset; res = window->res;
structresource *resource[PCI_BRIDGE_RESOURCE_NUM]; structlist_headresources;/* Address space routed to this bus */ structresourcebusn_res;/* Bus numbers routed to this bus */
/* * The Jailhouse hypervisor may pass individual functions of a * multi-function device to a guest without passing function 0. * Look for them as well. */ 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; } } }
/* * Scan bridges that are already configured. We don't touch them * unless they are misconfigured (which will be done in the second * scan below). */ for_each_pci_bridge(dev, bus) { cmax = max; max = pci_scan_bridge_extend(bus, dev, max, 0, 0);
/* * Reserve one bus for each bridge now to avoid extending * hotplug bridges too much during the second scan below. */ used_buses++; if (cmax - max > 1) used_buses += cmax - max - 1; }
/* Scan bridges that need to be reconfigured */ for_each_pci_bridge(dev, bus) { unsignedint buses = 0;
if (!hotplug_bridges && normal_bridges == 1) {
/* * There is only one bridge on the bus (upstream * port) so it gets all available buses which it * can then distribute to the possible hotplug * bridges below. */ buses = available_buses; } elseif (dev->is_hotplug_bridge) {
/* * Distribute the extra buses between hotplug * bridges if any. */ buses = available_buses / hotplug_bridges; buses = min(buses, available_buses - used_buses + 1); }
cmax = max; max = pci_scan_bridge_extend(bus, dev, cmax, buses, 1); /* One bus is already accounted so don't add it again */ if (max - cmax > 1) used_buses += max - cmax - 1; }
/* * Instead of touching interrupt line and base address registers * directly, use the values stored here. They might be different! */ unsignedint irq; structresourceresource[DEVICE_COUNT_RESOURCE];/* I/O and memory regions + expansion ROMs */
/* * Add the device to our list of discovered devices * and the bus list for fixup functions, etc. */ down_write(&pci_bus_sem); list_add_tail(&dev->bus_list, &bus->devices); up_write(&pci_bus_sem);
/* Notifier could use PCI capabilities */ dev->match_driver = false; ret = device_add(&dev->dev); WARN_ON(ret < 0);
if (child->ops->add_bus) { ret = child->ops->add_bus(child); if (WARN_ON(ret < 0)) dev_err(&child->dev, "failed to add bus: %d\n", ret); }
PCI Device Probing
Matching and Dynamic ID
编写驱动时,主要是实现一个pci_driver对象,其定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
structpci_driver { structlist_headnode; constchar *name; conststructpci_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 (*suspend_late)(struct pci_dev *dev, pm_message_t state); int (*resume_early)(struct pci_dev *dev); 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 */ conststructpci_error_handlers *err_handler; conststructattribute_group **groups; structdevice_driverdriver; structpci_dynidsdynids; };
/* * Unbound PCI devices are always put in D0, regardless of * runtime PM status. During probe, the device is set to * active and the usage count is incremented. If the driver * supports runtime PM, it should call pm_runtime_put_noidle(), * or any other runtime PM helper function decrementing the usage * count, in its probe routine and pm_runtime_get_noresume() in * its remove routine. */ pm_runtime_get_sync(dev); pci_dev->driver = pci_drv; rc = pci_drv->probe(pci_dev, ddi->id); if (!rc) return rc; if (rc < 0) { pci_dev->driver = NULL; pm_runtime_put_sync(dev); return rc; } /* * Probe function should return < 0 for failure, 0 for success * Treat values > 0 as success, but warn. */ dev_warn(dev, "Driver probe function unexpectedly returned %d\n", rc); return0; }