Sunday, August 17, 2014

I2C Tutorial

What is I2C?
        I2C (Inter-Integrated circuit), invented by Philips semiconductor in 1982. I2C is mainly use to connect system control devices such temperature sensors, voltage monitors, this bus mainly take cares of the system health.

Why I2C? do we have any other bus for this purpose?
        Yes, we have SPI (serial peripheral bus), for this purpose, but compare to I2C, SPI has few draw backs so i2c is the better option to choose.

What are the drawbacks with SPI compare to I2C?
·         I2C uses two pins to communicate (SDA, SCL).
·         SPI needs four pins (MOSI, MISO, SS, CLK) to communicate, if the slave devices are more then we need extra pins (SS)
·         I2C supports multi masters, SPI doesn’t
Does i2c synchronous or asynchronous?
        I2C is synchronous bus, since it uses clock line to communicate. Serial ports are asynchronous, because it sends all the bits at a time without waiting.

What is the I2C speed?
·       100 Khz
·       400 Khz
·       3Mhz

        We can configure the i2c bus speed by configuring the FDR(Frequency divide register) from processor.

Basic I2C communication protocol?

        When the master (your controller) wishes to talk to a slave it begins by issuing a start sequence on the I2C bus. A start sequence is one of two special sequences defined for the I2C bus, the other being the stop sequence. The start sequence and stop sequence are special in that these are the only places where the SDA (data line) is allowed to change while the SCL (clock line) is high. When data is being transferred, SDA must remain stable and not change whilst SCL is high. The start and stop sequences mark the beginning and end of a transaction with the slave device.




All I2C addresses are either 7 bits or 10 bits. The use of 10 bit addresses is rare and is not covered here. All of our modules and the common chips you will use will have 7 bit addresses. This means that you can have up to 128 devices on the I2C bus, since a 7bit number can be from 0 to 127. When sending out the 7 bit address, we still always send 8 bits. The extra bit is used to inform the slave if the master is  writing to it or reading from it. If the bit is zero the master is writing to the slave. If the bit is 1 the master is reading from the slave. The 7 bit address is placed in the upper 7 bits of the byte and the Read/Write (R/W) bit is in the LSB (Least Significant Bit).


