EECE-4029 Operating Systems Fall 2016
Lab 4

processes, mutex, semaphores, memory management, producer-consumer, files, deadlock, more..

Kernel module to monitor battery status

Due: Sep 30 (submit instructions: here)

Rationale:
    Gain a little insight into the design and use of ACPI and kernel module development. Observe how the OS interacts with the hardware. Note how data structures defined in the kernel sources are crucial to the generation and handling of interrupts.
 
Lab:
Write a linux kernel module called battcheck that checks battery status every second and reports an interesting result: that is, reports when either 1) the battery begins to discharge or 2) when it has begun to charge or 3) when it has reached full charge or 4) when the battery level has become low (only one report) or 5) when the battery level becomes critical (report every 30 seconds). As root, you should be able to load the module with insmod battcheck and unload it with rmmod battcheck.

Base the module on acpi_call.c using this Makefile.

You will need to find the name of the method that your computer uses to access battery information. It is likely something similar to the string \_SB_.BAT0._BIF, which has two parts: \_SB_.BAT0 and _BIF. The _BIF part is standard and is described in the ACPI spec. The \_SB_.BAT0 string is not standard and is different for different machines. To find this string on your machine do this (ubuntu Linux):

  prompt> ls /sys/class/power_supply
  ACAD@   BAT0@
  prompt> cat /sys/class/power_supply/BAT0/device/path
  \_SB_.BAT0
In other words, check the power_supply directory for mention of a battery link, probably BAT0 or BAT1, then perform the cat through that link to find the path to the battery function. When implementing this method in code, the first part of the method name will be invoked by one function which returns a pointer to a handle which itself is then invoked on the second part of the method name (two step process). That second call returns a structure that is documented in the ACPI spec, in this case in the section describing _BIF. To see precisely how this is implemented look at the last example below where the method name is \_PR_.CPU0._PSD.

My call (on Compaq Presario F700 and HP dv6) to \_SB_.BAT0._BIF, implemented in a manner similar to the way \_PR_.CPU0._PSD is called in the last example, returns this:

  [0x1, 0x15e0, 0x1360, 0x1, 0x2b5c, 0x25a, 0x160, 0xa, 0x19, 
  "Primary", " ", "LION", "Hewlett-Packard"]
which translates to mAh/mA (discharge units), 5600 mAh capacity, 4960 mAh last full charge, rechargeable, 11100 mV design voltage, low battery at 602 mAh, 352 mAh critical battery, Primary model, no serial number, Lithium Ion type, manuactured for Hewlett Packard. There is also something like \_SB_.BAT0._BST for getting battery status. My call returns
  [0x0, 0xffffffff, 0x1360, 0x31ba]
which translates to charged, 0 current discharge rate, 4960 mAh charge remaining, 12.7 volts.

You can print the information any way you choose. You can use some graphics, or print to a /proc file or print to the console.

You will probably want to create an infinite loop in either the init or read functions of your module. I do not think this will work unless you use a kernel thread. Another possibility is to use a work_queue. If you can do it without a thread or work_queue (or mod_timer) you get 10 brownie points. The sample code below shows one way to cause an action to repeat. That may not work with the acpi call but try it anyway and be prepared to reboot. Whatever way you do it, the code should examine the values returned by the battery status (_BST) method and, if you are ambitious, the battery information (_BIF) method. If the battery level is low (a simple comparison) or if it is discharging you want to alert the user. So as not to burden the processor, the (_BST) method could be called, say, every 1 second.

You cannot use sleep from the C language to do this - you must rely on kernel functions. One possibility is to create a timer that triggers your battery information function once a second. Such a timer could be fashioned from the following:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/timer.h>

static struct timer_list my_timer;

void my_timer_callback( unsigned long data ) {
   printk(KERN_INFO "my_timer_callback called\n");
   mod_timer( &my_timer, jiffies + msecs_to_jiffies(2000) );
}
			
int init_module( void ) {
   int ret;
				
   printk("Timer module installing\n");
					
   // my_timer.function, my_timer.data
   setup_timer( &my_timer, my_timer_callback, 0 );
							
   printk("Starting timer to fire in 1 sec\n");
   ret = mod_timer( &my_timer, jiffies + msecs_to_jiffies(1000) );
   if (ret) printk("Error in mod_timer\n");
		   
   return 0;
}
Another possibility is to use msleep (see the Assistance section below).

