# 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)