关闭

Android HAL JNI

389人阅读 评论(0) 收藏 举报
Android HAL层,即硬件抽象层,是Google响应厂家“希看不公然源码”的要求推出的新概念

1,源代码和目标位置

  源代码: /hardware/libhardware目录,该目录的目录结构如下:

/hardware/libhardware/hardware.c编译成,目标位置为/system/lib目录

/hardware/libhardware/include/hardware目录下包含如下头文件:

hardware.h 通用硬件模块头文件

copybit.h copybit模块头文件

gralloc.h gralloc模块头文件

lights.h  背光模块头文件

overlay.h overlay模块头文件

qemud.h  qemud模块头文件

sensors.h 传感器模块头文件

/hardware/libhardware/modules目录下定义了很多硬件模块

  这些硬件模块都编译成***.***.so,目标位置为/system/lib/hw目录

2,HAL层的实现方式

JNI->通用硬件模块->硬件模块->内核驱动接口

  具体一点:JNI->libhardware.so->***.***.so->kernel

具体来说:android frameworks中JNI调用/hardware/libhardware/hardware.c中定义的hw_get_module函数来获取硬件模块,

  然后调用硬件模块中的方法,硬件模块中的方法直接调用内核接口完成相关功能

3,通用硬件模块()

  (1)头文件为:/hardware/libhardware/include/hardware/hardware.h

头文件中主要定义了通用硬件模块结构体hw_module_t,声明了JNI调用的接口函数hw_get_module

  hw_module_t定义如下:

typedef struct hw_module_t {

  /** tag must be initialized to HARDWARE_MODULE_TAG */

  uint32_t tag;

  /** major version number for the module */

  uint16_t version_major;

  /** minor version number of the module */

  uint16_t version_minor;

  /** Identifier of module */

  const char *id;

  /** Name of this module */

  const char *name;

  /** Author/owner/implementor of the module */

  const char *author;

  /** Modules methods */

  struct hw_module_methods_t* methods; //硬件模块的方法

/** module's dso */

  void* dso;

  /** padding to 128 bytes, reserved for future use */

  uint32_t reserved[32-7];

  } hw_module_t;

硬件模块方法结构体hw_module_methods_t定义如下:

typedef struct hw_module_methods_t {

  /** Open a specific device */

  int (*open)(const struct hw_module_t* module, const char* id,

  struct hw_device_t** device);

  } hw_module_methods_t;

只定义了一个open方法,其中调用的设备结构体参数hw_device_t定义如下:

typedef struct hw_device_t {

  /** tag must be initialized to HARDWARE_DEVICE_TAG */

  uint32_t tag;

  /** version number for hw_device_t */

  uint32_t version;

  /** reference to the module this device belongs to */

  struct hw_module_t* module;

  /** padding reserved for future use */

  uint32_t reserved[12];

  /** Close this device */

  int (*close)(struct hw_device_t* device);

  } hw_device_t;

  hw_get_module函数声明如下:

int hw_get_module(const char *id, const struct hw_module_t **module);

参数id为模块标识,定义在/hardware/libhardware/include/hardware目录下的硬件模块头文件中,

  参数module是硬件模块地址,定义了/hardware/libhardware/include/hardware/hardware.h中

(2)hardware.c中主要是定义了hw_get_module函数如下:

#define HAL_LIBRARY_PATH "/system/lib/hw"

  static const char *variant_keys[] = {

  "ro.hardware",

  "ro.product.board",

  "ro.board.platform",

  "ro.arch"

  };

  static const int HAL_VARIANT_KEYS_COUNT =

  (sizeof(variant_keys)/sizeof(variant_keys[0]));

  int hw_get_module(const char *id, const struct hw_module_t **module) 

  {

  int status;

  int i;

  const struct hw_module_t *hmi = NULL;

  char prop[PATH_MAX];

  char path[PATH_MAX];

  for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) 

  {

  if (i < HAL_VARIANT_KEYS_COUNT) 

  {

  if (property_get(variant_keys[i], prop, NULL) == 0) 

  {

  continue;

  }

  snprintf(path, sizeof(path), "%s/%s.%",

  HAL_LIBRARY_PATH, id, prop);

  } 

  else 

  {

  snprintf(path, sizeof(path), "%s/%",

  HAL_LIBRARY_PATH, id);

  }

  if (access(path, R_OK)) 

  {

  continue;

  }

  /* we found a library matching this id/variant */

  break;

  }

  status = -ENOENT;

  if (i < HAL_VARIANT_KEYS_COUNT+1) {

  /* load the module, if this fails, we're doomed, and we should not try

  * to load a different variant. */

  status = load(id, path, module);

  }

  return status;

  }

