📄 pci.c
字号:
int pci_request_regions(struct pci_dev *pdev, char *res_name){ int i; for (i = 0; i < 6; i++) { if (pci_resource_len(pdev, i) == 0) continue; if (pci_resource_flags(pdev, i) & IORESOURCE_IO) { if (!request_region(pci_resource_start(pdev, i), pci_resource_len(pdev, i), res_name)) goto err_out; } else if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) { if (!request_mem_region(pci_resource_start(pdev, i), pci_resource_len(pdev, i), res_name)) goto err_out; } } return 0;err_out: printk (KERN_WARNING "PCI: Unable to reserve %s region #%d:%lx@%lx for device %s\n", pci_resource_flags(pdev, i) & IORESOURCE_IO ? "I/O" : "mem", i + 1, /* PCI BAR # */ pci_resource_len(pdev, i), pci_resource_start(pdev, i), pdev->slot_name); pci_release_regions(pdev); return -EBUSY;}/* * Registration of PCI drivers and handling of hot-pluggable devices. */static LIST_HEAD(pci_drivers);/** * pci_match_device - Tell if a PCI device structure has a matching PCI device id structure * @ids: array of PCI device id structures to search in * @dev: the PCI device structure to match against * * Used by a driver to check whether a PCI device present in the * system is in its list of supported devices.Returns the matching * pci_device_id structure or %NULL if there is no match. */const struct pci_device_id *pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev){ while (ids->vendor || ids->subvendor || ids->class_mask) { if ((ids->vendor == PCI_ANY_ID || ids->vendor == dev->vendor) && (ids->device == PCI_ANY_ID || ids->device == dev->device) && (ids->subvendor == PCI_ANY_ID || ids->subvendor == dev->subsystem_vendor) && (ids->subdevice == PCI_ANY_ID || ids->subdevice == dev->subsystem_device) && !((ids->class ^ dev->class) & ids->class_mask)) return ids; ids++; } return NULL;}static intpci_announce_device(struct pci_driver *drv, struct pci_dev *dev){ const struct pci_device_id *id; int ret = 0; if (drv->id_table) { id = pci_match_device(drv->id_table, dev); if (!id) { ret = 0; goto out; } } else id = NULL; dev_probe_lock(); if (drv->probe(dev, id) >= 0) { dev->driver = drv; ret = 1; } dev_probe_unlock();out: return ret;}/** * pci_register_driver - register a new pci driver * @drv: the driver structure to register * * Adds the driver structure to the list of registered drivers * Returns the number of pci devices which were claimed by the driver * during registration. The driver remains registered even if the * return value is zero. */intpci_register_driver(struct pci_driver *drv){ struct pci_dev *dev; int count = 0; list_add_tail(&drv->node, &pci_drivers); pci_for_each_dev(dev) { if (!pci_dev_driver(dev)) count += pci_announce_device(drv, dev); } return count;}/** * pci_unregister_driver - unregister a pci driver * @drv: the driver structure to unregister * * Deletes the driver structure from the list of registered PCI drivers, * gives it a chance to clean up by calling its remove() function for * each device it was responsible for, and marks those devices as * driverless. */voidpci_unregister_driver(struct pci_driver *drv){ struct pci_dev *dev; list_del(&drv->node); pci_for_each_dev(dev) { if (dev->driver == drv) { if (drv->remove) drv->remove(dev); dev->driver = NULL; } }}#ifdef CONFIG_HOTPLUG#ifndef FALSE#define FALSE (0)#define TRUE (!FALSE)#endifstatic voidrun_sbin_hotplug(struct pci_dev *pdev, int insert){ int i; char *argv[3], *envp[8]; char id[20], sub_id[24], bus_id[24], class_id[20]; if (!hotplug_path[0]) return; sprintf(class_id, "PCI_CLASS=%04X", pdev->class); sprintf(id, "PCI_ID=%04X:%04X", pdev->vendor, pdev->device); sprintf(sub_id, "PCI_SUBSYS_ID=%04X:%04X", pdev->subsystem_vendor, pdev->subsystem_device); sprintf(bus_id, "PCI_SLOT_NAME=%s", pdev->slot_name); i = 0; argv[i++] = hotplug_path; argv[i++] = "pci"; argv[i] = 0; i = 0; /* minimal command environment */ envp[i++] = "HOME=/"; envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; /* other stuff we want to pass to /sbin/hotplug */ envp[i++] = class_id; envp[i++] = id; envp[i++] = sub_id; envp[i++] = bus_id; if (insert) envp[i++] = "ACTION=add"; else envp[i++] = "ACTION=remove"; envp[i] = 0; call_usermodehelper (argv [0], argv, envp);}/** * pci_announce_device_to_drivers - tell the drivers a new device has appeared * @dev: the device that has shown up * * Notifys the drivers that a new device has appeared, and also notifys * userspace through /sbin/hotplug. */voidpci_announce_device_to_drivers(struct pci_dev *dev){ struct list_head *ln; for(ln=pci_drivers.next; ln != &pci_drivers; ln=ln->next) { struct pci_driver *drv = list_entry(ln, struct pci_driver, node); if (drv->remove && pci_announce_device(drv, dev)) break; } /* notify userspace of new hotplug device */ run_sbin_hotplug(dev, TRUE);}/** * pci_insert_device - insert a hotplug device * @dev: the device to insert * @bus: where to insert it * * Add a new device to the device lists and notify userspace (/sbin/hotplug). */voidpci_insert_device(struct pci_dev *dev, struct pci_bus *bus){ list_add_tail(&dev->bus_list, &bus->devices); list_add_tail(&dev->global_list, &pci_devices);#ifdef CONFIG_PROC_FS pci_proc_attach_device(dev);#endif pci_announce_device_to_drivers(dev);}static voidpci_free_resources(struct pci_dev *dev){ int i; for (i = 0; i < PCI_NUM_RESOURCES; i++) { struct resource *res = dev->resource + i; if (res->parent) release_resource(res); }}/** * pci_remove_device - remove a hotplug device * @dev: the device to remove * * Delete the device structure from the device lists and * notify userspace (/sbin/hotplug). */voidpci_remove_device(struct pci_dev *dev){ if (dev->driver) { if (dev->driver->remove) dev->driver->remove(dev); dev->driver = NULL; } list_del(&dev->bus_list); list_del(&dev->global_list); pci_free_resources(dev);#ifdef CONFIG_PROC_FS pci_proc_detach_device(dev);#endif /* notify userspace of hotplug device removal */ run_sbin_hotplug(dev, FALSE);}#endifstatic struct pci_driver pci_compat_driver = { name: "compat"};/** * pci_dev_driver - get the pci_driver of a device * @dev: the device to query * * Returns the appropriate pci_driver structure or %NULL if there is no * registered driver for the device. */struct pci_driver *pci_dev_driver(const struct pci_dev *dev){ if (dev->driver) return dev->driver; else { int i; for(i=0; i<=PCI_ROM_RESOURCE; i++) if (dev->resource[i].flags & IORESOURCE_BUSY) return &pci_compat_driver; } return NULL;}/* * This interrupt-safe spinlock protects all accesses to PCI * configuration space. */static spinlock_t pci_lock = SPIN_LOCK_UNLOCKED;/* * Wrappers for all PCI configuration access functions. They just check * alignment, do locking and call the low-level functions pointed to * by pci_dev->ops. */#define PCI_byte_BAD 0#define PCI_word_BAD (pos & 1)#define PCI_dword_BAD (pos & 3)#define PCI_OP(rw,size,type) \int pci_##rw##_config_##size (struct pci_dev *dev, int pos, type value) \{ \ int res; \ unsigned long flags; \ if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \ spin_lock_irqsave(&pci_lock, flags); \ res = dev->bus->ops->rw##_##size(dev, pos, value); \ spin_unlock_irqrestore(&pci_lock, flags); \ return res; \}PCI_OP(read, byte, u8 *)PCI_OP(read, word, u16 *)PCI_OP(read, dword, u32 *)PCI_OP(write, byte, u8)PCI_OP(write, word, u16)PCI_OP(write, dword, u32)/** * pci_set_master - enables bus-mastering for device dev * @dev: the PCI device to enable * * Enables bus-mastering on the device and calls pcibios_set_master() * to do the needed arch specific settings. */voidpci_set_master(struct pci_dev *dev){ u16 cmd; pci_read_config_word(dev, PCI_COMMAND, &cmd); if (! (cmd & PCI_COMMAND_MASTER)) { DBG("PCI: Enabling bus mastering for device %s\n", dev->slot_name); cmd |= PCI_COMMAND_MASTER; pci_write_config_word(dev, PCI_COMMAND, cmd); } pcibios_set_master(dev);}/** * pdev_set_mwi - arch helper function for pcibios_set_mwi * @dev: the PCI device for which MWI is enabled * * Helper function for implementation the arch-specific pcibios_set_mwi * function. Originally copied from drivers/net/acenic.c. * Copyright 1998-2001 by Jes Sorensen, <jes@trained-monkey.org>. * * RETURNS: An appriopriate -ERRNO error value on eror, or zero for success. */intpdev_set_mwi(struct pci_dev *dev){ int rc = 0; u8 cache_size; /* * Looks like this is necessary to deal with on all architectures, * even this %$#%$# N440BX Intel based thing doesn't get it right. * Ie. having two NICs in the machine, one will have the cache * line set at boot time, the other will not. */ pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &cache_size); cache_size <<= 2; if (cache_size != SMP_CACHE_BYTES) { printk(KERN_WARNING "PCI: %s PCI cache line size set incorrectly " "(%i bytes) by BIOS/FW, ", dev->slot_name, cache_size); if (cache_size > SMP_CACHE_BYTES) { printk("expecting %i\n", SMP_CACHE_BYTES); rc = -EINVAL; } else { printk("correcting to %i\n", SMP_CACHE_BYTES); pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, SMP_CACHE_BYTES >> 2); } } return rc;}/** * pci_set_mwi - enables memory-write-invalidate PCI transaction * @dev: the PCI device for which MWI is enabled * * Enables the Memory-Write-Invalidate transaction in %PCI_COMMAND, * and then calls @pcibios_set_mwi to do the needed arch specific * operations or a generic mwi-prep function. * * RETURNS: An appriopriate -ERRNO error value on eror, or zero for success. */intpci_set_mwi(struct pci_dev *dev){ int rc; u16 cmd;#ifdef HAVE_ARCH_PCI_MWI rc = pcibios_set_mwi(dev);#else rc = pdev_set_mwi(dev);#endif if (rc) return rc; pci_read_config_word(dev, PCI_COMMAND, &cmd); if (! (cmd & PCI_COMMAND_INVALIDATE)) { DBG("PCI: Enabling Mem-Wr-Inval for device %s\n", dev->slot_name); cmd |= PCI_COMMAND_INVALIDATE; pci_write_config_word(dev, PCI_COMMAND, cmd); } return 0;}/** * pci_clear_mwi - disables Memory-Write-Invalidate for device dev * @dev: the PCI device to disable * * Disables PCI Memory-Write-Invalidate transaction on the device */voidpci_clear_mwi(struct pci_dev *dev){ u16 cmd; pci_read_config_word(dev, PCI_COMMAND, &cmd); if (cmd & PCI_COMMAND_INVALIDATE) { cmd &= ~PCI_COMMAND_INVALIDATE; pci_write_config_word(dev, PCI_COMMAND, cmd); }}intpci_set_dma_mask(struct pci_dev *dev, u64 mask){ if (!pci_dma_supported(dev, mask)) return -EIO; dev->dma_mask = mask; return 0;} intpci_dac_set_dma_mask(struct pci_dev *dev, u64 mask){ if (!pci_dac_dma_supported(dev, mask)) return -EIO; dev->dma_mask = mask; return 0;} /* * Translate the low bits of the PCI base * to the resource type */static inline unsigned int pci_calc_resource_flags(unsigned int flags){ if (flags & PCI_BASE_ADDRESS_SPACE_IO) return IORESOURCE_IO; if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH) return IORESOURCE_MEM | IORESOURCE_PREFETCH; return IORESOURCE_MEM;}/* * Find the extent of a PCI decode.. */static u32 pci_size(u32 base, unsigned long mask){ u32 size = mask & base; /* Find the significant bits */ size = size & ~(size-1); /* Get the lowest of them to find the decode size */ return size-1; /* extent = size - 1 */}static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom){ unsigned int pos, reg, next; u32 l, sz; struct resource *res; for(pos=0; pos<howmany; pos = next) { next = pos+1; res = &dev->resource[pos]; res->name = dev->name; reg = PCI_BASE_ADDRESS_0 + (pos << 2); pci_read_config_dword(dev, reg, &l); pci_write_config_dword(dev, reg, ~0); pci_read_config_dword(dev, reg, &sz); pci_write_config_dword(dev, reg, l); if (!sz || sz == 0xffffffff) continue; if (l == 0xffffffff) l = 0; if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) { res->start = l & PCI_BASE_ADDRESS_MEM_MASK; res->flags |= l & ~PCI_BASE_ADDRESS_MEM_MASK; sz = pci_size(sz, PCI_BASE_ADDRESS_MEM_MASK); } else { res->start = l & PCI_BASE_ADDRESS_IO_MASK; res->flags |= l & ~PCI_BASE_ADDRESS_IO_MASK; sz = pci_size(sz, PCI_BASE_ADDRESS_IO_MASK & 0xffff); } res->end = res->start + (unsigned long) sz; res->flags |= pci_calc_resource_flags(l); if ((l & (PCI_BASE_ADDRESS_SPACE | PCI_BASE_ADDRESS_MEM_TYPE_MASK)) == (PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64)) { pci_read_config_dword(dev, reg+4, &l); next++;#if BITS_PER_LONG == 64 res->start |= ((unsigned long) l) << 32; res->end = res->start + sz; pci_write_config_dword(dev, reg+4, ~0); pci_read_config_dword(dev, reg+4, &sz); pci_write_config_dword(dev, reg+4, l); if (~sz) res->end = res->start + 0xffffffff + (((unsigned long) ~sz) << 32);#else if (l) { printk(KERN_ERR "PCI: Unable to handle 64-bit address for device %s\n", dev->slot_name); res->start = 0; res->flags = 0; continue; }#endif } } if (rom) { dev->rom_base_reg = rom; res = &dev->resource[PCI_ROM_RESOURCE]; pci_read_config_dword(dev, rom, &l); pci_write_config_dword(dev, rom, ~PCI_ROM_ADDRESS_ENABLE);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -