int pnpacpi_build_resource_template(acpi_handle handle, struct acpi_buffer *buffer) { struct acpi_resource *resource; int res_cnt = 0; acpi_status status; status = acpi_walk_resources(handle, METHOD_NAME__CRS, pnpacpi_count_resources, &res_cnt); if (ACPI_FAILURE(status)) { pnp_err("Evaluate _CRS failed"); return -EINVAL; } if (!res_cnt) return -EINVAL; buffer->length = sizeof(struct acpi_resource) * (res_cnt + 1) + 1; buffer->pointer = pnpacpi_kmalloc(buffer->length - 1, GFP_KERNEL); if (!buffer->pointer) return -ENOMEM; pnp_dbg("Res cnt %d", res_cnt); resource = (struct acpi_resource *)buffer->pointer; status = acpi_walk_resources(handle, METHOD_NAME__CRS, pnpacpi_type_resources, &resource); if (ACPI_FAILURE(status)) { kfree(buffer->pointer); pnp_err("Evaluate _CRS failed"); return -EINVAL; } /* resource will pointer the end resource now */ resource->id = ACPI_RSTYPE_END_TAG; return 0; }
static void pnpacpi_parse_dma_option(struct pnp_option *option, struct acpi_resource_dma *p) { int i; struct pnp_dma * dma; if (p->number_of_channels == 0) return; dma = pnpacpi_kmalloc(sizeof(struct pnp_dma), GFP_KERNEL); if (!dma) return; for(i = 0; i < p->number_of_channels; i++) dma->map |= 1 << p->channels[i]; dma->flags = 0; if (p->bus_master) dma->flags |= IORESOURCE_DMA_MASTER; switch (p->type) { case ACPI_COMPATIBILITY: dma->flags |= IORESOURCE_DMA_COMPATIBLE; break; case ACPI_TYPE_A: dma->flags |= IORESOURCE_DMA_TYPEA; break; case ACPI_TYPE_B: dma->flags |= IORESOURCE_DMA_TYPEB; break; case ACPI_TYPE_F: dma->flags |= IORESOURCE_DMA_TYPEF; break; default: /* Set a default value ? */ dma->flags |= IORESOURCE_DMA_COMPATIBLE; pnp_err("Invalid DMA type"); } switch (p->transfer) { case ACPI_TRANSFER_8: dma->flags |= IORESOURCE_DMA_8BIT; break; case ACPI_TRANSFER_8_16: dma->flags |= IORESOURCE_DMA_8AND16BIT; break; case ACPI_TRANSFER_16: dma->flags |= IORESOURCE_DMA_16BIT; break; default: /* Set a default value ? */ dma->flags |= IORESOURCE_DMA_8AND16BIT; pnp_err("Invalid DMA transfer type"); } pnp_register_dma_resource(option,dma); return; }
/** * pnp_auto_config_dev - automatically assigns resources to a device * @dev: pointer to the desired device * */ int pnp_auto_config_dev(struct pnp_dev *dev) { struct pnp_option *dep; int i = 1; if(!dev) return -EINVAL; if(!pnp_can_configure(dev)) { pnp_info("Device %s does not support resource configuration.", dev->dev.bus_id); return -ENODEV; } if (!dev->dependent) { if (pnp_assign_resources(dev, 0)) return 0; } else { dep = dev->dependent; do { if (pnp_assign_resources(dev, i)) return 0; dep = dep->next; i++; } while (dep); } pnp_err("Unable to assign resources to device %s.", dev->dev.bus_id); return -EBUSY; }
/** * pnp_disable_dev - disables device * @dev: pointer to the desired device * * inform the correct pnp protocol so that resources can be used by other devices */ int pnp_disable_dev(struct pnp_dev *dev) { if (!dev) return -EINVAL; if (!dev->active) { return 0; /* the device is already disabled */ } if (!pnp_can_disable(dev)) { pnp_info("Device %s does not supported disabling.", dev->dev.bus_id); return -EINVAL; } if (dev->protocol->disable(dev)<0) { pnp_err("Failed to disable device %s.", dev->dev.bus_id); return -EIO; } dev->active = 0; pnp_info("Device %s disabled.", dev->dev.bus_id); /* release the resources so that other devices can use them */ down(&pnp_res_mutex); pnp_clean_resource_table(&dev->res); up(&pnp_res_mutex); return 1; }
/** * pnp_activate_dev - activates a PnP device for use * @dev: pointer to the desired device * * does not validate or set resources so be careful. */ int pnp_activate_dev(struct pnp_dev *dev) { if (!dev) return -EINVAL; if (dev->active) { return 0; /* the device is already active */ } /* ensure resources are allocated */ if (pnp_auto_config_dev(dev)) return -EBUSY; if (!pnp_can_write(dev)) { pnp_info("Device %s does not supported activation.", dev->dev.bus_id); return -EINVAL; } if (dev->protocol->set(dev, &dev->res)<0) { pnp_err("Failed to activate device %s.", dev->dev.bus_id); return -EIO; } dev->active = 1; pnp_info("Device %s activated.", dev->dev.bus_id); return 1; }
static int pnp_assign_mem(struct pnp_dev *dev, struct pnp_mem *rule, int idx) { unsigned long *start, *end, *flags; if (!dev || !rule) return -EINVAL; if (idx >= PNP_MAX_MEM) { pnp_err("More than 8 mems is incompatible with pnp specifications."); /* pretend we were successful so at least the manager won't try again */ return 1; } /* check if this resource has been manually set, if so skip */ if (!(dev->res.mem_resource[idx].flags & IORESOURCE_AUTO)) return 1; start = &dev->res.mem_resource[idx].start; end = &dev->res.mem_resource[idx].end; flags = &dev->res.mem_resource[idx].flags; /* set the initial values */ *flags |= rule->flags | IORESOURCE_MEM; *flags &= ~IORESOURCE_UNSET; /* convert pnp flags to standard Linux flags */ if (!(rule->flags & IORESOURCE_MEM_WRITEABLE)) *flags |= IORESOURCE_READONLY; if (rule->flags & IORESOURCE_MEM_CACHEABLE) *flags |= IORESOURCE_CACHEABLE; if (rule->flags & IORESOURCE_MEM_RANGELENGTH) *flags |= IORESOURCE_RANGELENGTH; if (rule->flags & IORESOURCE_MEM_SHADOWABLE) *flags |= IORESOURCE_SHADOWABLE; if (!rule->size) { *flags |= IORESOURCE_DISABLED; return 1; /* skip disabled resource requests */ } *start = rule->min; *end = *start + rule->size -1; /* run through until pnp_check_mem is happy */ while (!pnp_check_mem(dev, idx)) { *start += rule->align; *end = *start + rule->size - 1; if (*start > rule->max || !rule->align) return 0; } return 1; }
static int pnp_assign_irq(struct pnp_dev * dev, struct pnp_irq *rule, int idx) { unsigned long *start, *end, *flags; int i; /* IRQ priority: this table is good for i386 */ static unsigned short xtab[16] = { 5, 10, 11, 12, 9, 14, 15, 7, 3, 4, 13, 0, 1, 6, 8, 2 }; if (!dev || !rule) return -EINVAL; if (idx >= PNP_MAX_IRQ) { pnp_err("More than 2 irqs is incompatible with pnp specifications."); /* pretend we were successful so at least the manager won't try again */ return 1; } /* check if this resource has been manually set, if so skip */ if (!(dev->res.irq_resource[idx].flags & IORESOURCE_AUTO)) return 1; start = &dev->res.irq_resource[idx].start; end = &dev->res.irq_resource[idx].end; flags = &dev->res.irq_resource[idx].flags; /* set the initial values */ *flags |= rule->flags | IORESOURCE_IRQ; *flags &= ~IORESOURCE_UNSET; if (bitmap_empty(rule->map, PNP_IRQ_NR)) { *flags |= IORESOURCE_DISABLED; return 1; /* skip disabled resource requests */ } /* TBD: need check for >16 IRQ */ *start = find_next_bit(rule->map, PNP_IRQ_NR, 16); if (*start < PNP_IRQ_NR) { *end = *start; return 1; } for (i = 0; i < 16; i++) { if(test_bit(xtab[i], rule->map)) { *start = *end = xtab[i]; if(pnp_check_irq(dev, idx)) return 1; } } return 0; }
struct pnp_option * pnp_register_independent_option(struct pnp_dev *dev) { struct pnp_option *option; if (!dev) return NULL; option = pnp_build_option(PNP_RES_PRIORITY_PREFERRED); /* this should never happen but if it does we'll try to continue */ if (dev->independent) pnp_err("independent resource already registered"); dev->independent = option; return option; }
int pnp_stop_dev(struct pnp_dev *dev) { if (!pnp_can_disable(dev)) { pnp_info("Device %s does not support disabling.", dev->dev.bus_id); return -EINVAL; } if (dev->protocol->disable(dev)<0) { pnp_err("Failed to disable device %s.", dev->dev.bus_id); return -EIO; } pnp_info("Device %s disabled.", dev->dev.bus_id); return 0; }
int pnp_start_dev(struct pnp_dev *dev) { if (!pnp_can_write(dev)) { pnp_info("Device %s does not support activation.", dev->dev.bus_id); return -EINVAL; } if (dev->protocol->set(dev, &dev->res)<0) { pnp_err("Failed to activate device %s.", dev->dev.bus_id); return -EIO; } pnp_info("Device %s activated.", dev->dev.bus_id); return 0; }
static int pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx) { unsigned long *start, *end, *flags; int i; /* DMA priority: this table is good for i386 */ static unsigned short xtab[8] = { 1, 3, 5, 6, 7, 0, 2, 4 }; if (!dev || !rule) return -EINVAL; if (idx >= PNP_MAX_DMA) { pnp_err("More than 2 dmas is incompatible with pnp specifications."); /* pretend we were successful so at least the manager won't try again */ return 1; } /* check if this resource has been manually set, if so skip */ if (!(dev->res.dma_resource[idx].flags & IORESOURCE_AUTO)) return 1; start = &dev->res.dma_resource[idx].start; end = &dev->res.dma_resource[idx].end; flags = &dev->res.dma_resource[idx].flags; /* set the initial values */ *flags |= rule->flags | IORESOURCE_DMA; *flags &= ~IORESOURCE_UNSET; if (!rule->map) { *flags |= IORESOURCE_DISABLED; return 1; /* skip disabled resource requests */ } for (i = 0; i < 8; i++) { if(rule->map & (1<<xtab[i])) { *start = *end = xtab[i]; if(pnp_check_dma(dev, idx)) return 1; } } return 0; }
static int pnp_assign_port(struct pnp_dev *dev, struct pnp_port *rule, int idx) { unsigned long *start, *end, *flags; if (!dev || !rule) return -EINVAL; if (idx >= PNP_MAX_PORT) { pnp_err("More than 4 ports is incompatible with pnp specifications."); /* pretend we were successful so at least the manager won't try again */ return 1; } /* check if this resource has been manually set, if so skip */ if (!(dev->res.port_resource[idx].flags & IORESOURCE_AUTO)) return 1; start = &dev->res.port_resource[idx].start; end = &dev->res.port_resource[idx].end; flags = &dev->res.port_resource[idx].flags; /* set the initial values */ *flags |= rule->flags | IORESOURCE_IO; *flags &= ~IORESOURCE_UNSET; if (!rule->size) { *flags |= IORESOURCE_DISABLED; return 1; /* skip disabled resource requests */ } *start = rule->min; *end = *start + rule->size - 1; /* run through until pnp_check_port is happy */ while (!pnp_check_port(dev, idx)) { *start += rule->align; *end = *start + rule->size - 1; if (*start > rule->max || !rule->align) return 0; } return 1; }
static int __init pnpacpi_add_device(struct acpi_device *device) { acpi_handle temp = NULL; acpi_status status; struct pnp_id *dev_id; struct pnp_dev *dev; if (!ispnpidacpi(acpi_device_hid(device)) || is_exclusive_device(device)) return 0; pnp_dbg("ACPI device : hid %s", acpi_device_hid(device)); dev = kcalloc(1, sizeof(struct pnp_dev), GFP_KERNEL); if (!dev) { pnp_err("Out of memory"); return -ENOMEM; } dev->data = device->handle; /* .enabled means if the device can decode the resources */ dev->active = device->status.enabled; status = acpi_get_handle(device->handle, "_SRS", &temp); if (ACPI_SUCCESS(status)) dev->capabilities |= PNP_CONFIGURABLE; dev->capabilities |= PNP_READ; if (device->flags.dynamic_status) dev->capabilities |= PNP_WRITE; if (device->flags.removable) dev->capabilities |= PNP_REMOVABLE; status = acpi_get_handle(device->handle, "_DIS", &temp); if (ACPI_SUCCESS(status)) dev->capabilities |= PNP_DISABLE; dev->protocol = &pnpacpi_protocol; if (strlen(acpi_device_name(device))) strncpy(dev->name, acpi_device_name(device), sizeof(dev->name)); else strncpy(dev->name, acpi_device_bid(device), sizeof(dev->name)); dev->number = num; /* set the initial values for the PnP device */ dev_id = kcalloc(1, sizeof(struct pnp_id), GFP_KERNEL); if (!dev_id) goto err; pnpidacpi_to_pnpid(acpi_device_hid(device), dev_id->id); pnp_add_id(dev_id, dev); if(dev->active) { /* parse allocated resource */ status = pnpacpi_parse_allocated_resource(device->handle, &dev->res); if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { pnp_err("PnPACPI: METHOD_NAME__CRS failure for %s", dev_id->id); goto err1; } } if(dev->capabilities & PNP_CONFIGURABLE) { status = pnpacpi_parse_resource_option_data(device->handle, dev); if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { pnp_err("PnPACPI: METHOD_NAME__PRS failure for %s", dev_id->id); goto err1; } } /* parse compatible ids */ if (device->flags.compatible_ids) { struct acpi_compatible_id_list *cid_list = device->pnp.cid_list; int i; for (i = 0; i < cid_list->count; i++) { if (!ispnpidacpi(cid_list->id[i].value)) continue; dev_id = kcalloc(1, sizeof(struct pnp_id), GFP_KERNEL); if (!dev_id) continue; pnpidacpi_to_pnpid(cid_list->id[i].value, dev_id->id); pnp_add_id(dev_id, dev); } } /* clear out the damaged flags */ if (!dev->active) pnp_init_resource_table(&dev->res); pnp_add_device(dev); num ++; return AE_OK; err1: kfree(dev_id); err: kfree(dev); return -EINVAL; }