1/* 2 * PCI Backend - Handle special overlays for broken devices. 3 * 4 * Author: Ryan Wilson <hap9@epoch.ncsc.mil> 5 * Author: Chris Bookholt <hap10@epoch.ncsc.mil> 6 */ 7 8#include <linux/kernel.h> 9#include <linux/pci.h> 10#include "pciback.h" 11#include "conf_space.h" 12#include "conf_space_quirks.h" 13 14LIST_HEAD(xen_pcibk_quirks); 15static inline const struct pci_device_id * 16match_one_device(const struct pci_device_id *id, const struct pci_dev *dev) 17{ 18 if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) && 19 (id->device == PCI_ANY_ID || id->device == dev->device) && 20 (id->subvendor == PCI_ANY_ID || 21 id->subvendor == dev->subsystem_vendor) && 22 (id->subdevice == PCI_ANY_ID || 23 id->subdevice == dev->subsystem_device) && 24 !((id->class ^ dev->class) & id->class_mask)) 25 return id; 26 return NULL; 27} 28 29static struct xen_pcibk_config_quirk *xen_pcibk_find_quirk(struct pci_dev *dev) 30{ 31 struct xen_pcibk_config_quirk *tmp_quirk; 32 33 list_for_each_entry(tmp_quirk, &xen_pcibk_quirks, quirks_list) 34 if (match_one_device(&tmp_quirk->devid, dev) != NULL) 35 goto out; 36 tmp_quirk = NULL; 37 printk(KERN_DEBUG DRV_NAME 38 ": quirk didn't match any device known\n"); 39out: 40 return tmp_quirk; 41} 42 43static inline void register_quirk(struct xen_pcibk_config_quirk *quirk) 44{ 45 list_add_tail(&quirk->quirks_list, &xen_pcibk_quirks); 46} 47 48int xen_pcibk_field_is_dup(struct pci_dev *dev, unsigned int reg) 49{ 50 int ret = 0; 51 struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(dev); 52 struct config_field_entry *cfg_entry; 53 54 list_for_each_entry(cfg_entry, &dev_data->config_fields, list) { 55 if (OFFSET(cfg_entry) == reg) { 56 ret = 1; 57 break; 58 } 59 } 60 return ret; 61} 62 63int xen_pcibk_config_quirks_add_field(struct pci_dev *dev, struct config_field 64 *field) 65{ 66 int err = 0; 67 68 switch (field->size) { 69 case 1: 70 field->u.b.read = xen_pcibk_read_config_byte; 71 field->u.b.write = xen_pcibk_write_config_byte; 72 break; 73 case 2: 74 field->u.w.read = xen_pcibk_read_config_word; 75 field->u.w.write = xen_pcibk_write_config_word; 76 break; 77 case 4: 78 field->u.dw.read = xen_pcibk_read_config_dword; 79 field->u.dw.write = xen_pcibk_write_config_dword; 80 break; 81 default: 82 err = -EINVAL; 83 goto out; 84 } 85 86 xen_pcibk_config_add_field(dev, field); 87 88out: 89 return err; 90} 91 92int xen_pcibk_config_quirks_init(struct pci_dev *dev) 93{ 94 struct xen_pcibk_config_quirk *quirk; 95 int ret = 0; 96 97 quirk = kzalloc(sizeof(*quirk), GFP_ATOMIC); 98 if (!quirk) { 99 ret = -ENOMEM; 100 goto out; 101 } 102 103 quirk->devid.vendor = dev->vendor; 104 quirk->devid.device = dev->device; 105 quirk->devid.subvendor = dev->subsystem_vendor; 106 quirk->devid.subdevice = dev->subsystem_device; 107 quirk->devid.class = 0; 108 quirk->devid.class_mask = 0; 109 quirk->devid.driver_data = 0UL; 110 111 quirk->pdev = dev; 112 113 register_quirk(quirk); 114out: 115 return ret; 116} 117 118void xen_pcibk_config_field_free(struct config_field *field) 119{ 120 kfree(field); 121} 122 123int xen_pcibk_config_quirk_release(struct pci_dev *dev) 124{ 125 struct xen_pcibk_config_quirk *quirk; 126 int ret = 0; 127 128 quirk = xen_pcibk_find_quirk(dev); 129 if (!quirk) { 130 ret = -ENXIO; 131 goto out; 132 } 133 134 list_del(&quirk->quirks_list); 135 kfree(quirk); 136 137out: 138 return ret; 139} 140