MIPS datapath

In this exercise, we will use a MIPS simulator in order to better understand the datapath of simple assembly instructions such as add , sub , lw , sw , beq and j .

Basic Arithmetic:

We execute the following assembly program on the MIPS simulator.

------------------------------------------------------------
.register $s0 -10
.register $s1 5
.register $s2 3

main:    add $s3, $s1, $s1    #5+5
            add $s4, $s1, $s0    #5+-10
            sub $s5, $s1, $s2    #5-3
            sub $s6, $s2, $s1    #3-5
            and $s7, $s2, $s1    #011 AND 101
exit:
------------------------------------------------------------


We will describe the datapath followed by the first add instruction.

Step 1: The first instruction is located in the instruction memory, InstrMemory . The address of the first instruction, PC = 0, is sent to InstrMemory and ALUIncr.

Step 2: The instruction pointed to by PC is sent to the Spliter .

Step 3: The RegWrite flag is asserted by Control . Hence, the result of the addition will be written to the destination register s3.

Step 4: The Spliter splits the instruction into four different parts. The function field of the instruction is sent to ALUControl . The register numbers to be read and the register number to be written are sent to the register file, Registers .

Step 5: ALUcontrol translates the function field into an ALU operation code. For the add operation, the code passed to Main ALU is 010.

Step 6: The register file transmits the two input values, the value of which are stored in the input registers from Step 4, to the ALU.

Step 7: The ALU carries out the addition and sends the result to the register file. The result is stored in the ouput register whose number was determined during Step 4. The zero flag is not set, since the result of the ALU operation, 4 + 4, is non-zero.

Step 8,9: The value of PC is incremented by four in the ALUIncr unit. The program counter now points to the next instruction to be executed.


The datapath of the sub and and operations are similar to the one decribed above. The operation code of the sub operation is 110, while the and operation has operation code 000.

Load/store operations

In order to illustrate the load and store operation, we execute the following assembly code

------------------------------------------------------------
.register $s0 -10
.register $s1 4

main:    sw $s0, 4($s1) # [8]=-10
            lw $s2, 4($s1) # s2=-10
exit:
------------------------------------------------------------



The first operation stores the value of s0 at the memory location s1+4 (word aligned). The second operation copies the data stored at s1+4 into s2.

Step 1,2: The first instruction is fetched from instruction memory and sent to the spliter.

Step 3: Register numbers correponding to s0 and s1 are sent to the register file. Both are used as input. The operation opcode is sent to the control unit, Control . The function field of the operation is sent to the ALU control, ALUControl .

Step 4: The control flags corresponding to a store operation are set. Since the value of s0 will be written to a memory location (and not to a register), the MemWrite flag is asserted, while the RegWrite and MemRead flags are deasserted.

Step 5: ALUCtrl passes the operation code for an addition (010) to the ALU. The ALU will add the offset value (4) to the memory address contained in s1.

Step 6,7: The value of s0 (-10) is passed to Memory . This value will be stored at the memory location computed by the ALU (addition). The first operand of the addition, i.e. the value of s1, is passed to the ALU.

Step 8,9: The second operand of the addition is determined by the ALUsrc flag. Since the flag is asserted, the second operand of the ALU is the signed-extended 16-bit offset (4). Otherwise, the second operand would be the second register read from Registers , s1.

Step 10: The destination memory address is computed by the ALU (4+s1=4+4=8).

Step 11,12: The value of s0, -10, is now stored at memory location 8.

Step 13,14,15,16: Finally, the program counter is incremented by 4.


The load operation follows the exact same data path. The only noteworthy difference is the value of the three flags MemWrite , RegWrite and MemRead . Since the value stored at memory location 4+s1 is read and then stored in a register , the control unit asserts MemRead and RegWrite while the MemWrite flag is desasserted.



Branching operations

Let us execute the following assembly code.

------------------------------------------------------------
.register $s0 7
.register $s1 7


main:    beq $s0, $s1, exit
           add $s2, $s1,$s0
exit:     sub $s2, $s0, $s1
------------------------------------------------------------


The main difference between a regular sub operation and a beq operation is the incrementation of the program counter. For both operations, the zero flag is asserted when the two input registers, s0 and s1, contain the same value. Moreover, for the beq operation, the BranchIn flag is asserted as well. As a consequence, the PCSrc flag, which is the logical and of the zero and the BranchIn flags, is also asserted. The PCSrc flag controls whether the program counter is incremented by 4 ( PCSrc desasserted) or by the 16-bit signed-extended offset contained in the instruction ( PCSrc asserted). Hence, the branch is taken if and only if the value of s0 and s1 are equal.

Jump operation

The jump operation is the simplest one.

------------------------------------------------------------
.register $s0 7
.register $s1 7

main:     j exit
            sub $s2, $s1,$s0
exit:    
------------------------------------------------------------



The instruction opcode (000010) is passed to the control unit, which then asserts the Jump flag. When the Jump flag is asserted, the program counter is incremented by the 26-bit immediate offset value contained in the instruction (multiplied by 4). Hence, the second assembly line is never executed, since the program counter jumps directly to the location, in instruction memory, pointed to by the exit label.

GPIO Linux Device Driver + Interrupts (Part 3)

We now demonstrate how to add some IOCTL system calls to our device driver. The new IOCTL calls enable a user application to
automatically toggle a GPIO output between 0 and 1. Moreover, the user application will be able to decide the frequency of the
toggling.

First, we modify the file_operations structure by adding an IOCTL entry.

