The method which extracts a string descriptor of one or more PCI devices and matches to a struct pci_dev is useful in general to other subsystems needing to match parameters or sysfs strings. Move this function to search.c for general use. Signed-off-by: Jonathan Derrick <jonathan.derrick@xxxxxxxxx> --- drivers/pci/pci.c | 163 ------------------------------------------- drivers/pci/search.c | 162 ++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 5 ++ 3 files changed, 167 insertions(+), 163 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 2127aba3550b..b58a8f5a7654 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -250,169 +250,6 @@ void __iomem *pci_ioremap_wc_bar(struct pci_dev *pdev, int bar) EXPORT_SYMBOL_GPL(pci_ioremap_wc_bar); #endif -/** - * pci_dev_str_match_path - test if a path string matches a device - * @dev: the PCI device to test - * @path: string to match the device against - * @endptr: pointer to the string after the match - * - * Test if a string (typically from a kernel parameter) formatted as a - * path of device/function addresses matches a PCI device. The string must - * be of the form: - * - * [<domain>:]<bus>:<device>.<func>[/<device>.<func>]* - * - * A path for a device can be obtained using 'lspci -t'. Using a path - * is more robust against bus renumbering than using only a single bus, - * device and function address. - * - * Returns 1 if the string matches the device, 0 if it does not and - * a negative error code if it fails to parse the string. - */ -static int pci_dev_str_match_path(struct pci_dev *dev, const char *path, - const char **endptr) -{ - int ret; - unsigned int seg, bus, slot, func; - char *wpath, *p; - char end; - - *endptr = strchrnul(path, ';'); - - wpath = kmemdup_nul(path, *endptr - path, GFP_ATOMIC); - if (!wpath) - return -ENOMEM; - - while (1) { - p = strrchr(wpath, '/'); - if (!p) - break; - ret = sscanf(p, "/%x.%x%c", &slot, &func, &end); - if (ret != 2) { - ret = -EINVAL; - goto free_and_exit; - } - - if (dev->devfn != PCI_DEVFN(slot, func)) { - ret = 0; - goto free_and_exit; - } - - /* - * Note: we don't need to get a reference to the upstream - * bridge because we hold a reference to the top level - * device which should hold a reference to the bridge, - * and so on. - */ - dev = pci_upstream_bridge(dev); - if (!dev) { - ret = 0; - goto free_and_exit; - } - - *p = 0; - } - - ret = sscanf(wpath, "%x:%x:%x.%x%c", &seg, &bus, &slot, - &func, &end); - if (ret != 4) { - seg = 0; - ret = sscanf(wpath, "%x:%x.%x%c", &bus, &slot, &func, &end); - if (ret != 3) { - ret = -EINVAL; - goto free_and_exit; - } - } - - ret = (seg == pci_domain_nr(dev->bus) && - bus == dev->bus->number && - dev->devfn == PCI_DEVFN(slot, func)); - -free_and_exit: - kfree(wpath); - return ret; -} - -/** - * pci_dev_str_match - test if a string matches a device - * @dev: the PCI device to test - * @p: string to match the device against - * @endptr: pointer to the string after the match - * - * Test if a string (typically from a kernel parameter) matches a specified - * PCI device. The string may be of one of the following formats: - * - * [<domain>:]<bus>:<device>.<func>[/<device>.<func>]* - * pci:<vendor>:<device>[:<subvendor>:<subdevice>] - * - * The first format specifies a PCI bus/device/function address which - * may change if new hardware is inserted, if motherboard firmware changes, - * or due to changes caused in kernel parameters. If the domain is - * left unspecified, it is taken to be 0. In order to be robust against - * bus renumbering issues, a path of PCI device/function numbers may be used - * to address the specific device. The path for a device can be determined - * through the use of 'lspci -t'. - * - * The second format matches devices using IDs in the configuration - * space which may match multiple devices in the system. A value of 0 - * for any field will match all devices. (Note: this differs from - * in-kernel code that uses PCI_ANY_ID which is ~0; this is for - * legacy reasons and convenience so users don't have to specify - * FFFFFFFFs on the command line.) - * - * Returns 1 if the string matches the device, 0 if it does not and - * a negative error code if the string cannot be parsed. - */ -static int pci_dev_str_match(struct pci_dev *dev, const char *p, - const char **endptr) -{ - int ret; - int count; - unsigned short vendor, device, subsystem_vendor, subsystem_device; - - if (strncmp(p, "pci:", 4) == 0) { - /* PCI vendor/device (subvendor/subdevice) IDs are specified */ - p += 4; - ret = sscanf(p, "%hx:%hx:%hx:%hx%n", &vendor, &device, - &subsystem_vendor, &subsystem_device, &count); - if (ret != 4) { - ret = sscanf(p, "%hx:%hx%n", &vendor, &device, &count); - if (ret != 2) - return -EINVAL; - - subsystem_vendor = 0; - subsystem_device = 0; - } - - p += count; - - if ((!vendor || vendor == dev->vendor) && - (!device || device == dev->device) && - (!subsystem_vendor || - subsystem_vendor == dev->subsystem_vendor) && - (!subsystem_device || - subsystem_device == dev->subsystem_device)) - goto found; - } else { - /* - * PCI Bus, Device, Function IDs are specified - * (optionally, may include a path of devfns following it) - */ - ret = pci_dev_str_match_path(dev, p, &p); - if (ret < 0) - return ret; - else if (ret) - goto found; - } - - *endptr = p; - return 0; - -found: - *endptr = p; - return 1; -} - static u8 __pci_find_next_cap_ttl(struct pci_bus *bus, unsigned int devfn, u8 pos, int cap, int *ttl) { diff --git a/drivers/pci/search.c b/drivers/pci/search.c index b4c138a6ec02..059fc5b9db4c 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -390,3 +390,165 @@ int pci_dev_present(const struct pci_device_id *ids) return 0; } EXPORT_SYMBOL(pci_dev_present); + +/** + * pci_dev_str_match_path - test if a path string matches a device + * @dev: the PCI device to test + * @path: string to match the device against + * @endptr: pointer to the string after the match + * + * Test if a string (typically from a kernel parameter) formatted as a + * path of device/function addresses matches a PCI device. The string must + * be of the form: + * + * [<domain>:]<bus>:<device>.<func>[/<device>.<func>]* + * + * A path for a device can be obtained using 'lspci -t'. Using a path + * is more robust against bus renumbering than using only a single bus, + * device and function address. + * + * Returns 1 if the string matches the device, 0 if it does not and + * a negative error code if it fails to parse the string. + */ +static int pci_dev_str_match_path(struct pci_dev *dev, const char *path, + const char **endptr) +{ + int ret; + unsigned int seg, bus, slot, func; + char *wpath, *p; + char end; + + *endptr = strchrnul(path, ';'); + + wpath = kmemdup_nul(path, *endptr - path, GFP_ATOMIC); + if (!wpath) + return -ENOMEM; + + while (1) { + p = strrchr(wpath, '/'); + if (!p) + break; + ret = sscanf(p, "/%x.%x%c", &slot, &func, &end); + if (ret != 2) { + ret = -EINVAL; + goto free_and_exit; + } + + if (dev->devfn != PCI_DEVFN(slot, func)) { + ret = 0; + goto free_and_exit; + } + + /* + * Note: we don't need to get a reference to the upstream + * bridge because we hold a reference to the top level + * device which should hold a reference to the bridge, + * and so on. + */ + dev = pci_upstream_bridge(dev); + if (!dev) { + ret = 0; + goto free_and_exit; + } + + *p = 0; + } + + ret = sscanf(wpath, "%x:%x:%x.%x%c", &seg, &bus, &slot, + &func, &end); + if (ret != 4) { + seg = 0; + ret = sscanf(wpath, "%x:%x.%x%c", &bus, &slot, &func, &end); + if (ret != 3) { + ret = -EINVAL; + goto free_and_exit; + } + } + + ret = (seg == pci_domain_nr(dev->bus) && + bus == dev->bus->number && + dev->devfn == PCI_DEVFN(slot, func)); + +free_and_exit: + kfree(wpath); + return ret; +} + +/** + * pci_dev_str_match - test if a string matches a device + * @dev: the PCI device to test + * @p: string to match the device against + * @endptr: pointer to the string after the match + * + * Test if a string (typically from a kernel parameter) matches a specified + * PCI device. The string may be of one of the following formats: + * + * [<domain>:]<bus>:<device>.<func>[/<device>.<func>]* + * pci:<vendor>:<device>[:<subvendor>:<subdevice>] + * + * The first format specifies a PCI bus/device/function address which + * may change if new hardware is inserted, if motherboard firmware changes, + * or due to changes caused in kernel parameters. If the domain is + * left unspecified, it is taken to be 0. In order to be robust against + * bus renumbering issues, a path of PCI device/function numbers may be used + * to address the specific device. The path for a device can be determined + * through the use of 'lspci -t'. + * + * The second format matches devices using IDs in the configuration + * space which may match multiple devices in the system. A value of 0 + * for any field will match all devices. (Note: this differs from + * in-kernel code that uses PCI_ANY_ID which is ~0; this is for + * legacy reasons and convenience so users don't have to specify + * FFFFFFFFs on the command line.) + * + * Returns 1 if the string matches the device, 0 if it does not and + * a negative error code if the string cannot be parsed. + */ +int pci_dev_str_match(struct pci_dev *dev, const char *p, const char **endptr) +{ + int ret; + int count; + unsigned short vendor, device, subsystem_vendor, subsystem_device; + + if (strncmp(p, "pci:", 4) == 0) { + /* PCI vendor/device (subvendor/subdevice) IDs are specified */ + p += 4; + ret = sscanf(p, "%hx:%hx:%hx:%hx%n", &vendor, &device, + &subsystem_vendor, &subsystem_device, &count); + if (ret != 4) { + ret = sscanf(p, "%hx:%hx%n", &vendor, &device, &count); + if (ret != 2) + return -EINVAL; + + subsystem_vendor = 0; + subsystem_device = 0; + } + + p += count; + + if ((!vendor || vendor == dev->vendor) && + (!device || device == dev->device) && + (!subsystem_vendor || + subsystem_vendor == dev->subsystem_vendor) && + (!subsystem_device || + subsystem_device == dev->subsystem_device)) + goto found; + } else { + /* + * PCI Bus, Device, Function IDs are specified + * (optionally, may include a path of devfns following it) + */ + ret = pci_dev_str_match_path(dev, p, &p); + if (ret < 0) + return ret; + else if (ret) + goto found; + } + + *endptr = p; + return 0; + +found: + *endptr = p; + return 1; +} diff --git a/include/linux/pci.h b/include/linux/pci.h index 09f704337955..0c907f94bb61 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1182,6 +1182,7 @@ struct pci_dev *pci_get_domain_bus_and_slot(int domain, unsigned int bus, unsigned int devfn); struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from); int pci_dev_present(const struct pci_device_id *ids); +int pci_dev_str_match(struct pci_dev *dev, const char *p, const char **endptr); int pci_bus_read_config_byte(struct pci_bus *bus, unsigned int devfn, int where, u8 *val); @@ -1816,6 +1817,10 @@ static inline struct pci_dev *pci_get_class(unsigned int class, static inline int pci_dev_present(const struct pci_device_id *ids) { return 0; } +static inline int pci_dev_str_match(struct pci_dev *dev, const char *p, + const char **endptr) +{ return 0; } + #define no_pci_devices() (1) #define pci_dev_put(dev) do { } while (0) -- 2.30.2