patch-2.4.6 linux/drivers/acpi/os.c

Next file: linux/drivers/acpi/ospm/Makefile
Previous file: linux/drivers/acpi/namespace/nsxfobj.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.5/linux/drivers/acpi/os.c linux/drivers/acpi/os.c
@@ -1,7 +1,15 @@
+/******************************************************************************
+ * 
+ * Module Name: os.c - Linux OSL functions
+ *		$Revision: 28 $
+ *
+ *****************************************************************************/
+
 /*
  *  os.c - OS-dependent functions
  *
  *  Copyright (C) 2000 Andrew Henroid
+ *  Copyright (C) 2001 Andrew Grover
  *
  *  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
@@ -17,24 +25,36 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
+/* Changes
+ *
+ * Christopher Liebman <liebman@sponsera.com> 2001-5-15
+ * - Fixed improper kernel_thread parameters 
+ */
 
 #include <linux/kernel.h>
-#include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/mm.h>
 #include <linux/pci.h>
-#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/kmod.h>
 #include <linux/delay.h>
 #include <asm/io.h>
-#include "acpi.h"
+#include <acpi.h>
 #include "driver.h"
 
-#define _COMPONENT	OS_DEPENDENT
+#define _COMPONENT	ACPI_OS_SERVICES
 	MODULE_NAME	("os")
 
-static int acpi_irq_irq = 0;
-static OSD_HANDLER acpi_irq_handler = NULL;
-static void *acpi_irq_context = NULL;
+typedef struct 
+{
+    OSD_EXECUTION_CALLBACK  function;
+    void		    *context;
+} ACPI_OS_DPC;
+
+
+/*****************************************************************************
+ *			       Debugger Stuff
+ *****************************************************************************/
 
 #ifdef ENABLE_DEBUGGER
 
@@ -47,6 +67,19 @@
 #endif
 
 
+/*****************************************************************************
+ *				    Globals
+ *****************************************************************************/
+
+static int acpi_irq_irq = 0;
+static OSD_HANDLER acpi_irq_handler = NULL;
+static void *acpi_irq_context = NULL;
+
+
+/******************************************************************************
+ *				   Functions
+ *****************************************************************************/
+
 ACPI_STATUS
 acpi_os_initialize(void)
 {
@@ -141,6 +174,17 @@
 		iounmap(virt);
 }
 
+ACPI_STATUS
+acpi_os_get_physical_address(void *virt, ACPI_PHYSICAL_ADDRESS *phys)
+{
+	if(!phys || !virt)
+		return AE_BAD_PARAMETER;
+
+	*phys = virt_to_phys(virt);
+
+	return AE_OK;
+}
+
 static void
 acpi_irq(int irq, void *dev_id, struct pt_regs *regs)
 {
@@ -171,6 +215,7 @@
 		free_irq(irq, acpi_irq);
 		acpi_irq_handler = NULL;
 	}
+
 	return AE_OK;
 }
 
