linux 內核驅動一 字符驅動

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章