一、register_chrdev基本介紹
register_chrdev函數是Linux設備驅動程序中非常重要的函數,它用於動態地分配主設備號和次設備號,並將主設備號和設備驅動程序所用的file_operations結構體綁定在一起。其基本的函數原型如下所示:
int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops);
其中major表示主設備號,如果它被設置為0,則表示系統自動分配主設備號。name為設備名稱,通常為設備驅動程序的名稱。fops為設備驅動程序的操作函數指針,它包含了所有與設備相關的操作函數。
二、register_chrdev函數詳解
1. register_chrdev函數的返回值
當register_chrdev函數成功被調用時,它會返回分配的主設備號,否則會返回一個負數,表示分配失敗。當主設備號被成功分配後,你就可以在file_operations結構體中使用這個主設備號了。
下面給出一個簡單的示例代碼,用於演示如何調用register_chrdev函數並檢查它的返回值:
#include <linux/fs.h>
#define MY_MAJOR 0
static int mydev_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "mydev open\n");
return 0;
}
static int mydev_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "mydev release\n");
return 0;
}
static struct file_operations mydev_fops = {
.owner = THIS_MODULE,
.open = mydev_open,
.release = mydev_release,
};
static int __init mydev_init(void)
{
int ret;
ret = register_chrdev(MY_MAJOR, "mydev", &mydev_fops);
if (ret < 0) {
printk(KERN_ERR "failed to register chrdev: %d\n", ret);
return ret;
}
printk(KERN_INFO "mydev initialized: %d\n", MAJOR);
return 0;
}
static void __exit mydev_exit(void)
{
unregister_chrdev(MY_MAJOR, "mydev");
printk(KERN_INFO "mydev exited\n");
}
module_init(mydev_init);
module_exit(mydev_exit);
MODULE_LICENSE("GPL");
通過上述代碼我們可以看到,在init函數中會調用register_chrdev函數,並檢查返回值是否小於0。如果返回值小於0,則說明分配主設備號失敗。如果分配成功,則可以在設備文件操作函數中使用該主設備號了。
2. register_chrdev與cdev_add函數
register_chrdev函數只是為設備分配主設備號,並將設備文件操作函數與主設備號綁定。在這個過程中,並沒有創建真正的設備文件,也沒有分配次設備號。這時候代碼是不能直接使用文件系統訪問設備的。為了在用戶態能夠訪問設備文件,需要使用cdev_add函數手動在內核中創建設備文件節點,綁定主設備號和次設備號,同時初始化file_operations結構體,例如:
struct cdev mycdev; // 創建一個cdev對象 int ret = register_chrdev(MY_MAJOR, "mydev", &mydev_fops); // 註冊設備,然後得到主設備號 cdev_init(&mycdev, &mydev_fops); // 初始化cdev對象,設置mydev_fops結構體 mycdev.owner = THIS_MODULE; ret = cdev_add(&mycdev, MKDEV(MY_MAJOR, 0), 1); // 在內核中創建設備文件節點,並綁定主設備號和次設備號
通過上面的代碼,我們可以使用該文件操作函數,來訪問到我們的設備,如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
int main()
{
int fd;
fd = open("/dev/mydev", O_RDWR);
if (fd == -1)
printf("failed to open\n");
else
printf("success to open\n");
close(fd);
return 0;
}
通過上面的代碼,我們可以發現,我們已經成功地訪問到了我們的設備了,大家可以嘗試編譯運行一下哦!
3. register_chrdev_region函數
在引入udev後,為了方便使用,我們需要對設備驅動程序編寫更嚴格的規範。register_chrdev_region函數是其中的一個重要函數。在 register_chrdev_region函數中,使用MKDEV函數構造出設備號devno,分配一段設備號區間,並與設備驅動程序的結構體綁定。這樣udev就可以自動地為該設備驅動程序創建設備文件節點了。下面是一段示例代碼:
static int mydev_init(void)
{
int ret;
dev_t devno;
if (MY_MAJOR) { // 必須顯式地分配主設備號才能使用register_chrdev_region函數
devno = MKDEV(MY_MAJOR, 0);
ret = register_chrdev_region(devno, 1, "mydev");
} else {
ret = alloc_chrdev_region(&devno, 0, 1, "mydev");
}
if (ret < 0) {
printk(KERN_ERR "failed to register chrdev: %d\n", ret);
return ret;
}
cdev_init(&mycdev, &mydev_fops);
mycdev.owner = THIS_MODULE;
ret = cdev_add(&mycdev, devno, 1);
if (ret < 0) {
unregister_chrdev_region(devno, 1);
return ret;
}
printk(KERN_INFO "mydev initialized: %d\n", MAJOR);
return 0;
}
static void __exit mydev_exit(void)
{
cdev_del(&mycdev);
unregister_chrdev_region(MKDEV(MY_MAJOR, 0), 1);
printk(KERN_INFO "mydev exited\n");
}
module_init(mydev_init);
module_exit(mydev_exit);
MODULE_LICENSE("GPL");
4. register_chrdev與class_create函數
class_create函數是對udev庫的簡單封裝,用於創建設備類,和初始化設備的屬性。多個設備驅動程序共用一個設備類。register_chrdev函數中可能涉及到設備的類型,比如常見的字符設備類型是 “cdev”。在設備創建完成後,我們通過class_create定義一個新的設備類,然後通過register_chrdev_register API註冊該設備。如下所示:
struct class *myclass;
static int mydev_init(void)
{
int ret;
dev_t devno;
// ...省略部分代碼
if (!MY_MAJOR) {
MY_MAJOR = MAJOR(devno);
}
myclass = class_create(THIS_MODULE, "mydev");
if (IS_ERR(myclass)) {
unregister_chrdev_region(devno, 1);
return PTR_ERR(myclass);
}
device_create(myclass, NULL, devno, NULL, "mydev");
return 0;
}
static void __exit mydev_exit(void)
{
device_destroy(myclass, MKDEV(MY_MAJOR, 0));
class_destroy(myclass);
cdev_del(&mycdev);
unregister_chrdev_region(MKDEV(MY_MAJOR, 0), 1);
printk(KERN_INFO "mydev exited\n");
}
module_init(mydev_init);
module_exit(mydev_exit);
MODULE_LICENSE("GPL");
上面的代碼中,我們對class_create函數進行了 wrap,並通過device_create API註冊了對應的設備信息。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/237797.html
微信掃一掃
支付寶掃一掃