Android HAL hardware abstraction layer loading process

Posted by maralynnj on Tue, 22 Feb 2022 02:00:43 +0100

The hardware manufacturer is protecting the core code and will present the core implementation in the HAL layer in the form of so library. When necessary, HAL will automatically call the relevant shared library.

Format of shared library

<MODULE_ID>.variant.so

  • id: the unique number of the hardware module
  • Variant: the variant name. This value is obtained from the system properties. The acquisition order is saved in variant_keys array.

static const char *variant_keys[] = {
    "ro.hardware",  /* This goes first so that it can pick up a different
                       file on the emulator. */
    "ro.product.board",
    "ro.board.platform",
    "ro.arch"
};

technological process
app calls hal layer HW through jni_ get_ Module function to obtain the hardware module, hw_get_module queries the shared library of the corresponding module through the module ID, calls load to open the shared library, and obtains the hardware structure address according to the fixed symbol HAL_MODULE_INFO_SYM find structure hw_module_t. Call hw_module_methods_t to open the hardware. HW passed in on open_ device_ T secondary pointer, which saves the operation function of the module in hw_device_t, realize the interaction with hardware.

hw_get_module

int hw_get_module_by_class(const char *class_id, const char *inst,
                           const struct hw_module_t **module)
{
    int i = 0;
    char prop[PATH_MAX] = {0};
    char path[PATH_MAX] = {0};
    char name[PATH_MAX] = {0};
    char prop_name[PATH_MAX] = {0};

    if (inst)
        snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
    else
        strlcpy(name, class_id, PATH_MAX);

    /*
     * Here we rely on the fact that calling dlopen multiple times on
     * the same .so will simply increment a refcount (and not load
     * a new copy of the library).
     * We also assume that dlopen() is thread-safe.
     */

    /* First try a property specific to the class and possibly instance */
    snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);
    if (property_get(prop_name, prop, NULL) > 0) {  //?? Get Zodiac
        if (hw_module_exists(path, sizeof(path), name, prop) == 0) {    //Check whether the module exists
            goto found;
        }
    }

    //How to search dynamic shared library in hal layer
    /* Loop through the configuration variants looking for a module */
    for (i=0 ; i<HAL_VARIANT_KEYS_COUNT; i++) {
        if (property_get(variant_keys[i], prop, NULL) == 0) {
            continue;
        }
        if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
            goto found;
        }
    }

    //Finally, try the default library
    /* Nothing found, try the default */
    if (hw_module_exists(path, sizeof(path), name, "default") == 0) {
        goto found;
    }

    return -ENOENT;

found:
    /* load the module, if this fails, we're doomed, and we should not try
     * to load a different variant. */
    return load(class_id, path, module); Load the library and get module
}

//Obtain the hardware module structure corresponding to the hardware id according to the hardware id
int hw_get_module(const char *id, const struct hw_module_t **module)
{   
    //The id will be part of the path, and the name of the following code
    return hw_get_module_by_class(id, NULL, module);
}
  • hw_get_module calls hw_get_module_by_class completes the loading process
  • hw_get_module_by_class according to the passed in variable class_id, query ro hardware.< ID > get the dependency value. If it exists, call HW as the variant value_ module_ Exit checks whether the target shared library exists and calls load to load
  • Does not exist, loop through variant_ The key defined by the keys array obtains the corresponding attribute value and determines whether there is a corresponding shared library. If there is a problem, call load to load, otherwise an error is returned

hw_ module_ The exists method queries whether there is a shared library according to the spliced path. The path of the shared library is determined by HAL_LIBRARY_PATH1 (system storage path), id (moudle ID) and variant (attribute).

Shared inventory placement path

#if defined(__LP64__)
#define HAL_LIBRARY_PATH1 "/system/lib64/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib64/hw"
#else
#define HAL_LIBRARY_PATH1 "/system/lib/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
#endif
  • The shared inventory is located in / system/lib64/hw, / system/lib/hw and / vendor/lib64/hw, / vendor/lib/hw
  • Share library to < module_ id>. variant. So is named, id is the module name, and variant is the variant name, which varies with the system platform.

load

static int load(const char *id,
        const char *path,
        const struct hw_module_t **pHmi)
{
    int status = -EINVAL;
    void *handle = NULL;
    struct hw_module_t *hmi = NULL;

    /*
     * load the symbols resolving undefined symbols before
     * dlopen returns. Since RTLD_GLOBAL is not or'd in with
     * RTLD_NOW the external symbols will not be global
     */
     //Call dlopen to open the target shared library defined by path and get the handle of the library file
    handle = dlopen(path, RTLD_NOW);
    if (handle == NULL) {
        char const *err_str = dlerror();
        ALOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
        status = -EINVAL;
        goto done;
    }

    /* Get the address of the struct hal_module_info. */
    const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
    //Call dlsym to get the symbol HAL_MODULE_INFO_SYM_AS_STR address, assigned to hmi
    hmi = (struct hw_module_t *)dlsym(handle, sym);
    if (hmi == NULL) {
        ALOGE("load: couldn't find symbol %s", sym);
        status = -EINVAL;
        goto done;
    }

    /* Check that the id matches */
    if (strcmp(id, hmi->id) != 0) {
        ALOGE("load: id=%s != hmi->id=%s", id, hmi->id);
        status = -EINVAL;
        goto done;
    }
    //Save shared library handle
    hmi->dso = handle;

    /* success */
    status = 0;

    done:
    if (status != 0) {
        hmi = NULL;
        if (handle != NULL) {
            dlclose(handle);
            handle = NULL;
        }
    } else {
        ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
                id, path, *pHmi, handle);
    }

    //Return the obtained HW_ module_ Pointer to T structure
    *pHmi = hmi;

    return status;
}
  • The load function calls dlopen to open the target module shared library, and uses dlsym to obtain the HMI address (HMI is the name of the module hw_module_t structure) to obtain the corresponding module HW_ module_ T pointer; Through HW_ module_ T pointer, which can operate the hardware.

