本文转载自:http://whylinux.blog.51cto.com/10900429/1932491

字符设备驱动用的fileopretion结构体。

1、板载蜂鸣器的驱动测试

我手里有一个BSP,九鼎的Bsp,里面有蜂鸣器的驱动,我们先测试一下好不好用。我们拿到一个BSP时,如果要做或移植蜂鸣器的驱动,首先要确定下这个内核

中究竟有没有蜂鸣器的驱动,我们可以用sourceInsight将内核放进去,搜索buzzer这个文件,看有没有,如果不行,也可以在内核中输入make menuconfig,利用这个配置界面来搜索buzzer英文,看不能找到相应的信息,从而也会知道这个设备在哪个路径下,通过对九鼎的内核进行make menuconfig后,搜索buzzer后,知道buzzer的驱动在/driver/char/buzzer/目录下,去这个目录中看,发现了x210-buzzer.c这么一个文件还有makefile和kconfig,说明这个文件就是驱动文件,这样我们就找到驱动代码,同时也说明在这个内核中是有蜂鸣器的驱动代码的。你还可以通过在内核源码目录下,通过输入grep "buzzer" * -nR的方式进行搜索含有buzzer字样的位置,从而确定你的板子的蜂鸣器驱动是哪个,或者有没有。

我们九鼎内核中的蜂鸣器的驱动源代码在/driver/char/buzzer/x210-buzzer.c中。这个驱动有没有工作,或者被编译到内核中,那就要取决于这个目录下的makefile文件中

1
obj-$(CONFIG_BUZZER_DRIVER) += x210-buzzer.o

CONFIG_BUZZER_DRIVER宏是否定义了,这个宏是否定义就要取决于这个目录下的kconfig文件中

1
2
3
4
5
config X210_BUZZER_DRIVER
    bool "x210 buzzer driver"
    default y
    help
    compile for buzzer driver,y for kernel,m for module.

给的值到底是y还是n了。这个kconfig的给的这个宏的值是y还是n,取决于make menuconfig中,你是否选择了这个蜂鸣器驱动。你也可以在内核源码目录下的.config文件中看这个CONFIG_BUZZER_DRIVER宏的值是否为y来确定是非让其编译到内核中。

蜂鸣器这个设备应该是属于misc设备的,所以按道理来说make menuconfig时应该是在misc设备中去找的,但是因为九鼎移植的时候很乱,并没有将蜂鸣器的驱动放在misc设备目录中,而是放在了char目录下,所以make menuconfig时我们要在char类型的设备下找这个蜂鸣器设备的驱动,看是否被使能了,如果使能了说明那个CONFIG_BUZZER_DRIVER宏就是被使能的了,我们也可以在源码目录下的.config文件中观察确认。

因为九鼎内核中已经提供了蜂鸣器的源码驱动,我们在make menuconfig之后在char类型的设备驱动中找到了x210 buzzer drvier选项,这个蜂鸣器的驱动,使能后重新编译内核,此时内核中就会有蜂鸣器的驱动了。

misc设备杂散类设备,驱动加载成功后,会在系统/dev目录下创建出一个设备节点文件出来,从而进行操作,但是我们系统启动后,在/dev目录下并没有看到buzzer这个设备节点文件,这是因为九鼎提供的蜂鸣器驱动有一个bug,这个bug就是蜂鸣器驱动源代码所在的目录,也就是/driver/char/buzzer/目录,这个目录里面的makefile的obj后面的宏是CONFIG_BUZZER_DRIVER,但是kconfig文件中的config名字叫做X210_BUZZER_DRIVER,全名为CONFIG_X210_BUZZER_DRVIER,在我们make menuconfig的时候已经将蜂鸣器驱动选上了,在.config文件中确实能够看到这个宏已经为y了,宏的名字叫做CONFIG_X210_BUZZER_DRIVER,但是makefile中obj后面的名字叫做CONFIG_BUZZER_DRVIER,这是不对的,makefile中的宏名也应该叫做CONFIG_X210_BUZZER_DRIVER,这样才能真正的编译进行去。修改的方法很简单,就是进去到源码的/drvier/char/buzzer目录下,将makefile中obj后面的宏名改为CONFIG_X210_BUZZER_DRIVER,这样就和.config文件中的蜂鸣器驱动使能的宏名一样了,此时makefiel才会将蜂鸣器驱动真正的编译到内核中。

