linux驱动开发之misc类设备介绍

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux驱动开发之misc类设备介绍相关的知识,希望对你有一定的参考价值。

    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” 博客,谢绝转载!

以上是关于linux驱动开发之misc类设备介绍的主要内容,如果未能解决你的问题,请参考以下文章

手把手教你从零实现Linux misc设备驱动一(基于友善之臂4412开发板)

linux设备驱动之misc驱动框架源码分析

linux驱动开发之misc设备与蜂鸣器驱动

misc子系统

Linux驱动之混杂设备(misc)

Linux驱动开发MISC