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