---
# System prepended metadata

title: Embedded System On Rasberry PI - Driver(I)
tags: [Embedded System]

---

# Embedded System On Rasberry PI - Driver(I)

###### tags : `Embedded System`

## Environment

- [x] PC x 1
- [X] Raspberry Pi x 1
- [X] microSD card x 1


## Goal

>1. Design a driver and mount on linux 
>2. Learn how does driver work

## What is driver
>1. A bridge between software and hardware
>2. A interface between kernel space and user space
>3. Provide a set of function calls to represent the operation of the hardware
>4. If hardware be changed , designer just need to load another driver instead of rewrite entire software program
>5. There are three main types of Linux driver, namely character, block, and network.

## Simple driver
>- Initial module 
>Similar to general C language main function
>- Open device 
>Function be called when device be opened 
>(read , write , and close is very similar)
>- I/O control
>The user can set parameters of the device through the ioctl function
>- Remove module
>The processing function executed when the driver is removed


## Implementation

1. Write a makefile  
- make file
```bash=
obj-m := hello.o
all:
    make -C <kernel-source-code’s PATH> M=$(PWD) modules
clean:
    make -C <kernel-source-code’s PATH> M=$(PWD) clean
```
2. Design a driver 
- An entire driver which including basic function like open , close , I/O control , read , and write.
- Register a driver first , then register I/O service .
```c=
// following header files are necessary
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
static ssize_t drv_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
{
    printk("device->read be called\n");
    return count;
}
static ssize_t drv_write(struct file *filp, const char *buf, size_t count, loff_t *ppos)
{
    printk("device->write be called\n");
    return count;
}
static int drv_open(struct inode *inode, struct file *filp)
{
    printk("device is opened\n");
    return 0;
}
long drv_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 
{
    printk("ioctl be called\n");
    return 0;
}
static int drv_release(struct inode *inode, struct file *filp)
{
    printk("device is closed\n");
    return 0;
}
struct file_operations drv_fops =
{
    .read=drv_read,
    .write=drv_write,
    .unlocked_ioctl=drv_ioctl,
    .open=drv_open,
    .release=drv_release,
};
#define MAJOR_NUM 60
#define MODULE_NAME "DEMO"
static int demo_init(void) {
    if (register_chrdev(MAJOR_NUM, "demo", &drv_fops) < 0) {
        printk("<1>%s: can't get major %d\n", MODULE_NAME,
        MAJOR_NUM);
        return (-EBUSY);
    }
    printk("<1>%s: started\n", MODULE_NAME);
    return 0;
}
static void demo_exit(void) {
    unregister_chrdev(MAJOR_NUM, "demo");
    printk("<1>%s: removed\n", MODULE_NAME);
}
module_init(demo_init);
module_exit(demo_exit);
```
:::info
printk() is a fuction very similar to printf().
printf() is a function called form user spce; drive is executed on kernel space, so it executes printk().
:::
3. Compile and setup
```bash=
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
// build a .ko file
$ sudo -s 
$ mknod /dev/demo c 60 0
// -> /dev/demo is device name
```
4. Test and demo
- Test file
```c=
#include <stdio.h>
int main()
{
    char buf[512];
    FILE *fp = fopen("/dev/demo", "w+");
    if (fp == NULL) {
        printf("Open device fail!\n");
        return 0;
    }
    int re ;
    re = fread(buf, sizeof(buf), 1, fp);
    printf("read %d \n" , re);
    re = fwrite(buf, sizeof(buf), 1, fp);
    printf("write %d \n" , re);
    fclose(fp);
    return 0;
}
```
- Compile by cross compiler
```shell=
$ arm-linux-gnueabihf-gcc -static -g test.c
```
- Demo
> Commands on linux 
>$ insmod 
>// mount a driver
>$ rmmod 
>// unmount a driver
>$ lsmod 
>// list all driver

![](https://i.imgur.com/B6mTP50.png)

> Execute test program
> If there is no message output, you can use "dmesg" to check out

![](https://i.imgur.com/YM85UkR.png)