@@ -248,27 +293,27 @@
 void
 acpi_os_mem_out8 (ACPI_PHYSICAL_ADDRESS phys_addr, UINT8 value)
 {
-	*(u8*) (u32) phys_addr = value;
+	*(u8*) phys_to_virt(phys_addr) = value;
 }
 
 void
 acpi_os_mem_out16 (ACPI_PHYSICAL_ADDRESS phys_addr, UINT16 value)
 {
-	*(u16*) (u32) phys_addr = value;
+	*(u16*) phys_to_virt(phys_addr) = value;
 }
 
 void
 acpi_os_mem_out32 (ACPI_PHYSICAL_ADDRESS phys_addr, UINT32 value)
 {
-	*(u32*) (u32) phys_addr = value;
+	*(u32*) phys_to_virt(phys_addr) = value;
 }
 
 ACPI_STATUS
 acpi_os_read_pci_cfg_byte(
-			  u32 bus,
-			  u32 func,
-			  u32 addr,
-			  u8 * val)
+	u32 bus,
+	u32 func,
+	u32 addr,
+	u8 * val)
 {
 	int devfn = PCI_DEVFN((func >> 16) & 0xffff, func & 0xffff);
 	struct pci_dev *dev = pci_find_slot(bus & 0xffff, devfn);
@@ -279,10 +324,10 @@
 
 ACPI_STATUS
 acpi_os_read_pci_cfg_word(
-			  u32 bus,
-			  u32 func,
-			  u32 addr,
-			  u16 * val)
+	u32 bus,
+	u32 func,
+	u32 addr,
+	u16 * val)
 {
 	int devfn = PCI_DEVFN((func >> 16) & 0xffff, func & 0xffff);
 	struct pci_dev *dev = pci_find_slot(bus & 0xffff, devfn);
@@ -293,10 +338,10 @@
 
 ACPI_STATUS
 acpi_os_read_pci_cfg_dword(
-			   u32 bus,
-			   u32 func,
-			   u32 addr,
-			   u32 * val)
+	u32 bus,
+	u32 func,
+	u32 addr,
+	u32 * val)
 {
 	int devfn = PCI_DEVFN((func >> 16) & 0xffff, func & 0xffff);
 	struct pci_dev *dev = pci_find_slot(bus & 0xffff, devfn);
@@ -307,10 +352,10 @@
 
 ACPI_STATUS
 acpi_os_write_pci_cfg_byte(
-			   u32 bus,
-			   u32 func,
-			   u32 addr,
-			   u8 val)
+	u32 bus,
+	u32 func,
+	u32 addr,
+	u8 val)
 {
 	int devfn = PCI_DEVFN((func >> 16) & 0xffff, func & 0xffff);
 	struct pci_dev *dev = pci_find_slot(bus & 0xffff, devfn);
@@ -321,10 +366,10 @@
 
 ACPI_STATUS
 acpi_os_write_pci_cfg_word(
-			   u32 bus,
-			   u32 func,
-			   u32 addr,
-			   u16 val)
+	u32 bus,
+	u32 func,
+	u32 addr,
+	u16 val)
 {
 	int devfn = PCI_DEVFN((func >> 16) & 0xffff, func & 0xffff);
 	struct pci_dev *dev = pci_find_slot(bus & 0xffff, devfn);
@@ -335,10 +380,10 @@
 
 ACPI_STATUS
 acpi_os_write_pci_cfg_dword(
-			    u32 bus,
-			    u32 func,
-			    u32 addr,
-			    u32 val)
+	u32 bus,
+	u32 func,
+	u32 addr,
+	u32 val)
 {
 	int devfn = PCI_DEVFN((func >> 16) & 0xffff, func & 0xffff);
 	struct pci_dev *dev = pci_find_slot(bus & 0xffff, devfn);
@@ -347,49 +392,317 @@
 	return AE_OK;
 }
 
-/*
- * Queue for interpreter thread
- */
+ACPI_STATUS
+acpi_os_load_module (
+	char *module_name)
+{
+	FUNCTION_TRACE("acpi_os_load_module");
+
+	if (!module_name)
+		return_ACPI_STATUS(AE_BAD_PARAMETER);
+
+	if (0 > request_module(module_name)) {
+		DEBUG_PRINT(ACPI_WARN, ("Unable to load module [%s].\n", module_name));
+		return_ACPI_STATUS(AE_ERROR);
+	}
+
+	return_ACPI_STATUS(AE_OK);
+}
 
 ACPI_STATUS
-acpi_os_queue_for_execution(
-			    u32 priority,
-			    OSD_EXECUTION_CALLBACK callback,
-			    void *context)
+acpi_os_unload_module (
+	char *module_name)
 {
-	if (acpi_run(callback, context))
-		return AE_ERROR;
-	return AE_OK;
+	FUNCTION_TRACE("acpi_os_unload_module");
+
+	if (!module_name)
+		return_ACPI_STATUS(AE_BAD_PARAMETER);
+
+	/* TODO: How on Linux? */
+	/* this is done automatically for all modules with
+	use_count = 0, I think. see: MOD_INC_USE_COUNT -ASG */
+
+	return_ACPI_STATUS(AE_OK);
 }
 
+
 /*
- * Semaphores are unused, interpreter access is single threaded
+ * See acpi_os_queue_for_execution(), too
  */
+static int
+acpi_os_queue_exec (
+	void *context)
+{
+	ACPI_OS_DPC		*dpc = (ACPI_OS_DPC*)context;
 
-ACPI_STATUS
-acpi_os_create_semaphore(u32 max_units, u32 init, ACPI_HANDLE * handle)
+	FUNCTION_TRACE("acpi_os_queue_exec");
+
+	daemonize();
+	strcpy(current->comm, "kacpidpc");
+    
+	if (!dpc || !dpc->function)
+		return_ACPI_STATUS(AE_BAD_PARAMETER);
+
+	DEBUG_PRINT(ACPI_INFO, ("Executing function [%p(%p)].\n", dpc->function, dpc->context));
+
+	dpc->function(dpc->context);
+
+	acpi_os_free(dpc);
+
+	return_VALUE(1);
+}
+
+static void
+acpi_os_schedule_exec (
+	void *context)
 {
-	/* a hack to fake out sems until we implement them */
-	*handle = (ACPI_HANDLE) handle;
-	return AE_OK;
+	ACPI_OS_DPC		*dpc = NULL;
+	int			thread_pid = -1;
+
+	FUNCTION_TRACE("acpi_os_schedule_exec");
+
+	dpc = (ACPI_OS_DPC*)context;
+	if (!dpc) {
+		DEBUG_PRINT(ACPI_ERROR, ("Invalid (NULL) context.\n"));
+		return;
+	}
+
+	DEBUG_PRINT(ACPI_INFO, ("Creating new thread to run function [%p(%p)].\n", dpc->function, dpc->context));
+
+	thread_pid = kernel_thread(acpi_os_queue_exec, dpc, 
+		(CLONE_FS | CLONE_FILES | SIGCHLD));
+	if (thread_pid < 0) {
+		DEBUG_PRINT(ACPI_ERROR, ("Call to kernel_thread() failed.\n"));
+		acpi_os_free(dpc);
+	}
+
+	return_VOID;
 }
 
 ACPI_STATUS
