1. 程式人生 > >Linux驅動入門篇(三):基本的字符設備模塊(2)

Linux驅動入門篇(三):基本的字符設備模塊(2)

連接 truct ace alloc orm 負數 -s tabs idt

  上一節中介紹了設備號的申請和釋放,這一節開始了解字符設備的相關操作。

  首先定位到<linux/cdev.h>文件,查看內核提供給字符設備的接口。

cdev結構

struct cdev {
	struct kobject kobj;	//內嵌的kobject對象
	struct module *owner;	//此結構所屬模塊
	const struct file_operations *ops;	//文件操作結構
	struct list_head list;	//通用雙向鏈表
	dev_t dev;		//設備號
	unsigned int count;
};

  owner成員一般初始化為 THIS_MODULE,THIS_MODULE 是一個指向當前模塊的 struct module結構指針,也就是指向當前模塊。

字符設備的函數接口

void cdev_init(struct cdev *, const struct file_operations *);
struct cdev *cdev_alloc(void);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_del(struct cdev *);

  以上是<linux/cdev.h>提供的部分函數接口。接下來一個一個地解決掉它們。

  首先是 cdev_init 函數,先看源碼。

void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
	memset(cdev, 0, sizeof *cdev);
	INIT_LIST_HEAD(&cdev->list);
	kobject_init(&cdev->kobj, &ktype_cdev_default);
	cdev->ops = fops;
}

  此函數的功能是初始化一個 cdev 結構。參數 cdev 即為要進行初始化的結構,參數 fops則是此設備的 file_operations(具體作用留到後面)。

  可以看到 cdev_init 的主要作用就是初始化 cdev 結構,把 fops 指針連接到 cdev結構。做好此設備被添加到系統的準備。

  接下來看 cdev_alloc 函數。

struct cdev *cdev_alloc(void)
{
	struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
	if (p) {
		INIT_LIST_HEAD(&p->list);
		kobject_init(&p->kobj, &ktype_cdev_dynamic);
	}
	return p;
}

  此函數為 cdev 結構申請了一塊內存,並返回它的地址,失敗時返回NULL。

  下一個函數是 cdev_add,它將一個字符設備添加到系統。

int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
	int error;

	p->dev = dev;
	p->count = count;

	error = kobj_map(cdev_map, dev, count, NULL,
			 exact_match, exact_lock, p);
	if (error)
		return error;

	kobject_get(p->kobj.parent);

	return 0;
}

  參數 p 是設備的 struct cdev指針,dev 是首個設備號,count 是連續的次設備號的數量。函數通過把指針 p 添加到系統,來描述設備的添加,使設備立即生效。若添加失敗,則返回負數的錯誤碼。

  最後一個函數是 cdev_del,它的功能是從系統移除一個 cdev 結構。

void cdev_del(struct cdev *p)
{
	cdev_unmap(p->dev, p->count);
	kobject_put(&p->kobj);
}

  此函數從系統中移除指針 p,有可能會釋放 p 指向的結構。

字符設備的註冊和註銷

  還記得上一節中未實現的 mycdev_setup 函數和 mycdev_del 函數嗎?現在我們已經可以實現它們啦。

#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
 
dev_t devno;    //設備號
 
static struct class *my_class;
static struct cdev my_cdev;
static struct file_operations my_fops;


static int __init mycdev_init(void)
{
        int ret;
        ret = alloc_chrdev_region(&devno, 0, 1, "mycdev");
        if(ret != 0){
                printk(KERN_NOTEICE "Alloc device number failed.");
                return -1;
        }

	//開始實現cdev_setup()
	cdev_init(&my_cdev, &my_fops);
	my_cdev.owner = THIS_MODULE;

	ret = cdev_add(&my_cdev, devno, 1);
	if(ret < 0){
		printk(KERN_NOTEICE "Add cdev failed.");
                return -2;
	}
	//cdev_setup()結束

        my_class = class_create(THIS_MODULE, "mycdev");
        device_create(my_class, NULL, devno, NULL, "mycdev");
 
        return 0;
}
 
static void mycdev_exit(void)
{
	//mycdev_del()實現
	cdev_del(&my_cdev);
	//mycdev_del()結束
 
        device_destroy(my_class, devno);
        class_destroy(my_class);
 
        unregister_chrdev_region(devno);
}
 
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE(“Dual BSD/GPL”);

  現在我們就完成了一個基本的字符設備模塊,它實現了設備號的申請與註銷,設備文件的創建與銷毀以及字符設備的初始化、註冊與註銷。

  但是,這還不夠。我們的目的是使用字符設備,至少需要讀或者寫此設備。如何讓字符設備模塊提供讀寫功能呢?這就和 struct file_operations 結構有關了,留待下一節詳細敘述。

Linux驅動入門篇(三):基本的字符設備模塊(2)