簡介
雜項設備驅動,就是最簡單的字符設備驅動,通常嵌套在platform總線驅動中實現複雜的驅動
主設備號都爲10,不同的設備使用不同的從設備號
MISC設備驅動用於解決字符設備驅動不斷增加設備號緊張的問題
MISC設備會自動創建cdev,不需要以前那樣手動創建
採用MISC設備驅動可以簡化字符設備驅動的編寫
驅動框架
我們需要向Linux內核註冊一個miscdevice設備,miscdevice是一個結構體
miscdevice
定義在\linux\miscdevice.h中,描述了misc設備
struct miscdevice {
int minor;
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
const struct attribute_group **groups;
const char *nodename;
umode_t mode;
};
我們需要設置minor、name、fops這三個成員變量
\linux\miscdevice.h中也預定義了一些子設備號,可以從中指定也可以自己定義
#define PSMOUSE_MINOR 1
#define MS_BUSMOUSE_MINOR 2 /* unused */
#define ATIXL_BUSMOUSE_MINOR 3 /* unused */
/*#define AMIGAMOUSE_MINOR 4 FIXME OBSOLETE */
#define ATARIMOUSE_MINOR 5 /* unused */
#define SUN_MOUSE_MINOR 6 /* unused */
#define APOLLO_MOUSE_MINOR 7 /* unused */
#define PC110PAD_MINOR 9 /* unused */
/*#define ADB_MOUSE_MINOR 10 FIXME OBSOLETE */
#define WATCHDOG_MINOR 130 /* Watchdog timer */
#define TEMP_MINOR 131 /* Temperature Sensor */
#define RTC_MINOR 135
#define EFI_RTC_MINOR 136 /* EFI Time services */
#define VHCI_MINOR 137
#define SUN_OPENPROM_MINOR 139
#define DMAPI_MINOR 140 /* unused */
#define NVRAM_MINOR 144
#define SGI_MMTIMER 153
#define STORE_QUEUE_MINOR 155 /* unused */
#define I2O_MINOR 166
#define MICROCODE_MINOR 184
#define VFIO_MINOR 196
#define TUN_MINOR 200
#define CUSE_MINOR 203
#define MWAVE_MINOR 219 /* ACP/Mwave Modem */
#define MPT_MINOR 220
#define MPT2SAS_MINOR 221
#define MPT3SAS_MINOR 222
#define UINPUT_MINOR 223
#define MISC_MCELOG_MINOR 227
#define HPET_MINOR 228
#define FUSE_MINOR 229
#define KVM_MINOR 232
#define BTRFS_MINOR 234
#define AUTOFS_MINOR 235
#define MAPPER_CTRL_MINOR 236
#define LOOP_CTRL_MINOR 237
#define VHOST_NET_MINOR 238
#define UHID_MINOR 239
#define MISC_DYNAMIC_MINOR 255
與字符設備的對比
註冊
設置好miscdevice結構體後使用misc_register向系統註冊一個MISC設備
int misc_register(struct miscdevice *misc);
原先我們需要
alloc_chrdev_region(); /* 申請設備號 */
cdev_init(); /* 初始化cdev */
cdev_add(); /* 添加cdev */
class_create(); /* 創建類 */
device_create(); /* 創建設備 */
卸載
當我們卸載驅動的時候調用misc_deregister來註銷MISC設備
int misc_deregister(struct miscdevice *misc);
原先我們需要
cdev_del(); /* 刪除cdev */
unregister_chrdev_region(); /* 註銷設備號 */
device_destroy(); /* 刪除設備 */
class_destroy(); /* 刪除類 */
實驗代碼與分析
實驗代碼
#include <linux/init.h>
#include <linux/ide.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/stat.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/errno.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <asm/io.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#define MISCBEEP_MINOR 144
#define MISCBEEP_NAME "miscbeep"
#define BEEPOFF 0
#define BEEPON 1
struct miscbeep_dev {
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
struct device_node *nd;
int beep_gpio;
};
struct miscbeep_dev miscbeep;
static int miscbeep_open(struct inode *inode,struct file *filp)
{
printk(KERN_EMERG "miscbeep_open enter!\n");
filp->private_data = &miscbeep;
return 0;
}
static ssize_t miscbeep_write(struct file *filp,const char __user *buf,size_t cnt,loff_t *offt)
{
int retvalue;
unsigned char databuf[2];
unsigned char beepstat;
struct miscbeep_dev *dev = (struct miscbeep_dev *)filp->private_data;
printk(KERN_EMERG "miscbeep_write enter!\n");
retvalue = copy_from_user(databuf, buf, cnt);
if(retvalue < 0)
{
printk(KERN_EMERG "kernel write failed!\n");
return -EFAULT;
}
beepstat = databuf[0];
if(beepstat == BEEPON)
{
gpio_set_value(dev->beep_gpio, 0);
}
else if(beepstat == BEEPOFF)
{
gpio_set_value(dev->beep_gpio, 1);
}
return 0;
}
static struct file_operations miscbeep_fops = {
.owner = THIS_MODULE,
.open = miscbeep_open,
.write = miscbeep_write,
};
static struct miscdevice beep_miscdev = {
.minor = MISCBEEP_MINOR,
.name = MISCBEEP_NAME,
.fops = &miscbeep_fops,
};
static int miscbeep_probe(struct platform_device *dev)
{
int ret = 0;
printk(KERN_EMERG "miscbeep_probe enter!\n");
/*1.Get device node*/
miscbeep.nd = of_find_node_by_path("/beep");
if(miscbeep.nd == NULL)
{
printk(KERN_EMERG "beep node not find!\n");
return -EINVAL;
}
/*2.get gpio property*/
miscbeep.beep_gpio = of_get_named_gpio(miscbeep.nd, "beep-gpio", 0);
if(miscbeep.beep_gpio < 0)
{
printk(KERN_EMERG "can't get beep-gpio!\n");
return -EINVAL;
}
/*3.set gpio output,output high default,close beep*/
ret = gpio_direction_output(miscbeep.beep_gpio, 1);
if(ret < 0)
{
printk(KERN_EMERG "can't set beep-gpio!\n");
}
ret = misc_register(&beep_miscdev);
if(ret < 0)
{
printk(KERN_EMERG "misc device register failed!\n");
return -EINVAL;
}
return 0;
}
static int miscbeep_remove(struct platform_device *dev)
{
/*close beep*/
gpio_set_value(miscbeep.beep_gpio, 1);
/*deregister misc driver*/
misc_deregister(&beep_miscdev);
return 0;
}
static const struct of_device_id miscbeep_of_match[] = {
{ .compatible = "atkalpha-beep"},
{ /**/ }
};
static struct platform_driver miscbeep_driver = {
.driver = {
.name = "im6ul-beep",
.of_match_table = miscbeep_of_match,
},
.probe = miscbeep_probe,
.remove = miscbeep_remove,
};
static int __init miscdriver_init(void)
{
return platform_driver_register(&miscbeep_driver);
}
static void __exit miscdriver_exit(void)
{
platform_driver_unregister(&miscbeep_driver);
}
module_init(miscdriver_init);
module_exit(miscdriver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("GYY");
代碼分析
- 總體來看我們可以發現,還是platform驅動的結構,需要定義platform_driver ,在init和exit函數中,做的是platform設備的註冊和卸載
- 我們定義了miscbeep_dev 結構體來表示一個我們的設備,這裏其實值用到了設備樹節點nd和GPIO用於操作蜂鳴器
- 我們還需要定義一個miscdevice結構體類型的變量代表misc設備
- 與之前platform驅動的區別之處就在於probe函數和remove函數中所做的事情,在probe函數中,我們只做了GPIO的獲取以及初始化,然後調用misc_register來註冊MISC設備,在remove函數中我們調用misc_deregister來卸載MISC設備
- 在open和write函數中的操作就是和普通驅動中是相同的