蜂鸣器驱动
1 linux驱动的代码重用
静态重用 将代码放到其他文件,使用时include进来
动态重用 一个linux驱动可以使用另一个linux驱动中的资源
1.1编泽多个文件组成的linux驱动
该驱动含有四个文件分别是:main.c fun.c product.h product.c
Main.c文件,示例代码如下:
#include
#include
#include
#include
#include
#include
#include "product.h"
//引用外部文件中的函数
extern int add(int a,int b);
//初始化linux驱动,__init不是必需,但可以提高linux的运行效率
static int __init main_init(void){
int a=10;
int b=20;
printk("multi_file_driver_init_success\n");
//调用外部文件中的add函数
printk("%d+%d=%d\n ",a,b,add(a,b));
printk("product name:%s\n",get_product_name());
return 0;
}
//卸载linux驱动
static void __exit main_exit(void){
//日志信息
printk("multi_file_driver_exit_success\n");
}
module_init(main_init);
module_exit(main_exit);
MODULE_AUTHOR("retacn");
MODULE_DESCRIPTION("multi file driver");
MODULE_ALIAS("multi file module");
MODULE_LICENSE("GPL");
Fun.c文件,示例代码如下:
//两个整数相加
int add(int a,int b){
return a+b;
}
Product.h文件,示例代码如下:
extern char* get_product_name(void);
Product.c文件,示例代码如下:
#include "product.h"
//返回产品名称
char* get_product_name(void){
return "android phone";
}
Build.sh文件,示例代码如下:
#build.sh
source /opt/linux/driver/common.sh
make -C $UBUNTU_KERNEL_PATH M=$PWD
testing=$(lsmod | grep "multi_file_driver")
if [ "$testing" != "" ]; then
rmmod multi_file_driver
fi
insmod $PWD/multi_file_driver.ko
测试结果
root@vm:/opt/linux/driver/multi_file_driver# dmesg
[ 2438.254007] multi_file_driver_init_success
[ 2438.254009] 10+20=30
[ 2438.254009] product name:android phone
1.2 linux驱动模块的依赖(导出符号) 动态重用
可以使用以下两个宏来导出函数
EXPROT_SYMBOL(符号名)
EXPTOR_SYMBOL_GPL(符号名)
symbol_producer.c文件内容如下:
#include
#include
#include
#include
#include
#include
//导出常量
static const char* symbol_const= "export const value";
static int result=0;
//导进函数,计算两个数相加
static int add(int a,int b){
return a+b;
}
//导出函数,计算两个数相减
static int sub(int a,int b){
return a-b;
}
//初始化驱动
static int __init symbol_producer_init(void){
int a=20;
int b=10;
printk("symbol_producer_init_success\n");
printk("%d + %d = %d\n",a,b,add(a,b));
printk("%d -%d = %d \n",a,b,sub(a,b));
//初始化result变量的值
result=add(a,b)+sub(a,b);
return 0;
}
//卸载驱动
static void __exit symbol_producer_exit(void){
//输出日志
printk("symbol_producer_exit_success\n");
}
module_init(symbol_producer_init);
module_exit(symbol_producer_exit);
//在使用export_symbol_gpl导出符号时,必需定义MODULE_LICENSE("GPL");
EXPORT_SYMBOL(add);
EXPORT_SYMBOL(result);
EXPORT_SYMBOL_GPL(sub);
EXPORT_SYMBOL_GPL(symbol_const);
MODULE_AUTHOR("retacn");
MODULE_DESCRIPTION("symbol_procucer_driver");
MODULE_ALIAS("symbol_producer_module");
MODULE_LICENSE("GPL");
symbol_consumer.c文件内容如下:
#include
#include
#include
#include
#include
#include
//定义被导入的常量
extern const char* symbol_const;
extern int result;
//定义被导入的函数
extern int add(int a,int b);
extern int sub(int a,int b);
//初始化驱动
static int __init symbol_consumer_init(void){
int a=40;
int b=30;
printk("symbol_consumer_init_success\n");
printk("%d + %d = %d\n",a,b,add(a,b));
printk("%d -%d = %d \n",a,b,sub(a,b));
//输出result变量的值
printk("result: %d\n",result);
//输出symbol_const变量的值
printk("symbol_const: %s\n",symbol_const);
return 0;
}
//卸载驱动
static void __exit symbol_consumer_exit(void){
printk("symbol_consumer_exit_success\n");
}
module_init(symbol_consumer_init);
module_exit(symbol_consumer_exit);
MODULE_AUTHOR("retacn");
MODULE_DESCRIPTION("symbol_consumer_driver");
MODULE_ALIAS("symbol_consumer_module");
MODULE_LICENSE("GPL");
Makefile文件的内容如下:
obj-m := symbol_producer.o
obj-m += symbol_consumer.o
Build.sh文件内容如下:
source /opt/linux/driver/common.sh
make -C $UBUNTU_KERNEL_PATH M=$PWD
testing=$(lsmod | grep "symbol_consumer")
# 如果symbol_consumer驱动已安装,先卸载symbol_consumer驱动
if [ "$testing" != "" ]; then
rmmod symbol_consumer
fi
testing=$(lsmod | grep "symbol_producer")
# 如果symbol_producer驱动已安装,先卸载symbol_producer驱动
if [ "$testing" != "" ]; then
rmmod symbol_producer
fi
# 安装linux_driver驱动
insmod $PWD/symbol_producer.ko
# 安装linux_driver驱动
insmod $PWD/symbol_consumer.ko
可以使用dmesg查看信息
root@vm:/opt/linux/driver/symbol_export# dmesg
[ 6899.127077] symbol_consumer_exit_success
[ 6899.131243] symbol_producer_exit_success
[ 6899.132749] symbol_producer_init_success
[ 6899.132751] 20 + 10 = 30
[ 6899.132751] 20 -10 = 10
[ 6899.133761] symbol_consumer_init_success
[ 6899.133764] 40 + 30 = 70
[ 6899.133765] 40 -30 = 10
[ 6899.133765] result: 40
[ 6899.133766] symbol_const: export
如果相查看symbol_producer的导出符号, 可以在/proc/kallsyms中查看
结果如下所示
root@vm:/opt/linux/driver/symbol_export# cat /proc/kallsyms | grep symbol_producer.
ffffffffa01e3000 T add[symbol_producer]
ffffffffa01e3010 t sub[symbol_producer]
ffffffffa01e5278 B result[symbol_producer]
ffffffffa01e3020 t symbol_producer_exit [symbol_producer]
ffffffffa01e4060 r __ksymtab_symbol_const [symbol_producer]
ffffffffa01e5000 d symbol_const[symbol_producer]
ffffffffa01e40fb r __kstrtab_symbol_const [symbol_producer]
ffffffffa01e4088 r __kcrctab_symbol_const [symbol_producer]
ffffffffa01e4050 r __ksymtab_sub [symbol_producer]
ffffffffa01e4108 r __kstrtab_sub [symbol_producer]
ffffffffa01e4080 r __kcrctab_sub [symbol_producer]
ffffffffa01e4040 r __ksymtab_result [symbol_producer]
ffffffffa01e410c r __kstrtab_result [symbol_producer]
ffffffffa01e4078 r __kcrctab_result [symbol_producer]
ffffffffa01e4030 r __ksymtab_add [symbol_producer]
ffffffffa01e4113 r __kstrtab_add [symbol_producer]
ffffffffa01e4070 r __kcrctab_add [symbol_producer]
ffffffffa01e5020 d __this_module [symbol_producer]
ffffffffa01e3020 t cleanup_module [symbol_producer]
查看linux驱动的依赖关系,所以在卸载的时候要先卸载依赖,装载的时候先装依赖
root@vm:/opt/linux/driver/symbol_export# lsmod | grep symbol_
symbol_consumer 12435 0
symbol_producer 13051 1 symbol_consumer
驱动的两种装载方法
Depmod/modprobe
Insmod
2 强行卸载linux驱动
在无法通过rmmod进行卸载或不想重启机器的情况下,卸载linux驱动
即在以下两种情况下:
a初始化函数崩溃
b卸载函数被阻塞
用于卸载驱动的模块驱动
Force_kill_driver.c文件,内容如下
#include
#include
#include
#include
#include
#include
#include
//要卸载的linux驱动的module结构体首地址(16进制)
static char* module_address="";
//空的linux驱动卸载函数,用于替换阻塞的卸载函数
void force(void){
}
int __init force_kill_driver_init(void){
//如果指定了module结构体的首地址,则卸载linux驱动模块
if(strcmp(module_address,"") != 0){
char *temp;
//将十六进制地址转换为十进制地址
long address=simple_strtol(module_address,&temp,16);
//将module结构体的首地址强制转换为module结构体指针
struct module *mod=(struct module*)address;
//将要卸载的linux驱动模块设置为活动状态
mod->state=MODULE_STATE_LIVE;
//将引用指针设为null
//对于情况1 可以用module_put,参数就是mod
//对于情况2 必须设为null
mod->refptr=NULL;
//替换要卸载的linux驱动的卸载函数指针
mod->exit=force;
}
return 0;
}
void __exit force_kill_driver_exit(void){
}
module_init(force_kill_driver_init);
module_exit(force_kill_driver_exit);
//定义字符串指针类型的模块参数
module_param(module_address,charp,S_IRUGO | S_IWUSR);
MODULE_AUTHOR("retacn");
MODULE_LICENSE("GPL");
Makefile文件内容如下:
Obj-m := force_kill_driver.o
Build.sh文件,内容如下:
#build.sh
source /opt/linux/driver/common.sh
make -C $UBUNTU_KERNEL_PATH M=$PWD
#读入要卸载的linux模块名
read -p "Please input module name:" module_name
#查询读入的模块名是否安装
temp=$(lsmod | grep "^""$module_name"" ")
if [ "$temp" == "" ]; then
echo "module <"$module_name"> does not exist!"
exit
fi
#使用正则表达式提取module结构体的首地址
module_line=$(cat /proc/kallsyms | grep __this_module | grep $module_name)
if [ "$module_line" == ""]; then
echo $module_line
#确是否删除
read -p "kill?(y/n)" yn
if [ "$yn" == "y" ]; then
#利用cut拆分字符串 (以空格为分隔符)
#取得module的首地址,-f1表示第一个值
module_address=$(echo $module_line | cut -d'' -f1)
#安装force_kill_driver
testing=$(lsmod | grep "force_kill_driver")
if [ "$testing" != "" ]; then
rmmod force_kill_driver
fi
insmod $PWD/force_kill_driver.ko module_address="$module_address"
#卸载linux驱动模块
rmmod $module_name
echo "<"$module_name"> is killed!"
fi
fi
测试卸载驱动用的两个错误linux驱动模块
Bad_driver1.c文件内容如下
#include
#include
#include
#include
#include
#include
//初始化驱动
static int __init bad_driver1_init(void){
strcat("abc","def");
return 0;
}
//卸载驱动
static void __exit bad_driver1_exit(void){
printk("bad_driver1_exit_success\n");
}
module_init(bad_driver1_init);
module_exit(bad_driver1_exit);
MODULE_AUTHOR("retacn");
MODULE_DESCRIPTION("bad driver1");
MODULE_LICENSE("GPL");
Bad_driver2.c文件内容如下:
#include
#include
#include
#include
#include
#include
static DECLARE_COMPLETION(test_completion);
//初始化驱动
static int __init bad_driver2_init(void){
printk("bad_driver2_init_success\n");
return 0;
}
//卸载驱动
static void __exit bad_driver2_exit(void){
printk("bad_driver2_exit_success\n");
wait_for_completion(&test_completion);
}
module_init(bad_driver2_init);
module_exit(bad_driver2_exit);
MODULE_AUTHOR("retacn");
MODULE_DESCRIPTION("bad driver2");
MODULE_LICENSE("GPL");
Makefile文件内容如下:
obj-m += bad_driver1.o bad_driver2.o
Build.sh文件内容如下:
#build.sh
source /opt/linux/driver/common.sh
make -C $UBUNTU_KERNEL_PATH M=$PWD
insmod $PWD/bad_driver1.ko
insmod $PWD/bad_driver2.ko
安装两个测试用错误驱动
root@vm:/opt/linux/driver/force_kill_driver/bad_driver# sh build.sh
查看驱动程序
root@vm:/opt/linux/driver/force_kill_driver/bad_driver# lsmod | grep bad
bad_driver2 12471 0
bad_driver1 17488 1
尝试手动卸载
root@vm:/opt/linux/driver/force_kill_driver/bad_driver# rmmod bad_driver1
ERROR: Module bad_driver1 is in use
//进入死循环状态
root@vm:/opt/linux/driver/force_kill_driver/bad_driver# rmmod bad_driver2
使用linux卸载模块来卸载linux驱动程序
root@vm:/opt/linux/driver/force_kill_driver# sh build.sh
make:进入目录'/usr/src/linux-headers-3.2.0-29-generic'
Building modules, stage 2.
MODPOST 1 modules
make:离开目录“/usr/src/linux-headers-3.2.0-29-generic”
Please input module name:bad_driver2
ffffffffa01fe020 d __this_module [bad_driver2]
kill?(y/n)y
查看是否卸载成功
root@vm:~# lsmod | grep bad
bad_driver1 17488 1
3 蜂鸣器驱动
蜂鸣器pwm(脉冲宽度调制),通过脉冲来打开和关闭
查看原理图:
由上图可知,pwm是由gpfcon寄存器来控制,最高位为10时打开pwm, 为00时关闭
查看android的内核源码,
宏定义在linux/arch/arm/mach-s3c64xx/include/mach/gpio-bank-f.h文件中
#define S3C64XX_GPFCON(S3C64XX_GPF_BASE + 0x00)
#define S3C64XX_GPFDAT (S3C64XX_GPF_BASE + 0x04)
#define S3C64XX_GPFPUD (S3C64XX_GPF_BASE + 0x08)
#define S3C64XX_GPFCONSLP (S3C64XX_GPF_BASE + 0x0c)
#define S3C64XX_GPFPUDSLP (S3C64XX_GPF_BASE + 0x10)
查看芯片手册
实现蜂鸣器驱动
由以下三个文件组成pwm.c pwm_fun.h pwm_fun.c
Pwm.c文件内容如下:
#include "pwm_fun.h"
//定义信号量
static struct semaphore lock;
//pwm设备文件的open函数
static int mini6410_pwm_open(struct inode *inode,struct file *file){
//使用信号量保证同一时间只能有一个进程打开pwm
if(!down_trylock(&lock)){
return 0;
}else{
return -EBUSY;
}
}
//pwm设备的close函数
static int mini6410_pwm_close(struct inode *inode,struct file *file){
//释放信号量
up(&lock);
return 0;
}
//通过ioctl来控制pwm
static long mini6410_pwm_ioctl(struct file *file,unsigned int cmd,unsigned long arg){
switch(cmd){
case PWM_IOCTL_START: //打开pwm
pwm_start();
break;
case PWM_IOCTL_STOP: //关闭pwm
pwm_stop();
break;
}
return 0;
}
//file_operations结构体
static struct file_operations dev_fops={
.owner = THIS_MODULE,
.open = mini6410_pwm_open,
.release = mini6410_pwm_close,
.unlocked_ioctl = mini6410_pwm_ioctl,
};
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
//初始化驱动
static int __init dev_init(void){
int ret;
//互斥体在运行时被初始化
//也可以创建信号量后,再声明和初始化互斥体
sema_init(&lock,1);
//DECLARE_MUTEX(name); 解锁状态
// DECLARE_MUTEX_LOCKED(&lock); //锁定状态
//init_MUTEX(&lock);
//注册设备
ret=misc_register(&misc);
printk(DEVICE_NAME"\t initialized \n");
return ret;
}
//卸载驱动
static void __exit dev_exit(void){
//移除设备
misc_deregister(&misc);
printk(DEVICE_NAME"\t exit \n");
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("retacn");
MODULE_DESCRIPTION("pwm driver");
Pwm_fun.h文件内容如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//设备文件名
#define DEVICE_NAME "pwm_dev"
//io命令,打开pwm
#define PWM_IOCTL_START 1
//io命令,关闭pwm
#define PWM_IOCTL_STOP 0
//打开和关闭 pwm的函数
extern void pwm_start(void);
extern void pwm_stop(void);
#include "pwm_fun.h"
void pwm_start(void)
{
unsigned tmp;
tmp = ioread32(S3C64XX_GPFCON);
tmp &=~(0x3U << 28); //
tmp |= (0x2U << 28); //
iowrite32(tmp, S3C64XX_GPFCON);
}
void pwm_stop( void )
{
unsigned tmp;
tmp = ioread32(S3C64XX_GPFCON);
tmp &= ~(0x3U << 28);
iowrite32(tmp, S3C64XX_GPFCON);
}
Makefile文件内容如下:
obj-m := pwm_driver.o
pwm_driver-objs := pwm.o pwm_fun.o
Build.sh文件内容如下:
source ./build_mini6410.sh
# build_mini6410.sh
source /opt/linux/driver/common.sh
#make -C $MINI6410_KERNEL_PATH M=${PWD}
make -C $MINI6410_ANDROID_KERNEL_PATH M=${PWD}
find_devices
if [ "$selected_device" == "" ]; then
exit
else
adb -s $selected_device push ${PWD}/pwm_driver.ko /data/local
testing=$(adb -s $selected_device shell lsmod | grep "pwm_driver")
if [ "$testing" != "" ]; then
adb -s $selected_device shell rmmod pwm_driver
fi
adb -s $selected_device shell "insmod /data/local/pwm_driver.ko"
Fi
测试pwm驱动
先安装ioctl驱动后再进行测试
./ioctl /dev/pwm_dev 1 0
./ioctl /dev/pwm_dev 0 0