If you do not use a kernel thread, you probably should use a work_queue to handle the acpi call. The reason is that interrupts are disabled in the kernel routines and if a critical interrupt is raised while a lot of time is spent in the module routines, the system is likely to crash.

 
Assistance:
Assume you can write a function that checks and does proper reporting of battery status. Call this function check_battery. Now all that needs to be done is to call this function every second. This can be accomplished by using a workqueue as described in class but an equally simple solution is to invoke a kernel thread which loops indefinitely with a 1 second delay followed by a call to check_battery. Create the kernel thread in the init module function with
   ts = kthread_run(batt_thread, NULL, "battcheck");
where ts is of type struct task_struct*, "battcheck" is just the name of the thread, batt_thread is a function of prototype int (*)(void*) which runs when the thread is created, and NULL just means that no arguments are passed to the thread function. The function batt_thread might be written something like this:
   int batt_thread(void *data){
      while(!kthread_should_stop()) {
         msleep(1000);
         check_battery();      
      }
      return 0;
   }
where msleep causes a 1000 msec = 1 sec delay and is defined in linux/delay.h, and kthread_should_stop is defined in linux/kthread.h. The latter function returns true (and terminates the thread) if kthread_stop (which is defined in linux/kthread.h) is called. That should happen in the module exit function.

Note: the above is inspired by the solution submitted by John Eversole

Structures:
    acpi_status     32 bit integer, defined in acpi/acpi.h
   
   typedef u32 acpi_status;  /* All ACPI Exceptions */
    acpi_string     just char*, defined in acpi/acpi.h
   
   typedef char* acpi_string;  /* Null terminated ASCII string */
    acpi_handle     just void*, defined in acpi/acpi.h
   
   typedef void* acpi_handle;  
    acpi_object_type     a number corresponding to a type, defined in acpi/acpi.h
   
   typedef u32 acpi_object_type;
    acpi_object     defined in acpi/acpi.h, could be a string, buffer, package, reference, processor, etc.
   
   union acpi_object {
      acpi_object_type type;
      struct { acpi_object_type type; u64 value; } integer;
      struct { acpi_object_type type; u32 length; char* pointer; } string;
      struct { acpi_object_type type; u32 length; u8* pointer; } buffer;
      struct { acpi_object_type type; u32 cnt; union acpi_object* elements; } package;
      struct { acpi_object_type type; acpi_object_type s; acpi_handle hdl; } reference;
      struct { acpi_object_type type; u32 pid; acpi_io_address blk; u32 l; } processor;
      struct { acpi_object_type type; u32 sys_lvl; u32 res_odr; } power_resource;
   };
    acpi_buffer     defined in acpi/acpi.h
   
   struct acpi_buffer {
      acpi_size length;
      void *pointer;
   };

Functions:
    acpi_get_handle     Inputs: object to search under, name, return_object;
Search for a caller-specified name in the name space. The caller can restrict the search region by specifying a non NULL parent. The parent value is itself of type acpi_handle.
    acpi_evaluate_object     Inputs: object handle, object pathname, parameters, output buffer;
Find and evaluate the given object, passing the given parameters, if necessary. One of "handle" or "pathname" must be non-null.

Example:
   
   acpi_status status;
   acpi_handle handle;
   union acpi_object* result;
   struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
   int var_a, var_b;

   status = acpi_get_handle(NULL, "\\_PR_.CPU0", &handle);
   status = acpi_evaluate_object(handle, "_PSD", NULL, &buffer);
   result = buffer.pointer;
   if (result) {
      var_a = result->package.elements[0].integer.value;
      var_b = result->package.elements[2].integer.value;
      printk(KERN_INFO "var_a=%d var_b=%d\n", var_a, var_b);
      kfree(result);
   }

An Applet:
    The code Batt.java is a simple Java program that can display the results of the kernel module battery probes. It just needs to be hooked up to a proc file that is set up to transfer info from kernel space to user space. Compile with
   javac Batt.java
and run with
   java Batt
You should see this:

if files battery_info.txt and battery_stat.txt, which are provided for testing, are in the same directory from which Batt.java was run - check the ACPI spec on _BST and _BIF to see the meaning of the fields. This probably will not work on a windows machine.