前段时间公司要做一个用旋钮调节音量的手机,简单粗暴的做法是应用层用一个死循环读驱动文件,然后驱动一个死循环读硬件ADC并写文件。但是这样太low了。相信很多小伙伴都知道UEvent机制,我们Android系统电池上报相关的信息就是用的这个机制。好了废话不多说了,直接上代码。
我加的文件路径:
kernel-3.18\drivers\input\keyboard\kpd.c
首先在kpd.c文件里面
#define VOLUME_MAJOR 156
unsigned int g_volume_val=0;
static struct class *volume_class = NULL;
static struct device *volume_dev = NULL;
static struct work_struct vol_work;
//当上层读取file时会调用show_volume_level函数来更新及显示文件内容
static ssize_t show_volume_level(struct device *dev,struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%u\n", g_volume_val);;
}
//当上层写入file时会调用 store_volume_level函数来更新及写入文件内容
static ssize_t store_volume_level(struct device *dev,struct device_attribute *attr, const char *buf, size_t size)
{
char *pvalue = NULL;
unsigned int test_item = 1;
if(buf != NULL)
{
test_item = simple_strtoul(buf,&pvalue,10);
g_volume_val = test_item;
printk("store_volume_level test_item=%d .\n",test_item);
}
return size;;
}
void vol_work_func(struct work_struct *work)
{
g_volume_val++;
kobject_uevent(&volume_dev->kobj, KOBJ_CHANGE);
mdelay(2000);
schedule_work(&vol_work);
}
static DEVICE_ATTR(volume_level, 0664, show_volume_level, store_volume_level);
static int __init kpd_mod_init(void)
{
//在sys文件系统下建立一个类
volume_class = class_create(THIS_MODULE, "volume");
//在类里建立一个设备
volume_dev = device_create(volume_class, NULL,
VOLUME_MAJOR,
NULL,
"volume_device");
//在设备目录下建立一个属性文件
ret_device_file = device_create_file(volume_dev, &dev_attr_volume_level);
//注意思在使用kobject_uevent函数发送uevent事件时需要建立一个工作队列来发送,否则会在发送过程中内核死掉。
INIT_WORK(&vol_work,vol_work_func);
schedule_work(&vol_work);
return 0;
}
static void __exit kpd_mod_exit(void)
{
class_destroy(volume_class);
}
module_init(kpd_mod_init);
module_exit(kpd_mod_exit);
上面代码只是大概流程,不是具体实现细节,小伙伴可根据自己的业务具体实现。
DEVICE_ATTR(volume_level, 0664, show_volume_level, store_volume_level) ,还有上面
store_volume_level ,show_volume_level 这2个方法是里面包含的volume_level 和黑体字标注是固定写法,可根据自己的业务重新命名。最大权限只能给0664 否则编译会出错。
kernel-3.18\drivers\input\keyboard\Makefile这个文件
obj-$(CONFIG_KEYBOARD_MTK) := kpd.o
具体Makefile根据自己需求去写。
com_android_server_vm_VolumeControlService.cpp
static jint readFromFile(JNIEnv* env, jobject obj) {
const char *path = "/sys/class/volume/volume_device/volume_level";
const int SIZE = 128;
char buf[SIZE];
ssize_t count = 0;
int g_vol_val = -1;
if (access(path, R_OK) == 0){
int fd = open(path, O_RDONLY, 0);
if (fd == -1) {
LOGI("Could not open '%s'\n",path);
return -1;
}
count = TEMP_FAILURE_RETRY(read(fd, buf, SIZE));
LOGI("g_vol_val '%s'\n",buf);
g_vol_val = atoi(buf);
LOGI("g_vol_val '%d'\n",g_vol_val);
close(fd);
}else{
LOGI("Could not access '%s'\n",path);
}
return g_vol_val;
}
jni 函数注册就不写了,自己不会百度一下。
public class VolumeControlService extends IVolumeControlService.Stub {
private String TAG = "VolumeControlService";
public VolumeControlService(Context context) {
mUEventObserver.startObserving("SUBSYSTEM=volume");
}
@Override
public int readState() throws RemoteException {
return 0;
}
private UEventObserver mUEventObserver = new UEventObserver() {
@Override
public void onUEvent(UEventObserver.UEvent event) {
int volumeLevel = readFromFile();
//TODO 发送广播到应用层,应用层监听
}
};
private static native int readFromFile();
}
需要用到的权限。
device.te里面定义
type volume_device, dev_type;
system_server.te里面添加
allow system_server volume_device:chr_file rw_file_perms;
allow system_server volume_device:dir r_dir_perms;
service.te里面添加
type volumecontrol_service, system_api_service, system_server_service, service_manager_type;
service_contexts里面添加
volumecontrol u:object_r:volumecontrol_service:s0
至此驱动到应用层通讯已经完成,如有疑问,请在下方留言。