1、什么是misc设备?
misc是英文的简称,中文名一般叫做杂项设备/杂散设备。
我们知道大部分的设备都有一个明确的分类class,有一些设备进行分类时不太好分,我们不知道一些设备到底应该分到哪一类设备中去,所以最后将这些不知道分到哪类中的设备给分到misc设备中,也就是分到了杂散类中。像蜂鸣器还有ADC设备都被分到了misc设备杂散类设备中。杂散类设备对应的是一个类的概念。随意进到系统中的/sys/class目录中就会看到一个misc目录,这个misc就是杂散类设备,进入到misc目录中,我们可以看到很多设备被丢入到了杂散类设备中。
其实led也可以丢到misc杂散类设备中的,像buzzer也可以放到别的设备类中,所以这种东西并不是一定的,有些人就是不按照常规做事,见到时也不要奇怪。
杂散类设备是典型的字符设备,所以归到misc杂散类设备中的全部都是字符类设备。在后面我们也会看到misc杂散类设备本身就是被作为字符设备来来注册到内核中的,主设备号是10,是固定的表示misc设备,次设备号才是来区分具体杂散类设备的。比如九鼎的蜂鸣器驱动的主设备号是10,次设备号是61,adc驱动的主设备号是10,次设备号是131.就像我们以前实现led的驱动一样,我们将板子上的四个led都归到了leds类中了,每个led的主设备号都是一样,次设备号是不一样的,主设备号一样表示同一类,次设备号不一样表示这类设备中的不同个体,来进行区分led1,led2,led3,led4。杂散类设备的主设备号就是10.
想要自动创建设备文件节点,就需要用udev或者mdev,想要udev或者mdev自动创建设备文件节点给一些设备驱动,那么就要将这些设备驱动给归到一个class设备驱动类中。内核之所以发明misc杂散类设备就是为将一些不知道该怎么归类的设备给放到misc中,将来创建设备节点的时候,可以使用udev或者mdev的机制来进行创建,不需要自己手动的去mknode创建设备文件节点,以及在驱动代码中进行创建类等。我们只需要创建设备类就行。
misc有一套驱动框架,内核实现了一部分(misc.c,在/drvier/char/misc/misc.c),驱动实现了一部分(x210-buzzer.c,这部分就是调用misc驱动框架中内核实现的那部分中的misc_register函数来进行创建设备,Misc_register函数中间接的调用了device_create函数创建设备)。内核实现的一部分中就创建出了misc类,我们驱动开发者只需要调用内核实现的接口misc_register函数,从而间接的调用device_create函数创建设备。我们只需要创建设备,misc类已经不用我恩去创建了。misc类已经由misc驱动框架提供了。
misc设备其实就是一种字符设备。很多典型的字符设备都可以归到misc设备中。
2、misc类设备的驱动架构
(1)内核开发者实现了一部分,具体工程师实现了一部分。内核开发着实现的misc设备的一部分是/drvier/char/misc/misc.c中,主要是misc类的创建。如代码
static int __init misc_init(void)
{
int err;
#ifdef CONFIG_PROC_FS
proc_create("misc", 0, NULL, &misc_proc_fops);
#endif
misc_class = class_create(THIS_MODULE, "misc");
err = PTR_ERR(misc_class);
if (IS_ERR(misc_class))
goto fail_remove;
err = -EIO;
if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) //注册字符设备
goto fail_printk;
misc_class->devnode = misc_devnode;
return 0;
fail_printk:
printk("unable to get major %d for misc devices\n", MISC_MAJOR);
class_destroy(misc_class);
fail_remove:
remove_proc_entry("misc", NULL);
return err;
}和给驱动开发者提供的misc_regiter函数接口。主要就是创建misc设备
int misc_register(struct miscdevice * misc)
{
struct miscdevice *c;
dev_t dev;
int err = 0;
INIT_LIST_HEAD(&misc->list);
mutex_lock(&misc_mtx);
list_for_each_entry(c, &misc_list, list) {
if (c->minor == misc->minor) {
mutex_unlock(&misc_mtx);
return -EBUSY;
}
}
if (misc->minor == MISC_DYNAMIC_MINOR) {
int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
if (i >= DYNAMIC_MINORS) {
mutex_unlock(&misc_mtx);
return -EBUSY;
}
misc->minor = DYNAMIC_MINORS - i - 1;
set_bit(i, misc_minors);
}
dev = MKDEV(MISC_MAJOR, misc->minor);
misc->this_device = device_create(misc_class, misc->parent, dev,
misc, "%s", misc->name); //创建设备
if (IS_ERR(misc->this_device)) {
int i = DYNAMIC_MINORS - misc->minor - 1;
if (i < DYNAMIC_MINORS && i >= 0)
clear_bit(i, misc_minors);
err = PTR_ERR(misc->this_device);
goto out;
}
/*
* Add it to the front, so that later devices can "override"
* earlier defaults
*/
list_add(&misc->list, &misc_list);
out:
mutex_unlock(&misc_mtx);
return err;
}驱动开发者实现的部分是x210-buzzer.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
#include <plat/regs-timer.h>
#include <mach/regs-irq.h>
#include <asm/mach/time.h>
#include <linux/clk.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <plat/gpio-cfg.h>
//#include <plat/regs-clock.h>
//#include <plat/regs-gpio.h>
//#include <plat/gpio-bank-e.h>
//#include <plat/gpio-bank-f.h>
//#include <plat/gpio-bank-k.h>
#define DEVICE_NAME "buzzer"
#define PWM_IOCTL_SET_FREQ 1
#define PWM_IOCTL_STOP 0
static struct semaphore lock;
// TCFG0在Uboot中设置,这里不再重复设置
// Timer0输入频率Finput=pclk/(prescaler1+1)/MUX1
// =66M/16/16
// TCFG0 = tcnt = (pclk/16/16)/freq;
// PWM0输出频率Foutput =Finput/TCFG0= freq
static void PWM_Set_Freq( unsigned long freq )
{
unsigned long tcon;
unsigned long tcnt;
unsigned long tcfg1;
struct clk *clk_p;
unsigned long pclk;
//unsigned tmp;
//设置GPD0_2为PWM输出
s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(2));
tcon = __raw_readl(S3C2410_TCON);
tcfg1 = __raw_readl(S3C2410_TCFG1);
//mux = 1/16
tcfg1 &= ~(0xf<<8);
tcfg1 |= (0x4<<8);
__raw_writel(tcfg1, S3C2410_TCFG1);
clk_p = clk_get(NULL, "pclk");
pclk = clk_get_rate(clk_p);
tcnt = (pclk/16/16)/freq;
__raw_writel(tcnt, S3C2410_TCNTB(2));
__raw_writel(tcnt/2, S3C2410_TCMPB(2));//占空比为50%
tcon &= ~(0xf<<12);
tcon |= (0xb<<12); //disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0
__raw_writel(tcon, S3C2410_TCON);
tcon &= ~(2<<12); //clear manual update bit
__raw_writel(tcon, S3C2410_TCON);
}
void PWM_Stop( void )
{
//将GPD0_2设置为input
s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(0));
}
static int x210_pwm_open(struct inode *inode, struct file *file)
{
if (!down_trylock(&lock))
return 0;
else
return -EBUSY;
}
static int x210_pwm_close(struct inode *inode, struct file *file)
{
up(&lock);
return 0;
}
// PWM:GPF14->PWM0
static int x210_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
switch (cmd)
{
case PWM_IOCTL_SET_FREQ:
printk("PWM_IOCTL_SET_FREQ:\r\n");
if (arg == 0)
return -EINVAL;
PWM_Set_Freq(arg);
break;
case PWM_IOCTL_STOP:
default:
printk("PWM_IOCTL_STOP:\r\n");
PWM_Stop();
break;
}
return 0;
}
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.open = x210_pwm_open,
.release = x210_pwm_close,
.ioctl = x210_pwm_ioctl,
};
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
static int __init dev_init(void)
{
int ret;
init_MUTEX(&lock);
ret = misc_register(&misc); //注册misc
/* GPD0_2 (PWMTOUT2) */
ret = gpio_request(S5PV210_GPD0(2), "GPD0");
if(ret)
printk("buzzer-x210: request gpio GPD0(2) fail");
s3c_gpio_setpull(S5PV210_GPD0(2), S3C_GPIO_PULL_UP);
s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(1));
gpio_set_value(S5PV210_GPD0(2), 0);
printk ("x210 "DEVICE_NAME" initialized\n");
return ret;
}
static void __exit dev_exit(void)
{
misc_deregister(&misc);
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("www.9tripod.com");
MODULE_DESCRIPTION("x210 PWM D本文出自 “whylinux” 博客,谢绝转载!
原文:http://whylinux.blog.51cto.com/10900429/1934097