#define FOUND_APCI 0x61504349 /* these are the names for the IBM ACPI pseudo-device */ #define IBM_HARDWARE_ID1 "IBM37D0" #define IBM_HARDWARE_ID2 "IBM37D4"
#define hpslot_to_sun(A) (to_slot(A)->sun)
/* union apci_descriptor - allows access to the * various device descriptors that are embedded in the * aPCI table
*/ union apci_descriptor { struct { char sig[4];
u8 len;
} header; struct {
u8 type;
u8 len;
u16 slot_id;
u8 bus_id;
u8 dev_num;
u8 slot_num;
u8 slot_attr[2];
u8 attn;
u8 status[2];
u8 sun;
u8 res[3];
} slot; struct {
u8 type;
u8 len;
} generic;
};
/* struct notification - keeps info about the device * that cause the ACPI notification event
*/ struct notification { struct acpi_device *device;
u8 event;
};
/** * ibm_slot_from_id - workaround for bad ibm hardware * @id: the slot number that linux refers to the slot by * * Description: This method returns the aCPI slot descriptor * corresponding to the Linux slot number. This descriptor * has info about the aPCI slot id and attention status. * This descriptor must be freed using kfree when done.
*/ staticunion apci_descriptor *ibm_slot_from_id(int id)
{ int ind = 0, size; union apci_descriptor *ret = NULL, *des; char *table;
size = ibm_get_table_from_acpi(&table); if (size < 0) return NULL;
des = (union apci_descriptor *)table; if (memcmp(des->header.sig, "aPCI", 4) != 0) goto ibm_slot_done;
des = (union apci_descriptor *)&table[ind += des->header.len]; while (ind < size && (des->generic.type != 0x82 ||
des->slot.slot_num != id)) {
des = (union apci_descriptor *)&table[ind += des->generic.len];
}
if (ind < size && des->slot.slot_num == id)
ret = des;
ibm_slot_done: if (ret) {
ret = kmalloc(sizeof(union apci_descriptor), GFP_KERNEL); if (ret)
memcpy(ret, des, sizeof(union apci_descriptor));
}
kfree(table); return ret;
}
/** * ibm_set_attention_status - callback method to set the attention LED * @slot: the hotplug_slot to work with * @status: what to set the LED to (0 or 1) * * Description: This method is registered with the acpiphp module as a * callback to do the device specific task of setting the LED status.
*/ staticint ibm_set_attention_status(struct hotplug_slot *slot, u8 status)
{ union acpi_object args[2]; struct acpi_object_list params = { .pointer = args, .count = 2 };
acpi_status stat; unsignedlonglong rc; union apci_descriptor *ibm_slot; int id = hpslot_to_sun(slot);
ibm_slot = ibm_slot_from_id(id); if (!ibm_slot) {
pr_err("APLS null ACPI descriptor for slot %d\n", id); return -ENODEV;
}
pr_debug("%s: set slot %d (%d) attention status to %d\n", __func__,
ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
(status ? 1 : 0));
/** * ibm_get_attention_status - callback method to get attention LED status * @slot: the hotplug_slot to work with * @status: returns what the LED is set to (0 or 1) * * Description: This method is registered with the acpiphp module as a * callback to do the device specific task of getting the LED status. * * Because there is no direct method of getting the LED status directly * from an ACPI call, we read the aPCI table and parse out our * slot descriptor to read the status from that.
*/ staticint ibm_get_attention_status(struct hotplug_slot *slot, u8 *status)
{ union apci_descriptor *ibm_slot; int id = hpslot_to_sun(slot);
ibm_slot = ibm_slot_from_id(id); if (!ibm_slot) {
pr_err("APLS null ACPI descriptor for slot %d\n", id); return -ENODEV;
}
pr_debug("%s: get slot %d (%d) attention status is %d\n", __func__,
ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
*status);
kfree(ibm_slot); return 0;
}
/** * ibm_handle_events - listens for ACPI events for the IBM37D0 device * @handle: an ACPI handle to the device that caused the event * @event: the event info (device specific) * @context: passed context (our notification struct) * * Description: This method is registered as a callback with the ACPI * subsystem it is called when this device has an event to notify the OS of. * * The events actually come from the device as two events that get * synthesized into one event with data by this function. The event * ID comes first and then the slot number that caused it. We report * this as one event to the OS. * * From section 5.6.2.2 of the ACPI 2.0 spec, I understand that the OSPM will * only re-enable the interrupt that causes this event AFTER this method * has returned, thereby enforcing serial access for the notification struct.
*/ staticvoid ibm_handle_events(acpi_handle handle, u32 event, void *context)
{
u8 detail = event & 0x0f;
u8 subevent = event & 0xf0; struct notification *note = context;
pr_debug("%s: Received notification %02x\n", __func__, event);
if (subevent == 0x80) {
pr_debug("%s: generating bus event\n", __func__);
acpi_bus_generate_netlink_event(note->device->pnp.device_class,
dev_name(¬e->device->dev),
note->event, detail);
} else
note->event = event;
}
/** * ibm_get_table_from_acpi - reads the APLS buffer from ACPI * @bufp: address to pointer to allocate for the table * * Description: This method reads the APLS buffer in from ACPI and * stores the "stripped" table into a single buffer * it allocates and passes the address back in bufp. * * If NULL is passed in as buffer, this method only calculates * the size of the table and returns that without filling * in the buffer. * * Returns < 0 on error or the size of the table on success.
*/ staticint ibm_get_table_from_acpi(char **bufp)
{ union acpi_object *package; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
acpi_status status; char *lbuf = NULL; int i, size = -EIO;
status = acpi_evaluate_object(ibm_acpi_handle, "APCI", NULL, &buffer); if (ACPI_FAILURE(status)) {
pr_err("%s: APCI evaluation failed\n", __func__); return -ENODEV;
}
/** * ibm_read_apci_table - callback for the sysfs apci_table file * @filp: the open sysfs file * @kobj: the kobject this binary attribute is a part of * @bin_attr: struct bin_attribute for this file * @buffer: the kernel space buffer to fill * @pos: the offset into the file * @size: the number of bytes requested * * Description: Gets registered with sysfs as the reader callback * to be executed when /sys/bus/pci/slots/apci_table gets read. * * Since we don't get notified on open and close for this file, * things get really tricky here... * our solution is to only allow reading the table in all at once.
*/ static ssize_t ibm_read_apci_table(struct file *filp, struct kobject *kobj, conststruct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t size)
{ int bytes_read = -EINVAL; char *table = NULL;
/** * ibm_find_acpi_device - callback to find our ACPI device * @handle: the ACPI handle of the device we are inspecting * @lvl: depth into the namespace tree * @context: a pointer to our handle to fill when we find the device * @rv: a return value to fill if desired * * Description: Used as a callback when calling acpi_walk_namespace * to find our device. When this method returns non-zero * acpi_walk_namespace quits its search and returns our value.
*/ static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
u32 lvl, void *context, void **rv)
{
acpi_handle *phandle = (acpi_handle *)context; unsignedlonglong current_status = 0;
acpi_status status; struct acpi_device_info *info; int retval = 0;
status = acpi_get_object_info(handle, &info); if (ACPI_FAILURE(status)) {
pr_err("%s: Failed to get device information status=0x%x\n",
__func__, status); return retval;
}
if (current_status && (info->valid & ACPI_VALID_HID) &&
(!strcmp(info->hardware_id.string, IBM_HARDWARE_ID1) ||
!strcmp(info->hardware_id.string, IBM_HARDWARE_ID2))) {
pr_debug("found hardware: %s, handle: %p\n",
info->hardware_id.string, handle);
*phandle = handle; /* returning non-zero causes the search to stop * and returns this value to the caller of * acpi_walk_namespace, but it also causes some warnings * in the acpi debug code to print...
*/
retval = FOUND_APCI;
}
kfree(info); return retval;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.