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
时间: 2024-08-08 17:44:05

linux驱动开发之misc类设备介绍的相关文章

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

1.板载蜂鸣器的驱动测试 我手里有一个BSP,九鼎的Bsp,里面有蜂鸣器的驱动,我们先测试一下好不好用.我们拿到一个BSP时,如果要做或移植蜂鸣器的驱动,首先要确定下这个内核 中究竟有没有蜂鸣器的驱动,我们可以用sourceInsight将内核放进去,搜索buzzer这个文件,看有没有,如果不行,也可以在内核中输入make menuconfig,利用这个配置界面来搜索buzzer英文,看不能找到相应的信息,从而也会知道这个设备在哪个路径下,通过对九鼎的内核进行make menuconfig后,搜

linux驱动开发之HelloWorld

最近实习,公司项目搞的是平板开发,而我分配的任务是将驱动加载到内核中. 准备工作,必要知识了解:加载有两种方式,一种是动态加载和卸载即模块加载,另一种是直接编译进入内核:Linux内核把驱动程序划分为3种类型:字符设备.块设备和网络设备.字符设备和块设备可以像文件一样被访问.它们的主要区别不在于能否seek,而是 在于系统对于这两种类型设备的管理方式.应用程序对于字符设备的每一个I/O操作,都会直接传递给系统内核对应的驱动程序:而应用程序对于块设备的操作, 要经过系统的缓冲区管理,间接传递给驱动

linux驱动开发之framebuffer驱动介绍

framebuffer是linux里面的显示设备.在驱动底下如何操作lcd实现图形的显示. 1.什么是framebuffer? (1)首先想一下在裸机中我们是怎么操作LCD的. Soc内部有lcd的控制器,Soc外面有lcd的驱动器,lcd的驱动器连接着lcd的屏幕,Soc的内部还有CPU,外部还有DDR内存.这些设备都参与到了lcd的显示中. 在裸机中我们是怎么搞的呢,lcd的驱动器和lcd的控制器之间通过排线进行链接,连接的接口就是lcd所特有的一个接口.lcd控制器里面是很多和lcd相关的

(56)Linux驱动开发之二

内核基础 1.linux内核主要是由进程调度.内存管理.虚拟文件系统(字符设备驱动和块设备驱动).网络接口(网络设备驱动)和进程通信5个子系统组成的. 1)进程调度控制系统中的多个进程对CPU的访问,使得多个进程能在CPU中"微观串行,宏观并行"地执行. 2)内存管理的主要作用就是控制多个进程安全的共享主内存区域,当CPU提供内存管理单元时,linux内存管理完成为每个进程进行虚拟内存到物理内存的转换.一般而言,linux的每一个进程享有4GB的内存空间.0~3GB为用户空间,3~4G

linux驱动开发之framebuffer应用编程实践(一)

1.framebuffer应用编程 (1)打开设备文件 (2)获取设备信息 宏定义的命令在/linux/fb.h中 不可变信息FSCREENINFO,使用ioctl参数有FBIOGET_FSCREENINFO宏名,表示用ioctl从驱动中获取lcd设备的不变的信息 可变信息VSCREENINFO,使用ioctl参数有FBIOGET_VSCREENINFO宏名,表示用ioctl从驱动中获取lcd设备的可变信息 fb的驱动框架将屏幕的所有硬件信息分为了两类,一类为不可变的,是通过软件不可更改的(比如

嵌入式Linux驱动开发之helloword心得

自从选择了物联网这个专业,智能XX的字样牵动着每一个学习这个专业的孩子. 大家兴致勃勃的来到了学校,结果一切想象和自己的设想并不一样.想象中的各种智能般梦幻的场景变成了真实的高数/电路/模电等等诸如此类!不知道这个世界什么时候变得如此的浮躁,当大家的一段时间的努力看不到结果的时候就往往会不太感兴趣,模电大家都没听懂,于是大家自我安慰tmd学这玩意到底干什么?本人当初也是这样,可是到了后来接触了单片机,接触了应用电路的设计才知道那些课程那个没用啊!当初还是too young,too simple呀

linux驱动开发之GCC问题

最近正在学习驱动开发,进展到字符设备驱动开发阶段. 先不多说,首先把刚看的一篇学习驱动步骤的帖子记录如下: 1. 学会写简单的makefile 2. 编一应用程序,可以用makefile跑起来 3. 学会写驱动的makefile 4. 写一简单char驱动,makefile编译通过,可以insmod, lsmod, rmmod. 在驱动的init函数里打印hello world, insmod后应该能够通过dmesg看到输出. 5. 写一完整驱动, 加上read, write, ioctl, p

驱动开发之I2C总线

驱动开发之I2C总线: I2C:数据线和时钟线. 起始信号:时钟线为高电平,数据线由高到低跳变. 结束信号:时钟线为高电平,数据线由低到高跳变. 应答信号:第九个时钟周期,时钟线保持为高电平,数据线为低电平,此时为成功应答. 读写位:站在主机的角度考虑. 0代表主机给从机发送数据. 1代表主机接收从机的数据. 硬件原理: 数据帧的封装:主机给从机发送数据: 起始信号 + 7位从机地址 写位 + 从机给主机应答 + 主机给从机发送8位数据 + 从机给主机应答 ... + 主机给从机发送8位数据 +

Android驱动开发之Hello实例

Android驱动开发之Hello实例: 驱动部分 modified:   kernel/arch/arm/configs/msm8909-1gb_w100_hd720p-perf_defconfig modified:   kernel/arch/arm/configs/msm8909-1gb_w100_hd720p_defconfig modified:   kernel/drivers/input/misc/Kconfig modified:   kernel/drivers/input/