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