蜂鸣器驱动

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

 

 

强行卸载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

//对于情况可以用module_put,参数就是mod

//对于情况必须设为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

 is killed!

查看是否卸载成功

root@vm:~# lsmod | grep bad

bad_driver1            17488  1 

 

蜂鸣器驱动

蜂鸣器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  

 

 

更多相关文章

  1. 【展讯平台】Android(安卓)驱动(Kernel)、系统(framework) 定制,
  2. Android(安卓)Jni调用浅述
  3. 在android中使用文件进行数据存储
  4. Android(安卓)apk文件拆解与重新打包
  5. android studio中xml文件报错: URI is not registered的解决办法
  6. Android(安卓)7.0后访问文件权限:android.os.FileUriExposedExcep
  7. Android::开机自启动C程序
  8. Android(安卓)TV 键值修改流程
  9. Android软件测试的日志文件问题

随机推荐

  1. Android(安卓)项目实战视频资料 学习充电
  2. android网络编程——使用Android中的网络
  3. Android Studio:AndroidX的迁移
  4. Android 中自定义View(三)
  5. android app模拟 persistent 属性可以保
  6. android ndk 开发之 在 应用程序中使用 j
  7. [置顶] android调用第三方库——第二篇—
  8. Relativelayout的一些属性
  9. Android摄像头照相机技术-android学习之
  10. Android XMl文件中tools前缀