亚洲欧美第一页_禁久久精品乱码_粉嫩av一区二区三区免费野_久草精品视频

蟲蟲首頁| 資源下載| 資源專輯| 精品軟件
登錄| 注冊

您現在的位置是:首頁 > 技術閱讀 >  linux驅動開發(2、第一個字符設備驅動)

linux驅動開發(2、第一個字符設備驅動)

時間:2024-06-01

文章目錄

  • 設備號

  • 常用接口

    • 設備號申請/釋放

      • register_chrdev_region函數

      • alloc_chrdev_region函數

      • unregister_chrdev_region函數

      • 其它宏定義

    • 字符設備的定義

      • cdev_alloc函數

    • 字符設備的綁定

      • cdev_init函數

    • 字符設備注冊/注銷

      • cdev_add函數

      • cdev_del函數

    • file_operations結構

  • 實現一個簡單的字符設備驅動

  • 以前的接口

    • register_chrdev函數

    • unregister_chrdev函數

  • 測試流程及結果

  • 總結


點擊下方閱讀原文可訪問文中超鏈接

設備號

設備號(統稱,由主設備號和次設備號組成)是一種資源,更是應用層與驅動層之間的紐帶,設備號由主設備號和次設備號組成,主設備號用于區分是哪類設備,次設備號用于區分某一類設備中的各個子設備;設備號的分配規則是從大往小分配,其中某些設備號是系統已經約定好了的預分配給指定設備;
當向內核申請了設備號后,就可以在/proc/devices文件中看到對應的主設備號及設備名,此時使用mknod也可以在/dev目錄生成設備節點,不過這個節點是個空節點,沒有任何用;設備號被注銷后,/proc/devices中也會自動刪除,如下:

常用接口

設備號申請/釋放

位于頭文件:include/linux/fs.h

register_chrdev_region函數

向內核申請指定的設備號,如果指定的設備號已存在則會申請失敗。

int register_chrdev_region(dev_t, unsigned, const char *);

示例用法:

//MKDEV是一個宏,用于將主次設備號合并成設備號
//第二個參數表示申請多少個設備號
//第三個參數是設備名
register_chrdev_region(MKDEV(247,0),1,"test_char_dev");

alloc_chrdev_region函數

向內核申請設備號,并且由內核自動分配可用的設備號。

int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);

示例用法:

//devno存儲申請的設備號
//第二個參數表示次設備號的起始值
//第三個參數表示申請多少個設備號
//第四個參數是設備名
alloc_chrdev_region(&devno,0,1,"test_char_dev");

unregister_chrdev_region函數

用于注銷由register_chrdev_regionalloc_chrdev_region申請的設備號,前面也說了設備號是一種資源,所以要養成良好的習慣,不用就要歸還。

void unregister_chrdev_region(dev_t, unsigned);

示例用法:

//devno申請的設備號
//第二個參數表示申請的個數,申請了多少就要注銷多少
unregister_chrdev_region(devno,1);

其它宏定義

位于頭文件:include/linux/kdev_t.h
用于提取設備號的主設備號。

#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))

用于提取設備號的次設備號。

#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))

字符設備的定義

cdev_alloc函數

位于頭文件:include/linux/cdev.h
使用動態內存的方式定義一個字符設備。

struct cdev *cdev_alloc(void);

或者直接定義為靜態全局變量

static struct cdev chr_dev;

字符設備的綁定

cdev_init函數

將一個字符設備與file_operations結構綁定起來。

void cdev_init(struct cdev *, const struct file_operations *);

字符設備注冊/注銷

cdev_add函數

向內核注冊字符設備。

int cdev_add(struct cdev *, dev_t, unsigned);

cdev_del函數

注銷一個字符設備。

void cdev_del(struct cdev *);

file_operations結構

位于頭文件:include/linux/fs.h
這個結構體包含了一系列的函數指針,基本上每一個函數指針都對應了應用層一個接口函數,比如調用應用層的open函數時,最終就會調用到這個結構體里面的open函數指針。我們就可以通過實現一些需要的函數來操作硬件。
示例代碼:

static int test_open (struct inode *inode, struct file *file)
{
return 0;
}

static int test_close (struct inode *inode, struct file *file)
{
return 0;
}

static const struct file_operations fops = {
.owner = THIS_MODULE,
.open = test_open,
.release = test_close,
};

實現一個簡單的字符設備驅動

一個簡單的字符設備驅動大致分為以下幾個步驟:

  • 向內核申請設備號

  • 定義一個字符設備對象

  • 綁定字符設備對象與file_operations結構

  • 向內核注冊字符設備

  • 實現file_operations結構體內的部分接口

簡單源碼如下(注意未實現各種錯誤及邊界檢查,實際應用時應完善,以保證程序的健壯性):

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>

