# 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

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