linux 字符驱动是最基本的设备驱动程序,接下来的所有内容都会基于这个Demo,当然也会不断的修改其中的BUG。(早晨逛超市突然想出一句话,“看别人吃肉,自己在流口水的同时,也需要想想他是怎么捕猎的”)。开个玩笑。
进入正题:
#include <linux/module.h> /* 最基本的文件,支持动态添加和卸载模块 */
#include <linux/fs.h> /* 包含了文件操作相关struct的定义,例如大名鼎鼎的struct file_operations; 同时包含了定义struct inode结构体、MINOR、MAJOR的头文件 */
#include <linux/slab.h> /* 定义了kmalloc相关的函数,要用到kmalloc 时需要include此头文件 */
#include <linux/errno.h> /* 包含了对返回值的宏定义,这样用户程序可以用perror输出错误信息 */
#include <linux/types.h> /* 对一些特殊类型的定义,例如dev_t, off_t, pid_t */
#include <linux/cdev.h> /* cdev 结构及相关函数的定义 */
#include <linux/wait.h> /* 等代队列相关头文件,同时它包含了自旋锁的头文件 */
#include <linux/init.h> /* 初始化头文件 */
#include <linux/kernel.h> /* 驱动要写入内核,与内核相关的头文件 */
#include <linux/uaccess.h> /* 包含了copy_to_user、copy_from_user等内核访问用户进程内存地址的函数定义 */
#include <linux/device.h> /* 包含了device、class 等结构的定义 */
#include <linux/io.h> /* 包含了ioremap、iowrite等内核访问IO内存等函数的定义 */
#include <linux/ioctl.h> /* _IO(type, nr)等定义 */
#include <linux/semaphore.h> /* 使用信号量必须的头文件 */
#include <asm/delay.h> /* delay */
//#define TEST_QUEUE_DEBUD/*日志控制定义 */
#ifdef TEST_QUEUE_DEBUD
#define LOG(fmt,args...) printk(fmt,##args)
#else
#define LOG(fmt,args...)
#endif
#define DEVICE_NAME "test_wait_queue" /*设备名字*/
#define FILE_NAME "test_wait_queue0"
static int test_open(struct inode *inode, struct file* filp);
static int test_close(struct inode *inode, struct file *filp);
static ssize_t test_read(struct file *file_p, char __user * buf, size_t len, loff_t * ppos);
static ssize_t test_write(struct file *file_p, char __user * buf, size_t len, loff_t * ppos);
static char *g_buf;
static struct test_wait_queue_t
{
struct cdev cdev; /*字符设备结构体*/
struct class *queue_class; /*创建设备文件 */
struct device *queue_device; /*创建设备文件 */
};
static struct file_operations fops={
.owner = THIS_MODULE,
.open = test_open,
.release = test_close,
.read = test_read,
.write = test_write,
};
static struct test_wait_queue_t test_wait_queue; /* 设备相关结构体实例 */
static int test_open(struct inode *inode, struct file* filp) /*打开设备 */
{
LOG("Enter %s",__FUNCTION__);
return 0;
}
static int test_close(struct inode *inode, struct file *filp) /*关闭设备 */
{
LOG("Enter %s",__FUNCTION__);
return 0;
}
static ssize_t test_read(struct file *file_p, char __user * buf, size_t len , loff_t * ppos)
{
int ret;
static int num = 0;
int length=0;
LOG("Enter %s",__FUNCTION__);
LOG(g_buf);
length=strlen(g_buf)+1;
copy_to_user(buf,g_buf,length);
memset(g_buf,0, 100);
return length;
}
static ssize_t test_write(struct file *file_p, char __user * buf, size_t len, loff_t * ppos)
{
static int num = 0;
LOG("Enter %s",__FUNCTION__);
memset(g_buf,0, 100);
copy_from_user(g_buf,buf,len);
LOG(g_buf);
return len;
}
static int test_queue_init(void)
{
int ret;
LOG("Enter %s\n",__FUNCTION__);
cdev_init(&test_wait_queue.cdev,&fops); /*注册字符设备*/
alloc_chrdev_region(&test_wait_queue.cdev.dev, 0, 1, DEVICE_NAME);
cdev_add(&test_wait_queue.cdev, test_wait_queue.cdev.dev, 1);
/* 自动创建设备节点 */
test_wait_queue.queue_class = class_create(THIS_MODULE, DEVICE_NAME);
test_wait_queue.queue_device= device_create(test_wait_queue.queue_class, NULL, test_wait_queue.cdev.dev, NULL, FILE_NAME);
g_buf = (char *)kmalloc(100,GFP_KERNEL);
return 0;
}
static void test_queue_exit(void)
{
device_unregister(test_wait_queue.queue_device);
class_destroy(test_wait_queue.queue_class);
/* 注销字符设备 */
cdev_del(&test_wait_queue.cdev);
/* 释放设备号 */
unregister_chrdev_region(test_wait_queue.cdev.dev, 1);
LOG("Exit %s\n",__FUNCTION__);
kfree(g_buf);
}
module_init(test_queue_init);
module_exit(test_queue_exit);
MODULE_AUTHOR("yhl");
MODULE_DESCRIPTION("test_queue_exit");
MODULE_LICENSE("GPL");
总结一下下,编写一个最简单的字符驱动设备,有以下几个必须的步骤:
1. 通过module_init()来创建自己的入口函数;
2. 填充file_op结构体,创建字符设备;
3. 创建设备文件 即 /dev下的文件;