#define DEBUG(fmt, ...) \
do{ \
if(if_debug) \
printk(KERN_INFO "DEBUG > " fmt, ##__VA_ARGS__); \
}while(0)


bool if_debug = false;
/*定義一個bool類型的變量,作為一個模塊入參,在裝載模塊時可賦值*/
module_param(if_debug, bool, S_IRUSR);

#define DEV_COUNT 1

/*設備號*/
static dev_t devno;
/*定義一個字符設備*/
static struct cdev *char_dev;

static int test_open (struct inode *inode, struct file *file)
{
DEBUG("open test..\r\n");

return 0;
}

static int test_close (struct inode *inode, struct file *file)
{
DEBUG("close test..\r\n");

return 0;
}

static const struct file_operations fops = {
.owner = THIS_MODULE,
.open = test_open,
.release = test_close,
};

static int __init test_init(void)
{
/*申請設備號*/
alloc_chrdev_region(&devno,0,DEV_COUNT,"test_char_dev");
/*使用動態內存定義一個字符設備*/
char_dev = cdev_alloc();
/*綁定字符設備和file_operations結構*/
cdev_init(char_dev,&fops);
/*注冊字符設備*/
cdev_add(char_dev,devno,DEV_COUNT);

return 0;
}

static void __exit test_exit(void)
{
/*注銷一個字符設備*/
cdev_del(char_dev);
/*注銷申請的設備號*/
unregister_chrdev_region(devno,DEV_COUNT);
}

/*此宏聲明內核模塊的初始化入口點*/
module_init(test_init);
/*此宏聲明內核模塊的退出入口點*/
module_exit(test_exit);

/*聲明開源協議*/
MODULE_LICENSE("GPL");
/*聲明作者*/

MODULE_AUTHOR("wei");
/*聲明模塊的描述*/
MODULE_DESCRIPTION("this is a test driver");

以前的接口

同樣是創建字符設備,但是內核還有兩個接口,可以大大簡化上面的代碼。

register_chrdev函數

此函數可一步到位,完成了設備號的申請,字符設備與file_operations結構的綁定以及字符設備的注冊全部操作,只不過這個函數會自動申請256個主設備號相同的設備號(次設備號為0到255),如果沒有這么多同類子設備的話會造成空間的浪費

int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)

示例代碼:

//第一個參數為主設備號,如果為0則內核自動分配
//第二個參數為設備名
//第三個參數為file_operations結構體
register_chrdev(247,"test_char_dev",fops);

unregister_chrdev函數

用于注銷由register_chrdev申請的資源。

void unregister_chrdev(unsigned int major, const char *name)

示例用法:

//第一個參數為主設備號
//第二個參數為設備名
unregister_chrdev(247,"test_char_dev");

第二種更簡潔的寫法:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>

#define DEBUG(fmt, ...) \
do{ \
if(if_debug) \
printk(KERN_INFO "DEBUG > " fmt, ##__VA_ARGS__); \
}while(0)


bool if_debug = false;
/*定義一個bool類型的變量,作為一個模塊入參,在裝載模塊時可賦值*/
module_param(if_debug, bool, S_IRUSR);

#define CHRDEV_NAME "test_char_dev"
/*主設備號*/
static dev_t major;

static int test_open (struct inode *inode, struct file *file)
{
DEBUG("open test..\r\n");

return 0;
}

static int test_close (struct inode *inode, struct file *file)
{
DEBUG("close test..\r\n");

return 0;
}

static const struct file_operations fops = {
.owner = THIS_MODULE,
.open = test_open,
.release = test_close,
};

static int __init test_init(void)
{
major = register_chrdev(0,CHRDEV_NAME,&fops);

return 0;
}

static void __exit test_exit(void)
{
unregister_chrdev(major,CHRDEV_NAME);
}

/*此宏聲明內核模塊的初始化入口點*/
module_init(test_init);
/*此宏聲明內核模塊的退出入口點*/
module_exit(test_exit);

/*聲明開源協議*/
MODULE_LICENSE("GPL");
/*聲明作者*/
MODULE_AUTHOR("wei");
/*聲明模塊的描述*/
MODULE_DESCRIPTION("this is a test driver");

驅動寫好后,還需要讓應用程序能夠使用,而應用程序是通過設備節點來訪問的,所以接下來創建設備節點,以讓應用程序可以訪問寫好的驅動。使用mknod命令創建設備節點:

mknod 設備名 設備類型 主設備號 次設備號

示例用法:

mknod /dev/test_char_dev c 247 0

此程序沒有實現任何的硬件操作,下面寫個簡單的應用程序測試一下驅動是否能正常工作,因為只實現了openrelease接口,所以在應用程序中也只能使用這兩個接口。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <stdio.h>

int main(int argc,char *argv[])
{
int fd;

fd = open("/dev/test_char_dev",O_RDONLY);
if(fd < 0)
{
perror("open");

return -1;
}

close(fd);

return 0;
}

測試流程及結果

# 裝載驅動
insmod test.ko if_debug=1
# 查看申請的設備號
cat /proc/devices
# 創建設備節點(主設備號由上條命令查看,次設備號與驅動程序一致)
mknod /dev/test_char_dev c 247 0
# 執行應用程序
./app_test
# 運行結果會打印出驅動程序中的兩個調試信息
DEBUG > open test..
DEBUG > close test..
# 卸載驅動
rmmod test.ko
# 刪除設備節點
rm /dev/test_char_dev

總結

我根據自己的理解,畫了一個關系圖。

測試源碼獲取:點我

主站蜘蛛池模板: 大名县| 宁晋县| 会泽县| 文登市| 镇雄县| 廊坊市| 瑞安市| 宣化县| 桦南县| 台北县| 五寨县| 福贡县| 汉中市| 凤阳县| 阿克苏市| 安岳县| 延吉市| 伊宁县| 隆安县| 侯马市| 长岛县| 波密县| 临洮县| 惠州市| 铜川市| 德格县| 满洲里市| 大连市| 蕲春县| 商城县| 广安市| 博客| 木里| 阿拉善右旗| 通化市| 伊宁市| 旌德县| 周宁县| 富宁县| 全椒县| 常宁市|