FM 发射模块QN8027软件android 5.1实现分析
一,kernel层中的驱动:(主要为厂家提供,主要配置对应的I2C口线)
由三个文件组成:
1, Makefile /*驱动的编译文件,让该驱动被编译到内核中去*/
2, qn8027.c /*驱动的源文件,厂家提供,具体实现不分析,这里主要关注如何与JAVA层的通讯*/
3, qn8027.h /*驱动头文件,厂家提供。定义通讯的IO*/
Makefile文档:
include $(srctree)/drivers/misc/mediatek/Makefile.custom
obj-y += qn8027.o
qn8027.c文档:
#include
#include
#include
#include
#include
#include
#include
#include
//#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include"cust_gpio_usage.h"
#include"qn8027.h"
staticsize_t qn8027_iic_log_on = false;
#defineQN8027_IIC_LOG(fmt, arg...) \
do { \
if (qn8027_iic_log_on){printk("[qn8027]%s,#%d ", __func__, __LINE__); printk(fmt, ##arg);}\
}while (0)
#defineQN8027_IIC_FUNC() \
do { \
if(qn8027_iic_log_on)printk("[qn8027] %s\n", __func__); \
}while (0)
/*----------------------------------------------------------------------------*/
// qn8027device information
/*----------------------------------------------------------------------------*/
#defineMAX_TRANSACTION_LENGTH 8
#defineQN8027_DEVICE_NAME "FMtransmitter"
#defineQN8027_I2C_SLAVE_ADDR 0x58
#defineIIC_BUSNUM 2
#defineGPIO_EN_QN8027_PIN (GPIO31 |0x80000000)
/*EINT_AMP Y23 */
#defineGPIO_SPEAKER_EN_PIN (GPIO88| 0x80000000)
staticbool QN8027_FLAG = FALSE;
externbool accdet_plug_state();
/*----------------------------------------------------------------------------*/
staticint qn8027_i2c_probe(struct i2c_client *client, const struct i2c_device_id*id);
staticint qn8027_i2c_remove(struct i2c_client *client);
staticstruct i2c_client *qn8027_i2c_client = NULL;
staticconst struct i2c_device_id qn8027_i2c_id[] = {{QN8027_DEVICE_NAME,0},{}};
staticstruct i2c_board_info __initdata i2c_qn8027 = {I2C_BOARD_INFO(QN8027_DEVICE_NAME, (QN8027_I2C_SLAVE_ADDR>>1))};
externvoid set_pwm(int vol);
/*----------------------------------------------------------------------------*/
structi2c_driver qn8027_i2c_driver = {
.probe = qn8027_i2c_probe,
.remove = qn8027_i2c_remove,
.driver = { .name = QN8027_DEVICE_NAME,},
.id_table = qn8027_i2c_id,
};
structqn8027_i2c_data {
struct i2c_client *client;
uint16_t addr;
int use_reset; //use RESET flag
int use_irq; //use EINT flag
int retry;
};
staticstruct qn8027_i2c_data *obj_i2c_data = NULL;
boolqn8027_status()
{
return QN8027_FLAG;
}
EXPORT_SYMBOL(qn8027_status);
/*----------------------------------------------------------------------------*/
intqn8027_i2c_read(u16 addr, u32 *data)
{
u8 rxBuf[1] = {0};
int ret = 0;
struct i2c_client *client = qn8027_i2c_client;
u8 lens;
client->addr = (client->addr &I2C_MASK_FLAG);
client->timing = 400;
client->ext_flag = I2C_WR_FLAG;
rxBuf[0] = addr;
lens = 1;
ret = i2c_master_send(client,&rxBuf[0], (1 << 8) | lens);
if (ret < 0)
{
QN8027_IIC_LOG("qn8027_i2c_read reg[0x%X] fail, Error code [0x%X]\n", addr, ret);
return -EFAULT;
}
//ret = i2c_master_recv(client,&rxBuf[0], 0x1);
*data = rxBuf[0];
QN8027_IIC_LOG("qn8027_i2c_readreg[0x%X] = 0x%X\n", addr, rxBuf[0]);
return 0;
}
/*----------------------------------------------------------------------------*/
EXPORT_SYMBOL_GPL(qn8027_i2c_read);
/*----------------------------------------------------------------------------*/
intqn8027_i2c_write(u16 addr, u32 data)
{
struct i2c_client *client =qn8027_i2c_client;
u8 buffer[8];
u8 write_data[2] = {0};
int ret = 0;
struct i2c_msg msg =
{
.addr = client->addr &I2C_MASK_FLAG,
.flags = 0,
.len = 2,
.buf = write_data,
.timing = 400,
};
write_data[0] = addr;
write_data[1] = data;
//ret = i2c_master_send(client,write_data, 0x2);
ret = i2c_transfer(client->adapter,&msg, 1);
if (ret < 0)
{
QN8027_IIC_LOG("qn8027_write reg[0x%X] fail, Error code [0x%X]\n", addr, ret);
return -EFAULT;
}
QN8027_IIC_LOG("[QN8027]qn8027_i2c_write reg[0x%X] = 0x%X\n", addr, data);
return 0;
}
/*----------------------------------------------------------------------------*/
EXPORT_SYMBOL_GPL(qn8027_i2c_write);
voidqn8027_reg_init()
{
qn8027_i2c_write(0x00, 0x81);
msleep(20);
//qn8027_i2c_write(0x03, 0x50);
qn8027_i2c_write(0x04, 0x32);
qn8027_i2c_write(0x00, 0x41);
qn8027_i2c_write(0x00, 0x01);
msleep(20);
//qn8027_i2c_write(0x18, 0xe4);
//qn8027_i2c_write(0x1b, 0xf0);
qn8027_i2c_write(0x01, 0x00); //sendrate 88.8M
qn8027_i2c_write(0x02, 0xb9);
qn8027_i2c_write(0x00, 0x21);
}
staticvoid start_transfer()
{
qn8027_i2c_write(0x00, 0x81);
msleep(20);
qn8027_i2c_write(0x04, 0x32);
qn8027_i2c_write(0x00, 0x41);
qn8027_i2c_write(0x00, 0x01);
msleep(20);
qn8027_i2c_write(0x01, 0x00); //sendrate 88.8M
qn8027_i2c_write(0x02, 0xb9);
qn8027_i2c_write(0x00, 0x21);
}
staticvoid stop_transfer()
{
qn8027_i2c_write(0x00, 0x81);
msleep(20);
}
staticvoid set_transfer_rate(int freq)
{
int freqH;
int freqL;
if(freq < 760 || freq > 1080)
{
printk("fmtrans: the freqis not right\n");
}
else
{
freq = (freq - 760)*10 / 5;
if(freq > 0xff)
{
freqH = 0x20 |(freq>>8 & 0x3);
freqL = freq&0xff;
printk("set thefreqL 0x%x\n", freqL);
qn8027_i2c_write(0x01,freqL);
printk("set thefreqH 0x%x\n", freqH);
qn8027_i2c_write(0x00,freqH);
}
else
{
qn8027_i2c_write(0x01,freq);
qn8027_i2c_write(0x00,0x20);
}
}
}
staticint qn8027_open(struct inode *inode, struct file *file)
{
file->private_data =qn8027_i2c_client;
return nonseekable_open(inode, file);
}
staticint qn8027_release(struct inode *inode, struct file *file)
{
file->private_data = NULL;
return 0;
}
staticlong qn8027_ioctl(struct file *file, unsigned int cmd, unsigned long arg)/*IOCTL 接口,用于接受和处理上层发送过来的IO命令。注意具体命令格式和参数要对应。*/
{
struct i2c_client *client = (structi2c_client*)file->private_data;
void __user *data;
int freq = 0;
printk("jimmy: qn8027_ioctl cmd =%d\n", cmd);
switch(cmd)
{
caseFMTRANS_IOCTL_START:/*开启FM发送模块的命令*/
//mt_set_gpio_mode(GPIO_SPEAKER_EN_PIN,GPIO_MODE_00);
//mt_set_gpio_dir(GPIO_SPEAKER_EN_PIN,GPIO_DIR_OUT);
//mt_set_gpio_out(GPIO_SPEAKER_EN_PIN,GPIO_OUT_ZERO);
QN8027_FLAG= TRUE;
set_pwm(0);/*本地声音静音处理*/
msleep(200);
/*下面是控制QN8027的上电*/
mt_set_gpio_mode(GPIO_EN_QN8027_PIN,GPIO_MODE_00);
mt_set_gpio_dir(GPIO_EN_QN8027_PIN,GPIO_DIR_OUT);
mt_set_gpio_out(GPIO_EN_QN8027_PIN,GPIO_OUT_ONE);
msleep(200);
start_transfer();/*I2C写入对应寄存器,让QN8027工作起来,具体寄存器不讨论,可以看datasheet,或者直接找FAE就好了*/
printk("FMTRANS_IOCTL_START:QN8027_FLAG =%d\n", QN8027_FLAG);
break;
case FMTRANS_IOCTL_STOP:
stop_transfer();
msleep(200);
QN8027_FLAG= FALSE;
mt_set_gpio_mode(GPIO_EN_QN8027_PIN,GPIO_MODE_00);
mt_set_gpio_dir(GPIO_EN_QN8027_PIN,GPIO_DIR_OUT);
mt_set_gpio_out(GPIO_EN_QN8027_PIN,GPIO_OUT_ZERO);
msleep(200);
//if(!accdet_plug_state())
{
//mt_set_gpio_mode(GPIO_SPEAKER_EN_PIN,GPIO_MODE_00);
//mt_set_gpio_dir(GPIO_SPEAKER_EN_PIN,GPIO_DIR_OUT);
//mt_set_gpio_out(GPIO_SPEAKER_EN_PIN,GPIO_OUT_ONE);
set_pwm(100);
}
printk("FMTRANS_IOCTL_STOP:QN8027_FLAG= %d\n", QN8027_FLAG);
break;
case FMTRANS_IOCTL_SET_RATE:
/*
data = (void __user*)arg;
copy_from_user(&freq, data, sizeof(freq));
*/
__get_user(freq, (int*)arg);
printk("get therate %d\n", freq);
set_transfer_rate(freq);
break;
default :
break;
}
return 0;
}
staticstruct file_operations qn8027_fops = {
.owner = THIS_MODULE,
.open = qn8027_open,
.release = qn8027_release,
.unlocked_ioctl = qn8027_ioctl,
};
staticstruct miscdevice qn8027_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "fmtransmitter",
.fops =&qn8027_fops,
};
/*----------------------------------------------------------------------------*/
// IICProbe
/*----------------------------------------------------------------------------*/
staticint qn8027_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)/*I2C设备对应的probe,设备注册成功后会调用。*/
{
int ret = -1;
struct qn8027_i2c_data *obj;
QN8027_IIC_FUNC();
obj =kzalloc(sizeof(*obj), GFP_KERNEL);
if (obj == NULL) {
ret = -ENOMEM;
QN8027_IIC_LOG(QN8027_DEVICE_NAME ": Allocate ts memoryfail\n");
return ret;
}
obj_i2c_data = obj;
client->timing = 400;
obj->client = client;
qn8027_i2c_client = obj->client;
i2c_set_clientdata(client, obj);
if(misc_register(&qn8027_device))/*关键的地方,注册了一个misc类型的device,注册成功后,会在 /dev目录下生产一个对应的设备文件,后面就是通过这个设备文件,用IOCTL的方式来实现kennel成的驱动与JAVA层进行通信的,中间还经过了JNI层的转换*/
{
ret = -ENOMEM;
QN8027_IIC_LOG(QN8027_DEVICE_NAME ": misc_register error!\n");
return ret;
}
//qn8027_reg_init();
QN8027_IIC_LOG("MediaTek QN8027 i2cprobe success\n");
return 0;
}
/*----------------------------------------------------------------------------*/
staticint qn8027_i2c_remove(struct i2c_client *client)
{
QN8027_IIC_FUNC();
qn8027_i2c_client = NULL;
i2c_unregister_device(client);
kfree(i2c_get_clientdata(client));
misc_deregister(&qn8027_device);
return 0;
}
intg_reg_value_qn8027=0;
staticunsigned int reg_address = 0;
staticssize_t show_qn8027_access(struct device *dev,struct device_attribute *attr,char *buf)
{
qn8027_i2c_read(reg_address,&g_reg_value_qn8027);
printk("[show_qn8027_access]0x%x\n", g_reg_value_qn8027);
return sprintf(buf,"0x%x=0x%x\n", reg_address, g_reg_value_qn8027);
}
staticssize_t store_qn8027_access(struct device *dev,struct device_attribute *attr,const char *buf, size_t size)
{
int ret=0;
char *pvalue = NULL;
unsigned int reg_value = 0;
printk("[store_qn8027_access]\n");
if(buf != NULL && size != 0)
{
printk("[store_qn8027_access] buf is %sand size is %d \n",buf,size);
reg_address =simple_strtoul(buf,&pvalue,16);
if(size > 5)
{
reg_value =simple_strtoul((pvalue+1),NULL,16);
printk("[store_qn8027_access] write qn8027 reg 0x%x with value 0x%x!\n",reg_address,reg_value);
qn8027_i2c_write(reg_address, reg_value);
}
}
return size;
}
staticstruct kobj_attribute reg_attribute = {
.attr = {.name ="qn8027_reg", .mode = 0666},
.show = show_qn8027_access,
.store = store_qn8027_access,
};
/*----------------------------------------------------------------------------*/
staticint __init qn8027_init(void)/*驱动模块的入口*/
{
QN8027_IIC_FUNC();
i2c_register_board_info(IIC_BUSNUM,&i2c_qn8027, 1);/*设置I2C的口线*/
if(i2c_add_driver(&qn8027_i2c_driver)) /*如名字,把qn8027_i2c_driver加入到i2c的驱动中去*/
{
QN8027_IIC_LOG("unable to addqn8027 i2c driver.\n");
return -1;
}
sysfs_create_file(kernel_kobj,®_attribute.attr);
/*增加一个node节点,主要是方便用adb命令对寄存器进行调试*/
return 0;
}
/*----------------------------------------------------------------------------*/
staticvoid __exit qn8027_exit(void)
{
i2c_del_driver(&qn8027_i2c_driver);
}
/*----------------------------------------------------------------------------*/
module_init(qn8027_init);
module_exit(qn8027_exit);
qn8027.h文档:/*定义IO口命令,FMTRANS这个定义为0x58,不知道为什么。好像是定义的这个器件的地址。估计只是为了区分开别的设备,定义一个值而已吧*/
#include
#defineFMTRANS 0x58
#defineFMTRANS_IOCTL_START _IO(FMTRANS, 0x01)
#defineFMTRANS_IOCTL_STOP _IO(FMTRANS, 0x02)
#define FMTRANS_IOCTL_SET_RATE _IOW(FMTRANS,0x03, int)
二,JNI层的实现:
Android.mk /*编译文件,主要生产一个的libfm_qn8027_jni.so库,让JAVA层调用*/
fm_qn8027.cpp /*JNI具体的转换文件*/
fm_qn8027.h /*头文件,与kernel层的想对应*/
Android.mk文档:
LOCAL_PATH:=$(call my-dir)
include$(CLEAR_VARS)
LOCAL_MODULE_TAGS:= optional
LOCAL_CERTIFICATE:= platform
LOCAL_SRC_FILES:=\
fm_qn8027.cpp
LOCAL_SHARED_LIBRARIES:= \
libandroid_runtime \
libnativehelper \
libcutils \
libutils \
liblog \
libhardware
LOCAL_C_INCLUDES+= \
$(JNI_H_INCLUDE) \
libcore/include \
LOCAL_MODULE:=libfm_qn8027_jni
ALL_DEFAULT_INSTALLED_MODULES+= $(LOCAL_MODULE)
include $(BUILD_SHARED_LIBRARY)
fm_qn8027.cpp文档:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include"fm_qn8027.h"
#ifdefLOG_TAG
#undefLOG_TAG
#endif
#defineLOG_TAG "QN8027_control"
usingnamespace android;
//#defineFMTRANS_IOCTL_START 0x5801
//#defineFMTRANS_IOCTL_STOP 0x5802
//#defineFMTRANS_IOCTL_SET_RATE 0x40045803
staticint fm_fd;
staticint jni_pid;
staticint fm_status;
//MutexmLock;
// mustbe kept in sync with the values in WindowManager
enumFmControlType {
FM_START = 0,
FM_STOP = 1,
FM_SET_RATE = 2
};
enumFmControlStatus {
UNKNOW = 0,
FM_S_START = 1,
FM_s_STOP = 2
};
staticjint qn8027_init(JNIEnv *env, jobject thiz)
{
ALOGD("qn8027_init --->");
return 0;
}
staticjint qn8027_finish(JNIEnv *env, jobject thiz)
{
ALOGD("qn8027_finish--->");
return 0;
}
staticjint qn8027_open(JNIEnv *env, jobject thiz)
{
fm_fd = open(DEVICE_NAME,O_RDWR); //打开设备
if(fm_fd < 0)
{
ALOGE("qn8027_open fm_fd= %d", fm_fd);
}
return fm_fd;
}
staticjint qn8027_close(JNIEnv *env, jobject thiz)
{
jint ret;
ret = close(fm_fd);
return ret;
}
staticjint qn8027_start(JNIEnv *env, jobject thiz)
{
jint ret;
ret = ioctl(fm_fd,FMTRANS_IOCTL_START);
if (ret < 0)
{
ALOGE("qn8027_start ret= %d", ret);
return ret;
}
return 0;
}
staticjint qn8027_stop(JNIEnv *env, jobject thiz)
{
jint ret;
ret = ioctl(fm_fd, FMTRANS_IOCTL_STOP);
if (ret < 0)
{
ALOGE("qn8027_stop ret =%d", ret);
return ret;
}
return 0;
}
staticjint qn8027_set_rate(JNIEnv *env, jobject thiz, jint a)
{
static jint set_rate = 0;
jint ret;
set_rate = a; //760 <= set_rate <= 1080
ALOGD("--->set_rate = %d",set_rate);
ret = ioctl(fm_fd,FMTRANS_IOCTL_SET_RATE, &set_rate);
if (ret < 0)
{
ALOGE("qn8027_set_rateret = %d", ret);
return ret;
}
return 0;
}
//com/hcn/fmemitter
staticconst char *classPathName = "com/hcn/fmemitter/MainActivity";
/*增加的具体方法*/
static JNINativeMethod methods[] = {
// {"add","(II)I", (void*)add },
{"FmInit","()I", (void*)qn8027_init },
{"FmFinish", "()I", (void*)qn8027_finish},
{"FmOpen","()I", (void*)qn8027_open },
{"FmClose","()I", (void*)qn8027_close},
{"FmStart","()I", (void*)qn8027_start },
{"FmStop","()I", (void*)qn8027_stop },
// {"FmStatus", "()I", (void*)qn8027_status },
{"FmSetRate", "(I)I", (void*)qn8027_set_rate },
};
/*
* Register several native methods for oneclass.
*/
staticint registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
ALOGE("Native registration unableto find class '%s'", className);
return JNI_FALSE;
}
if (env->RegisterNatives(clazz,gMethods, numMethods) < 0) {
ALOGE("RegisterNatives failed for'%s'", className);
return JNI_FALSE;
}
return JNI_TRUE;
}
/*
* Register native methods for all classes weknow about.
*
* returns JNI_TRUE on success.
*/
staticint registerNatives(JNIEnv* env)
{
/*注册一个本地的方法,classPathName就是需要增加这些方法的class*/
if (!registerNativeMethods(env,classPathName,
methods, sizeof(methods) /sizeof(methods[0]))) {
return JNI_FALSE;
}
return JNI_TRUE;
}
//----------------------------------------------------------------------------
/*
* This is called by the VM when the sharedlibrary is first loaded.
*/
typedefunion {
JNIEnv* env;
void* venv;
}UnionJNIEnvToVoid;
jintJNI_OnLoad(JavaVM* vm, void* reserved)/*JNI的入口,具体流程待学习*/
{
UnionJNIEnvToVoid uenv;
uenv.venv = NULL;
jint result = -1;
JNIEnv* env = NULL;
ALOGI("JNI_OnLoad");
if (vm->GetEnv(&uenv.venv,JNI_VERSION_1_4) != JNI_OK) {
ALOGE("ERROR: GetEnvfailed");
goto bail;
}
env = uenv.env;
if (registerNatives(env)!= JNI_TRUE) {
ALOGE("ERROR: registerNativesfailed");
goto bail;
}
result = JNI_VERSION_1_4;
bail:
return result;
}
fm_qn8027.h文档:
#ifndef__QN8027_H__
#define__QN8027_H__
#include
#include
#defineFMTRANS 0x58
#defineFMTRANS_IOCTL_START _IO(FMTRANS,0x01)
#defineFMTRANS_IOCTL_STOP _IO(FMTRANS,0x02)
#defineFMTRANS_IOCTL_SET_RATE _IOW(FMTRANS,0x03, unsigned int)
#defineDEVICE_NAME "/dev/fmtransmitter"
#endif
三,JAVA层的调用:
加载LIB,然后申明这个接口为本地的方法。后面就可以当做内部函数来调用了。
static
{
System.loadLibrary("fm_qn8027_jni");
}
private static native int FmClose();
private static native int FmFinish();
private static native int FmInit();
private static native int FmOpen();
private static native int FmSetRate(int paramInt);
private static native int FmStart();
private static native int FmStop();
四,FM发送对应sh命令的制作。
Android.mk /*编译文档*/
fmtest.c /*命令的实现*/
Android.mk文档:
LOCAL_PATH:= $(call my-dir)
include$(CLEAR_VARS)
LOCAL_SRC_FILES:= fmtest.c
LOCAL_SHARED_LIBRARIES:= \
libcutils
LOCAL_CERTIFICATE:= platform
LOCAL_MODULE:= fmtest
include $(BUILD_EXECUTABLE)
fmtest.c文档:
/*************************************************************************
> File Name: fmtest.c
> Created by JinQuan, Time: Fri 19Jun 2015 02:05:57 PM CST
************************************************************************/
#include
#include
#include
#include
#include
#include
#define FMTRANS 0x58
#define FMTRANS_IOCTL_START _IO(FMTRANS, 0x01)
#define FMTRANS_IOCTL_STOP _IO(FMTRANS, 0x02)
#define FMTRANS_IOCTL_SET_RATE _IOW(FMTRANS,0x03, unsigned int)
/*
fmtest cmd:
0: stop
1: start
other: set rate
关闭FM发射adb命令:
Fmtest /dev/fmtransmitter 0
打开FM发送:
Fmtest /dev/fmtransmitter 1
设置FM发送的频率:例如频率为90.0MHZ
Fmtest /dev/fmtransmitter 2 900
*/
int main(int argc, char **argv)
{
int fd;
int ret;
int cmd = argv[2][0];
if(argc < 2)
{
printf("please entercmd\n");
return 0;
}
// fd = open("/dev/tty",O_RDWR | O_NOCTTY | O_NDELAY);
printf("1-->>%s\n",argv[0]);
printf("2-->>%s\n",argv[1]);
// fd =open("/dev/ttyMT1", O_RDWR );
fd = open(argv[1], O_RDWR );
// fd =open("/dev/fmtransmitter", O_RDONLY);
if(fd <=0 )
{
printf("open deviceserror!fd=%d\n",fd);
return -1;
} else {
printf("open success!fd=%d\n",fd);
}
sleep(5);
switch(cmd)
{
case '0':
printf("FMTRANS_IOCTL_STOP\n");
ret = ioctl(fd,FMTRANS_IOCTL_STOP);
printf("ioctlreturn is %d\n", ret);
break;
case '1':
printf("FMTRANS_IOCTL_START\n");
ioctl(fd, FMTRANS_IOCTL_START);
break;
case '2':
if(argc < 3)
{
printf("parameter notright\n");
return -1;
}
int freq =strtoul(argv[3], 0, 0);
printf("FMTRANS_IOCTL_SET_RATE %d\n", freq);
ioctl(fd,FMTRANS_IOCTL_SET_RATE, &freq);
break;
default:
break;
}
close(fd);
return 0;
}