struct file_operations mygpio_Fops = {
    .owner   = THIS_MODULE,
    .open    = mygpio_open,
    .release = mygpio_release,
    .write   = mygpio_write,
    .read    = mygpio_read,
    .ioctl   = mygpio_ioctl,
}; 


Here, mygpio_ioctl is our implementation of the IOCTL system call:

int mygpio_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){

 switch(cmd) {
  case GPIO_START_TIMER: 
   printk(KERN_ALERT "START TIMER.\n");
   mytimer_add(inode);
   break;

  case GPIO_STOP_TIMER: 
   printk(KERN_ALERT "STOP TIMER.\n");
   mytimer_del();
   break;

  case GPIO_SET_FREQ:
   printk(KERN_ALERT "SET FREQ to %lu.\n",arg);
   tdelay = HZ*arg/1000;
   break;

  default:
   printk(KERN_ALERT "Invalid IOCTL command %d.\n",cmd);
   return -EINVAL;
 }
 return 0;
}

The symbolic names of the commands involved are defined in a separate header file ioctlnum.h . The associated values are
computed using a special 'magic number', as described in [LDD, p. 138]. We let 'x' be our magic number, since this value is not
used by any drivers listed in Documentation/ioctl-number.txt.

ioctlnum.h :
#ifndef IOCTLNUM_H_
#define IOCTLNUM_H_

#include <linux/ioctl.h>

//Use 'x' as magic number. 
//Apparently unused by other drivers, cf. Documentation/ioctl-number.txt
#define GPIO_IOC_MAGIC 'x'

#define GPIO_START_TIMER _IO(GPIO_IOC_MAGIC, 1)
#define GPIO_STOP_TIMER  _IO(GPIO_IOC_MAGIC, 2)
#define GPIO_SET_FREQ  _IOW(GPIO_IOC_MAGIC, 3, int)

#define GPIO_IOC_MAX 4

#endif


The implementation of the three commands GPIO_START_TIMER, GPIO_STOP_TIMER and GPIO_SET_FREQ is described
below:

GPIO_START_TIMER: Using a kernel timer, the toggling of an opened GPIO can be carried out at regular intervals. Recall
that at any moment, the current value of the internal kernel counter is stored in the read-only variable jiffies (defined
in linux/jiffies.h). A kernel timer can be configured by filling up a timer_list with 1) the jiffies value
when the timer is expected to run, 2) the function to be called when the timer goes off and 3) the arguments passed to that
function. Then, a call to add_timer adds the future event (the function call) to the list of events maintained by the
kernel, c.f. [LDD, p. 199].

int mytimer_add(struct inode *inode){

 unsigned long j = jiffies;

 //Avoid data race on flag_timer using a Mutex.
 if ( !down_trylock(&timer_mutex) )
  goto busy;
 
 if (flag_timer != 0)
  goto busy;
 
 flag_timer = 1;
 
 mytimer_list.function = mytimer_fn;
 mytimer_list.data = MINOR(inode->i_rdev);
 mytimer_list.expires = j + tdelay;

 add_timer(&mytimer_list);
 return 0;

busy:
 return -EBUSY;

}

When (approximately) tdelay HZ have elapsed, the function mytimer_fn is called by the kernel. The MINOR value of
the GPIO being toggled is passed as an argument.

void mytimer_fn(unsigned long arg){
 
 gpio_set_value(gpio[arg].num,value);
 ++value;
 value %=2;
 
 mytimer_list.expires += tdelay;
 add_timer(&mytimer_list);
 }

After having updated the expires field of mytimer_list, a new invocation of add_timer notifies the kernel that
mytimer_fn must be called again when a delay of tdelay HZ has elapsed.

GPIO_STOP_TIMER: The user application may stop a running kernel timer by issuing the IOCTL GPIO_STOP_TIMER command.
When del_timer is invoked, the next scheduled call to mytimer_fn is canceled, thereby breaking the periodic renewal
of the kernel timer.

int mytimer_del(){

 //Avoid data race on flag_timer using a Mutex.
 if ( !down_trylock(&timer_mutex) )
  goto busy;

 if (flag_timer == 0)
  goto busy;

 del_timer(&mytimer_list);
 flag_timer = 0;
 
 return 0;

busy:
 return -EBUSY;

}


To ensure that only one kernel timer is running at the time, we use a special flag, flag_timer, which is set to 1
when an IOCTL GPIO_START_TIMER command is issued. A subsequent IOCTL GPIO_STOP_TIMER command clears the flag. Note that a
data race on flag_timer may occur when two different user threads try to initiate the GPIO toggling concurrently. To avoid
this situation, we protect mytimer_add and mytimer_del with a mutex, timer_mutex.


GPIO_SET_FREQ: The frequency with which mytimer_fn is called, after a successful IOCTL GPIO_START_TIMER
command, is determined by the value of tdelay. A delay of arg milliseconds is equivalent to HZ*arg/1000 HZs.



Links to the driver, the header and the sample application.

GPIO Linux Device Driver + Interrupts (Part 2)

We slightly modify the device driver described in the previous post. We demonstrate how to trigger an interrupt 
when the value of a GPIO port changes. As a consequence, any read() operation on one of the GPIOs will block
until new data is ready to be read.
 
