6.misc类设备与蜂鸣器驱动

 1.板载蜂鸣器驱动测试

1.1、驱动部分
(1)移植内核已经提供了蜂鸣器驱动源码
(2)make menuconfig
(3)bug排查。修改Makefile中的宏名,最终可以在系统中看到 /dev/buzzer
1.2、应用部分
(1)应用编写:打开文件+ioctl
(2)测试实践


2.misc类设备介绍

由于Linux驱动倾向于分层设计,所以各个具体的设备都可以找到它归属的类型,从而套到它相应的架构里面去,并且只需要实现最底层的那一部分。但是,也有部分类似buzzer的字符设备,确实不知道它属于什么类型,一般采用miscdevice框架结构。
2.1、何为misc

(1)中文名:杂项设备\杂散设备
(2)/sys/class/misc
(3)典型的字符设备
(4)有一套驱动框架,内核实现一部分(misc.c),驱动实现一部分(x210-buzzer.c)。
(5)misc是对原始的字符设备注册接口的一个类层次的封装,很多典型字符设备都可以归类到misc类中,使用misc驱动框架来管理。
2.2、misc类设备驱动架构
(1)内核开发者实现部分,关键点有2个:一个是类的创建,另一个是开放给驱动开发者的接口
(2)具体设备驱动工程师实现部分


3 . misc类设备原理

3.1、misc源码框架基础
(1)misc源码框架本身也是一个模块,内核启动时自动加载
(2)源码框架的主要工作:注册misc类,使用老接口注册字符设备驱动(主设备号10),开放device注册的接口misc_register给驱动工程师

3.2、misc类设备的注册
(1)驱动工程师需要借助misc来加载自己的驱动时,只需要调用misc_register接口注册自己的设备即可,其余均不用管。
(2)misc_list链表的作用。内核定义了一个misc_list链表用来记录所有内核中注册了的杂散类设备。当我们向内核注册一个misc类设备时,内核就会向misc_list链表中insert一个节点。

#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
    struct list_head name = LIST_HEAD_INIT(name)

原式子:static LIST_HEAD(misc_list);
展开后:static struct list_head misc_list = { &(misc_list), &(misc_list) }


3.3 主设备号和次设备号的作用和区分

miscdevice本质上也是字符设备

只是在miscdevice核心层的misc_init()函数中,通过register_chrdev(MISC_MAJOR, "misc",&misc_fops)注册了字符设备

而具体miscdevice实例调用misc_register()的时候又自动完成了device_create()、获取动态次设备号的动作

每个misc设备的主设备号都是MISC_MAJOR = 10

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 char *nodename;
	mode_t mode;
};

如果minor为MISC_DYNAMIC_MINOR, miscdevice核心层会自动找一个空闲的次设备号,否则用minor指定的次设备号

miscdevice驱动的注册和注销分别用下面两个API:
 

int misc_register(struct miscdevice * misc);
int misc_deregister(struct miscdevice *misc);

因此miscdevice驱动的一般结构形如:
 

static const struct file_operations xxx_fops = {
    .unlocked_ioctl = xxx_ioctl,
    .open= xxx_open,
};
static struct miscdevice xxx_dev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "xxx",
    .fops = &xxx_fops
};
static int __init xxx_init(void)
{
    printk("ARC Hostlink driver mmap at 0x%p\n", __HOSTLINK__);
    return misc_register(&xxx_dev);//注册
}
static void __exit xxx_exit(void)
{
    misc_deregister(&xxx_dev);//注销
}

4. 注册和注销接口代码解析

注册:

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);//把该设备挂到misc设备链表上
 out:
	mutex_unlock(&misc_mtx);
	return err;
}

注销

int misc_deregister(struct miscdevice *misc)
{
	int i = DYNAMIC_MINORS - misc->minor - 1;//得到次设备号

	if (list_empty(&misc->list))//得到的设备节点为空
		return -EINVAL;

	mutex_lock(&misc_mtx);
	list_del(&misc->list);//从链表中删除这个次设备号
	device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));//删除设备文件
	if (i < DYNAMIC_MINORS && i >= 0)
		clear_bit(i, misc_minors);//清除位图的该位
	mutex_unlock(&misc_mtx);
	return 0;
}

重要的函数和结构体:

static LIST_HEAD(misc_list);//链表初始化
static DEFINE_MUTEX(misc_mtx);//定义互斥锁


