linux misc device字元雜項裝置驅動程式
雜項裝置也是在嵌入式系統中用得比較多的一種裝置驅動。miscdevice共享一個主裝置號MISC_MAJOR(即10),但次裝置號不同。misc裝置其實就是特殊的字元裝置,主裝置編號採用10,並且可自動生成裝置節點。
雜項裝置作為字元裝置的封裝,為字元裝置提供的簡單的程式設計介面,如果編寫新的字元驅動,可以考慮使用雜項裝置介面,方便簡單,只需要初始化一個miscdevice的結構,呼叫misc_register就可以了,misc_register最終也是通過呼叫register_chrdev()來註冊裝置。系統最多有255個雜項裝置,因為雜項裝置模組自己佔用了一個次裝置號。
1. 實現struct file_operations
static struct file_operations leds_ops = {
.owner = THIS_MODULE,
.open = leds_open,
.release = leds_release,
.unlocked_ioctl = leds_ioctl,
};
2. 初始化struct miscdevice,定義一個misc裝置
static struct miscdevice leds_dev = { .minor = MISC_DYNAMIC_MINOR, .fops = &leds_ops, .name = "leds", //此名稱將顯示在/dev目錄下面 };
注:其中minor如果填充MISC_DYNAMIC_MINOR,則是動態動態次裝置號。
3. 註冊和釋放misc裝置
1)註冊
int misc_register(struct miscdevice * misc);
misc_register(&leds_dev);
注:此函式中會自動建立裝置節點,即裝置檔案。無需mknod指令建立裝置檔案,因為misc_register()會呼叫device_create()建立裝置節點。
2)釋放
int misc_deregister(struct miscdevice *misc);
misc_deregister(&leds_dev);
4. 其它
1)標頭檔案 #include <linux/miscdevice.h>
2)miscdevice 結構體
struct miscdevice { int minor; //次裝置號 通常為MISC_DYNAMIC_MINOR 動態分配 const char *name; //裝置的名字 const struct file_operations *fops;//函式操作集 struct list_head list; struct device *parent; struct device *this_device; const char *nodename; umode_t mode; };
通常 miscdevice的 minor 、name 和 fops是需要實現的。
5. A33下一個GPIO驅動例項
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <mach/gpio.h>
#include <linux/gpio.h>
#include <linux/miscdevice.h>
static int led_gpios[] = {
GPIOH(7),
};
#define LED_NUM ARRAY_SIZE(led_gpios)
int leds_open(struct inode *inode,struct file *filp)
{
printk("leds device opened success!\n");
return nonseekable_open(inode,filp); //通知核心你的裝置不支援llseek
}
int leds_release(struct inode *inode,struct file *filp)
{
printk("leds device closed success!\n");
return 0;
}
long leds_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{
printk("debug: leds_ioctl cmd is %d\n" , cmd);
switch(cmd)
{
case 0: //不加break,執行case1
case 1:
if (arg > LED_NUM) {
return -EINVAL;
}
gpio_set_value(led_gpios[arg], cmd);
break;
default:
return -EINVAL;
}
return 0;
}
static struct file_operations leds_ops = {
.owner = THIS_MODULE,
.open = leds_open,
.release = leds_release,
.unlocked_ioctl = leds_ioctl,
};
static struct miscdevice leds_dev = {
.minor = MISC_DYNAMIC_MINOR,
.fops = &leds_ops,
.name = "leds", //此名稱將顯示在/dev目錄下面
};
static int __init leds_init(void)
{
int ret, i;
char *banner = "leds Initialize\n";
printk(banner);
for(i=0; i<LED_NUM; i++)
{
//申請gpio,設定為輸出,高電平
ret = gpio_request_one(led_gpios[i], GPIOF_OUT_INIT_HIGH,"LED");
if (ret) {
printk("leds: request GPIO %d for LED failed, ret = %d\n", led_gpios[i], ret);
return ret;
}
}
ret = misc_register(&leds_dev);
if(ret<0)
{
printk("leds: register device failed!\n");
goto exit;
}
return 0;
exit:
misc_deregister(&leds_dev);
return ret;
}
static void __exit leds_exit(void)
{
misc_deregister(&leds_dev);
}
module_init(leds_init);
module_exit(leds_exit);
MODULE_LICENSE("Dual BSD/GPL");