编写一个虚拟的驱动在msm

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(避免访问冲突)
    如图: 统一管理
    其他的app通过SystemServer访问硬件
  • 硬件访问服务的顺序:
  1. loadLibrary 加载c库
  2. JNI_ONLOAD 注册本地方法. 分别调用各个硬件的函数来注册本地方法 { led. 振动器 串口 ... }
  3. System Server : {对每个硬件 add}
  4. 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

思路:

  1. 写一个 JNI 和 HAL
    com_android_server_LedService.cpp (可用其他名字) 注册JNI本地方法
    👇 加载它
    hal_led.c 这里面调用 (open read write ioctl ...) 访问硬件
  2. 修改 onload.cpp 调用 [com_android_server_LedService.cpp]
  3. 修改 SystemServer.java : new LedService.addService
  4. LedService.java : 调用本地方法
  5. ILedService.java : 让App调用
    韦东山老师的图片哦

仿照振动器代码实现

编写系统代码

  1. 实现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 查找,修改

编写APP代码

编写HAL代码

使用反射

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值