1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Simple stub driver to reserve a PCI device |
4 | * |
5 | * Copyright (C) 2008 Red Hat, Inc. |
6 | * Author: |
7 | * Chris Wright |
8 | * |
9 | * Usage is simple, allocate a new id to the stub driver and bind the |
10 | * device to it. For example: |
11 | * |
12 | * # echo "8086 10f5" > /sys/bus/pci/drivers/pci-stub/new_id |
13 | * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind |
14 | * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/pci-stub/bind |
15 | * # ls -l /sys/bus/pci/devices/0000:00:19.0/driver |
16 | * .../0000:00:19.0/driver -> ../../../bus/pci/drivers/pci-stub |
17 | */ |
18 | |
19 | #include <linux/module.h> |
20 | #include <linux/pci.h> |
21 | |
22 | static char ids[1024] __initdata; |
23 | |
24 | module_param_string(ids, ids, sizeof(ids), 0); |
25 | MODULE_PARM_DESC(ids, "Initial PCI IDs to add to the stub driver, format is " |
26 | "\"vendor:device[:subvendor[:subdevice[:class[:class_mask]]]]\"" |
27 | " and multiple comma separated entries can be specified" ); |
28 | |
29 | static int pci_stub_probe(struct pci_dev *dev, const struct pci_device_id *id) |
30 | { |
31 | pci_info(dev, "claimed by stub\n" ); |
32 | return 0; |
33 | } |
34 | |
35 | static struct pci_driver stub_driver = { |
36 | .name = "pci-stub" , |
37 | .id_table = NULL, /* only dynamic id's */ |
38 | .probe = pci_stub_probe, |
39 | .driver_managed_dma = true, |
40 | }; |
41 | |
42 | static int __init pci_stub_init(void) |
43 | { |
44 | char *p, *id; |
45 | int rc; |
46 | |
47 | rc = pci_register_driver(&stub_driver); |
48 | if (rc) |
49 | return rc; |
50 | |
51 | /* no ids passed actually */ |
52 | if (ids[0] == '\0') |
53 | return 0; |
54 | |
55 | /* add ids specified in the module parameter */ |
56 | p = ids; |
57 | while ((id = strsep(&p, "," ))) { |
58 | unsigned int vendor, device, subvendor = PCI_ANY_ID, |
59 | subdevice = PCI_ANY_ID, class = 0, class_mask = 0; |
60 | int fields; |
61 | |
62 | if (!strlen(id)) |
63 | continue; |
64 | |
65 | fields = sscanf(id, "%x:%x:%x:%x:%x:%x" , |
66 | &vendor, &device, &subvendor, &subdevice, |
67 | &class, &class_mask); |
68 | |
69 | if (fields < 2) { |
70 | pr_warn("pci-stub: invalid ID string \"%s\"\n" , id); |
71 | continue; |
72 | } |
73 | |
74 | pr_info("pci-stub: add %04X:%04X sub=%04X:%04X cls=%08X/%08X\n" , |
75 | vendor, device, subvendor, subdevice, class, class_mask); |
76 | |
77 | rc = pci_add_dynid(drv: &stub_driver, vendor, device, |
78 | subvendor, subdevice, class, class_mask, driver_data: 0); |
79 | if (rc) |
80 | pr_warn("pci-stub: failed to add dynamic ID (%d)\n" , |
81 | rc); |
82 | } |
83 | |
84 | return 0; |
85 | } |
86 | |
87 | static void __exit pci_stub_exit(void) |
88 | { |
89 | pci_unregister_driver(dev: &stub_driver); |
90 | } |
91 | |
92 | module_init(pci_stub_init); |
93 | module_exit(pci_stub_exit); |
94 | |
95 | MODULE_LICENSE("GPL" ); |
96 | MODULE_AUTHOR("Chris Wright <chrisw@sous-sol.org>" ); |
97 | |