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下的文件;