/*
In the following program, we implement an interrupt driven device driver as described in  [LDD, Chapter 3,6,10].

First, connect GPIO 130 and GPIO 134 on the devkit add-on board using the diagrams found at 
http://devkit8000addon.wikispaces.com/Interfaces and http://devkit8000addon.wikispaces.com/PCB.

In VMware, open 3 terminals.

In terminal 1, establish a connection to devkit using
> ssh root@10.9.8.2

In terminal 2, do the same
> ssh root@10.9.8.2

In terminal 3, compile the module and transfer it to devkit
> cat > Makefile
obj-m := mygpio_intDOC.o
(CTRL-C)
> make -C /home/stud/source/linux-2.6.28-omap/ M=`pwd` modules
> scp mygpio_intDOC.ko  root@10.9.8.2:/home/root/


Back to terminal 1, insert the module into the kernel and check that no error occurred
> insmod mygpio_intDOC.ko
> dmesg

Create two nodes, one for input, GPIO 130, and one for output, GPIO 134.
> mknod gpio0 c 62 0
> mknod gpio4 c 62 4

In terminal 2, read data from GPIO 130 using
> cat /dev/gpio0


In terminal 1, set GPIO 134 to 0 or 1 (low/high) using
> echo 0 > /dev/gpio4
> echo 1 > /dev/gpio4
> echo 0 > /dev/gpio4
> echo 1 > /dev/gpio4
> echo 1 > /dev/gpio4
> echo 0 > /dev/gpio4
> etc.


In terminal 2, the new value on GPIO 130/134 is written to the screen each time the value changes from 0 
to 1 or from 1 to 0. In other terms, two successive 'echo 1 > /dev/gpio4' will not trigger the interrupt. 
Finally, remove the module from the kernel by writing in terminal 1 or 2
> rmmod mygpio_intDOC.ko
> dmesg
NB: In terminal 2, 'cat /dev/gpio0' must be aborted using CTRL-C before the module is removed from the 
kernel. Otherwise, rmmod will fail (check with dmesg).

*/

/*
Bibliography:
[LDD] Linux Device Drivers, 3rd Ed.
*/
 
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>

#include <linux/types.h>
#include <linux/interrupt.h>


#define MYGPIO_MAJOR  62
#define MYGPIO_MINOR   0
#define MYGPIO_CHS     8
#define MAXLEN   512
#define MYGPIO_MIN_IN 0
#define MYGPIO_MAX_IN 3
#define MYGPIO_MIN_OUT 4
#define MYGPIO_MAX_OUT 7

static struct cdev MyGpioDev;
struct file_operations mygpio_Fops;
static int devno;


struct gpioPort{
  unsigned int num;
  enum direnu { in, out } dir;
  const char label[10]; // Very important to specify a size.
} gpio[] = {
  {130, in, "gpio0_0"},  //Read only
  {131, in, "gpio0_1"},  //Read only
  {132, in, "gpio0_2"},  //Read only
  {133, in, "gpio0_3"},  //Read only
  {134, out, "gpio0_4"}, //Write only
  {135, out, "gpio0_5"}, //Write only
  {136, out, "gpio0_6"}, //Write only
  {137, out, "gpio0_7"}  //Write only
};


/*
We use this structure to keep track of which GPIO ports are associated to a corresponding interrupt 
number.
*/
int gpio_irq[MYGPIO_CHS] = {-1};

/* Define and initialize a structure wq of type wait_queue_head_t, c.f. [LDD, p. 149]
*/
static DECLARE_WAIT_QUEUE_HEAD(wq);
static int flag = 0;

/*****************************************************************/
/************************** IRQ HANDLER **************************/
/*****************************************************************/
static irqreturn_t irqHandler(int irq, void *dev_id, struct pt_regs *regs){

  printk(KERN_ALERT "IRQ handler.\n");

  flag=1;
  /*In this driver module, all read() operations on input GPIOs will block and the calling process will be 
  asked to wait in the wait queue, wq. Whenever the value of an input GPIO changes (from 0 to 1 or from 1 
  to 0), sleeping processes must be notified using wake_up_interruptible(), c.f. [LDD, p.149-150].
  */
  wake_up_interruptible(&wq);
  /*
  When a sleeping process wakes up, it checks that the value of 'flag' is equal to 1. If flag==1, the 
  process sets the flag back to 0 and reads the value present on the input GPIO. Otherwise, the process 
  must sleep some more until wake_up_interruptible() is called again. In this module, a data race on 
  'flag' may occur, c.f. [LDD, p.149-150].
  */

  return IRQ_HANDLED;
}

/*****************************************************************/
/*************************** INIT ********************************/
/*****************************************************************/
static int __init mygpio_init(void)
{
 int err, i;
    printk(KERN_ALERT "Mygpio Module Inserted\n");
 devno = MKDEV(MYGPIO_MAJOR, MYGPIO_MINOR);

 if((err=register_chrdev_region(devno, MYGPIO_CHS , "myGpio"))<0){
      printk(KERN_ALERT "Can't Register Major no: %i\n", MYGPIO_MAJOR);
      return err;
    }
 cdev_init(&MyGpioDev, &mygpio_Fops);
 MyGpioDev.owner = THIS_MODULE;
 
    for(i=0; i< MYGPIO_CHS; ++i) {
  if ( (err=gpio_request(gpio[i].num, gpio[i].label))<0){
   printk(KERN_ALERT "Error %d requesting GPIO %d.\n",err,i);
   gpio_free(gpio[i].num);
   goto fail_init;
  }
    }

 for(i=0; i< MYGPIO_CHS; ++i) {
      if ( (gpio[i].dir == in) && (err=gpio_direction_input(gpio[i].num))<0 ) {
 printk(KERN_ALERT "Error %d marking direction on GPIO %d.\n",err,i);
 goto fail_dir;
 }
      else if ( (gpio[i].dir == out) && !(err=gpio_direction_output(gpio[i].num,0))<0 ){
 printk(KERN_ALERT "Error %d marking direction on GPIO %d.\n",err,i);
 goto fail_dir;
      }
    }

 err = cdev_add(&MyGpioDev, devno, MYGPIO_CHS );
    if (err) {
        printk (KERN_ALERT "Error %d adding MyGpio device\n", err);
        goto fail_add;
    }

    //No errors..
    return 0;

fail_add:

fail_dir:
 for(i=0; i< MYGPIO_CHS; ++i) {
  gpio_free(gpio[i].num);
 }

fail_init:
    cdev_del(&MyGpioDev);
    unregister_chrdev_region(devno, MYGPIO_CHS );
    return err;
}