-acpi_os_delete_semaphore(ACPI_HANDLE handle)
+acpi_os_queue_for_execution(
+	u32			priority,
+	OSD_EXECUTION_CALLBACK	function,
+	void			*context)
+{
+	ACPI_STATUS 		status = AE_OK;
+	ACPI_OS_DPC 		*dpc = NULL;
+
+	FUNCTION_TRACE("acpi_os_queue_for_execution");
+
+	DEBUG_PRINT(ACPI_INFO, ("Scheduling function [%p(%p)] for deferred execution.\n", function, context));
+
+	if (!function)
+		return_ACPI_STATUS(AE_BAD_PARAMETER);
+
+	/*
+	 * Allocate/initialize DPC structure.  Note that this memory will be
+	 * freed by the callee.
+	 */
+	dpc = acpi_os_callocate(sizeof(ACPI_OS_DPC));
+	if (!dpc) 
+		return AE_NO_MEMORY;
+
+	dpc->function = function;
+	dpc->context = context;
+
+	/*
+	 * Queue via DPC:
+	 * --------------
+	 * Note that we have to use two different processes for queuing DPCs:
+	 *	 Interrupt-Level: Use schedule_task; can't spawn a new thread.
+	 *	    Kernel-Level: Spawn a new kernel thread, as schedule_task has
+	 *			  its limitations (e.g. single-threaded model), and
+	 *			  all other task queues run at interrupt-level.
+	 */
+	switch (priority) {
+
+	case OSD_PRIORITY_GPE:
+	{
+		static struct tq_struct task;
+
+		memset(&task, 0, sizeof(struct tq_struct));
+
+		task.routine = acpi_os_schedule_exec;
+		task.data = (void*)dpc;
+
+		if (schedule_task(&task) < 0) {
+			DEBUG_PRINT(ACPI_ERROR, ("Call to schedule_task() failed.\n"));
+			status = AE_ERROR;
+		}
+	}
+	break;
+
+	default:
+		acpi_os_schedule_exec(dpc);
+		break;
+	}
+
+	return_ACPI_STATUS(status);
+}
+
+
+ACPI_STATUS
+acpi_os_create_semaphore(
+	u32		max_units,
+	u32		initial_units,
+	ACPI_HANDLE	*handle)
 {
-	return AE_OK;
+    struct semaphore	    *sem = NULL;
+
+    FUNCTION_TRACE("acpi_os_create_semaphore");
+
+    sem = acpi_os_callocate(sizeof(struct semaphore));
+    if (!sem)
+	return_ACPI_STATUS(AE_NO_MEMORY);
+
+    sema_init(sem, initial_units);
+
+    *handle = (ACPI_HANDLE*)sem;
+
+    DEBUG_PRINT(ACPI_INFO, ("Creating semaphore[%p|%d].\n", *handle, initial_units));
+
+    return_ACPI_STATUS(AE_OK);
 }
 
+
+/* 
+ * TODO: A better way to delete semaphores?  Linux doesn't have a
+ * 'delete_semaphore()' function -- may result in an invalid
+ * pointer dereference for non-synchronized consumers.	Should
+ * we at least check for blocked threads and signal/cancel them?
+ */
+
 ACPI_STATUS
-acpi_os_wait_semaphore(ACPI_HANDLE handle, u32 units, u32 timeout)
+acpi_os_delete_semaphore(
+	ACPI_HANDLE handle)
 {
-	return AE_OK;
+    struct semaphore	    *sem = (struct semaphore*)handle;
+
+    FUNCTION_TRACE("acpi_os_delete_semaphore");
+
+    if (!sem) 
+	return AE_BAD_PARAMETER;
+
+    DEBUG_PRINT(ACPI_INFO, ("Deleting semaphore[%p].\n", handle));
+
+    acpi_os_free(sem); sem =  NULL;
+
+    return_ACPI_STATUS(AE_OK);
 }
 
