一、板载蜂鸣器驱动测试
- 应用编写:打开文件 + ioctl。
#include <stdio.h>
#include <sys/stat.h>
#incldue <fcntl.h>
#define DEVNAME "/dev/buzzer"
#define PWM_IOCTL_SET_FREQ 1
#define PWM_IOCTL_STOP 0
int main(void)
{
int fd=-1;
fd=open(DEVNAME,O_RDWR);
if(fd<0){
perror("open");return -1;
}
ioctl(fd,PWM_IOCTL_SET_FREQ,10000);
sleep(3);
ioctl(fd,PWM_IOCTL_STOP);
sleep(3);
ioctl(fd,PWM_IOCTL_SET_FREQ,3000);
sleep(3);
ioctl(fd,PWM_IOCTL_STOP);
sleep(3);
close(fd);return 0;
}
二、misc 类设备介绍
1、何为misc
- (1)中文名:杂项设备/杂散设备。
- (2)位于/sys/class/misc 中。
- (3)有一套驱动框架,内核实现一部分(misc.c),驱动实现一部分(x210-buzzer.c)。
- (4)misc 是对原始字符设备注册接口的一个类层次的封装,很多典型字符设备可以归到 misc 类,使用 misc 驱动框架来管理。
2、misc 类驱动框架架构
- (1)内核开发者实现部分,关键点有两个:一个是类的创建,另一个是开放给驱动开发者的接口。
- (2)设备工程师具体实现部分。
三、misc驱动框架源码分析1
1、misc 源码框架基础
- (1)misc 源码框架本身也是一个模块,内核启动时自动加载。
- (2)misc 源码框架的主要工作:注册 misc 类(class_create),使用老接口(register_chrdev) 注册字符设备驱动(主设备号 10),开放 misc_register 给驱动工程师。
2、misc 类设备的注册
- (1)驱动工程师需要借助 misc 来加载自己的驱动时,只需要调用 misc_register 接口注册自 己的设备即可,其余不用管。
- (2)misc_list 链表的作用:内核定义了一个 misc_list 链表用来记录所有内核中注册了的杂 散类设备,当我们向内核注册一个 misc 类设备时,内核就会向 misc_list 链表中 insert 一个节点。
四、misc 驱动框架源码分析 2
1、open 函数分析
- (1)open 函数经过层层调用,最终调用来 file_operations 里面的 open 函数。
2、misc 在 proc 下的展现
- (1)调用 proc_create。
3、内核互斥锁
- (1)何为互斥锁:一种特殊的信号量,用来对特定的代码段实行段保护。
- (2)定义:DEFINE_MUTEX
- (3)上锁 mutex_lock 和解锁 mutex_unlock。
- (4)内核防止竞争状态的手段:原子操作、自旋锁、互斥锁、信号量。
- (5)原子访问主要用来做计数、自旋锁后面讲中断会讲、互斥锁和信号量很相似。互斥锁 出现比信号量晚,实现上比信号量优秀,尽量使用互斥锁。
五、蜂鸣器驱动源码分析 1
1、dev_init
- (1)信号量。 (2)miscdevice。 (3)gpio_request。 (4)printk。
2、ioctl
- (1)为什么需要 ioctl:ioctl 即可实现读,也可实现写,且除了命令外还可加参数。
- (2)ioctl 怎么用:见驱动源码。
#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>
#define DEVICE_NAME "buzzer"
#define PWM_IOCTL_SET_FREQ 1
#define PWM_IOCTL_STOP 0
typedef unsigned long u_long;
static struct semaphore lock;
//被ioctl调用// TCFG0 在 Uboot 中设置,这里不再重复设置
static void PWM_Set_Freq(unsigned long freq)
{
u_long tcon,tcnt,tcfg1,pclk;
struct clk* clk_p;
s3c_gpio_cfgpin(S5PV210_GPD0(2),S3C_GPIO_SFN(2));//设置 GPD0_2 为 PWM 输出
tcon=__raw_readl(S3C2410_TCON);
tcfg=__raw_readl(S3C2410_TCFG1);
tcfg1&=~(0xf<<8);//mux=1/16
tcfg1|=(0x4<<8);
__raw_write(tcfg1,S3C2410_TCFG1);
clk_p=clk_get(NULL,"pclk");
pclk=clk_get_rate(clk_p);
tcnt=(pclk/16/16)/freq;
__raw_write(tcnt,S3C2410_TCNTB(2));
__raw_write(tcnt/2,S3C2410_TCMPB(2));//占空比0.5
tcon &= ~(0xf<<12);
tcon |= (0xb<<12);
__raw_writel(tcon, S3C2410_TCON);
tcon &= ~(2<<12);
__raw_writel(tcon, S3C2410_TCON);
}
// 被ioctl调用
void PWM_Stop(void)
{
s3c_gpio_cfgpin(S5PV210_GP0(2),S3C_GPIO_SFN(0));//将GPD0_2设置为input
}
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,unsigned int cmd,unsigned long arg)
{
ip(&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 int __init dev_init(void)
{
.minor=MISC_DYNAMIC_MINOR,
.name=DEVICE_NAME,
.fops=&dev_fops,
}
//设备安装函数
static int __init dev_init(void)
{
int ret=-1;
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);//gpio初始化
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);//注销设备
gpio_free(S5PV210_GPD0(2));//释放资源
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("www.9tripod.com");
MODULE_DESCRIPTION("x210 PWM Driver");