/*****************************************************************/
/*************************** EXIT ********************************/
/*****************************************************************/
static void mygpio_exit(void)
{
  int err,i;
  int irq;

  printk(KERN_ALERT "Removing Mygpio Module\n");
  for(i=0; i < MYGPIO_CHS; ++i) {
 if (  (err=gpio_direction_input(gpio[i].num)) <0  ) {
  printk(KERN_ALERT "Error %d reseting GPIO %d.\n",err,i);
 }
 } 

    for(i=0; i< MYGPIO_CHS; ++i) {
  gpio_free(gpio[i].num);
 }

 
 /*
 The requested IRQs are freed when the module is removed from the kernel.
 */
 for (i=0; i<MYGPIO_CHS; ++i) {
  irq=gpio_irq[i];
  if (irq > 0) {
   printk(KERN_ALERT "Free IRQ %i.\n",irq);
   free_irq(irq, NULL);
   gpio_irq[i]=-1;
  }
    }

 cdev_del(&MyGpioDev);
 unregister_chrdev_region(devno, MYGPIO_CHS );
}


/*****************************************************************/
/*************************** OPEN ********************************/
/*****************************************************************/
int mygpio_open(struct inode *inode, struct file *filep)
{
    int major, minor;
 int irq;
 
 
    major = MAJOR(inode->i_rdev);
    minor = MINOR(inode->i_rdev);
 
 printk("Opening MyGpio Device [major], [minor]: %i, %i\n", major, minor);


 if ( major != MYGPIO_MAJOR ) {
  printk(KERN_ALERT  "Invalid major device number.\n");
  return -EFAULT;
 }

 if ( minor < MYGPIO_MINOR || minor >= MYGPIO_MINOR + MYGPIO_CHS  ) {
  printk(KERN_ALERT  "Invalid minor device number.\n");
  return -EFAULT;
 }

 /* If gpio_irq[minor] is not equal to -1, the IRQ has already been allocated to this GPIO number.
 */
 if (gpio_irq[minor] > 0)
  goto irq_ok;
 
 /*A GPIO number can be mapped to an IRQ using gpio_to_irq()
 */
 if ( (irq = gpio_to_irq(gpio[minor].num))< 0 )
  goto irq_fail;
  
 /* The interrupt is requested using request_irq(), c.f. [LDD, p. 261]. Whenever the GPIO goes from low 
to high (IRQF_TRIGGER_RISING)  or from high to low (IRQF_TRIGGER_FALLING), the corresponding interrupt is 
triggered and  irqHandler is called.
 */
 if( request_irq(irq, irqHandler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING , "GPIO", NULL) )
  goto irq_fail;
 
 gpio_irq[minor] = irq;
 printk(KERN_ALERT "IRQ request succedeed.\n");
 
irq_ok:
 if (!try_module_get(mygpio_Fops.owner))
      return -ENODEV;
 return 0;

irq_fail:
 printk(KERN_ALERT "IRQ request failed.\n");
 return -EFAULT;

}

/*****************************************************************/
/*************************** RELEASE *****************************/
/*****************************************************************/
int mygpio_release(struct inode *inode, struct file *filep)
{
 
    int minor, major;
    major = MAJOR(inode->i_rdev);
    minor = MINOR(inode->i_rdev);
 
 printk("Closing MyGpio Device [major], [minor]: %i, %i\n", major, minor);

 if ( major != MYGPIO_MAJOR ) {
  printk(KERN_ALERT "Invalid major device number.\n");
  return -EFAULT;
 }

 if ( minor < MYGPIO_MINOR || minor >= MYGPIO_MINOR + MYGPIO_CHS  ) {
  printk(KERN_ALERT  "Invalid minor device number.\n");
  return -EFAULT;
 }

 module_put(mygpio_Fops.owner);

    return 0;
}


/*****************************************************************/
/*************************** WRITE *******************************/
/*****************************************************************/
ssize_t mygpio_write(struct file *filep, const char __user *ubuf,
                  size_t count, loff_t *f_pos)
{
    int err = 0;
 int minor, len, value;
    char kbuf[MAXLEN];

    minor = MINOR(filep->f_dentry->d_inode->i_rdev);
    printk(KERN_ALERT "Writing to MyGpio [Minor] %i \n", minor);

    if ( minor < MYGPIO_MIN_OUT || minor > MYGPIO_MAX_OUT) {
  printk(KERN_ALERT "Invalid minor device number.\n");
  return -EFAULT;
 }

 len = count < MAXLEN ? count : MAXLEN;
 err = copy_from_user(kbuf, ubuf, len);
 if (err != 0) {
  printk(KERN_ALERT "copy_from_user failed with error %d.\n",err);
  goto fail;
 }
  
    kbuf[len] = '\0';
    printk("String from user: %s\n", kbuf);
 sscanf(kbuf,"%i", &value);
    value = value > 0 ? 1 : 0;
 
 gpio_set_value(gpio[minor].num,value);

 return len;

fail:
  return -EFAULT;
}