+
+/*
+ * TODO: The kernel doesn't have a 'down_timeout' function -- had to
+ * improvise.  The process is to sleep for one scheduler quantum
+ * until the semaphore becomes available.  Downside is that this
+ * may result in starvation for timeout-based waits when there's
+ * lots of semaphore activity.
+ *
+ * TODO: Support for units > 1?
+ */
+ACPI_STATUS
+acpi_os_wait_semaphore(
+	ACPI_HANDLE	handle,
+	u32			units,
+	u32			timeout)
+{
+	ACPI_STATUS		status = AE_OK;
+	struct semaphore	*sem = (struct semaphore*)handle;
+	int			ret = 0;
+
+	FUNCTION_TRACE("acpi_os_wait_semaphore");
+
+	if (!sem || (units < 1)) 
+		return_ACPI_STATUS(AE_BAD_PARAMETER);
+
+	if (units > 1)
+		return_ACPI_STATUS(AE_SUPPORT);
+
+	DEBUG_PRINT(ACPI_INFO, ("Waiting for semaphore[%p|%d|%d]\n", handle, units, timeout));
+
+	switch (timeout)
+	{
+		/*
+		 * No Wait:
+		 * --------
+		 * A zero timeout value indicates that we shouldn't wait - just
+		 * acquire the semaphore if available otherwise return AE_TIME
+		 * (a.k.a. 'would block').
+		 */
+		case 0:
+		ret = down_trylock(sem);
+		if (ret < 0)
+			status = AE_TIME;
+		break;
+
+		/*
+		 * Wait Indefinitely:
+		 * ------------------
+		 */
+		case WAIT_FOREVER:
+		ret = down_interruptible(sem);
+		if (ret < 0)
+			status = AE_ERROR;
+		break;
+
+		/*
+		 * Wait w/ Timeout:
+		 * ----------------
+		 */
+		default:
+		// TODO: A better timeout algorithm?
+		{
+			int i = 0;
+			static const int quantum_ms = 1000/HZ;
+
+			ret = down_trylock(sem);
+			for (i = timeout; (i > 0 && ret < 0); i -= quantum_ms) {
+				current->state = TASK_INTERRUPTIBLE;
+				schedule_timeout(1);
+				ret = down_trylock(sem);
+			}
+	
+			if (ret < 0)
+			 status = AE_TIME;
+			}
+		break;
+	}
+
+	if (ACPI_FAILURE(status)) {
+		DEBUG_PRINT(ACPI_INFO, ("Failed to acquire semaphore[%p|%d|%d]\n", handle, units, timeout));
+	}
+	else {
+		DEBUG_PRINT(ACPI_INFO, ("Acquired semaphore[%p|%d|%d]\n", handle, units, timeout));
+	}
+
+	return_ACPI_STATUS(status);
+}
+
+
+/*
+ * TODO: Support for units > 1?
+ */
 ACPI_STATUS
-acpi_os_signal_semaphore(ACPI_HANDLE handle, u32 units)
+acpi_os_signal_semaphore(
+    ACPI_HANDLE 	    handle, 
+    u32 		    units)
 {
-	return AE_OK;
+	struct semaphore *sem = (struct semaphore *) handle;
+
+	FUNCTION_TRACE("acpi_os_signal_semaphore");
+
+	if (!sem || (units < 1)) 
+		return_ACPI_STATUS(AE_BAD_PARAMETER);
+
+	if (units > 1)
+		return_ACPI_STATUS(AE_SUPPORT);
+
+	DEBUG_PRINT(ACPI_INFO, ("Signaling semaphore[%p|%d]\n", handle, units));
+
+	up(sem);
+
+	return_ACPI_STATUS(AE_OK);
 }
 
 ACPI_STATUS
@@ -399,8 +712,10 @@
 	return AE_OK;
 }
 
+
 void
 acpi_os_dbg_trap(char *msg)
+
 {
 	acpi_os_printf("trap: %s", msg);
 }
@@ -421,7 +736,7 @@
 
 		kdb_read(buffer, sizeof(line_buf));
 
-		/* remove the CR kdb includes */
+		/* remove the CR kdb includes */ 
 		chars = strlen(buffer) - 1;
 		buffer[chars] = '\0';
 	}
@@ -444,4 +759,15 @@
 acpi_os_writable(void *ptr, u32 len)
 {
 	return 1;
+}
+
+u32
+acpi_os_get_thread_id (void)
+{
+	if (!in_interrupt())
+		return current->pid;
+
+	/*acpi_os_printf("acpi_os_get_thread_id called from interrupt level!\n");*/
+
+	return 0;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)