1 /* 2 * pnpacpi -- PnP ACPI driver 3 * 4 * Copyright (c) 2004 Matthieu Castet <castet.matthieu@free.fr> 5 * Copyright (c) 2004 Li Shaohua <shaohua.li@intel.com> 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the 9 * Free Software Foundation; either version 2, or (at your option) any 10 * later version. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 22 #include <linux/acpi.h> 23 #include <linux/pnp.h> 24 #include <acpi/acpi_bus.h> 25 #include "pnpacpi.h" 26 27 static int num = 0; 28 29 static char __initdata excluded_id_list[] = 30 "PNP0C0A," /* Battery */ 31 "PNP0C0C,PNP0C0E,PNP0C0D," /* Button */ 32 "PNP0C09," /* EC */ 33 "PNP0C0B," /* Fan */ 34 "PNP0A03," /* PCI root */ 35 "PNP0C0F," /* Link device */ 36 "PNP0000," /* PIC */ 37 "PNP0100," /* Timer */ 38 ; 39 static inline int is_exclusive_device(struct acpi_device *dev) 40 { 41 return (!acpi_match_ids(dev, excluded_id_list)); 42 } 43 44 void *pnpacpi_kmalloc(size_t size, int f) 45 { 46 void *p = kmalloc(size, f); 47 if (p) 48 memset(p, 0, size); 49 return p; 50 } 51 52 /* 53 * Compatible Device IDs 54 */ 55 #define TEST_HEX(c) \ 56 if (!(('0' <= (c) && (c) <= '9') || ('A' <= (c) && (c) <= 'F'))) \ 57 return 0 58 #define TEST_ALPHA(c) \ 59 if (!('@' <= (c) || (c) <= 'Z')) \ 60 return 0 61 static int __init ispnpidacpi(char *id) 62 { 63 TEST_ALPHA(id[0]); 64 TEST_ALPHA(id[1]); 65 TEST_ALPHA(id[2]); 66 TEST_HEX(id[3]); 67 TEST_HEX(id[4]); 68 TEST_HEX(id[5]); 69 TEST_HEX(id[6]); 70 if (id[7] != '\0') 71 return 0; 72 return 1; 73 } 74 75 static void __init pnpidacpi_to_pnpid(char *id, char *str) 76 { 77 str[0] = id[0]; 78 str[1] = id[1]; 79 str[2] = id[2]; 80 str[3] = tolower(id[3]); 81 str[4] = tolower(id[4]); 82 str[5] = tolower(id[5]); 83 str[6] = tolower(id[6]); 84 str[7] = '\0'; 85 } 86 87 static int pnpacpi_get_resources(struct pnp_dev * dev, struct pnp_resource_table * res) 88 { 89 acpi_status status; 90 status = pnpacpi_parse_allocated_resource((acpi_handle)dev->data, 91 &dev->res); 92 return ACPI_FAILURE(status) ? -ENODEV : 0; 93 } 94 95 static int pnpacpi_set_resources(struct pnp_dev * dev, struct pnp_resource_table * res) 96 { 97 acpi_handle handle = dev->data; 98 struct acpi_buffer buffer; 99 int ret = 0; 100 acpi_status status; 101 102 ret = pnpacpi_build_resource_template(handle, &buffer); 103 if (ret) 104 return ret; 105 ret = pnpacpi_encode_resources(res, &buffer); 106 if (ret) { 107 kfree(buffer.pointer); 108 return ret; 109 } 110 status = acpi_set_current_resources(handle, &buffer); 111 if (ACPI_FAILURE(status)) 112 ret = -EINVAL; 113 kfree(buffer.pointer); 114 return ret; 115 } 116 117 static int pnpacpi_disable_resources(struct pnp_dev *dev) 118 { 119 acpi_status status; 120 121 /* acpi_unregister_gsi(pnp_irq(dev, 0)); */ 122 status = acpi_evaluate_object((acpi_handle)dev->data, 123 "_DIS", NULL, NULL); 124 return ACPI_FAILURE(status) ? -ENODEV : 0; 125 } 126 127 struct pnp_protocol pnpacpi_protocol = { 128 .name = "Plug and Play ACPI", 129 .get = pnpacpi_get_resources, 130 .set = pnpacpi_set_resources, 131 .disable = pnpacpi_disable_resources, 132 }; 133 134 static int __init pnpacpi_add_device(struct acpi_device *device) 135 { 136 acpi_handle temp = NULL; 137 acpi_status status; 138 struct pnp_id *dev_id; 139 struct pnp_dev *dev; 140 141 if (!ispnpidacpi(acpi_device_hid(device)) || 142 is_exclusive_device(device)) 143 return 0; 144 145 pnp_dbg("ACPI device : hid %s", acpi_device_hid(device)); 146 dev = pnpacpi_kmalloc(sizeof(struct pnp_dev), GFP_KERNEL); 147 if (!dev) { 148 pnp_err("Out of memory"); 149 return -ENOMEM; 150 } 151 dev->data = device->handle; 152 /* .enabled means if the device can decode the resources */ 153 dev->active = device->status.enabled; 154 status = acpi_get_handle(device->handle, "_SRS", &temp); 155 if (ACPI_SUCCESS(status)) 156 dev->capabilities |= PNP_CONFIGURABLE; 157 dev->capabilities |= PNP_READ; 158 if (device->flags.dynamic_status) 159 dev->capabilities |= PNP_WRITE; 160 if (device->flags.removable) 161 dev->capabilities |= PNP_REMOVABLE; 162 status = acpi_get_handle(device->handle, "_DIS", &temp); 163 if (ACPI_SUCCESS(status)) 164 dev->capabilities |= PNP_DISABLE; 165 166 dev->protocol = &pnpacpi_protocol; 167 168 if (strlen(acpi_device_name(device))) 169 strncpy(dev->name, acpi_device_name(device), sizeof(dev->name)); 170 else 171 strncpy(dev->name, acpi_device_bid(device), sizeof(dev->name)); 172 173 dev->number = num; 174 175 /* set the initial values for the PnP device */ 176 dev_id = pnpacpi_kmalloc(sizeof(struct pnp_id), GFP_KERNEL); 177 if (!dev_id) 178 goto err; 179 pnpidacpi_to_pnpid(acpi_device_hid(device), dev_id->id); 180 pnp_add_id(dev_id, dev); 181 182 if(dev->active) { 183 /* parse allocated resource */ 184 status = pnpacpi_parse_allocated_resource(device->handle, &dev->res); 185 if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { 186 pnp_err("PnPACPI: METHOD_NAME__CRS failure for %s", dev_id->id); 187 goto err1; 188 } 189 } 190 191 if(dev->capabilities & PNP_CONFIGURABLE) { 192 status = pnpacpi_parse_resource_option_data(device->handle, 193 dev); 194 if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { 195 pnp_err("PnPACPI: METHOD_NAME__PRS failure for %s", dev_id->id); 196 goto err1; 197 } 198 } 199 200 /* parse compatible ids */ 201 if (device->flags.compatible_ids) { 202 struct acpi_compatible_id_list *cid_list = device->pnp.cid_list; 203 int i; 204 205 for (i = 0; i < cid_list->count; i++) { 206 if (!ispnpidacpi(cid_list->id[i].value)) 207 continue; 208 dev_id = pnpacpi_kmalloc(sizeof(struct pnp_id), 209 GFP_KERNEL); 210 if (!dev_id) 211 continue; 212 213 pnpidacpi_to_pnpid(cid_list->id[i].value, dev_id->id); 214 pnp_add_id(dev_id, dev); 215 } 216 } 217 218 /* clear out the damaged flags */ 219 if (!dev->active) 220 pnp_init_resource_table(&dev->res); 221 pnp_add_device(dev); 222 num ++; 223 224 return AE_OK; 225 err1: 226 kfree(dev_id); 227 err: 228 kfree(dev); 229 return -EINVAL; 230 } 231 232 static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle, 233 u32 lvl, void *context, void **rv) 234 { 235 struct acpi_device *device; 236 237 if (!acpi_bus_get_device(handle, &device)) 238 pnpacpi_add_device(device); 239 else 240 return AE_CTRL_DEPTH; 241 return AE_OK; 242 } 243 244 int pnpacpi_disabled __initdata; 245 int __init pnpacpi_init(void) 246 { 247 if (acpi_disabled || pnpacpi_disabled) { 248 pnp_info("PnP ACPI: disabled"); 249 return 0; 250 } 251 pnp_info("PnP ACPI init"); 252 pnp_register_protocol(&pnpacpi_protocol); 253 acpi_get_devices(NULL, pnpacpi_add_device_handler, NULL, NULL); 254 pnp_info("PnP ACPI: found %d devices", num); 255 return 0; 256 } 257 subsys_initcall(pnpacpi_init); 258 259 static int __init pnpacpi_setup(char *str) 260 { 261 if (str == NULL) 262 return 1; 263 if (!strncmp(str, "off", 3)) 264 pnpacpi_disabled = 1; 265 return 1; 266 } 267 __setup("pnpacpi=", pnpacpi_setup); 268 269 EXPORT_SYMBOL(pnpacpi_protocol); 270