Obtain module HW through dlopen and dlsym_ module_ T pointer, operating hardware.

Bluetooth HAL loading

Each module in the hardware abstraction layer must customize a hardware abstraction layer module structure, and the first member must be hw_module_t. The second is the relevant information of the module; Hal must also be included_ MODULE_ INFO_ SYM .

Bluetooth HAL loaded on COM_ android_ bluetooth_ btservice_ AdapterService. In the classInitNative method of CPP,

static void classInitNative(JNIEnv* env, jclass clazz) {
    int err;
    hw_module_t* module;
    // ........ Middle omission
    char value[PROPERTY_VALUE_MAX];
    property_get("bluetooth.mock_stack", value, "");

    const char *id = (strcmp(value, "1")? BT_STACK_MODULE_ID : BT_STACK_TEST_MODULE_ID);

    //Get Bluetooth module hw_module_t pointer
    err = hw_get_module(id, (hw_module_t const**)&module);

    if (err == 0) {
        hw_device_t* abstraction;
    //Call the open method to obtain the Bluetooth device hw_device_t pointer
        err = module->methods->open(module, id, &abstraction);
        if (err == 0) {
            //Bluetooth device in Bluetooth implementation_ device_ T and bluetooth_module_t is defined as the same value
            bluetooth_module_t* btStack = (bluetooth_module_t *)abstraction;
      //Obtain the Bluetooth module interface and operate the Bluetooth device through sBluetoothInterface
            sBluetoothInterface = btStack->get_bluetooth_interface();
        } else {
           ALOGE("Error while opening Bluetooth library");
        }
    } else {
        ALOGE("No Bluetooth Library found");
    }
}

BT_STACK_MODULE_ID is defined in Bluetooth H in the document,

 #define BT_HARDWARE_MODULE_ID "bluetooth"
#define BT_STACK_MODULE_ID "bluetooth"

hw_get_module to obtain the Bluetooth module hw_module_t pointer,
Bluetooth custom hardware module hw_module_t definition

struct hw_module_t HAL_MODULE_INFO_SYM = {
    .tag = HARDWARE_MODULE_TAG,
    .version_major = 1,
    .version_minor = 0,
    .id = BT_HARDWARE_MODULE_ID,
    .name = "Bluetooth Stack",
    .author = "The Android Open Source Project",
    .methods = &bt_stack_module_methods
};

Bluetooth calling custom module HW_ module_ open method in t to get hw_device_t pointer,

static struct hw_module_methods_t bt_stack_module_methods = {
    .open = open_bluetooth_stack,
};

static int open_bluetooth_stack(const struct hw_module_t *module, UNUSED_ATTR char const *name, struct hw_device_t **abstraction) {
  //To bluetooth_device_t assignment
  static bluetooth_device_t device = {
    //common variable assignment
    .common = {     
      .tag = HARDWARE_DEVICE_TAG,
      .version = 0,
      .close = close_bluetooth_stack,
    },
    //Get Bluetooth module interface
    .get_bluetooth_interface = bluetooth__get_bluetooth_interface
  };

  device.common.module = (struct hw_module_t *)module;
  //bluetooth_device_t pointer cast to hw_device_t pointer assigned to abstraction
  *abstraction = (struct hw_device_t *)&device;
  return 0;
}

open_bluetooth_stack create bluetooth_device_t structure, initialization, conversion to hw_device_t. Module - > methods - > open to get Bluetooth_ device_ The pointer to the T structure is also Bluetooth_ module_ Pointer to the T structure.

typedef struct {
    struct hw_device_t common;
    const bt_interface_t* (*get_bluetooth_interface)();
} bluetooth_device_t;

typedef bluetooth_device_t bluetooth_module_t;

bluetooth_device_t contains hw_device_t; bluetooth_device_t rename to bluetooth_module_t.

Open method (open_bluetooth_stack) to obtain the Bluetooth device hw_device_t pointer, converted to bluetooth_module_t pointer, in open_ bluetooth_ Bluetooth in stack_ get_ bluetooth_ Assign interface to get_bluetooth_interface, and then assign it to sBluetoothInterface.

bluetooth_get_bluetooth_interface defines the basic interface of Bluetooth module, bluetooth_get_bluetooth_interface is finally assigned to sBluetoothInterface. Calling sBluetoothInterface can operate Bluetooth module and interact with hardware. The specific interface implementation is platform related.

Topics: Java Android Apache