Linux字符设备驱动
一、首先列出程序的全部源码
/root/drivers/freg
----freg.c
----freg.h
----Makefile
freg.h
#ifndef _FAKE_REG_H_ #define _FAKE_REG_H_ #include <linux/cdev.h> #include <linux/semaphore.h> #define FREG_DEVICE_NODE_NAME "freg" #define FREG_DEVICE_FILE_NAME "freg" #define FREG_DEVICE_PROC_NAME "freg" #define FREG_DEVICE_CLASS_NAME "freg" struct fake_reg_dev { int val; struct semaphore sem; struct cdev dev; }; #endif
freg.c
#include <linux/init.h> #include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/proc_fs.h> #include <linux/device.h> #include <asm/uaccess.h> #include "freg.h" static int freg_major = 0; static int freg_minor = 0; static struct class* freg_class = NULL; static struct fake_reg_dev* freg_dev = NULL; static int freg_open(struct inode* inode, struct file* filp); static int freg_release(struct inode* inode, struct file* filp); static ssize_t freg_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos); static ssize_t freg_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos); static struct file_operations freg_fops = { .owner = THIS_MODULE, .open = freg_open, .release = freg_release, .read = freg_read, .write = freg_write, }; static int freg_open(struct inode* inode, struct file* filp) { struct fake_reg_dev* dev; dev = container_of(inode->i_cdev, struct fake_reg_dev, dev); filp->private_data = dev; return 0; } static int freg_release(struct inode* inode, struct file* filp) { return 0; } static ssize_t freg_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) { ssize_t err = 0; struct fake_reg_dev* dev = filp->private_data; if(down_interruptible(&(dev->sem))) { return -ERESTARTSYS; } if(count < sizeof(dev->val)) { goto out; } if(copy_to_user(buf, &(dev->val), sizeof(dev->val))) { err = -EFAULT; goto out; } err = sizeof(dev->val); out: up(&(dev->sem)); return err; } static ssize_t freg_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos) { struct fake_reg_dev* dev = filp->private_data; ssize_t err = 0; if(down_interruptible(&(dev->sem))) { return -ERESTARTSYS; } if(count != sizeof(dev->val)) { goto out; } if(copy_from_user(&(dev->val), buf, count)) { err = -EFAULT; goto out; } err = sizeof(dev->val); out: up(&(dev->sem)); return err; } static int __freg_setup_dev(struct fake_reg_dev* dev) { int err; dev_t devno = MKDEV(freg_major, freg_minor); memset(dev, 0, sizeof(struct fake_reg_dev)); cdev_init(&(dev->dev), &freg_fops); dev->dev.owner = THIS_MODULE; dev->dev.ops = &freg_fops;//回调函数 err = cdev_add(&(dev->dev),devno, 1); if(err) { return err; } init_MUTEX(&(dev->sem));//信号量 dev->val = 0; return 0; } static int __init freg_init(void) { int err = -1; dev_t dev = 0;/一个32位的数,其中12位用来表示主设备号,而其余20位用来表示次设备号 struct device* temp = NULL; printk(KERN_ALERT"Initializing freg device.\n"); err = alloc_chrdev_region(&dev, 0, 1, FREG_DEVICE_NODE_NAME);//动态分配主设备号和次设备号 if(err < 0) { printk(KERN_ALERT"Failed to alloc char dev region.\n"); goto fail; } freg_major = MAJOR(dev);//获取主设备号 freg_minor = MINOR(dev);//获取次设备号 freg_dev = kmalloc(sizeof(struct fake_reg_dev), GFP_KERNEL); if(!freg_dev) { err = -ENOMEM; printk(KERN_ALERT"Failed to alloc freg device.\n"); goto unregister; } err = __freg_setup_dev(freg_dev); if(err) { printk(KERN_ALERT"Failed to setup freg device: %d.\n", err); goto cleanup; } freg_class = class_create(THIS_MODULE, FREG_DEVICE_CLASS_NAME); if(IS_ERR(freg_class)) { err = PTR_ERR(freg_class); printk(KERN_ALERT"Failed to create freg device class.\n"); goto destroy_cdev; } temp = device_create(freg_class, NULL, dev, "%s", FREG_DEVICE_FILE_NAME); if(IS_ERR(temp)) { err = PTR_ERR(temp); printk(KERN_ALERT"Failed to create freg device.\n"); goto destroy_class; } printk(KERN_ALERT"Succedded to initialize freg device.\n"); return 0; destroy_class://错误处理机制 class_destroy(freg_class); destroy_cdev: cdev_del(&(freg_dev->dev)); cleanup: kfree(freg_dev); unregister: unregister_chrdev_region(MKDEV(freg_major, freg_minor), 1); fail: return err; } static void __exit freg_exit(void) { dev_t devno = MKDEV(freg_major, freg_minor);//获取dev_t变量 printk(KERN_ALERT"Destroy freg device.\n"); if(freg_class) { device_destroy(freg_class, MKDEV(freg_major,freg_minor));//删除/dev/freg文件 class_destroy(freg_class); } if(freg_dev) {//删除字符设备 cdev_del(&(freg_dev->dev)); kfree(freg_dev); } unregister_chrdev_region(devno, 1);//取消原来申请的主设备号和次设备号 } MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Fake Register Driver");//如果是android内核源码中,make menuconfig出现的界面会有此项 module_init(freg_init);//insmod执行这个函数 module_exit(freg_exit);//rmmod执行这个函数
Makefile
obj-m :=freg.o
第1步:建立Linux驱动骨架(装载和卸载Linux驱动)
任何类型的程序都需要一个开始执行的函数,比如C语言需要一个main函数,Linux提供两个函数module_init和module_exit来分别处理驱动程序的初始化和退出。module_init相当于C语言的main函数。
module_init(freg_init); module_exit(freg_exit);
第2步:注册和注销设备文件
注册设备文件在本程序中分为3小步:
1、动态分配主设备号和次设备号;
dev_t dev = 0; err = alloc_chrdev_region(&dev, 0, 1, FREG_DEVICE_NODE_NAME);//动态分配主设备号和次设备号dev_t是一个32位的数,其中12位用来表示主设备号,而其余20位用来表示次设备号。
MAJOR(dev_t dev)用来获取主设备号,MINOR(dev_t dev)用来获取次设备号。
MKDEV(int major,int minor)将主设备号和次设备号转换成dev_t类型。
2、根据得到的主设备号和次设备号注册字符设备;
static int __freg_setup_dev(struct fake_reg_dev* dev) { int err; dev_t devno = MKDEV(freg_major, freg_minor); memset(dev, 0, sizeof(struct fake_reg_dev)); cdev_init(&(dev->dev), &freg_fops); dev->dev.owner = THIS_MODULE; dev->dev.ops = &freg_fops; err = cdev_add(&(dev->dev),devno, 1); if(err) { return err; } init_MUTEX(&(dev->sem)); dev->val = 0; return 0; }
3、为驱动程序建立文件/dev/freg,来操作字符设备驱动。
freg_class = class_create(THIS_MODULE, FREG_DEVICE_CLASS_NAME); if(IS_ERR(freg_class)) { err = PTR_ERR(freg_class); printk(KERN_ALERT"Failed to create freg device class.\n"); goto destroy_cdev; } temp = device_create(freg_class, NULL, dev, "%s", FREG_DEVICE_FILE_NAME); if(IS_ERR(temp)) { err = PTR_ERR(temp); printk(KERN_ALERT"Failed to create freg device.\n"); goto destroy_class; }
前面3步的逆操作。
static void __exit freg_exit(void) { dev_t devno = MKDEV(freg_major, freg_minor); printk(KERN_ALERT"Destroy freg device.\n"); if(freg_class) { device_destroy(freg_class, MKDEV(freg_major,freg_minor)); class_destroy(freg_class); } if(freg_dev) { cdev_del(&(freg_dev->dev)); kfree(freg_dev); } unregister_chrdev_region(devno, 1); }
第3步:指定与驱动相关信息
MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Fake Register Driver");
static int freg_open(struct inode* inode, struct file* filp); static int freg_release(struct inode* inode, struct file* filp); static ssize_t freg_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos); static ssize_t freg_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos); static struct file_operations freg_fops = { .owner = THIS_MODULE, .open = freg_open, .release = freg_release, .read = freg_read, .write = freg_write, }; static int freg_open(struct inode* inode, struct file* filp) { struct fake_reg_dev* dev; dev = container_of(inode->i_cdev, struct fake_reg_dev, dev); filp->private_data = dev; return 0; } static int freg_release(struct inode* inode, struct file* filp) { return 0; } static ssize_t freg_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) { ssize_t err = 0; struct fake_reg_dev* dev = filp->private_data; if(down_interruptible(&(dev->sem))) { return -ERESTARTSYS; } if(count < sizeof(dev->val)) { goto out; } if(copy_to_user(buf, &(dev->val), sizeof(dev->val))) { err = -EFAULT; goto out; } err = sizeof(dev->val); out: up(&(dev->sem)); return err; } static ssize_t freg_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos) { struct fake_reg_dev* dev = filp->private_data; ssize_t err = 0; if(down_interruptible(&(dev->sem))) { return -ERESTARTSYS; } if(count != sizeof(dev->val)) { goto out; } if(copy_from_user(&(dev->val), buf, count)) { err = -EFAULT; goto out; } err = sizeof(dev->val); out: up(&(dev->sem)); return err; }可能有的读者注意到了。freg.c中的所有函数、变量都声明成了static。这是因为在C语言中用static声明函数、变量等资源,系统会将这些函数和变量单独放在内存的某一个区域,直到程序完全退出,否则这些资源不会被释放。Linux驱动一旦装载,除非手动卸载或关机,驱动会一直驻留内存,因此这些函数和变量资源会一直在内存中。也就是说多次调用这些资源不用再进行压栈、出栈操作了。有利于提高驱动的运行效率。
第5步:编写MakeFile文件
obj-m :=freg.o
第6步:编译Linux驱动模块
sudo su 进入root模式
make -C /usr/src/linux-headers-2.6.32-21-generic/ M=/root/drivers/freg/
第7步:安装和卸载Linux驱动
安装freg驱动:
cd /root/drivers/freg/
insmod freg.ko
查看freg是否成功安装:
lsmod | grep freg
显示如下:
freg 2188 0
查看/dev/freg信息
ls -l /dev/freg
显示如下:
crw-rw---- 1 root root 248, 0 2014-06-06 23:50 /dev/freg
卸载Linux驱动
rmmod freg
查看由Linux驱动输出的日志信息
dmesg | grep freg
最后三行,显示如下:
[ 7722.273680] Initializing freg device. [ 7722.274095] Succedded to initialize freg device. [10766.282006] Destroy freg device.
三、编写C语言程序验证freg驱动程序
/root/drivers/freg
----hello.c
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #define DEVICE_NAME "/dev/freg" int main(int argc, char** argv) { int fd = -1; int val = 0; fd = open(DEVICE_NAME, O_RDWR); if(fd == -1) { printf("Failed to open device %s.\n", DEVICE_NAME); return -1; } printf("Read original value:\n"); read(fd, &val, sizeof(val)); printf("%d.\n\n", val); val = 5; printf("Write value %d to %s.\n\n", val, DEVICE_NAME); write(fd, &val, sizeof(val)); printf("Read the value again:\n"); read(fd, &val, sizeof(val)); printf("%d.\n\n", val); close(fd); return 0; }
编译gcc hello.c -o hello
运行./hello,结果如下:
Read original value: 0. Write value 5 to /dev/freg. Read the value again: 5.
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。