此时系统在启动后,就会在/dev目录下看到buzzer这个设备驱动文件节点。此时就可以正常使用这个设备文件来操作蜂鸣器驱动了。这个时候我们就可以写应用程序控制蜂鸣器,来验证蜂鸣器驱动是否好用了。要想写应用程序,就要知道驱动是怎么实现的,应用程序和驱动是配套的,驱动中提供了哪些应用程序来操作蜂鸣器的接口程序,所以此时我们要看下蜂鸣器驱动代码是怎么实现的,从而知道驱动提供了哪些接口函数给应用层。蜂鸣器的驱动代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#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");

我们观察到这个misc设备蜂鸣器设备的驱动用的是file_opreations结构体,这个结构体里面绑定了几个成员函数,就是对应的操作蜂鸣器驱动的方法,其中open和close函数没有什么内容,就是上锁和解锁操作。在file_opreation结构体中成员中,还有一个成员ioctl,绑定的是x210_pwm_ioctl函数,观察代码可以知道,这个蜂鸣器驱动是使用x210_pwm_ioctl函数来操作蜂鸣器设备的,既然这个函数被绑定到了ioctl中,那么应用程序就是使用ioctl这个函数来进行操作蜂鸣器的。

我们可以在linux系统中用man 3 ioctl来知道ioctl函数有几个参数,第一个参数是文件描述符,第二个参数是命令码,之后就是arg。

在驱动中也可以看出和ioctl绑定的x210_pwm_ioctl函数的参数第一个是文件节点,就是文件描述符。在x210_pwm_iotcl函数的代码,可以看出来cmd参数就是命令码,可以看出PWM_IOCTL_SET_FREQ宏和PWM_IOCTL_STOP宏就是操作蜂鸣器设备文件的命令码,看代码知道第一个命令码宏是打开蜂鸣器并且设置其频率,第二个命令码宏是关闭蜂鸣器,第一个命令码宏需要再带参数arg,带的参数rag就是频率,第二个命令码宏不需要再带参数arg。

既然已经知道了应用程序操作蜂鸣器设备的方法是使用ioctl这个函数,第一个参数是设备文件的描述符,第二个参数是命令码,第三个参数是arg参数,在命令码为PWM_IOCTL_SET_FREQ宏时,代表打开蜂鸣器并设备蜂鸣器的频率,需要第三个参数arg来表示频率是多少,在命令码为PWM_IOCTL_STOP时,表示关闭蜂鸣器设备,不需要arg参数,那么我们就可以写应用程序来使用驱动提供的api接口来操作蜂鸣器设备了。

应用程序利用九鼎的蜂鸣器驱动操作蜂鸣器设备的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>
#include <sys/stat.h>
#include <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;
}

将应用程序编译运行后,验证确实可以操作蜂鸣器。

