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/config.h> 23 #include <linux/acpi.h> 24 #include <linux/pnp.h> 25 #include <acpi/acpi_bus.h> 26 #include "pnpacpi.h" 27 28 static int num = 0; 29 30 static char __initdata excluded_id_list[] = 31 "PNP0C0A," /* Battery */ 32 "PNP0C0C,PNP0C0E,PNP0C0D," /* Button */ 33 "PNP0C09," /* EC */ 34 "PNP0C0B," /* Fan */ 35 "PNP0A03," /* PCI root */ 36 "PNP0C0F," /* Link device */ 37 "PNP0000," /* PIC */ 38 "PNP0100," /* Timer */ 39 ; 40 static inline int is_exclusive_device(struct acpi_device *dev) 41 { 42 return (!acpi_match_ids(dev, excluded_id_list)); 43 } 44 45 /* 46 * Compatible Device IDs 47 */ 48 #define TEST_HEX(c) \ 49 if (!(('0' <= (c) && (c) <= '9') || ('A' <= (c) && (c) <= 'F'))) \ 50 return 0 51 #define TEST_ALPHA(c) \ 52 if (!('@' <= (c) || (c) <= 'Z')) \ 53 return 0 54 static int __init ispnpidacpi(char *id) 55 { 56 TEST_ALPHA(id[0]); 57 TEST_ALPHA(id[1]); 58 TEST_ALPHA(id[2]); 59 TEST_HEX(id[3]); 60 TEST_HEX(id[4]); 61 TEST_HEX(id[5]); 62 TEST_HEX(id[6]); 63 if (id[7] != '\0') 64 return 0; 65 return 1; 66 } 67 68 static void __init pnpidacpi_to_pnpid(char *id, char *str) 69 { 70 str[0] = id[0]; 71 str[1] = id[1]; 72 str[2] = id[2]; 73 str[3] = tolower(id[3]); 74 str[4] = tolower(id[4]); 75 str[5] = tolower(id[5]); 76 str[6] = tolower(id[6]); 77 str[7] = '\0'; 78 } 79 80 static int pnpacpi_get_resources(struct pnp_dev * dev, struct pnp_resource_table * res) 81 { 82 acpi_status status; 83 status = pnpacpi_parse_allocated_resource((acpi_handle)dev->data, 84 &dev->res); 85 return ACPI_FAILURE(status) ? -ENODEV : 0; 86 } 87 88 static int pnpacpi_set_resources(struct pnp_dev * dev, struct pnp_resource_table * res) 89 { 90 acpi_handle handle = dev->data; 91 struct acpi_buffer buffer; 92 int ret = 0; 93 acpi_status status; 94 95 ret = pnpacpi_build_resource_template(handle, &buffer); 96 if (ret) 97 return ret; 98 ret = pnpacpi_encode_resources(res, &buffer); 99 if (ret) { 100 kfree(buffer.pointer); 101 return ret; 102 } 103 status = acpi_set_current_resources(handle, &buffer); 104 if (ACPI_FAILURE(status)) 105 ret = -EINVAL; 106 kfree(buffer.pointer); 107 return ret; 108 } 109 110 static int pnpacpi_disable_resources(struct pnp_dev *dev) 111 { 112 acpi_status status; 113 114 /* acpi_unregister_gsi(pnp_irq(dev, 0)); */ 115 status = acpi_evaluate_object((acpi_handle)dev->data, 116 "_DIS", NULL, NULL); 117 return ACPI_FAILURE(status) ? -ENODEV : 0; 118 } 119 120 static struct pnp_protocol pnpacpi_protocol = { 121 .name = "Plug and Play ACPI", 122 .get = pnpacpi_get_resources, 123 .set = pnpacpi_set_resources, 124 .disable = pnpacpi_disable_resources, 125 }; 126 127 static int __init pnpacpi_add_device(struct acpi_device *device) 128 { 129 acpi_handle temp = NULL; 130 acpi_status status; 131 struct pnp_id *dev_id; 132 struct pnp_dev *dev; 133 134 if (!ispnpidacpi(acpi_device_hid(device)) || 135 is_exclusive_device(device)) 136 return 0; 137 138 pnp_dbg("ACPI device : hid %s", acpi_device_hid(device)); 139 dev = kcalloc(1, sizeof(struct pnp_dev), GFP_KERNEL); 140 if (!dev) { 141 pnp_err("Out of memory"); 142 return -ENOMEM; 143 } 144 dev->data = device->handle; 145 /* .enabled means if the device can decode the resources */ 146 dev->active = device->status.enabled; 147 status = acpi_get_handle(device->handle, "_SRS", &temp); 148 if (ACPI_SUCCESS(status)) 149 dev->capabilities |= PNP_CONFIGURABLE; 150 dev->capabilities |= PNP_READ; 151 if (device->flags.dynamic_status) 152 dev->capabilities |= PNP_WRITE; 153 if (device->flags.removable) 154 dev->capabilities |= PNP_REMOVABLE; 155 status = acpi_get_handle(device->handle, "_DIS", &temp); 156 if (ACPI_SUCCESS(status)) 157 dev->capabilities |= PNP_DISABLE; 158 159 dev->protocol = &pnpacpi_protocol; 160 161 if (strlen(acpi_device_name(device))) 162 strncpy(dev->name, acpi_device_name(device), sizeof(dev->name)); 163 else 164 strncpy(dev->name, acpi_device_bid(device), sizeof(dev->name)); 165 166 dev->number = num; 167 168 /* set the initial values for the PnP device */ 169 dev_id = kcalloc(1, sizeof(struct pnp_id), GFP_KERNEL); 170 if (!dev_id) 171 goto err; 172 pnpidacpi_to_pnpid(acpi_device_hid(device), dev_id->id); 173 pnp_add_id(dev_id, dev); 174 175 if(dev->active) { 176 /* parse allocated resource */ 177 status = pnpacpi_parse_allocated_resource(device->handle, &dev->res); 178 if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { 179 pnp_err("PnPACPI: METHOD_NAME__CRS failure for %s", dev_id->id); 180 goto err1; 181 } 182 } 183 184 if(dev->capabilities & PNP_CONFIGURABLE) { 185 status = pnpacpi_parse_resource_option_data(device->handle, 186 dev); 187 if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { 188 pnp_err("PnPACPI: METHOD_NAME__PRS failure for %s", dev_id->id); 189 goto err1; 190 } 191 } 192 193 /* parse compatible ids */ 194 if (device->flags.compatible_ids) { 195 struct acpi_compatible_id_list *cid_list = device->pnp.cid_list; 196 int i; 197 198 for (i = 0; i < cid_list->count; i++) { 199 if (!ispnpidacpi(cid_list->id[i].value)) 200 continue; 201 dev_id = kcalloc(1, sizeof(struct pnp_id), GFP_KERNEL); 202 if (!dev_id) 203 continue; 204 205 pnpidacpi_to_pnpid(cid_list->id[i].value, dev_id->id); 206 pnp_add_id(dev_id, dev); 207 } 208 } 209 210 /* clear out the damaged flags */ 211 if (!dev->active) 212 pnp_init_resource_table(&dev->res); 213 pnp_add_device(dev); 214 num ++; 215 216 return AE_OK; 217 err1: 218 kfree(dev_id); 219 err: 220 kfree(dev); 221 return -EINVAL; 222 } 223 224 static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle, 225 u32 lvl, void *context, void **rv) 226 { 227 struct acpi_device *device; 228 229 if (!acpi_bus_get_device(handle, &device)) 230 pnpacpi_add_device(device); 231 else 232 return AE_CTRL_DEPTH; 233 return AE_OK; 234 } 235 236 int pnpacpi_disabled __initdata; 237 static int __init pnpacpi_init(void) 238 { 239 if (acpi_disabled || pnpacpi_disabled) { 240 pnp_info("PnP ACPI: disabled"); 241 return 0; 242 } 243 pnp_info("PnP ACPI init"); 244 pnp_register_protocol(&pnpacpi_protocol); 245 acpi_get_devices(NULL, pnpacpi_add_device_handler, NULL, NULL); 246 pnp_info("PnP ACPI: found %d devices", num); 247 return 0; 248 } 249 subsys_initcall(pnpacpi_init); 250 251 static int __init pnpacpi_setup(char *str) 252 { 253 if (str == NULL) 254 return 1; 255 if (!strncmp(str, "off", 3)) 256 pnpacpi_disabled = 1; 257 return 1; 258 } 259 __setup("pnpacpi=", pnpacpi_setup); 260 261 #if 0 262 EXPORT_SYMBOL(pnpacpi_protocol); 263 #endif 264