/*****************************************************************/
/*************************** READ *******************************/
/*****************************************************************/
ssize_t mygpio_read(struct file *filep, char __user *buf,
                 size_t count, loff_t *f_pos)
{
 char readBuf[MAXLEN];
 int len, result=-1, minor;
 int err;

    minor = MINOR(filep->f_dentry->d_inode->i_rdev);
    printk(KERN_ALERT "Reading from MyGpio [Minor] %i \n", minor);
 
 if ( minor < MYGPIO_MIN_IN || minor > MYGPIO_MAX_IN ) {
  printk(KERN_ALERT "Invalid minor device number.\n");
  return -EFAULT;
 }
  
 /*
 Wait until a write() action triggers an interrupt, c.f. irqHandler() defined above.
 */
 wait_event_interruptible(wq,flag != 0);
 flag = 0;
  
    result = gpio_get_value(gpio[minor].num);

    len=sprintf(readBuf,"%i", result);
    len++;

    err = copy_to_user(buf, readBuf, len);
 if (err != 0) {
  printk(KERN_ALERT "copy_to_user failed with error %d.\n",err);
  goto fail;
 }

 return len;

fail:
 return -EFAULT;
}


struct file_operations mygpio_Fops =
{
    .owner   = THIS_MODULE,
    .open    = mygpio_open,
    .release = mygpio_release,
    .write   = mygpio_write,
    .read    = mygpio_read,
};


module_init(mygpio_init); //... is invoked when the module is loaded into the kernel (insmod)
module_exit(mygpio_exit); //... is invoked when the module is removed from the kernel (rmmod)

MODULE_DESCRIPTION("My GPIO Module");
MODULE_AUTHOR("Jérémy");
MODULE_LICENSE("GPL");

GPIO Linux Device Driver + Interrupts (Part 1)

/*
In the following, we implement a linux character driver as described in [LDD, Chapter 
3].

First, connect GPIO 130 and GPIO 134 on the devkit add-on board using the diagrams found 
at http://devkit8000addon.wikispaces.com/Interfaces and 
http://devkit8000addon.wikispaces.com/PCB.

In VMware, open 3 terminals.

In terminal 1, establish a connection to devkit using
> ssh root@10.9.8.2

In terminal 2, do the same
> ssh root@10.9.8.2

In terminal 3, compile the module and transfer it to devkit
> cat > Makefile
obj-m := mygpioDOC.o
(CTRL-C)
> make -C /home/stud/source/linux-2.6.28-omap/ M=`pwd` modules
> scp mygpioDOC.ko  root@10.9.8.2:/home/root/


Back to terminal 1, insert the module into the kernel and check that no error occurred
> insmod mygpioDOC.ko
> dmesg

Create two nodes, one for input, GPIO 130, and one for output, GPIO 134.
> mknod gpio0 c 62 0
> mknod gpio4 c 62 4

In terminal 2, read data from GPIO 130 using
> cat /dev/gpio0
NB: this is not exactly the right way to read from the GPIO but it is enough for our 
purposes

In terminal 1, set GPIO 134 to 0 or 1 (low/high) using
> echo 0 > /dev/gpio4
> echo 1 > /dev/gpio4
> etc.

In terminal 2, sequences of 0s or 1s are output depending on the value written to GPIO 
134. Use CTRL-C to end the output. Finally, remove the module from the kernel by writing 
in terminal 1 or 2
> rmmod mygpioDOC.ko
> dmesg


*/

/*
Bibliography:
[LDD] Linux Device Drivers, 3rd Ed.
*/


#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/types.h>

/*
The major number MYGPIO_MAJOR identifies our driver and the minor numbers MYGPIO_MINOR, 
MYGPIO_MINOR+1,..., MYGPIO_MINOR + MYGPIO_CHS -1 identify each of the eight GPIOs.
*/
#define MYGPIO_MAJOR  62
#define MYGPIO_MINOR   0
#define MYGPIO_CHS     8

/*

The eight GPIO's are accessed through character device files gpio0,...,gpio7 that must be 
created in the /dev directory.

> sudo mknod /dev/gpio0 c 62 0
> sudo mknod /dev/gpio1 c 62 1
...
> sudo mknod /dev/gpio7 c 62 7

*/

#define MAXLEN            512
#define MYGPIO_MIN_IN    0
#define MYGPIO_MAX_IN    3
#define MYGPIO_MIN_OUT    4
#define MYGPIO_MAX_OUT    7

/*
Char DEVices are represented in the kernel by means of a cdev structure defined in 
linux/cdev.h, c.f. [LDD, p. 55]. This structure must be initialized in the init() 
function below
*/
static struct cdev MyGpioDev;

/*
Each field in the file_operations structure points to a function supported by the device. 
The eight GPIO character devices support as a minimum the 4 file operations open(), 
release(), read() and write(), which are implemented below.
*/
struct file_operations mygpio_Fops;


static int devno;

