⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 acpiphp_glue.c

📁 audio driver for hotplug pci on linux 2.6.27
💻 C
📖 第 1 页 / 共 3 页
字号:
}static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle){	struct list_head *head;	list_for_each(head, &bridge_list) {		struct acpiphp_bridge *bridge = list_entry(head,						struct acpiphp_bridge, list);		if (bridge->handle == handle)			return bridge;	}	return NULL;}static void cleanup_bridge(struct acpiphp_bridge *bridge){	struct list_head *list, *tmp;	struct acpiphp_slot *slot;	acpi_status status;	acpi_handle handle = bridge->handle;	status = acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,					    handle_hotplug_event_bridge);	if (ACPI_FAILURE(status))		err("failed to remove notify handler\n");	if ((bridge->type != BRIDGE_TYPE_HOST) &&	    ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func)) {		status = acpi_install_notify_handler(bridge->func->handle,						ACPI_SYSTEM_NOTIFY,						handle_hotplug_event_func,						bridge->func);		if (ACPI_FAILURE(status))			err("failed to install interrupt notify handler\n");	}	slot = bridge->slots;	while (slot) {		struct acpiphp_slot *next = slot->next;		list_for_each_safe (list, tmp, &slot->funcs) {			struct acpiphp_func *func;			func = list_entry(list, struct acpiphp_func, sibling);			if (is_dock_device(func->handle)) {				unregister_hotplug_dock_device(func->handle);				unregister_dock_notifier(&func->nb);			}			if (!(func->flags & FUNC_HAS_DCK)) {				status = acpi_remove_notify_handler(func->handle,						ACPI_SYSTEM_NOTIFY,						handle_hotplug_event_func);				if (ACPI_FAILURE(status))					err("failed to remove notify handler\n");			}			pci_dev_put(func->pci_dev);			list_del(list);			kfree(func);		}		acpiphp_unregister_hotplug_slot(slot);		list_del(&slot->funcs);		kfree(slot);		slot = next;	}	pci_dev_put(bridge->pci_dev);	list_del(&bridge->list);	kfree(bridge);}static acpi_statuscleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv){	struct acpiphp_bridge *bridge;	/* cleanup p2p bridges under this P2P bridge	   in a depth-first manner */	acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,				cleanup_p2p_bridge, NULL, NULL);	bridge = acpiphp_handle_to_bridge(handle);	if (bridge)		cleanup_bridge(bridge);	return AE_OK;}static void remove_bridge(acpi_handle handle){	struct acpiphp_bridge *bridge;	/* cleanup p2p bridges under this host bridge	   in a depth-first manner */	acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,				(u32)1, cleanup_p2p_bridge, NULL, NULL);	/*	 * On root bridges with hotplug slots directly underneath (ie,	 * no p2p bridge inbetween), we call cleanup_bridge(). 	 *	 * The else clause cleans up root bridges that either had no	 * hotplug slots at all, or had a p2p bridge underneath.	 */	bridge = acpiphp_handle_to_bridge(handle);	if (bridge)		cleanup_bridge(bridge);	else		acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,					   handle_hotplug_event_bridge);}static struct pci_dev * get_apic_pci_info(acpi_handle handle){	struct acpi_pci_id id;	struct pci_bus *bus;	struct pci_dev *dev;	if (ACPI_FAILURE(acpi_get_pci_id(handle, &id)))		return NULL;	bus = pci_find_bus(id.segment, id.bus);	if (!bus)		return NULL;	dev = pci_get_slot(bus, PCI_DEVFN(id.device, id.function));	if (!dev)		return NULL;	if ((dev->class != PCI_CLASS_SYSTEM_PIC_IOAPIC) &&	    (dev->class != PCI_CLASS_SYSTEM_PIC_IOXAPIC))	{		pci_dev_put(dev);		return NULL;	}	return dev;}static int get_gsi_base(acpi_handle handle, u32 *gsi_base){	acpi_status status;	int result = -1;	unsigned long long gsb;	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};	union acpi_object *obj;	void *table;	status = acpi_evaluate_integer(handle, "_GSB", NULL, &gsb);	if (ACPI_SUCCESS(status)) {		*gsi_base = (u32)gsb;		return 0;	}	status = acpi_evaluate_object(handle, "_MAT", NULL, &buffer);	if (ACPI_FAILURE(status) || !buffer.length || !buffer.pointer)		return -1;	obj = buffer.pointer;	if (obj->type != ACPI_TYPE_BUFFER)		goto out;	table = obj->buffer.pointer;	switch (((struct acpi_subtable_header *)table)->type) {	case ACPI_MADT_TYPE_IO_SAPIC:		*gsi_base = ((struct acpi_madt_io_sapic *)table)->global_irq_base;		result = 0;		break;	case ACPI_MADT_TYPE_IO_APIC:		*gsi_base = ((struct acpi_madt_io_apic *)table)->global_irq_base;		result = 0;		break;	default:		break;	} out:	kfree(buffer.pointer);	return result;}static acpi_statusioapic_add(acpi_handle handle, u32 lvl, void *context, void **rv){	acpi_status status;	unsigned long long sta;	acpi_handle tmp;	struct pci_dev *pdev;	u32 gsi_base;	u64 phys_addr;	struct acpiphp_ioapic *ioapic;	/* Evaluate _STA if present */	status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);	if (ACPI_SUCCESS(status) && sta != ACPI_STA_ALL)		return AE_CTRL_DEPTH;	/* Scan only PCI bus scope */	status = acpi_get_handle(handle, "_HID", &tmp);	if (ACPI_SUCCESS(status))		return AE_CTRL_DEPTH;	if (get_gsi_base(handle, &gsi_base))		return AE_OK;	ioapic = kmalloc(sizeof(*ioapic), GFP_KERNEL);	if (!ioapic)		return AE_NO_MEMORY;	pdev = get_apic_pci_info(handle);	if (!pdev)		goto exit_kfree;	if (pci_enable_device(pdev))		goto exit_pci_dev_put;	pci_set_master(pdev);	if (pci_request_region(pdev, 0, "I/O APIC(acpiphp)"))		goto exit_pci_disable_device;	phys_addr = pci_resource_start(pdev, 0);	if (acpi_register_ioapic(handle, phys_addr, gsi_base))		goto exit_pci_release_region;	ioapic->gsi_base = gsi_base;	ioapic->dev = pdev;	spin_lock(&ioapic_list_lock);	list_add_tail(&ioapic->list, &ioapic_list);	spin_unlock(&ioapic_list_lock);	return AE_OK; exit_pci_release_region:	pci_release_region(pdev, 0); exit_pci_disable_device:	pci_disable_device(pdev); exit_pci_dev_put:	pci_dev_put(pdev); exit_kfree:	kfree(ioapic);	return AE_OK;}static acpi_statusioapic_remove(acpi_handle handle, u32 lvl, void *context, void **rv){	acpi_status status;	unsigned long long sta;	acpi_handle tmp;	u32 gsi_base;	struct acpiphp_ioapic *pos, *n, *ioapic = NULL;	/* Evaluate _STA if present */	status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);	if (ACPI_SUCCESS(status) && sta != ACPI_STA_ALL)		return AE_CTRL_DEPTH;	/* Scan only PCI bus scope */	status = acpi_get_handle(handle, "_HID", &tmp);	if (ACPI_SUCCESS(status))		return AE_CTRL_DEPTH;	if (get_gsi_base(handle, &gsi_base))		return AE_OK;	acpi_unregister_ioapic(handle, gsi_base);	spin_lock(&ioapic_list_lock);	list_for_each_entry_safe(pos, n, &ioapic_list, list) {		if (pos->gsi_base != gsi_base)			continue;		ioapic = pos;		list_del(&ioapic->list);		break;	}	spin_unlock(&ioapic_list_lock);	if (!ioapic)		return AE_OK;	pci_release_region(ioapic->dev, 0);	pci_disable_device(ioapic->dev);	pci_dev_put(ioapic->dev);	kfree(ioapic);	return AE_OK;}static int acpiphp_configure_ioapics(acpi_handle handle){	ioapic_add(handle, 0, NULL, NULL);	acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,			    ACPI_UINT32_MAX, ioapic_add, NULL, NULL);	return 0;}static int acpiphp_unconfigure_ioapics(acpi_handle handle){	ioapic_remove(handle, 0, NULL, NULL);	acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,			    ACPI_UINT32_MAX, ioapic_remove, NULL, NULL);	return 0;}static int power_on_slot(struct acpiphp_slot *slot){	acpi_status status;	struct acpiphp_func *func;	struct list_head *l;	int retval = 0;	/* if already enabled, just skip */	if (slot->flags & SLOT_POWEREDON)		goto err_exit;	list_for_each (l, &slot->funcs) {		func = list_entry(l, struct acpiphp_func, sibling);		if (func->flags & FUNC_HAS_PS0) {			dbg("%s: executing _PS0\n", __func__);			status = acpi_evaluate_object(func->handle, "_PS0", NULL, NULL);			if (ACPI_FAILURE(status)) {				warn("%s: _PS0 failed\n", __func__);				retval = -1;				goto err_exit;			} else				break;		}	}	/* TBD: evaluate _STA to check if the slot is enabled */	slot->flags |= SLOT_POWEREDON; err_exit:	return retval;}static int power_off_slot(struct acpiphp_slot *slot){	acpi_status status;	struct acpiphp_func *func;	struct list_head *l;	int retval = 0;	/* if already disabled, just skip */	if ((slot->flags & SLOT_POWEREDON) == 0)		goto err_exit;	list_for_each (l, &slot->funcs) {		func = list_entry(l, struct acpiphp_func, sibling);		if (func->flags & FUNC_HAS_PS3) {			status = acpi_evaluate_object(func->handle, "_PS3", NULL, NULL);			if (ACPI_FAILURE(status)) {				warn("%s: _PS3 failed\n", __func__);				retval = -1;				goto err_exit;			} else				break;		}	}	/* TBD: evaluate _STA to check if the slot is disabled */	slot->flags &= (~SLOT_POWEREDON); err_exit:	return retval;}/** * acpiphp_max_busnr - return the highest reserved bus number under the given bus. * @bus: bus to start search with */static unsigned char acpiphp_max_busnr(struct pci_bus *bus){	struct list_head *tmp;	unsigned char max, n;	/*	 * pci_bus_max_busnr will return the highest	 * reserved busnr for all these children.	 * that is equivalent to the bus->subordinate	 * value.  We don't want to use the parent's	 * bus->subordinate value because it could have	 * padding in it.	 */	max = bus->secondary;	list_for_each(tmp, &bus->children) {		n = pci_bus_max_busnr(pci_bus_b(tmp));		if (n > max)			max = n;	}	return max;}/** * acpiphp_bus_add - add a new bus to acpi subsystem * @func: acpiphp_func of the bridge */static int acpiphp_bus_add(struct acpiphp_func *func){	acpi_handle phandle;	struct acpi_device *device, *pdevice;	int ret_val;	acpi_get_parent(func->handle, &phandle);	if (acpi_bus_get_device(phandle, &pdevice)) {		dbg("no parent device, assuming NULL\n");		pdevice = NULL;	}	if (!acpi_bus_get_device(func->handle, &device)) {		dbg("bus exists... trim\n");		/* this shouldn't be in here, so remove		 * the bus then re-add it...		 */		ret_val = acpi_bus_trim(device, 1);		dbg("acpi_bus_trim return %x\n", ret_val);	}	ret_val = acpi_bus_add(&device, pdevice, func->handle,		ACPI_BUS_TYPE_DEVICE);	if (ret_val) {		dbg("error adding bus, %x\n",			-ret_val);		goto acpiphp_bus_add_out;	}	/*	 * try to start anyway.  We could have failed to add	 * simply because this bus had previously been added	 * on another add.  Don't bother with the return value	 * we just keep going.	 */	ret_val = acpi_bus_start(device);acpiphp_bus_add_out:	return ret_val;}/** * acpiphp_bus_trim - trim a bus from acpi subsystem * @handle: handle to acpi namespace */static int acpiphp_bus_trim(acpi_handle handle){	struct acpi_device *device;	int retval;	retval = acpi_bus_get_device(handle, &device);	if (retval) {		dbg("acpi_device not found\n");		return retval;	}	retval = acpi_bus_trim(device, 1);	if (retval)		err("cannot remove from acpi list\n");	return retval;}/** * enable_device - enable, configure a slot * @slot: slot to be enabled * * This function should be called per *physical slot*, * not per each slot object in ACPI namespace. */static int __ref enable_device(struct acpiphp_slot *slot){	struct pci_dev *dev;	struct pci_bus *bus = slot->bridge->pci_bus;	struct list_head *l;	struct acpiphp_func *func;	int retval = 0;	int num, max, pass;	acpi_status status;	if (slot->flags & SLOT_ENABLED)		goto err_exit;	/* sanity check: dev should be NULL when hot-plugged in */	dev = pci_get_slot(bus, PCI_DEVFN(slot->device, 0));	if (dev) {		/* This case shouldn't happen */		err("pci_dev structure already exists.\n");		pci_dev_put(dev);		retval = -1;		goto err_exit;	}	num = pci_scan_slot(bus, PCI_DEVFN(slot->device, 0));	if (num == 0) {		err("No new device found\n");		retval = -1;		goto err_exit;	}	max = acpiphp_max_busnr(bus);	for (pass = 0; pass < 2; pass++) {		list_for_each_entry(dev, &bus->devices, bus_list) {			if (PCI_SLOT(dev->devfn) != slot->device)				continue;			if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||			    dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) {				max = pci_scan_bridge(bus, dev, max, pass);				if (pass && dev->subordinate)					pci_bus_size_bridges(dev->subordinate);			}		}	}	list_for_each (l, &slot->funcs) {		func = list_entry(l, struct acpiphp_func, sibling);		acpiphp_bus_add(func);	}	pci_bus_assign_resources(bus);	acpiphp_sanitize_bus(bus);	acpiphp_set_hpp_values(slot->bridge->handle, bus);	list_for_each_entry(func, &slot->funcs, sibling)		acpiphp_configure_ioapics(func->handle);	pci_enable_bridges(bus);	pci_bus_add_devices(bus);	/* associate pci_dev to our representation */	list_for_each (l, &slot->funcs) {		func = list_entry(l, struct acpiphp_func, sibling);		func->pci_dev = pci_get_slot(bus, PCI_DEVFN(slot->device,							func->function));		if (!func->pci_dev)			continue;		if (func->pci_dev->hdr_type != PCI_HEADER_TYPE_BRIDGE &&		    func->pci_dev->hdr_type != PCI_HEADER_TYPE_CARDBUS)			continue;		status = find_p2p_bridge(func->handle, (u32)1, bus, NULL);		if (ACPI_FAILURE(status))			warn("find_p2p_bridge failed (error code = 0x%x)\n",				status);	}	slot->flags |= SLOT_ENABLED; err_exit:	return retval;}static void disable_bridges(struct pci_bus *bus){	struct pci_dev *dev;	list_for_each_entry(dev, &bus->devices, bus_list) {		if (dev->subordinate) {			disable_bridges(dev->subordinate);			pci_disable_device(dev);		}	}}/** * disable_device - disable a slot * @slot: ACPI PHP slot */static int disable_device(struct acpiphp_slot *slot){	int retval = 0;	struct acpiphp_func *func;	struct list_head *l;	/* is this slot already disabled? */	if (!(slot->flags & SLOT_ENABLED))		goto err_exit;	list_for_each (l, &slot->funcs) {		func = list_entry(l, struct acpiphp_func, sibling);		if (func->bridge) {			/* cleanup p2p bridges under this P2P bridge */			cleanup_p2p_bridge(func->bridge->handle,						(u32)1, NULL, NULL);			func->bridge = NULL;		}		if (func->pci_dev) {			pci_stop_bus_device(func->pci_dev);			if (func->pci_dev->subordinate) {				disable_bridges(func->pci_dev->subordinate);				pci_disable_device(func->pci_dev);			}		}	}	list_for_each (l, &slot->funcs) {		func = list_entry(l, struct acpiphp_func, sibling);		acpiphp_unconfigure_ioapics(func->handle);		acpiphp_bus_trim(func->handle);		/* try to remove anyway.		 * acpiphp_bus_add might have been failed */		if (!func->pci_dev)			continue;		pci_remove_bus_device(func->pci_dev);		pci_dev_put(func->pci_dev);		func->pci_dev = NULL;	}	slot->flags &= (~SLOT_ENABLED); err_exit:	return retval;}/** * get_slot_status - get ACPI slot status * @slot: ACPI PHP slot * * If a slot has _STA for each function and if any one of them * returned non-zero status, return it. *

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -