首页 > 代码库 > Linux字符设备驱动
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 = http://www.mamicode.com/dev;>
Makefileobj-m :=freg.o二、编写Linux驱动程序的步骤
第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");第4步:指定回调函数
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 = http://www.mamicode.com/dev;> 可能有的读者注意到了。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.