1/* 2 * driver.c - device id matching, driver model, etc. 3 * 4 * Copyright 2002 Adam Belay <ambx1@neo.rr.com> 5 * 6 */ 7 8#include <linux/string.h> 9#include <linux/list.h> 10#include <linux/module.h> 11#include <linux/ctype.h> 12#include <linux/slab.h> 13#include <linux/pnp.h> 14#include "base.h" 15 16static int compare_func(const char *ida, const char *idb) 17{ 18 int i; 19 /* we only need to compare the last 4 chars */ 20 for (i=3; i<7; i++) 21 { 22 if (ida[i] != 'X' && 23 idb[i] != 'X' && 24 toupper(ida[i]) != toupper(idb[i])) 25 return 0; 26 } 27 return 1; 28} 29 30int compare_pnp_id(struct pnp_id *pos, const char *id) 31{ 32 if (!pos || !id || (strlen(id) != 7)) 33 return 0; 34 if (memcmp(id,"ANYDEVS",7)==0) 35 return 1; 36 while (pos){ 37 if (memcmp(pos->id,id,3)==0) 38 if (compare_func(pos->id,id)==1) 39 return 1; 40 pos = pos->next; 41 } 42 return 0; 43} 44 45static const struct pnp_device_id * match_device(struct pnp_driver *drv, struct pnp_dev *dev) 46{ 47 const struct pnp_device_id *drv_id = drv->id_table; 48 if (!drv_id) 49 return NULL; 50 51 while (*drv_id->id) { 52 if (compare_pnp_id(dev->id, drv_id->id)) 53 return drv_id; 54 drv_id++; 55 } 56 return NULL; 57} 58 59int pnp_device_attach(struct pnp_dev *pnp_dev) 60{ 61 spin_lock(&pnp_lock); 62 if(pnp_dev->status != PNP_READY){ 63 spin_unlock(&pnp_lock); 64 return -EBUSY; 65 } 66 pnp_dev->status = PNP_ATTACHED; 67 spin_unlock(&pnp_lock); 68 return 0; 69} 70 71void pnp_device_detach(struct pnp_dev *pnp_dev) 72{ 73 spin_lock(&pnp_lock); 74 if (pnp_dev->status == PNP_ATTACHED) 75 pnp_dev->status = PNP_READY; 76 spin_unlock(&pnp_lock); 77 pnp_disable_dev(pnp_dev); 78} 79 80static int pnp_device_probe(struct device *dev) 81{ 82 int error; 83 struct pnp_driver *pnp_drv; 84 struct pnp_dev *pnp_dev; 85 const struct pnp_device_id *dev_id = NULL; 86 pnp_dev = to_pnp_dev(dev); 87 pnp_drv = to_pnp_driver(dev->driver); 88 89 pnp_dbg("match found with the PnP device '%s' and the driver '%s'", dev->bus_id,pnp_drv->name); 90 91 error = pnp_device_attach(pnp_dev); 92 if (error < 0) 93 return error; 94 95 if (pnp_dev->active == 0) { 96 if (!(pnp_drv->flags & PNP_DRIVER_RES_DO_NOT_CHANGE)) { 97 error = pnp_activate_dev(pnp_dev); 98 if (error < 0) 99 return error; 100 } 101 } else if ((pnp_drv->flags & PNP_DRIVER_RES_DISABLE) 102 == PNP_DRIVER_RES_DISABLE) { 103 error = pnp_disable_dev(pnp_dev); 104 if (error < 0) 105 return error; 106 } 107 error = 0; 108 if (pnp_drv->probe) { 109 dev_id = match_device(pnp_drv, pnp_dev); 110 if (dev_id != NULL) 111 error = pnp_drv->probe(pnp_dev, dev_id); 112 } 113 if (error >= 0){ 114 pnp_dev->driver = pnp_drv; 115 error = 0; 116 } else 117 goto fail; 118 return error; 119 120fail: 121 pnp_device_detach(pnp_dev); 122 return error; 123} 124 125static int pnp_device_remove(struct device *dev) 126{ 127 struct pnp_dev * pnp_dev = to_pnp_dev(dev); 128 struct pnp_driver * drv = pnp_dev->driver; 129 130 if (drv) { 131 if (drv->remove) 132 drv->remove(pnp_dev); 133 pnp_dev->driver = NULL; 134 } 135 pnp_device_detach(pnp_dev); 136 return 0; 137} 138 139static int pnp_bus_match(struct device *dev, struct device_driver *drv) 140{ 141 struct pnp_dev * pnp_dev = to_pnp_dev(dev); 142 struct pnp_driver * pnp_drv = to_pnp_driver(drv); 143 if (match_device(pnp_drv, pnp_dev) == NULL) 144 return 0; 145 return 1; 146} 147 148static int pnp_bus_suspend(struct device *dev, pm_message_t state) 149{ 150 struct pnp_dev * pnp_dev = to_pnp_dev(dev); 151 struct pnp_driver * pnp_drv = pnp_dev->driver; 152 int error; 153 154 if (!pnp_drv) 155 return 0; 156 157 if (pnp_drv->suspend) { 158 error = pnp_drv->suspend(pnp_dev, state); 159 if (error) 160 return error; 161 } 162 163 if (!(pnp_drv->flags & PNP_DRIVER_RES_DO_NOT_CHANGE) && 164 pnp_can_disable(pnp_dev)) { 165 error = pnp_stop_dev(pnp_dev); 166 if (error) 167 return error; 168 } 169 170 return 0; 171} 172 173static int pnp_bus_resume(struct device *dev) 174{ 175 struct pnp_dev * pnp_dev = to_pnp_dev(dev); 176 struct pnp_driver * pnp_drv = pnp_dev->driver; 177 int error; 178 179 if (!pnp_drv) 180 return 0; 181 182 if (!(pnp_drv->flags & PNP_DRIVER_RES_DO_NOT_CHANGE)) { 183 error = pnp_start_dev(pnp_dev); 184 if (error) 185 return error; 186 } 187 188 if (pnp_drv->resume) 189 return pnp_drv->resume(pnp_dev); 190 191 return 0; 192} 193 194struct bus_type pnp_bus_type = { 195 .name = "pnp", 196 .match = pnp_bus_match, 197 .probe = pnp_device_probe, 198 .remove = pnp_device_remove, 199 .suspend = pnp_bus_suspend, 200 .resume = pnp_bus_resume, 201}; 202 203int pnp_register_driver(struct pnp_driver *drv) 204{ 205 pnp_dbg("the driver '%s' has been registered", drv->name); 206 207 drv->driver.name = drv->name; 208 drv->driver.bus = &pnp_bus_type; 209 210 return driver_register(&drv->driver); 211} 212 213void pnp_unregister_driver(struct pnp_driver *drv) 214{ 215 driver_unregister(&drv->driver); 216 pnp_dbg("the driver '%s' has been unregistered", drv->name); 217} 218 219/** 220 * pnp_add_id - adds an EISA id to the specified device 221 * @id: pointer to a pnp_id structure 222 * @dev: pointer to the desired device 223 * 224 */ 225 226int pnp_add_id(struct pnp_id *id, struct pnp_dev *dev) 227{ 228 struct pnp_id *ptr; 229 if (!id) 230 return -EINVAL; 231 if (!dev) 232 return -EINVAL; 233 id->next = NULL; 234 ptr = dev->id; 235 while (ptr && ptr->next) 236 ptr = ptr->next; 237 if (ptr) 238 ptr->next = id; 239 else 240 dev->id = id; 241 return 0; 242} 243 244EXPORT_SYMBOL(pnp_register_driver); 245EXPORT_SYMBOL(pnp_unregister_driver); 246EXPORT_SYMBOL(pnp_device_attach); 247EXPORT_SYMBOL(pnp_device_detach); 248