misc类设备与蜂鸣器驱动

来源:互联网 发布:马哥linux培训 编辑:程序博客网 时间:2024/05/15 09:32

以下内容源于朱有鹏《物联网大讲堂》课程的学习,如有侵权,请告知删除。


一、板载蜂鸣器驱动测试

1、驱动部分

(1)九鼎移植内核已经提供了蜂鸣器驱动源码,但如何查找是否已经有驱动文件?

  • 法一,在源码文件集合中搜索关键字buzzer,x210-buzzer……
  • 法二,make menuconfig,然后查找关键字buzzer,知道路径……


(2)是否被编译?

  • 取决于Makefile文件中的宏:obj-$(CONFIG_X210_BUZZER_DRIVER) += x210-buzzer.o;
  • 而宏又取决于Kconfig文件中的项目(X210_BUZZER_DRIVER)是否显示“y”;
  • 而Kconfig文件中的项目是否显示“y”,是通过make menuconfig来配置的(路径和源码文件的路径基本一致的)。

(3)bug排查

  • 修改Makefile中的宏名(将CONFIG_BUZZER_DRIVER改为CONFIG_X210_BUZZER_DRIVER,因为Makefile中的要和Kconfig中的对应(make menuconfig是以Kconfig的为准的,配置后可以在.config文件中看到CONFIG_X210_BUZZER_DRIVER是“y”),最终可以在系统中看到/dev/buzzer

2、应用部分

(1)应用编写

  • 先查看自己编写的驱动文件x210_buzzer.c,看都有哪些操作:打开文件+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>//#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_FREQ1#define PWM_IOCTL_STOP0static struct semaphore lock;// TCFG0在Uboot中设置,这里不再重复设置// Timer0输入频率Finput=pclk/(prescaler1+1)/MUX1//                     =66M/16/16// TCFG0 = tcnt = (pclk/16/16)/freq;// PWM0输出频率Foutput =Finput/TCFG0= freqstatic 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/16tcfg1 &= ~(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设置为inputs3c_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;elsereturn -EBUSY;}static int x210_pwm_close(struct inode *inode, struct file *file){up(&lock);return 0;}// PWM:GPF14->PWM0static 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");

(2)测试实践

  • 编写完下面的app_buzzer.c后,执行 arm-linux-gcc app_buzzer.c -o app.buzzer;
  • 然后将app.buzzer拷贝到开发板根文件系统下,最后./app.buzzer执行。
  • 注意,必须将buzzer编译进内核才能如此操作!

#include <stdio.h>#include <sys/stat.h>#include <fcntl.h>#define DEVNAME"/dev/buzzer" //蜂鸣器对应的设备文件#define PWM_IOCTL_SET_FREQ1   //命令来自驱动文件,应用层知道这些宏的含义#define PWM_IOCTL_STOP0int 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)都属于字符设备。

(4)有一套驱动框架,内核实现一部分(misc.c),驱动实现一部分(x210-buzzer.c)

(5)misc是对原始的字符设备注册接口的一个类层次的封装,很多典型字符设备都可以归类到misc类中,使用misc驱动框架来管理。

(6)这些字符设备不符合预先确定的字符设备范畴

  • 所有这些设备采用主编号10,次设备号不一样。它们一起归于misc device,其实misc_register就是用主标号10调用register_chrdev()的。

2、misc类设备驱动架构

(1)内核开发者实现部分,关键点有2个

  • 一个是类的创建(因此驱动开发者不需要再创建类);
  • 一个是开放给驱动开发者的接口misc_deregister函数。

(2)具体设备驱动工程师实现部分

3、本部分学习方法

(1)蜂鸣器驱动源码已有,分析为主;

(2)复习并验证前面讲的驱动框架的思维;

(3)开始注意一些细节。


三、misc驱动框架源码分析1(内核开发者实现的部分)

1、misc源码框架基础

(1)在/driver/char/misc.c中。

(2)源码框架本身也是一个模块(因此可以实现裁剪),内核启动时自动加载


  • 因此会在/sys/class目录下创建misc子目录,即misc这个类


(3)源码框架的主要工作

  • 注册miscc类;
  • 使用老接口注册字符设备驱动(主设备号为10);
  • 开放device注册的接口misc_register给驱动工程师。

2、misc类设备的注册


(1)驱动工程师需要借助misc来加载自己的驱动时,只需要调用misc_register接口注册自己的设备即可,其余均不用管。

(2)misc_list链表的作用

  • 内核定义了一个misc_list链表(static LIST_HEAD(misc_list);),用来记录所有内核中注册了的杂散类设备。
  • 当我们向内核注册一个misc类设备时,内核就会向misc_list链表中insert一个节点。
  • 之前字符设备时,是固定大小(255)的数组。
  • 当使用cat /proc/misc打印出信息时,其实就是遍历此链表。

#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)主设备号和次设备号的作用和区分。

  • 主设备号,表示类;次设备号,表示某具体设备。


四、misc驱动框架源码分析2(misc.c文件,内核开发者提供)

1、open函数分析



  • 最终还是会映射到x210_buzzer.c中的open函数。

2、misc在proc下的展现


3、内核互斥锁

(1)何为互斥锁?

  • 实现互斥访问。类似于图书馆借书。

(2)定义:DEFINE_MUTEX

  • 解析方法同上面三2(2)

原式子:static DEFINE_MUTEX(misc_mtx);……


(3)上锁mutex_lock和解锁mutex_unlock

  • 要访问某资源时,要给该资源上锁;使用完后,要解锁。
  • 当某进程想访问已经被上锁的资源时,会休眠,直到其他进程解锁后此进程才能访问该资源。类似于上厕所(休眠锁、等待锁)。




(4)内核防止竞争状态的手段:原子访问,自旋锁、互斥锁、信号量

  • 原子访问主要用来做计数;如int count;count++(这个可能被打断)
  • 自旋锁后面讲中断会详细讲;应用于多核心。
  • 互斥锁和信号量很相似(其实就是计数值为1的信号量),互斥锁的出现比信号量晚,实现上比信号量优秀,尽量使用互斥锁。


五、蜂鸣器驱动源码分析1

1、dev_init


(1)信号量

  • init_MUTEX(&lock);//将信号量初始化为1,其示就是互斥锁

(2)miscdevice

static struct miscdevice misc = {.minor = MISC_DYNAMIC_MINOR,//动态次设备号.name = DEVICE_NAME,.fops = &dev_fops,};

(3)gpio_request

(4)printk

2、ioctl


(1)为什么需要ioctl(input output control,输入输出控制)?

  • 这个函数让设备进行输入输出,但为何不用read和write?(因为read和write有缺陷,驱动和应用的交互有麻烦。)
  • 缺陷:写驱动的人定义亮灭,但应用层的人怎么知道呢?

(2)ioctl怎么用


六、蜂鸣器驱动源码分析2

硬件操作有关的代码