从源代码我们可以看出,hw_get_module完成的主要工作是根据模块id寻找硬件模块动态连接库地址,然后调用load函数往打开动态连接库

  并从动态链接库中获取硬件模块结构体地址。硬件模块路径格式如下:

HAL_LIBRARY_PATH/

  HAL_LIBRARY_PATH定义为/system/lib/hw

  id是hw_get_module函数的第一个参数所传进,prop部分首先按照variant_keys数组中的名称逐一调用property_get获取对应的系统属性,

  然后访问HAL_LIBRARY_PATH/,假如找到能访问的就结束,否则就访问HAL_LIBRARY_PATH/

举例如下:

  假定访问的是背光模块,id定义为"lights"则系统会按照如下的顺序往访问文件:

/system/lib/hw/lights.[ro.hardware属性值].so

  /system/lib/hw/lights.[ro.product.board属性值].so

  /system/lib/hw/lights.[ro.board.platform属性值].so

  /system/lib/hw/lights.[ro.arch属性值].so

  /system/lib/hw/

所以开发硬件模块的时候Makefile文件(Android.mk)中模块的命名LOCAL_MODULE要参考上面的内容,否则就会访问不到没作用了。

load函数的关键部分代码如下:

handle = dlopen(path, RTLD_NOW);  //打开动态链接库

if (handle == NULL) {

  char const *err_str = dlerror();

  LOGE("load: module=%sn%s", path, err_str?err_str:"unknown");

  status = -EINVAL;

  goto done;

  }

  const char *sym = HAL_MODULE_INFO_SYM_AS_STR;

  hmi = (struct hw_module_t *)dlsym(handle, sym); //从动态链接库中获取硬件模块结构体的指针

if (hmi == NULL) {

  LOGE("load: couldn't find symbol %s", sym);

  status = -EINVAL;

  goto done;

  }

  HAL_MODULE_INFO_SYM_AS_STR是硬件模块在动态链接库中的标志,定义在hardware.h中如下:

#define HAL_MODULE_INFO_SYM         HMI

  #define HAL_MODULE_INFO_SYM_AS_STR  "HMI"

  4,硬件模块

  硬件模块的开发主要是完成/hardware/libhardware/include/hardware目录下对应的头文件中的内容,主要是硬件模块头文件和hardware.h中

  的结构体中定义了一些函数指针,调用内核提供的接口将具体的函数实现,然后编译成指定名称的动态链接库放到/system/lib/hw目录下即可。

  用一句话来概括:硬件模块的开发就是定义一个hardware.h中定义的hw_module_t结构体,结构体名称为宏HAL_MODULE_INFO_SYM,然后实现结构体

  的相关内容即可。

5,内核驱动

  主要是要向用户层开放接口,让硬件模块和内核可以交互 1,总论

  背光模块属于HAL层开发,HAL层开发,用一句话来概括就是定义一个hardware.h中定义的名称为宏HAL_MODULE_INFO_SYM的hw_module_t结构体,

  然后实现结构体的相关内容

2,驱动方面的预备

  简单的嵌进式linux驱动,编写LCD背光驱动,并提供接口给上层修改,我所用的是直接修改接口文件,接口如下:

/sys/class/backlight/pwm-backlight/brightness  这个是亮度调节

/sys/class/backlight/pwm-backlight/max_brightness 这个是最大亮度,按照android系统的要求应该设置成255

控制亮度直接写brightness文件即可

  背光驱动主要是通过PWM来完成,这里不具体说明。

3,需要包含的头文件

/hardware/libhardware/include/hardware目录下的hardware.h和lights.h

其中hardware.h中定义了通用硬件模块,lights.h中定义了背光设备相关的内容

4,android已有的硬件模块在/hardware/libhardware/modules目录下,
港台化妆品为了区分,我们开发的背光模块放置在如下的目录:

vendor/ardent/merlin/lights目录下,编译成放置到/system/lib/hw目录下,模块命名规则可以

  参考上一节的内容。

5,修改vendor/ardent/merlin目录下AndroidBoard.mk文件,添加如下内容:

include $(LOCAL_PATH)/lights/Mdroid.mk

  6,lights目录新建Mdroid.mk文件,内容如下:

LOCAL_PATH:= $(call my-dir)

  include $(CLEAR_VARS)

  LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw

  LOCAL_SRC_FILES:= lights.c

  LOCAL_SHARED_LIBRARIES := 

  libutils 

  libcutils 

  libhardware

  LOCAL_PRELINK_MODULE := false

  LOCAL_MODULE := lights.default

  include $(BUILD_SHARED_LIBRARY)

  7,lights目录下新建一个lights.c文件,如下:

const struct hw_module_t HAL_MODULE_INFO_SYM = {

  .tag = HARDWARE_MODULE_TAG,

  .version_major = 1,

  .version_minor = 0,

  .id = LIGHTS_HARDWARE_MODULE_ID,

  .name = "lights module",

  .author = "allen",

  .methods = NULL,

  };

  8,上面的内容可以直接编译通过,但是由于我将其methods部分指向了空指针,因此没有任何功能,下面来实现此部分

hw_module_t机构体的methods成员是一个指向hw_module_methods_t结构体的一个指针,hw_module_methods_t结构体定义如下:

typedef struct hw_module_methods_t {

  int (*open)(const struct hw_module_t* module, const char* id,struct hw_device_t** device);

  } hw_module_methods_t;

据此我们定义一个hw_module_methods_t类型的参数lights_module_methods如下:

struct hw_module_methods_t lights_module_methods = {

  .open = lights_device_open

  };

然后将上面的methods由NULL改成lights_module_methods

  9,接下来就是定义lights_device_open函数了,此函数的参数和返回值由hw_module_methods_t结构体的open成员决定,此函数定义如下:

static int lights_device_open(const struct hw_module_t *module,const char *id, struct hw_device_t **device)

从lights_device_open函数的参数来看,第一个参数和第二个参数是常量,第三个参数是 一个指向hw_device_t结构体的指针,因此可以断定

  实现此函数也就是要完成第三个参数的内容,具体的内容我们可以参考直接调用该函数的内容,在frameworks/base/services/jni目录下的

com_android_server_LightsService.cpp文件中,内容如下:

static light_device_t* get_device(hw_module_t* module, char const* name)

  {

  int err;

  hw_device_t* device;

  err = module->methods->open(module, name, &device);

  if (err == 0) {

  return (light_device_t*)device;//device由hw_device_t指针强制转换成light_device_t指针

} else {

  return NULL;

  }

  }

  static jint init_native(JNIEnv *env, jobject clazz)

  {

  int err;

  hw_module_t* module;

  Devices* devices;

  devices = (Devices*)malloc(sizeof(Devices));

  err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);

  if (err == 0) {

  devices->lights[LIGHT_INDEX_BACKLIGHT]

  = get_device(module, LIGHT_ID_BACKLIGHT);

  devices->lights[LIGHT_INDEX_KEYBOARD]

  = get_device(module, LIGHT_ID_KEYBOARD);

  devices->lights[LIGHT_INDEX_BUTTONS]

  = get_device(module, LIGHT_ID_BUTTONS);

  devices->lights[LIGHT_INDEX_BATTERY]

  = get_device(module, LIGHT_ID_BATTERY);

  devices->lights[LIGHT_INDEX_NOTIFICATIONS]

  = get_device(module, LIGHT_ID_NOTIFICATIONS);

  devices->lights[LIGHT_INDEX_ATTENTION]

  = get_device(module, LIGHT_ID_ATTENTION);

  devices->lights[LIGHT_INDEX_BLUETOOTH]

  = get_device(module, LIGHT_ID_BLUETOOTH);

  devices->lights[LIGHT_INDEX_WIFI]

  = get_device(module, LIGHT_ID_WIFI);

  } else {

  memset(devices, 0, sizeof(Devices));

  }

  return (jint)devices;

  }

从上面的内容我们可以看出lights_device_open的第一个参数是JNI层用hw_get_module所获得,第二个参数根据设备的不同有很多种情况

  该参数的内容定义在lights.h中,全部情况如下:

#define LIGHT_ID_BACKLIGHT          "backlight"

  #define LIGHT_ID_KEYBOARD           "keyboard"

  #define LIGHT_ID_BUTTONS            "buttons"

  #define LIGHT_ID_BATTERY            "battery"

  #define LIGHT_ID_NOTIFICATIONS      "notifications"

  #define LIGHT_ID_ATTENTION          "attention"

  #define LIGHT_ID_BLUETOOTH          "bluetooth"

  #define LIGHT_ID_WIFI               "wifi"

  lights调节有背光,键盘,按键,电池,通知,提醒,蓝牙和WIF

