acpiphp_dock.c

来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 439 行

C
439
字号
/* * ACPI PCI HotPlug dock functions to ACPI CA subsystem * * Copyright (C) 2006 Kristen Carlson Accardi (kristen.c.accardi@intel.com) * Copyright (C) 2006 Intel Corporation * * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or * NON INFRINGEMENT.  See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Send feedback to <kristen.c.accardi@intel.com> * */#include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/pci.h>#include <linux/smp_lock.h>#include <linux/mutex.h>#include "../pci.h"#include "pci_hotplug.h"#include "acpiphp.h"static struct acpiphp_dock_station *ds;#define MY_NAME "acpiphp_dock"int is_dependent_device(acpi_handle handle){	return (get_dependent_device(handle) ? 1 : 0);}static acpi_statusfind_dependent_device(acpi_handle handle, u32 lvl, void *context, void **rv){	int *count = (int *)context;	if (is_dependent_device(handle)) {		(*count)++;		return AE_CTRL_TERMINATE;	} else {		return AE_OK;	}}void add_dependent_device(struct dependent_device *new_dd){	list_add_tail(&new_dd->device_list, &ds->dependent_devices);}void add_pci_dependent_device(struct dependent_device *new_dd){	list_add_tail(&new_dd->pci_list, &ds->pci_dependent_devices);}struct dependent_device * get_dependent_device(acpi_handle handle){	struct dependent_device *dd;	if (!ds)		return NULL;	list_for_each_entry(dd, &ds->dependent_devices, device_list) {		if (handle == dd->handle)			return dd;	}	return NULL;}struct dependent_device *alloc_dependent_device(acpi_handle handle){	struct dependent_device *dd;	dd = kzalloc(sizeof(*dd), GFP_KERNEL);	if (dd) {		INIT_LIST_HEAD(&dd->pci_list);		INIT_LIST_HEAD(&dd->device_list);		dd->handle = handle;	}	return dd;}static int is_dock(acpi_handle handle){	acpi_status status;	acpi_handle tmp;	status = acpi_get_handle(handle, "_DCK", &tmp);	if (ACPI_FAILURE(status)) {		return 0;	}	return 1;}static int dock_present(void){	unsigned long sta;	acpi_status status;	if (ds) {		status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);		if (ACPI_SUCCESS(status) && sta)			return 1;	}	return 0;}static void eject_dock(void){	struct acpi_object_list arg_list;	union acpi_object arg;	arg_list.count = 1;	arg_list.pointer = &arg;	arg.type = ACPI_TYPE_INTEGER;	arg.integer.value = 1;	if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0",					&arg_list, NULL)) || dock_present())		warn("%s: failed to eject dock!\n", __FUNCTION__);	return;}static acpi_status handle_dock(int dock){	acpi_status status;	struct acpi_object_list arg_list;	union acpi_object arg;	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};	dbg("%s: %s\n", __FUNCTION__, dock ? "docking" : "undocking");	/* _DCK method has one argument */	arg_list.count = 1;	arg_list.pointer = &arg;	arg.type = ACPI_TYPE_INTEGER;	arg.integer.value = dock;	status = acpi_evaluate_object(ds->handle, "_DCK",					&arg_list, &buffer);	if (ACPI_FAILURE(status))		err("%s: failed to execute _DCK\n", __FUNCTION__);	acpi_os_free(buffer.pointer);	return status;}static inline void dock(void){	handle_dock(1);}static inline void undock(void){	handle_dock(0);}/* * the _DCK method can do funny things... and sometimes not * hah-hah funny. * * TBD - figure out a way to only call fixups for * systems that require them. */static void post_dock_fixups(void){	struct pci_bus *bus;	u32 buses;	struct dependent_device *dd;	list_for_each_entry(dd, &ds->pci_dependent_devices, pci_list) {		bus = dd->func->slot->bridge->pci_bus;		/* fixup bad _DCK function that rewrites	 	 * secondary bridge on slot	 	 */		pci_read_config_dword(bus->self,				PCI_PRIMARY_BUS,				&buses);		if (((buses >> 8) & 0xff) != bus->secondary) {			buses = (buses & 0xff000000)	     			| ((unsigned int)(bus->primary)     <<  0)	     			| ((unsigned int)(bus->secondary)   <<  8)	     			| ((unsigned int)(bus->subordinate) << 16);			pci_write_config_dword(bus->self,					PCI_PRIMARY_BUS,					buses);		}	}}static void hotplug_pci(u32 type){	struct dependent_device *dd;	list_for_each_entry(dd, &ds->pci_dependent_devices, pci_list)		handle_hotplug_event_func(dd->handle, type, dd->func);}static inline void begin_dock(void){	ds->flags |= DOCK_DOCKING;}static inline void complete_dock(void){	ds->flags &= ~(DOCK_DOCKING);	ds->last_dock_time = jiffies;}static int dock_in_progress(void){	if (ds->flags & DOCK_DOCKING ||		ds->last_dock_time == jiffies) {		dbg("dock in progress\n");		return 1;	}	return 0;}static voidhandle_hotplug_event_dock(acpi_handle handle, u32 type, void *context){	dbg("%s: enter\n", __FUNCTION__);	switch (type) {		case ACPI_NOTIFY_BUS_CHECK:			dbg("BUS Check\n");			if (!dock_in_progress() && dock_present()) {				begin_dock();				dock();				if (!dock_present()) {					err("Unable to dock!\n");					break;				}				post_dock_fixups();				hotplug_pci(type);				complete_dock();			}			break;		case ACPI_NOTIFY_EJECT_REQUEST:			dbg("EJECT request\n");			if (!dock_in_progress() && dock_present()) {				hotplug_pci(type);				undock();				eject_dock();				if (dock_present())					err("Unable to undock!\n");			}			break;	}}static acpi_statusfind_dock_ejd(acpi_handle handle, u32 lvl, void *context, void **rv){	acpi_status status;	acpi_handle tmp;	acpi_handle dck_handle = (acpi_handle) context;	char objname[64];	struct acpi_buffer buffer = { .length = sizeof(objname),				      .pointer = objname };	struct acpi_buffer ejd_buffer = {ACPI_ALLOCATE_BUFFER, NULL};	union acpi_object *ejd_obj;	status = acpi_get_handle(handle, "_EJD", &tmp);	if (ACPI_FAILURE(status))		return AE_OK;	/* make sure we are dependent on the dock device,	 * by executing the _EJD method, then getting a handle	 * to the device referenced by that name.  If that	 * device handle is the same handle as the dock station	 * handle, then we are a device dependent on the dock station	 */	acpi_get_name(dck_handle, ACPI_FULL_PATHNAME, &buffer);	status = acpi_evaluate_object(handle, "_EJD", NULL, &ejd_buffer);	if (ACPI_FAILURE(status)) {		err("Unable to execute _EJD!\n");		goto find_ejd_out;	}	ejd_obj = ejd_buffer.pointer;	status = acpi_get_handle(NULL, ejd_obj->string.pointer, &tmp);	if (ACPI_FAILURE(status))		goto find_ejd_out;	if (tmp == dck_handle) {		struct dependent_device *dd;		dbg("%s: found device dependent on dock\n", __FUNCTION__);		dd = alloc_dependent_device(handle);		if (!dd) {			err("Can't allocate memory for dependent device!\n");			goto find_ejd_out;		}		add_dependent_device(dd);	}find_ejd_out:	acpi_os_free(ejd_buffer.pointer);	return AE_OK;}int detect_dependent_devices(acpi_handle *bridge_handle){	acpi_status status;	int count;	count = 0;	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle,					(u32)1, find_dependent_device,					(void *)&count, NULL);	return count;}static acpi_statusfind_dock(acpi_handle handle, u32 lvl, void *context, void **rv){	int *count = (int *)context;	if (is_dock(handle)) {		dbg("%s: found dock\n", __FUNCTION__);		ds = kzalloc(sizeof(*ds), GFP_KERNEL);		ds->handle = handle;		INIT_LIST_HEAD(&ds->dependent_devices);		INIT_LIST_HEAD(&ds->pci_dependent_devices);		/* look for devices dependent on dock station */		acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,			ACPI_UINT32_MAX, find_dock_ejd, handle, NULL);		acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,			handle_hotplug_event_dock, ds);		(*count)++;	}	return AE_OK;}int find_dock_station(void){	int num = 0;	ds = NULL;	/* start from the root object, because some laptops define	 * _DCK methods outside the scope of PCI (IBM x-series laptop)	 */	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,			ACPI_UINT32_MAX, find_dock, &num, NULL);	return num;}void remove_dock_station(void){	struct dependent_device *dd, *tmp;	if (ds) {		if (ACPI_FAILURE(acpi_remove_notify_handler(ds->handle,			ACPI_SYSTEM_NOTIFY, handle_hotplug_event_dock)))			err("failed to remove dock notify handler\n");		/* free all dependent devices */		list_for_each_entry_safe(dd, tmp, &ds->dependent_devices,				device_list)			kfree(dd);		/* no need to touch the pci_dependent_device list,		 * cause all memory was freed above		 */		kfree(ds);	}}

⌨️ 快捷键说明

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