參考資料: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中做的事情都清理掉,需要注意清理的順序。