第三个参数是一个指向一个hw_device_t的指针,但是com_android_server_LightsService.cpp文件中的背光调节函数定义如下:

static void setLight_native(JNIEnv *env, jobject clazz, int ptr,

  int light, int colorARGB, int flashMode, int onMS, int offMS, int brightnessMode)

  {

  Devices* devices = (Devices*)ptr;

  light_state_t state;

  if (light < 0 || light >= LIGHT_COUNT || devices->lights[light] == NULL) {

  return ;

  }

  memset(&state, 0, sizeof(light_state_t));

  state.color = colorARGB;

  state.flashMode = flashMode;

  state.flashOnMS = onMS;

  state.flashOffMS = offMS;

  state.brightnessMode = brightnessMode;

  devices->lights[light]->set_light(devices->lights[lig ht], &state);

  }

  get_device函数中将hw_device_t指针强制转换成light_device_t指针给调节背光用,而light_device_t定义如下:

struct light_device_t {

  struct hw_device_t common;

  int (*set_light)(struct light_device_t* dev,

  struct light_state_t const* state);

  };

因此在实现lights_device_open的第三个参数的时候,我们应该定义一个light_device_t类型结构体,然后

  将起common域的指针地址传递过往。
这样固然传递的是一个hw_device_t指针地址,但是JNI层可以将其强制转换

  成light_device_t指针地址用,否则devices->lights[light]->set_light就会起不到作用了。实现如下:

static int lights_device_open(const struct hw_module_t *module,const char *id, struct hw_device_t **device)

  {

  struct light_device_t *dev = NULL;

  int resvalue = -1;

  dev = calloc(sizeof(struct light_device_t),1);

  dev->common.tag = HARDWARE_DEVICE_TAG;

  dev->common.version = 0;

  dev->common.module = (struct hw_module_t *)module;

  dev->common.close = lights_device_close;

  if(!strcmp(id, LIGHT_ID_BACKLIGHT))

  {

  dev->set_light = lcd_set_light;

  resvalue = 0;

  }

  else

  {

  dev->set_light = other_set_light;

  resvalue = 0;

  }

  *device = &dev->common;

  return resvalue;

  }

  10,实现lights_device_close,lcd_set_light和other_set_light,这个主要是调用驱动提供的接口直接控制硬件,举例如下:

static int lights_device_close(struct hw_device_t* device)

  {

  struct light_device_t *m_device = (struct light_device_t *)device;

  if(m_device)

  free(m_device);

  return 0;

  }

  static int lcd_set_light(struct light_device_t* dev,struct light_state_t const* state)

  {

  int fd = -1;

  int bytes = 0;

  int rlt = -1;

  unsigned char brightness = ((77*((state->color>>16)&0x00ff))

  + (150*((state->color>>8)&0x00ff)) 

  + (29*(state->color&0x00ff))) >> 8;

  fd = open("/sys/class/backlight/pwm-backlight/brightnes s", O_RDWR);

  if(fd>0)

  {

  char buffer[20];

  memset(buffer, 0, 20);

  bytes = sprintf(buffer, "%d", brightness);

  rlt = write(fd, buffer, bytes);

  if(rlt>0)

  {

  close(fd);

  return 0;

  }

  }

  close(fd);

  return -1;

  }

  static int other_set_light(struct light_device_t* dev,struct light_state_t const* state)

  {

  return 0;

  }

  11,由于上面调节背光是通过写/sys/class/backlight/pwm-backlight/brightness文件来完成,因此一定要设置该文件的权限,

  在init.***.rc文件中添加如下的内容:

# for control LCD backlight

  chown system system /sys/class/backlight/pwm-backlight/brightness

  chmod 0666 /sys/class/backlight/pwm-backlight/brightness

  12,修改完成后经验证亮度调节可用,上面的例子只是实现了lights部分功能,假如需要完成所有的功能,请参考hardware.h, lights.h和com_android_server_LightsService.cpp文件中的内容.
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:55548次
    • 积分:733
    • 等级:
    • 排名:千里之外
    • 原创:9篇
    • 转载:42篇
    • 译文:0篇
    • 评论:3条
    最新评论