diff options
author | Mauro Carvalho Chehab <m.chehab@samsung.com> | 2014-01-24 10:25:07 +0100 |
---|---|---|
committer | Mauro Carvalho Chehab <m.chehab@samsung.com> | 2014-03-04 18:34:27 +0100 |
commit | b240eacdd536bac23c9d48dfc3d527ed6870ddad (patch) | |
tree | e77bc3ab087b942d20ef7cc03d3a2d1d8ef7d6cf | |
parent | [media] drx-j: prepend function names with drx_ at drx_driver.c (diff) | |
download | linux-b240eacdd536bac23c9d48dfc3d527ed6870ddad.tar.xz linux-b240eacdd536bac23c9d48dfc3d527ed6870ddad.zip |
[media] drx-j: get rid of drx_driver.c
This file contains just the firmware load code, that it is also
somewhat duplicated at drxj.c.
Move the code into there. Latter patches will remove the
duplicated code.
Acked-by: Devin Heitmueller <dheitmueller@kernellabs.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
-rw-r--r-- | drivers/media/dvb-frontends/drx39xyj/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/dvb-frontends/drx39xyj/drx_driver.c | 651 | ||||
-rw-r--r-- | drivers/media/dvb-frontends/drx39xyj/drxj.c | 597 |
3 files changed, 593 insertions, 657 deletions
diff --git a/drivers/media/dvb-frontends/drx39xyj/Makefile b/drivers/media/dvb-frontends/drx39xyj/Makefile index f84c5d87d771..d9ed094e0d18 100644 --- a/drivers/media/dvb-frontends/drx39xyj/Makefile +++ b/drivers/media/dvb-frontends/drx39xyj/Makefile @@ -1,4 +1,4 @@ -drx39xyj-objs := drx39xxj.o drx_driver.o drx39xxj_dummy.o drxj.o drx_dap_fasi.o +drx39xyj-objs := drx39xxj.o drx39xxj_dummy.o drxj.o drx_dap_fasi.o obj-$(CONFIG_DVB_DRX39XYJ) += drx39xyj.o diff --git a/drivers/media/dvb-frontends/drx39xyj/drx_driver.c b/drivers/media/dvb-frontends/drx39xyj/drx_driver.c deleted file mode 100644 index 0ebc0d285296..000000000000 --- a/drivers/media/dvb-frontends/drx39xyj/drx_driver.c +++ /dev/null @@ -1,651 +0,0 @@ -/* - Generic DRX functionality, DRX driver core. - - Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of Trident Microsystems nor Hauppauge Computer Works - nor the names of its contributors may be used to endorse or promote - products derived from this software without specific prior written - permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ - -/*------------------------------------------------------------------------------ -INCLUDE FILES -------------------------------------------------------------------------------*/ -#include "drx_driver.h" - -#define VERSION_MAJOR 0 -#define VERSION_MINOR 0 -#define VERSION_PATCH 0 - -/* - * DEFINES - */ - -/* - * MICROCODE RELATED DEFINES - */ - -/* Magic word for checking correct Endianess of microcode data */ -#define DRX_UCODE_MAGIC_WORD ((((u16)'H')<<8)+((u16)'L')) - -/* CRC flag in ucode header, flags field. */ -#define DRX_UCODE_CRC_FLAG (0x0001) - -/* - * Maximum size of buffer used to verify the microcode. - * Must be an even number - */ -#define DRX_UCODE_MAX_BUF_SIZE (DRXDAP_MAX_RCHUNKSIZE) - -#if DRX_UCODE_MAX_BUF_SIZE & 1 -#error DRX_UCODE_MAX_BUF_SIZE must be an even number -#endif - -/* - * Power mode macros - */ - -#define DRX_ISPOWERDOWNMODE(mode) ((mode == DRX_POWER_MODE_9) || \ - (mode == DRX_POWER_MODE_10) || \ - (mode == DRX_POWER_MODE_11) || \ - (mode == DRX_POWER_MODE_12) || \ - (mode == DRX_POWER_MODE_13) || \ - (mode == DRX_POWER_MODE_14) || \ - (mode == DRX_POWER_MODE_15) || \ - (mode == DRX_POWER_MODE_16) || \ - (mode == DRX_POWER_DOWN)) - -/*------------------------------------------------------------------------------ -STRUCTURES -------------------------------------------------------------------------------*/ - -/** - * struct drxu_code_block_hdr - Structure of the microcode block headers - * - * @addr: Destination address of the data in this block - * @size: Size of the block data following this header counted in - * 16 bits words - * @CRC: CRC value of the data block, only valid if CRC flag is - * set. - */ -struct drxu_code_block_hdr { - u32 addr; - u16 size; - u16 flags; - u16 CRC; -}; - -/*------------------------------------------------------------------------------ -FUNCTIONS -------------------------------------------------------------------------------*/ - -/* - * Microcode related functions - */ - -/** - * drx_u_code_compute_crc - Compute CRC of block of microcode data. - * @block_data: Pointer to microcode data. - * @nr_words: Size of microcode block (number of 16 bits words). - * - * returns The computed CRC residue. - */ -static u16 drx_u_code_compute_crc(u8 *block_data, u16 nr_words) -{ - u16 i = 0; - u16 j = 0; - u32 crc_word = 0; - u32 carry = 0; - - while (i < nr_words) { - crc_word |= (u32)be16_to_cpu(*(u32 *)(block_data)); - for (j = 0; j < 16; j++) { - crc_word <<= 1; - if (carry != 0) - crc_word ^= 0x80050000UL; - carry = crc_word & 0x80000000UL; - } - i++; - block_data += (sizeof(u16)); - } - return (u16)(crc_word >> 16); -} - -/** - * drx_check_firmware - checks if the loaded firmware is valid - * - * @demod: demod structure - * @mc_data: pointer to the start of the firmware - * @size: firmware size - */ -static int drx_check_firmware(struct drx_demod_instance *demod, u8 *mc_data, - unsigned size) -{ - struct drxu_code_block_hdr block_hdr; - int i; - unsigned count = 2 * sizeof(u16); - u32 mc_dev_type, mc_version, mc_base_version; - u16 mc_nr_of_blks = be16_to_cpu(*(u32 *)(mc_data + sizeof(u16))); - - /* - * Scan microcode blocks first for version info - * and firmware check - */ - - /* Clear version block */ - DRX_ATTR_MCRECORD(demod).aux_type = 0; - DRX_ATTR_MCRECORD(demod).mc_dev_type = 0; - DRX_ATTR_MCRECORD(demod).mc_version = 0; - DRX_ATTR_MCRECORD(demod).mc_base_version = 0; - - for (i = 0; i < mc_nr_of_blks; i++) { - if (count + 3 * sizeof(u16) + sizeof(u32) > size) - goto eof; - - /* Process block header */ - block_hdr.addr = be32_to_cpu(*(u32 *)(mc_data + count)); - count += sizeof(u32); - block_hdr.size = be16_to_cpu(*(u32 *)(mc_data + count)); - count += sizeof(u16); - block_hdr.flags = be16_to_cpu(*(u32 *)(mc_data + count)); - count += sizeof(u16); - block_hdr.CRC = be16_to_cpu(*(u32 *)(mc_data + count)); - count += sizeof(u16); - - pr_debug("%u: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n", - count, block_hdr.addr, block_hdr.size, block_hdr.flags, - block_hdr.CRC); - - if (block_hdr.flags & 0x8) { - u8 *auxblk = ((void *)mc_data) + block_hdr.addr; - u16 auxtype; - - if (block_hdr.addr + sizeof(u16) > size) - goto eof; - - auxtype = be16_to_cpu(*(u32 *)(auxblk)); - - /* Aux block. Check type */ - if (DRX_ISMCVERTYPE(auxtype)) { - if (block_hdr.addr + 2 * sizeof(u16) + 2 * sizeof (u32) > size) - goto eof; - - auxblk += sizeof(u16); - mc_dev_type = be32_to_cpu(*(u32 *)(auxblk)); - auxblk += sizeof(u32); - mc_version = be32_to_cpu(*(u32 *)(auxblk)); - auxblk += sizeof(u32); - mc_base_version = be32_to_cpu(*(u32 *)(auxblk)); - - DRX_ATTR_MCRECORD(demod).aux_type = auxtype; - DRX_ATTR_MCRECORD(demod).mc_dev_type = mc_dev_type; - DRX_ATTR_MCRECORD(demod).mc_version = mc_version; - DRX_ATTR_MCRECORD(demod).mc_base_version = mc_base_version; - - pr_info("Firmware dev %x, ver %x, base ver %x\n", - mc_dev_type, mc_version, mc_base_version); - - } - } else if (count + block_hdr.size * sizeof(u16) > size) - goto eof; - - count += block_hdr.size * sizeof(u16); - } - return 0; -eof: - pr_err("Firmware is truncated at pos %u/%u\n", count, size); - return -EINVAL; -} - -/** - * drx_ctrl_u_code - Handle microcode upload or verify. - * @dev_addr: Address of device. - * @mc_info: Pointer to information about microcode data. - * @action: Either UCODE_UPLOAD or UCODE_VERIFY - * - * This function returns: - * 0: - * - In case of UCODE_UPLOAD: code is successfully uploaded. - * - In case of UCODE_VERIFY: image on device is equal to - * image provided to this control function. - * -EIO: - * - In case of UCODE_UPLOAD: I2C error. - * - In case of UCODE_VERIFY: I2C error or image on device - * is not equal to image provided to this control function. - * -EINVAL: - * - Invalid arguments. - * - Provided image is corrupt - */ -static int drx_ctrl_u_code(struct drx_demod_instance *demod, - struct drxu_code_info *mc_info, - enum drxu_code_action action) -{ - struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr; - int rc; - u16 i = 0; - u16 mc_nr_of_blks = 0; - u16 mc_magic_word = 0; - const u8 *mc_data_init = NULL; - u8 *mc_data = NULL; - unsigned size; - char *mc_file = mc_info->mc_file; - - /* Check arguments */ - if (!mc_info || !mc_file) - return -EINVAL; - - if (!demod->firmware) { - const struct firmware *fw = NULL; - - rc = request_firmware(&fw, mc_file, demod->i2c->dev.parent); - if (rc < 0) { - pr_err("Couldn't read firmware %s\n", mc_file); - return -ENOENT; - } - demod->firmware = fw; - - if (demod->firmware->size < 2 * sizeof(u16)) { - rc = -EINVAL; - pr_err("Firmware is too short!\n"); - goto release; - } - - pr_info("Firmware %s, size %zu\n", - mc_file, demod->firmware->size); - } - - mc_data_init = demod->firmware->data; - size = demod->firmware->size; - - mc_data = (void *)mc_data_init; - /* Check data */ - mc_magic_word = be16_to_cpu(*(u32 *)(mc_data)); - mc_data += sizeof(u16); - mc_nr_of_blks = be16_to_cpu(*(u32 *)(mc_data)); - mc_data += sizeof(u16); - - if ((mc_magic_word != DRX_UCODE_MAGIC_WORD) || (mc_nr_of_blks == 0)) { - rc = -EINVAL; - pr_err("Firmware magic word doesn't match\n"); - goto release; - } - - if (action == UCODE_UPLOAD) { - rc = drx_check_firmware(demod, (u8 *)mc_data_init, size); - if (rc) - goto release; - - /* After scanning, validate the microcode. - It is also valid if no validation control exists. - */ - rc = drx_ctrl(demod, DRX_CTRL_VALIDATE_UCODE, NULL); - if (rc != 0 && rc != -ENOTSUPP) { - pr_err("Validate ucode not supported\n"); - return rc; - } - pr_info("Uploading firmware %s\n", mc_file); - } else if (action == UCODE_VERIFY) { - pr_info("Verifying if firmware upload was ok.\n"); - } - - /* Process microcode blocks */ - for (i = 0; i < mc_nr_of_blks; i++) { - struct drxu_code_block_hdr block_hdr; - u16 mc_block_nr_bytes = 0; - - /* Process block header */ - block_hdr.addr = be32_to_cpu(*(u32 *)(mc_data)); - mc_data += sizeof(u32); - block_hdr.size = be16_to_cpu(*(u32 *)(mc_data)); - mc_data += sizeof(u16); - block_hdr.flags = be16_to_cpu(*(u32 *)(mc_data)); - mc_data += sizeof(u16); - block_hdr.CRC = be16_to_cpu(*(u32 *)(mc_data)); - mc_data += sizeof(u16); - - pr_debug("%u: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n", - (unsigned)(mc_data - mc_data_init), block_hdr.addr, - block_hdr.size, block_hdr.flags, block_hdr.CRC); - - /* Check block header on: - - data larger than 64Kb - - if CRC enabled check CRC - */ - if ((block_hdr.size > 0x7FFF) || - (((block_hdr.flags & DRX_UCODE_CRC_FLAG) != 0) && - (block_hdr.CRC != drx_u_code_compute_crc(mc_data, block_hdr.size))) - ) { - /* Wrong data ! */ - rc = -EINVAL; - pr_err("firmware CRC is wrong\n"); - goto release; - } - - if (!block_hdr.size) - continue; - - mc_block_nr_bytes = block_hdr.size * ((u16) sizeof(u16)); - - /* Perform the desired action */ - switch (action) { - case UCODE_UPLOAD: /* Upload microcode */ - if (demod->my_access_funct->write_block_func(dev_addr, - block_hdr.addr, - mc_block_nr_bytes, - mc_data, 0x0000)) { - rc = -EIO; - pr_err("error writing firmware at pos %u\n", - (unsigned)(mc_data - mc_data_init)); - goto release; - } - break; - case UCODE_VERIFY: { /* Verify uploaded microcode */ - int result = 0; - u8 mc_data_buffer[DRX_UCODE_MAX_BUF_SIZE]; - u32 bytes_to_comp = 0; - u32 bytes_left = mc_block_nr_bytes; - u32 curr_addr = block_hdr.addr; - u8 *curr_ptr = mc_data; - - while (bytes_left != 0) { - if (bytes_left > DRX_UCODE_MAX_BUF_SIZE) - bytes_to_comp = DRX_UCODE_MAX_BUF_SIZE; - else - bytes_to_comp = bytes_left; - - if (demod->my_access_funct-> - read_block_func(dev_addr, - curr_addr, - (u16)bytes_to_comp, - (u8 *)mc_data_buffer, - 0x0000)) { - pr_err("error reading firmware at pos %u\n", - (unsigned)(mc_data - mc_data_init)); - return -EIO; - } - - result =drxbsp_hst_memcmp(curr_ptr, - mc_data_buffer, - bytes_to_comp); - - if (result) { - pr_err("error verifying firmware at pos %u\n", - (unsigned)(mc_data - mc_data_init)); - return -EIO; - } - - curr_addr += ((dr_xaddr_t)(bytes_to_comp / 2)); - curr_ptr =&(curr_ptr[bytes_to_comp]); - bytes_left -=((u32) bytes_to_comp); - } - break; - } - default: - return -EINVAL; - break; - - } - mc_data += mc_block_nr_bytes; - } - - return 0; - -release: - release_firmware(demod->firmware); - demod->firmware = NULL; - - return rc; -} - -/*============================================================================*/ - -/** - * drx_ctrl_version - Build list of version information. - * @demod: A pointer to a demodulator instance. - * @version_list: Pointer to linked list of versions. - * - * This function returns: - * 0: Version information stored in version_list - * -EINVAL: Invalid arguments. - */ -static int drx_ctrl_version(struct drx_demod_instance *demod, - struct drx_version_list **version_list) -{ - static char drx_driver_core_module_name[] = "Core driver"; - static char drx_driver_core_version_text[] = - DRX_VERSIONSTRING(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); - - static struct drx_version drx_driver_core_version; - static struct drx_version_list drx_driver_core_version_list; - - struct drx_version_list *demod_version_list = NULL; - int return_status = -EIO; - - /* Check arguments */ - if (version_list == NULL) - return -EINVAL; - - /* Get version info list from demod */ - return_status = (*(demod->my_demod_funct->ctrl_func)) (demod, - DRX_CTRL_VERSION, - (void *) - &demod_version_list); - - /* Always fill in the information of the driver SW . */ - drx_driver_core_version.module_type = DRX_MODULE_DRIVERCORE; - drx_driver_core_version.module_name = drx_driver_core_module_name; - drx_driver_core_version.v_major = VERSION_MAJOR; - drx_driver_core_version.v_minor = VERSION_MINOR; - drx_driver_core_version.v_patch = VERSION_PATCH; - drx_driver_core_version.v_string = drx_driver_core_version_text; - - drx_driver_core_version_list.version = &drx_driver_core_version; - drx_driver_core_version_list.next = (struct drx_version_list *) (NULL); - - if ((return_status == 0) && (demod_version_list != NULL)) { - /* Append versioninfo from driver to versioninfo from demod */ - /* Return version info in "bottom-up" order. This way, multiple - devices can be handled without using malloc. */ - struct drx_version_list *current_list_element = demod_version_list; - while (current_list_element->next != NULL) - current_list_element = current_list_element->next; - current_list_element->next = &drx_driver_core_version_list; - - *version_list = demod_version_list; - } else { - /* Just return versioninfo from driver */ - *version_list = &drx_driver_core_version_list; - } - - return 0; -} - -/* - * Exported functions - */ - -/** - * drx_open - Open a demodulator instance. - * @demod: A pointer to a demodulator instance. - * - * This function returns: - * 0: Opened demod instance with succes. - * -EIO: Driver not initialized or unable to initialize - * demod. - * -EINVAL: Demod instance has invalid content. - * - */ - -int drx_open(struct drx_demod_instance *demod) -{ - int status = 0; - - if ((demod == NULL) || - (demod->my_demod_funct == NULL) || - (demod->my_common_attr == NULL) || - (demod->my_ext_attr == NULL) || - (demod->my_i2c_dev_addr == NULL) || - (demod->my_common_attr->is_opened)) { - return -EINVAL; - } - - status = (*(demod->my_demod_funct->open_func)) (demod); - - if (status == 0) - demod->my_common_attr->is_opened = true; - - return status; -} - -/*============================================================================*/ - -/** - * drx_close - Close device - * @demod: A pointer to a demodulator instance. - * - * Free resources occupied by device instance. - * Put device into sleep mode. - * - * This function returns: - * 0: Closed demod instance with succes. - * -EIO: Driver not initialized or error during close - * demod. - * -EINVAL: Demod instance has invalid content. - */ -int drx_close(struct drx_demod_instance *demod) -{ - int status = 0; - - if ((demod == NULL) || - (demod->my_demod_funct == NULL) || - (demod->my_common_attr == NULL) || - (demod->my_ext_attr == NULL) || - (demod->my_i2c_dev_addr == NULL) || - (!demod->my_common_attr->is_opened)) { - return -EINVAL; - } - - status = (*(demod->my_demod_funct->close_func)) (demod); - - DRX_ATTR_ISOPENED(demod) = false; - - return status; -} -/** - * drx_ctrl - Control the device. - * @demod: A pointer to a demodulator instance. - * @ctrl: Reference to desired control function. - * @ctrl_data: Pointer to data structure for control function. - * - * Data needed or returned by the control function is stored in ctrl_data. - * - * This function returns: - * 0: Control function completed successfully. - * -EIO: Driver not initialized or error during control demod. - * -EINVAL: Demod instance or ctrl_data has invalid content. - * -ENOTSUPP: Specified control function is not available. - */ - -int drx_ctrl(struct drx_demod_instance *demod, u32 ctrl, void *ctrl_data) -{ - int status = -EIO; - - if ((demod == NULL) || - (demod->my_demod_funct == NULL) || - (demod->my_common_attr == NULL) || - (demod->my_ext_attr == NULL) || (demod->my_i2c_dev_addr == NULL) - ) { - return -EINVAL; - } - - if (((!demod->my_common_attr->is_opened) && - (ctrl != DRX_CTRL_PROBE_DEVICE) && (ctrl != DRX_CTRL_VERSION)) - ) { - return -EINVAL; - } - - if ((DRX_ISPOWERDOWNMODE(demod->my_common_attr->current_power_mode) && - (ctrl != DRX_CTRL_POWER_MODE) && - (ctrl != DRX_CTRL_PROBE_DEVICE) && - (ctrl != DRX_CTRL_NOP) && (ctrl != DRX_CTRL_VERSION) - ) - ) { - return -ENOTSUPP; - } - - /* Fixed control functions */ - switch (ctrl) { - /*======================================================================*/ - case DRX_CTRL_NOP: - /* No operation */ - return 0; - break; - - /*======================================================================*/ - case DRX_CTRL_VERSION: - return drx_ctrl_version(demod, (struct drx_version_list **)ctrl_data); - break; - - /*======================================================================*/ - default: - /* Do nothing */ - break; - } - - /* Virtual functions */ - /* First try calling function from derived class */ - status = (*(demod->my_demod_funct->ctrl_func)) (demod, ctrl, ctrl_data); - if (status == -ENOTSUPP) { - /* Now try calling a the base class function */ - switch (ctrl) { - /*===================================================================*/ - case DRX_CTRL_LOAD_UCODE: - return drx_ctrl_u_code(demod, - (struct drxu_code_info *)ctrl_data, - UCODE_UPLOAD); - break; - - /*===================================================================*/ - case DRX_CTRL_VERIFY_UCODE: - { - return drx_ctrl_u_code(demod, - (struct drxu_code_info *)ctrl_data, - UCODE_VERIFY); - } - break; - - /*===================================================================*/ - default: - pr_err("control %d not supported\n", ctrl); - return -ENOTSUPP; - } - } else { - return status; - } - - return 0; -} diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index b1ad26b9778a..a06c45d92955 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -39,6 +39,7 @@ INCLUDE FILES #include "drxj.h" #include "drxj_map.h" +#include "drx_driver.h" /*============================================================================*/ /*=== DEFINES ================================================================*/ @@ -309,6 +310,40 @@ DEFINES #define DRX_UIO_MODE_FIRMWARE_SMA DRX_UIO_MODE_FIRMWARE0 #define DRX_UIO_MODE_FIRMWARE_SAW DRX_UIO_MODE_FIRMWARE1 +/* + * MICROCODE RELATED DEFINES + */ + +/* Magic word for checking correct Endianess of microcode data */ +#define DRX_UCODE_MAGIC_WORD ((((u16)'H')<<8)+((u16)'L')) + +/* CRC flag in ucode header, flags field. */ +#define DRX_UCODE_CRC_FLAG (0x0001) + +/* + * Maximum size of buffer used to verify the microcode. + * Must be an even number + */ +#define DRX_UCODE_MAX_BUF_SIZE (DRXDAP_MAX_RCHUNKSIZE) + +#if DRX_UCODE_MAX_BUF_SIZE & 1 +#error DRX_UCODE_MAX_BUF_SIZE must be an even number +#endif + +/* + * Power mode macros + */ + +#define DRX_ISPOWERDOWNMODE(mode) ((mode == DRX_POWER_MODE_9) || \ + (mode == DRX_POWER_MODE_10) || \ + (mode == DRX_POWER_MODE_11) || \ + (mode == DRX_POWER_MODE_12) || \ + (mode == DRX_POWER_MODE_13) || \ + (mode == DRX_POWER_MODE_14) || \ + (mode == DRX_POWER_MODE_15) || \ + (mode == DRX_POWER_MODE_16) || \ + (mode == DRX_POWER_DOWN)) + #ifdef DRXJ_SPLIT_UCODE_UPLOAD /*============================================================================*/ /*=== MICROCODE RELATED DEFINES ==============================================*/ @@ -1050,20 +1085,25 @@ struct drxj_hi_cmd { u16 param6; }; -#ifdef DRXJ_SPLIT_UCODE_UPLOAD /*============================================================================*/ /*=== MICROCODE RELATED STRUCTURES ===========================================*/ /*============================================================================*/ +/** + * struct drxu_code_block_hdr - Structure of the microcode block headers + * + * @addr: Destination address of the data in this block + * @size: Size of the block data following this header counted in + * 16 bits words + * @CRC: CRC value of the data block, only valid if CRC flag is + * set. + */ struct drxu_code_block_hdr { u32 addr; u16 size; - u16 flags; /* bit[15..2]=reserved, - bit[1]= compression on/off - bit[0]= CRC on/off */ + u16 flags; u16 CRC; }; -#endif /* DRXJ_SPLIT_UCODE_UPLOAD */ /*----------------------------------------------------------------------------- FUNCTIONS @@ -20607,3 +20647,550 @@ drxj_ctrl(struct drx_demod_instance *demod, u32 ctrl, void *ctrl_data) } return 0; } + +/* + * Microcode related functions + */ + +/** + * drx_u_code_compute_crc - Compute CRC of block of microcode data. + * @block_data: Pointer to microcode data. + * @nr_words: Size of microcode block (number of 16 bits words). + * + * returns The computed CRC residue. + */ +static u16 drx_u_code_compute_crc(u8 *block_data, u16 nr_words) +{ + u16 i = 0; + u16 j = 0; + u32 crc_word = 0; + u32 carry = 0; + + while (i < nr_words) { + crc_word |= (u32)be16_to_cpu(*(u32 *)(block_data)); + for (j = 0; j < 16; j++) { + crc_word <<= 1; + if (carry != 0) + crc_word ^= 0x80050000UL; + carry = crc_word & 0x80000000UL; + } + i++; + block_data += (sizeof(u16)); + } + return (u16)(crc_word >> 16); +} + +/** + * drx_check_firmware - checks if the loaded firmware is valid + * + * @demod: demod structure + * @mc_data: pointer to the start of the firmware + * @size: firmware size + */ +static int drx_check_firmware(struct drx_demod_instance *demod, u8 *mc_data, + unsigned size) +{ + struct drxu_code_block_hdr block_hdr; + int i; + unsigned count = 2 * sizeof(u16); + u32 mc_dev_type, mc_version, mc_base_version; + u16 mc_nr_of_blks = be16_to_cpu(*(u32 *)(mc_data + sizeof(u16))); + + /* + * Scan microcode blocks first for version info + * and firmware check + */ + + /* Clear version block */ + DRX_ATTR_MCRECORD(demod).aux_type = 0; + DRX_ATTR_MCRECORD(demod).mc_dev_type = 0; + DRX_ATTR_MCRECORD(demod).mc_version = 0; + DRX_ATTR_MCRECORD(demod).mc_base_version = 0; + + for (i = 0; i < mc_nr_of_blks; i++) { + if (count + 3 * sizeof(u16) + sizeof(u32) > size) + goto eof; + + /* Process block header */ + block_hdr.addr = be32_to_cpu(*(u32 *)(mc_data + count)); + count += sizeof(u32); + block_hdr.size = be16_to_cpu(*(u32 *)(mc_data + count)); + count += sizeof(u16); + block_hdr.flags = be16_to_cpu(*(u32 *)(mc_data + count)); + count += sizeof(u16); + block_hdr.CRC = be16_to_cpu(*(u32 *)(mc_data + count)); + count += sizeof(u16); + + pr_debug("%u: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n", + count, block_hdr.addr, block_hdr.size, block_hdr.flags, + block_hdr.CRC); + + if (block_hdr.flags & 0x8) { + u8 *auxblk = ((void *)mc_data) + block_hdr.addr; + u16 auxtype; + + if (block_hdr.addr + sizeof(u16) > size) + goto eof; + + auxtype = be16_to_cpu(*(u32 *)(auxblk)); + + /* Aux block. Check type */ + if (DRX_ISMCVERTYPE(auxtype)) { + if (block_hdr.addr + 2 * sizeof(u16) + 2 * sizeof (u32) > size) + goto eof; + + auxblk += sizeof(u16); + mc_dev_type = be32_to_cpu(*(u32 *)(auxblk)); + auxblk += sizeof(u32); + mc_version = be32_to_cpu(*(u32 *)(auxblk)); + auxblk += sizeof(u32); + mc_base_version = be32_to_cpu(*(u32 *)(auxblk)); + + DRX_ATTR_MCRECORD(demod).aux_type = auxtype; + DRX_ATTR_MCRECORD(demod).mc_dev_type = mc_dev_type; + DRX_ATTR_MCRECORD(demod).mc_version = mc_version; + DRX_ATTR_MCRECORD(demod).mc_base_version = mc_base_version; + + pr_info("Firmware dev %x, ver %x, base ver %x\n", + mc_dev_type, mc_version, mc_base_version); + + } + } else if (count + block_hdr.size * sizeof(u16) > size) + goto eof; + + count += block_hdr.size * sizeof(u16); + } + return 0; +eof: + pr_err("Firmware is truncated at pos %u/%u\n", count, size); + return -EINVAL; +} + +/** + * drx_ctrl_u_code - Handle microcode upload or verify. + * @dev_addr: Address of device. + * @mc_info: Pointer to information about microcode data. + * @action: Either UCODE_UPLOAD or UCODE_VERIFY + * + * This function returns: + * 0: + * - In case of UCODE_UPLOAD: code is successfully uploaded. + * - In case of UCODE_VERIFY: image on device is equal to + * image provided to this control function. + * -EIO: + * - In case of UCODE_UPLOAD: I2C error. + * - In case of UCODE_VERIFY: I2C error or image on device + * is not equal to image provided to this control function. + * -EINVAL: + * - Invalid arguments. + * - Provided image is corrupt + */ +static int drx_ctrl_u_code(struct drx_demod_instance *demod, + struct drxu_code_info *mc_info, + enum drxu_code_action action) +{ + struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr; + int rc; + u16 i = 0; + u16 mc_nr_of_blks = 0; + u16 mc_magic_word = 0; + const u8 *mc_data_init = NULL; + u8 *mc_data = NULL; + unsigned size; + char *mc_file = mc_info->mc_file; + + /* Check arguments */ + if (!mc_info || !mc_file) + return -EINVAL; + + if (!demod->firmware) { + const struct firmware *fw = NULL; + + rc = request_firmware(&fw, mc_file, demod->i2c->dev.parent); + if (rc < 0) { + pr_err("Couldn't read firmware %s\n", mc_file); + return -ENOENT; + } + demod->firmware = fw; + + if (demod->firmware->size < 2 * sizeof(u16)) { + rc = -EINVAL; + pr_err("Firmware is too short!\n"); + goto release; + } + + pr_info("Firmware %s, size %zu\n", + mc_file, demod->firmware->size); + } + + mc_data_init = demod->firmware->data; + size = demod->firmware->size; + + mc_data = (void *)mc_data_init; + /* Check data */ + mc_magic_word = be16_to_cpu(*(u32 *)(mc_data)); + mc_data += sizeof(u16); + mc_nr_of_blks = be16_to_cpu(*(u32 *)(mc_data)); + mc_data += sizeof(u16); + + if ((mc_magic_word != DRX_UCODE_MAGIC_WORD) || (mc_nr_of_blks == 0)) { + rc = -EINVAL; + pr_err("Firmware magic word doesn't match\n"); + goto release; + } + + if (action == UCODE_UPLOAD) { + rc = drx_check_firmware(demod, (u8 *)mc_data_init, size); + if (rc) + goto release; + + /* After scanning, validate the microcode. + It is also valid if no validation control exists. + */ + rc = drx_ctrl(demod, DRX_CTRL_VALIDATE_UCODE, NULL); + if (rc != 0 && rc != -ENOTSUPP) { + pr_err("Validate ucode not supported\n"); + return rc; + } + pr_info("Uploading firmware %s\n", mc_file); + } else if (action == UCODE_VERIFY) { + pr_info("Verifying if firmware upload was ok.\n"); + } + + /* Process microcode blocks */ + for (i = 0; i < mc_nr_of_blks; i++) { + struct drxu_code_block_hdr block_hdr; + u16 mc_block_nr_bytes = 0; + + /* Process block header */ + block_hdr.addr = be32_to_cpu(*(u32 *)(mc_data)); + mc_data += sizeof(u32); + block_hdr.size = be16_to_cpu(*(u32 *)(mc_data)); + mc_data += sizeof(u16); + block_hdr.flags = be16_to_cpu(*(u32 *)(mc_data)); + mc_data += sizeof(u16); + block_hdr.CRC = be16_to_cpu(*(u32 *)(mc_data)); + mc_data += sizeof(u16); + + pr_debug("%u: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n", + (unsigned)(mc_data - mc_data_init), block_hdr.addr, + block_hdr.size, block_hdr.flags, block_hdr.CRC); + + /* Check block header on: + - data larger than 64Kb + - if CRC enabled check CRC + */ + if ((block_hdr.size > 0x7FFF) || + (((block_hdr.flags & DRX_UCODE_CRC_FLAG) != 0) && + (block_hdr.CRC != drx_u_code_compute_crc(mc_data, block_hdr.size))) + ) { + /* Wrong data ! */ + rc = -EINVAL; + pr_err("firmware CRC is wrong\n"); + goto release; + } + + if (!block_hdr.size) + continue; + + mc_block_nr_bytes = block_hdr.size * ((u16) sizeof(u16)); + + /* Perform the desired action */ + switch (action) { + case UCODE_UPLOAD: /* Upload microcode */ + if (demod->my_access_funct->write_block_func(dev_addr, + block_hdr.addr, + mc_block_nr_bytes, + mc_data, 0x0000)) { + rc = -EIO; + pr_err("error writing firmware at pos %u\n", + (unsigned)(mc_data - mc_data_init)); + goto release; + } + break; + case UCODE_VERIFY: { /* Verify uploaded microcode */ + int result = 0; + u8 mc_data_buffer[DRX_UCODE_MAX_BUF_SIZE]; + u32 bytes_to_comp = 0; + u32 bytes_left = mc_block_nr_bytes; + u32 curr_addr = block_hdr.addr; + u8 *curr_ptr = mc_data; + + while (bytes_left != 0) { + if (bytes_left > DRX_UCODE_MAX_BUF_SIZE) + bytes_to_comp = DRX_UCODE_MAX_BUF_SIZE; + else + bytes_to_comp = bytes_left; + + if (demod->my_access_funct-> + read_block_func(dev_addr, + curr_addr, + (u16)bytes_to_comp, + (u8 *)mc_data_buffer, + 0x0000)) { + pr_err("error reading firmware at pos %u\n", + (unsigned)(mc_data - mc_data_init)); + return -EIO; + } + + result =drxbsp_hst_memcmp(curr_ptr, + mc_data_buffer, + bytes_to_comp); + + if (result) { + pr_err("error verifying firmware at pos %u\n", + (unsigned)(mc_data - mc_data_init)); + return -EIO; + } + + curr_addr += ((dr_xaddr_t)(bytes_to_comp / 2)); + curr_ptr =&(curr_ptr[bytes_to_comp]); + bytes_left -=((u32) bytes_to_comp); + } + break; + } + default: + return -EINVAL; + break; + + } + mc_data += mc_block_nr_bytes; + } + + return 0; + +release: + release_firmware(demod->firmware); + demod->firmware = NULL; + + return rc; +} + +/*============================================================================*/ + +/** + * drx_ctrl_version - Build list of version information. + * @demod: A pointer to a demodulator instance. + * @version_list: Pointer to linked list of versions. + * + * This function returns: + * 0: Version information stored in version_list + * -EINVAL: Invalid arguments. + */ +static int drx_ctrl_version(struct drx_demod_instance *demod, + struct drx_version_list **version_list) +{ + static char drx_driver_core_module_name[] = "Core driver"; + static char drx_driver_core_version_text[] = + DRX_VERSIONSTRING(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); + + static struct drx_version drx_driver_core_version; + static struct drx_version_list drx_driver_core_version_list; + + struct drx_version_list *demod_version_list = NULL; + int return_status = -EIO; + + /* Check arguments */ + if (version_list == NULL) + return -EINVAL; + + /* Get version info list from demod */ + return_status = (*(demod->my_demod_funct->ctrl_func)) (demod, + DRX_CTRL_VERSION, + (void *) + &demod_version_list); + + /* Always fill in the information of the driver SW . */ + drx_driver_core_version.module_type = DRX_MODULE_DRIVERCORE; + drx_driver_core_version.module_name = drx_driver_core_module_name; + drx_driver_core_version.v_major = VERSION_MAJOR; + drx_driver_core_version.v_minor = VERSION_MINOR; + drx_driver_core_version.v_patch = VERSION_PATCH; + drx_driver_core_version.v_string = drx_driver_core_version_text; + + drx_driver_core_version_list.version = &drx_driver_core_version; + drx_driver_core_version_list.next = (struct drx_version_list *) (NULL); + + if ((return_status == 0) && (demod_version_list != NULL)) { + /* Append versioninfo from driver to versioninfo from demod */ + /* Return version info in "bottom-up" order. This way, multiple + devices can be handled without using malloc. */ + struct drx_version_list *current_list_element = demod_version_list; + while (current_list_element->next != NULL) + current_list_element = current_list_element->next; + current_list_element->next = &drx_driver_core_version_list; + + *version_list = demod_version_list; + } else { + /* Just return versioninfo from driver */ + *version_list = &drx_driver_core_version_list; + } + + return 0; +} + +/* + * Exported functions + */ + +/** + * drx_open - Open a demodulator instance. + * @demod: A pointer to a demodulator instance. + * + * This function returns: + * 0: Opened demod instance with succes. + * -EIO: Driver not initialized or unable to initialize + * demod. + * -EINVAL: Demod instance has invalid content. + * + */ + +int drx_open(struct drx_demod_instance *demod) +{ + int status = 0; + + if ((demod == NULL) || + (demod->my_demod_funct == NULL) || + (demod->my_common_attr == NULL) || + (demod->my_ext_attr == NULL) || + (demod->my_i2c_dev_addr == NULL) || + (demod->my_common_attr->is_opened)) { + return -EINVAL; + } + + status = (*(demod->my_demod_funct->open_func)) (demod); + + if (status == 0) + demod->my_common_attr->is_opened = true; + + return status; +} + +/*============================================================================*/ + +/** + * drx_close - Close device + * @demod: A pointer to a demodulator instance. + * + * Free resources occupied by device instance. + * Put device into sleep mode. + * + * This function returns: + * 0: Closed demod instance with succes. + * -EIO: Driver not initialized or error during close + * demod. + * -EINVAL: Demod instance has invalid content. + */ +int drx_close(struct drx_demod_instance *demod) +{ + int status = 0; + + if ((demod == NULL) || + (demod->my_demod_funct == NULL) || + (demod->my_common_attr == NULL) || + (demod->my_ext_attr == NULL) || + (demod->my_i2c_dev_addr == NULL) || + (!demod->my_common_attr->is_opened)) { + return -EINVAL; + } + + status = (*(demod->my_demod_funct->close_func)) (demod); + + DRX_ATTR_ISOPENED(demod) = false; + + return status; +} +/** + * drx_ctrl - Control the device. + * @demod: A pointer to a demodulator instance. + * @ctrl: Reference to desired control function. + * @ctrl_data: Pointer to data structure for control function. + * + * Data needed or returned by the control function is stored in ctrl_data. + * + * This function returns: + * 0: Control function completed successfully. + * -EIO: Driver not initialized or error during control demod. + * -EINVAL: Demod instance or ctrl_data has invalid content. + * -ENOTSUPP: Specified control function is not available. + */ + +int drx_ctrl(struct drx_demod_instance *demod, u32 ctrl, void *ctrl_data) +{ + int status = -EIO; + + if ((demod == NULL) || + (demod->my_demod_funct == NULL) || + (demod->my_common_attr == NULL) || + (demod->my_ext_attr == NULL) || (demod->my_i2c_dev_addr == NULL) + ) { + return -EINVAL; + } + + if (((!demod->my_common_attr->is_opened) && + (ctrl != DRX_CTRL_PROBE_DEVICE) && (ctrl != DRX_CTRL_VERSION)) + ) { + return -EINVAL; + } + + if ((DRX_ISPOWERDOWNMODE(demod->my_common_attr->current_power_mode) && + (ctrl != DRX_CTRL_POWER_MODE) && + (ctrl != DRX_CTRL_PROBE_DEVICE) && + (ctrl != DRX_CTRL_NOP) && (ctrl != DRX_CTRL_VERSION) + ) + ) { + return -ENOTSUPP; + } + + /* Fixed control functions */ + switch (ctrl) { + /*======================================================================*/ + case DRX_CTRL_NOP: + /* No operation */ + return 0; + break; + + /*======================================================================*/ + case DRX_CTRL_VERSION: + return drx_ctrl_version(demod, (struct drx_version_list **)ctrl_data); + break; + + /*======================================================================*/ + default: + /* Do nothing */ + break; + } + + /* Virtual functions */ + /* First try calling function from derived class */ + status = (*(demod->my_demod_funct->ctrl_func)) (demod, ctrl, ctrl_data); + if (status == -ENOTSUPP) { + /* Now try calling a the base class function */ + switch (ctrl) { + /*===================================================================*/ + case DRX_CTRL_LOAD_UCODE: + return drx_ctrl_u_code(demod, + (struct drxu_code_info *)ctrl_data, + UCODE_UPLOAD); + break; + + /*===================================================================*/ + case DRX_CTRL_VERIFY_UCODE: + { + return drx_ctrl_u_code(demod, + (struct drxu_code_info *)ctrl_data, + UCODE_VERIFY); + } + break; + + /*===================================================================*/ + default: + pr_err("control %d not supported\n", ctrl); + return -ENOTSUPP; + } + } else { + return status; + } + + return 0; +}
\ No newline at end of file |