linux驱动开发之九鼎板载蜂鸣器驱动测试【转】的更多相关文章

  1. linux驱动开发—基于Device tree机制的驱动编写

    前言Device Tree是一种用来描述硬件的数据结构,类似板级描述语言,起源于OpenFirmware(OF).在目前广泛使用的Linux kernel 2.6.x版本中,对于不同平台.不同硬件,往 ...

  2. 嵌入式linux驱动开发之点亮led(驱动编程思想之初体验)

    这节我们就开始开始进行实战啦!这里顺便说一下啊,出来做开发的基础很重要啊,基础不好,迟早是要恶补的.个人深刻觉得像这种嵌入式的开发对C语言和微机接口与原理是非常依赖的,必须要有深厚的基础才能hold的 ...

  3. C++第三十八篇 -- 研究一下Windows驱动开发(二)--WDM式驱动的加载

    基于Windows驱动开发技术详解这本书 一.简单的INF文件剖析 INF文件是一个文本文件,由若干个节(Section)组成.每个节的名称用一个方括号指示,紧接着方括号后面的就是节内容.每一行就是一 ...

  4. C++第四十篇 -- 研究一下Windows驱动开发(三)-- NT式驱动的基本结构

    对于NT式驱动来说,主要的函数是DriverEntry例程.卸载例程及各个IRP的派遣例程. 一.驱动加载过程与驱动入口函数(DriverEntry) 和编写普通应用程序一样,驱动程序有个入口函数,也 ...

  5. 驱动开发:内核运用LoadImage屏蔽驱动

    在笔者上一篇文章<驱动开发:内核监视LoadImage映像回调>中LyShark简单介绍了如何通过PsSetLoadImageNotifyRoutine函数注册回调来监视驱动模块的加载,注 ...

  6. [Windows驱动开发](三)基础知识——驱动例程

    一.NT式驱动的基本例程 1. 驱动入口函数——DriverEntry // 驱动程序的一般性定义 NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObje ...

  7. linux驱动开发(四) 字符设备驱动框架(自动创建设备节点)

    代码如下 #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> # ...

  8. linux驱动开发(三) 字符设备驱动框架

    还是老规矩先上代码 demo.c #include <linux/init.h> #include <linux/module.h> #include <linux/ke ...

  9. Windows7下驱动开发与调试体系构建——1.驱动开发的环境准备

    目录/参考资料:https://www.cnblogs.com/railgunRG/p/14412321.html 系统基础环境 开发环境 win7下开发驱动需要安装vs,这里使用2017. 安装vs ...

随机推荐

  1. mysql语句优化方案(网上流传)

    关于mysql处理百万级以上的数据时如何提高其查询速度的方法 最近一段时间由于工作需要,开始关注针对Mysql数据库的select查询语句的相关优化方法. 由于在参与的实际项目中发现当mysql表的数 ...

  2. java 汉字保存到mysql 乱码

    保存之前正常,插入数据乱码 确认jsp mysql编码都确定为utf8 在连接数据库是加上编码 jdbc:mysql://localhost:3306/test?useUnicode=true& ...

  3. LeetCode OJ--Evaluate Reverse Polish Notation

    http://oj.leetcode.com/problems/evaluate-reverse-polish-notation/ 栈使用 #include <iostream> #inc ...

  4. 洛谷—— P1605 迷宫

    P1605 迷宫 题目背景 迷宫 [问题描述] 给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过.给定起点坐标和 终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案.在 ...

  5. windows安装RabbitMQ注意事项

    1.首先下载好ERLANG.RabbitMQ安装包,先安装erlang,设置好环境变量,然后再去安装MQ; 2.别人有两个报错: 一:RabbitMQ安装目录中不允许有空格; 二:安装rabbitmq ...

  6. 阿里云***(java应用)

    阿里云***(FQ)实战 前言 ​ 因为公司涉及国外业务,依赖于google的map服务,生产环境我们使用的是亚马逊服务器,所以访问google地图没问题,但是国内的开发.测试环境,使用的是阿里云,想 ...

  7. 将文件从已Root Android手机中copy出来的几个cmd窗口命令

    将文件从已Root Android手机中copy出来的几个cmd窗口命令: 以shell身份登录adbadb shell进入adb后切换至root用户su更改文件的所属chown shell *更改文 ...

  8. 数字巨头们的表态--<大佬与大话>

    作者魏武挥 类别非虚构 / 中篇 本书为作者为<21世纪商业评论>的专栏文章合集,共20篇,算是第一卷吧,后期还会写下去.这个专栏的名字叫<大佬与大话>,专门收集TMT圈子商业 ...

  9. Qt5官方demo解析集30——Extending QML - Binding Example

    本系列全部文章能够在这里查看http://blog.csdn.net/cloud_castle/article/category/2123873 接上文Qt5官方demo解析集29--Extendin ...

  10. 【Todo】git的fast forward & git命令学习 & no-ff

    git的fast-forward在之前的文章有介绍过,但是介绍的不细: http://www.cnblogs.com/charlesblc/p/5953066.html fast-forward方式就 ...