1 什么是混雜設(shè)備
在Linux驅(qū)動中抬伺,會把一些無法歸類的設(shè)備定義為混雜設(shè)備——misc(在文件drivers/char/misc.c中實現(xiàn)),它們是擁有著共同的特性的簡單字符設(shè)備(也就是說本質(zhì)上是字符設(shè)備)读慎,它們的特點是共享統(tǒng)一的主設(shè)備號10漱贱,但每個設(shè)備可以選擇一個單獨的次設(shè)備號。如果一個字符設(shè)備驅(qū)動要驅(qū)動多個設(shè)備夭委,那么它就不應(yīng)該用misc設(shè)備來實現(xiàn)幅狮。通常情況下,一個字符設(shè)備都不得不在初始化的過程中進行下面的步驟:
通過alloc_chrdev_region()分配主/次設(shè)備號株灸。
使用cdev_init()和cdev_add()來以一個字符設(shè)備注冊自己崇摄。
而一個misc驅(qū)動,則可以只用一個調(diào)用misc_register()來完成這所有的步驟慌烧。
所有的miscdevice設(shè)備形成一個鏈表逐抑,對設(shè)備訪問時,內(nèi)核根據(jù)次設(shè)備號查找對應(yīng)的miscdevice設(shè)備屹蚊,然后調(diào)用其file_operations中注冊的文件操作方法進行操作厕氨。
在Linux內(nèi)核中,使用struct miscdevice來表示miscdevice汹粤。這個結(jié)構(gòu)體的定義為:
struct miscdevice {
int minor;
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
const char *nodename;
mode_t mode;
};
minor是這個混雜設(shè)備的次設(shè)備號命斧,若由系統(tǒng)自動配置,則可以設(shè)置為MISC_DYNANIC_MINOR嘱兼,name是設(shè)備名国葬。
每一個misc驅(qū)動會自動出現(xiàn)在/sys/class/misc下,而不需要驅(qū)動程序作者明確的去做芹壕。
2 混雜設(shè)備的API
包含頭文件
#include<linux/miscdevice.h>
建一個miscdevice結(jié)構(gòu)體
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
在驅(qū)動函數(shù)初始化中注冊這個混雜設(shè)備
misc_register(&misc);
驅(qū)動卸載時胃惜,注銷這個設(shè)備
misc_deregister(&misc);
混雜設(shè)備miscdevice說明
miscdevice結(jié)構(gòu)體
struct miscdevice {
int minor;
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
const char *nodename;
mode_t mode;
};
設(shè)備注冊和注銷
extern int misc_register(struct miscdevice * misc);
extern int misc_deregister(struct miscdevice *misc);
函數(shù)具體實現(xiàn)在/drivers/char/misc.c
在misc_init()函數(shù)中
調(diào)用class_create()函數(shù)創(chuàng)建了一個名misc的類,注冊了一個主設(shè)備號為10哪雕,設(shè)備名為misc的字符設(shè)備。
misc_register()函數(shù)用于注冊一個混雜設(shè)備鲫趁,其主設(shè)備號為10斯嚎,如果次設(shè)備號指定為MISC_DYNAMIC_MINOR將由系統(tǒng)去指定一個次設(shè)備號,調(diào)用device_create創(chuàng)建設(shè)備節(jié)點。
misc_deregister用于注銷這個混雜設(shè)備堡僻,其中調(diào)用了device_destroy刪除設(shè)備節(jié)點糠惫。
3 混雜設(shè)備驅(qū)動實例
LED驅(qū)動程序可以用作一個典型的混雜設(shè)備,下面就簡單的看一份代碼來學(xué)習(xí)一下混雜設(shè)備驅(qū)動代碼的編寫:
/*
* ioctl 控制 LED
* 使用庫函數(shù)钉疫,非操作寄存器硼讽,沒有重映射地址
* 使用混雜設(shè)備驅(qū)動
*/
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#define DEVICE_NAME "GPIO_Control_LED"
#define LED_NUM 4
/* 定義幻數(shù) */
#define LED_IOC_MAGIC 'L'
/* 定義命令 */
#define IOCTL_GPIO_ON _IOR(LED_IOC_MAGIC,1,int) //從用戶空間讀
#define IOCTL_GPIO_OFF _IOR(LED_IOC_MAGIC,2,int)
#define IOCTL_GPIO_GET_STATE _IOWR(LED_IOC_MAGIC,3,int) //向用戶空間寫
//最大命令數(shù)
#define LED_IOC_MAXNR 3
//IO 口
static unsigned long gpio_table [] =
{
S3C2410_GPB5,
S3C2410_GPB6,
S3C2410_GPB7,
S3C2410_GPB8,
};
//IO口 配置
static unsigned int gpio_cfg_table [] =
{
S3C2410_GPB5_OUTP,
S3C2410_GPB6_OUTP,
S3C2410_GPB7_OUTP,
S3C2410_GPB8_OUTP,
};
static int tq2440_gpio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
static int pin_state=0;
static int ret = 0;
static int ioarg = 0;
static int err = 0;
/* 檢測命令的有效性 */
if (_IOC_TYPE(cmd) != LED_IOC_MAGIC)
return -EINVAL;
if (_IOC_NR(cmd) > LED_IOC_MAXNR)
return -EINVAL;
/* 根據(jù)命令類型,檢測參數(shù)空間是否可以訪問 */
if (_IOC_DIR(cmd) & _IOC_READ) //如果是讀命令牲阁,則判斷是否可以向用戶空間 寫
err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
if (err)
return -EFAULT;
/* 根據(jù)命令固阁,來選擇執(zhí)行哪個分支,并根據(jù)傳入的參數(shù)打開城菊、關(guān)閉或得到某個燈的狀態(tài) */
switch(cmd)
{
case IOCTL_GPIO_ON:
ret = __get_user(ioarg, (int *)arg);
printk("SET the LED %d to ON \n",ioarg);
s3c2410_gpio_setpin(gpio_table[ioarg], 0);
return 0;
case IOCTL_GPIO_OFF:
ret = __get_user(ioarg, (int *)arg);
printk("SET the LED %d to OFF \n",ioarg);
s3c2410_gpio_setpin(gpio_table[ioarg], 1);
return 0;
case IOCTL_GPIO_GET_STATE:
ret = __get_user(ioarg, (int *)arg);
pin_state=s3c2410_gpio_getpin(gpio_table[ioarg]);
ret = __put_user(pin_state, (int *)arg);
printk("IN KERNEL ---===>>> I got the stat of pin is %d \n",pin_state);
return 0;
default:
return -EINVAL;
}
}
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.ioctl = tq2440_gpio_ioctl,
};
/* 這個函數(shù)是混雜設(shè)備的關(guān)鍵函數(shù)备燃,用以注冊混雜設(shè)備 */
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR, //自動分配次設(shè)備號
.name = DEVICE_NAME,
.fops = &dev_fops, //關(guān)聯(lián)你的操作函數(shù)
};
static int __init dev_init(void)
{
int ret;
int i;
for (i = 0; i < LED_NUM; i++)
{
s3c2410_gpio_cfgpin(gpio_table[i], gpio_cfg_table[i]);
s3c2410_gpio_setpin(gpio_table[i], 1); //開始時LED全亮
}
ret = misc_register(&misc); //混雜設(shè)備驅(qū)動注冊
printk ("%s initialized ---===>>>\n",DEVICE_NAME);
return ret;
}
static void __exit dev_exit(void)
{
misc_deregister(&misc);
printk ("%s UNloaded ---===>>>\n",DEVICE_NAME);
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("GPIO control for My 2440 Board");