/*
The gpioPort structure relates the 8 GPIOs with their respective MINOR number and I/O 
direction.
*/
struct gpioPort{
  unsigned int num;
  enum direnu { in, out } dir;
  const char label[10]; // Very important to specify a size.
} gpio[] = {
  {130, in, "gpio0_0"},  //Read only
  {131, in, "gpio0_1"},  //Read only
  {132, in, "gpio0_2"},  //Read only
  {133, in, "gpio0_3"},  //Read only
  {134, out, "gpio0_4"}, //Write only
  {135, out, "gpio0_5"}, //Write only
  {136, out, "gpio0_6"}, //Write only
  {137, out, "gpio0_7"//Write only
};


/*****************************************************************/
/*************************** INIT ********************************/
/*****************************************************************/
static int __init mygpio_init(void)
{
/*
The purpose of the init() function is to register the functionalities implemented by our 
module.
*/

    int err, i;


    printk(KERN_ALERT "Mygpio Module Inserted\n");

    /*
    The kernel uses the dev_t type (linux/types.h) to hold the major and minor numbers of 
a device.    The MKDEV macro    constructs the dev_t number of a device given its major and 
minor numbers.
    */
    devno = MKDEV(MYGPIO_MAJOR, MYGPIO_MINOR);

    /*
    The device numbers that we wish to work with are allocated using the 
register_chrdev_region() function. Here, we allocate device numbers devno,...,devno + 
MYGPIO_CHS -1, or in other terms, major number 62 and minor numbers 0,...,7. The name, 
"myGpio", associated with this device range will appear in /proc/devices, c.f. [LDD, p. 
45].
    */
    if((err=register_chrdev_region(devno, MYGPIO_CHS , "myGpio"))<0){
      printk(KERN_ALERT "Can't Register Major no: %i\n", MYGPIO_MAJOR);
      return err;
    }

    
    /*
    The cdev structure used by the kernel to represent the device must be initialized 
with the supported file operations, open(), release(), read() and write(). Pointers to 
those file operations are contained in the mygpio_Fops structure. The owner field should 
be set manually to THIS_MODULE, c.f. [LDD, p. 56]
    */
    cdev_init(&MyGpioDev, &mygpio_Fops);
    MyGpioDev.owner = THIS_MODULE;
    
    /*
    Each of the 8 GPIOs are now requested explicitly in order to ensure that they have 
not already been claimed in another module. According to Documentation/gpio.txt, the call 
to gpio_request is optional. It gives us information on whether the GPIOs can be accessed 
without any conflicts with other drivers present on the system.
    */
    for(i=0; i< MYGPIO_CHS; ++i) {
        if ( (err=gpio_request(gpio[i].num, gpio[i].label))<0){
            printk(KERN_ALERT "Error %d requesting GPIO %d.\n",err,i);
            gpio_free(gpio[i].num);
            goto fail_init;
        }
    }


    /*
    The first four GPIOs will be used for input, i.e. they do not support the write 
operation. The last four GPIOs are only used for output operations, i.e. they do not 
support the read operation.
    */
    for(i=0; i< MYGPIO_CHS; ++i) {
      if ( (gpio[i].dir == in) && (err=gpio_direction_input(gpio[i].num))<0 ) {
    printk(KERN_ALERT "Error %d marking direction on GPIO %d.\n",err,i);
    goto fail_dir;
    }
      else if ( (gpio[i].dir == out) && !(err=gpio_direction_output(gpio[i].num,0))<0 ){
    printk(KERN_ALERT "Error %d marking direction on GPIO %d.\n",err,i);
    goto fail_dir;
      }
    }
    
    /*
    We are now ready to handle operations on the devices. The kernel is informed of the 
MyGpioDev structure. If the call to cdev_add() fails, the device has not been added to 
the system. If the call succeeds, the operations registered above with cdev_init() can 
now be called by the kernel. Therefore, the call to cdev_add should only be made after 
the character device and the GPIOs have been properly initialized with cdev_init and 
gpio_direction_*
    */
    err = cdev_add(&MyGpioDev, devno, MYGPIO_CHS );
    if (err) {
        printk (KERN_ALERT "Error %d adding MyGpio device\n", err);
        goto fail_add;
    }

    //No errors..
    return 0;

fail_add:

fail_dir:
    for(i=0; i< MYGPIO_CHS; ++i) {
        gpio_free(gpio[i].num);
    }


 fail_init:
    cdev_del(&MyGpioDev);
    unregister_chrdev_region(devno, MYGPIO_CHS );
    return err;
}


/*****************************************************************/
/*************************** EXIT ********************************/
/*****************************************************************/
static void mygpio_exit(void)
{
  int err,i;
/*
The purpose of the exit() function is to release the resources that were previously 
allocated in the init() function
*/
    printk(KERN_ALERT "Removing Mygpio Module\n");

    /*
    The eight GPIOs are reset to their original (input) state
    */
    for(i=0; i < MYGPIO_CHS; ++i) {
        if (  (err=gpio_direction_input(gpio[i].num)) <0  ) {
            printk(KERN_ALERT "Error %d reseting GPIO %d.\n",err,i);
        }
    }

    /*
    The eight GPIOs are released. Other device drivers are now allowed to request them.
    */
    for(i=0; i< MYGPIO_CHS; ++i) {
        gpio_free(gpio[i].num);
    }

    /*
    The char device previously added to the system by calling cdev_init()/cdev_add() is 
now removed from the system.     */
    cdev_del(&MyGpioDev);
    /*
    The device numbers requested by the call to register_chrdev_region() are freed with
    */
    unregister_chrdev_region(devno, MYGPIO_CHS );

}

/*****************************************************************/
/*************************** OPEN ********************************/
/*****************************************************************/
int mygpio_open(struct inode *inode, struct file *filep)
{
    int major, minor;
    
    /*
    Fetch the actual device number from the unique inode structure that is internally 
used by the kernel to represent the device file
    */
    major = MAJOR(inode->i_rdev);
    minor = MINOR(inode->i_rdev);
    /*
    Note that [LDD] recommends the use of the two macros
    - unsigned int iminor(struct inode *inode),
    - unsigned int imajor(struct inode *inode),
    instead of MAJOR and MINOR.
    */

    printk("Opening MyGpio Device [major], [minor]: %i, %i\n", major, minor);


    /*
    As a safety measure, we check that the major and minor numbers of the character 
device, which was opened for IO, actually corresponds to one of the eight GPIOs. If not, 
something is wrong!
    */
    if ( major != MYGPIO_MAJOR ) {
        printk(KERN_ALERT  "Invalid major device number.\n");
        return -EFAULT;
    }

    //if minor < 0 or minor > 7, the minor number is invalid
    if ( minor < MYGPIO_MINOR || minor >= MYGPIO_MINOR + MYGPIO_CHS  ) {
        printk(KERN_ALERT  "Invalid minor device number.\n");
        return -EFAULT;
    }


    /*
    Whenever a user application or another module opens one of the GPIO devices for IO, 
it must hold a reference to this module. Otherwise, this module could be removed from the 
kernel (rmmod) while another module is asleep inside this one during a blocking read or 
write. According to http://www.kernel.org/pub/linux/kernel/people/rusty/modules/FAQ 
(Rusty Russel wrote the last version of module.h in which try_get_module() and 
put_module() are defined), the try_module_get() function fails "when the module is not 
ready to be entered (ie. still in init_module) or it is being removed.  This prevents you 
from entering the module as it is being discarded (init might fail, or it's being 
removed)".
    */
    if (!try_module_get(mygpio_Fops.owner))
      return -ENODEV;

    return 0;
}

/*****************************************************************/
/*************************** RELEASE *****************************/
/*****************************************************************/
int mygpio_release(struct inode *inode, struct file *filep)
{
    
    int minor, major;
    major = MAJOR(inode->i_rdev);
    minor = MINOR(inode->i_rdev);
    
    printk("Closing MyGpio Device [major], [minor]: %i, %i\n", major, minor);

    /*
    As a safety measure, we check that the major and minor numbers of the character 
device being closed actually corresponds to one of the eight GPIOs.
    */
    if ( major != MYGPIO_MAJOR ) {
        printk(KERN_ALERT "Invalid major device number.\n");
        return -EFAULT;
    }

    //if minor < 0 or minor > 7, the minor number is invalid
    if ( minor < MYGPIO_MINOR || minor >= MYGPIO_MINOR + MYGPIO_CHS  ) {
        printk(KERN_ALERT  "Invalid minor device number.\n");
        return -EFAULT;
    }

    /* The reference to this module is put back. The danger of being blocked inside this 
    module while it is destroyed is over.
    */
    module_put(mygpio_Fops.owner);

    return 0;
}


/*****************************************************************/
/*************************** WRITE *******************************/
/*****************************************************************/
ssize_t mygpio_write(struct file *filep, const char __user *ubuf,
                  size_t count, loff_t *f_pos)
{
    int err = 0;
    int minor, len, value;
    char kbuf[MAXLEN];

    /*
     * Retrieve minor number from the file structure, *filep.
     */
    minor = MINOR(filep->f_dentry->d_inode->i_rdev);
    printk(KERN_ALERT "Writing to MyGpio [Minor] %i \n", minor);


    /*
    If the minor number if strictly less than 4 or strictly greater than 7, it does not 
represent a GPIO port meant for output.
    */
    if ( minor < MYGPIO_MIN_OUT || minor > MYGPIO_MAX_OUT) {
        printk(KERN_ALERT "Invalid minor device number.\n");
        return -EFAULT;
    }


    /*
    We now demonstrate how data can be transfered from user space (user application) to 
kernel space (this driver module). copy_from_user() returns the number of bytes that were 
NOT successfully copied. Hence, a return value equal to 0 means success.
     */
    len = count < MAXLEN ? count : MAXLEN;
    // The destination buffer can only contain up to MAXLEN bytes. Avoid buffer overflows 
in kernel space, please!
    err = copy_from_user(kbuf, ubuf, len);
    if (err != 0) {
        printk(KERN_ALERT "copy_from_user failed with error %d.\n",err);
        goto fail;
    }
    
    /*
    NB: In the particular case of a GPIO device, the file pointer may be left unchanged 
after an IO operation.
    */

    /*
    The bytes transfered from user space are converted into a string and then into an 
integer value.
    */
    kbuf[len] = '\0';
    printk("String from user: %s\n", kbuf);
    sscanf(kbuf,"%i", &value);
    value = value > 0 ? 1 : 0;
    /*
    Set the GPIO port to either 0 or 1...
    */
    gpio_set_value(gpio[minor].num,value);

    /*
    Return the number of bytes copied from user space to kernel space. If copy_from_user 
failed, return -EFAULT.
    */
    return len;

fail:
     return -EFAULT;
}

/*****************************************************************/
/*************************** READ *******************************/
/*****************************************************************/
ssize_t mygpio_read(struct file *filep, char __user *buf,
                 size_t count, loff_t *f_pos)
{
    char readBuf[MAXLEN];
    int len, result=-1, minor;
    int err;
    /*
     Retrieve the minor device number from *filep
     */
    minor = MINOR(filep->f_dentry->d_inode->i_rdev);
    printk(KERN_ALERT "Reading from MyGpio [Minor] %i \n", minor);


    /*
    If the minor number is <0 or >3, it does not represent a GPIO port meant for input.
    */
    if ( minor < MYGPIO_MIN_IN || minor > MYGPIO_MAX_IN ) {
        printk(KERN_ALERT "Invalid minor device number.\n");
        return -EFAULT;
    }

    /*
    Read a single input byte from the GPIO port
    */
    result = gpio_get_value(gpio[minor].num);

    len=sprintf(readBuf,"%i", result);
    len++; // To include null-termination

    /*
    Copy string result to User Space
     */
    err = copy_to_user(buf, readBuf, len);
    if (err != 0) {
        printk(KERN_ALERT "copy_to_user failed with error %d.\n",err);
        goto fail;
    }

    return len;

fail:
    return -EFAULT;
}

/*
The file_operation structure used in init() is initialized as follows:
*/
struct file_operations mygpio_Fops =
{
    .owner   = THIS_MODULE,
    .open    = mygpio_open,
    .release = mygpio_release,
    .write   = mygpio_write,
    .read    = mygpio_read,
};
/* Each field represents a pointer to a functionality supported by the driver. The .owner 
field should be set to THIS_MODULE, c.f. [LDD, p. 53] */


module_init(mygpio_init); //... is invoked when the module is loaded into the kernel 
(insmod)
module_exit(mygpio_exit); //... is invoked when the module is removed from the kernel 
(rmmod)

MODULE_DESCRIPTION("My GPIO Module");
MODULE_AUTHOR("Jérémy");
MODULE_LICENSE("GPL");

Øvelse 4: Linux Kernemoduler

#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>

#include <string.h>
#include <errno.h>


void SetLed(int hfile, char * value);

int main()
{
    char ch;
    int hBrightness=0;
    int hTrigger=0;
    
    

    char *ON = "1";
    char *OFF = "0";
    char *NONE = "none";
    char *HEARTBEAT = "heartbeat";

    enum BOOL {false=0,true} bLoop = true;

    

    if ((hBrightness = open("/sys/class/leds/led3/brightness", O_WRONLY ) ) == -1){
        printf("Error opening Brightness file: %s\n",strerror(errno));
    }
    else {
        printf("Brightness file successfully opened.\n");
    }

    if ((hTrigger = open("/sys/class/leds/led3/trigger", O_WRONLY ) ) == -1){
        printf("Error opening Trigger file: %s\n",strerror(errno));
    }
    else {
        printf("Trigger file successfully opened.\n");
    }

    //As long as 'q' is not pressed
    while ( bLoop ) {
    
        printf("Actions on LED3:\n");
        printf("\t 0 - Turn off. \n");
        printf("\t 1 - Turn on. \n");
        printf("\t h - Heartbeat. \n");
        printf("\t n - No heartbeat. \n");
        printf("\t q - Quit. \n");
        printf("Your choice: ");
        ch=getchar();
        
        switch (ch) {
            case '0' : SetLed(hBrightness,ON); break;
            case '1' : SetLed(hBrightness,OFF); break;
            case 'h' : SetLed(hTrigger,HEARTBEAT); break;
            case 'n' : SetLed(hTrigger,NONE); break;
            case 'q' : bLoop = false; break;
            default  : break;
        }    
        //empty input buffer        
        while((ch=getchar()) != '\n'){}

    

    }



    if ( close(hBrightness) != 0  ) {
        printf("Error closing Brightness file. Error: %s\n",strerror(errno));
    }
    else {
        printf("Brightness file successfully closed.\n");
    }

    if ( close(hTrigger) != 0  ) {
        printf("Error closing Trigger file. Error: %s\n",strerror(errno));
    }
    else {
        printf("Trigger file successfully closed.\n");
    }
        
    exit(0);
}


void SetLed(int hFile, char * value){
            
        int length = strlen(value);
        int nwrite;
        if ( (nwrite = write(hFile,value,length) ) != length )    
        {
            printf("Error writing to file. Error: %s.\n",nwrite,strerror(errno));
        }

}

Øvelse 1: Endianness

Her er et adresseområde i hukommelsen:

       00  01  02  03  04   05  06  07  08  09  0A  0B  0C  0D  0E   0F 
00:  4E  55  20  4B  4A  48  20  41  20  54  41   49  56   20   55   4E
10:  4D  45  44  54  20   47  53  47  4F  44  20   41  42   45   45   46

Hukommelsen har en 8-bit atomisk størrelse, dvs. den mindste
datastørrelse, man kan tilgå, er en byte.

1) Fra adresseområdet laves en række læsninger:

A: Read32Big(0x00);    0x4E55204B
B: Read8Big(0x0A);     0x41
C: Read32Little(0x0B); 0x55205649
D: Read16Little(0x08); 0x5420
E: Read32Big(0x12);    0x44542047
F: Read32Little(0x04);  0x4120484A
G: Read8Little(0x16);   0x53
H: Read16Little(0x10); 0x454D
I: Read8Big(0x0F);       0x4E
J: Read16Big(0x17);     0x474F
K: Read8Big(0x1B);     0x41
L: Read16Little(0x19);  0x2044


2) Vi sammensætter resultaterne i rækkefølgen
A & B & I & L & C & G & D & J & E & K & F & H,
og får følgende hexadecimal udtryk
0x4E 0x55 0x20 0x4B 0x41 0x4E 0x20 0x44 0x55 0x20 0x56 0x49 0x53 0x54
0x20 0x47 0x4F 0x44 0x54 0x20 0x47 0x41 0x41 0x20 0x48 0x4A 0x45 0x4D

3) Oversættelsen af ovenstående hexadecimal udtryk til ASCII
giver: "NU KAN DU VIST GODT GAA HJEM"

4) At tilgå data med den forkerte atomiske størrelse (f.eks. 16-bit eller 32-bit) ville
resultere i en ombytning af bogstaverne i den endelige ASCII-oversættelse.