| /* |
| * Synaptics TCM touchscreen driver |
| * |
| * Copyright (C) 2017-2019 Synaptics Incorporated. All rights reserved. |
| * |
| * Copyright (C) 2017-2019 Scott Lin <scott.lin@tw.synaptics.com> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS |
| * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, |
| * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. |
| * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION |
| * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED |
| * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES |
| * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' |
| * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. |
| * DOLLARS. |
| */ |
| |
| #ifndef _SYNAPTICS_TCM_CORE_H_ |
| #define _SYNAPTICS_TCM_CORE_H_ |
| |
| #include <linux/version.h> |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/input.h> |
| #include <linux/delay.h> |
| #include <linux/platform_device.h> |
| #include <linux/slab.h> |
| #include <linux/input/synaptics_tcm.h> |
| #ifdef CONFIG_DRM |
| #include <linux/msm_drm_notify.h> |
| #elif CONFIG_FB |
| #include <linux/fb.h> |
| #include <linux/notifier.h> |
| #endif |
| #include <uapi/linux/sched/types.h> |
| |
| #define SYNAPTICS_TCM_ID_PRODUCT (1 << 0) |
| #define SYNAPTICS_TCM_ID_VERSION 0x0101 |
| #define SYNAPTICS_TCM_ID_SUBVERSION 0 |
| |
| #define PLATFORM_DRIVER_NAME "synaptics_tcm" |
| |
| #define TOUCH_INPUT_NAME "synaptics_tcm_touch" |
| #define TOUCH_INPUT_PHYS_PATH "synaptics_tcm/touch_input" |
| |
| /* #define WAKEUP_GESTURE */ |
| |
| #define RD_CHUNK_SIZE 0 /* read length limit in bytes, 0 = unlimited */ |
| #define WR_CHUNK_SIZE 0 /* write length limit in bytes, 0 = unlimited */ |
| |
| #define MESSAGE_HEADER_SIZE 4 |
| #define MESSAGE_MARKER 0xa5 |
| #define MESSAGE_PADDING 0x5a |
| |
| #define LOGx(func, dev, log, ...) \ |
| func(dev, "%s: " log, __func__, ##__VA_ARGS__) |
| |
| #define LOGy(func, dev, log, ...) \ |
| func(dev, "%s (line %d): " log, __func__, __LINE__, ##__VA_ARGS__) |
| |
| #define LOGD(dev, log, ...) LOGx(dev_dbg, dev, log, ##__VA_ARGS__) |
| #define LOGI(dev, log, ...) LOGx(dev_info, dev, log, ##__VA_ARGS__) |
| #define LOGN(dev, log, ...) LOGx(dev_notice, dev, log, ##__VA_ARGS__) |
| #define LOGW(dev, log, ...) LOGy(dev_warn, dev, log, ##__VA_ARGS__) |
| #define LOGE(dev, log, ...) LOGy(dev_err, dev, log, ##__VA_ARGS__) |
| |
| #define INIT_BUFFER(buffer, is_clone) \ |
| mutex_init(&buffer.buf_mutex); \ |
| buffer.clone = is_clone |
| |
| #define LOCK_BUFFER(buffer) \ |
| mutex_lock(&buffer.buf_mutex) |
| |
| #define UNLOCK_BUFFER(buffer) \ |
| mutex_unlock(&buffer.buf_mutex) |
| |
| #define RELEASE_BUFFER(buffer) \ |
| do { \ |
| if (buffer.clone == false) { \ |
| kfree(buffer.buf); \ |
| buffer.buf_size = 0; \ |
| buffer.data_length = 0; \ |
| } \ |
| } while (0) |
| |
| #define MAX(a, b) \ |
| ({__typeof__(a) _a = (a); \ |
| __typeof__(b) _b = (b); \ |
| _a > _b ? _a : _b; }) |
| |
| #define MIN(a, b) \ |
| ({__typeof__(a) _a = (a); \ |
| __typeof__(b) _b = (b); \ |
| _a < _b ? _a : _b; }) |
| |
| #define STR(x) #x |
| |
| #define CONCAT(a, b) a##b |
| |
| #define SHOW_PROTOTYPE(m_name, a_name) \ |
| static ssize_t CONCAT(m_name##_sysfs, _##a_name##_show)(struct device *dev, \ |
| struct device_attribute *attr, char *buf); \ |
| \ |
| static struct device_attribute dev_attr_##a_name = \ |
| __ATTR(a_name, 0444, \ |
| CONCAT(m_name##_sysfs, _##a_name##_show), \ |
| syna_tcm_store_error) |
| |
| #define STORE_PROTOTYPE(m_name, a_name) \ |
| static ssize_t CONCAT(m_name##_sysfs, _##a_name##_store)(struct device *dev, \ |
| struct device_attribute *attr, const char *buf, size_t count); \ |
| \ |
| static struct device_attribute dev_attr_##a_name = \ |
| __ATTR(a_name, 0220, \ |
| syna_tcm_show_error, \ |
| CONCAT(m_name##_sysfs, _##a_name##_store)) |
| |
| #define SHOW_STORE_PROTOTYPE(m_name, a_name) \ |
| static ssize_t CONCAT(m_name##_sysfs, _##a_name##_show)(struct device *dev, \ |
| struct device_attribute *attr, char *buf); \ |
| \ |
| static ssize_t CONCAT(m_name##_sysfs, _##a_name##_store)(struct device *dev, \ |
| struct device_attribute *attr, const char *buf, size_t count); \ |
| \ |
| static struct device_attribute dev_attr_##a_name = \ |
| __ATTR(a_name, 0664, \ |
| CONCAT(m_name##_sysfs, _##a_name##_show), \ |
| CONCAT(m_name##_sysfs, _##a_name##_store)) |
| |
| #define ATTRIFY(a_name) (&dev_attr_##a_name) |
| |
| enum module_type { |
| TCM_TOUCH = 0, |
| TCM_DEVICE = 1, |
| TCM_TESTING = 2, |
| TCM_REFLASH = 3, |
| TCM_RECOVERY = 4, |
| TCM_ZEROFLASH = 5, |
| TCM_DIAGNOSTICS = 6, |
| TCM_LAST, |
| }; |
| |
| enum boot_mode { |
| MODE_APPLICATION = 0x01, |
| MODE_HOST_DOWNLOAD = 0x02, |
| MODE_BOOTLOADER = 0x0b, |
| MODE_TDDI_BOOTLOADER = 0x0c, |
| MODE_PRODUCTION_TEST = 0x0e, |
| }; |
| |
| enum boot_status { |
| BOOT_STATUS_OK = 0x00, |
| BOOT_STATUS_BOOTING = 0x01, |
| BOOT_STATUS_APP_BAD_DISPLAY_CRC = 0xfc, |
| BOOT_STATUS_BAD_DISPLAY_CONFIG = 0xfd, |
| BOOT_STATUS_BAD_APP_FIRMWARE = 0xfe, |
| BOOT_STATUS_WARM_BOOT = 0xff, |
| }; |
| |
| enum app_status { |
| APP_STATUS_OK = 0x00, |
| APP_STATUS_BOOTING = 0x01, |
| APP_STATUS_UPDATING = 0x02, |
| APP_STATUS_BAD_APP_CONFIG = 0xff, |
| }; |
| |
| enum firmware_mode { |
| FW_MODE_BOOTLOADER = 0, |
| FW_MODE_APPLICATION = 1, |
| FW_MODE_PRODUCTION_TEST = 2, |
| }; |
| |
| enum dynamic_config_id { |
| DC_UNKNOWN = 0x00, |
| DC_NO_DOZE, |
| DC_DISABLE_NOISE_MITIGATION, |
| DC_INHIBIT_FREQUENCY_SHIFT, |
| DC_REQUESTED_FREQUENCY, |
| DC_DISABLE_HSYNC, |
| DC_REZERO_ON_EXIT_DEEP_SLEEP, |
| DC_CHARGER_CONNECTED, |
| DC_NO_BASELINE_RELAXATION, |
| DC_IN_WAKEUP_GESTURE_MODE, |
| DC_STIMULUS_FINGERS, |
| DC_GRIP_SUPPRESSION_ENABLED, |
| DC_ENABLE_THICK_GLOVE, |
| DC_ENABLE_GLOVE, |
| }; |
| |
| enum command { |
| CMD_NONE = 0x00, |
| CMD_CONTINUE_WRITE = 0x01, |
| CMD_IDENTIFY = 0x02, |
| CMD_RESET = 0x04, |
| CMD_ENABLE_REPORT = 0x05, |
| CMD_DISABLE_REPORT = 0x06, |
| CMD_GET_BOOT_INFO = 0x10, |
| CMD_ERASE_FLASH = 0x11, |
| CMD_WRITE_FLASH = 0x12, |
| CMD_READ_FLASH = 0x13, |
| CMD_RUN_APPLICATION_FIRMWARE = 0x14, |
| CMD_SPI_MASTER_WRITE_THEN_READ = 0x15, |
| CMD_REBOOT_TO_ROM_BOOTLOADER = 0x16, |
| CMD_RUN_BOOTLOADER_FIRMWARE = 0x1f, |
| CMD_GET_APPLICATION_INFO = 0x20, |
| CMD_GET_STATIC_CONFIG = 0x21, |
| CMD_SET_STATIC_CONFIG = 0x22, |
| CMD_GET_DYNAMIC_CONFIG = 0x23, |
| CMD_SET_DYNAMIC_CONFIG = 0x24, |
| CMD_GET_TOUCH_REPORT_CONFIG = 0x25, |
| CMD_SET_TOUCH_REPORT_CONFIG = 0x26, |
| CMD_REZERO = 0x27, |
| CMD_COMMIT_CONFIG = 0x28, |
| CMD_DESCRIBE_DYNAMIC_CONFIG = 0x29, |
| CMD_PRODUCTION_TEST = 0x2a, |
| CMD_SET_CONFIG_ID = 0x2b, |
| CMD_ENTER_DEEP_SLEEP = 0x2c, |
| CMD_EXIT_DEEP_SLEEP = 0x2d, |
| CMD_GET_TOUCH_INFO = 0x2e, |
| CMD_GET_DATA_LOCATION = 0x2f, |
| CMD_DOWNLOAD_CONFIG = 0x30, |
| CMD_ENTER_PRODUCTION_TEST_MODE = 0x31, |
| CMD_GET_FEATURES = 0x32, |
| }; |
| |
| enum status_code { |
| STATUS_IDLE = 0x00, |
| STATUS_OK = 0x01, |
| STATUS_BUSY = 0x02, |
| STATUS_CONTINUED_READ = 0x03, |
| STATUS_NOT_EXECUTED_IN_DEEP_SLEEP = 0x0b, |
| STATUS_RECEIVE_BUFFER_OVERFLOW = 0x0c, |
| STATUS_PREVIOUS_COMMAND_PENDING = 0x0d, |
| STATUS_NOT_IMPLEMENTED = 0x0e, |
| STATUS_ERROR = 0x0f, |
| STATUS_INVALID = 0xff, |
| }; |
| |
| enum report_type { |
| REPORT_IDENTIFY = 0x10, |
| REPORT_TOUCH = 0x11, |
| REPORT_DELTA = 0x12, |
| REPORT_RAW = 0x13, |
| REPORT_STATUS = 0x1b, |
| REPORT_PRINTF = 0x82, |
| REPORT_HDL = 0xfe, |
| }; |
| |
| enum command_status { |
| CMD_IDLE = 0, |
| CMD_BUSY = 1, |
| CMD_ERROR = -1, |
| }; |
| |
| enum flash_area { |
| BOOTLOADER = 0, |
| BOOT_CONFIG, |
| APP_FIRMWARE, |
| APP_CONFIG, |
| DISP_CONFIG, |
| CUSTOM_OTP, |
| CUSTOM_LCM, |
| CUSTOM_OEM, |
| PPDT, |
| }; |
| |
| enum flash_data { |
| LCM_DATA = 1, |
| OEM_DATA, |
| PPDT_DATA, |
| }; |
| |
| enum helper_task { |
| HELP_NONE = 0, |
| HELP_RUN_APPLICATION_FIRMWARE, |
| HELP_SEND_RESET_NOTIFICATION, |
| }; |
| |
| struct syna_tcm_helper { |
| atomic_t task; |
| struct work_struct work; |
| struct workqueue_struct *workqueue; |
| }; |
| |
| struct syna_tcm_watchdog { |
| bool run; |
| unsigned char count; |
| struct delayed_work work; |
| struct workqueue_struct *workqueue; |
| }; |
| |
| struct syna_tcm_buffer { |
| bool clone; |
| unsigned char *buf; |
| unsigned int buf_size; |
| unsigned int data_length; |
| struct mutex buf_mutex; |
| }; |
| |
| struct syna_tcm_report { |
| unsigned char id; |
| struct syna_tcm_buffer buffer; |
| }; |
| |
| struct syna_tcm_identification { |
| unsigned char version; |
| unsigned char mode; |
| unsigned char part_number[16]; |
| unsigned char build_id[4]; |
| unsigned char max_write_size[2]; |
| }; |
| |
| struct syna_tcm_boot_info { |
| unsigned char version; |
| unsigned char status; |
| unsigned char asic_id[2]; |
| unsigned char write_block_size_words; |
| unsigned char erase_page_size_words[2]; |
| unsigned char max_write_payload_size[2]; |
| unsigned char last_reset_reason; |
| unsigned char pc_at_time_of_last_reset[2]; |
| unsigned char boot_config_start_block[2]; |
| unsigned char boot_config_size_blocks[2]; |
| unsigned char display_config_start_block[4]; |
| unsigned char display_config_length_blocks[2]; |
| unsigned char backup_display_config_start_block[4]; |
| unsigned char backup_display_config_length_blocks[2]; |
| unsigned char custom_otp_start_block[2]; |
| unsigned char custom_otp_length_blocks[2]; |
| }; |
| |
| struct syna_tcm_app_info { |
| unsigned char version[2]; |
| unsigned char status[2]; |
| unsigned char static_config_size[2]; |
| unsigned char dynamic_config_size[2]; |
| unsigned char app_config_start_write_block[2]; |
| unsigned char app_config_size[2]; |
| unsigned char max_touch_report_config_size[2]; |
| unsigned char max_touch_report_payload_size[2]; |
| unsigned char customer_config_id[16]; |
| unsigned char max_x[2]; |
| unsigned char max_y[2]; |
| unsigned char max_objects[2]; |
| unsigned char num_of_buttons[2]; |
| unsigned char num_of_image_rows[2]; |
| unsigned char num_of_image_cols[2]; |
| unsigned char has_hybrid_data[2]; |
| }; |
| |
| struct syna_tcm_touch_info { |
| unsigned char image_2d_scale_factor[4]; |
| unsigned char image_0d_scale_factor[4]; |
| unsigned char hybrid_x_scale_factor[4]; |
| unsigned char hybrid_y_scale_factor[4]; |
| }; |
| |
| struct syna_tcm_message_header { |
| unsigned char marker; |
| unsigned char code; |
| unsigned char length[2]; |
| }; |
| |
| struct syna_tcm_features { |
| unsigned char byte_0_reserved; |
| unsigned char byte_1_reserved; |
| unsigned char dual_firmware:1; |
| unsigned char byte_2_reserved:7; |
| } __packed; |
| |
| struct syna_tcm_hcd { |
| pid_t isr_pid; |
| atomic_t command_status; |
| atomic_t host_downloading; |
| atomic_t firmware_flashing; |
| wait_queue_head_t hdl_wq; |
| wait_queue_head_t reflash_wq; |
| int irq; |
| bool init_okay; |
| bool do_polling; |
| bool in_suspend; |
| bool irq_enabled; |
| bool host_download_mode; |
| unsigned char marker; |
| unsigned char fb_ready; |
| unsigned char command; |
| unsigned char async_report_id; |
| unsigned char status_report_code; |
| unsigned char response_code; |
| unsigned int read_length; |
| unsigned int payload_length; |
| unsigned int packrat_number; |
| unsigned int rd_chunk_size; |
| unsigned int wr_chunk_size; |
| unsigned int app_status; |
| struct platform_device *pdev; |
| struct regulator *pwr_reg; |
| struct regulator *bus_reg; |
| struct kobject *sysfs_dir; |
| struct kobject *dynamnic_config_sysfs_dir; |
| struct mutex extif_mutex; |
| struct mutex reset_mutex; |
| struct mutex irq_en_mutex; |
| struct mutex io_ctrl_mutex; |
| struct mutex rw_ctrl_mutex; |
| struct mutex command_mutex; |
| struct mutex identify_mutex; |
| struct delayed_work polling_work; |
| struct workqueue_struct *polling_workqueue; |
| struct task_struct *notifier_thread; |
| #if defined(CONFIG_DRM) || defined(CONFIG_FB) |
| struct notifier_block fb_notifier; |
| #endif |
| struct syna_tcm_buffer in; |
| struct syna_tcm_buffer out; |
| struct syna_tcm_buffer resp; |
| struct syna_tcm_buffer temp; |
| struct syna_tcm_buffer config; |
| struct syna_tcm_report report; |
| struct syna_tcm_app_info app_info; |
| struct syna_tcm_boot_info boot_info; |
| struct syna_tcm_touch_info touch_info; |
| struct syna_tcm_identification id_info; |
| struct syna_tcm_helper helper; |
| struct syna_tcm_watchdog watchdog; |
| struct syna_tcm_features features; |
| const struct syna_tcm_hw_interface *hw_if; |
| int (*reset)(struct syna_tcm_hcd *tcm_hcd, bool hw, bool update_wd); |
| int (*sleep)(struct syna_tcm_hcd *tcm_hcd, bool en); |
| int (*identify)(struct syna_tcm_hcd *tcm_hcd, bool id); |
| int (*enable_irq)(struct syna_tcm_hcd *tcm_hcd, bool en, bool ns); |
| int (*switch_mode)(struct syna_tcm_hcd *tcm_hcd, |
| enum firmware_mode mode); |
| int (*read_message)(struct syna_tcm_hcd *tcm_hcd, |
| unsigned char *in_buf, unsigned int length); |
| int (*write_message)(struct syna_tcm_hcd *tcm_hcd, |
| unsigned char command, unsigned char *payload, |
| unsigned int length, unsigned char **resp_buf, |
| unsigned int *resp_buf_size, unsigned int *resp_length, |
| unsigned char *response_code, |
| unsigned int polling_delay_ms); |
| int (*get_dynamic_config)(struct syna_tcm_hcd *tcm_hcd, |
| enum dynamic_config_id id, unsigned short *value); |
| int (*set_dynamic_config)(struct syna_tcm_hcd *tcm_hcd, |
| enum dynamic_config_id id, unsigned short value); |
| int (*get_data_location)(struct syna_tcm_hcd *tcm_hcd, |
| enum flash_area area, unsigned int *addr, |
| unsigned int *length); |
| int (*read_flash_data)(enum flash_area area, bool run_app_firmware, |
| struct syna_tcm_buffer *output); |
| void (*report_touch)(void); |
| void (*update_watchdog)(struct syna_tcm_hcd *tcm_hcd, bool en); |
| }; |
| |
| struct syna_tcm_module_cb { |
| enum module_type type; |
| int (*init)(struct syna_tcm_hcd *tcm_hcd); |
| int (*remove)(struct syna_tcm_hcd *tcm_hcd); |
| int (*syncbox)(struct syna_tcm_hcd *tcm_hcd); |
| int (*asyncbox)(struct syna_tcm_hcd *tcm_hcd); |
| int (*reset)(struct syna_tcm_hcd *tcm_hcd); |
| int (*suspend)(struct syna_tcm_hcd *tcm_hcd); |
| int (*resume)(struct syna_tcm_hcd *tcm_hcd); |
| int (*early_suspend)(struct syna_tcm_hcd *tcm_hcd); |
| }; |
| |
| struct syna_tcm_module_handler { |
| bool insert; |
| bool detach; |
| struct list_head link; |
| struct syna_tcm_module_cb *mod_cb; |
| }; |
| |
| struct syna_tcm_module_pool { |
| bool initialized; |
| bool queue_work; |
| bool reconstructing; |
| struct mutex mutex; |
| struct list_head list; |
| struct work_struct work; |
| struct workqueue_struct *workqueue; |
| struct syna_tcm_hcd *tcm_hcd; |
| }; |
| |
| struct syna_tcm_bus_io { |
| unsigned char type; |
| int (*rmi_read)(struct syna_tcm_hcd *tcm_hcd, unsigned short addr, |
| unsigned char *data, unsigned int length); |
| int (*rmi_write)(struct syna_tcm_hcd *tcm_hcd, unsigned short addr, |
| unsigned char *data, unsigned int length); |
| int (*read)(struct syna_tcm_hcd *tcm_hcd, unsigned char *data, |
| unsigned int length); |
| int (*write)(struct syna_tcm_hcd *tcm_hcd, unsigned char *data, |
| unsigned int length); |
| }; |
| |
| struct syna_tcm_hw_interface { |
| struct syna_tcm_board_data *bdata; |
| const struct syna_tcm_bus_io *bus_io; |
| }; |
| |
| int syna_tcm_bus_init(void); |
| |
| void syna_tcm_bus_exit(void); |
| |
| int syna_tcm_add_module(struct syna_tcm_module_cb *mod_cb, bool insert); |
| |
| static inline int syna_tcm_rmi_read(struct syna_tcm_hcd *tcm_hcd, |
| unsigned short addr, unsigned char *data, unsigned int length) |
| { |
| return tcm_hcd->hw_if->bus_io->rmi_read(tcm_hcd, addr, data, length); |
| } |
| |
| static inline int syna_tcm_rmi_write(struct syna_tcm_hcd *tcm_hcd, |
| unsigned short addr, unsigned char *data, unsigned int length) |
| { |
| return tcm_hcd->hw_if->bus_io->rmi_write(tcm_hcd, addr, data, length); |
| } |
| |
| static inline int syna_tcm_read(struct syna_tcm_hcd *tcm_hcd, |
| unsigned char *data, unsigned int length) |
| { |
| return tcm_hcd->hw_if->bus_io->read(tcm_hcd, data, length); |
| } |
| |
| static inline int syna_tcm_write(struct syna_tcm_hcd *tcm_hcd, |
| unsigned char *data, unsigned int length) |
| { |
| return tcm_hcd->hw_if->bus_io->write(tcm_hcd, data, length); |
| } |
| |
| static inline ssize_t syna_tcm_show_error(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| pr_err("%s: Attribute not readable\n", |
| __func__); |
| |
| return -EPERM; |
| } |
| |
| static inline ssize_t syna_tcm_store_error(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t count) |
| { |
| pr_err("%s: Attribute not writable\n", |
| __func__); |
| |
| return -EPERM; |
| } |
| |
| static inline int secure_memcpy(unsigned char *dest, unsigned int dest_size, |
| const unsigned char *src, unsigned int src_size, |
| unsigned int count) |
| { |
| if (dest == NULL || src == NULL) |
| return -EINVAL; |
| |
| if (count > dest_size || count > src_size) { |
| pr_err("%s: src_size = %d, dest_size = %d, count = %d\n", |
| __func__, src_size, dest_size, count); |
| return -EINVAL; |
| } |
| |
| memcpy((void *)dest, (const void *)src, count); |
| |
| return 0; |
| } |
| |
| static inline int syna_tcm_realloc_mem(struct syna_tcm_hcd *tcm_hcd, |
| struct syna_tcm_buffer *buffer, unsigned int size) |
| { |
| int retval; |
| unsigned char *temp; |
| |
| if (size > buffer->buf_size) { |
| temp = buffer->buf; |
| |
| buffer->buf = kmalloc(size, GFP_KERNEL); |
| if (!(buffer->buf)) { |
| dev_err(tcm_hcd->pdev->dev.parent, |
| "%s: Failed to allocate memory\n", |
| __func__); |
| kfree(temp); |
| buffer->buf_size = 0; |
| return -ENOMEM; |
| } |
| |
| retval = secure_memcpy(buffer->buf, |
| size, |
| temp, |
| buffer->buf_size, |
| buffer->buf_size); |
| if (retval < 0) { |
| dev_err(tcm_hcd->pdev->dev.parent, |
| "%s: Failed to copy data\n", |
| __func__); |
| kfree(temp); |
| kfree(buffer->buf); |
| buffer->buf_size = 0; |
| return retval; |
| } |
| |
| kfree(temp); |
| buffer->buf_size = size; |
| } |
| |
| return 0; |
| } |
| |
| static inline int syna_tcm_alloc_mem(struct syna_tcm_hcd *tcm_hcd, |
| struct syna_tcm_buffer *buffer, unsigned int size) |
| { |
| if (size > buffer->buf_size) { |
| kfree(buffer->buf); |
| buffer->buf = kmalloc(size, GFP_KERNEL); |
| if (!(buffer->buf)) { |
| dev_err(tcm_hcd->pdev->dev.parent, |
| "%s: Failed to allocate memory\n", |
| __func__); |
| dev_err(tcm_hcd->pdev->dev.parent, |
| "%s: Allocation size = %d\n", |
| __func__, size); |
| buffer->buf_size = 0; |
| buffer->data_length = 0; |
| return -ENOMEM; |
| } |
| buffer->buf_size = size; |
| } |
| |
| memset(buffer->buf, 0x00, buffer->buf_size); |
| buffer->data_length = 0; |
| |
| return 0; |
| } |
| |
| static inline unsigned int le2_to_uint(const unsigned char *src) |
| { |
| return (unsigned int)src[0] + |
| (unsigned int)src[1] * 0x100; |
| } |
| |
| static inline unsigned int le4_to_uint(const unsigned char *src) |
| { |
| return (unsigned int)src[0] + |
| (unsigned int)src[1] * 0x100 + |
| (unsigned int)src[2] * 0x10000 + |
| (unsigned int)src[3] * 0x1000000; |
| } |
| |
| static inline unsigned int ceil_div(unsigned int dividend, |
| unsigned int divisor) |
| { |
| return (dividend + divisor - 1) / divisor; |
| } |
| |
| #endif |