文章目录
android8.0 编写一个简单的驱动,上层可通过jni访问
设备
设备: pxcel 1
系统: Ubuntu 18.04.2 LTS
代码版本: android-msm-marlin-3.18-pie-qpr2
简单的驱动代码
新增代码: /home/zp/msm/private/msm-google/drivers/char/myled.c
#include <linux/module.h> // module_init module_exit
#include <linux/init.h> // __init __exit
#include <linux/sysfs.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/ctype.h>
// static int zpcount = 0;
static long led_ioctl( struct file *file, unsigned int cmd, unsigned long arg )
{
/*
根据传入的参数,设置GPIO的高/低电平 控制硬件(看各个厂商文档和demo代码)
*/
printk(KERN_INFO "zpcmd : %u", cmd);
printk(KERN_INFO "zparg : %lu", arg);
// zpcount++;
// printk(KERN_INFO "zpcount : %d", zpcount);
return 0; // 自己的测试逻辑
}
static int led_open(struct inode *inode, struct file *file)
{
/* 配置GPIO为输出引脚 */
return 0; // 自定义
}
static ssize_t led_read(struct file *filp, char *buf,
size_t size, loff_t *offp)
{
printk(KERN_INFO "zp read --<");
return 0;
}
static struct file_operations leds_ops = {
.owner = THIS_MODULE,
.open = led_open,
.unlocked_ioctl = led_ioctl, // 32bit
.compat_ioctl = led_ioctl, // 64bit
.read = led_read,
};
// ---------- 注册:
static int major; // 主设备号
static struct class *cls;
// 模块安装函数
static int __init chrdev_init(void)
{
// dmesg 看 printk 的消息
printk(KERN_INFO "chrdev_init helloworld init\n");
/*
注册一个字符设备
major: 0: 让系统给我们分配
name:
fops: 结构体
*/
major = register_chrdev(0 , "myleds",&leds_ops);
if(major < 0 ) ; // error
/* 为了让系统 udev, mdev 给我们创建设备节点 */
/* 创建类, 在类下的创建设别 : /sys */ // 目的是在system下面创建一些文件
// udev 会根据设备文件创建设备节点
cls = class_create(THIS_MODULE, "myleds");
device_create(
cls ,
NULL,
MKDEV(major, 0), // 主设备号:major | 次设备号是:0
NULL,
"myleds" // 设备名
); // 类的下面创建一个设备 /dev/leds <<
return 0;
}
// 模块卸载函数
static void __exit chrdev_exit(void)
{
device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);
unregister_chrdev(major, "leds");
printk(KERN_INFO "chrdev_exit helloworld exit\n");
}
module_init(chrdev_init);
module_exit(chrdev_exit);
// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("zp"); // 描述模块的作者
MODULE_DESCRIPTION("module test"); // 描述模块的介绍信息
MODULE_ALIAS("alias xxx"); // 描述模块的别名信息
/*
上层jni使用:
#include <jni.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <android/log.h>
#include <errno.h>
fun()
{
int fd = open("/dev/myleds", O_RDWR);
__android_log_print(ANDROID_LOG_INFO, "zp ndk", "fd: %d", fd);
int ret = ioctl(fd, 1, 1);
__android_log_print(ANDROID_LOG_INFO, "zp ndk errno: ", "%d %s\n", errno, strerror(errno));
close(fd);
return ret;
}
> man 2 open
> man 2 ioctl
*/
*/
修改makefile
/home/zp/msm/private/msm-google/drivers/char/Makefile
// 加入
obj-y += mem.o random.o
编译
# zp@ubuntu:~/msm$ 目录下
export DIST_DIR='/home/zp/msm/out/android-msm-marlin-3.18/private/msm-google/arch/arm64/boot'
export TARGET_PREBUILT_KERNEL=$DIST_DIR/Image.lz4-dtb
build/build.sh
adb reboot bootloader # 到bootloader
sudo fastboot boot $TARGET_PREBUILT_KERNEL # 通过img直接启动,不刷入
android 代码编写:
jni
问题汇总:
adb要用apt-get的, 不要用 out 目录下编译好的
关于 执行 build/build.sh 不报错,需要注释掉这行: (关闭多线程编译)
/home/zp/msm/build/build.sh
👇👇👇
export MAKEFLAGS="-j$(nproc) ${MAKEFLAGS}"
no permissions (verify udev rules); see [http://developer.android.com/tools/device.html] fastboot
解决问题
android studio 无法访问Google的 Gradle :
解决问题
未解决问题
问题:可以open,但是ioctl返回-1
解决1: 记得区分32位和64位的 ioctl 入口
解决2:ioctl(fd, 1, 1); 第二个参数2时报错???
通过service访问服务(解决同时访问问题)
- 名词解释:
server : 服务
service : 服务提供者
Server 里面很多 Service
为什么要通过 SystemServer 来操作硬件?
- android 系统中,由 SystemServer 访问硬件。其他的app访问SystemServer(避免访问冲突)
如图:统一管理
- 硬件访问服务的顺序:
- loadLibrary 加载c库
- JNI_ONLOAD 注册本地方法.
分别调用各个硬件的函数来注册本地方法 { led. 振动器 串口 ... }
- System Server :
{对每个硬件 add}
- app怎么使用?
{ 获得服务,使用服务 }
例子:TelephonyManager tm = (TelephonyManager) getApplication().getSystemService(Service.TELEPHONY_SERVICE); tm.getDeviceId()
源码分析
系统:
入口:SystemServer.java (\frameworks\base\services\java\com\android\server\SystemServer.java)
- main -> run -> System.loadLibrary(“android_servers”); -> startOtherServices()
Onload.cpp (frameworks/../jni)
- JNI_OnLoad ; 分别调用各个函数注册本地方法
-
eg: register_android_server_VibratorService(env);
振动器例子: vibrator
思路:
- 写一个 JNI 和 HAL
com_android_server_LedService.cpp (可用其他名字)注册JNI本地方法
👇 加载它
hal_led.c这里面调用 (open read write ioctl ...) 访问硬件
- 修改 onload.cpp
调用 [com_android_server_LedService.cpp]
- 修改 SystemServer.java :
new LedService.addService
- LedService.java :
调用本地方法
- ILedService.java :
让App调用
仿照振动器代码实现
编写系统代码
- 实现aidl文件
搜索*vibrator*aidl
copy, 修改
/*
ILedService.aidl
仿照:
*vibrator*aidl
\frameworks\base\core\java\android\os\
adb常用:
dmesg # 查看内核指令
chmod 777 /dev/myleds
getenforce
setenforce 0
Android.mk
[/home/zp/android-8.0.0_r15/frameworks/base/Android.mk]
core/java/android/os/IVibratorService.aidl \
core/java/android/os/ILedService.aidl \ // ←
> base目录下执行 `mmm .` 编译模块
`mmm .` 和 `mmm frameworks/base/` 一样
*/
package android.os;
/** {@hide} */
interface ILedService // IVibratorService
{
int ledCtrl(int which, int status);
}
/*
编译之后, 看看能不能找到:
/out$ find -name "ILedService.java"
上层app使用:
再搜索 IVibratorService 仿照使用: `\frameworks\base\core\java\android\os\SystemVibrator.java`
{
private final IVibratorService mService;
mService = IVibratorService.Stub.asInterface(ServiceManager.getService("vibrator"));
mService.hasVibrator(); // * boolean hasVibrator(); 会发送给 LedService.java [所以接下来写一个java]
...
}
*/
/*
1. 写了一个 aidl =生成> java | 使用(方法) ↑
2. 实现 LedService.java
修改 SystemServer.java -< 添加
traceBeginAndSlog("myledService"); // -<
ServiceManager.addService("myled", new LedService(context));
traceEnd();
3. com_android_server_LedService.cpp ; 供 LedService.java 使用
修改 onLoad.cpp -< 修改添加
int register_android_server_LedService(JNIEnv* env); // -< 声明
register_android_server_LedService(env); // -< 使用
O:\androidSource-halSource\frameworks\base\services\java\com\android\server\SystemServer.java System.loadLibrary('android_servers') ↓
O:\androidSource-halSource\frameworks\base\services\core\jni\onload.cpp ←+
\frameworks\base\services\core\jni\com_android_server_LedService.cpp ↓ 给它注册 native 函数
\frameworks\base\services\core\jni\com_android_server_VibratorService.cpp
O:\androidSource-halSource\frameworks\base\services\core\java\com\android\server\LedService.java ↑
O:\androidSource-halSource\frameworks\base\services\java\com\android\server\SystemServer.java [keep SystemServer startOtherServices]
startOtherServices()
{构造 service 注册到 系统里面去 ( service_manager.c )}
new LedService 的时候, 调用了构造方法 open /mnt/dev
service 一直跑着, 上层可以调用了
*/
/*
mk 文件:
java:
\frameworks\base\services\core\java\com\android\server\LedService.java
&
\frameworks\base\services\java\com\android\server\SystemServer.java
* \frameworks\base\services\core\Android.mk # 这个文件不需要修改->因为他将所有文件都包含了
LOCAL_SRC_FILES += \
$(call all-java-files-under,java) \
cpp:
* \frameworks\base\services\core\jni\Android.mk
{
$(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
+ $(LOCAL_REL_DIR)/com_android_server_LedService.cpp \
}
$ mmm frameworkds/base/services ; 为啥在这目录:
\frameworks\base\services\core\Android.mk ; 一般来说会包含子目录的Android.mk, 但是这个没有包含, `就再往上面找一层`
\frameworks\base\services\core\jni\Android.mk
\frameworks\base\services\Android.mk
{
include $(wildcard $(LOCAL_PATH)/*\/jni/Android.mk) ; 包含了 jni
LOCAL_MODULE:= libandroid_servers ; 对应↑ 的 loadLibrary('android_servers')
}
$ make snod
$ diff xxx yyy
$ ./gen-img.sh # androoid 5.0用的, 忽略. 可直接用 ↑生成的out目录下的img文件烧写
*/
/*
com_android_server_LedService.cpp
\frameworks\base\services\core\jni\--
\frameworks\base\services\core\jni\onload.cpp (onload.cpp 也是在这个文件)
*vibratorservice*cpp
*/
#define LOG_TAG "zp myleds LedService"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <utils/misc.h>
#include <utils/Log.h>
#include <inttypes.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
using android::hardware::Return;
namespace android
{
static jint fd;
jint ledopen(JNIEnv *env, jobject instance){
fd = open("/dev/myleds", O_RDWR);
return fd;
}
jint ledctl(JNIEnv *env, jobject instance, jint which, jint status){
ALOGI("fd: %d", fd);
int ret = ioctl(fd, 1, 1);
ALOGI("%d %s\n", errno, strerror(errno));
return ret;
}
jint ledclose(JNIEnv *env, jobject instance){
return close(fd);
}
static const JNINativeMethod method_table[] = {
{ "native_ledOpen", "()I", (void*)ledopen }
{ "native_ledClose", "()I", (void*)ledclose }
{ "native_ledCtrl", "(II)I", (void*)ledctl }
};
int register_android_server_LedService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/LedService",
method_table, NELEM(method_table));
}
};
/*
LedService.java
\frameworks\base\services\core\java\com\android\server\--
\frameworks\base\services\java\com\android\server\SystemServer.java
*vibratorservice*java
*/
package com.android.server;
import android.os.ILedService;
public class LedService extends ILedService.Stub {
private static final String TAG = "LedService";
private static final boolean DEBUG = false;
/* call native fun to access hardware */
public int ledCtrl(int which, int status)
{
return native_ledCtrl(which, status);
}
public LedService(){
native_ledOpen();
}
public static native int native_ledOpen();
public static native void native_ledClose();
public static native int native_ledCtrl(int which, int status);
}
/*
修改 SystemServer.java
添加一个
{
traceBeginAndSlog("myled");
ServiceManager.addService("myled", new LedService(context));
traceEnd();
}
*/
> android.mk 查找,修改