Linux下的驱动程序也没有听上去的那么难实现,我们可以看一下helloworld这个例子就完全可以了解它的编写的方式!
我们还是先看一个这个例子,helloworld
1. [代码]helloworld.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include <linux/module.h>//与module相关的信息 #include <linux/kernel.h> #include <linux/init.h> //与init相关的函数 static int __init hellokernel_init( void ) { printk(KERN_INFO "Hello kernel!\n" ); return 0; } static void __exit hellokernel_exit( void ) { printk(KERN_INFO "Exit kernel!\n" ); } module_init(hellokernel_init); module_exit(hellokernel_exit); MODULE_LICENSE( "GPL" ); MODULE_AUTHOR( "xxxx" ); |
2. [代码]Makefile
1 2 3 4 5 6 7 8 9 | obj-m := helloworld.o PWD := $(shell pwd) all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules #TAB not space clean: rm -rf *.o *~ core .*.cmd *.mod.c ./tmp_version |
3. [代码]执行与运行结果 [1] [2] [3] [全屏预览]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 3)执行make 编译成功之后会生成相应有ko文件,也就是我们想要的驱动了 4)驱动程序的相关操作 a)查看ko模块的信息 modinfo b)插入模块 insmod helloworld.ko c)卸载模块 rmmod helloworld d)还有一个modprobe功能,以后介绍! 5)查看驱动的打印信息 使用dmesg可以查看在驱动的相关打印信息! 现在有例子是会有如下的打印内容: ---------------------log start---------------------------- [27520.195551] Exit kernel! [27948.531569] Hello kernel! ---------------------log end---------------------------- |
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
(1)驱动程序Hello.c的源代码:
#include <linux/miscdevice.h> #include <linux/delay.h> #include <asm/irq.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/delay.h> #include <linux/unistd.h> #include <linux/string.h> #include <linux/fcntl.h> #include <asm/uaccess.h> #include <linux/kdev_t.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/version.h> #include <linux/kernel.h> #include <linux/interrupt.h> #include <linux/sched.h> #include <linux/device.h> MODULE_LICENSE("Dual BSD/GPL"); int Hello_read(struct inode *inode, struct file *filp) { printk("enter cdd_open!\n"); return 0; } int Hello_write(struct file *filp, const char __user *buf, size_t count, loff_t *offset) { printk("enter cdd_write!\n"); return 0; } static struct file_operations io_dev_fops = { .owner = THIS_MODULE, .read = Hello_read, .write = Hello_write, }; int __init hello_init(void) { register_chrdev(123, "Hello",&io_dev_fops); printk("Ralink gpio driver initialized\n"); return 0; } void __exit hello_exit(void) { unregister_chrdev(123, "Hello"); printk("Ralink hello driver exited\n"); } module_init(hello_init); module_exit(hello_exit);
驱动是靠近底层,与硬件和内核打交道的。内核通过驱动将请求转化为命令操作硬件设备。因此驱动程序向上要给内核提供接口,向下要能够读写硬件的寄存器,能够控制硬件。驱动首先要告诉内核它的存在,也就是注册。代码中的注册函数在下面这个函数中,同时这个函数就是这个驱动的入口,当我们利用insmod命令挂在驱动的时候,首先会进入到这个函数中:
int __init hello_init(void) { register_chrdev(123, "Hello",&io_dev_fops); printk("Ralink gpio driver initialized\n"); return 0; }
register_chrdev(123, "Hello",&io_dev_fops);123为主设备号“Hello”是在/proc/devices下面显示的该驱动的名称,io_dev_fops是一个file_operations结构体
static struct file_operations io_dev_fops = { .owner = THIS_MODULE, .read = Hello_read, .write = Hello_write, };
这个结构体相当于一个连接,通过它可以找到在这个驱动中的其他的函数Hello_read、Hello_write(这两个函数分别对应着应用层程序中的函数write、read,当在应用层调用这两个函数,就会通过一系列的动作找到对应的驱动程序中的Hello_read、Hello_write函数),这函数里面就可以写一些对于硬件的操作,因为我写的是最简单的驱动程序,因此没有对于写硬件的操作。
下面是出口函数,当使用rmmod卸载函数时会进入到下面这个函数中
void __exit hello_exit(void) { unregister_chrdev(123, "Hello"); printk("Ralink hello driver exited\n"); }
unregister_chrdev(123, "Hello");是注销驱动程序的注册信息的。
每个设备文件对应有两个设备号:一个是主设备号,标识该设备的种类,也标识了该设备所使用的驱动程序;另一个是次设备号,标识使用同一设备驱动程序的不同硬件设备。设备文件的主设备号必须与设备驱动程序在登录该设备时申请的主设备号一致,否则用户进程将无法访问到设备驱动程序。
一般说来,PCI卡通常都属于字符设备
(2)测试程序:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main() { int fd; int value=1; fd=open("/dev/hello",O_RDWR); if (fd<0) { printf("open led_driver error"); exit(1); } write(fd,&value,4); return 0; }
(3)测试
1、利用tftp将文件下载到开发板上
后面的IP地址是tftp服务器的地址。
2、查看一下/proc/devices,没有此驱动程序要申请的主设备号123
3、利用insmod命令安装驱动后再次查看,有主设备号123 Hello,说明驱动安装成功,并且输出Ralink gpio driver initialized,说明进入到驱动的初始化函数中。
4、测试驱动
运行test发现出现错误:
查找原因是因为在/dev下没有设备节点/dev/Hello
利用mknod新建设备节点并再次查看/dev,有设备节点hello
再次运行test:
测试程序调用驱动成功,输出enter cdd_write,说明成功进入调用到Hello_write函数!
5、卸载驱动
到此为止,第一个驱动程序编写成功!