#define DYNAMIC_MINORS 64 /* like dynamic majors */
static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS);//定义位图

//                 Misc设备打开函数
static int misc_open(struct inode * inode, struct file * file)

//          misc设备类
static struct class *misc_class;

//          misc的操作结构体
static const struct file_operations misc_fops = {
	.owner		= THIS_MODULE,
	.open		= misc_open,
};

//         misc注册函数
int misc_register(struct miscdevice * misc)

//         misc注销函数
int misc_deregister(struct miscdevice *misc)

//   导出
EXPORT_SYMBOL(misc_register);
EXPORT_SYMBOL(misc_deregister);

static char *misc_devnode(struct device *dev, mode_t *mode)//检验读写权限

static int __init misc_init(void)//初始化

subsys_initcall(misc_init);

 

5. struct file的好处

可以利用struct file的private_data挂接驱动的结构体,这就可以通过struct file找到驱动的结构体

每一个文件都有一个对应的结构体,这个结构体非常重要

struct file {  //有些变量我没有写上

	const struct file_operations	*f_op;  //用于挂接设备驱动的file_operations
	spinlock_t		f_lock;  /* f_ep_links, f_flags, no IRQ */
	atomic_long_t		f_count;
	unsigned int 		f_flags;
	fmode_t			f_mode;
	loff_t			f_pos;
	struct fown_struct	f_owner;
	const struct cred	*f_cred;
	struct file_ra_state	f_ra;

	u64			f_version;

	void			*private_data;  //用作私有数据,可以挂接设备结构体

	/* Used by fs/eventpoll.c to link all the hooks to this file */
	struct list_head	f_ep_links;

	unsigned long f_mnt_write_state;
};

在调用misc_register(&xxx_dev)时,该函数内部会自动调用device_create(),而device_create()会以xxx_dev作为drvdata参数。其次,在miscdevice核心层misc_open()函数的帮助下,在file_operations的成员函数中, xxx_dev会自动成为file的private_data(misc_open会完成file->private_data的赋值操作)。

static int misc_open(struct inode * inode, struct file * file)
{
	int minor = iminor(inode);//得到次设备号
	struct miscdevice *c;
	int err = -ENODEV;
	const struct file_operations *old_fops, *new_fops = NULL;

	mutex_lock(&misc_mtx);//上互斥锁
	
	list_for_each_entry(c, &misc_list, list) {
		if (c->minor == minor) {//查找次设备号相同的设备
			new_fops = fops_get(c->fops);	//得到file_operations
			break;
		}
	}
		
	if (!new_fops) {//如果为空
		mutex_unlock(&misc_mtx);
		request_module("char-major-%d-%d", MISC_MAJOR, minor);
		mutex_lock(&misc_mtx);

		list_for_each_entry(c, &misc_list, list) {
			if (c->minor == minor) {
				new_fops = fops_get(c->fops);
				break;
			}
		}
		if (!new_fops)
			goto fail;
	}

	err = 0;
	old_fops = file->f_op;//得到老的文件的file_operations
	file->f_op = new_fops;//变为自己定义的file_operations
	if (file->f_op->open) {//如果file_operations的open变量不为空
		file->private_data = c;//把设备miscdevice 挂接到文件的私有数据上
		err=file->f_op->open(inode,file);//判断能不能打开文件
		if (err) {//不能打开
			fops_put(file->f_op);
			file->f_op = fops_get(old_fops);
		}
	}
	fops_put(old_fops);
fail:
	mutex_unlock(&misc_mtx);
	return err;
}

如果我们用面向对象的封装思想把一个设备的属性、自旋锁、互斥体、等待队列、 miscdevice等封装在一个结构体里面:

struct xxx_dev {
unsigned int version;
unsigned int size;
spinlock_t lock;
//...
struct miscdevice miscdev;
};

在file_operations的成员函数中,就可以通过container_of()和file->private_data反推出xxx_dev的实例
 

static long xxx_ioctl(struct file *file, unsigned int cmd, unsigned long i)
{
struct xxx_dev *xxx = container_of(file->private_data,struct xxx_dev, miscdev);
//...
}

 

蜂鸣器驱动:

#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);
	
	/* 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 Driver");


 

 

 

 

 

 

 

 

 


 

 

 

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值