The placement of the 7 bit address in the upper 7 bits of the byte is a source of confusion for the newcomer. It means that to write to address 21, you must actually send out 42 which is 21 moved over by 1 bit.
1.Send a start sequence
2.Send the I2C address of the slave with the R/W bit low (even address)
3.Send the internal register number you want to write to
4.Send the data byte
5.Send the stop sequence.
As an example, address of 0xE0. you would write 0x51 to the command register at 0x00 like this:
1. Send a start sequence
2. Send 0xE0 ( I2C address of the SRF08 with the R/W bit low (even address)
3. Send 0x00 (Internal address of the command register)
4. Send 0x51 (The command to start the SRF08 ranging)
5. Send the stop sequence.

Read from Device.

1. Send a start sequence
2. Send 0xC0 ( I2C address of the CMPS03 with the R/W bit low (even address)
3. Send 0x01 (Internal address of the bearing register)
4. Send a start sequence again (repeated start)
5. Send 0xC1 ( I2C address of the CMPS03 with the R/W bit high (odd address)
6. Read data byte from CMPS03
7. Send the stop sequence.

Thursday, August 7, 2014

Memory mapped i/o VS Port mapped i/o



                                    
                                      In memory-mapped systems, the I/O device is accessed like it is a part of the memory. Load and Store commands are executed for reading from and writing to I/O devices, just like they are used for the memory (port-mapped has special commands for I/O). This means I/O devices use the same address bus as memory, meaning that CPU can refer to memory or the I/O device based on the value of the address. This approach requires isolation in the address space: that is, addresses reserved for I/O should not be available to physical memory.
Below is an image of a simple, basic computer system. The case is much more complicated in contemporary systems.
enter image description here


Port-Mapped I/O

According to Wikipedia
Port-mapped I/O often uses a special class of CPU instructions specifically for performing I/O. This is found on Intel microprocessors, with the IN and OUT instructions. These instructions can read and write one to four bytes (outb, outw, outl) to an I/O device. I/O devices have a separate address space from general memory, either accomplished by an extra "I/O" pin on the CPU's physical interface, or an entire bus dedicated to I/O. Because the address space for I/O is isolated from that for main memory, this is sometimes referred to as isolated I/O.

As for the advantages and disadvantages: since the peripheral devices are slower than the memory, sharing data and address buses may slow the memory access. On the other hand, by the I/O simplicity memory-mapped systems provide, CPU requires less internal logic and this helps for faster, cheaper, less power consuming CPUs to be implemented. The logic is similar to that of RISC systems: reduce the complexity, get a more dedicated and a robust system which comes quite handy for embedded systems, for example.
On the contrary (again from Wiki):
Port-mapped I/O instructions are often very limited, often providing only for simple load and store operations between CPU registers and I/O ports, so that, for example, to add a constant to a port-mapped device register would require three instructions: read the port to a CPU register, add the constant to the CPU register, and write the result back to the port.
I strongly recommend that you read that wiki article for further information.

To answer one of your questions:
What or where am I writing to if it's not in memory?
You are writing to the registers of the I/O interface through the data bus, which later (when ready) sends the data to the actual I/O device. Below is an image of an example I/O device interface.

Friday, April 18, 2014

Interrupts in Linux

What is an interrupt in linux?
       An interrupt is simply a signal that the hardware can send when it wants the processor’s attention.
Why we need interrupt?
        We need interrupt to save the CPU cycle. For an instance, we have a keyboard, when we press any key, we need response, CPU can’t wait for our input all the time. So the keyboard driver registers an interrupt handler, so that we press a key, the interrupt will signal to CPU.
How to register interrupt hanlder?
Interupt handler must be register in kernel module. we can register the handler in init or open. I would suggest to put it in open().
Here is the example:
int request_irq (
unsigned int  
irq,

irq_handler_t  
handler,

unsigned long  
irqflags,

const char *  
devname,

void *  
dev_id);



void my_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
    printk("Interrupt should be handled there\n");
}

static int __init
my_init(void)
{
    unsigned int irq;
    unsigned int irqflags;
    int ret;

    irq=68;
    irqflags=IRQF_SHARED | IRQF_NO_SUSPEND;
    ret = request_irq(irq, my_interrupt,
            irqflags, "my_dev", NULL);
    if (ret!=0) {
            printk("ERROR: Cannot request IRQ %d", irq);
           
    }
}
static void __exit
my_exit(void)
{
    unsigned int irq;
    irq=68;
    free_irq(irq, NULL);
    printk("CLCDINT_EXIT\n");
}


Parameters for request_irq:

unsigned int irq
          The interrupt number being requested.

irq_hander_t
          The pointer to the handling function being installed.

unsigned long flags
          The bit mask, to manage the interrupt

const char *dev_name
          The string passed to request_irq is used in /proc/interrupts to show the owner of the interrupt

void *dev_id
          The set to NULL, if the interrupt line is not shared, if interrupt line is shared then, it has the pointer to provate data to identify which device is interrupting.

unsigned long flags: SA_INTERRUPT
          When set, this indicates a “fast” interrupt handler. Fast handlers are executed with interrupts disabled on the current processor.

unsigned long flags: SA_SHIRQ
          This bit signals that the interrupt can be shared between devices.

unsigned long flags: SA_SAMPLE_RANDOM
          This bit indicates that the generated interrupts can contribute to the entropy pool used by /dev/random and /dev/urandom. These devices return truly random numbers when read and are designed to help application software choose secure keys for encryption. Such random numbers are extracted from an entropy pool that is contributed by various random events. If your device generates interrupts at truly random times, you should set this flag. 



Wednesday, April 9, 2014

Simple char driver and user space application

Is there any different between kernel 2.4 and kernel 2.6 for writing the kernel module?

       Yes, there are differents. In kernel 2.4 we use register_chrdev() to allocate and register the file operations structure. But this method is history now. Below are the type and functions introduced in kernel 2.6

dev_t, register_chrdev_region, alloc_chrdev_region.


Why we don’t use register_chrdev()?

register_chrdev() still works in kernel 2.6, but the problem in kernel 2.4 was, we have limited major number allocation. We had only 8bit for both major and minor number. So total we could have only 255 major or minor numbers. Where as in new methods, we have 32 bit for major and minor numbers.(12 bits for major, 20 bits for minor).

What is the different between register_chrdev_region and alloc_chrdev_region?

        register_chrdev() basically allocates major number and registers the file operation structure. But register_chrdev_region and alloc_chrdev_region only allocates major number. (register_chrdev_region allocates static major number. Alloc_chrdev_region allocates dynamic major number).


What is MAJOR and MINOR numbers?

        In unix everything is file, the kernel device is also consider as file aka special file. These special files are located in /dev.
You can do open, read, write, these devices, by doing this you can really access the devices.

        Ok, Now consider you have five devices say simple timer, each timer has one register, by reading that you can see the different time zone.

        We need only one driver, but five devices./dev/time1, /dev/time2, so on. So when you open /dev/time1 to read the india time. Your kernel module open should call, because there must be many kernel module running in our kernel, and each has open call.

        In our kernel module we register our fops structure with our own major number. And by doing mknod /dev/time1 c 249 1 we map the node to major number.

        249 1- /dev/time1 - our fops

         
1 is minor number, because we five devices so minor would be 1, 2, 3, 4, 5.
Main purpose of major number is, we can have many devices with the same major number.

Simple driver with static major no allocation.
#include <linux/module.h>
#include <linux/kernel.h>       //for printk
#include <linux/init.h>         //for __init __exit
#include <linux/fs.h>           //for file_operations

dev_t dev;

static int __init mymodule_init(void)
{
        int ret_val;
        dev = MKDEV(248, 0);
        ret_val = register_chrdev_region(dev, 1, "mydev");
        if(ret_val < 0){
                printk("\n Error: register_chrdev_region \n");
                return -1;
        }
        printk("\n Major No: %d Minor_no : %d \n", MAJOR(dev), MINOR(dev));
        return 0;
}

static void __exit mymodule_exit(void)
{
        unregister_chrdev_region(dev, 1);
        printk ("Unloading my module.\n");
        return;
}

module_init(mymodule_init);
module_exit(mymodule_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("R.Kalimuthu");

output:

make
insmod test_module.ko
dmesg | tail -20

Major No: 248 minor_no : 0


What is dev_t?

       dev_t is the data type introduced in kerne 2.6. its 32 bit, first 12 bits holds the major number and remaining 20 bits holds the minor number.
Example:
        dev_t dev;
        dev = 0x 0F800001
        MAJOR(dev) = 0x0F800001 >> 20 = 0x0F8 = 248
        MINOR(dev) = 0x0F800001  & 0x000FFFFF = 0x1 = 1
What is the maximum major number?

       dev_t can hold 12 bits for major number so we can have 4095 major number, if we give more than that, then kernel will allocate random number.

What are the parameter for register_chrdev_region()?

dev_t, number of consecutive major numbers, dev name.


Can I give any number to for major number allocation, I mean less than 4095?

No, you can see /proc/devices to know the major number which was already allocated, so you should not give the same number which was already allocated. You can choose any number which was not allocated.

How do I cross compile this module for powerpc?

Cross compile is bit different, then user space program. We need to have changes in makefile

Makefile:

obj-m := test_module.o
all:
        #make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
        make -C /vobs/linux_2_6_34/ M=$(PWD) modules

clean:
        #make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
        make -C /vobs /linux_2_6_34/ M=$(PWD) clean


the lines commented out is for x86. Basically we need to give a kernel path which was compiled for powerpc.

Compiling also bit different, refer below.


make CROSS_COMPILE=/vobs/toolchains/ppc/gcc-3.4.6-glibc-2.3.6/powerpc-linux/bin/powerpc-linux- ARCH=powerpc 


How can I allocate the major number dynamically?

        You can allocate the major number and minor number dynamically during the execution. Refer below code.

#include <linux/module.h>
#include <linux/kernel.h>       //for printk
#include <linux/init.h>         //for __init __exit
#include <linux/fs.h>           //for file_operations

dev_t dev;

static int __init mymodule_init(void)
{
        int ret_val;
        ret_val = alloc_chrdev_region(&dev, 0, 1, "mydev_dynamic");
        if(ret_val < 0){
                printk("\n Error: alloc__chrdev_region \n");
                return -1;
        }
        printk("\n Major No: %d Minor_no : %d \n", MAJOR(dev), MINOR(dev));
        return 0;
}

static void __exit mymodule_exit(void)
{
        unregister_chrdev_region(dev, 1);
        printk ("Unloading my module.\n");
        return;
}

module_init(mymodule_init);
module_exit(mymodule_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("R.Kalimuthu");

output
make
insmod test_module.ko
dmesg | tail -1
 Major No: 226 Minor_no : 0


alloc_chrdev_region() allocates dynamic major number. It accepts dev_t, starting minor number, no.of minors, device name.


Does alloc_chrdev_region() or register_chrdev_region() register the file operation structure?

        No, these two function just allocates major number and registers the device, whereas the old register_chrdev() allocates major number and registers the file operations structure and the device.

What is file operations structure?

        File operations structure is available in linux/fs.h, this structure has the field for open, read, write callbacks ponters. By registering this structure in your kernel, you can access the devices from the user space program.

How to register file operations structure with the kernel?

        Below is code.

#include  linux/module.h
#include linux/kernel.h       //for printk
#include linux/init.h        //for __init __exit
#include linux/fs.h           //for file_operations
#include linux/cdev.h         //for cdev struct

dev_t dev;
struct cdev *my_cdev;

static int my_open(struct inode *inode, struct file  *file)
{
        printk("\n my_open \n");
        return 0;
}


static struct file_operations fops = {
        .open = my_open
};

static int __init mymodule_init(void)
{
        int ret_val;
        ret_val = alloc_chrdev_region(&dev, 0, 1, "mydev_dynamic");
        if(ret_val < 0){
                printk("\n Error: alloc__chrdev_region \n");
                return -1;
        }
        printk("\n Major No: %d Minor_no : %d \n", MAJOR(dev), MINOR(dev));

        my_cdev = cdev_alloc();
        my_cdev->owner = THIS_MODULE;
        my_cdev-&gtops = &fops;

        cdev_add(my_cdev, dev, 1);
        return 0;
}

static void __exit mymodule_exit(void)
{
        unregister_chrdev_region(dev, 1);
        cdev_del(my_cdev);
        printk ("Unloading my module.\n");
        return;
}

module_init(mymodule_init);
module_exit(mymodule_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("R.Kalimuthu");


What is cdev?

        cdev is newly introduced in kernel 2.6. This cdev structure is an internal representation of a char device. It has a member field as struct file operations pointer. You have to create and populate your file operations structure and assign the address of that structure to this member.

# include
struct cdev
{
    struct kobject kobj;
    struct module* owner;
    const struct file_operations* ops;
    struct list_head list;
    dev_t dev;
    unsigned int count;
};







What is cdev_init and cdev_alloc?

cdev_alloc() allocates cdev object and return to you. Then you need to give fops pointer
cdev_init() will be used, if you manually allocate using kmalloc, in this way you don’t need to assign fops manualy.
Example:
1)
        struct cdev *cdev;
        cdev = cdev_alloc();
        cdev->fops = &fops
               
2)
                struct foo {
                struc cdev cdev;
                int  flag;
        };

        foo f ;
                f= kmalloc(100);
                cdev_init(f.cdev, &fops);          
   
        Here is the complete kernel module and the user application.

#include <linux/module.h>
#include <linux/kernel.h>       //for printk
#include <linux/init.h>         //for __init __exit
#include <linux/fs.h>           //for file_operations
#include <linux/cdev.h>         //for cdev struct
#include <linux/types.h>        //for ssize_t
#include <linux/slab.h>         //for kmalloc
#include <asm/uaccess.h>        //for copy_to_user
#define ADD 0x1
#define SUB 0x2
#define MUL 0x3

dev_t dev;
struct cdev *my_cdev;
char *ptr;
struct foo {
        int a;
        int b;
        int c;
};

static int my_open(struct inode *inode, struct file  *file)
{
        printk("\n Device Major : %d \n", imajor(inode));
        printk("\n Device Minor : %d \n", iminor(inode));
        ptr = kmalloc(10, GFP_KERNEL);
        return 0;
}
static int my_release(struct inode *inode, struct file *file)
{
        kfree(ptr);
        printk("\n Device Released and freed memory \n");
        return 0;
}
static ssize_t my_read(struct file *filp,char *buffer,
                                size_t length,loff_t *offset)
{
        copy_to_user(buffer, ptr, length);
        return 0;
}
static ssize_t my_write(struct file *filp, const char *buffer,
                                size_t length, loff_t *offset)
{
        printk("\n Receiced String :%s \n", buffer);
        copy_from_user(ptr, buffer, length);
        return length;
}
static long my_ioctl(struct file *filp, unsigned int cmd,
                                        unsigned long args)
{
        struct foo f;
        switch(cmd){
                case ADD:
                        copy_from_user(&f, (struct foo *)args, sizeof(struct foo));
                        f.c = f.a + f.b;
                        copy_to_user((struct foo *)args, &f, sizeof(struct foo));
                break;
                case SUB:
                        copy_from_user(&f, (struct foo *)args, sizeof(struct foo));
                        f.c = f.a - f.b;
                        copy_to_user((struct foo *)args, &f, sizeof(struct foo));
                break;
                case MUL:
                        copy_from_user(&f, (struct foo *)args, sizeof(struct foo));
                        f.c = f.a * f.b;
                        copy_to_user((struct foo *)args, &f, sizeof(struct foo));
                break;
                default:
                        copy_from_user(&f, (struct foo *)args, sizeof(struct foo));
                        f.c = 0;
                        copy_to_user((struct foo *)args, &f, sizeof(struct foo));
                break;

        }
        return 0;
}

static struct file_operations fops = {
        .open = my_open,
        .read = my_read,
        .write = my_write,
        .release = my_release,
        .unlocked_ioctl = my_ioctl
};

static int __init mymodule_init(void)
{
        int ret_val;
        ret_val = alloc_chrdev_region(&dev, 0, 1, "mydev_dynamic");
        if(ret_val < 0){
                printk("\n Error: alloc__chrdev_region \n");
                return -1;
        }
        printk("\n Major No: %d Minor_no : %d \n", MAJOR(dev), MINOR(dev));

        my_cdev = cdev_alloc();
        my_cdev->owner = THIS_MODULE;
        my_cdev->ops = &fops;

        cdev_add(my_cdev, dev, 1);
        return 0;
}

static void __exit mymodule_exit(void)
{
        unregister_chrdev_region(dev, 1);
        cdev_del(my_cdev);
        printk ("Unloading my module.\n");
        return;
}

module_init(mymodule_init);
module_exit(mymodule_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("R.Kalimuthu");


User Application:


#include<stdio.h>
#include<fcntl.h>
#define ADD 0x1
#define SUB 0x2
#define MUL 0x3
#define UNKNOWN 0x4

struct foo{
        int a;
        int b;
        int c;
};
int main()
{
struct foo f;
int fd;
char buff[15];
fd = open("/dev/mydev_dynamic", O_RDWR);
write(fd, "Hello world", 12);
read(fd, buff, 12);
printf("%s", buff);
f.a = 15;
f.b = 10;
ioctl(fd, ADD, &f);
printf("\n Add : %d \n", f.c);
ioctl(fd, SUB, &f);
printf("\n Sub : %d \n", f.c);
ioctl(fd, MUL, &f);
printf("\n mul : %d \n", f.c);
ioctl(fd, UNKNOWN, &f);
printf("\n Unknown : %d \n", f.c);
close(fd);
return 0;
}


Output:

./a.out
Hello world
 Add : 25

 Sub : 5

 mul : 150

 Unknown : 0

dmesg | tail -10

 Major No: 226 Minor_no : 0

 Device Major : 226

 Device Minor : 0

 Receiced String :Hello world


 Device Released and freed memory


How does the open call in user context move to kernel context?

Basically, the open call triggers the soft interrupt we say that 0x80, this interrupt is for system call(). This is initialized in the interrupt vector table during set_system_gate (0x80, &system_call. This system call interrupt forces the context switch.


Why copy_to_user or copy_from_user? Can we use memcpy?

Hmm, I guess the memcpy also works fine, but the main reason for copy_to_user or copy_from_user is mainly for protection. We don’t want a user space program to make a panic in kernel. copy_to_user or copy_from_user checks the memory before read or write.