| /* |
| * dbdcd.c |
| * |
| * DSP-BIOS Bridge driver support functions for TI OMAP processors. |
| * |
| * This file contains the implementation of the DSP/BIOS Bridge |
| * Configuration Database (DCD). |
| * |
| * Notes: |
| * The fxn dcd_get_objects can apply a callback fxn to each DCD object |
| * that is located in a specified COFF file. At the moment, |
| * dcd_auto_register, dcd_auto_unregister, and NLDR module all use |
| * dcd_get_objects. |
| * |
| * Copyright (C) 2005-2006 Texas Instruments, Inc. |
| * |
| * This package is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED |
| * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
| */ |
| #include <linux/types.h> |
| |
| /* ----------------------------------- Host OS */ |
| #include <dspbridge/host_os.h> |
| |
| /* ----------------------------------- DSP/BIOS Bridge */ |
| #include <dspbridge/dbdefs.h> |
| /* ----------------------------------- Trace & Debug */ |
| #include <dspbridge/dbc.h> |
| |
| /* ----------------------------------- Platform Manager */ |
| #include <dspbridge/cod.h> |
| |
| /* ----------------------------------- Others */ |
| #include <dspbridge/uuidutil.h> |
| |
| /* ----------------------------------- This */ |
| #include <dspbridge/dbdcd.h> |
| |
| /* ----------------------------------- Global defines. */ |
| #define MAX_INT2CHAR_LENGTH 16 /* Max int2char len of 32 bit int */ |
| |
| /* Name of section containing dependent libraries */ |
| #define DEPLIBSECT ".dspbridge_deplibs" |
| |
| /* DCD specific structures. */ |
| struct dcd_manager { |
| struct cod_manager *cod_mgr; /* Handle to COD manager object. */ |
| }; |
| |
| /* Pointer to the registry support key */ |
| static struct list_head reg_key_list; |
| static DEFINE_SPINLOCK(dbdcd_lock); |
| |
| /* Global reference variables. */ |
| static u32 refs; |
| static u32 enum_refs; |
| |
| /* Helper function prototypes. */ |
| static s32 atoi(char *psz_buf); |
| static int get_attrs_from_buf(char *psz_buf, u32 ul_buf_size, |
| enum dsp_dcdobjtype obj_type, |
| struct dcd_genericobj *gen_obj); |
| static void compress_buf(char *psz_buf, u32 ul_buf_size, s32 char_size); |
| static char dsp_char2_gpp_char(char *word, s32 dsp_char_size); |
| static int get_dep_lib_info(struct dcd_manager *hdcd_mgr, |
| struct dsp_uuid *uuid_obj, |
| u16 *num_libs, |
| u16 *num_pers_libs, |
| struct dsp_uuid *dep_lib_uuids, |
| bool *prstnt_dep_libs, |
| enum nldr_phase phase); |
| |
| /* |
| * ======== dcd_auto_register ======== |
| * Purpose: |
| * Parses the supplied image and resigsters with DCD. |
| */ |
| int dcd_auto_register(struct dcd_manager *hdcd_mgr, |
| char *sz_coff_path) |
| { |
| int status = 0; |
| |
| DBC_REQUIRE(refs > 0); |
| |
| if (hdcd_mgr) |
| status = dcd_get_objects(hdcd_mgr, sz_coff_path, |
| (dcd_registerfxn) dcd_register_object, |
| (void *)sz_coff_path); |
| else |
| status = -EFAULT; |
| |
| return status; |
| } |
| |
| /* |
| * ======== dcd_auto_unregister ======== |
| * Purpose: |
| * Parses the supplied DSP image and unresiters from DCD. |
| */ |
| int dcd_auto_unregister(struct dcd_manager *hdcd_mgr, |
| char *sz_coff_path) |
| { |
| int status = 0; |
| |
| DBC_REQUIRE(refs > 0); |
| |
| if (hdcd_mgr) |
| status = dcd_get_objects(hdcd_mgr, sz_coff_path, |
| (dcd_registerfxn) dcd_register_object, |
| NULL); |
| else |
| status = -EFAULT; |
| |
| return status; |
| } |
| |
| /* |
| * ======== dcd_create_manager ======== |
| * Purpose: |
| * Creates DCD manager. |
| */ |
| int dcd_create_manager(char *sz_zl_dll_name, |
| struct dcd_manager **dcd_mgr) |
| { |
| struct cod_manager *cod_mgr; /* COD manager handle */ |
| struct dcd_manager *dcd_mgr_obj = NULL; /* DCD Manager pointer */ |
| int status = 0; |
| |
| DBC_REQUIRE(refs >= 0); |
| DBC_REQUIRE(dcd_mgr); |
| |
| status = cod_create(&cod_mgr, sz_zl_dll_name); |
| if (status) |
| goto func_end; |
| |
| /* Create a DCD object. */ |
| dcd_mgr_obj = kzalloc(sizeof(struct dcd_manager), GFP_KERNEL); |
| if (dcd_mgr_obj != NULL) { |
| /* Fill out the object. */ |
| dcd_mgr_obj->cod_mgr = cod_mgr; |
| |
| /* Return handle to this DCD interface. */ |
| *dcd_mgr = dcd_mgr_obj; |
| } else { |
| status = -ENOMEM; |
| |
| /* |
| * If allocation of DcdManager object failed, delete the |
| * COD manager. |
| */ |
| cod_delete(cod_mgr); |
| } |
| |
| DBC_ENSURE((!status) || |
| ((dcd_mgr_obj == NULL) && (status == -ENOMEM))); |
| |
| func_end: |
| return status; |
| } |
| |
| /* |
| * ======== dcd_destroy_manager ======== |
| * Purpose: |
| * Frees DCD Manager object. |
| */ |
| int dcd_destroy_manager(struct dcd_manager *hdcd_mgr) |
| { |
| struct dcd_manager *dcd_mgr_obj = hdcd_mgr; |
| int status = -EFAULT; |
| |
| DBC_REQUIRE(refs >= 0); |
| |
| if (hdcd_mgr) { |
| /* Delete the COD manager. */ |
| cod_delete(dcd_mgr_obj->cod_mgr); |
| |
| /* Deallocate a DCD manager object. */ |
| kfree(dcd_mgr_obj); |
| |
| status = 0; |
| } |
| |
| return status; |
| } |
| |
| /* |
| * ======== dcd_enumerate_object ======== |
| * Purpose: |
| * Enumerates objects in the DCD. |
| */ |
| int dcd_enumerate_object(s32 index, enum dsp_dcdobjtype obj_type, |
| struct dsp_uuid *uuid_obj) |
| { |
| int status = 0; |
| char sz_reg_key[DCD_MAXPATHLENGTH]; |
| char sz_value[DCD_MAXPATHLENGTH]; |
| struct dsp_uuid dsp_uuid_obj; |
| char sz_obj_type[MAX_INT2CHAR_LENGTH]; /* str. rep. of obj_type. */ |
| u32 dw_key_len = 0; |
| struct dcd_key_elem *dcd_key; |
| int len; |
| |
| DBC_REQUIRE(refs >= 0); |
| DBC_REQUIRE(index >= 0); |
| DBC_REQUIRE(uuid_obj != NULL); |
| |
| if ((index != 0) && (enum_refs == 0)) { |
| /* |
| * If an enumeration is being performed on an index greater |
| * than zero, then the current enum_refs must have been |
| * incremented to greater than zero. |
| */ |
| status = -EIDRM; |
| } else { |
| /* |
| * Pre-determine final key length. It's length of DCD_REGKEY + |
| * "_\0" + length of sz_obj_type string + terminating NULL. |
| */ |
| dw_key_len = strlen(DCD_REGKEY) + 1 + sizeof(sz_obj_type) + 1; |
| DBC_ASSERT(dw_key_len < DCD_MAXPATHLENGTH); |
| |
| /* Create proper REG key; concatenate DCD_REGKEY with |
| * obj_type. */ |
| strncpy(sz_reg_key, DCD_REGKEY, strlen(DCD_REGKEY) + 1); |
| if ((strlen(sz_reg_key) + strlen("_\0")) < |
| DCD_MAXPATHLENGTH) { |
| strncat(sz_reg_key, "_\0", 2); |
| } else { |
| status = -EPERM; |
| } |
| |
| /* This snprintf is guaranteed not to exceed max size of an |
| * integer. */ |
| status = snprintf(sz_obj_type, MAX_INT2CHAR_LENGTH, "%d", |
| obj_type); |
| |
| if (status == -1) { |
| status = -EPERM; |
| } else { |
| status = 0; |
| if ((strlen(sz_reg_key) + strlen(sz_obj_type)) < |
| DCD_MAXPATHLENGTH) { |
| strncat(sz_reg_key, sz_obj_type, |
| strlen(sz_obj_type) + 1); |
| } else { |
| status = -EPERM; |
| } |
| } |
| |
| if (!status) { |
| len = strlen(sz_reg_key); |
| spin_lock(&dbdcd_lock); |
| list_for_each_entry(dcd_key, ®_key_list, link) { |
| if (!strncmp(dcd_key->name, sz_reg_key, len) |
| && !index--) { |
| strncpy(sz_value, &dcd_key->name[len], |
| strlen(&dcd_key->name[len]) + 1); |
| break; |
| } |
| } |
| spin_unlock(&dbdcd_lock); |
| |
| if (&dcd_key->link == ®_key_list) |
| status = -ENODATA; |
| } |
| |
| if (!status) { |
| /* Create UUID value using string retrieved from |
| * registry. */ |
| uuid_uuid_from_string(sz_value, &dsp_uuid_obj); |
| |
| *uuid_obj = dsp_uuid_obj; |
| |
| /* Increment enum_refs to update reference count. */ |
| enum_refs++; |
| |
| status = 0; |
| } else if (status == -ENODATA) { |
| /* At the end of enumeration. Reset enum_refs. */ |
| enum_refs = 0; |
| |
| /* |
| * TODO: Revisit, this is not an errror case but code |
| * expects non-zero value. |
| */ |
| status = ENODATA; |
| } else { |
| status = -EPERM; |
| } |
| } |
| |
| DBC_ENSURE(uuid_obj || (status == -EPERM)); |
| |
| return status; |
| } |
| |
| /* |
| * ======== dcd_exit ======== |
| * Purpose: |
| * Discontinue usage of the DCD module. |
| */ |
| void dcd_exit(void) |
| { |
| struct dcd_key_elem *rv, *rv_tmp; |
| DBC_REQUIRE(refs > 0); |
| |
| refs--; |
| if (refs == 0) { |
| cod_exit(); |
| list_for_each_entry_safe(rv, rv_tmp, ®_key_list, link) { |
| list_del(&rv->link); |
| kfree(rv->path); |
| kfree(rv); |
| } |
| } |
| |
| DBC_ENSURE(refs >= 0); |
| } |
| |
| /* |
| * ======== dcd_get_dep_libs ======== |
| */ |
| int dcd_get_dep_libs(struct dcd_manager *hdcd_mgr, |
| struct dsp_uuid *uuid_obj, |
| u16 num_libs, struct dsp_uuid *dep_lib_uuids, |
| bool *prstnt_dep_libs, |
| enum nldr_phase phase) |
| { |
| int status = 0; |
| |
| DBC_REQUIRE(refs > 0); |
| DBC_REQUIRE(hdcd_mgr); |
| DBC_REQUIRE(uuid_obj != NULL); |
| DBC_REQUIRE(dep_lib_uuids != NULL); |
| DBC_REQUIRE(prstnt_dep_libs != NULL); |
| |
| status = |
| get_dep_lib_info(hdcd_mgr, uuid_obj, &num_libs, NULL, dep_lib_uuids, |
| prstnt_dep_libs, phase); |
| |
| return status; |
| } |
| |
| /* |
| * ======== dcd_get_num_dep_libs ======== |
| */ |
| int dcd_get_num_dep_libs(struct dcd_manager *hdcd_mgr, |
| struct dsp_uuid *uuid_obj, |
| u16 *num_libs, u16 *num_pers_libs, |
| enum nldr_phase phase) |
| { |
| int status = 0; |
| |
| DBC_REQUIRE(refs > 0); |
| DBC_REQUIRE(hdcd_mgr); |
| DBC_REQUIRE(num_libs != NULL); |
| DBC_REQUIRE(num_pers_libs != NULL); |
| DBC_REQUIRE(uuid_obj != NULL); |
| |
| status = get_dep_lib_info(hdcd_mgr, uuid_obj, num_libs, num_pers_libs, |
| NULL, NULL, phase); |
| |
| return status; |
| } |
| |
| /* |
| * ======== dcd_get_object_def ======== |
| * Purpose: |
| * Retrieves the properties of a node or processor based on the UUID and |
| * object type. |
| */ |
| int dcd_get_object_def(struct dcd_manager *hdcd_mgr, |
| struct dsp_uuid *obj_uuid, |
| enum dsp_dcdobjtype obj_type, |
| struct dcd_genericobj *obj_def) |
| { |
| struct dcd_manager *dcd_mgr_obj = hdcd_mgr; /* ptr to DCD mgr */ |
| struct cod_libraryobj *lib = NULL; |
| int status = 0; |
| u32 ul_addr = 0; /* Used by cod_get_section */ |
| u32 ul_len = 0; /* Used by cod_get_section */ |
| u32 dw_buf_size; /* Used by REG functions */ |
| char sz_reg_key[DCD_MAXPATHLENGTH]; |
| char *sz_uuid; /*[MAXUUIDLEN]; */ |
| struct dcd_key_elem *dcd_key = NULL; |
| char sz_sect_name[MAXUUIDLEN + 2]; /* ".[UUID]\0" */ |
| char *psz_coff_buf; |
| u32 dw_key_len; /* Len of REG key. */ |
| char sz_obj_type[MAX_INT2CHAR_LENGTH]; /* str. rep. of obj_type. */ |
| |
| DBC_REQUIRE(refs > 0); |
| DBC_REQUIRE(obj_def != NULL); |
| DBC_REQUIRE(obj_uuid != NULL); |
| |
| sz_uuid = kzalloc(MAXUUIDLEN, GFP_KERNEL); |
| if (!sz_uuid) { |
| status = -ENOMEM; |
| goto func_end; |
| } |
| |
| if (!hdcd_mgr) { |
| status = -EFAULT; |
| goto func_end; |
| } |
| |
| /* Pre-determine final key length. It's length of DCD_REGKEY + |
| * "_\0" + length of sz_obj_type string + terminating NULL */ |
| dw_key_len = strlen(DCD_REGKEY) + 1 + sizeof(sz_obj_type) + 1; |
| DBC_ASSERT(dw_key_len < DCD_MAXPATHLENGTH); |
| |
| /* Create proper REG key; concatenate DCD_REGKEY with obj_type. */ |
| strncpy(sz_reg_key, DCD_REGKEY, strlen(DCD_REGKEY) + 1); |
| |
| if ((strlen(sz_reg_key) + strlen("_\0")) < DCD_MAXPATHLENGTH) |
| strncat(sz_reg_key, "_\0", 2); |
| else |
| status = -EPERM; |
| |
| status = snprintf(sz_obj_type, MAX_INT2CHAR_LENGTH, "%d", obj_type); |
| if (status == -1) { |
| status = -EPERM; |
| } else { |
| status = 0; |
| |
| if ((strlen(sz_reg_key) + strlen(sz_obj_type)) < |
| DCD_MAXPATHLENGTH) { |
| strncat(sz_reg_key, sz_obj_type, |
| strlen(sz_obj_type) + 1); |
| } else { |
| status = -EPERM; |
| } |
| |
| /* Create UUID value to set in registry. */ |
| uuid_uuid_to_string(obj_uuid, sz_uuid, MAXUUIDLEN); |
| |
| if ((strlen(sz_reg_key) + MAXUUIDLEN) < DCD_MAXPATHLENGTH) |
| strncat(sz_reg_key, sz_uuid, MAXUUIDLEN); |
| else |
| status = -EPERM; |
| |
| /* Retrieve paths from the registry based on struct dsp_uuid */ |
| dw_buf_size = DCD_MAXPATHLENGTH; |
| } |
| if (!status) { |
| spin_lock(&dbdcd_lock); |
| list_for_each_entry(dcd_key, ®_key_list, link) { |
| if (!strncmp(dcd_key->name, sz_reg_key, |
| strlen(sz_reg_key) + 1)) |
| break; |
| } |
| spin_unlock(&dbdcd_lock); |
| if (&dcd_key->link == ®_key_list) { |
| status = -ENOKEY; |
| goto func_end; |
| } |
| } |
| |
| |
| /* Open COFF file. */ |
| status = cod_open(dcd_mgr_obj->cod_mgr, dcd_key->path, |
| COD_NOLOAD, &lib); |
| if (status) { |
| status = -EACCES; |
| goto func_end; |
| } |
| |
| /* Ensure sz_uuid + 1 is not greater than sizeof sz_sect_name. */ |
| DBC_ASSERT((strlen(sz_uuid) + 1) < sizeof(sz_sect_name)); |
| |
| /* Create section name based on node UUID. A period is |
| * pre-pended to the UUID string to form the section name. |
| * I.e. ".24BC8D90_BB45_11d4_B756_006008BDB66F" */ |
| strncpy(sz_sect_name, ".", 2); |
| strncat(sz_sect_name, sz_uuid, strlen(sz_uuid)); |
| |
| /* Get section information. */ |
| status = cod_get_section(lib, sz_sect_name, &ul_addr, &ul_len); |
| if (status) { |
| status = -EACCES; |
| goto func_end; |
| } |
| |
| /* Allocate zeroed buffer. */ |
| psz_coff_buf = kzalloc(ul_len + 4, GFP_KERNEL); |
| if (psz_coff_buf == NULL) { |
| status = -ENOMEM; |
| goto func_end; |
| } |
| #ifdef _DB_TIOMAP |
| if (strstr(dcd_key->path, "iva") == NULL) { |
| /* Locate section by objectID and read its content. */ |
| status = |
| cod_read_section(lib, sz_sect_name, psz_coff_buf, ul_len); |
| } else { |
| status = |
| cod_read_section(lib, sz_sect_name, psz_coff_buf, ul_len); |
| dev_dbg(bridge, "%s: Skipped Byte swap for IVA!!\n", __func__); |
| } |
| #else |
| status = cod_read_section(lib, sz_sect_name, psz_coff_buf, ul_len); |
| #endif |
| if (!status) { |
| /* Compres DSP buffer to conform to PC format. */ |
| if (strstr(dcd_key->path, "iva") == NULL) { |
| compress_buf(psz_coff_buf, ul_len, DSPWORDSIZE); |
| } else { |
| compress_buf(psz_coff_buf, ul_len, 1); |
| dev_dbg(bridge, "%s: Compressing IVA COFF buffer by 1 " |
| "for IVA!!\n", __func__); |
| } |
| |
| /* Parse the content of the COFF buffer. */ |
| status = |
| get_attrs_from_buf(psz_coff_buf, ul_len, obj_type, obj_def); |
| if (status) |
| status = -EACCES; |
| } else { |
| status = -EACCES; |
| } |
| |
| /* Free the previously allocated dynamic buffer. */ |
| kfree(psz_coff_buf); |
| func_end: |
| if (lib) |
| cod_close(lib); |
| |
| kfree(sz_uuid); |
| |
| return status; |
| } |
| |
| /* |
| * ======== dcd_get_objects ======== |
| */ |
| int dcd_get_objects(struct dcd_manager *hdcd_mgr, |
| char *sz_coff_path, dcd_registerfxn register_fxn, |
| void *handle) |
| { |
| struct dcd_manager *dcd_mgr_obj = hdcd_mgr; |
| int status = 0; |
| char *psz_coff_buf; |
| char *psz_cur; |
| struct cod_libraryobj *lib = NULL; |
| u32 ul_addr = 0; /* Used by cod_get_section */ |
| u32 ul_len = 0; /* Used by cod_get_section */ |
| char seps[] = ":, "; |
| char *token = NULL; |
| struct dsp_uuid dsp_uuid_obj; |
| s32 object_type; |
| |
| DBC_REQUIRE(refs > 0); |
| if (!hdcd_mgr) { |
| status = -EFAULT; |
| goto func_end; |
| } |
| |
| /* Open DSP coff file, don't load symbols. */ |
| status = cod_open(dcd_mgr_obj->cod_mgr, sz_coff_path, COD_NOLOAD, &lib); |
| if (status) { |
| status = -EACCES; |
| goto func_cont; |
| } |
| |
| /* Get DCD_RESIGER_SECTION section information. */ |
| status = cod_get_section(lib, DCD_REGISTER_SECTION, &ul_addr, &ul_len); |
| if (status || !(ul_len > 0)) { |
| status = -EACCES; |
| goto func_cont; |
| } |
| |
| /* Allocate zeroed buffer. */ |
| psz_coff_buf = kzalloc(ul_len + 4, GFP_KERNEL); |
| if (psz_coff_buf == NULL) { |
| status = -ENOMEM; |
| goto func_cont; |
| } |
| #ifdef _DB_TIOMAP |
| if (strstr(sz_coff_path, "iva") == NULL) { |
| /* Locate section by objectID and read its content. */ |
| status = cod_read_section(lib, DCD_REGISTER_SECTION, |
| psz_coff_buf, ul_len); |
| } else { |
| dev_dbg(bridge, "%s: Skipped Byte swap for IVA!!\n", __func__); |
| status = cod_read_section(lib, DCD_REGISTER_SECTION, |
| psz_coff_buf, ul_len); |
| } |
| #else |
| status = |
| cod_read_section(lib, DCD_REGISTER_SECTION, psz_coff_buf, ul_len); |
| #endif |
| if (!status) { |
| /* Compress DSP buffer to conform to PC format. */ |
| if (strstr(sz_coff_path, "iva") == NULL) { |
| compress_buf(psz_coff_buf, ul_len, DSPWORDSIZE); |
| } else { |
| compress_buf(psz_coff_buf, ul_len, 1); |
| dev_dbg(bridge, "%s: Compress COFF buffer with 1 word " |
| "for IVA!!\n", __func__); |
| } |
| |
| /* Read from buffer and register object in buffer. */ |
| psz_cur = psz_coff_buf; |
| while ((token = strsep(&psz_cur, seps)) && *token != '\0') { |
| /* Retrieve UUID string. */ |
| uuid_uuid_from_string(token, &dsp_uuid_obj); |
| |
| /* Retrieve object type */ |
| token = strsep(&psz_cur, seps); |
| |
| /* Retrieve object type */ |
| object_type = atoi(token); |
| |
| /* |
| * Apply register_fxn to the found DCD object. |
| * Possible actions include: |
| * |
| * 1) Register found DCD object. |
| * 2) Unregister found DCD object (when handle == NULL) |
| * 3) Add overlay node. |
| */ |
| status = |
| register_fxn(&dsp_uuid_obj, object_type, handle); |
| if (status) { |
| /* if error occurs, break from while loop. */ |
| break; |
| } |
| } |
| } else { |
| status = -EACCES; |
| } |
| |
| /* Free the previously allocated dynamic buffer. */ |
| kfree(psz_coff_buf); |
| func_cont: |
| if (lib) |
| cod_close(lib); |
| |
| func_end: |
| return status; |
| } |
| |
| /* |
| * ======== dcd_get_library_name ======== |
| * Purpose: |
| * Retrieves the library name for the given UUID. |
| * |
| */ |
| int dcd_get_library_name(struct dcd_manager *hdcd_mgr, |
| struct dsp_uuid *uuid_obj, |
| char *str_lib_name, |
| u32 *buff_size, |
| enum nldr_phase phase, bool *phase_split) |
| { |
| char sz_reg_key[DCD_MAXPATHLENGTH]; |
| char sz_uuid[MAXUUIDLEN]; |
| u32 dw_key_len; /* Len of REG key. */ |
| char sz_obj_type[MAX_INT2CHAR_LENGTH]; /* str. rep. of obj_type. */ |
| int status = 0; |
| struct dcd_key_elem *dcd_key = NULL; |
| |
| DBC_REQUIRE(uuid_obj != NULL); |
| DBC_REQUIRE(str_lib_name != NULL); |
| DBC_REQUIRE(buff_size != NULL); |
| DBC_REQUIRE(hdcd_mgr); |
| |
| dev_dbg(bridge, "%s: hdcd_mgr %p, uuid_obj %p, str_lib_name %p," |
| " buff_size %p\n", __func__, hdcd_mgr, uuid_obj, str_lib_name, |
| buff_size); |
| |
| /* |
| * Pre-determine final key length. It's length of DCD_REGKEY + |
| * "_\0" + length of sz_obj_type string + terminating NULL. |
| */ |
| dw_key_len = strlen(DCD_REGKEY) + 1 + sizeof(sz_obj_type) + 1; |
| DBC_ASSERT(dw_key_len < DCD_MAXPATHLENGTH); |
| |
| /* Create proper REG key; concatenate DCD_REGKEY with obj_type. */ |
| strncpy(sz_reg_key, DCD_REGKEY, strlen(DCD_REGKEY) + 1); |
| if ((strlen(sz_reg_key) + strlen("_\0")) < DCD_MAXPATHLENGTH) |
| strncat(sz_reg_key, "_\0", 2); |
| else |
| status = -EPERM; |
| |
| switch (phase) { |
| case NLDR_CREATE: |
| /* create phase type */ |
| sprintf(sz_obj_type, "%d", DSP_DCDCREATELIBTYPE); |
| break; |
| case NLDR_EXECUTE: |
| /* execute phase type */ |
| sprintf(sz_obj_type, "%d", DSP_DCDEXECUTELIBTYPE); |
| break; |
| case NLDR_DELETE: |
| /* delete phase type */ |
| sprintf(sz_obj_type, "%d", DSP_DCDDELETELIBTYPE); |
| break; |
| case NLDR_NOPHASE: |
| /* known to be a dependent library */ |
| sprintf(sz_obj_type, "%d", DSP_DCDLIBRARYTYPE); |
| break; |
| default: |
| status = -EINVAL; |
| DBC_ASSERT(false); |
| } |
| if (!status) { |
| if ((strlen(sz_reg_key) + strlen(sz_obj_type)) < |
| DCD_MAXPATHLENGTH) { |
| strncat(sz_reg_key, sz_obj_type, |
| strlen(sz_obj_type) + 1); |
| } else { |
| status = -EPERM; |
| } |
| /* Create UUID value to find match in registry. */ |
| uuid_uuid_to_string(uuid_obj, sz_uuid, MAXUUIDLEN); |
| if ((strlen(sz_reg_key) + MAXUUIDLEN) < DCD_MAXPATHLENGTH) |
| strncat(sz_reg_key, sz_uuid, MAXUUIDLEN); |
| else |
| status = -EPERM; |
| } |
| if (!status) { |
| spin_lock(&dbdcd_lock); |
| list_for_each_entry(dcd_key, ®_key_list, link) { |
| /* See if the name matches. */ |
| if (!strncmp(dcd_key->name, sz_reg_key, |
| strlen(sz_reg_key) + 1)) |
| break; |
| } |
| spin_unlock(&dbdcd_lock); |
| } |
| |
| if (&dcd_key->link == ®_key_list) |
| status = -ENOKEY; |
| |
| /* If can't find, phases might be registered as generic LIBRARYTYPE */ |
| if (status && phase != NLDR_NOPHASE) { |
| if (phase_split) |
| *phase_split = false; |
| |
| strncpy(sz_reg_key, DCD_REGKEY, strlen(DCD_REGKEY) + 1); |
| if ((strlen(sz_reg_key) + strlen("_\0")) < |
| DCD_MAXPATHLENGTH) { |
| strncat(sz_reg_key, "_\0", 2); |
| } else { |
| status = -EPERM; |
| } |
| sprintf(sz_obj_type, "%d", DSP_DCDLIBRARYTYPE); |
| if ((strlen(sz_reg_key) + strlen(sz_obj_type)) |
| < DCD_MAXPATHLENGTH) { |
| strncat(sz_reg_key, sz_obj_type, |
| strlen(sz_obj_type) + 1); |
| } else { |
| status = -EPERM; |
| } |
| uuid_uuid_to_string(uuid_obj, sz_uuid, MAXUUIDLEN); |
| if ((strlen(sz_reg_key) + MAXUUIDLEN) < DCD_MAXPATHLENGTH) |
| strncat(sz_reg_key, sz_uuid, MAXUUIDLEN); |
| else |
| status = -EPERM; |
| |
| spin_lock(&dbdcd_lock); |
| list_for_each_entry(dcd_key, ®_key_list, link) { |
| /* See if the name matches. */ |
| if (!strncmp(dcd_key->name, sz_reg_key, |
| strlen(sz_reg_key) + 1)) |
| break; |
| } |
| spin_unlock(&dbdcd_lock); |
| |
| status = (&dcd_key->link != ®_key_list) ? |
| 0 : -ENOKEY; |
| } |
| |
| if (!status) |
| memcpy(str_lib_name, dcd_key->path, strlen(dcd_key->path) + 1); |
| return status; |
| } |
| |
| /* |
| * ======== dcd_init ======== |
| * Purpose: |
| * Initialize the DCD module. |
| */ |
| bool dcd_init(void) |
| { |
| bool init_cod; |
| bool ret = true; |
| |
| DBC_REQUIRE(refs >= 0); |
| |
| if (refs == 0) { |
| /* Initialize required modules. */ |
| init_cod = cod_init(); |
| |
| if (!init_cod) { |
| ret = false; |
| /* Exit initialized modules. */ |
| if (init_cod) |
| cod_exit(); |
| } |
| |
| INIT_LIST_HEAD(®_key_list); |
| } |
| |
| if (ret) |
| refs++; |
| |
| DBC_ENSURE((ret && (refs > 0)) || (!ret && (refs == 0))); |
| |
| return ret; |
| } |
| |
| /* |
| * ======== dcd_register_object ======== |
| * Purpose: |
| * Registers a node or a processor with the DCD. |
| * If psz_path_name == NULL, unregister the specified DCD object. |
| */ |
| int dcd_register_object(struct dsp_uuid *uuid_obj, |
| enum dsp_dcdobjtype obj_type, |
| char *psz_path_name) |
| { |
| int status = 0; |
| char sz_reg_key[DCD_MAXPATHLENGTH]; |
| char sz_uuid[MAXUUIDLEN + 1]; |
| u32 dw_path_size = 0; |
| u32 dw_key_len; /* Len of REG key. */ |
| char sz_obj_type[MAX_INT2CHAR_LENGTH]; /* str. rep. of obj_type. */ |
| struct dcd_key_elem *dcd_key = NULL; |
| |
| DBC_REQUIRE(refs > 0); |
| DBC_REQUIRE(uuid_obj != NULL); |
| DBC_REQUIRE((obj_type == DSP_DCDNODETYPE) || |
| (obj_type == DSP_DCDPROCESSORTYPE) || |
| (obj_type == DSP_DCDLIBRARYTYPE) || |
| (obj_type == DSP_DCDCREATELIBTYPE) || |
| (obj_type == DSP_DCDEXECUTELIBTYPE) || |
| (obj_type == DSP_DCDDELETELIBTYPE)); |
| |
| dev_dbg(bridge, "%s: object UUID %p, obj_type %d, szPathName %s\n", |
| __func__, uuid_obj, obj_type, psz_path_name); |
| |
| /* |
| * Pre-determine final key length. It's length of DCD_REGKEY + |
| * "_\0" + length of sz_obj_type string + terminating NULL. |
| */ |
| dw_key_len = strlen(DCD_REGKEY) + 1 + sizeof(sz_obj_type) + 1; |
| DBC_ASSERT(dw_key_len < DCD_MAXPATHLENGTH); |
| |
| /* Create proper REG key; concatenate DCD_REGKEY with obj_type. */ |
| strncpy(sz_reg_key, DCD_REGKEY, strlen(DCD_REGKEY) + 1); |
| if ((strlen(sz_reg_key) + strlen("_\0")) < DCD_MAXPATHLENGTH) |
| strncat(sz_reg_key, "_\0", 2); |
| else { |
| status = -EPERM; |
| goto func_end; |
| } |
| |
| status = snprintf(sz_obj_type, MAX_INT2CHAR_LENGTH, "%d", obj_type); |
| if (status == -1) { |
| status = -EPERM; |
| } else { |
| status = 0; |
| if ((strlen(sz_reg_key) + strlen(sz_obj_type)) < |
| DCD_MAXPATHLENGTH) { |
| strncat(sz_reg_key, sz_obj_type, |
| strlen(sz_obj_type) + 1); |
| } else |
| status = -EPERM; |
| |
| /* Create UUID value to set in registry. */ |
| uuid_uuid_to_string(uuid_obj, sz_uuid, MAXUUIDLEN); |
| if ((strlen(sz_reg_key) + MAXUUIDLEN) < DCD_MAXPATHLENGTH) |
| strncat(sz_reg_key, sz_uuid, MAXUUIDLEN); |
| else |
| status = -EPERM; |
| } |
| |
| if (status) |
| goto func_end; |
| |
| /* |
| * If psz_path_name != NULL, perform registration, otherwise, |
| * perform unregistration. |
| */ |
| |
| if (psz_path_name) { |
| dw_path_size = strlen(psz_path_name) + 1; |
| spin_lock(&dbdcd_lock); |
| list_for_each_entry(dcd_key, ®_key_list, link) { |
| /* See if the name matches. */ |
| if (!strncmp(dcd_key->name, sz_reg_key, |
| strlen(sz_reg_key) + 1)) |
| break; |
| } |
| spin_unlock(&dbdcd_lock); |
| if (&dcd_key->link == ®_key_list) { |
| /* |
| * Add new reg value (UUID+obj_type) |
| * with COFF path info |
| */ |
| |
| dcd_key = kmalloc(sizeof(struct dcd_key_elem), |
| GFP_KERNEL); |
| if (!dcd_key) { |
| status = -ENOMEM; |
| goto func_end; |
| } |
| |
| dcd_key->path = kmalloc(strlen(sz_reg_key) + 1, |
| GFP_KERNEL); |
| |
| if (!dcd_key->path) { |
| kfree(dcd_key); |
| status = -ENOMEM; |
| goto func_end; |
| } |
| |
| strncpy(dcd_key->name, sz_reg_key, |
| strlen(sz_reg_key) + 1); |
| strncpy(dcd_key->path, psz_path_name , |
| dw_path_size); |
| spin_lock(&dbdcd_lock); |
| list_add_tail(&dcd_key->link, ®_key_list); |
| spin_unlock(&dbdcd_lock); |
| } else { |
| /* Make sure the new data is the same. */ |
| if (strncmp(dcd_key->path, psz_path_name, |
| dw_path_size)) { |
| /* The caller needs a different data size! */ |
| kfree(dcd_key->path); |
| dcd_key->path = kmalloc(dw_path_size, |
| GFP_KERNEL); |
| if (dcd_key->path == NULL) { |
| status = -ENOMEM; |
| goto func_end; |
| } |
| } |
| |
| /* We have a match! Copy out the data. */ |
| memcpy(dcd_key->path, psz_path_name, dw_path_size); |
| } |
| dev_dbg(bridge, "%s: psz_path_name=%s, dw_path_size=%d\n", |
| __func__, psz_path_name, dw_path_size); |
| } else { |
| /* Deregister an existing object */ |
| spin_lock(&dbdcd_lock); |
| list_for_each_entry(dcd_key, ®_key_list, link) { |
| if (!strncmp(dcd_key->name, sz_reg_key, |
| strlen(sz_reg_key) + 1)) { |
| list_del(&dcd_key->link); |
| kfree(dcd_key->path); |
| kfree(dcd_key); |
| break; |
| } |
| } |
| spin_unlock(&dbdcd_lock); |
| if (&dcd_key->link == ®_key_list) |
| status = -EPERM; |
| } |
| |
| if (!status) { |
| /* |
| * Because the node database has been updated through a |
| * successful object registration/de-registration operation, |
| * we need to reset the object enumeration counter to allow |
| * current enumerations to reflect this update in the node |
| * database. |
| */ |
| enum_refs = 0; |
| } |
| func_end: |
| return status; |
| } |
| |
| /* |
| * ======== dcd_unregister_object ======== |
| * Call DCD_Register object with psz_path_name set to NULL to |
| * perform actual object de-registration. |
| */ |
| int dcd_unregister_object(struct dsp_uuid *uuid_obj, |
| enum dsp_dcdobjtype obj_type) |
| { |
| int status = 0; |
| |
| DBC_REQUIRE(refs > 0); |
| DBC_REQUIRE(uuid_obj != NULL); |
| DBC_REQUIRE((obj_type == DSP_DCDNODETYPE) || |
| (obj_type == DSP_DCDPROCESSORTYPE) || |
| (obj_type == DSP_DCDLIBRARYTYPE) || |
| (obj_type == DSP_DCDCREATELIBTYPE) || |
| (obj_type == DSP_DCDEXECUTELIBTYPE) || |
| (obj_type == DSP_DCDDELETELIBTYPE)); |
| |
| /* |
| * When dcd_register_object is called with NULL as pathname, |
| * it indicates an unregister object operation. |
| */ |
| status = dcd_register_object(uuid_obj, obj_type, NULL); |
| |
| return status; |
| } |
| |
| /* |
| ********************************************************************** |
| * DCD Helper Functions |
| ********************************************************************** |
| */ |
| |
| /* |
| * ======== atoi ======== |
| * Purpose: |
| * This function converts strings in decimal or hex format to integers. |
| */ |
| static s32 atoi(char *psz_buf) |
| { |
| char *pch = psz_buf; |
| s32 base = 0; |
| |
| while (isspace(*pch)) |
| pch++; |
| |
| if (*pch == '-' || *pch == '+') { |
| base = 10; |
| pch++; |
| } else if (*pch && tolower(pch[strlen(pch) - 1]) == 'h') { |
| base = 16; |
| } |
| |
| return simple_strtoul(pch, NULL, base); |
| } |
| |
| /* |
| * ======== get_attrs_from_buf ======== |
| * Purpose: |
| * Parse the content of a buffer filled with DSP-side data and |
| * retrieve an object's attributes from it. IMPORTANT: Assume the |
| * buffer has been converted from DSP format to GPP format. |
| */ |
| static int get_attrs_from_buf(char *psz_buf, u32 ul_buf_size, |
| enum dsp_dcdobjtype obj_type, |
| struct dcd_genericobj *gen_obj) |
| { |
| int status = 0; |
| char seps[] = ", "; |
| char *psz_cur; |
| char *token; |
| s32 token_len = 0; |
| u32 i = 0; |
| #ifdef _DB_TIOMAP |
| s32 entry_id; |
| #endif |
| |
| DBC_REQUIRE(psz_buf != NULL); |
| DBC_REQUIRE(ul_buf_size != 0); |
| DBC_REQUIRE((obj_type == DSP_DCDNODETYPE) |
| || (obj_type == DSP_DCDPROCESSORTYPE)); |
| DBC_REQUIRE(gen_obj != NULL); |
| |
| switch (obj_type) { |
| case DSP_DCDNODETYPE: |
| /* |
| * Parse COFF sect buffer to retrieve individual tokens used |
| * to fill in object attrs. |
| */ |
| psz_cur = psz_buf; |
| token = strsep(&psz_cur, seps); |
| |
| /* u32 cb_struct */ |
| gen_obj->obj_data.node_obj.ndb_props.cb_struct = |
| (u32) atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| /* dsp_uuid ui_node_id */ |
| uuid_uuid_from_string(token, |
| &gen_obj->obj_data.node_obj.ndb_props. |
| ui_node_id); |
| token = strsep(&psz_cur, seps); |
| |
| /* ac_name */ |
| DBC_REQUIRE(token); |
| token_len = strlen(token); |
| if (token_len > DSP_MAXNAMELEN - 1) |
| token_len = DSP_MAXNAMELEN - 1; |
| |
| strncpy(gen_obj->obj_data.node_obj.ndb_props.ac_name, |
| token, token_len); |
| gen_obj->obj_data.node_obj.ndb_props.ac_name[token_len] = '\0'; |
| token = strsep(&psz_cur, seps); |
| /* u32 ntype */ |
| gen_obj->obj_data.node_obj.ndb_props.ntype = atoi(token); |
| token = strsep(&psz_cur, seps); |
| /* u32 cache_on_gpp */ |
| gen_obj->obj_data.node_obj.ndb_props.cache_on_gpp = atoi(token); |
| token = strsep(&psz_cur, seps); |
| /* dsp_resourcereqmts dsp_resource_reqmts */ |
| gen_obj->obj_data.node_obj.ndb_props.dsp_resource_reqmts. |
| cb_struct = (u32) atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| gen_obj->obj_data.node_obj.ndb_props. |
| dsp_resource_reqmts.static_data_size = atoi(token); |
| token = strsep(&psz_cur, seps); |
| gen_obj->obj_data.node_obj.ndb_props. |
| dsp_resource_reqmts.global_data_size = atoi(token); |
| token = strsep(&psz_cur, seps); |
| gen_obj->obj_data.node_obj.ndb_props. |
| dsp_resource_reqmts.program_mem_size = atoi(token); |
| token = strsep(&psz_cur, seps); |
| gen_obj->obj_data.node_obj.ndb_props. |
| dsp_resource_reqmts.wc_execution_time = atoi(token); |
| token = strsep(&psz_cur, seps); |
| gen_obj->obj_data.node_obj.ndb_props. |
| dsp_resource_reqmts.wc_period = atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| gen_obj->obj_data.node_obj.ndb_props. |
| dsp_resource_reqmts.wc_deadline = atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| gen_obj->obj_data.node_obj.ndb_props. |
| dsp_resource_reqmts.avg_exection_time = atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| gen_obj->obj_data.node_obj.ndb_props. |
| dsp_resource_reqmts.minimum_period = atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| /* s32 prio */ |
| gen_obj->obj_data.node_obj.ndb_props.prio = atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| /* u32 stack_size */ |
| gen_obj->obj_data.node_obj.ndb_props.stack_size = atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| /* u32 sys_stack_size */ |
| gen_obj->obj_data.node_obj.ndb_props.sys_stack_size = |
| atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| /* u32 stack_seg */ |
| gen_obj->obj_data.node_obj.ndb_props.stack_seg = atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| /* u32 message_depth */ |
| gen_obj->obj_data.node_obj.ndb_props.message_depth = |
| atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| /* u32 num_input_streams */ |
| gen_obj->obj_data.node_obj.ndb_props.num_input_streams = |
| atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| /* u32 num_output_streams */ |
| gen_obj->obj_data.node_obj.ndb_props.num_output_streams = |
| atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| /* u32 timeout */ |
| gen_obj->obj_data.node_obj.ndb_props.timeout = atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| /* char *str_create_phase_fxn */ |
| DBC_REQUIRE(token); |
| token_len = strlen(token); |
| gen_obj->obj_data.node_obj.str_create_phase_fxn = |
| kzalloc(token_len + 1, GFP_KERNEL); |
| strncpy(gen_obj->obj_data.node_obj.str_create_phase_fxn, |
| token, token_len); |
| gen_obj->obj_data.node_obj.str_create_phase_fxn[token_len] = |
| '\0'; |
| token = strsep(&psz_cur, seps); |
| |
| /* char *str_execute_phase_fxn */ |
| DBC_REQUIRE(token); |
| token_len = strlen(token); |
| gen_obj->obj_data.node_obj.str_execute_phase_fxn = |
| kzalloc(token_len + 1, GFP_KERNEL); |
| strncpy(gen_obj->obj_data.node_obj.str_execute_phase_fxn, |
| token, token_len); |
| gen_obj->obj_data.node_obj.str_execute_phase_fxn[token_len] = |
| '\0'; |
| token = strsep(&psz_cur, seps); |
| |
| /* char *str_delete_phase_fxn */ |
| DBC_REQUIRE(token); |
| token_len = strlen(token); |
| gen_obj->obj_data.node_obj.str_delete_phase_fxn = |
| kzalloc(token_len + 1, GFP_KERNEL); |
| strncpy(gen_obj->obj_data.node_obj.str_delete_phase_fxn, |
| token, token_len); |
| gen_obj->obj_data.node_obj.str_delete_phase_fxn[token_len] = |
| '\0'; |
| token = strsep(&psz_cur, seps); |
| |
| /* Segment id for message buffers */ |
| gen_obj->obj_data.node_obj.msg_segid = atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| /* Message notification type */ |
| gen_obj->obj_data.node_obj.msg_notify_type = atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| /* char *str_i_alg_name */ |
| if (token) { |
| token_len = strlen(token); |
| gen_obj->obj_data.node_obj.str_i_alg_name = |
| kzalloc(token_len + 1, GFP_KERNEL); |
| strncpy(gen_obj->obj_data.node_obj.str_i_alg_name, |
| token, token_len); |
| gen_obj->obj_data.node_obj.str_i_alg_name[token_len] = |
| '\0'; |
| token = strsep(&psz_cur, seps); |
| } |
| |
| /* Load type (static, dynamic, or overlay) */ |
| if (token) { |
| gen_obj->obj_data.node_obj.load_type = atoi(token); |
| token = strsep(&psz_cur, seps); |
| } |
| |
| /* Dynamic load data requirements */ |
| if (token) { |
| gen_obj->obj_data.node_obj.data_mem_seg_mask = |
| atoi(token); |
| token = strsep(&psz_cur, seps); |
| } |
| |
| /* Dynamic load code requirements */ |
| if (token) { |
| gen_obj->obj_data.node_obj.code_mem_seg_mask = |
| atoi(token); |
| token = strsep(&psz_cur, seps); |
| } |
| |
| /* Extract node profiles into node properties */ |
| if (token) { |
| |
| gen_obj->obj_data.node_obj.ndb_props.count_profiles = |
| atoi(token); |
| for (i = 0; |
| i < |
| gen_obj->obj_data.node_obj. |
| ndb_props.count_profiles; i++) { |
| token = strsep(&psz_cur, seps); |
| if (token) { |
| /* Heap Size for the node */ |
| gen_obj->obj_data.node_obj. |
| ndb_props.node_profiles[i]. |
| heap_size = atoi(token); |
| } |
| } |
| } |
| token = strsep(&psz_cur, seps); |
| if (token) { |
| gen_obj->obj_data.node_obj.ndb_props.stack_seg_name = |
| (u32) (token); |
| } |
| |
| break; |
| |
| case DSP_DCDPROCESSORTYPE: |
| /* |
| * Parse COFF sect buffer to retrieve individual tokens used |
| * to fill in object attrs. |
| */ |
| psz_cur = psz_buf; |
| token = strsep(&psz_cur, seps); |
| |
| gen_obj->obj_data.proc_info.cb_struct = atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| gen_obj->obj_data.proc_info.processor_family = atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| gen_obj->obj_data.proc_info.processor_type = atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| gen_obj->obj_data.proc_info.clock_rate = atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| gen_obj->obj_data.proc_info.internal_mem_size = atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| gen_obj->obj_data.proc_info.external_mem_size = atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| gen_obj->obj_data.proc_info.processor_id = atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| gen_obj->obj_data.proc_info.ty_running_rtos = atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| gen_obj->obj_data.proc_info.node_min_priority = atoi(token); |
| token = strsep(&psz_cur, seps); |
| |
| gen_obj->obj_data.proc_info.node_max_priority = atoi(token); |
| |
| #ifdef _DB_TIOMAP |
| /* Proc object may contain additional(extended) attributes. */ |
| /* attr must match proc.hxx */ |
| for (entry_id = 0; entry_id < 7; entry_id++) { |
| token = strsep(&psz_cur, seps); |
| gen_obj->obj_data.ext_proc_obj.ty_tlb[entry_id]. |
| gpp_phys = atoi(token); |
| |
| token = strsep(&psz_cur, seps); |
| gen_obj->obj_data.ext_proc_obj.ty_tlb[entry_id]. |
| dsp_virt = atoi(token); |
| } |
| #endif |
| |
| break; |
| |
| default: |
| status = -EPERM; |
| break; |
| } |
| |
| return status; |
| } |
| |
| /* |
| * ======== CompressBuffer ======== |
| * Purpose: |
| * Compress the DSP buffer, if necessary, to conform to PC format. |
| */ |
| static void compress_buf(char *psz_buf, u32 ul_buf_size, s32 char_size) |
| { |
| char *p; |
| char ch; |
| char *q; |
| |
| p = psz_buf; |
| if (p == NULL) |
| return; |
| |
| for (q = psz_buf; q < (psz_buf + ul_buf_size);) { |
| ch = dsp_char2_gpp_char(q, char_size); |
| if (ch == '\\') { |
| q += char_size; |
| ch = dsp_char2_gpp_char(q, char_size); |
| switch (ch) { |
| case 't': |
| *p = '\t'; |
| break; |
| |
| case 'n': |
| *p = '\n'; |
| break; |
| |
| case 'r': |
| *p = '\r'; |
| break; |
| |
| case '0': |
| *p = '\0'; |
| break; |
| |
| default: |
| *p = ch; |
| break; |
| } |
| } else { |
| *p = ch; |
| } |
| p++; |
| q += char_size; |
| } |
| |
| /* NULL out remainder of buffer. */ |
| while (p < q) |
| *p++ = '\0'; |
| } |
| |
| /* |
| * ======== dsp_char2_gpp_char ======== |
| * Purpose: |
| * Convert DSP char to host GPP char in a portable manner |
| */ |
| static char dsp_char2_gpp_char(char *word, s32 dsp_char_size) |
| { |
| char ch = '\0'; |
| char *ch_src; |
| s32 i; |
| |
| for (ch_src = word, i = dsp_char_size; i > 0; i--) |
| ch |= *ch_src++; |
| |
| return ch; |
| } |
| |
| /* |
| * ======== get_dep_lib_info ======== |
| */ |
| static int get_dep_lib_info(struct dcd_manager *hdcd_mgr, |
| struct dsp_uuid *uuid_obj, |
| u16 *num_libs, |
| u16 *num_pers_libs, |
| struct dsp_uuid *dep_lib_uuids, |
| bool *prstnt_dep_libs, |
| enum nldr_phase phase) |
| { |
| struct dcd_manager *dcd_mgr_obj = hdcd_mgr; |
| char *psz_coff_buf = NULL; |
| char *psz_cur; |
| char *psz_file_name = NULL; |
| struct cod_libraryobj *lib = NULL; |
| u32 ul_addr = 0; /* Used by cod_get_section */ |
| u32 ul_len = 0; /* Used by cod_get_section */ |
| u32 dw_data_size = COD_MAXPATHLENGTH; |
| char seps[] = ", "; |
| char *token = NULL; |
| bool get_uuids = (dep_lib_uuids != NULL); |
| u16 dep_libs = 0; |
| int status = 0; |
| |
| DBC_REQUIRE(refs > 0); |
| |
| DBC_REQUIRE(hdcd_mgr); |
| DBC_REQUIRE(num_libs != NULL); |
| DBC_REQUIRE(uuid_obj != NULL); |
| |
| /* Initialize to 0 dependent libraries, if only counting number of |
| * dependent libraries */ |
| if (!get_uuids) { |
| *num_libs = 0; |
| *num_pers_libs = 0; |
| } |
| |
| /* Allocate a buffer for file name */ |
| psz_file_name = kzalloc(dw_data_size, GFP_KERNEL); |
| if (psz_file_name == NULL) { |
| status = -ENOMEM; |
| } else { |
| /* Get the name of the library */ |
| status = dcd_get_library_name(hdcd_mgr, uuid_obj, psz_file_name, |
| &dw_data_size, phase, NULL); |
| } |
| |
| /* Open the library */ |
| if (!status) { |
| status = cod_open(dcd_mgr_obj->cod_mgr, psz_file_name, |
| COD_NOLOAD, &lib); |
| } |
| if (!status) { |
| /* Get dependent library section information. */ |
| status = cod_get_section(lib, DEPLIBSECT, &ul_addr, &ul_len); |
| |
| if (status) { |
| /* Ok, no dependent libraries */ |
| ul_len = 0; |
| status = 0; |
| } |
| } |
| |
| if (status || !(ul_len > 0)) |
| goto func_cont; |
| |
| /* Allocate zeroed buffer. */ |
| psz_coff_buf = kzalloc(ul_len + 4, GFP_KERNEL); |
| if (psz_coff_buf == NULL) |
| status = -ENOMEM; |
| |
| /* Read section contents. */ |
| status = cod_read_section(lib, DEPLIBSECT, psz_coff_buf, ul_len); |
| if (status) |
| goto func_cont; |
| |
| /* Compress and format DSP buffer to conform to PC format. */ |
| compress_buf(psz_coff_buf, ul_len, DSPWORDSIZE); |
| |
| /* Read from buffer */ |
| psz_cur = psz_coff_buf; |
| while ((token = strsep(&psz_cur, seps)) && *token != '\0') { |
| if (get_uuids) { |
| if (dep_libs >= *num_libs) { |
| /* Gone beyond the limit */ |
| break; |
| } else { |
| /* Retrieve UUID string. */ |
| uuid_uuid_from_string(token, |
| &(dep_lib_uuids |
| [dep_libs])); |
| /* Is this library persistent? */ |
| token = strsep(&psz_cur, seps); |
| prstnt_dep_libs[dep_libs] = atoi(token); |
| dep_libs++; |
| } |
| } else { |
| /* Advanc to next token */ |
| token = strsep(&psz_cur, seps); |
| if (atoi(token)) |
| (*num_pers_libs)++; |
| |
| /* Just counting number of dependent libraries */ |
| (*num_libs)++; |
| } |
| } |
| func_cont: |
| if (lib) |
| cod_close(lib); |
| |
| /* Free previously allocated dynamic buffers. */ |
| kfree(psz_file_name); |
| |
| kfree(psz_coff_buf); |
| |
| return status; |
| } |