一、字符驱动框架
问:应用程序open、read、write如何找到驱动程序的open、read、write函数?
答:应用程序的open、read、write是在C库里面实现的,它里面通过swi val指令去触发一个异常,这个异常就会进入到内核空间,在内核的异常处理函数里面有根据我们传入的val来决定调用system_open还是system_read、system_write函数,这些函数根据我们打开不同的文件、不同的文件就有不同的属性(例如设备类型(字符设备还是块设备还是网络设备)、主设备号),根据这些属性找到更底层的驱动程序。
问:什么是主设备号,什么是次设备号
答:Linux主设备号用来区分不同硬件设备类型,如LED和串口之间的区别;
Linux次设备号用来区分不同硬件设备,如串口1和串口2之间的区别;
问:通过什么样的方法来找到驱动程序的open函数
答:通过一个注册函数+设备节点
注册函数如下(旧的注册函数,新的以后再说):
register_chrdev(unsigned int major, const char * name, const struct file_operations * fops)
参数1:主设备号(重要)
参数2:名字(不重要)
参数3:file_operations结构体(重要)
设备节点:
可以手工创建也可以自动创建,这里暂且只说手工创建
mknod /dev/xxx c 252 0
创建一个名为/dev/xxx的设备节点,c表示字符设备节点,252是主设备号,0是次设备号。
问:应用程序一般是由main函数开始执行,那么驱动程序一般是先执行什么?
答:通过一个宏,指定驱动程序的入口函数,当装载驱动时就会执行入口函数。
例如:module_init(first_drv_init); //用于修饰入口函数
自然地,驱动程序的出口函数,则是在卸载驱动时就会执行出口函数。
例如:module_exit(first_drv_exit); //用于修饰出口函数
驱动源程序如下:
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/module.h>int major;
static int first_drv_open(struct inode * inode, struct file * filp)
{printk("first_drv_open\n");return 0;
}
static int first_drv_write(struct file * file, const char __user * buffer, size_t count, loff_t * ppos)
{printk("first_drv_write\n");return 0;
}/* File operations struct for character device */
static const struct file_operations first_drv_fops = {.owner = THIS_MODULE,.open = first_drv_open,.write = first_drv_write,
};/* 驱动入口函数 */
static int first_drv_init(void)
{/* 主设备号设置为0表示由系统自动分配主设备号 */major = register_chrdev(0, "first_drv", &first_drv_fops);return 0;
}/* 驱动出口函数 */
static void first_drv_exit(void)
{unregister_chrdev(major, "first_drv");
}module_init(first_drv_init); //用于修饰入口函数
module_exit(first_drv_exit); //用于修饰出口函数 MODULE_AUTHOR("LWJ");
MODULE_DESCRIPTION("Just for Demon");
MODULE_LICENSE("GPL"); //遵循GPL协议
Makefile源码如下:
ifneq ($(KERNELRELEASE),)obj-m := first_drv.oelseKDIR := /home/opt/EmbedSky/linux-2.6.30.4all:make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
clean:rm -f *.ko *.o *.mod.o *.mod.c *.symversendif
测试程序如下:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main(void){int fd;int val = 1;fd = open("/dev/xxx",O_RDWR); //打开名为 /dev/xxx 的设备节点if(fd < 0){printf("open error\n");}write(fd,&val,4);return 0;
}
开发板上的测试步骤如下:
[WJ2440]# insmod first_drv.ko
[WJ2440]# ./first_test
open error //没有创建设备节点
[WJ2440]# cat proc/devices
Character devices:1 mem4 /dev/vc/04 tty5 /dev/tty5 /dev/console5 /dev/ptmx7 vcs10 misc13 input14 sound29 fb81 video4linux89 i2c90 mtd
116 alsa
128 ptm
136 pts
180 usb
188 ttyUSB
189 usb_device
204 tq2440_serial
252 first_drv //我们创建的字符设备,主设备号252
253 usb_endpoint
254 rtcBlock devices:
259 blkext7 loop8 sd31 mtdblock65 sd66 sd67 sd68 sd69 sd70 sd71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
[WJ2440]# mknod /dev/xxx c 252 0 //手动创建一个字符设备节点
[WJ2440]# ls -l /dev/xxx
crw-r--r-- 1 root root 252, 0 Jan 1 20:49 /dev/xxx
[WJ2440]# ./first_test //有设备节点后成功我们的应用程序运行了
first_drv_open
first_drv_write
[WJ2440]#
本文参考:
https://blog.csdn.net/lwj103862095/article/details/17468587