Linux設備驅動筆記:字符設備驅動程序


參考資料:LDD3,精通Linux設備驅動程序
關鍵字:編寫Linux字符設備驅動程序
 

Linux提供了幾種註冊字符設備的方式。如下:

示例一:register_chrdev

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

#include <linux/cdev.h>

#include <linux/fs.h>


#define DEV_MAJOR	100
#define DEV_NAME	"test"

static int myopen(struct inode *inode, struct file *filp)
{
	printk("myopen!\n");	
	return 0;
}

static int myrelease(struct inode *inode, struct file *filp)
{
	printk("myrelease\n");
	return 0;
}

static int myread(struct file *filp, const char __user *buf, size_t len, loff_t *loff)
{
	printk("myread\n");
	return 0;	
}

static int mywrite(struct file *filp, const char __user *buf, size_t len, loff_t *loff)
{
	printk("mywrite!\n");	
	return 0;
}

struct file_operations fops = {
	.owner = THIS_MODULE,
	.open = myopen,
	.release = myrelease,
	.read = myread,
	.write = mywrite,
};

static int __init test_init(void)
{
	int res;

	printk("hello kernel!\n");

	res = register_chrdev(DEV_MAJOR, DEV_NAME, &fops);

	return res;
}

static void __exit test_exit(void)
{
	unregister_chrdev(DEV_MAJOR, DEV_NAME);
	printk("exit kernel!\n");
}


module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("HGL");

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

這裏, major 是感興趣的主編號, name 是驅動的名子(出現在 /proc/devices), fops 是缺省的file_operations 結構. 一個對 register_chrdev 的調用爲給定的主編號註冊 0 - 255 的次編號, 並且爲每一個建立一個缺省的 cdev 結構. 使用這個接口的驅動必須準備好處理對所有 256 個次編號的 open調用( 不管它們是否對應真實設備 ), 它們不能使用大於 255 的主或次編號.如果你使用 register_chrdev, 從系統中去除你的設備的正確的函數是:

  • int unregister_chrdev(unsigned int major, const char *name);

major 和 name 必須和傳遞給 register_chrdev 的相同, 否則調用會失敗。

通過register_chrdev去註冊設備,需要確保主設備號沒有被佔用,也不能自動在/dev下生產設備節點,需要通過mknod去創建設備文件。如:

$ mknod test c 100 0

 
 

示例二:device_create

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

#include <linux/cdev.h>

#include <linux/fs.h>

#include <linux/device.h>

dev_t dev_id;

static struct cdev cdev;
static struct class *cls = NULL;

static struct device *device;

#define DEV_MINOR	5

#define DEV_MAJOR	100
#define DEV_NAME	"test"

static int myopen(struct inode *inode, struct file *filp)
{
	printk("myopen!\n");	
	return 0;
}

static int myrelease(struct inode *inode, struct file *filp)
{
	printk("myrelease\n");
	return 0;
}

static int myread(struct file *filp, const char __user *buf, size_t len, loff_t *loff)
{
	printk("myread\n");
	return 0;	
}

static int mywrite(struct file *filp, const char __user *buf, size_t len, loff_t *loff)
{
	printk("mywrite!\n");	
	return 0;
}

struct file_operations fops = {
	.owner = THIS_MODULE,
	.open = myopen,
	.release = myrelease,
	.read = myread,
	.write = mywrite,
};

static int __init test_init(void)
{
	int res;

	printk("hello kernel!\n");

	res = alloc_chrdev_region(&dev_id, DEV_MINOR, 3,  DEV_NAME);
	if (res < 0)
		return res;

	cdev_init(&cdev, &fops);	//將fops和設備關聯
	res = cdev_add(&cdev, dev_id, 3);
	if (res < 0)
		return res;
	
	cls = class_create(THIS_MODULE, "test");

	device = device_create(cls, NULL, dev_id, NULL, "test%d", 1);


	return res;
}

static void __exit test_exit(void)
{
	printk("exit kernel!\n");

	device_destroy(cls, dev_id);

	class_destroy(cls);

	cdev_del(&cdev);
	unregister_chrdev_region(dev_id, 3);
}


module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("HGL");

cdev_add和register_chrdev的區別在於cdev_add可以通過alloc_chrdev_region去動態獲取主設備號,但同樣不能自動生成設備節點。這裏使用device_create這個函數,可以幫我們自動創建設備節點。這個函數需要傳入一個類class,可以通過class_create這個函數去獲取,並通過device_create將設備歸入該類。
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

總結

在init函數中至少需要完成如下工作:
1),申請設備號。
2),將入口函數(open,read等)與字符驅動程序的cdev抽象相關聯。
3),將設備號與cdev相關聯。
需要創建設備文件讓應用程序去調用設備驅動,可以手動通過mknod去創建,也可以使用device_create自動生成。
在exit函數中需要將init中做的事情都清理掉,需要注意清理的順序。

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