From cb345f2621f8a34d408dfe5be1c4b62779433674 Mon Sep 17 00:00:00 2001 From: Hoegeun Kwon Date: Wed, 17 Aug 2016 15:57:23 +0900 Subject: [PATCH] sensors: add support for the BCM 4773 sensorhub The Broadcom BCM4773 is a microcontroller that works as a sensorhub. It's placed in between sensors and AP. It's connected to the AP through SPI bus and has support for a range of sensors: - icm20610 - yas532 - bmp182 - tmg399x - mobeam The config file provides a "choice" option in order to choose between different targets, S333 and TM2(e). The BCM4773 it can handle GPS and the gps driver is the main entry point to the sensorhub driver as an SPI device. Change-Id: I07f676459cf53cbb88d75cac0459b5e374f353a5 Signed-off-by: Hoegeun Kwon --- drivers/sensors/Kconfig | 9 + drivers/sensors/Makefile | 1 + drivers/sensors/brcm/Kconfig | 87 + drivers/sensors/brcm/Makefile | 19 + drivers/sensors/brcm/bbdpl1/Makefile | 9 + drivers/sensors/brcm/bbdpl1/bbd.h | 54 + drivers/sensors/brcm/bbdpl1/bbd_common.c | 479 + drivers/sensors/brcm/bbdpl1/bbd_control.c | 283 + drivers/sensors/brcm/bbdpl1/bbd_debug.h | 72 + drivers/sensors/brcm/bbdpl1/bbd_ifc.h | 65 + drivers/sensors/brcm/bbdpl1/bbd_internal.h | 232 + drivers/sensors/brcm/bbdpl1/bbd_main.c | 348 + drivers/sensors/brcm/bbdpl1/bbd_packet.c | 144 + drivers/sensors/brcm/bbdpl1/bbd_patch.c | 78 + drivers/sensors/brcm/bbdpl1/bbd_reliable.c | 153 + drivers/sensors/brcm/bbdpl1/bbd_sensor.c | 300 + drivers/sensors/brcm/bbdpl1/bbd_sio.c | 157 + drivers/sensors/brcm/bbdpl1/bbd_ssi_spi.c | 388 + drivers/sensors/brcm/bbdpl1/bbd_sysfs.c | 324 + drivers/sensors/brcm/bbdpl1/bbd_tty.c | 463 + drivers/sensors/brcm/bbdpl1/bcm477x_debug.h | 16 + drivers/sensors/brcm/bbdpl1/bcm477x_ssi_spi.h | 41 + drivers/sensors/brcm/bbdpl1/bcm_gps_spi.c | 1719 +++ drivers/sensors/brcm/bbdpl1/bcm_gps_spi.h | 45 + drivers/sensors/brcm/bbdpl1/transport/Makefile | 6 + .../sensors/brcm/bbdpl1/transport/bbd_bridge_c.c | 328 + .../sensors/brcm/bbdpl1/transport/bbd_bridge_c.h | 138 + drivers/sensors/brcm/bbdpl1/transport/bbd_engine.c | 417 + drivers/sensors/brcm/bbdpl1/transport/bbd_engine.h | 60 + .../brcm/bbdpl1/transport/bbd_packet_layer_c.c | 124 + .../brcm/bbdpl1/transport/bbd_packet_layer_c.h | 61 + .../sensors/brcm/bbdpl1/transport/rpc_codec_c.h | 81 + .../brcm/bbdpl1/transport/transport_layer_c.c | 1692 +++ .../brcm/bbdpl1/transport/transport_layer_c.h | 259 + .../brcm/bbdpl1/transport/transport_layer_custom.h | 64 + drivers/sensors/brcm/bbdpl1/utils/Makefile | 6 + drivers/sensors/brcm/bbdpl1/utils/bbd_utils.h | 38 + drivers/sensors/brcm/bbdpl1/utils/crc8bits_c.c | 84 + drivers/sensors/brcm/bbdpl1/utils/crc8bits_c.h | 45 + drivers/sensors/brcm/bbdpl1/utils/ring_buffer_c.c | 156 + drivers/sensors/brcm/bbdpl1/utils/ring_buffer_c.h | 68 + drivers/sensors/brcm/bbdpl1/utils/stream_codec_c.c | 209 + drivers/sensors/brcm/bbdpl1/utils/stream_codec_c.h | 86 + drivers/sensors/brcm/factory/accel_icm20610.c | 453 + .../sensors/brcm/factory/barcode_emul_tmg3992.c | 310 + drivers/sensors/brcm/factory/gesture_tmg399x.c | 157 + drivers/sensors/brcm/factory/gyro_icm20610.c | 615 + drivers/sensors/brcm/factory/light_tmg399x.c | 78 + drivers/sensors/brcm/factory/magnetic_yas532.c | 522 + drivers/sensors/brcm/factory/magnetic_yas532.h | 16 + drivers/sensors/brcm/factory/mcu_bcm4773.c | 269 + drivers/sensors/brcm/factory/pressure_bmp182.c | 232 + drivers/sensors/brcm/factory/prox_tmg399x.c | 437 + drivers/sensors/brcm/gps/Kconfig | 7 + drivers/sensors/brcm/gps/Makefile | 5 + drivers/sensors/brcm/gps/sec_gps_bcm47531.c | 255 + .../sensors/brcm/platform/bbd_s333_patch_file.h | 12881 +++++++++++++++++++ drivers/sensors/brcm/platform/bbd_tm2_patch_file.h | 12881 +++++++++++++++++++ drivers/sensors/brcm/sensors_core.c | 173 + drivers/sensors/brcm/ssp.h | 711 + drivers/sensors/brcm/ssp_bbd.c | 406 + drivers/sensors/brcm/ssp_c12sd.c | 554 + drivers/sensors/brcm/ssp_data.c | 377 + drivers/sensors/brcm/ssp_debug.c | 416 + drivers/sensors/brcm/ssp_dev.c | 555 + drivers/sensors/brcm/ssp_firmware.c | 26 + drivers/sensors/brcm/ssp_i2c.c | 758 ++ drivers/sensors/brcm/ssp_iio_ring.c | 66 + drivers/sensors/brcm/ssp_iio_trigger.c | 50 + drivers/sensors/brcm/ssp_input.c | 1145 ++ drivers/sensors/brcm/ssp_misc.c | 67 + drivers/sensors/brcm/ssp_sensorhub.c | 714 + drivers/sensors/brcm/ssp_sensorhub.h | 81 + drivers/sensors/brcm/ssp_spi.c | 254 + drivers/sensors/brcm/ssp_sysfs.c | 1020 ++ 75 files changed, 45899 insertions(+) create mode 100644 drivers/sensors/brcm/Kconfig create mode 100644 drivers/sensors/brcm/Makefile create mode 100644 drivers/sensors/brcm/bbdpl1/Makefile create mode 100644 drivers/sensors/brcm/bbdpl1/bbd.h create mode 100644 drivers/sensors/brcm/bbdpl1/bbd_common.c create mode 100644 drivers/sensors/brcm/bbdpl1/bbd_control.c create mode 100644 drivers/sensors/brcm/bbdpl1/bbd_debug.h create mode 100644 drivers/sensors/brcm/bbdpl1/bbd_ifc.h create mode 100644 drivers/sensors/brcm/bbdpl1/bbd_internal.h create mode 100644 drivers/sensors/brcm/bbdpl1/bbd_main.c create mode 100644 drivers/sensors/brcm/bbdpl1/bbd_packet.c create mode 100644 drivers/sensors/brcm/bbdpl1/bbd_patch.c create mode 100644 drivers/sensors/brcm/bbdpl1/bbd_reliable.c create mode 100644 drivers/sensors/brcm/bbdpl1/bbd_sensor.c create mode 100644 drivers/sensors/brcm/bbdpl1/bbd_sio.c create mode 100644 drivers/sensors/brcm/bbdpl1/bbd_ssi_spi.c create mode 100644 drivers/sensors/brcm/bbdpl1/bbd_sysfs.c create mode 100644 drivers/sensors/brcm/bbdpl1/bbd_tty.c create mode 100644 drivers/sensors/brcm/bbdpl1/bcm477x_debug.h create mode 100644 drivers/sensors/brcm/bbdpl1/bcm477x_ssi_spi.h create mode 100644 drivers/sensors/brcm/bbdpl1/bcm_gps_spi.c create mode 100644 drivers/sensors/brcm/bbdpl1/bcm_gps_spi.h create mode 100644 drivers/sensors/brcm/bbdpl1/transport/Makefile create mode 100644 drivers/sensors/brcm/bbdpl1/transport/bbd_bridge_c.c create mode 100644 drivers/sensors/brcm/bbdpl1/transport/bbd_bridge_c.h create mode 100644 drivers/sensors/brcm/bbdpl1/transport/bbd_engine.c create mode 100644 drivers/sensors/brcm/bbdpl1/transport/bbd_engine.h create mode 100644 drivers/sensors/brcm/bbdpl1/transport/bbd_packet_layer_c.c create mode 100644 drivers/sensors/brcm/bbdpl1/transport/bbd_packet_layer_c.h create mode 100644 drivers/sensors/brcm/bbdpl1/transport/rpc_codec_c.h create mode 100644 drivers/sensors/brcm/bbdpl1/transport/transport_layer_c.c create mode 100644 drivers/sensors/brcm/bbdpl1/transport/transport_layer_c.h create mode 100644 drivers/sensors/brcm/bbdpl1/transport/transport_layer_custom.h create mode 100644 drivers/sensors/brcm/bbdpl1/utils/Makefile create mode 100644 drivers/sensors/brcm/bbdpl1/utils/bbd_utils.h create mode 100644 drivers/sensors/brcm/bbdpl1/utils/crc8bits_c.c create mode 100644 drivers/sensors/brcm/bbdpl1/utils/crc8bits_c.h create mode 100644 drivers/sensors/brcm/bbdpl1/utils/ring_buffer_c.c create mode 100644 drivers/sensors/brcm/bbdpl1/utils/ring_buffer_c.h create mode 100644 drivers/sensors/brcm/bbdpl1/utils/stream_codec_c.c create mode 100644 drivers/sensors/brcm/bbdpl1/utils/stream_codec_c.h create mode 100644 drivers/sensors/brcm/factory/accel_icm20610.c create mode 100644 drivers/sensors/brcm/factory/barcode_emul_tmg3992.c create mode 100644 drivers/sensors/brcm/factory/gesture_tmg399x.c create mode 100644 drivers/sensors/brcm/factory/gyro_icm20610.c create mode 100644 drivers/sensors/brcm/factory/light_tmg399x.c create mode 100644 drivers/sensors/brcm/factory/magnetic_yas532.c create mode 100644 drivers/sensors/brcm/factory/magnetic_yas532.h create mode 100644 drivers/sensors/brcm/factory/mcu_bcm4773.c create mode 100644 drivers/sensors/brcm/factory/pressure_bmp182.c create mode 100644 drivers/sensors/brcm/factory/prox_tmg399x.c create mode 100644 drivers/sensors/brcm/gps/Kconfig create mode 100644 drivers/sensors/brcm/gps/Makefile create mode 100644 drivers/sensors/brcm/gps/sec_gps_bcm47531.c create mode 100644 drivers/sensors/brcm/platform/bbd_s333_patch_file.h create mode 100644 drivers/sensors/brcm/platform/bbd_tm2_patch_file.h create mode 100644 drivers/sensors/brcm/sensors_core.c create mode 100644 drivers/sensors/brcm/ssp.h create mode 100644 drivers/sensors/brcm/ssp_bbd.c create mode 100644 drivers/sensors/brcm/ssp_c12sd.c create mode 100644 drivers/sensors/brcm/ssp_data.c create mode 100644 drivers/sensors/brcm/ssp_debug.c create mode 100644 drivers/sensors/brcm/ssp_dev.c create mode 100644 drivers/sensors/brcm/ssp_firmware.c create mode 100644 drivers/sensors/brcm/ssp_i2c.c create mode 100644 drivers/sensors/brcm/ssp_iio_ring.c create mode 100644 drivers/sensors/brcm/ssp_iio_trigger.c create mode 100644 drivers/sensors/brcm/ssp_input.c create mode 100644 drivers/sensors/brcm/ssp_misc.c create mode 100644 drivers/sensors/brcm/ssp_sensorhub.c create mode 100644 drivers/sensors/brcm/ssp_sensorhub.h create mode 100644 drivers/sensors/brcm/ssp_spi.c create mode 100644 drivers/sensors/brcm/ssp_sysfs.c diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig index 630df62..8791103 100644 --- a/drivers/sensors/Kconfig +++ b/drivers/sensors/Kconfig @@ -60,5 +60,14 @@ config SENSORS_SSP_STM_RINATO if SENSORS_SSP_STM_RINATO source "drivers/sensors/stm_rinato/Kconfig" endif + +config SENSORS_SSP_BBD_BRCM + bool "Sensorhub for BRCM" + help + This is enables Sensorhub for BRCM +if SENSORS_SSP_BBD_BRCM +source "drivers/sensors/brcm/Kconfig" +endif + endif endmenu diff --git a/drivers/sensors/Makefile b/drivers/sensors/Makefile index 0a6d0b5..8065061 100644 --- a/drivers/sensors/Makefile +++ b/drivers/sensors/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_SENSORS_CORE) += sensors_core.o obj-$(CONFIG_SENSORS_MAX86902) += max86902.o obj-$(CONFIG_SENSORS_MAX_NOTCHFILTER) += max_notchfilter/ obj-$(CONFIG_SENSORS_SSP_STM_RINATO) += stm_rinato/ +obj-$(CONFIG_SENSORS_SSP_BBD_BRCM) += brcm/ diff --git a/drivers/sensors/brcm/Kconfig b/drivers/sensors/brcm/Kconfig new file mode 100644 index 0000000..c9829e2 --- /dev/null +++ b/drivers/sensors/brcm/Kconfig @@ -0,0 +1,87 @@ +# +# sensor drivers configuration +# +config SENSORS_SSP_BBD + tristate "Sensors ssp brcm" + default n + depends on SPI + help + ssp driver for sensor hub. + If you say yes here you get ssp support for + sensor hub. + To compile this driver as a module, choose M here: the + module will be called ssp. + +choice + depends on SENSORS_SSP_BBD + prompt "Choose target patch" + default SENSORHUB_TM2 + +config SENSORHUB_S333 + bool "BROADCOM_SENSORHUB_S333" + help + Support for BRCM S333 + +config SENSORHUB_TM2 + bool "BROADCOM_SENSORHUB_TM2" + help + Support for BRCM TM2 +endchoice + +config SENSORS_SSP_ICM20610 + tristate "Sensors ssp icm20610" + default n + depends on SPI + help + icm20610 file for factory test in ssp driver. + If you say yes here you get icm20610 support for + factory test. + To compile this driver as a module, choose M here: the + module will be called ssp. + +config SENSORS_SSP_YAS532 + tristate "Sensors ssp yas532" + default n + depends on SPI + help + yas532 file for factory test in ssp driver. + If you say yes here you get yas532 support for + factory test. + To compile this driver as a module, choose M here: the + module will be called ssp. + +config SENSORS_SSP_BMP182 + tristate "Sensor ssp bmp182" + default n + depends on SPI + help + bmp182 file for factory test in ssp driver. + If you say yes here you get lps25h support for + factory test. + To compile this driver as a module, choose M here: the + module will be called ssp. + +config SENSORS_SSP_TMG399X + tristate "Sensors ssp tmg399x" + default n + depends on SPI + help + max88005 file for factory test in ssp driver. + If you say yes here you get tmg399x support for + factory test. + To compile this driver as a module, choose M here: the + module will be called ssp. + +config SENSORS_SSP_MOBEAM + tristate "Sensors ssp mobeam" + default n + depends on I2C + help + mobeam file for ssp driver. + If you say yes here you get mobeam support using TMG399x. + To compile this driver as a module, choose M here: the + module will be called ssp. + +config GPS_BCM47531 + bool "BROADCOM_GPS_CHIPSET_47531" + default n diff --git a/drivers/sensors/brcm/Makefile b/drivers/sensors/brcm/Makefile new file mode 100644 index 0000000..71843a1 --- /dev/null +++ b/drivers/sensors/brcm/Makefile @@ -0,0 +1,19 @@ +# +# Makefile for the sensor drivers. +# + +# Each configuration option enables a list of files. +obj-$(CONFIG_SENSORS_SSP) += ssp_dev.o ssp_i2c.o ssp_data.o ssp_sysfs.o \ + ssp_input.o ssp_firmware.o ssp_debug.o \ + ssp_iio_ring.o ssp_iio_trigger.o + +obj-$(CONFIG_SENSORS_SSP_ICM20610) += factory/accel_icm20610.o factory/gyro_icm20610.o +obj-$(CONFIG_SENSORS_SSP_YAS532) += factory/magnetic_yas532.o +obj-$(CONFIG_SENSORS_SSP_TMG399X) += factory/light_tmg399x.o factory/prox_tmg399x.o \ + factory/gesture_tmg399x.o +obj-$(CONFIG_SENSORS_SSP_BMP182) += factory/pressure_bmp182.o +obj-$(CONFIG_SENSORS_SSP_MOBEAM) += factory/barcode_emul_tmg3992.o +obj-$(CONFIG_SENSORS_SSP_SENSORHUB) += ssp_sensorhub.o ssp_misc.o +obj-$(CONFIG_SENSORS_SSP_BBD) += ssp_bbd.o factory/mcu_bcm4773.o \ + bbdpl1/ +obj-$(CONFIG_GPS_BCM47531) += gps/sec_gps_bcm47531.o diff --git a/drivers/sensors/brcm/bbdpl1/Makefile b/drivers/sensors/brcm/bbdpl1/Makefile new file mode 100644 index 0000000..d9f10f3 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the Broadcom Bridge Driver +# + +# Each configuration option enables a list of files. +obj-$(CONFIG_SENSORS_SSP_BBD) += bbd_common.o bbd_control.o bbd_main.o bbd_packet.o \ + bbd_reliable.o bbd_sensor.o bbd_sysfs.o \ + bbd_ssi_spi.o bbd_tty.o bcm_gps_spi.o bbd_patch.o +obj-$(CONFIG_SENSORS_SSP_BBD) += transport/ utils/ diff --git a/drivers/sensors/brcm/bbdpl1/bbd.h b/drivers/sensors/brcm/bbdpl1/bbd.h new file mode 100644 index 0000000..4b8b679 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/bbd.h @@ -0,0 +1,54 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#ifndef __BBD_H__ +#define __BBD_H__ + +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define MIN(a,b) ((a) > (b) ? (b) : (a)) +#define BBD_MAX_DATA_SIZE 4096 /* max data size for transition */ +#define BBD_MAX_FREED_NUM 10 /* max number of freed list */ + +/** the status of downloading firmware **/ +enum { + BBD_FW_COMPLETED, + BBD_FW_DOWNLOADING, + BBD_FW_FAILED +}; + +typedef int (*bbd_on_packet)(void *ssh_data, unsigned char *buf, size_t size); +typedef int (*bbd_on_packet_alarm)(void *ssh_data); +typedef int (*bbd_on_control)(void *ssh_data, char *str_ctrl); +typedef int (*bbd_on_mcu_ready)(void *ssh_data, bool ready); + +typedef struct { + bbd_on_packet on_packet; + bbd_on_packet_alarm on_packet_alarm; + bbd_on_control on_control; + bbd_on_mcu_ready on_mcu_ready; +}bbd_callbacks; + +extern void bbd_register(void* ext_data, bbd_callbacks *pcallbacks); +extern ssize_t bbd_send_packet(unsigned char *buf, size_t size); +extern ssize_t bbd_pull_packet(unsigned char *pbuff, size_t len, unsigned int timeout_ms); +extern int bbd_control(char *str_ctrl); +extern int bbd_mcu_reset(void); + +#endif /* __BBD_H__ */ diff --git a/drivers/sensors/brcm/bbdpl1/bbd_common.c b/drivers/sensors/brcm/bbdpl1/bbd_common.c new file mode 100644 index 0000000..cf23cd4 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/bbd_common.c @@ -0,0 +1,479 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#include "bbd_internal.h" +#include "utils/bbd_utils.h" + +#include /* printk() */ +#include /* kmalloc() */ + +#undef DEBUG_MEMORY + +bool bbd_debug(void) +{ + return ((gpbbd_dev != NULL) && gpbbd_dev->db); +} + +struct BbdEngine *bbd_engine(void) +{ + return &gpbbd_dev->bbd_engine; +} + +static int nAlloc; /* Linux guarantees statics are cleared to 0 */ + +int bbd_alloc_count(void) +{ + return nAlloc; +} + +void* bbd_alloc(size_t size) +{ + void* p = kmalloc(size, GFP_KERNEL); + if (!p) { + printk("%s failed to alloc\n", __func__); + return p; + } + + memset(p, 0, size); + ++nAlloc; +#ifdef DEBUG_MEMORY + dprint("bbd_alloc(%u) #%d = %p\n", size, nAlloc, p); +#endif + return p; +} + +void bbd_free(void* p) +{ +#ifdef DEBUG_MEMORY + dprint("bbd_free() #%d = %p\n", nAlloc, p); +#endif + --nAlloc; + kfree(p); +} + +/*--------------------------------------------------------------- + * + * Manage the queue of freed buffers + * Allow max 10 buffers freed + * + *--------------------------------------------------------------- + */ + +#define BBD_FREED_BUFFER_THRESHOLD 10 + +#ifdef DEBUG_MEMORY +#define MSG_INIT_FREED char* msg = "freed" +#define MSG_SET_CACHED msg = "cached" +#else +#define MSG_INIT_FREED +#define MSG_SET_CACHED +#endif + +void bbd_qitem_cache_add(struct bbd_qitem *qi) +{ + struct bbd_device *p = gpbbd_dev; + MSG_INIT_FREED; + + mutex_lock(&p->qlock); + if (p->qcount < BBD_FREED_BUFFER_THRESHOLD) { + ++p->qcount; + qi->next = p->qfree; + p->qfree = qi; + MSG_SET_CACHED; + } + else { + bbd_free(qi); + } + mutex_unlock(&p->qlock); + +#ifdef DEBUG_MEMORY + dprint(KERN_INFO"%s(%p) %d %s\n", __func__, qi, p->qcount, msg); +#endif +} + +struct bbd_qitem *bbd_qitem_cache_get(size_t req_size) +{ + struct bbd_qitem **q_prev = NULL; + struct bbd_qitem *q = NULL; + + mutex_lock(&gpbbd_dev->qlock); + q_prev = &gpbbd_dev->qfree; + q = *q_prev; + while (q) + { + if (req_size <= q->len) + { + *q_prev = q->next; + --gpbbd_dev->qcount; + mutex_unlock(&gpbbd_dev->qlock); + q->next = 0; + q->lenUsed = req_size; +#ifdef DEBUG_MEMORY + dprint(KERN_INFO"%s(%u) reuse %p\n", __func__, req_size, q); +#endif + return q; + } + q_prev = &q->next; + q = q->next; + } + mutex_unlock(&gpbbd_dev->qlock); + return 0; +} + +int bbd_qitem_cache_uninit(void) +{ + int freed = 0; + mutex_lock(&gpbbd_dev->qlock); + freed = bbd_queue_uninit(gpbbd_dev->qfree); + mutex_unlock(&gpbbd_dev->qlock); + return freed; +} + +/*--------------------------------------------------------------- + * + * Common queue and device code. + * + *--------------------------------------------------------------- + */ + +static void bbd_base_init_common(struct bbd_buffer *p) +{ + mutex_init(&p->lock); + p->count = 0; + p->tout_cnt = 0; + init_waitqueue_head(&p->poll_inq); + init_waitqueue_head(&p->comp_inq); +} + +void bbd_base_init_vars(struct bbd_device *dev, struct bbd_base *p, int* result) +{ + if (*result) + return; + + memset(p, 0, sizeof(*p)); + /* p->esw_ready = false; */ + /* p->rxb.buff_size = 0; ** done by memset() */ + + /* allocate memory for rx packet from LHD */ + p->rxb.pbuff = bbd_alloc(BBD_MAX_DATA_SIZE); + if (!p->rxb.pbuff) { + printk(KERN_ERR "%s():failed to allocate mem for rxbuff\n", + __func__); + *result = -ENOMEM; + } + /* printk("%s(%p,%p)\n", __func__, dev, p); */ + + bbd_base_init_common((struct bbd_buffer *) &p->txq); + bbd_base_init_common( &p->rxb); +} + +int bbd_queue_uninit(struct bbd_qitem *q) +{ + int freed = 0; + while (q) + { + struct bbd_qitem* pq_next = q->next; + q->next = 0; + bbd_free(q); + ++freed; + q = pq_next; + } + return freed; +} + +#undef EXTENSIVE_REINIT + +int bbd_base_reinit(int minor) +{ + struct bbd_base *p = gpbbd_dev->bbd_ptr[minor]; + if (!p) return -EFAULT; + + FUNS(p->name); +#ifdef EXTENSIVE_REINIT + p->rxb.buff_size = 0; +#endif + return 0; +} + +int bbd_base_uninit(struct bbd_base *p) +{ + int freed = bbd_queue_uninit(p->txq.qhead); + p->txq.qhead = p->txq.qtail = NULL; + bbd_free(p->rxb.pbuff); + dprint(KERN_INFO"%s(%s) %d freed\n", __func__, p->name, freed); + return freed; +} + +struct bbd_qitem *bbd_qitem_alloc(size_t req_size) +{ + struct bbd_qitem *qi = bbd_qitem_cache_get(req_size); + if (!qi) + { + int usable_size = BBD_BUFFER_SIZE; + if ((int) req_size > usable_size) + usable_size = req_size; + qi = (struct bbd_qitem *) bbd_alloc(usable_size + QITEM_OVERHEAD); + if (qi) { + qi->lenUsed = req_size; + qi->len = usable_size; + qi->next = NULL; + } + } + return qi; +} + +bool bbd_q_add(struct bbd_queue *q, const unsigned char *pbuff, size_t size) +{ + struct bbd_qitem *qi = NULL; + + mutex_lock(&q->lock); + + qi = bbd_qitem_alloc(size); + if (!qi) { + mutex_unlock(&q->lock); + return false; + } + memcpy(qi->data, pbuff, size); + if (q->qtail) { + q->qtail->next = qi; + } + else { + q->qhead = qi; + } + q->qtail = qi; + qi->next = NULL; + + ++q->count; + + mutex_unlock(&q->lock); + return true; +} + +struct bbd_qitem *bbd_q_get(struct bbd_queue *q) +{ + struct bbd_qitem *qi = NULL; + + mutex_lock(&q->lock); + qi = q->qhead; + if (qi) { + q->qhead = qi->next; + if (!q->qhead) q->qtail = NULL; + qi->next = NULL; + --q->count; + } + mutex_unlock(&q->lock); + + return qi; +} + +/*---------------------------------------------------------------------- + * + * bbd_on_read + * + * Common code to fill a buffer to the user. + * + * Note: this code needs to handle N packets, not just one buffer-full + * + *---------------------------------------------------------------------- + */ +#define BBD_ON_READ_Q_THRESHOLD 1024 +ssize_t bbd_on_read(int minor, const unsigned char *pbuff, size_t size) +{ + struct bbd_base *p = NULL; + struct bbd_queue *q = NULL; + + if (!gpbbd_dev) + return -EFAULT; + + p = gpbbd_dev->bbd_ptr[minor]; + + /* allow only some byte-stuffing */ + if (!p || size > 2*BBD_MAX_DATA_SIZE) + return -EFAULT; + q = &p->txq; + + if(q->count < BBD_ON_READ_Q_THRESHOLD) + bbd_q_add(q, pbuff, size); + + wake_up(&q->poll_inq); /* Tell about new data */ + + dprint("%s %d #%u\n", p->name, (int) size, q->count); + BbdEngine_OnTimer(); + return size; +} + +/*---------------------------------------------------------------------- + * + * bbd_write + * + * Common code to fill a buffer from the user. + * Notes: + * This code lacks capacity for N packets. + * It touches the "rx" buffer, so the naming is skewed. + * + *---------------------------------------------------------------------- + */ + +ssize_t bbd_write(int minor, const char *buf, size_t size, bool fromUser) +{ + struct bbd_base *p = gpbbd_dev->bbd_ptr[minor]; + struct bbd_buffer *prxb = NULL; + bool userFail = false; + size_t wr_size = MIN(2*BBD_MAX_DATA_SIZE, size); + + BbdEngine_OnTimer(); + + if (!p) { + dprint("%s\n", "no BBD"); + return -EFAULT; + } + prxb = &p->rxb; + + if (size > 2*BBD_MAX_DATA_SIZE) { + dprint("%s [%d]\n", p->name, size); + return -EFAULT; + } + dprint("%s(%s.%s)[%d]\n", __func__, p->name, + (fromUser) ? "user" : "internal", + (int) size); + + if (wr_size != size) { + dprint("%s [%d]\n", p->name, size); + return -EFAULT; + } + + mutex_lock(&prxb->lock); + if (fromUser) + userFail = copy_from_user(prxb->pbuff, buf, wr_size); + else + memcpy(prxb->pbuff, buf, wr_size); + + bbd_log_hex(p->name, prxb->pbuff, wr_size); + + if (!userFail) { + prxb->buff_size = wr_size; + prxb->count += wr_size; + } + mutex_unlock(&prxb->lock); + + if (userFail) + return -EINVAL; + return wr_size; +} + +/*---------------------------------------------------------------------- + * + * bbd_read + * + * Common read code to fill a user buffer from an internal queue. + * Notes: + * This code lacks capacity for N packets. + * It touches the "rx" buffer. + * + *---------------------------------------------------------------------- + */ + +ssize_t bbd_read(int minor, char __user *buf, size_t size, bool bUser) +{ + struct bbd_base *p = gpbbd_dev->bbd_ptr[minor]; + struct bbd_queue *q = NULL; + struct bbd_qitem *qi = NULL; + + ssize_t rd_size = 0; + + if (!p) + return -EFAULT; + + BbdEngine_OnTimer(); + q = &p->txq; + + qi = bbd_q_get(q); + if (!qi) + return 0; + + rd_size = MIN(qi->lenUsed, size); + if (size < qi->lenUsed) { + dprint("%d %s %d<%d\n", __LINE__, p->name, size, qi->lenUsed); + rd_size = -EFAULT; + goto err; + } + + if (bUser) { + if (copy_to_user(buf, (void *)qi->data, rd_size)) { + rd_size = -EFAULT; + goto err; + } + } + else { + memcpy(buf, qi->data, rd_size); + } + + + dprint("%s(%s.%s)[%d]\n", __func__, p->name, + (bUser) ? "user" : "internal", (int) rd_size); + bbd_log_hex(p->name, qi->data, rd_size); + + bbd_qitem_cache_add(qi); + wake_up(&q->comp_inq); + + return rd_size; + +err: + bbd_qitem_cache_add(qi); + return rd_size; +} + +unsigned int bbd_poll(int minor, struct file *filp, poll_table * wait) +{ + struct bbd_base *p = gpbbd_dev->bbd_ptr[minor]; + unsigned int mask = 0; + + if (!p) + return -EFAULT; + + BbdEngine_OnTimer(); + if (p->txq.count > 0) { + mask |= POLLIN; /* no need to sleep - already have data */ + return mask; + } + + poll_wait(filp, &p->txq.poll_inq, wait); /* wait until wake up */ + + if (p->txq.count > 0) + mask |= POLLIN; + + return mask; +} + +/* Return the number of packets, or -1 upon error + */ + +int bbd_count(int minor) +{ + struct bbd_base *p = gpbbd_dev->bbd_ptr[minor]; + + return (p) ? p->txq.count : -1; +} + +void bbd_log_hex(const char* pIntroduction, + const unsigned char* pData, + unsigned long ulDataLen) +{ +} diff --git a/drivers/sensors/brcm/bbdpl1/bbd_control.c b/drivers/sensors/brcm/bbdpl1/bbd_control.c new file mode 100644 index 0000000..767160f --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/bbd_control.c @@ -0,0 +1,283 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#include "bbd_internal.h" + +ssize_t bbd_ESW_control(char *buf, ssize_t len); + +int bbd_tty_open (void); +int bbd_tty_close(void); + +struct bcm4773_uart_port; +void bcm4773_mcu_resp_float(bool do_float); +void BbdEngine_SetUp(struct BbdEngine *p); +struct ssp_data; +extern void reset_mcu(struct ssp_data *data); + +extern bool ssp_dbg; +extern bool ssp_pkt_dbg; +extern bool ssi_dbg; + +/** + * initialize control-specific variables + * + * @pbbd_dev: bbd driver pointer + * + * @return: *result 0 = success, others = failure + */ + +void bbd_control_init_vars(struct bbd_device *dev, int* result) +{ + struct bbd_control_device *p = NULL; + + if (*result) + return; + + p = bbd_alloc(sizeof(struct bbd_control_device)); + if (!p) { + *result = -ENOMEM; + return; + } + + bbd_base_init_vars(dev, &p->base, result); + if (*result) { + bbd_free(p); + return; + } + dev->bbd_ptr[BBD_MINOR_CONTROL] = p; + p->base.name = "control"; +} + +struct bbd_control_device *bbd_control_ptr(void) +{ + if (!gpbbd_dev) + return NULL; + return gpbbd_dev->bbd_ptr[BBD_MINOR_CONTROL]; +} + +struct bbd_base *bbd_control_ptr_base(void) +{ + struct bbd_control_device *p = bbd_control_ptr(); + if (!p) + return NULL; + return &p->base; +} + +int bbd_control_uninit(void) +{ + struct bbd_control_device* p = bbd_control_ptr(); + int freed = 0; + if (p) { + freed = bbd_base_uninit(&p->base); + bbd_free(p); + gpbbd_dev->bbd_ptr[BBD_MINOR_CONTROL] = NULL; + } + return freed; +} + +/** + * sensor hub driver or internal PL sends string control + * + * somewhat primitive - no queueing or guarantees at all. + * + * @str_ctrl: string control + * + * @return: 0 = success, -1 = failure + */ +int bbd_control(char *str) +{ + return bbd_on_read(BBD_MINOR_CONTROL, str, strlen(str) + 1); +} +EXPORT_SYMBOL(bbd_control); + +/** + * sensor hub request reset to 477x + * + * @return: 0 = success, -1 = failure + */ +int bbd_mcu_reset(void) +{ + dprint("reset request from sensor hub\n"); + return bbd_control(BBD_CTRL_RESET_REQ); +} +EXPORT_SYMBOL(bbd_mcu_reset); + + +/** + * open bbd control driver + */ +int bbd_control_open(struct inode *inode, struct file *filp) +{ + filp->private_data = gpbbd_dev; + dprint("opened\n"); + return 0; +} + +/** + * close bbd control driver + */ +int bbd_control_release(struct inode *inode, struct file *filp) +{ + if (!gpbbd_dev) + return -EFAULT; + + return bbd_base_reinit(BBD_MINOR_CONTROL); +} + +/** + * read signal/data from bbd_list_head to user + */ +ssize_t bbd_control_read(struct file *filp, char __user *buf, + size_t size, loff_t *ppos) +{ + return bbd_read(BBD_MINOR_CONTROL, buf, size, true); +} + + +/** + * send string control directly from lhd to bbd for these purposes: + * 1) BBD control + * 2) BBD packet layer + * 3) SH driver + */ + +ssize_t bbd_control_write(struct file *filp, + const char __user *buf, + size_t size, loff_t *ppos) +{ + struct bbd_base *pb = bbd_control_ptr_base(); + ssize_t len = bbd_write(BBD_MINOR_CONTROL, buf, size, true); + if (len < 0) + return len; + return bbd_ESW_control(pb->rxb.pbuff, pb->rxb.buff_size); +} + +bool esw_ready(void) +{ + struct bbd_control_device *p = bbd_control_ptr(); + if (!p) + return false; + + return p->esw_ready; +} + +ssize_t bbd_ESW_control(char *buf, ssize_t len) +{ + ssize_t ret = len; + struct bbd_control_device *p = bbd_control_ptr(); + struct bbd_sensor_device *ps = bbd_sensor_ptr(); + struct BbdEngine *pEng = &gpbbd_dev->bbd_engine; + bool bSetup = false; + + if (!p || !ps) + return -EFAULT; + + dprint("%s(%s) callbacks %sregistered.\n", __func__, buf, + (ps->callbacks) ? "" : "NOT "); + + if (strstr(buf, ESW_CTRL_READY)) { + p->esw_ready = true; + bcm4773_mcu_resp_float(true); + if (ps->callbacks && ps->callbacks->on_mcu_ready) { + ret = ps->callbacks->on_mcu_ready( + ps->priv_data, + p->esw_ready); + } + } else if (strstr(buf, ESW_CTRL_NOTREADY)) { + p->esw_ready = false; + ps->base.rxb.buff_size = 0; + bcm4773_mcu_resp_float(false); + if (ps->callbacks && ps->callbacks->on_mcu_ready) { + ret = ps->callbacks->on_mcu_ready(ps->priv_data, + p->esw_ready); + } + } else if (strstr(buf, ESW_CTRL_CRASHED)) { + p->esw_ready = false; + ps->base.rxb.buff_size = 0; + bcm4773_mcu_resp_float(false); + if (ps->callbacks && ps->callbacks->on_control) + ret = ps->callbacks->on_control(ps->priv_data, buf); + } else if (strstr(buf, BBD_CTRL_DEBUG_ON)) { + gpbbd_dev->db = true; + bSetup = true; + } else if (strstr(buf, BBD_CTRL_DEBUG_OFF)) { + gpbbd_dev->db = false; + bSetup = true; + } else if (strstr(buf, SSP_DEBUG_ON)) { + ssp_dbg = true; + ssp_pkt_dbg = true; + } else if (strstr(buf, SSP_DEBUG_OFF)) { + ssp_dbg = false; + ssp_pkt_dbg = false; + } else if (strstr(buf, SSI_DEBUG_ON)) { + ssi_dbg = true; + } else if (strstr(buf, SSI_DEBUG_OFF)) { + ssi_dbg = false; + } else if (strstr(buf, BBD_CTRL_SHMD_ON)) { + gpbbd_dev->shmd = true; + bSetup = true; + } else if (strstr(buf, BBD_CTRL_SHMD_OFF)) { + gpbbd_dev->shmd = false; + bSetup = true; + } else if (strstr(buf, BBD_CTRL_SPI)) { + bbd_tty_close(); + gpbbd_dev->sio_type = BBD_SERIAL_SPI; + bSetup = true; + } else if (strstr(buf, BBD_CTRL_TTY)) { + gpbbd_dev->sio_type = BBD_SERIAL_TTY; + bSetup = true; + bbd_tty_open(); + } else if (BbdBridge_SetControlMessage(&pEng->bridge, buf)) { + return len; + } else if (ps->callbacks && ps->callbacks->on_control) { + /* Tell SHMD about the unknown control string */ + ps->callbacks->on_control(ps->priv_data, buf); + } + + if (bSetup) { + BbdEngine_SetUp(pEng); + } + else /* No need to send it to the SHMD */ + { + const char* msg = "NO CB"; + if (ps->callbacks) + msg = (ps->callbacks->on_mcu_ready) ? "rdy cb" : "no cb"; + dprint("%s() %s RDY %d DBG %d = %d\n", __func__, msg, + p->esw_ready, bbd_debug(), ret); + ret = len; + } + + return ret; +} + +/** + * tx control string to LHD + * + * There are several paths for control strings: + * + * SHMD-->BBD_CONTROL-->LHD "need reset" + * SHMD<--BBD_CONTROL<--LHD (TBD) + * BBD_CONTROL<--LHD ESW, BBD, and PL control + * BBD_CONTROL-->LHD BBD and PL status and errors + */ +unsigned int bbd_control_poll(struct file *filp, poll_table * wait) +{ + return bbd_poll(BBD_MINOR_CONTROL, filp, wait); +} diff --git a/drivers/sensors/brcm/bbdpl1/bbd_debug.h b/drivers/sensors/brcm/bbdpl1/bbd_debug.h new file mode 100644 index 0000000..55b056e --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/bbd_debug.h @@ -0,0 +1,72 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#ifndef __BBD_DEBUG_H__ // { +#define __BBD_DEBUG_H__ + +#define xBBD_DEBUG 1 + +bool bbd_debug(void); + +#ifdef BBD_DEBUG // } +#ifdef TDD +#define dprint(fmt, args...) \ + if(db) printf("BBD:(%s): " fmt, __func__, ##args) +#else + +#ifndef KERN_INFO +#define KERN_INFO "" +#endif + +#ifdef __cplusplus +extern "C" { +#endif +int printk(const char *fmt, ...); +#ifdef __cplusplus +} +#endif + +#define dprint(fmt, args...) \ + if(bbd_debug()) printk(KERN_INFO "BBD::%s()/%d " fmt, \ + __func__, __LINE__, ##args) +#endif + +#define FUNC() if (bbd_debug()) printk(KERN_INFO "BBD:%s()/%d\n", __FUNCTION__, __LINE__) +#define FUNS(s) if (bbd_debug()) printk(KERN_INFO "BBD:%s(\"%s\")/%d\n", __FUNCTION__, s, __LINE__) +#define FUNI(i) if (bbd_debug()) printk(KERN_INFO "BBD:%s(%lu)/%d\n", __FUNCTION__, (unsigned long) i, __LINE__) +#define FUNSI(s,i) if (bbd_debug()) printk(KERN_INFO "BBD:%s(\"%s\",%lu)/%d\n", __FUNCTION__, s, (unsigned long) i, __LINE__) + +#else // } else { + +#define dprint(format, args...) +#define FUNC() +#define FUNS(s) +#define FUNI(i) +#define FUNSI(s,i) + +#endif // } BBD_DEBUG + + +void bbd_log_hex(const char* pIntroduction, + const unsigned char* pData, + unsigned long ulDataLen); + + +#endif // } __BBD_DEBUG_H__ diff --git a/drivers/sensors/brcm/bbdpl1/bbd_ifc.h b/drivers/sensors/brcm/bbdpl1/bbd_ifc.h new file mode 100644 index 0000000..5ab501d --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/bbd_ifc.h @@ -0,0 +1,65 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ +/*******************************************************************************/ +/** \file bbd_ifc.h Interface between the BBD and Rpc code in user space +* +*******************************************************************************/ + +#ifndef BBD_IFC_H /* { */ +#define BBD_IFC_H + +#define BBD_ESW_CTRL_MAX_STR_LEN 100 + +/* BBD control (LHD <--- BBD <--- SHMD*/ +#define BBD_CTRL_RESET_REQ "BBD:RESET_REQ" /* SHMD requests ESW reset */ +#define BBD_CTRL_TICK "BBD:tick" /* timer tick inside the bridge */ + +/* ESW status (LHD ---> BBD */ +#define ESW_CTRL_READY "ESW:READY" +#define ESW_CTRL_NOTREADY "ESW:NOTREADY" +#define ESW_CTRL_CRASHED "ESW:CRASHED" + +#define BBD_CTRL_PASSTHRU_ON "BBD:PassThru=1" +#define BBD_CTRL_PASSTHRU_OFF "BBD:PassThru=0" +#define BBD_CTRL_SHMD_ON "BBD:SHMD=1" +#define BBD_CTRL_SHMD_OFF "BBD:SHMD=0" +#define BBD_CTRL_DEBUG_ON "BBD:DEBUG=1" +#define BBD_CTRL_DEBUG_OFF "BBD:DEBUG=0" + +#define SSP_DEBUG_ON "SSP:DEBUG=1" +#define SSP_DEBUG_OFF "SSP:DEBUG=0" +#define SSI_DEBUG_ON "SSI:DEBUG=1" +#define SSI_DEBUG_OFF "SSI:DEBUG=0" + +#define BBD_CTRL_SPI "BBD:Serial=SPI" +#define BBD_CTRL_TTY "BBD:Serial=TTY" +#define BBD_CTRL_SIO "BBD:Serial=SIO" + +#define SHMD_RESET_MCU "SHMD:RESET_MCU" + +/* Packet Layer handshake LHD <---> BBD */ +#define BBD_CTRL_PL_START_REMOTE_SYNC "PL:StartRemoteSync" +#define BBD_CTRL_PL_REMOTE_SYNC_COMPLETE "PL:RemoteSyncComplete" +#define BBD_CTRL_PL_ON_RELIABLE_ERROR "PL:OnReliableError" +#define BBD_CTRL_PL_ON_EXCEPTION "PL:OnException:" +#define BBD_CTRL_PL_ON_COMMUNICATION_ERROR "PL:OnCommunicationError" + + +#endif /* } BBD_IFC_H */ diff --git a/drivers/sensors/brcm/bbdpl1/bbd_internal.h b/drivers/sensors/brcm/bbdpl1/bbd_internal.h new file mode 100644 index 0000000..99307bb --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/bbd_internal.h @@ -0,0 +1,232 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#ifndef BBD_INTERNAL_H__ /* { */ +#define BBD_INTERNAL_H__ + +#include +#include +#include +#include +#include +#include +#include + +#include /* printk() */ +#include /* kmalloc() */ +#include /* everything */ +#include /* error codes */ +#include /* size_t */ +#include +#include /* O_ACCMODE */ +#include +#include +#include +#include + +#include +#include +#include + +#include "bbd.h" +#include "bbd_ifc.h" +#include "bbd_debug.h" +#include "transport/bbd_engine.h" + +#define BBD_DEVICE_MAJOR 239 + +#define BBD_PACKET_TIMEOUT 1000 + +enum { + BBD_MINOR_SENSOR = 0, + BBD_MINOR_CONTROL = 1, + BBD_MINOR_PACKET = 2, + BBD_MINOR_RELIABLE = 3, + BBD_MINOR_PATCH = 4, + BBD_MINOR_SSI_SPI_DEBUG = 5, + BBD_DEVICE_INDEX +}; + +struct bbd_dev; + +struct bbd_buffer { + struct mutex lock; /* lock */ + wait_queue_head_t poll_inq; /* wait queue for poll */ + wait_queue_head_t comp_inq; /* wait queue for completion */ + unsigned int count; /* total size count */ + unsigned int tout_cnt; /* timeout count */ + + unsigned char *pbuff; /* buff pointer for tx */ + int buff_size; /* buff size */ +}; + +#define BBD_BUFFER_SIZE 1024 + +struct bbd_qitem { + struct bbd_qitem *next; + unsigned short len; + unsigned short lenUsed; + unsigned char data[BBD_BUFFER_SIZE]; +}; + +#define QITEM_OVERHEAD (sizeof(struct bbd_qitem) - BBD_BUFFER_SIZE) + + +struct bbd_queue { + struct mutex lock; /* lock */ + wait_queue_head_t poll_inq; /* wait queue for poll */ + wait_queue_head_t comp_inq; /* wait queue for completion */ + unsigned int count; /* total size count */ + unsigned int tout_cnt; /* timeout count */ + + struct bbd_qitem *qhead; /* queue of data */ + struct bbd_qitem *qtail; /* last item in q */ +}; + +#define BBD_SERIAL_SPI 0 /* stacked ssi-spi driver for 4773 */ +#define BBD_SERIAL_TTY 1 /* stacked tty driver for 477x */ + +#define xSUPORT_MCU_HOST_WAKE /* JK temporary patch for new HW */ +#define BBD_NEW_HW_REV 8 /* connnected mcu_req, mcu_resp from HW rev 8 */ + +/* + * bbd device structure + */ +struct bbd_device { + struct BbdEngine bbd_engine; + struct cdev dev [BBD_DEVICE_INDEX]; /* device for bbd */ + void *bbd_ptr[BBD_DEVICE_INDEX]; /* individual structures */ + + struct mutex qlock; + struct bbd_qitem *qfree; /* list of free qitems */ + int qcount; + + bool db; /* debug flag */ + bool shmd; /* enable shmd-->bridge flow */ + int sio_type; /* 0 [default] SPI, 1 TTY */ + unsigned long jiffies_to_wake; + int tty_baud; + + unsigned int hw_rev; /* JK added temporary feature for old hw */ +}; + +extern struct bbd_device *gpbbd_dev; + +/* + * bbd common device structure + */ +struct bbd_base { + struct bbd_queue txq; /* tx buffer LHD<--BBD */ + struct bbd_buffer rxb; /* rx buffer LHD-->BBD */ + const char* name; +}; + +/* The overall data flow (e.g. packet, reliable, control) is like this: + * bbd_***_write() --> bbd_write() + * Fills the internal bbd_base [rx side] with data from the user. + * It is OK for this RX buffer to be a single-entry deep because + * various routines then pull this data immediately for processing + * - packet data goes into the BbdBridge + * - reliable data goes into the BbdBridge + * - control data gets scanned and handled immediately + * + * The reverse stream: + * When various data sources have packets for the user they call: + * bbd_on_read(int minor...) + * Which fills the TX queue. + * When the user code wakes up (as signalled by bbd_***_poll), it + * calls bbd_***_read() which calls bbd_read(). + */ +ssize_t bbd_write (int minor, const char *pbuff, size_t size, bool bUser); +ssize_t bbd_on_read(int minor, const unsigned char *pbuff, size_t size); +ssize_t bbd_read (int minor, char __user *pbuff, size_t size, bool bUser); +unsigned int bbd_poll (int minor, struct file *filp, poll_table *wait); + +/* + * bbd sensor device structure + */ +struct bbd_sensor_device { + struct bbd_base base; + void *priv_data; /* private data pointer */ + bbd_callbacks *callbacks; +}; + + +/* + * bbd control device structure + */ +struct bbd_control_device { + struct bbd_base base; + bool esw_ready; +}; + +/* + * bbd packet device structure + */ +struct bbd_packet_device { + struct bbd_base base; +}; + +/* + * bbd reliable device structure + */ +struct bbd_reliable_device { + struct bbd_base base; +}; + +struct Bcm477x_Debug_Xfer +{ + unsigned char a_mode; /* 0x80 */ + unsigned char a_cmd; /* 0xD1 */ + unsigned char a_spi_addr; /* 0x00 */ + unsigned char a_addr[4]; + unsigned char a_len [2]; + unsigned char a_dma; /* 0x03 */ + unsigned char b_mode; /* 0x80 */ + unsigned char b_cmd; /* 0xD1 */ + unsigned char b_spi_addr; /* 0x24 */ + unsigned char b_data[1]; /* get actual size from a_len[] */ +}; + +/* + * bbd ssi spi debug device structure + */ +struct bbd_ssi_spi_debug_device { + struct bbd_base base; + unsigned long numXfers; + unsigned long numBytes; + struct Bcm477x_Debug_Xfer trace; +}; + +void bbd_base_init_vars(struct bbd_device *dev, struct bbd_base *p, int* result); +int bbd_base_uninit ( struct bbd_base *p ); +int bbd_base_reinit (int minor); +int bbd_queue_uninit (struct bbd_qitem *q); + +void bbd_free (void *p ); +void* bbd_alloc(size_t size); +#define bbd_calloc bbd_alloc + +struct bbd_sensor_device* bbd_sensor_ptr(void); + +struct BbdEngine *bbd_engine(void); + + +#endif /* BBD_INTERNAL_H__ } */ diff --git a/drivers/sensors/brcm/bbdpl1/bbd_main.c b/drivers/sensors/brcm/bbdpl1/bbd_main.c new file mode 100644 index 0000000..f83d079 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/bbd_main.c @@ -0,0 +1,348 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#include +#include +#include +#include + +#include "bbd_internal.h" + +#define BBD_DEVICE_MAJOR 239 + +int bbd_ssi_spi_open(void); + +int bbd_tty_close(void); + +struct bbd_device *gpbbd_dev; + +void bbd_sensor_init_vars (struct bbd_device *dev, int* result); +void bbd_control_init_vars (struct bbd_device *dev, int* result); +void bbd_packet_init_vars (struct bbd_device *dev, int* result); +void bbd_reliable_init_vars(struct bbd_device *dev, int* result); +void bbd_patch_init_vars (struct bbd_device *dev, int* result); +void bbd_ssi_spi_debug_init_vars(struct bbd_device *dev, int* result); + +int bbd_sensor_uninit(void); +int bbd_control_uninit(void); +int bbd_packet_uninit(void); +int bbd_reliable_uninit(void); +int bbd_patch_uninit(void); + +int bbd_sensor_open(struct inode *inode, struct file *filp); +int bbd_sensor_release(struct inode *inode, struct file *filp); +ssize_t bbd_sensor_read(struct file *filp, char __user *buf, + size_t size, loff_t *ppos); +ssize_t bbd_sensor_write(struct file *filp, + const char __user *buf, + size_t size, loff_t *ppos); +unsigned int bbd_sensor_poll(struct file *filp, poll_table * wait); + +int bbd_control_open(struct inode *inode, struct file *filp); +int bbd_control_release(struct inode *inode, struct file *filp); +ssize_t bbd_control_read(struct file *filp, char __user *buf, + size_t size, loff_t *ppos); +ssize_t bbd_control_write(struct file *filp, + const char __user *buf, + size_t size, loff_t *ppos); +unsigned int bbd_control_poll(struct file *filp, poll_table * wait); + +int bbd_packet_open(struct inode *inode, struct file *filp); +int bbd_packet_release(struct inode *inode, struct file *filp); +ssize_t bbd_packet_read(struct file *filp, char __user *buf, + size_t size, loff_t *ppos); +ssize_t bbd_packet_write(struct file *filp, + const char __user *buf, + size_t size, loff_t *ppos); +unsigned int bbd_packet_poll(struct file *filp, poll_table * wait); + +int bbd_reliable_open(struct inode *inode, struct file *filp); +int bbd_reliable_release(struct inode *inode, struct file *filp); +ssize_t bbd_reliable_read(struct file *filp, char __user *buf, + size_t size, loff_t *ppos); +ssize_t bbd_reliable_write(struct file *filp, + const char __user *buf, + size_t size, loff_t *ppos); +unsigned int bbd_reliable_poll(struct file *filp, poll_table * wait); + +int bbd_patch_open(struct inode *inode, struct file *filp); +int bbd_patch_release(struct inode *inode, struct file *filp); +ssize_t bbd_patch_read(struct file *filp, char __user *buf, + size_t size, loff_t *ppos); + +int bbd_ssi_spi_debug_open(struct inode *inode, struct file *filp); +int bbd_ssi_spi_debug_release(struct inode *inode, struct file *filp); +ssize_t bbd_ssi_spi_debug_read(struct file *filp, char __user *buf, + size_t size, loff_t *ppos); +ssize_t bbd_ssi_spi_debug_write(struct file *filp, + const char __user *buf, + size_t size, loff_t *ppos); + +int bbd_alloc_count(void); + +static const struct file_operations bbd_fops[BBD_DEVICE_INDEX] = { + /* bbd sensor file operations (legacy) */ + { + .owner = THIS_MODULE, + .open = bbd_sensor_open, + .release = bbd_sensor_release, + .read = bbd_sensor_read, + .write = bbd_sensor_write, + .poll = bbd_sensor_poll, + }, + /* bbd control file operations */ + { + .owner = THIS_MODULE, + .open = bbd_control_open, + .release = bbd_control_release, + .read = bbd_control_read, + .write = bbd_control_write, + .poll = bbd_control_poll, + }, + /* bbd packet file operations */ + { + .owner = THIS_MODULE, + .open = bbd_packet_open, + .release = bbd_packet_release, + .read = bbd_packet_read, + .write = bbd_packet_write, + .poll = bbd_packet_poll, + }, + /* bbd reliable file operations */ + { + .owner = THIS_MODULE, + .open = bbd_reliable_open, + .release = bbd_reliable_release, + .read = bbd_reliable_read, + .write = bbd_reliable_write, + .poll = bbd_reliable_poll, + }, + /* bbd patch file operations */ + { + .owner = THIS_MODULE, + .open = bbd_patch_open, + .release = bbd_patch_release, + .read = bbd_patch_read, + .write = NULL, + .poll = NULL, + }, + /* bbd ssi spi debug operations */ + { + .owner = THIS_MODULE, + .open = bbd_ssi_spi_debug_open, + .release = bbd_ssi_spi_debug_release, + .read = bbd_ssi_spi_debug_read, + .write = bbd_ssi_spi_debug_write, + .poll = NULL, + } +}; + +/***************************************************************************** +* +* bbd platform +* +******************************************************************************/ +static void bbd_platform_release(struct device *dev) +{ + printk(KERN_INFO "%s()\n", __func__); +} + +static struct platform_device bbd_platform_dev = { + .name = "bbd", + .id = 0, + .dev = { + .release = bbd_platform_release, + } +}; + +static int bbd_dev_major = BBD_DEVICE_MAJOR; + +int bbd_sysfs_init (struct kobject *kobj); +void bbd_sysfs_uninit(struct kobject *kobj); + +static int bbd_init_vars(struct bbd_device *pbbd_dev) +{ + int result = 0; + + if (!pbbd_dev) + return -EINVAL; + + memset(pbbd_dev, 0, sizeof(*pbbd_dev)); + pbbd_dev->sio_type = BBD_SERIAL_SPI; + pbbd_dev->shmd = true; + pbbd_dev->tty_baud = 921600; + mutex_init(&pbbd_dev->qlock); + + bbd_sensor_init_vars (pbbd_dev, &result); + bbd_control_init_vars (pbbd_dev, &result); + bbd_packet_init_vars (pbbd_dev, &result); + bbd_reliable_init_vars(pbbd_dev, &result); + bbd_patch_init_vars (pbbd_dev, &result); + bbd_ssi_spi_debug_init_vars (pbbd_dev, &result); + if (!result) + BbdEngine_BbdEngine(&gpbbd_dev->bbd_engine); + return result; +} + +static int __init bbd_device_init(void) +{ + int i, ret = -ENOMEM; + dev_t devno[BBD_DEVICE_INDEX] = {0,}; + struct device *dev = NULL; + struct class *bbd_class; + + const char *dev_name[BBD_DEVICE_INDEX] = { + "bbd_sensor", + "bbd_control", + "bbd_packet", + "bbd_reliable", + "bbd_patch", + "bbd_ssi_spi_debug" }; + + gpbbd_dev = bbd_alloc(sizeof(struct bbd_device)); + if (!gpbbd_dev) + goto exit; + + ret = bbd_init_vars(gpbbd_dev); + if (ret < 0) + goto exit; + + bbd_class = class_create(THIS_MODULE, "bbd"); + if (IS_ERR(bbd_class)) { + printk(KERN_ERR "BBD:%s() failed to create class\n", __func__); + goto free_bbd_dev; + } + + for (i=0; idev[i], &bbd_fops[i]); + gpbbd_dev->dev[i].owner = THIS_MODULE; + gpbbd_dev->dev[i].ops = &bbd_fops[i]; + ret = cdev_add(&gpbbd_dev->dev[i], devno[i], 1); + if (ret) { + printk(KERN_ERR "failed to add bbd_device device(ret=%d).\n", ret); + while(--i) + cdev_del(&gpbbd_dev->dev[i]); + goto destroy_dev; + } + } + + if (platform_device_register(&bbd_platform_dev)) { + printk(KERN_ERR "%s(): failed to register platfrom device(bbd_device)\n", __func__); + goto del_cdev; + } + + dev = &bbd_platform_dev.dev; + dev_set_drvdata(dev, (void *)gpbbd_dev); + + if (bbd_sysfs_init(&dev->kobj)) + goto unreg_platform_dev; + + bbd_ssi_spi_open(); + + return 0; + +unreg_platform_dev: + platform_device_unregister(&bbd_platform_dev); +del_cdev: + for (i=0; idev[i]); +destroy_dev: + for (i=0; idev[i]); + unregister_chrdev_region(MKDEV(bbd_dev_major, i), 1); + } + + BbdEngine_dtor(&pbbd_dev->bbd_engine); + + bbd_tty_close(); + freedActive += bbd_patch_uninit(); + freedActive += bbd_reliable_uninit(); + freedActive += bbd_packet_uninit(); + freedActive += bbd_control_uninit(); + freedActive += bbd_sensor_uninit(); + + freedIdle = bbd_qitem_cache_uninit(); + pbbd_dev->qfree = 0; + bbd_free(pbbd_dev); + gpbbd_dev = 0; + + printk("%s Buffer stats: %d active %d idle %d lost\n", __func__, + freedActive, freedIdle, bbd_alloc_count()); + + bbd_sysfs_uninit(&dev->kobj); + platform_device_unregister(&bbd_platform_dev); +} + +MODULE_AUTHOR("Broadcom"); +MODULE_LICENSE("Dual BSD/GPL"); +/* bbd_device_init() should be called before shmd/ssp init function */ +subsys_initcall(bbd_device_init); +module_exit(bbd_device_exit); diff --git a/drivers/sensors/brcm/bbdpl1/bbd_packet.c b/drivers/sensors/brcm/bbdpl1/bbd_packet.c new file mode 100644 index 0000000..ba5b0cc --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/bbd_packet.c @@ -0,0 +1,144 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#include "bbd_internal.h" + +void BbdEngine_Open(struct BbdEngine *p); +void BbdEngine_Close(struct BbdEngine *p); + +/** + * initialize packet-specific variables + * + * @pbbd_dev: bbd driver pointer + * + * @return: 0 = success, others = failure + */ + +void bbd_packet_init_vars(struct bbd_device *dev, int* result) +{ + struct bbd_packet_device *p = NULL; + + if (*result) + return; + + p = bbd_alloc(sizeof(struct bbd_packet_device)); + if (!p) { + *result = -ENOMEM; + return; + } + + bbd_base_init_vars(dev, &p->base, result); + if (*result) { + bbd_free(p); + return; + } + dev->bbd_ptr[BBD_MINOR_PACKET] = p; + p->base.name = "packet"; +} + +struct bbd_packet_device* bbd_packet_ptr(void) +{ + if (!gpbbd_dev) + return NULL; + return gpbbd_dev->bbd_ptr[BBD_MINOR_PACKET]; +} + +struct bbd_base* bbd_packet_ptr_base(void) +{ + struct bbd_packet_device *p = bbd_packet_ptr(); + if (!p) + return NULL; + return &p->base; +} + +int bbd_packet_uninit(void) +{ + struct bbd_packet_device* p = bbd_packet_ptr(); + int freed = 0; + if (p) { + freed = bbd_base_uninit(&p->base); + bbd_free(p); + gpbbd_dev->bbd_ptr[BBD_MINOR_PACKET] = NULL; + } + return freed; +} + +/** + * open bbd driver + */ +int bbd_packet_open(struct inode *inode, struct file *filp) +{ + dprint("BBD:%s()\n", __func__); + + if (!gpbbd_dev) + return -EFAULT; + + filp->private_data = gpbbd_dev; + BbdEngine_Open(&gpbbd_dev->bbd_engine); + + return 0; +} + +/** + * close bbd driver + */ +int bbd_packet_release(struct inode *inode, struct file *filp) +{ + if (!gpbbd_dev) + return -EFAULT; + + dprint("BBD:%s()\n", __func__); + BbdEngine_Close(&gpbbd_dev->bbd_engine); + + return bbd_base_reinit(BBD_MINOR_PACKET); +} + +/** + * read signal/data from bbd_list_head to user + */ +ssize_t bbd_packet_read(struct file *filp, + char __user *buf, + size_t size, loff_t *ppos) +{ + return bbd_read(BBD_MINOR_PACKET, buf, size, true); +} + +/* + * send a packet from user space to external driver + */ +ssize_t bbd_packet_write(struct file *filp, + const char __user *rpcStream, + size_t size, loff_t *ppos) +{ + ssize_t len = bbd_write(BBD_MINOR_PACKET, rpcStream, size, true); + if (len < 0) + return len; + BbdBridge_SendPacket(&bbd_engine()->bridge, + bbd_packet_ptr_base()->rxb.pbuff, len); + return len; +} + +/** + * this function is for tx packet + */ +unsigned int bbd_packet_poll(struct file *filp, poll_table * wait) +{ + return bbd_poll(BBD_MINOR_PACKET, filp, wait); +} diff --git a/drivers/sensors/brcm/bbdpl1/bbd_patch.c b/drivers/sensors/brcm/bbdpl1/bbd_patch.c new file mode 100644 index 0000000..c2d9303 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/bbd_patch.c @@ -0,0 +1,78 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#include "bbd_internal.h" + +static unsigned char bbd_new_patch[] = +{ +#if defined(CONFIG_SENSORHUB_TM2) +#include "../platform/bbd_tm2_patch_file.h" +#elif defined(CONFIG_SENSORHUB_S333) +#include "../platform/bbd_s333_patch_file.h" +#endif +}; + +void bbd_patch_init_vars(struct bbd_device *dev, int* result) +{ + printk(KERN_INFO "BBD:%s() %lu\n", __func__, sizeof(bbd_new_patch)); + dev->bbd_ptr[BBD_MINOR_PATCH] = NULL; +} + +int bbd_patch_uninit(void) +{ + return 0; +} + +int bbd_patch_open(struct inode *inode, struct file *filp) +{ + printk(KERN_INFO "BBD:%s() %lu\n", __func__, sizeof(bbd_new_patch)); + + if (!gpbbd_dev) + return -EFAULT; + + filp->private_data = gpbbd_dev; + + return 0; +} + +int bbd_patch_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +ssize_t bbd_patch_read( struct file *filp, + char __user *buf, + size_t size, loff_t *ppos) +{ + ssize_t rd_size = size; + size_t offset = filp->f_pos; + + if (offset >= sizeof(bbd_new_patch)) { /* signal EOF */ + *ppos = 0; + return 0; + } + if (offset+size > sizeof(bbd_new_patch)) + rd_size = sizeof(bbd_new_patch) - offset; + if (copy_to_user(buf, bbd_new_patch + offset, rd_size)) + rd_size = -EFAULT; + else + *ppos = filp->f_pos + rd_size; + return rd_size; +} diff --git a/drivers/sensors/brcm/bbdpl1/bbd_reliable.c b/drivers/sensors/brcm/bbdpl1/bbd_reliable.c new file mode 100644 index 0000000..8fe3b6e --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/bbd_reliable.c @@ -0,0 +1,153 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#include "bbd_internal.h" + +/** + * initialize reliable-specific variables + * + * @pbbd_dev: bbd driver pointer + * + * @return: 0 = success, others = failure + */ + +void bbd_reliable_init_vars(struct bbd_device *dev, int* result) +{ + struct bbd_reliable_device *p = NULL; + + if (*result) + return; + + p = bbd_alloc(sizeof(struct bbd_reliable_device)); + if (!p) { + *result = -ENOMEM; + return; + } + + bbd_base_init_vars(dev, &p->base, result); + if (*result) { + bbd_free(p); + return; + } + dev->bbd_ptr[BBD_MINOR_RELIABLE] = p; + p->base.name = "reliable"; +} + +struct bbd_reliable_device* bbd_reliable_ptr(void) +{ + if (!gpbbd_dev) + return NULL; + return gpbbd_dev->bbd_ptr[BBD_MINOR_RELIABLE]; +} + +struct bbd_base* bbd_reliable_ptr_base(void) +{ + struct bbd_reliable_device *ps = bbd_reliable_ptr(); + if (!ps) + return NULL; + return &ps->base; +} + +int bbd_reliable_uninit(void) +{ + struct bbd_reliable_device* p = bbd_reliable_ptr(); + int freed = 0; + if (p) { + freed = bbd_base_uninit(&p->base); + bbd_free(p); + gpbbd_dev->bbd_ptr[BBD_MINOR_RELIABLE] = NULL; + } + return freed; +} + +int bbd_tty_open (void); +int bbd_tty_close(void); + +/** + * open bbd driver + */ +int bbd_reliable_open(struct inode *inode, struct file *filp) +{ + dprint(KERN_INFO "BBD:%s()\n", __func__); + + if (!gpbbd_dev) + return -EFAULT; + + filp->private_data = gpbbd_dev; + + if (gpbbd_dev->sio_type == BBD_SERIAL_TTY) + bbd_tty_open(); + + return 0; +} + +/** + * close bbd driver + */ +int bbd_reliable_release(struct inode *inode, struct file *filp) +{ + if (!gpbbd_dev) + return -EFAULT; + + bbd_tty_close(); + + return bbd_base_reinit(BBD_MINOR_RELIABLE); +} + +/** + * read signal/data from bbd_list_head to user + */ +ssize_t bbd_reliable_read(struct file *filp, + char __user *buf, + size_t size, loff_t *ppos) +{ + return bbd_read(BBD_MINOR_RELIABLE, buf, size, true); +} + +/** + * send data from user space to external driver + */ +ssize_t bbd_reliable_write(struct file *filp, + const char __user *buf, + size_t size, loff_t *ppos) +{ + ssize_t len = -EINVAL; + if (size != sizeof(struct sBbdReliableTransaction)) + return len; + + len = bbd_write(BBD_MINOR_RELIABLE, buf, size, true); + if (len < 0) + return len; + + /* Send reliable data to the BbdBridge --> PacketLayer + * --> uart --> ESW */ + if (BbdBridge_SendReliablePacket(&bbd_engine()->bridge, + (struct sBbdReliableTransaction *) bbd_reliable_ptr_base()->rxb.pbuff)) + return len; + return -EINVAL; +} + +/* + * + */ +unsigned int bbd_reliable_poll(struct file *filp, poll_table * wait) +{ + return bbd_poll(BBD_MINOR_RELIABLE, filp, wait); +} diff --git a/drivers/sensors/brcm/bbdpl1/bbd_sensor.c b/drivers/sensors/brcm/bbdpl1/bbd_sensor.c new file mode 100644 index 0000000..4158133 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/bbd_sensor.c @@ -0,0 +1,300 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#include "bbd_internal.h" + +struct bbd_control_device *bbd_control_ptr(void); + +struct bbd_sensor_device *bbd_sensor_ptr(void) +{ + if (!gpbbd_dev) + return NULL; + return (struct bbd_sensor_device *) gpbbd_dev->bbd_ptr[BBD_MINOR_SENSOR]; +} + +struct bbd_base *bbd_sensor_ptr_base(void) +{ + struct bbd_sensor_device *ps = bbd_sensor_ptr(); + if (!ps) + return NULL; + return &ps->base; +} + +/** + * initialize sensor-specific variables + * + * @pbbd_dev: bbd driver pointer + * + * @return: 0 = success, others = failure + */ + +void bbd_sensor_init_vars(struct bbd_device *dev, int* result) +{ + struct bbd_sensor_device *p = NULL; + + if (*result) + return; + + p = bbd_alloc(sizeof(struct bbd_sensor_device)); + if (!p) { + *result = -ENOMEM; + return; + } + + bbd_base_init_vars(dev, &p->base, result); + if (*result) { + bbd_free(p); + return; + } + p->priv_data = NULL; + p->callbacks = NULL; + dev->bbd_ptr[BBD_MINOR_SENSOR] = p; + p->base.name = "sensor"; +} + +int bbd_sensor_uninit(void) +{ + struct bbd_sensor_device* p = bbd_sensor_ptr(); + int freed = 0; + if (p) { + freed = bbd_base_uninit(&p->base); + bbd_free(p); + gpbbd_dev->bbd_ptr[BBD_MINOR_SENSOR] = NULL; + } + return freed; +} + +/** + * external driver registers callback function for getting data from bbd driver + * + * @ssh_data: sensor hub driver data pointer + * @pcallbacks: callback function list + */ +void bbd_register(void *ssh_data, bbd_callbacks *pcallbacks) +{ + struct bbd_sensor_device *ps = bbd_sensor_ptr(); + if (!ps) + return; + ps->priv_data = ssh_data; + ps->callbacks = pcallbacks; +} +EXPORT_SYMBOL(bbd_register); + + +/** + * send packet from external (SHMD) driver to bbd driver + * + * @pbuff: buffer pointer + * @len: buffer length + * + * @return: pushed data length = success + */ + +ssize_t bbd_send_packet(unsigned char *pbuff, size_t size) +{ + if (gpbbd_dev->shmd) + return BbdEngine_SendSensorData(&gpbbd_dev->bbd_engine, + pbuff, size); + /* Stage 1 fallback: */ + return bbd_on_read(BBD_MINOR_SENSOR, pbuff, size); +} + +EXPORT_SYMBOL(bbd_send_packet); + +/** + * pull packet from rx_list buffer directly + * + * @pbuff: buffer pointer + * @len : length of pulling data + * @timeout_ms: timeout for waiting data in rx_list buffer + * if timeout_ms is 0, pull one packet from rx_list buffer directly + * + * @return: length of buffer, -ETIMEDOUT = timeout + */ +ssize_t bbd_pull_packet(unsigned char *pbuff, + size_t len, + unsigned int timeout_ms) +{ + struct bbd_buffer *prxb = NULL; + int pull_size = 0; + int timeout = 0; + struct bbd_base* p = bbd_sensor_ptr_base(); + + if (!p || pbuff == NULL) + return -EINVAL; + + prxb = &p->rxb; + + if (prxb->buff_size <= 0) { + dprint("error : prxb->buff_size = %d\n", prxb->buff_size); + return prxb->buff_size; + } + + if (timeout_ms) { + timeout = wait_event_interruptible_timeout( + prxb->poll_inq, + (prxb->buff_size > 0), + msecs_to_jiffies(timeout_ms)); + if (!timeout) { + dprint("error : timeout for prx->poll_inq\n"); + prxb->buff_size = -ETIMEDOUT; + return -ETIMEDOUT; + } + } + + mutex_lock(&prxb->lock); + pull_size = MIN(prxb->buff_size, len); + memcpy(pbuff, prxb->pbuff, pull_size); + prxb->buff_size -= pull_size; + + /* shift. TODO: circ buf */ + { + unsigned char *top = prxb->pbuff + pull_size; + memcpy(prxb->pbuff, top, prxb->buff_size); + } + mutex_unlock(&prxb->lock); + + wake_up(&prxb->comp_inq); + + return pull_size; +} +EXPORT_SYMBOL(bbd_pull_packet); + +/** + * open bbd driver + */ +int bbd_sensor_open(struct inode *inode, struct file *filp) +{ + if (!gpbbd_dev) + return -EFAULT; + + filp->private_data = gpbbd_dev; + + return 0; +} + +/** + * close bbd driver + */ +int bbd_sensor_release(struct inode *inode, struct file *filp) +{ + if (gpbbd_dev) + return bbd_base_reinit(BBD_MINOR_SENSOR); + return -EFAULT; +} + +/** + * read sensor data from bbd_list_head to user + * (This should be used only when shmd-->bridge is disabled + */ +ssize_t bbd_sensor_read(struct file *filp, + char __user *buf, + size_t size, loff_t *ppos) +{ + return bbd_read(BBD_MINOR_SENSOR, buf, size, true); +} + +static ssize_t bbd_sensor_write_handshake(struct bbd_sensor_device *ps, + struct bbd_buffer *prxb) +{ + if (!ps->callbacks) + return 0; + + /* If registred on_packet_alarm: Inform alarm to ssh driver. + * ssh driver can pull the received data from rx_list directly + */ + if (ps->callbacks && ps->callbacks->on_packet_alarm) { + ps->callbacks->on_packet_alarm(ps->priv_data); + wake_up(&prxb->poll_inq); + } + + return 0; +} + +/** + * send data from user space to external driver + */ +ssize_t bbd_sensor_write(struct file *filp, + const char __user *buf, + size_t size, loff_t *ppos) +{ + int wr_size = MIN(BBD_MAX_DATA_SIZE, size); + struct bbd_buffer *prxb = NULL; + struct bbd_sensor_device* ps = bbd_sensor_ptr(); + struct bbd_base* p = bbd_sensor_ptr_base(); + + if (!p || !ps) + return -EINVAL; + + dprint("%s[%d]\n", __func__, (int) size); + + prxb = &p->rxb; + + mutex_lock(&prxb->lock); + if (copy_from_user(prxb->pbuff, buf, wr_size)) { + wr_size = -EFAULT; + } + else { + prxb->buff_size = wr_size; + bbd_sensor_write_handshake(ps, prxb); + } + mutex_unlock(&prxb->lock); + return wr_size; +} + + +/** + * send data from user space to external driver + */ + +ssize_t bbd_sensor_write_internal(const char *sensorData, size_t size) +{ + int wr_size; + struct bbd_buffer *prxb = NULL; + struct bbd_sensor_device* ps = bbd_sensor_ptr(); + struct bbd_base* p = bbd_sensor_ptr_base(); + + if (!p || !ps) + return -EINVAL; + + dprint("%s[%d]\n", __func__, (int) size); + + prxb = &p->rxb; + + mutex_lock(&prxb->lock); + + wr_size = MIN((BBD_MAX_DATA_SIZE - prxb->buff_size), size); + memcpy(prxb->pbuff + prxb->buff_size, sensorData, wr_size); + prxb->buff_size += wr_size; + prxb->count += wr_size; + bbd_sensor_write_handshake(ps, prxb); + + mutex_unlock(&prxb->lock); + + return wr_size; +} + +/** + * this function is for tx packet + */ +unsigned int bbd_sensor_poll(struct file *filp, poll_table * wait) +{ + return bbd_poll(BBD_MINOR_SENSOR, filp, wait); +} diff --git a/drivers/sensors/brcm/bbdpl1/bbd_sio.c b/drivers/sensors/brcm/bbdpl1/bbd_sio.c new file mode 100644 index 0000000..ab86e92 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/bbd_sio.c @@ -0,0 +1,157 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#include "bbd_internal.h" +#include "transport/bbd_engine.h" +#include "transport/bbd_bridge_c.h" + +/** + * initialize sio-specific variables + * + * @pbbd_dev: bbd driver pointer + * + * @return: 0 = success, others = failure + */ + +void bbd_sio_init_vars(struct bbd_device *dev, int* result) +{ + struct bbd_sio_device *p = NULL; + if (*result) + return; + + p = bbd_alloc(sizeof(struct bbd_sio_device)); + if (!p) { + *result = -ENOMEM; + return; + } + + bbd_base_init_vars(dev, &p->base, result); + if (*result) { + bbd_free(p); + return; + } + dev->bbd_ptr[BBD_MINOR_SIO] = p; + p->base.name = "sio"; +} + +struct bbd_sio_device* bbd_sio_ptr(void) +{ + if (!gpbbd_dev) + return NULL; + return gpbbd_dev->bbd_ptr[BBD_MINOR_SIO]; +} + +struct bbd_base* bbd_sio_ptr_base(void) +{ + struct bbd_sio_device *p = bbd_sio_ptr(); + if (!p) + return NULL; + return &p->base; +} + +int bbd_sio_uninit(void) +{ + struct bbd_sio_device* p = bbd_sio_ptr(); + int freed = 0; + if (p) { + freed = bbd_base_uninit(&p->base); + bbd_free(p); + gpbbd_dev->bbd_ptr[BBD_MINOR_SIO] = NULL; + } + return freed; +} + +/* We are passed a pointer to the bridge + * if we're active, zero otherwise + * Could consider a pass-thru flag + * from /dev/bbd_control for testing. + */ +void bbd_sio_install(struct BbdBridge* bbd) +{ + FUNC(); +} + +/** + * open bbd driver + */ +int bbd_sio_open(struct inode *inode, struct file *filp) +{ + dprint(KERN_INFO "BBD:%s()\n", __func__); + + if (!gpbbd_dev) + return -EFAULT; + + filp->private_data = gpbbd_dev; + + return 0; +} + +/** + * close bbd driver + */ +int bbd_sio_release(struct inode *inode, struct file *filp) +{ + if (!gpbbd_dev) + return -EFAULT; + + return bbd_base_reinit(BBD_MINOR_SIO); +} + +/** + * read signal/data from bbd_list_head to user + */ +ssize_t bbd_sio_read(struct file *filp, + char __user *buf, + size_t size, loff_t *ppos) +{ + return bbd_read(BBD_MINOR_SIO, buf, size, true); +} + +/** + * send data from user space to external driver + */ +ssize_t bbd_sio_write(struct file *filp, + const char __user *buf, + size_t size, loff_t *ppos) +{ + ssize_t len = bbd_write(BBD_MINOR_SIO, buf, size, true); + struct BbdEngine* pEng; + struct BbdBridge* bbd = NULL; + if (len != size) + return len; + + pEng = bbd_engine(); + if (pEng) { + bbd = &pEng->bridge; + BbdBridge_SetData(bbd, buf, size); + } else { + len = -EINVAL; + } + + return len; +} + +/** + * this function is for tx sio + */ +unsigned int bbd_sio_poll(struct file *filp, poll_table * wait) +{ + return bbd_poll(BBD_MINOR_SIO, filp, wait); +} diff --git a/drivers/sensors/brcm/bbdpl1/bbd_ssi_spi.c b/drivers/sensors/brcm/bbdpl1/bbd_ssi_spi.c new file mode 100644 index 0000000..c579b5a --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/bbd_ssi_spi.c @@ -0,0 +1,388 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Interface from BBD to the SSI SPI: + * (Broadcom Bridge Driver to the Serial Streaming Interface -SPI) + * Placeholder until actual code is ready. + */ + +#include "bbd_internal.h" +#include "transport/bbd_engine.h" +#include "transport/bbd_bridge_c.h" +#include "bcm477x_debug.h" +#include "bcm477x_ssi_spi.h" + +extern unsigned char* bcm477x_debug_buffer(size_t* len); +extern int bcm477x_debug_write(const unsigned char* buf, size_t len, int flag); +extern int bcm477x_debug_transfer(unsigned char *tx, unsigned char *rx, int len, int flag); + +extern int bbd_tty_send(unsigned char *buf, int count); +extern int bbd_tty_open(void); +extern int bbd_tty_close(void); + +int bbd_ssi_spi_open(void) +{ + return bbd_tty_open(); +} + +int bbd_ssi_spi_send(unsigned char *buf, int count) +{ + return bbd_tty_send(buf, count); +} + +//----------------------------------------------------------------------------- +// +// bbd_ssi_spi_debug +// +// Use debug mode to push download data directly into 477x memory, +// bypassing all TL, RPCs, CRC, etc. +// +//----------------------------------------------------------------------------- + +struct bbd_ssi_spi_debug_device *bbd_ssi_spi_debug_ptr(void) +{ + if (!gpbbd_dev) + return NULL; + return (struct bbd_ssi_spi_debug_device *) gpbbd_dev->bbd_ptr[BBD_MINOR_SSI_SPI_DEBUG]; +} + +struct bbd_base *bbd_ssi_spi_debug_ptr_base(void) +{ + struct bbd_ssi_spi_debug_device *ps = bbd_ssi_spi_debug_ptr(); + if (!ps) + return NULL; + return &ps->base; +} + +int bbd_ssi_spi_debug_open(struct inode *inode, struct file *filp) +{ + printk(KERN_INFO "BBD:%s()\n", __func__); + + if (!gpbbd_dev) + return -EFAULT; + + filp->private_data = gpbbd_dev; + + return 0; +} + +int bbd_ssi_spi_debug_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +//----------------------------------------------------------------------------- +// +// bbd_ssi_spi_debug_init_vars +// +//----------------------------------------------------------------------------- + +void bbd_ssi_spi_debug_init_vars(struct bbd_device* dev, int* result) +{ + struct bbd_ssi_spi_debug_device* p = NULL; + + if (*result) + return; + + p = bbd_alloc(sizeof(struct bbd_ssi_spi_debug_device)); + if (!p) { + *result = -ENOMEM; + return; + } + + bbd_base_init_vars(dev, &p->base, result); + if (*result) { + bbd_free(p); + return; + } + p->numXfers = 0; + p->numBytes = 0; + dev->bbd_ptr[BBD_MINOR_SSI_SPI_DEBUG] = p; + p->base.name = "ssi_spi_debug"; +} + +struct bbd_download_direct +{ + unsigned long addr; + unsigned char* buf; // CAUTION: user address + int size; +}; + +static +ssize_t bbd_ssi_spi_debug_data(const struct bbd_download_direct* d, + unsigned char* spi_buf, + size_t* payload_len); +static +ssize_t bbd_ssi_spi_debug_addr(const struct bbd_download_direct* d, + unsigned char* spi_buf); + + +//----------------------------------------------------------------------------- +// +// bbd_ssi_spi_debug_read +// +//----------------------------------------------------------------------------- + +ssize_t bbd_ssi_spi_debug_read( struct file *filp, + char __user *buf, + size_t size, loff_t *ppos) +{ + int status = 1; + struct bbd_download_direct d; + unsigned char* p_rx_buf; + int rx_len,tx_len; + unsigned char tx_dbg_read[256] = {0x80, 0xD1, 0x00}; + unsigned char rx_dbg_read[256] = {0x00, 0x00, 0x00}; + unsigned char tx_dbg_rfifo_depth[4] = {0xA0, 0xD1, (0x21<<1), 0x00}; + unsigned char rx_dbg_rfifo_depth[4] = {0x00, 0x00, 0x00, 0x00}; + + dprint("%s(%u)\n", __func__, size); + if (size != sizeof(d)) + return -EINVAL; + + if (copy_from_user(&d, buf, size)) + return -EFAULT; + + p_rx_buf = &d.buf[0]; + rx_len = d.size; + tx_len = 0; + + tx_dbg_read[3] = (unsigned char)(d.addr & 0xff); + tx_dbg_read[4] = (unsigned char)((d.addr >> 8) & 0xff); + tx_dbg_read[5] = (unsigned char)((d.addr >> 16) & 0xff); + tx_dbg_read[6] = (unsigned char)((d.addr >> 24) & 0xff); + + tx_dbg_read[7] = (unsigned char)(d.size & 0xff); + tx_dbg_read[8] = (unsigned char)((d.size >> 8) & 0xff); + + tx_dbg_read[9] = 1; /* Bit0: 0:disable/done, 1:enable */ + + bcm477x_debug_transfer(tx_dbg_read,rx_dbg_read,10,0); + + while ( rx_len && status ) { + /* Read target RFIFO Depth to determine how much we can read from it */ + bcm477x_debug_transfer(tx_dbg_rfifo_depth,rx_dbg_rfifo_depth,4,0); + + status = (int)rx_dbg_rfifo_depth[2]; + if ( status ) { + memset(tx_dbg_read, 0x00, sizeof(tx_dbg_read)); + memset(rx_dbg_read, 0x00, sizeof(rx_dbg_read)); + + /* FIXME: 0xA0, 0xD1 ? probably 0xA0, 0xD0 brcause it's read operation */ + tx_dbg_read[0] = 0xA0; + tx_dbg_read[1] = 0xD1; + tx_dbg_read[3] = (0x22 << 1); /* RFIFO Read Data */ + + bcm477x_debug_transfer(tx_dbg_read,rx_dbg_read,status + 3,0); + + if ( rx_len < status ) status = rx_len; /* To be safe only */ + if (copy_to_user(buf + tx_len, (void *)rx_dbg_read + 2, status)) { + return -EFAULT; + } + + rx_len -= status; + tx_len += status; + } + } + + return d.size - rx_len; +} + +//----------------------------------------------------------------------------- +// +// bbd_ssi_spi_debug_write +// +//----------------------------------------------------------------------------- + +ssize_t bbd_ssi_spi_debug_write(struct file *filp, + const char __user *buf, + size_t size, loff_t *ppos) +{ + struct bbd_download_direct d; + + dprint("%s(%u)\n", __func__, size); + if (size != sizeof(d)) + return -EINVAL; + + if (copy_from_user(&d, buf, size)) + return -EFAULT; + + do + { + ssize_t spi_buf_len = -__LINE__; + unsigned char* spi_buf = bcm477x_debug_buffer(&spi_buf_len); + size_t payload_len = 0; + int written = 0; + + if (!spi_buf || spi_buf_len < sizeof(d)) + return -EINVAL; + + d.size += 16 - ((d.size + 3) % 16); + + /* Send the addr+len+DMA start command: */ + spi_buf_len = bbd_ssi_spi_debug_addr(&d, spi_buf); + dprint("%s(%d)/%d\n", __func__, spi_buf_len,__LINE__); + if (spi_buf_len < 1) + return spi_buf_len; + + written = bcm477x_debug_write(spi_buf, spi_buf_len, 0); + dprint("%s(%d)/%d %d\n", __func__, spi_buf_len,__LINE__, written); + if (written != spi_buf_len) + return -EINVAL; + + /* Send the DATA: */ + spi_buf_len = bbd_ssi_spi_debug_data(&d, spi_buf, &payload_len); + dprint("%s(%d)/%d\n", __func__, spi_buf_len,__LINE__); + if (spi_buf_len < 0) + return spi_buf_len; + + written = bcm477x_debug_write(spi_buf, spi_buf_len, 0); + dprint("%s(%d)/%d %d\n", __func__, spi_buf_len,__LINE__, written); + if (written != spi_buf_len) + return -EINVAL; + + d.size -= payload_len; + d.addr += payload_len; + d.buf += payload_len; + } while (d.size > 0); + + return size; +} + +//----------------------------------------------------------------------------- +// +// bbd_ssi_spi_debug_addr +// bbd_ssi_spi_debug_data +// +// Fill the buffer with SSI-debug data. +// Do two transfers inside one transaction: +// a) DebugMode(addr+len) +// b) DebugMode(data) +// +//----------------------------------------------------------------------------- + +/* True overhead for SSI: (no b_data[1]) */ +#define SSI_DEBUG_OVERHEAD 4 + +#define MAX_TX_DEBUG_BUF_SIZE (MAX_TX_RX_BUF_SIZE - SSI_DEBUG_OVERHEAD) + +#define SSI_SPI_SLAVE_ADDR 0x68 /* From Romi's spec. */ +#define SSI_SPI_SLAVE_WR 0x01 +#define SSI_SPI_SLAVE_RD 0x00 + +#define SSI_SPI_SLAVE ((SSI_SPI_SLAVE_ADDR << 1) | SSI_SPI_SLAVE_WR) + +#define SSI_SPI_DMA_START_ADDR 0x00 +#define SSI_I2C_DMA_DATA_ADDR 0x12 /* From Romi's I2C DEBUG spec. */ +#define SSI_SPI_DMA_DATA_ADDR (SSI_I2C_DMA_DATA_ADDR << 1) + +#define DMA_CONTROL_ENABLE 0x1 +#define DMA_CONTROL_WRITE 0x2 +#define DMA_CONTROL (DMA_CONTROL_ENABLE | DMA_CONTROL_WRITE) + +static ssize_t bbd_ssi_spi_debug_addr(const struct bbd_download_direct* d, + unsigned char* spi_buf) +{ + struct Bcm477x_Debug_Xfer* x = (struct Bcm477x_Debug_Xfer*) spi_buf; + struct bbd_ssi_spi_debug_device* p = bbd_ssi_spi_debug_ptr(); + + union long_union + { + unsigned char uc[sizeof(unsigned long)]; + unsigned long ul; + } swap_addr; + + union short_union + { + unsigned char uc[sizeof(unsigned short)]; + unsigned short us; + } swap_len; + + int len = d->size; + if (len > MAX_TX_DEBUG_BUF_SIZE) + len = MAX_TX_DEBUG_BUF_SIZE; + + x->a_mode = (SSI_MODE_DEBUG | SSI_MODE_HALF_DUPLEX | SSI_WRITE_TRANS); + x->a_cmd = SSI_SPI_SLAVE; + x->a_spi_addr = SSI_SPI_DMA_START_ADDR; + + swap_addr.ul = d->addr; + x->a_addr[0] = swap_addr.uc[0]; + x->a_addr[1] = swap_addr.uc[1]; + x->a_addr[2] = swap_addr.uc[2]; + x->a_addr[3] = swap_addr.uc[3]; + + swap_len.us = len; + x->a_len[0] = swap_len.uc[0]; + x->a_len[1] = swap_len.uc[1]; + x->a_dma = DMA_CONTROL; + + len = 10; + if (p) { + memcpy(&p->trace, x, len); + ++p->numXfers; + } + + if (bbd_debug()) { + const unsigned char* p = spi_buf; + printk("bbd_ssi_spi_debug_addr(0x%lx, 0x%lx[%u] = \n", + (unsigned long)d->addr, (unsigned long)d->buf, d->size); + printk(" a { %02X %02X %02X %02X.%02X.%02X.%02X %02X.%02X %02X }\n", + p[0],p[1],p[2], p[3],p[4],p[5],p[6], p[7],p[8], p[9] ); + // Expect: 80, D1, 00, address(byte-swap) len(swap), 03 + } + return len; +} + +static ssize_t bbd_ssi_spi_debug_data(const struct bbd_download_direct* d, + unsigned char* spi_buf, + size_t* payload_len) +{ + struct Bcm477x_Debug_Xfer* x = (struct Bcm477x_Debug_Xfer*) spi_buf; + struct bbd_ssi_spi_debug_device* p = bbd_ssi_spi_debug_ptr(); + + int len = d->size; + if (len > MAX_TX_DEBUG_BUF_SIZE) + len = MAX_TX_DEBUG_BUF_SIZE; + + if (!payload_len) + return -EINVAL; + + x->a_mode = (SSI_MODE_DEBUG | SSI_MODE_HALF_DUPLEX | SSI_WRITE_TRANS); + x->a_cmd = SSI_SPI_SLAVE; + x->a_spi_addr = SSI_SPI_DMA_DATA_ADDR; + + if (copy_from_user(x->a_addr, d->buf, len)) + return -EFAULT; + + if (p) + { + memcpy(&p->trace.b_mode, x, 4); + p->numBytes += len; + } + + if (bbd_debug()) + { + const unsigned char* p = spi_buf; + printk("bbd_ssi_spi_debug_data(0x%lx, 0x%lx[%u] = \n", + (unsigned long)d->addr, (unsigned long)d->buf, d->size); + printk(" b { %02X %02X %02X %02X,%02X,%02X,%02X... }[%u]\n", + p[0],p[1],p[2], p[3],p[4],p[5],p[6], len); + } + *payload_len = len; + return len + 3; +} diff --git a/drivers/sensors/brcm/bbdpl1/bbd_sysfs.c b/drivers/sensors/brcm/bbdpl1/bbd_sysfs.c new file mode 100644 index 0000000..15ceab6 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/bbd_sysfs.c @@ -0,0 +1,324 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#include +#include +#include +#include +#include +#include +#include + +#include /* printk() */ +#include /* kmalloc() */ +#include /* everything */ +#include /* error codes */ +#include /* size_t */ +#include +#include /* O_ACCMODE */ +#include +#include +#include +#include + +#include +#include +#include + +#include "bbd_internal.h" +#include "transport/transport_layer_c.h" + +struct bbd_control_device* bbd_control_ptr (void); +struct bbd_base* bbd_sensor_ptr_base(void); +struct bbd_sensor_device* bbd_sensor_ptr (void); +ssize_t bbd_ESW_control (char *buf, ssize_t len); +struct bbd_ssi_spi_debug_device* bbd_ssi_spi_debug_ptr(void); + +/***************************************************************************** +* +* sysfs for bbd +* sysfs directory : /sys/devices/platform/bbd/ +* files : BBD = send control to lhd +* ESW = send control to ssp +* bbd_info = display information of bbd driver +* +******************************************************************************/ + +static ssize_t store_sysfs_BBD_control(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + bbd_on_read(BBD_MINOR_CONTROL, buf, len); + return len; +} + +static ssize_t store_sysfs_ESW_control(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct bbd_sensor_device* ps = bbd_sensor_ptr(); + /* struct bbd_device *gpbbd_dev = dev_get_drvdata(dev); */ + + if (!ps || !ps->callbacks) { + printk(KERN_ERR "%s():callbacks not registered.\n", __func__); + return -EFAULT; + } + + { + char str_ctrl[BBD_ESW_CTRL_MAX_STR_LEN]; + sscanf(buf, "%s", str_ctrl); + bbd_ESW_control(str_ctrl, strlen(buf) + 1); + } + return len; +} + +/* Same as ESW, except callbacks can be missing. + */ + +static ssize_t store_sysfs_DEV_control(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct bbd_sensor_device* ps = bbd_sensor_ptr(); + if (ps) { + char str_ctrl[BBD_ESW_CTRL_MAX_STR_LEN]; + sscanf(buf, "%s", str_ctrl); + bbd_ESW_control(str_ctrl, strlen(buf) + 1); + } + else + len = -EFAULT; + return len; +} + +static ssize_t show_sysfs_bbd_shmd(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bbd_base* p = bbd_sensor_ptr_base(); + struct bbd_queue *ptxq = NULL; + struct bbd_buffer *prxb = NULL; + int len = 0; + + if (!p) + return -EFAULT; + + ptxq = &p->txq; + prxb = &p->rxb; + + len = sprintf(buf, "Tx:total=%dB,tout=%d, Rx:total=%dB, tout=%d\n", + ptxq->count, ptxq->tout_cnt, + prxb->count, prxb->tout_cnt); + return len; +} + +static ssize_t show_sysfs_bbd_debug(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (!gpbbd_dev) + return 0; + return sprintf(buf, "%d", gpbbd_dev->db); +} + +static ssize_t store_sysfs_bbd_debug(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + int debug = 0; + if (!gpbbd_dev) + return 0; + sscanf(buf, "%d", &debug); + gpbbd_dev->db = debug; + return len; +} + +static ssize_t show_sysfs_bbd_baud(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (!gpbbd_dev) + return 0; + return sprintf(buf, "%d", gpbbd_dev->tty_baud); +} + +static ssize_t store_sysfs_bbd_baud(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + int tty_baud = 0; + if (!gpbbd_dev) + return 0; + sscanf(buf, "%d", &tty_baud); + gpbbd_dev->tty_baud = tty_baud; + return len; +} + +static ssize_t show_sysfs_bbd_passthru(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (!gpbbd_dev) + return 0; + return sprintf(buf, "%d", + gpbbd_dev->bbd_engine.bridge.m_otTL.m_bPassThrough); +} + +static ssize_t show_sysfs_bbd_pl(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + const struct stTransportLayerStats *p = NULL; + if (!gpbbd_dev) + return 0; + p = TransportLayer_GetStats(&gpbbd_dev->bbd_engine.bridge.m_otTL); + if (!p) + return 0; + + return sprintf(buf, + "RxGarbageBytes=%lu\n" + "RxPacketLost=%lu\n" + "RemotePacketLost=%lu\n" + "RemoteGarbage=%lu\n" + "PacketSent=%lu\n" + "PacketReceived=%lu\n" + "AckReceived=%lu\n" + "ReliablePacketSent=%lu\n" + "ReliableRetransmit=%lu\n" + "ReliablePacketReceived=%lu\n" + "MaxRetransmitCount=%lu\n", + p->ulRxGarbageBytes, + p->ulRxPacketLost, + p->ulRemotePacketLost, /* approximate */ + p->ulRemoteGarbage, /* approximate */ + p->ulPacketSent, /* number of normal packets sent */ + p->ulPacketReceived, + p->ulAckReceived, + p->ulReliablePacketSent, + p->ulReliableRetransmit, + p->ulReliablePacketReceived, + p->ulMaxRetransmitCount); +} + +/* Show the number of buffers + */ + +int bbd_count(int minor); +int bbd_alloc_count(void); + +static ssize_t show_sysfs_bbd_buf(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + if (!gpbbd_dev) + return 0; + return sprintf(buf, + "sensor=%d\n" + "control=%d\n" + "packet=%d\n" + "reliable=%d\n" + "freed=%d\n" + "nAlloc=%d\n", + bbd_count(BBD_MINOR_SENSOR), + bbd_count(BBD_MINOR_CONTROL), + bbd_count(BBD_MINOR_PACKET), + bbd_count(BBD_MINOR_RELIABLE), + gpbbd_dev->qcount, + bbd_alloc_count()); +} + +static ssize_t show_sysfs_bbd_ssi_xfer(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bbd_ssi_spi_debug_device* p = bbd_ssi_spi_debug_ptr(); + if (!gpbbd_dev || !p) + return 0; + return sprintf(buf, "%lu\n", p->numXfers); +} + +static ssize_t show_sysfs_bbd_ssi_count(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bbd_ssi_spi_debug_device* p = bbd_ssi_spi_debug_ptr(); + if (!gpbbd_dev || !p) + return 0; + return sprintf(buf, "%lu\n", p->numBytes); +} + +static ssize_t show_sysfs_bbd_ssi_trace(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bbd_ssi_spi_debug_device* d = bbd_ssi_spi_debug_ptr(); + const unsigned char* p = NULL; + if (!gpbbd_dev || !d || !d->numXfers) + return 0; + p = (const unsigned char*) &d->trace; + return sprintf(buf, + "a { %02X %02X %02X %02X.%02X.%02X.%02X %02X.%02X %02X }\n" + "b { %02X %02X %02X %02X... }\n", + p[ 0],p[ 1],p[ 2], p[ 3],p[4],p[5],p[6], p[7],p[8], p[9], + p[10],p[11],p[12], p[13]); +} + +static DEVICE_ATTR(BBD, 0640, NULL, store_sysfs_BBD_control); +static DEVICE_ATTR(ESW, 0640, NULL, store_sysfs_ESW_control); +static DEVICE_ATTR(DEV, 0640, NULL, store_sysfs_DEV_control); +static DEVICE_ATTR(debug, 0640, show_sysfs_bbd_debug,store_sysfs_bbd_debug); +static DEVICE_ATTR(baud, 0640, show_sysfs_bbd_baud, store_sysfs_bbd_baud); +static DEVICE_ATTR(shmd, 0640, show_sysfs_bbd_shmd, NULL); +static DEVICE_ATTR(pl, 0640, show_sysfs_bbd_pl, NULL); +static DEVICE_ATTR(buf, 0640, show_sysfs_bbd_buf, NULL); +static DEVICE_ATTR(passthru,0640, show_sysfs_bbd_passthru, NULL); +static DEVICE_ATTR(ssi_xfer, 0640, show_sysfs_bbd_ssi_xfer, NULL); +static DEVICE_ATTR(ssi_count, 0640, show_sysfs_bbd_ssi_count, NULL); +static DEVICE_ATTR(ssi_trace, 0640, show_sysfs_bbd_ssi_trace, NULL); + +static struct attribute *bbd_esw_attributes[] = { + &dev_attr_BBD.attr, + &dev_attr_ESW.attr, + &dev_attr_DEV.attr, + &dev_attr_shmd.attr, + &dev_attr_debug.attr, + &dev_attr_baud.attr, + &dev_attr_pl.attr, + &dev_attr_buf.attr, + &dev_attr_passthru.attr, + &dev_attr_ssi_xfer.attr, + &dev_attr_ssi_count.attr, + &dev_attr_ssi_trace.attr, + NULL +}; + +static const struct attribute_group bbd_esw_group = { + .attrs = bbd_esw_attributes, +}; + +int bbd_sysfs_init(struct kobject *kobj) +{ + int ret = sysfs_create_group(kobj, &bbd_esw_group); + if (ret < 0) { + printk(KERN_ERR "%s():couldn't register current attribute\n", + __func__); + } + return ret; +} + +void bbd_sysfs_uninit(struct kobject *kobj) +{ + sysfs_remove_group(kobj, &bbd_esw_group); +} diff --git a/drivers/sensors/brcm/bbdpl1/bbd_tty.c b/drivers/sensors/brcm/bbdpl1/bbd_tty.c new file mode 100644 index 0000000..7f252fa --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/bbd_tty.c @@ -0,0 +1,463 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD TTY (Broadcom Bridge TTY Driver) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "bbd_internal.h" +#include "transport/bbd_engine.h" +#include "transport/bbd_bridge_c.h" + +void BbdEngine_Open(struct BbdEngine *p); +void BbdEngine_Close(struct BbdEngine *p); + +/*------------------------------------------------------------------------------ + * + * internal static data + * + *------------------------------------------------------------------------------ + */ + +static struct file *tty; +static bool sent_autobaud; +static atomic_t bbd_tty_run; + +/*------------------------------------------------------------------------------ + * + * Customization + * + * 1) Autobaud sequence + * 2) portname, speed, etc. + * 3) Stop condition + * + *------------------------------------------------------------------------------ + */ + +#define GN3 +#define OPT_WORK_QUEUE +#define BBD_USE_AUTOBAUD_not + +#ifdef OPT_WORK_QUEUE +static struct workqueue_struct *workq; +#endif +static struct work_struct bbd_tty_read_work; + + +#ifdef GN3 /* { */ + +#define PORT_NAME "/dev/ttyBCM0"; + +static bool bbd_tty_ioctl(struct file *f, int config, unsigned long param, + const char* msg) +{ + return (tty_ioctl(f, config, param) >= 0); +} + +#else /* } else not GN3 { */ + +#define PORT_NAME "/dev/ttyO1"; + +static bool bbd_tty_ioctl(struct file *f, int config, unsigned long param, + const char* msg) +{ + int result = f->f_op->unlocked_ioctl(f, config, param); + dprint("%s(%p,%d,0x%lx) %s = %d\n", + __func__, f, config, param, msg, result); + return result >= 0; +} + +#endif /* } // GN3 */ + + +/* configure port to use a custom speed. + * Set the custom divisor using ioctl(TIOSSERIAL) + * Set the speed to 38400 to use the custom speed. + */ + +static bool bbd_tty_setcustomspeed(struct file *f, int speed) +{ + bool result = true; + + struct serial_struct ss; + int closestSpeed = 0; + if (!bbd_tty_ioctl(f, TIOCGSERIAL, (unsigned long) &ss, "TIOCGSERIAL")) + result = false; + + ss.flags = (ss.flags & ~ASYNC_SPD_MASK) | ASYNC_SPD_CUST; + ss.custom_divisor = (ss.baud_base + (speed / 2)) / speed; + closestSpeed = ss.baud_base / ss.custom_divisor; + + if ( closestSpeed < speed * 98 / 100 + || closestSpeed > speed * 102 / 100) { + dprint("BBDTTY: Cannot set serial port speed to %d. " + "Closest possible is %d\n", + speed, closestSpeed); + } + return bbd_tty_ioctl(f, TIOCSSERIAL, (unsigned long) &ss, "TIOCSSERIAL"); +} + + + + + + +/*------------------------------------------------------------------------------ + * + * bbd_tty_setspeed + * + *------------------------------------------------------------------------------ + */ + +static bool bbd_tty_setspeed(struct file *f, int speed) +{ + mm_segment_t oldfs; + bool result = true; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + { + /* Set speed */ + struct termios settings; + + if (!bbd_tty_ioctl(f, TCGETS, (unsigned long)&settings, + "TCGETS")) + result = false; + settings.c_iflag = 0; + settings.c_oflag = 0; + settings.c_lflag = 0; + settings.c_cflag = CRTSCTS | CLOCAL | CS8 | CREAD; + settings.c_cc[VMIN ] = 0; + settings.c_cc[VTIME] = 1; /* 100ms timeout */ + switch (speed) { + case 2400: + settings.c_cflag |= B2400; + break; + case 4800: + settings.c_cflag |= B4800; + break; + case 9600: + settings.c_cflag |= B9600; + break; + case 19200: + settings.c_cflag |= B19200; + break; + case 38400: + settings.c_cflag |= B38400; + break; + case 57600: + settings.c_cflag |= B57600; + break; + case 115200: + settings.c_cflag |= B115200; + break; + case 921600: + settings.c_cflag |= B921600; + break; + case 1000000: + settings.c_cflag |= B1000000; + break; + case 1152000: + settings.c_cflag |= B1152000; + break; + case 1500000: + settings.c_cflag |= B1500000; + break; + case 2000000: + settings.c_cflag |= B2000000; + break; + case 2500000: + settings.c_cflag |= B2500000; + break; + case 3000000: + settings.c_cflag |= B3000000; + break; + case 3500000: + settings.c_cflag |= B3500000; + break; + case 4000000: + settings.c_cflag |= B4000000; + break; + default: + bbd_tty_setcustomspeed(f, speed); + settings.c_cflag |= B38400; + break; + } + if (!bbd_tty_ioctl(f, TCSETS, (unsigned long)&settings, + "TCSETS")) + result = false; + } + { + /* Set low latency */ + struct serial_struct settings; + + if (!bbd_tty_ioctl(f, TIOCGSERIAL, (unsigned long)&settings, + "TIOCGSERIAL")) + result = false; + settings.flags |= ASYNC_LOW_LATENCY; + if (!bbd_tty_ioctl(f, TIOCSSERIAL, (unsigned long)&settings, + "TIOCSSERIAL")) + result = false; + } + + set_fs(oldfs); + return result; +} + +/*------------------------------------------------------------------------------ + * + * bbd_tty_close + * + *------------------------------------------------------------------------------ + */ + +static inline struct tty_struct *file_tty(struct file *file) +{ + return ((struct tty_file_private *)file->private_data)->tty; +} + +int bbd_tty_close(void) +{ + if (!atomic_read(&bbd_tty_run)) { + dprint("BBDTTY: already closed\n"); + return 0; + } + + /* This is needed for JTAG download (without LHD) */ + BbdEngine_Close(&gpbbd_dev->bbd_engine); + + sent_autobaud = false; + atomic_set(&bbd_tty_run, 0); + + dprint("BBDTTY: %s TTY %p ERR %ld\n", __func__, tty, IS_ERR(tty)); + +#ifdef OPT_WORK_QUEUE + if (workq) { + struct tty_struct *tty_st = file_tty(tty); + set_bit(TTY_OTHER_CLOSED, &tty_st->flags); + wake_up_interruptible(&tty_st->read_wait); + wake_up_interruptible(&tty_st->write_wait); + + cancel_work_sync(&bbd_tty_read_work); + } +#endif + if ((tty != 0) && !IS_ERR(tty)) { + filp_close(tty, 0); /* REVIEW: should cause read() to exit */ + tty = 0; + } + +#ifdef OPT_WORK_QUEUE + if (workq) { + dprint("BBDTTY: %s()/%d\n", __func__, __LINE__); + destroy_workqueue(workq); + workq = 0; + } +#endif + return 0; +} + +/*------------------------------------------------------------------------------ + * + * bbd_tty_read + * + *------------------------------------------------------------------------------ + */ + +static int bbd_tty_read(struct file *f, int maxRead, unsigned char *ch) +{ + int result = -1; + + if (!IS_ERR(f)) { + mm_segment_t oldfs = get_fs(); + set_fs(KERNEL_DS); + + f->f_pos = 0; + result = f->f_op->read(f, ch, maxRead, &f->f_pos); + set_fs(oldfs); + } + + return result; +} + +/*------------------------------------------------------------------------------ + * + * bbd_tty_write + * + *------------------------------------------------------------------------------ + */ + +static int bbd_tty_write(struct file *f, unsigned char *buf, int count) +{ + int result = -1; + mm_segment_t oldfs; + bbd_log_hex(__func__, buf, count); + oldfs = get_fs(); + set_fs(KERNEL_DS); + f->f_pos = 0; + result = f->f_op->write(f, buf, count, &f->f_pos); + set_fs(oldfs); + + return result; +} + +/*------------------------------------------------------------------------------ + * + * bbd_tty_read_func + * + *------------------------------------------------------------------------------ + */ + +static void bbd_tty_read_func(struct work_struct *work) +{ + struct BbdEngine *pEng = 0; + + while (atomic_read(&bbd_tty_run) && tty) { + char buf[256]; + int nRead = bbd_tty_read(tty, sizeof(buf), buf); + if (nRead <= 0) + continue; + pEng = bbd_engine(); + + bbd_log_hex(__func__, buf, nRead); + if (pEng && atomic_read(&bbd_tty_run)) + BbdBridge_SetData(&pEng->bridge, buf, nRead); + else + dprint("BBDTTY: bbdengine %p run %d, rx ignored\n", + pEng, atomic_read(&bbd_tty_run)); + } + dprint("BBDTTY: exit\n"); +} + +/*------------------------------------------------------------------------------ + * + * bbd_tty_open + * + *------------------------------------------------------------------------------ + */ + +int bbd_tty_open(void) +{ + int result = 0; + char *portname = PORT_NAME; + + if (atomic_read(&bbd_tty_run)) { + dprint("BBDTTY: already opened\n"); + return 0; + } + + dprint("BBDTTY: open(%s)\n", portname); + + tty = filp_open(portname, O_RDWR, 0); + if (IS_ERR(tty)) { + result = (int)PTR_ERR(tty); + dprint("serial_2002: open(%s) error = %d\n", + portname, result); + } + else { + bool ok = false; + dprint("BBDTTY: open(%s) OK\n", portname); + ok = bbd_tty_setspeed(tty, gpbbd_dev->tty_baud); + dprint("BBDTTY: setspeed(%d) %s\n", gpbbd_dev->tty_baud, + ok ? "OK" : "FAIL"); + atomic_set(&bbd_tty_run, 1); +#ifdef OPT_WORK_QUEUE + { + int q = 0; + workq = create_singlethread_workqueue("BbdTty"); + INIT_WORK(&bbd_tty_read_work, bbd_tty_read_func); + q = queue_work(workq, &bbd_tty_read_work); + dprint("BBDTTY: BbdTty worker %s\n", + (q) ? "Already running" : "OK"); + } +#else + { + INIT_WORK(&bbd_tty_read_work, bbd_tty_read_func); + schedule_work(&bbd_tty_read_work); + dprint("BBDTTY: worker OK\n"); + } +#endif + /* Needed for JTAG download of ESW without LH daemon */ + BbdEngine_Open(&gpbbd_dev->bbd_engine); + } + return result; +} + +/*------------------------------------------------------------------------------ + * + * bbd_tty_autobaud + * + *------------------------------------------------------------------------------ + */ + +int bbd_tty_autobaud(void) +{ + int res = -1; + + if (!tty || IS_ERR(tty)) + return res; + + if (!atomic_read(&bbd_tty_run)) + return res; + +#ifdef BBD_USE_AUTOBAUD + res = bbd_tty_write(tty, autobaud, sizeof(autobaud)); + sent_autobaud = (res == sizeof(autobaud)); + dprint("BBDTTY: autobaud %d res %d\n", sent_autobaud, res); +#else + res = 0; +#endif + return res; +} + +/*------------------------------------------------------------------------------ + * + * bbd_tty_send + * + *------------------------------------------------------------------------------ + */ + +int bbd_tty_send(unsigned char *buf, int count) +{ + int res = -1; + + if (!tty || IS_ERR(tty)) + return res; + + if (!atomic_read(&bbd_tty_run)) + return res; + +#ifdef BBD_USE_AUTOBAUD + if (!sent_autobaud) { + res = bbd_tty_autobaud(); + } +#endif + + res = bbd_tty_write(tty, buf, count); + return res; +} + +void __init bbd_tty_init(void) +{ + atomic_set(&bbd_tty_run, 0); +} diff --git a/drivers/sensors/brcm/bbdpl1/bcm477x_debug.h b/drivers/sensors/brcm/bbdpl1/bcm477x_debug.h new file mode 100644 index 0000000..09c3cab --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/bcm477x_debug.h @@ -0,0 +1,16 @@ +#ifndef _BCM477x_DEBUG_ +#define _BCM477x_DEBUG_ + +#define MAX_BCM477X_DEBUG_BUFFER_SIZE (64*1024) + +struct bcm477x_debug +{ + bool is_opened; + + bool buf_full; + unsigned long buf_head; + unsigned long buf_tail; + unsigned char *buf; +}; + +#endif /* _BCM477x_DEBUG_ */ diff --git a/drivers/sensors/brcm/bbdpl1/bcm477x_ssi_spi.h b/drivers/sensors/brcm/bbdpl1/bcm477x_ssi_spi.h new file mode 100644 index 0000000..94000d1 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/bcm477x_ssi_spi.h @@ -0,0 +1,41 @@ +#ifndef _BCM477x_SSI_SPI_H_ +#define _BCM477x_SSI_SPI_H_ +#define MAX_TX_RX_BUF_SIZE (2048 + 2) + +enum +{ + SSI_MODE_STREAM = 0 + ,SSI_MODE_DEBUG = 0x80 +}; + +enum +{ + SSI_MODE_HALF_DUPLEX = 0 + ,SSI_MODE_FULL_DUPLEX = 0x40 +}; + +enum +{ + SSI_WRITE_TRANS = 0x0 + ,SSI_READ_TRANS = 0x20 +}; + +struct bcm477x_platform_data { + unsigned int host_req_gpio; /* HOST_REQ : to indicate that ASIC has data to send. */ +}; + +typedef int (*bcm477x_on_read_cb)(unsigned char *buf, size_t len); + +struct bcm477x_ssi_spi +{ + bool is_opened; + bool is_driver_loaded; + struct spi_device *spi; + + bcm477x_on_read_cb on_read; +}; + +extern int bcm477x_open(bcm477x_on_read_cb on_read); +extern int bcm477x_write(const unsigned char* buf, size_t len, int flag); + +#endif /* _BCM477x_SSI_SPI_H_ */ diff --git a/drivers/sensors/brcm/bbdpl1/bcm_gps_spi.c b/drivers/sensors/brcm/bbdpl1/bcm_gps_spi.c new file mode 100644 index 0000000..42c245c --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/bcm_gps_spi.c @@ -0,0 +1,1719 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bcm_gps_spi.h" +#include "bbd_internal.h" + +#include +#include + +#include +#include +#include + +#define xCONFIG_SPI_BCM4773_DMA 0 +#define CONFIG_SPI_DMA_BITS_PER_WORD 32 +#define xCONFIG_SPI_NO_FULL_DUPLEX 0 +#define CONFIG_SPI_NO_IDENTIFY 1 +#define CONFIG_SPI_NO_AUTOBAUD 0 +#define CONFIG_DDS +#define CONFIG_4WORD_BURST + + +#ifdef CONFIG_SPI_DMA_BITS_PER_WORD +#define CONFIG_SPI_DMA_BYTES_PER_WORD (CONFIG_SPI_DMA_BITS_PER_WORD >> 3) +#else +#define CONFIG_SPI_DMA_BYTES_PER_WORD 1 +#endif + +#ifdef CONFIG_4WORD_BURST +#define WORD_BURST_SIZE 4 +#else +#define WORD_BURST_SIZE 1 +#endif + + +#ifdef CONFIG_SPI_BCM4773_DMA /* Should be defined in the configuration file, e.g. arm/arch/panda_defconfig or drivers/char/Kconfig */ +#include +#endif + +#define GPS_VERSION "1.26" +#define PFX "bcmgps:" +/* FIXME: Temporary disabled. Do we need this code at all ? */ + + +#ifdef DEBUG +#define xDEBUG_PROFILE 1 +#define xDEBUG_SET_TERMIOS 0 +#define DEBUG_TRANSFER 0 +#define xDEBUG_TRANSFER__READ_TRANSMIT 0 +#define xDEBUG_TRANSFER__RXTX_WORK 0 +#define xDEBUG_TX 0 +#define xDEBUG_IRQ_HANDLER 0 +#define xDEBUG_RX 0 +#define xDEBUG_INSERT_FLIP_STRING 0 +#define xDEBUG_DDS_GPIO 0 + +#ifdef DEBUG_TRANSFER +bool ssi_dbg; +static void pk_log(const struct device *dev,char* dir,unsigned char cmd_stat, char* data, int len, int transferred, short status); +#endif //DEBUG_TRANSFER +#ifdef DEBUG_PROFILE +unsigned long clock_get_us(void); +struct bcm4773_profile +{ + unsigned long count[2]; + unsigned long total_time[2]; + unsigned long total_size[2]; + unsigned long min_time[2]; + unsigned long max_time[2]; + unsigned long rxtx_work[2]; // rxtx_work_profile + unsigned long rxtx_enter[2]; // +}; + +#define WRITE_TRANS_NUM 500 + +struct bcm4773_profile_collect_data +{ + int trans_type; // 0 - DMA, 1 - GPIO + int io; // 0 - W, 1 - R + unsigned long len; + unsigned long time; + unsigned long start_time; +}; + +int trans = 0; +int bcm4773_trans = 0; +struct bcm4773_profile profile_rxtx_work; +struct bcm4773_profile profile_spi_sync; + +static void bcm4773_init_the_profile(struct bcm4773_profile *p) +{ + p->count[0]=0; p->count[1]=0; + p->total_time[0]=0; p->total_time[1]=0; + p->total_size[0]=0; p->total_size[1]=0; + p->min_time[0]=0; p->min_time[1]=0; + p->max_time[0]=0; p->max_time[1]=0; + + p->rxtx_work[0]=0; p->rxtx_work[1]=0; // rxtx_work_profile + p->rxtx_enter[0]=0; p->rxtx_enter[1]=0; // + +} + +#if DEBUG_PROFILE==2 +static void bcm4773_get_freq(unsigned long bit_len,unsigned long time,unsigned long *d,unsigned long *d1,unsigned long *d2 ) +{ + unsigned long n; + + *d = bit_len / time; + n = (bit_len - (*d * time)) * 10; + *d1 = n / time; + *d1 %= 10; + n = (*d - (*d1 * time)) * 10; + *d2 = n / time; + *d2 %= 10; +} +#endif + +static void bcm4773_profile_print(struct uart_port *port,const char *func,long line) +{ + pr_debug(PFX "[SSPBBD]: %s.01 rxtx_work: count %ld time %ld in %s(%ld)\n",__func__, + profile_rxtx_work.count[0], profile_rxtx_work.total_time[0], + func, line); + +#if DEBUG_PROFILE==2 + pr_debug(PFX "[SSPBBD]: %s.02.1 spi_sync: W: count %ld time %ld, R: count %ld time %ld\n",__func__, + profile_spi_sync.count[0], profile_spi_sync.total_time[0], + profile_spi_sync.count[1], profile_spi_sync.total_time[1]); +#endif + + pr_debug(PFX "[SSPBBD]: %s.02.2 rxtx_start: tx %ld(%ld) irq %ld(%ld), rxtx_work: tx %ld(%ld) irq %ld(%ld)\n",__func__, + profile_spi_sync.rxtx_work[0],profile_spi_sync.rxtx_enter[0],profile_spi_sync.rxtx_work[1],profile_spi_sync.rxtx_enter[1], // rxtx_work_profile + profile_rxtx_work.rxtx_work[0],profile_rxtx_work.rxtx_enter[0],profile_rxtx_work.rxtx_work[1],profile_rxtx_work.rxtx_enter[1]); + + pr_debug(PFX "[SSPBBD]: %s.03 uart_port: RX: icnt=%lu be=%lu oe=%lu, TX: icnt=%lu ff=%lu\n",__func__, + (unsigned long)port->icount.rx, + (unsigned long)port->icount.brk, + (unsigned long)port->icount.overrun, + (unsigned long)port->icount.tx, + (unsigned long)port->icount.buf_overrun); +} + +static void bcm4773_profile_init(struct uart_port *port) +{ + trans = 0; + port->icount.rx = 0; + port->icount.brk = 0; + port->icount.overrun = 0; + port->icount.tx = 0; + port->icount.buf_overrun = 0; + + bcm4773_init_the_profile(&profile_rxtx_work); + bcm4773_init_the_profile(&profile_spi_sync); + +} + +#endif //DEBUG_PROFILE + +#endif //DEBUG + +/* Ted :: Exported Samsung SSP Driver */ +extern struct spi_driver *pssp_driver; + +/* Setting up maximum size of buffer (UART_XMIT_SIZE) for transfer that corresponds size of xmit_uart_circ buffer */ +/* FIXME:: Temporary fix for H-phone: Using transfer size 254 instead of more because there is bug in bus driver spi-s3c64xx.c + DMA starts working automatically if transfer block has more 256 bytes size. + During transfer of a few packets (size > 256 bytes), one block transfer has CS0 low delay about 2.5ms after all bytes has been transferred in packet */ +#define BCM4773_MSG_BUF_SIZE (UART_XMIT_SIZE) +struct bcm4773_message +{ + unsigned char cmd_stat; + unsigned char data[UART_XMIT_SIZE]; +} __attribute__((__packed__)); + +/* structures */ +struct bcm4773_uart_port +{ + int host_req_pin; + int mcu_req; + int mcu_resp; + volatile int rx_enabled; + volatile int tx_enabled; + struct ktermios termios; + spinlock_t lock; + struct uart_port port; + struct work_struct rxtx_work; + struct work_struct start_tx; + struct work_struct stop_rx; + struct work_struct stop_tx; + struct workqueue_struct *serial_wq; + spinlock_t irq_lock; + atomic_t irq_enabled; + atomic_t suspending; + + struct spi_transfer master_transfer; + struct spi_transfer dbg_transfer; + struct wake_lock bcm4773_wake_lock; +}; + +/* UART name and device definitions */ +#define BCM4773_SERIAL_NAME "ttyBCM" +#define BCM4773_SERIAL_MAJOR 205 +#define BCM4773_SERIAL_MINOR 0 + +struct uart_driver bcm4773_uart_driver= +{ + .owner = THIS_MODULE, + .driver_name = BCM4773_SERIAL_NAME, + .dev_name = BCM4773_SERIAL_NAME, + .nr = 1, + .major = BCM4773_SERIAL_MAJOR, + .minor = BCM4773_SERIAL_MINOR, + .cons = NULL, +}; + +static struct bcm4773_uart_port *g_bport; + +static struct spi_driver bcm4773_spi_driver; +static struct uart_ops bcm4773_serial_ops; +static bcm4773_stat_t bcm4773_read_transmit( struct bcm4773_uart_port *bcm_port, int *length ); +static irqreturn_t bcm4773_irq_handler( int irq, void *pdata ); +#ifndef CONFIG_SPI_NO_AUTOBAUD +static void bcm4773_autobaud(struct bcm4773_uart_port *bport); +#endif +static bcm4773_stat_t bcm4773_reset_fifo( struct bcm4773_uart_port *bcm_port ); +#ifndef CONFIG_SPI_NO_IDENTIFY +static int bcm4773_do_identify( struct bcm4773_uart_port *bcm_port ); +#endif + +static inline struct spi_device *bcm4773_uart_to_spidevice( struct bcm4773_uart_port *bcm_port ) +{ + return to_spi_device( bcm_port->port.dev ); +} + +static inline unsigned char *bcm4773_get_write_buf( struct bcm4773_uart_port *bcm_port ) +{ + struct bcm4773_message *mesg=(struct bcm4773_message *) bcm_port->master_transfer.tx_buf; + + return mesg->data; +} + +static inline unsigned char *bcm4773_get_read_buf( struct bcm4773_uart_port *bcm_port ) +{ + struct bcm4773_message *mesg=(struct bcm4773_message *) bcm_port->master_transfer.rx_buf; + + return mesg->data; +} + + +/** + * bcm4773_debug_buffer - Return a pointer to a buffer usable by the SSI-debug layer above us. + * @bport: bcm uart device which is requesting to transfer + * @len: length of internal tx buffer + */ +static inline unsigned char* bcm4773_debug_buffer(struct bcm4773_uart_port *bport, size_t* len) +{ + if ( len ) { + struct bcm4773_message *bcm4773_write_msg=(struct bcm4773_message *) (bport->dbg_transfer.tx_buf); + memset(bcm4773_write_msg, 0xA5, sizeof(struct bcm4773_message)); + *len = sizeof( *bcm4773_write_msg); + return (unsigned char*) bcm4773_write_msg; + } + return NULL; +} + +static inline int bcm4773_debug_transfer(struct bcm4773_uart_port *bport, const unsigned char* buf, size_t length) +{ + int status = 0; + struct bcm4773_message *bcm4773_write_msg=(struct bcm4773_message *) (bport->dbg_transfer.tx_buf); + struct bcm4773_message *bcm4773_read_msg=(struct bcm4773_message *) (bport->dbg_transfer.rx_buf); + struct spi_message msg; + struct spi_transfer transfer = {0,}; + unsigned char cmd_stat; +#ifdef DEBUG_TRANSFER + char the_dir[10] = {'w',' ',0 }; +#endif + + if( length > sizeof( bcm4773_write_msg->data ) || length == 0 ) + return -EFAULT; + + if ( buf != &bcm4773_write_msg->cmd_stat ) + return -EFAULT; + + /* Prepare the data. */ + memcpy( &transfer, &(bport->dbg_transfer), sizeof( transfer ) ); + cmd_stat = bcm4773_write_msg->cmd_stat; + spi_message_init( &msg ); + + msg.spi=bcm4773_uart_to_spidevice( bport ); + msg.is_dma_mapped=0; + + transfer.len = length; + transfer.tx_dma=0; + transfer.rx_dma=0; + transfer.bits_per_word = 8; + +#ifdef DEBUG_TRANSFER + the_dir[0] = cmd_stat & SSI_READ_TRANS ? 'W':'w'; + pk_log(bport->port.dev,the_dir, cmd_stat,(char *)bcm4773_write_msg->data, length, length, status); +#endif + + + spi_message_add_tail( &transfer, &msg ); + +#ifdef CONFIG_SPI_DMA_BITS_PER_WORD + if ( (cmd_stat == (SSI_MODE_HALF_DUPLEX | SSI_WRITE_TRANS | SSI_MODE_DEBUG)) && (length > SSI_MAX_RW_BYTE_COUNT)) + { + int align = (CONFIG_SPI_DMA_BYTES_PER_WORD * WORD_BURST_SIZE); + transfer.bits_per_word = (transfer.len % align) ? 8 : CONFIG_SPI_DMA_BITS_PER_WORD; + if (transfer.bits_per_word == 8) + printk("%s: byte not aligned\n", __func__); + } +#endif + + status = spi_sync_locked( msg.spi, &msg ); + + if( status ) + return 0; + else { +#ifdef DEBUG_TRANSFER + the_dir[0] = cmd_stat & SSI_READ_TRANS ? 'R':'r'; + cmd_stat = bcm4773_read_msg->cmd_stat; //bcm4773_write_msg->data[0]; + pk_log(bport->port.dev,the_dir, cmd_stat, (char *)&bcm4773_read_msg->data[0], length, length, status); +#endif + return length; + } +} + + +static unsigned int bcm4773_get_mctrl( struct uart_port *port ) +{ + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + +static void bcm4773_set_mctrl( struct uart_port *port, unsigned int mctrl ) +{ + return; +} + +static unsigned int bcm4773_tx_empty( struct uart_port *port ) +{ + if( uart_circ_chars_pending( &port->state->xmit ) == 0 ) + return TIOCSER_TEMT; + else + return 0; +} + +static void bcm4773_stop_tx_work( struct work_struct *work ) +{ +#ifdef DEBUG_TX + struct bcm4773_uart_port *bport=container_of( work, struct bcm4773_uart_port, stop_tx ); + + pr_debug(PFX "[SSPBBD]: %s tx_enabled %d rx_enabled %d\n",__func__,bport->tx_enabled,bport->rx_enabled); +#endif + return; +} + +static void bcm4773_stop_tx( struct uart_port *port ) +{ + struct bcm4773_uart_port *bport=container_of( port, struct bcm4773_uart_port, port ); + + queue_work( bport->serial_wq, &(bport->stop_tx) ); + return; +} + +static void bcm4773_start_tx_work( struct work_struct *work ) +{ + struct bcm4773_uart_port *bport=container_of( work, struct bcm4773_uart_port, start_tx ); + unsigned long int flags; + +#ifdef DEBUG_IRQ_HANDLER + struct uart_port *port=&(bport->port); + int pending=uart_circ_chars_pending( &port->state->xmit ); + pr_debug(PFX "[SSPBBD]: %s.RXTX_WORK pn %d, tx %d\n",__func__,pending,bport->tx_enabled); +#endif + + spin_lock_irqsave( &(bport->lock), flags ); + + queue_work(bport->serial_wq, &(bport->rxtx_work) ); + +#ifdef DEBUG_PROFILE + profile_spi_sync.rxtx_work[0]++; // rxtx_work_profile +#endif + spin_unlock_irqrestore( &(bport->lock), flags ); + + return; +} + +static void bcm4773_start_tx( struct uart_port *port ) +{ + struct bcm4773_uart_port *bport=container_of( port, struct bcm4773_uart_port, port ); + unsigned long int flags; + + spin_lock_irqsave( &bport->irq_lock, flags); + if (atomic_xchg(&bport->irq_enabled, 0)) { + struct irq_desc *desc = irq_to_desc(bport->port.irq); + if (desc->depth!=0) { + printk("[SSPBBD]: %s irq depth mismatch. enabled irq has depth %d!\n", __func__, desc->depth); + desc->depth = 0; + } + disable_irq_nosync(bport->port.irq); + } + spin_unlock_irqrestore( &bport->irq_lock, flags); + + queue_work(bport->serial_wq, &(bport->start_tx) ); + + return; +} + +static void bcm4773_stop_rx_work( struct work_struct *work ) +{ + struct bcm4773_uart_port *bport=container_of( work, struct bcm4773_uart_port, stop_rx ); + unsigned long int flags; + +#ifdef DEBUG_RX + struct uart_port *port=&(bport->port); + pr_debug(PFX "[SSPBBD]: %s tx %d, rx %d, oe %d\n",__func__,bport->tx_enabled,bport->rx_enabled,port->icount.buf_overrun); +#endif + + spin_lock_irqsave( &(bport->lock), flags ); + if( bport->rx_enabled ) + { + bport->rx_enabled=0; + } + +#ifdef DEBUG_PROFILE + bcm4773_profile_print(__func__,__LINE__); +#endif + + spin_unlock_irqrestore( &(bport->lock), flags ); + + return; +} + +static void bcm4773_stop_rx( struct uart_port *port ) +{ + struct bcm4773_uart_port *bport=container_of( port, struct bcm4773_uart_port, port ); + + queue_work( bport->serial_wq, &(bport->stop_rx) ); + return; +} + +static void bcm4773_enable_ms(struct uart_port *port) +{ + return; +} + +static void bcm4773_break_ctl(struct uart_port *port, int break_state) +{ + return; +} + +static int bcm4773_startup( struct uart_port *port ) +{ + struct bcm4773_uart_port *bport=container_of( port, struct bcm4773_uart_port, port ); + unsigned long int flags; + + spin_lock_irqsave( &(bport->lock), flags ); + bport->rx_enabled=1; + bport->tx_enabled=0; + spin_unlock_irqrestore( &(bport->lock), flags ); + + spin_lock_irqsave( &bport->irq_lock, flags); + if (!atomic_xchg(&bport->irq_enabled, 1)) { + struct irq_desc *desc = irq_to_desc(bport->port.irq); + if (desc->depth!=1) { + printk("[SSPBBD]: %s irq depth mismatch. disabled irq has depth %d!\n", __func__, desc->depth); + desc->depth = 1; + } + enable_irq(bport->port.irq); + } + spin_unlock_irqrestore( &bport->irq_lock, flags); + + enable_irq_wake(bport->port.irq); + +#ifndef CONFIG_SPI_NO_AUTOBAUD + bcm4773_autobaud(bport); +#endif + +#ifdef DEBUG_PROFILE + bcm4773_profile_init(); +#endif + + return 0; +} + +static void bcm4773_shutdown( struct uart_port *port ) +{ + struct bcm4773_uart_port *bport=container_of( port, struct bcm4773_uart_port, port ); + unsigned long int flags; + + spin_lock_irqsave( &(bport->lock), flags ); + + if( bport->rx_enabled ) + bcm4773_stop_rx( port ); + + if( bport->tx_enabled ) + bcm4773_stop_tx( port ); + + spin_unlock_irqrestore( &(bport->lock), flags ); + + msleep(10); + + spin_lock_irqsave( &bport->irq_lock, flags); + if (atomic_xchg(&bport->irq_enabled, 0)) { + struct irq_desc *desc = irq_to_desc(bport->port.irq); + if (desc->depth!=0) { + printk("[SSPBBD]: %s irq depth mismatch. enabled irq has depth %d!\n", __func__, desc->depth); + desc->depth = 0; + } + disable_irq_nosync(bport->port.irq); + } + spin_unlock_irqrestore( &bport->irq_lock, flags); + + disable_irq_wake(bport->port.irq); + return; +} + +static void bcm4773_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old ) +{ + struct bcm4773_uart_port *bport=container_of( port, struct bcm4773_uart_port, port ); + +#ifdef DEBUG_SET_TERMIOS + pr_debug(PFX "[SSPBBD]: %s TTY <== i=0x%08X o=0x%08X c=0x%08X l=0x%08X m%d t%d %d %d...oe %d\n",__func__, + termios->c_iflag, termios->c_oflag, + termios->c_cflag, termios->c_lflag, + termios->c_cc[VMIN], termios->c_cc[VTIME], + termios->c_ispeed,termios->c_ospeed, + port->icount.buf_overrun); + pr_debug(PFX "[SSPBBD]: %s opened line %d\n",__func__, port->line); +#endif + + /* Termios field is fake. */ + memcpy( &(bport->termios), termios, sizeof( bport->termios ) ); + + /* It doesn't matter. */ + uart_update_timeout( port, CS8, 115200 ); + return; +} + +static const char *bcm4773_type(struct uart_port *port) +{ + return "[SSPBBD]: BCM4773_SPI"; +} + +static void bcm4773_release_port(struct uart_port *port) +{ + return; +} + +static int bcm4773_request_port(struct uart_port *port) +{ + return 0; +} + +static void bcm4773_config_port(struct uart_port *port, int flags) +{ + return; +} + +static int bcm4773_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + if( port->type == PORT_16550A ) + return 0; + else + return -EINVAL; +} + +static bool bcm4773_hello(struct bcm4773_uart_port *bport) +{ +#ifdef CONFIG_DDS + int count=0, retries=0; + + gpio_set_value(bport->mcu_req, 1); +#ifdef DEBUG_DDS_GPIO + printk("%s get MCU_REQ_RESP %d\n", __func__, gpio_get_value(bport->mcu_resp)); + printk("%s set MCU_REQ %d\n", __func__, gpio_get_value(bport->mcu_req)); +#endif + while (!gpio_get_value(bport->mcu_resp)) { +#ifdef DEBUG_DDS_GPIO + printk("%s get MCU_REQ_RESP %d\n", __func__, gpio_get_value(bport->mcu_resp)); +#endif + + if (count++ > 100) { + gpio_set_value(bport->mcu_req, 0); + printk("%s get MCU_REQ_RESP timeout. MCU_RESP(gpio%d) not responding to MCU_REQ(gpio%d)\n", + __func__, bport->mcu_resp, bport->mcu_req); + return false; + } + + mdelay(1); + + if (gpio_get_value(bport->mcu_resp)) { + break; + } + + if (count%20==0 && retries++ < 3) { + gpio_set_value(bport->mcu_req, 0); +#ifdef DEBUG_DDS_GPIO + printk("%s set MCU_REQ %d\n", __func__, gpio_get_value(bport->mcu_req)); +#endif + mdelay(1); + gpio_set_value(bport->mcu_req, 1); +#ifdef DEBUG_DDS_GPIO + printk("%s set MCU_REQ %d\n", __func__, gpio_get_value(bport->mcu_req)); +#endif + mdelay(1); + } + + } +#ifdef DEBUG_DDS_GPIO + printk("%s get MCU_REQ_RESP %d\n", __func__, gpio_get_value(bport->mcu_resp)); +#endif +#endif + return true; +} + +static void bcm4773_bye(struct bcm4773_uart_port *bport) +{ +#ifdef CONFIG_DDS + gpio_set_value(bport->mcu_req, 0); +#ifdef DEBUG_DDS_GPIO + printk("%s set MCU_REQ %d\n", __func__, gpio_get_value(bport->mcu_req)); +#endif +#endif +} + +void bcm4773_mcu_resp_float(bool do_float) +{ +#ifdef CONFIG_DDS + if(!g_bport) + return; +#endif +} + +/* returns status. */ +static bcm4773_stat_t bcm4773_transfer( struct bcm4773_uart_port *bport, unsigned char cmd_stat, int length, int use_dma ) +{ + int status = 0; + struct bcm4773_message *bcm4773_write_msg=(struct bcm4773_message *) (bport->master_transfer.tx_buf); + struct bcm4773_message *bcm4773_read_msg=(struct bcm4773_message *) (bport->master_transfer.rx_buf); + struct spi_message msg; + struct spi_transfer transfer; +#ifdef DEBUG_TRANSFER + char the_dir[10] = {'w',' ',0 }; +#endif +#ifdef DEBUG_PROFILE +#if DEBUG_PROFILE==2 + unsigned long t1, t0 = clock_get_us(); + trans = cmd_stat & SSI_READ_TRANS ? 1:0; +#endif +#endif + + if( length > sizeof( bcm4773_write_msg->data ) || length == 0 ) + return 0xFF; + + /* Prepare the data. */ + memcpy( &transfer, &(bport->master_transfer), sizeof( transfer ) ); + spi_message_init( &msg ); + + if( use_dma ) + { + msg.is_dma_mapped=1; + } + else + { + transfer.tx_dma=0; + transfer.rx_dma=0; + msg.is_dma_mapped=0; + } + + bcm4773_write_msg->cmd_stat=cmd_stat; + + if ( length != 0 && (cmd_stat & SSI_READ_TRANS) == 0 ) { + + if ( (cmd_stat & SSI_MODE_FULL_DUPLEX) != 0 ) { + bcm4773_write_msg->data[0]=length; + length += 1; // 1 means RX_PKT_LEN for Full Duplex Mode + } else { + transfer.rx_buf = NULL; // FIXME: To get rid of call of callback function in bus driver in DMA mode. + bcm4773_read_msg->cmd_stat = 0; + bcm4773_read_msg->data[0]= 0; + } + } + +#ifdef DEBUG_TRANSFER + the_dir[0] = cmd_stat & SSI_READ_TRANS ? 'W':'w'; + pk_log(bport->port.dev,the_dir, cmd_stat,(char *)bcm4773_write_msg->data, length, length, status); +#endif + + transfer.len=length + 1; // + 1 byte is for cmd_stat + +#ifdef CONFIG_SPI_DMA_BITS_PER_WORD + if ( (cmd_stat == (SSI_MODE_HALF_DUPLEX | SSI_WRITE_TRANS)) +#ifdef CONFIG_SPI_NO_FULL_DUPLEX + && + (length >= SSI_MAX_RW_BYTE_COUNT) +#endif + ) + { + transfer.bits_per_word=CONFIG_SPI_DMA_BITS_PER_WORD; + } +#endif + + spi_message_add_tail( &transfer, &msg ); + msg.spi=bcm4773_uart_to_spidevice( bport ); + +#ifdef DEBUG_PROFILE +#if DEBUG_PROFILE==2 + bcm4773_trans = 1; +#endif +#endif + status = spi_sync( msg.spi, &msg ); + +#ifdef DEBUG_PROFILE +#if DEBUG_PROFILE==2 + bcm4773_trans = 0; + t1 = clock_get_us(); + profile_spi_sync.count[trans]++; + profile_spi_sync.total_time[trans] += (t1-t0); +#endif +#endif + + if( status ) + return 0xFF; + else { +#ifdef DEBUG_TRANSFER + the_dir[0] = cmd_stat & SSI_READ_TRANS ? 'R':'r'; + cmd_stat = bcm4773_read_msg->cmd_stat; //bcm4773_write_msg->data[0]; + pk_log(bport->port.dev,the_dir, cmd_stat, (char *)&bcm4773_read_msg->data[0], length, length, status); +#endif + return BCM4773_RET(bcm4773_read_msg); + } +} + +static bcm4773_stat_t bcm4773_read( struct bcm4773_uart_port *bcm_port, unsigned char cmd_stat, int length ) +{ + unsigned char *write_buf=bcm4773_get_write_buf( bcm_port ); + + memset( write_buf, 0, length ); + +#ifdef CONFIG_SPI_BCM4773_DMA + return bcm4773_transfer( bcm_port, cmd_stat, length, (length > 1 ? 1 : 0) ); +#else + return bcm4773_transfer( bcm_port, cmd_stat, length, 0 ); +#endif +} + +static inline bcm4773_stat_t bcm4773_write( struct bcm4773_uart_port *bcm_port, unsigned char cmd_stat, int length) +{ +#ifdef CONFIG_SPI_BCM4773_DMA + return bcm4773_transfer( bcm_port, cmd_stat, length, (length > 1 ? 1 : 0) ); +#else + return bcm4773_transfer( bcm_port, cmd_stat, length, 0 ); +#endif +} + +static bcm4773_stat_t bcm4773_reset_fifo( struct bcm4773_uart_port *bcm_port ) +{ + int i=0; + bcm4773_stat_t retval; + int length = 0; + do { + i++; + retval = bcm4773_read_transmit( bcm_port, &length ); + } while (i < 3 ); + + return 0; +} + + +static inline bcm4773_stat_t bcm4773_read_status( struct bcm4773_uart_port *bcm_port ) +{ + return bcm4773_read( bcm_port, SSI_READ_TRANS | SSI_MODE_HALF_DUPLEX, 1 ); +} + +static bcm4773_stat_t bcm4773_read_transmit( struct bcm4773_uart_port *bcm_port, int *length ) +{ + bcm4773_stat_t retval = bcm4773_read_status( bcm_port ); + bcm4773_stat_t status = BCM4773_MSG_STAT(retval); + +#ifdef DEBUG_TRANSFER__READ_TRANSMIT + pr_debug(PFX "[SSPBBD]: %s: retval=0x%04X ( len %d , stat = 0x%02X )\n", __func__,retval,BCM4773_MSG_LEN(retval),status); +#endif + + if( status == 0 ) + { + *length=BCM4773_MSG_LEN(retval); + if( *length != 0 ) + { + int len2; + if (*length == 255) { + *length = 254; + } + retval=bcm4773_read( bcm_port, SSI_READ_TRANS | SSI_MODE_HALF_DUPLEX, *length+1); //KOM:: FIXME It doesn't matter what we are using HALF or FULL DUPLEX + len2 = BCM4773_MSG_LEN(retval); + if ( len2 < *length ) { + struct uart_port *port=&(bcm_port->port); + pr_err(PFX "[SSPBBD]: %s error reading from the chipset! Read %d, expected %d\n", __func__,len2,*length); + *length = len2; + port->icount.brk++; + } + /* Keep using length in bcm4773_read_status for return value (*length) not length in next bcm4773_read because + next length in bcm4773_read may be > length of previous read in bcm4773_read_status */ + } + } + + return retval; +} + +static bcm4773_stat_t bcm4773_write_receive( struct bcm4773_uart_port *bcm_port, int n, int *length ) +{ + bcm4773_stat_t retval = bcm4773_write( bcm_port, SSI_WRITE_TRANS | SSI_MODE_FULL_DUPLEX, n); + + /* See comment in bcm4773_read_transmit + Don't need to check status because "retval" is returned + bcm4773_stat_t status = BCM4773_MSG_STAT(retval); + if( status == 0 ) + */ + { + int len2 = BCM4773_MSG_LEN(retval); + if ( n > len2) + *length = len2; + else + *length = n; + } + + return retval; +} + + +static irqreturn_t bcm4773_irq_handler( int irq, void *pdata ) +{ + struct bcm4773_uart_port *bport=(struct bcm4773_uart_port *) pdata; + + /* [FW4773-413] Check if we really have pending irq */ + bcm4773_stat_t status = gpio_get_value(bport->host_req_pin); + if (!status) + return IRQ_HANDLED; + +#ifdef DEBUG_IRQ_HANDLER + // FIXME Do we need to check HOST_REQ is set or not ??? + bcm4773_stat_t status = gpio_get_value(bport->host_req_pin); + + struct uart_port *port=&(bport->port); + pr_debug(PFX "[SSPBBD]: %s.RXTX_WORK hq %d, rx %d\n",__func__,status,bport->rx_enabled); +#endif + spin_lock(&bport->irq_lock); + if (atomic_xchg(&bport->irq_enabled, 0)) { + struct irq_desc *desc = irq_to_desc(bport->port.irq); + if (desc->depth!=0) { + printk("[SSPBBD]: %s irq depth mismatch. enabled irq has depth %d!\n", __func__, desc->depth); + desc->depth = 0; + } + disable_irq_nosync(bport->port.irq); + } + spin_unlock(&bport->irq_lock); + + queue_work(bport->serial_wq, &bport->rxtx_work); + + return IRQ_HANDLED; +} + +static void bcm4773_insert_flip_string(struct uart_port *port, struct bcm4773_uart_port *bport,int length,const char *func,long line) +{ + int count; + + if( port->state->port.tty != NULL && length > 0 ) + { + unsigned char *data = bcm4773_get_read_buf( bport); + + count=tty_insert_flip_string(&port->state->port, data + 1, length ); /* 1 means excluding RX_PKT_LEN */ + if( count < length ) + { + pr_err(PFX "[SSPBBD]: tty_insert_flip_string input overrun error by (%i - %i) = %i bytes!\n", length, count, length - count ); + port->icount.buf_overrun+=length - count; + } + port->icount.rx+=count; + tty_flip_buffer_push(&port->state->port); + +#ifdef DEBUG_INSERT_FLIP_STRING + pr_debug(PFX "[SSPBBD]: %s inserts %d bytes from %s(%ld)\n",__func__,length,func,line); +#endif + } +} + + +static void bcm4773_rxtx_work( struct work_struct *work ) +{ + struct bcm4773_uart_port *bport=container_of( work, struct bcm4773_uart_port, rxtx_work ); + struct uart_port *port=&(bport->port); + int length = 0; + int pending; + int count; + bcm4773_stat_t status; + unsigned long int flags; + +#ifdef DEBUG_PROFILE + t0=clock_get_us(); + count = (bport->tx_enabled-1) % 2; // rxtx_work_profile + profile_rxtx_work.rxtx_work[count]++; + profile_rxtx_work.rxtx_enter[count]++; +#endif + + status = gpio_get_value(bport->host_req_pin); + pending=uart_circ_chars_pending( &port->state->xmit ); + +#ifdef DEBUG_TRANSFER__RXTX_WORK + pr_debug(PFX "[SSPBBD]: %s.START pn %d, hq %d, rx %d, tx %d, oe %d\n",__func__,pending,status,bport->rx_enabled,bport->tx_enabled,port->icount.buf_overrun); +#endif + if (bcm4773_hello(bport)) { + + /* Drain the TX/RX buffer in a loop equally. */ + do + { + if ( status ) + { + status=bcm4773_read_transmit( bport, &length ); + + if( BCM4773_MSG_STAT(status) != 0x00 ) + { + pr_err(PFX "[SSPBBD]: bcm4773_read_transmit buffer error! retval=0x%04X, port->irq = %d\n", status,port->irq); + + break; //FIXME: return ??? + } else if ( BCM4773_MSG_LEN(status) != 0x00 ) { + + bcm4773_insert_flip_string(port,bport,length,__func__,__LINE__); + } + +#ifndef CONFIG_SPI_NO_FULL_DUPLEX + status = gpio_get_value(bport->host_req_pin); /* Keep checking HOST_REQ for the following write operation in Full Duplex Mode. */ +#else + status = 0; +#endif + } + + if( pending != 0 ) + { + /* We need to update the buffer first, as it can change while we work with it. */ + spin_lock_irqsave(&port->lock, flags); + if( pending > BCM4773_MSG_BUF_SIZE ) pending=BCM4773_MSG_BUF_SIZE; + + count=CIRC_CNT_TO_END( port->state->xmit.head, port->state->xmit.tail, UART_XMIT_SIZE ); + if( pending > count ) pending=count; + + /* Select Half or Full Duplex mode for writing, because maximum size for Full Duplex is 255 bytes */ + /* Where: 1 means TX_PKT_LEN byte for Full Duplex mode */ + +#ifndef CONFIG_SPI_NO_FULL_DUPLEX + length = 1; +#else + length = 0; +#endif + if ( pending > SSI_MAX_RW_BYTE_COUNT ) { + if ( status ) { /* Checking HOST_REQ (status) also if data is available to read. */ + pending = SSI_MAX_RW_BYTE_COUNT; + } else { +#ifdef CONFIG_SPI_DMA_BITS_PER_WORD + pending = pending - (pending % (CONFIG_SPI_DMA_BYTES_PER_WORD * WORD_BURST_SIZE)) - 1; // "-1" because we need align at 16 or 32 bound and we have extra byte "cmd_stat" +#endif + length = 0; + } + } + + /* Copy the data. */ + memcpy( bcm4773_get_write_buf( bport ) + length, port->state->xmit.buf + port->state->xmit.tail, pending ); + + /* Update the circular buffer. */ + port->state->xmit.tail=(port->state->xmit.tail + pending) & (UART_XMIT_SIZE - 1); + spin_unlock_irqrestore(&port->lock, flags); + + if ( length == 0 ) { + /* Write the data in Half Duplex mode */ + status=bcm4773_write( bport, SSI_WRITE_TRANS | SSI_MODE_HALF_DUPLEX, pending); + status = BCM4773_MSG_STAT(status); + } else { + /* Write/Read the data in Full Duplex mode */ + status=bcm4773_write_receive( bport, pending, &length ); + } + + if( BCM4773_MSG_STAT(status) == 0x00 ) + { + /* Transmission successfull. Update the TX count. */ + port->icount.tx+=pending; + + //FIXED: Full Duplex issue, length = BCM4773_MSG_LEN(status); + if ( length != 0 ) + bcm4773_insert_flip_string(port,bport,length,__func__,__LINE__); + } + else + { + /* Very unlikely that we get here.... */ + port->icount.overrun+=pending; + } + } + + status = gpio_get_value(bport->host_req_pin); + pending=uart_circ_chars_pending( &port->state->xmit ); + +#ifdef DEBUG_TRANSFER__RXTX_WORK_LOOP + pr_debug(PFX "[SSPBBD]: %s.LOOP pending %d, host_req %d, rx %d, tx %d\n",__func__,pending,status,bport->rx_enabled,bport->tx_enabled); +#endif + + } while( ( pending || status)); + + bcm4773_bye(bport); + } + else { + pr_err("[SSPBBD]: %s timeout!!\n", __func__); + } + + pending=uart_circ_chars_pending( &port->state->xmit ); + if( (pending < WAKEUP_CHARS) && (port->state->port.tty != NULL) ) + tty_wakeup( port->state->port.tty ); + + spin_lock_irqsave( &(bport->lock), flags ); + + +#ifdef DEBUG_TRANSFER__RXTX_WORK + pr_debug(PFX "[SSPBBD]: %s.EXIT pn %d, hq %d, rx %d, tx %d, oe %d\n",__func__,pending,status,bport->rx_enabled,bport->tx_enabled,port->icount.buf_overrun); +#endif + +#ifdef DEBUG_PROFILE + t1 = clock_get_us(); + profile_rxtx_work.count[0]++; + profile_rxtx_work.total_time[0] += (t1-t0); + count = (bport->tx_enabled-1) % 2; // rxtx_work_profile + profile_rxtx_work.rxtx_enter[count]--; +#endif + spin_unlock_irqrestore( &(bport->lock), flags ); + + spin_lock_irqsave( &bport->irq_lock, flags); + if (!atomic_read(&bport->suspending)) // we dont' want to enable irq when going to suspendq + { + /* Let irq happen again */ + if (!atomic_xchg(&bport->irq_enabled, 1)) { + struct irq_desc *desc = irq_to_desc(bport->port.irq); + if (desc->depth!=1) { + printk("[SSPBBD]: %s irq depth mismatch. disabled irq has depth %d!\n", __func__, desc->depth); + desc->depth = 1; + } + enable_irq(bport->port.irq); + } + } + spin_unlock_irqrestore( &bport->irq_lock, flags); + return; +} + + +#ifndef CONFIG_SPI_NO_AUTOBAUD +static void bcm4773_autobaud(struct bcm4773_uart_port *bport) +{ + struct spi_device *spidev = bcm4773_uart_to_spidevice( bport ); + + unsigned char autobaud[16] = { + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 + }; + unsigned char dummy[16]; + struct spi_message msg; + struct spi_transfer transfer = { + .tx_buf = autobaud, + .rx_buf = dummy, + .len = 16, + }; + + spi_message_init(&msg); + msg.spi = spidev; + spi_message_add_tail(&transfer, &msg); + + if (bcm4773_hello(bport)) { + spi_sync(spidev, &msg); + bcm4773_bye(bport); + } + else { + pr_err("[SSPBBD]: %s timeout!!\n", __func__); + } +} +#endif + +#ifndef CONFIG_SPI_NO_IDENTIFY +static int bcm4773_do_identify( struct bcm4773_uart_port *bcm_port ) +{ + const char ident_write[]= { //0x00, // Space is for TX_PKT_LEN for Full Duplex Mode + 0xB0, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x37, 0xb0, 0x01 + }; + const char ident_read[] = { + 0xB0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x8F, 0xB0, 0x01, 0xB0, + 0x00, 0x01, 0x06, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x20, 0xB0, 0x01 + }; + + char buffer[255+1]; + int length=0,n=0,n2; + bcm4773_stat_t status; + + /* Do identify. */ + memcpy( bcm4773_get_write_buf( bcm_port ) + 1, ident_write, sizeof( ident_write ) ); // 1 means RX_PKT_LEN for Full Duplex Mode + status=bcm4773_write_receive( bcm_port, sizeof( ident_write ), &length ); + + if( BCM4773_MSG_STAT(status) != 0x00 ) + { + pr_err(PFX "[SSPBBD]: Error writing identify! Status %.2X\n", status ); + return -ENODEV; + } + //FIXED: Full Duplex issue, length = BCM4773_MSG_LEN(status); + if ( length > 0 ) { + memcpy( buffer, bcm4773_get_read_buf( bcm_port ) + 1, length); // 1 means RX_PKT_LEN for both modes + } + n = length; + + mdelay( 10 ); + status=bcm4773_read_transmit( bcm_port, &length ); + if( BCM4773_MSG_STAT(status) != 0x00 ) + { + pr_err(PFX "[SSPBBD]: Error receiving identify result! Status %.2X\n", status ); + return -ENODEV; + } + + n2 = BCM4773_MSG_LEN(status); + length = sizeof(buffer) - n; + if ( length > n2 ) + length = n2; + + if ( length > 0 ) { + memcpy( &buffer[n], bcm4773_get_read_buf( bcm_port ) + 1, length); // 1 means RX_PKT_LEN for both modes + length +=n; + } + + length +=n; + + if( (length != sizeof( ident_read )) || memcmp( ident_read, buffer, length ) ) + { + pr_err(PFX "[SSPBBD]: Ident data received is not correct!\n" ); + for( length=0; length < sizeof( ident_read ); length++ ) + pr_notice(PFX "[SSPBBD]: SPI Data Compare passed for byte %2d. Exp = %.2X, Act = %.2X\n",length,ident_read[length],buffer[length]); + + pr_err(PFX "[SSPBBD]: Length received: %i, expected %u\n", n + n2, sizeof( ident_read ) ); + return -ENODEV; + } + + pr_notice(PFX "[SSPBBD]: Identify success!\n" ); + pr_notice(PFX "[SSPBBD]: Received: " ); + for( length=0; length < sizeof( ident_read ); length++ ) + pr_notice( "%.2X ", buffer[length] ); + pr_notice(PFX "\n[SSPBBD]: Status: %.2X\n", status ); + return 0; +} +#endif + +static int bcm4773_spi_probe( struct spi_device *spi ) +{ + struct bcm4773_uart_port *bcm4773_uart_port; + int retval; + struct ssp_platform_data dflt; + + struct ssp_platform_data *pdata = spi->dev.platform_data; + if (!pdata) { + pr_warning(PFX "[SSPBBD]: Platform_data null has been provided. Use local allocation.\n"); + pdata = &dflt; + if (!spi->dev.of_node) { + pr_err(PFX "[SSPBBD]: Failed to find of_node\n"); + return -1; + } + } + + /* All the gpio pins SCLK,CS0,MOSI,MISO and HOST_REQ should be defined in arch/arm/boot/dts/exynos5430-kqlte_eur_open_00.dts + * The fake HOST_REQ pin is commented out. + * pdata->mcu_int1 = of_get_named_gpio(spi->dev.of_node, "ssp-irq2", 0); + */ + pdata->mcu_int1 = of_get_named_gpio(spi->dev.of_node, "ssp-irq", 0); + + /* We don't need to setup the gpio HOST_REQ pin "mcu_int1" + * - gpio_request(pdata->mcu_int1, "mcu_ap_int1"); + * - gpio_export(pdata->mcu_int1,1); + * - gpio_direction_input(pdata->mcu_int1); + */ + + if (pdata->mcu_int1<0) { + pr_err(PFX "[SSPBBD]: Failed to get mcu_ap_int1 from DT, err %d\n",pdata->mcu_int1); + return -1; + } + + spi->irq = gpio_to_irq(pdata->mcu_int1); + if ( spi->irq < 0 ) { + pr_err(PFX "[SSPBBD]: Failed to get mcu_ap_int1 gpio %d, err %d\n",pdata->mcu_int1,spi->irq); + return -1; + } + + if (pdata->mcu_int1 <0) + { + pr_err(PFX "[SSPBBD]: Failed to find ssp-irq in of_node\n"); + return -1; + } + pr_debug(PFX "[SSPBBD]: OK, found ssp-irq %d ", pdata->mcu_int1); + + /* Allocate memory for the private driver data. */ + bcm4773_uart_port=(struct bcm4773_uart_port *) kmalloc( sizeof( struct bcm4773_uart_port ), GFP_ATOMIC ); + + pr_notice(PFX "[SSPBBD]: %s SPI/SSI UART Driver v"GPS_VERSION", pdata->gpio_spi = %d, spi->irq = %d, ac_data = 0x%p\n",__func__, + pdata->mcu_int1,spi->irq, bcm4773_uart_port); + + if( bcm4773_uart_port == NULL ) + { + pr_err(PFX "[SSPBBD]: Failed to allocate memory for the BCM4773 SPI-UART driver.\n"); + return -ENOMEM; + } + else memset( bcm4773_uart_port, 0, sizeof( struct bcm4773_uart_port ) ); + + /* Initialize the structure. */ + spin_lock_init( &(bcm4773_uart_port->port.lock) ); + spin_lock_init( &(bcm4773_uart_port->lock) ); + spin_lock_init( &(bcm4773_uart_port->irq_lock) ); + bcm4773_uart_port->port.iotype=UPIO_MEM; + bcm4773_uart_port->port.irq=spi->irq; + bcm4773_uart_port->port.custom_divisor=1; + bcm4773_uart_port->port.fifosize=256; // It doesn't matter fifosize for us + bcm4773_uart_port->port.ops=&bcm4773_serial_ops; + bcm4773_uart_port->port.flags=UPF_BOOT_AUTOCONF; + bcm4773_uart_port->port.type=PORT_16550A; // It doesn't matter what serial type is used + bcm4773_uart_port->port.dev=&(spi->dev); + + /* Allocate memory for the transfer buffer. */ +#ifdef CONFIG_SPI_BCM4773_DMA + spi->dev.coherent_dma_mask = 0xffffffffUL; + + bcm4773_uart_port->master_transfer.rx_buf=dma_alloc_coherent( &(spi->dev), sizeof( struct bcm4773_message ), + &(bcm4773_uart_port->master_transfer.rx_dma), GFP_KERNEL | GFP_DMA ); + bcm4773_uart_port->master_transfer.tx_buf=dma_alloc_coherent( &(spi->dev), sizeof( struct bcm4773_message ), + &(bcm4773_uart_port->master_transfer.tx_dma), GFP_KERNEL | GFP_DMA ); + bcm4773_uart_port->dbg_transfer.rx_buf=dma_alloc_coherent( &(spi->dev), sizeof( struct bcm4773_message ), + &(bcm4773_uart_port->dbg_transfer.rx_dma), GFP_KERNEL | GFP_DMA ); + bcm4773_uart_port->dbg_transfer.tx_buf=dma_alloc_coherent( &(spi->dev), sizeof( struct bcm4773_message ), + &(bcm4773_uart_port->dbg_transfer.tx_dma), GFP_KERNEL | GFP_DMA ); +#else + bcm4773_uart_port->master_transfer.tx_buf=kmalloc( sizeof( struct bcm4773_message ), GFP_KERNEL ); + bcm4773_uart_port->master_transfer.rx_buf=kmalloc( sizeof( struct bcm4773_message ), GFP_KERNEL ); + bcm4773_uart_port->master_transfer.tx_dma=0; + bcm4773_uart_port->master_transfer.rx_dma=0; + bcm4773_uart_port->dbg_transfer.tx_buf=kmalloc( sizeof( struct bcm4773_message ), GFP_KERNEL ); + bcm4773_uart_port->dbg_transfer.rx_buf=kmalloc( sizeof( struct bcm4773_message ), GFP_KERNEL ); + bcm4773_uart_port->dbg_transfer.tx_dma=0; + bcm4773_uart_port->dbg_transfer.rx_dma=0; +#endif + + if( !bcm4773_uart_port->master_transfer.tx_buf || !bcm4773_uart_port->master_transfer.rx_buf || + !bcm4773_uart_port->dbg_transfer.tx_buf || !bcm4773_uart_port->dbg_transfer.rx_buf ) + { + pr_err(PFX "[SSPBBD]: Failed to allocate transfer buffer memory.\n" ); + if( bcm4773_uart_port->master_transfer.tx_buf ) +#ifdef CONFIG_SPI_BCM4773_DMA + dma_free_coherent( &(spi->dev), sizeof( struct bcm4773_message ), (void *) bcm4773_uart_port->master_transfer.tx_buf, + bcm4773_uart_port->master_transfer.tx_dma ); +#else + kfree( bcm4773_uart_port->master_transfer.tx_buf ); +#endif + if( bcm4773_uart_port->master_transfer.rx_buf ) +#ifdef CONFIG_SPI_BCM4773_DMA + dma_free_coherent( &(spi->dev), sizeof( struct bcm4773_message ), (void *) bcm4773_uart_port->master_transfer.rx_buf, + bcm4773_uart_port->master_transfer.rx_dma ); +#else + kfree( bcm4773_uart_port->master_transfer.rx_buf ); +#endif + kfree( bcm4773_uart_port ); + return -ENOMEM; + } + bcm4773_uart_port->master_transfer.speed_hz=0; /* Setting 0 and SPI low driver will use .max_speed_hz from arch/arm/mach-omap2/board-omap4panda.c. + Otherwise we can change speed_hz in bcm4773_set_termios() */ + bcm4773_uart_port->master_transfer.bits_per_word=8; + bcm4773_uart_port->master_transfer.delay_usecs=0; + + bcm4773_uart_port->dbg_transfer.speed_hz=0; + bcm4773_uart_port->dbg_transfer.bits_per_word=8; + bcm4773_uart_port->dbg_transfer.delay_usecs=0; + + /* Reset FIFO */ + bcm4773_reset_fifo( bcm4773_uart_port ); + + /* Detect. */ +#ifndef CONFIG_SPI_NO_IDENTIFY + retval = bcm4773_do_identify( bcm4773_uart_port ); + if( retval < 0 ) + { + pr_err(PFX "[SSPBBD]: Failed to identify BCM4773 chip! (Is the chip powered?)\n"); +#ifdef CONFIG_SPI_BCM4773_DMA + dma_free_coherent( &(spi->dev), sizeof( struct bcm4773_message ), (void *) bcm4773_uart_port->master_transfer.tx_buf, + bcm4773_uart_port->master_transfer.tx_dma ); + dma_free_coherent( &(spi->dev), sizeof( struct bcm4773_message ), (void *) bcm4773_uart_port->master_transfer.rx_buf, + bcm4773_uart_port->master_transfer.rx_dma ); +#else + kfree( bcm4773_uart_port->master_transfer.tx_buf ); + kfree( bcm4773_uart_port->master_transfer.rx_buf ); +#endif + kfree( bcm4773_uart_port ); + return -ENODEV; + } +#endif + + /* Register the serial driver. */ + retval=uart_register_driver( &bcm4773_uart_driver ); + if( retval ) + { + pr_err(PFX "[SSPBBD]: Failed to register BCM4773 SPI-UART driver.\n"); +#ifdef CONFIG_SPI_BCM4773_DMA + dma_free_coherent( &(spi->dev), sizeof( struct bcm4773_message ), (void *) bcm4773_uart_port->master_transfer.tx_buf, + bcm4773_uart_port->master_transfer.tx_dma ); + dma_free_coherent( &(spi->dev), sizeof( struct bcm4773_message ), (void *) bcm4773_uart_port->master_transfer.rx_buf, + bcm4773_uart_port->master_transfer.rx_dma ); +#else + kfree( bcm4773_uart_port->master_transfer.tx_buf ); + kfree( bcm4773_uart_port->master_transfer.rx_buf ); +#endif + kfree( bcm4773_uart_port ); + return retval; + } + + /* Register the one TTY device. */ + uart_add_one_port( &bcm4773_uart_driver, &(bcm4773_uart_port->port) ); + + //set closing_wait 0. See jira FW4773-391 + bcm4773_uart_driver.state->port.closing_wait = ASYNC_CLOSING_WAIT_NONE; + + bcm4773_uart_port->host_req_pin = pdata->mcu_int1; + bcm4773_uart_port->mcu_req = of_get_named_gpio(spi->dev.of_node, "ssp-mcu-req", 0); + bcm4773_uart_port->mcu_resp = of_get_named_gpio(spi->dev.of_node, "ssp-mcu-resp", 0); + + /* Set the private info. */ + spi_set_drvdata( spi, bcm4773_uart_port ); + + /* Setup the workqueue. */ + INIT_WORK( &(bcm4773_uart_port->rxtx_work), bcm4773_rxtx_work ); + INIT_WORK( &(bcm4773_uart_port->start_tx), bcm4773_start_tx_work ); + INIT_WORK( &(bcm4773_uart_port->stop_rx), bcm4773_stop_rx_work ); + INIT_WORK( &(bcm4773_uart_port->stop_tx), bcm4773_stop_tx_work ); + bcm4773_uart_port->serial_wq= alloc_workqueue("bcm4773_wq", WQ_HIGHPRI | WQ_UNBOUND | WQ_MEM_RECLAIM, 1); + + //FIXME: Do we need make this call here? + pssp_driver->probe(spi); + + /* Request the interrupt. */ + retval=request_irq( bcm4773_uart_port->port.irq, bcm4773_irq_handler, + IRQF_TRIGGER_HIGH, + bcm4773_uart_driver.driver_name, + bcm4773_uart_port ); + if( retval ) + { + pr_err(PFX "[SSPBBD]: Failed to register BCM4773 SPI TTY IRQ %d.\n",bcm4773_uart_port->port.irq); + uart_remove_one_port( &bcm4773_uart_driver, &(bcm4773_uart_port->port) ); + uart_unregister_driver( &bcm4773_uart_driver ); +#ifdef CONFIG_SPI_BCM4773_DMA + dma_free_coherent( &(spi->dev), sizeof( struct bcm4773_message ), (void *) bcm4773_uart_port->master_transfer.tx_buf, + bcm4773_uart_port->master_transfer.tx_dma ); + dma_free_coherent( &(spi->dev), sizeof( struct bcm4773_message ), (void *) bcm4773_uart_port->master_transfer.rx_buf, + bcm4773_uart_port->master_transfer.rx_dma ); +#else + kfree( bcm4773_uart_port->master_transfer.tx_buf ); + kfree( bcm4773_uart_port->master_transfer.rx_buf ); +#endif + kfree( bcm4773_uart_port ); + return retval; + } + + g_bport = bcm4773_uart_port; + + // Init wakelock + wake_lock_init(&bcm4773_uart_port->bcm4773_wake_lock, WAKE_LOCK_SUSPEND, "bcm4773_wake_lock"); + + /* Disable interrupts */ + pr_notice(PFX "[SSPBBD]: Initialized. irq = %d\n", bcm4773_uart_port->port.irq); + disable_irq( bcm4773_uart_port->port.irq ); + atomic_set( &(bcm4773_uart_port->irq_enabled), 0 ); + + atomic_set(&bcm4773_uart_port->suspending, 0); + +#ifdef CONFIG_DDS + /* Prepare MCU request/response */ + gpio_request(g_bport->mcu_req, "MCU REQ"); + gpio_direction_output(g_bport->mcu_req, 0); + + gpio_request(g_bport->mcu_resp, "MCU RESP"); + gpio_direction_input(g_bport->mcu_resp); +#endif + return 0; +} + +static inline struct bcm4773_uart_port* bcm4773_spi_get_drvdata( struct spi_device *spi ) +{ + // TODO: This is a temporary solution. + // We sometimes have wrong drv data from spi_get_drvdata. + if (g_bport) + return g_bport; + else + return (struct bcm4773_uart_port *) spi_get_drvdata( spi ); +} + +static int bcm4773_spi_remove( struct spi_device *spi ) +{ + struct bcm4773_uart_port *bport = bcm4773_spi_get_drvdata( spi ); + unsigned long int flags; + + pr_notice(PFX "[SSPBBD]: %s : called\n", __func__); + + spin_lock_irqsave( &(bport->lock), flags ); + + bport->rx_enabled = 0; + bport->tx_enabled = 0; + + spin_unlock_irqrestore( &(bport->lock), flags ); + + flush_workqueue( bport->serial_wq ); + destroy_workqueue( bport->serial_wq ); + uart_remove_one_port( &bcm4773_uart_driver, &(bport->port) ); + uart_unregister_driver( &bcm4773_uart_driver ); +#ifdef CONFIG_SPI_BCM4773_DMA + dma_free_coherent( &(spi->dev), sizeof( struct bcm4773_message ), (void *) bport->master_transfer.tx_buf, + bport->master_transfer.tx_dma ); + dma_free_coherent( &(spi->dev), sizeof( struct bcm4773_message ), (void *) bport->master_transfer.rx_buf, + bport->master_transfer.rx_dma ); + dma_free_coherent( &(spi->dev), sizeof( struct bcm4773_message ), (void *) bport->dbg_transfer.tx_buf, + bport->dbg_transfer.tx_dma ); + dma_free_coherent( &(spi->dev), sizeof( struct bcm4773_message ), (void *) bport->dbg_transfer.rx_buf, + bport->dbg_transfer.rx_dma ); +#else + kfree( bport->master_transfer.rx_buf ); + kfree( bport->master_transfer.tx_buf ); + kfree( bport->dbg_transfer.rx_buf ); + kfree( bport->dbg_transfer.tx_buf ); +#endif + free_irq( spi->irq, bport ); + kfree( bport ); + g_bport = NULL; + + return 0; +} + +static struct uart_ops bcm4773_serial_ops= +{ + .tx_empty = bcm4773_tx_empty, + .get_mctrl = bcm4773_get_mctrl, + .set_mctrl = bcm4773_set_mctrl, + .stop_tx = bcm4773_stop_tx, + .start_tx = bcm4773_start_tx, + .stop_rx = bcm4773_stop_rx, + .enable_ms = bcm4773_enable_ms, + .break_ctl = bcm4773_break_ctl, + .startup = bcm4773_startup, + .shutdown = bcm4773_shutdown, + .set_termios = bcm4773_set_termios, + .type = bcm4773_type, + .release_port = bcm4773_release_port, + .request_port = bcm4773_request_port, + .config_port = bcm4773_config_port, + .verify_port = bcm4773_verify_port, +}; + +static int bcm4773_suspend( struct device *dev, pm_message_t state ) +{ + struct spi_device *spi = container_of(dev, struct spi_device, dev); + struct bcm4773_uart_port *bport = bcm4773_spi_get_drvdata( spi ); + unsigned long int flags; + + atomic_set(&bport->suspending, 1); + + + spin_lock_irqsave( &bport->irq_lock, flags); + if (atomic_xchg(&bport->irq_enabled, 0)) { + struct irq_desc *desc = irq_to_desc(bport->port.irq); + if (desc->depth!=0) { + printk("[SSPBBD]: %s irq depth mismatch. enabled irq has depth %d!\n", __func__, desc->depth); + desc->depth = 0; + } + disable_irq_nosync(bport->port.irq); + } + spin_unlock_irqrestore( &bport->irq_lock, flags); + + flush_workqueue(bport->serial_wq ); + + if (pssp_driver->driver.pm && pssp_driver->driver.pm->suspend) + pssp_driver->driver.pm->suspend(&spi->dev); + + return 0; +} + +static int bcm4773_resume( struct device *dev ) +{ + struct spi_device *spi = container_of(dev, struct spi_device, dev); + struct bcm4773_uart_port *bport = bcm4773_spi_get_drvdata( spi ); + unsigned long int flags; + + if (pssp_driver->driver.pm && pssp_driver->driver.pm->suspend) + pssp_driver->driver.pm->resume(&spi->dev); + + atomic_set(&bport->suspending, 0); + + /* Let irq happen again */ + spin_lock_irqsave( &bport->irq_lock, flags); + if (!atomic_xchg(&bport->irq_enabled, 1)) { + struct irq_desc *desc = irq_to_desc(bport->port.irq); + if (desc->depth!=1) { + printk("[SSPBBD]: %s irq depth mismatch. disabled irq has depth %d!\n", __func__, desc->depth); + desc->depth = 1; + } + enable_irq(bport->port.irq); + } + spin_unlock_irqrestore( &bport->irq_lock, flags); + + wake_lock_timeout(&g_bport->bcm4773_wake_lock, HZ/2); + + return 0; +} + +/* Ted */ +static void bcm4773_spi_shutdown(struct spi_device *spi) +{ + pssp_driver->shutdown(spi); +} + + +static const struct spi_device_id gpsspi_id[] = { + {"ssp-spi", 0}, + {} +}; + +#ifdef CONFIG_OF +static struct of_device_id ssp_match_table[] = { + { .compatible = "ssp,BCM4773",}, + {}, +}; +#endif + +static struct spi_driver bcm4773_spi_driver= +{ + .id_table = gpsspi_id, + .probe = bcm4773_spi_probe, + .remove = bcm4773_spi_remove, + .shutdown = bcm4773_spi_shutdown, + + + .driver= + { + .name = "ssp", + .owner = THIS_MODULE, + .suspend = bcm4773_suspend, + .resume = bcm4773_resume, +#ifdef CONFIG_OF + .of_match_table = ssp_match_table +#endif + }, +}; + + + +#ifdef DEBUG_PROFILE +#define THOUSAND(p) (1000 * (p)) +#define MILLION(p) (THOUSAND(THOUSAND(p))) +unsigned long clock_get_us(void) +{ + unsigned long ulMicroseconds; + struct timeval tv; + do_gettimeofday(&tv); + ulMicroseconds = ((unsigned long) tv.tv_sec) * (unsigned long) MILLION(1); + ulMicroseconds += (unsigned long) tv.tv_usec; + return ulMicroseconds; +} +EXPORT_SYMBOL_GPL(clock_get_us); +#endif + + +#ifdef DEBUG_TRANSFER +static unsigned long init_time = 0; +static unsigned long clock_get_ms(void) +{ + struct timeval t; + unsigned long now; + + do_gettimeofday(&t); + now = t.tv_usec / 1000 + t.tv_sec * 1000; + if ( init_time == 0 ) + init_time = now; + + return now - init_time; +} + + +static void pk_log(const struct device *dev,char* dir,unsigned char cmd_stat, char* data, int len, int transferred, short status) +{ + char acB[960]; + char *p = acB; + char the_dir[10]; + int the_trans = transferred; + int i,j,n; + bool w; + + char ic = len == transferred ? 'D':'E'; + char xc = dir[0] == 'r' || dir[0] == 'w' ? 'x':'X'; + + if (likely(!ssi_dbg)) + return; + + strcpy(the_dir, dir); + + if ( the_trans == 0 ) the_dir[0] = xc; + + n = len ? len+1:0; + p += snprintf(acB,sizeof(acB),"#%06ld%c %s 0x%02X, %5d: %02X ", + clock_get_ms() % 1000000,ic, the_dir, cmd_stat,n,cmd_stat); // Where 'D' means droped data, cmd_stat instead of "data[0] & 0xff" + + w = dir[0] == 'w' || dir[0] == 'W'; + i = 0; + + for(n=31; i < len; i = j,n=32) + { + for(j = i; j < (i + n) && j < len && the_trans != 0; j++,the_trans--) { + p += snprintf(p,sizeof(acB) - (p - acB), "%02X ", data[j] & 0xFF); + } + dev_dbg(dev,"%s\n",acB); //pr_debug + if(j < len) { + p = acB; + if ( the_trans == 0 ) { the_dir[0] = xc; the_trans--; } + p += snprintf(acB,sizeof(acB)," %2s ",the_dir); + } + } + + if ( i == 0 ) + dev_dbg(dev,"%s\n",acB); //pr_debug // + + if(len != transferred) { + pr_err("error, stat %d trans'd %d\n", status,transferred); + } +} +#endif /* DEBUG */ + + +unsigned char* bcm477x_debug_buffer(size_t* len) +{ + if (!g_bport) { + return 0; + } + + return bcm4773_debug_buffer(g_bport, len); +} +EXPORT_SYMBOL(bcm477x_debug_buffer); + + + +int bcm477x_debug_write(const unsigned char* buf, size_t len, int flag) +{ + if (!g_bport) { + return 0; + } + return bcm4773_debug_transfer(g_bport, buf, len); +} +EXPORT_SYMBOL(bcm477x_debug_write); + + +int bcm477x_debug_transfer(unsigned char *tx, unsigned char *rx, int len, int flag) +{ + if (!g_bport) { + return 0; + } + return bcm4773_debug_transfer(g_bport, tx, len); +} +EXPORT_SYMBOL(bcm477x_debug_transfer); + + +static int __init bcm4773_spi_init( void ) +{ + return spi_register_driver( &bcm4773_spi_driver ); +} + +static void __exit bcm4773_spi_exit( void ) +{ + spi_unregister_driver( &bcm4773_spi_driver ); + return; +} + +module_init( bcm4773_spi_init ); +module_exit( bcm4773_spi_exit ); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("BCM4773 SPI/SSI UART Driver"); + diff --git a/drivers/sensors/brcm/bbdpl1/bcm_gps_spi.h b/drivers/sensors/brcm/bbdpl1/bcm_gps_spi.h new file mode 100644 index 0000000..69dca3f --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/bcm_gps_spi.h @@ -0,0 +1,45 @@ +#ifndef _BCM_GPS_SPI_H_ +#define _BCM_GPS_SPI_H_ + + +typedef unsigned short bcm4773_stat_t; +#define BCM4773_MSG_STAT(w) (w & 0xFF) +#define BCM4773_MSG_LEN(w) ((w & 0xFF00)>>8) +#define BCM4773_RET(msg) ((bcm4773_stat_t)msg->cmd_stat | ((bcm4773_stat_t)msg->data[0] << 8)) + +enum +{ + SSI_HW_FIFO_DEPTH = 128 + ,SSI_MAX_RW_BYTE_COUNT = 255 + ,SSI_OUTPUT_BUFFER_SIZE = 10000 // buffer size for the output. That should match what the APP sends + ,SSI_INPUT_BUFFER_SIZE = 20000 // buffer size for the input. That should match what the APP sends + ,SSI_OVERLAP_BUFFER_SIZE = 1024 // partial buffer to hold data while overlap operation completes + ,SSI_INPUT_ROUTINE_BUFFER_SIZE = 2000 +}; + +enum +{ + SSI_MODE_STREAM = 0 + ,SSI_MODE_DEBUG = 0x80 +}; + +enum +{ + SSI_MODE_HALF_DUPLEX = 0 + ,SSI_MODE_FULL_DUPLEX = 0x40 +}; + +enum +{ + SSI_WRITE_TRANS = 0x0 + ,SSI_READ_TRANS = 0x20 +}; + + +struct bcm4773_gps_platform_data { + unsigned int gpio_spi; /* HOST_REQ : to indicate that ASIC has data to send. */ + void (*suspend) (void); + void (*resume) (void); +}; + +#endif //_BCM_GPS_SPI_H_ diff --git a/drivers/sensors/brcm/bbdpl1/transport/Makefile b/drivers/sensors/brcm/bbdpl1/transport/Makefile new file mode 100644 index 0000000..23d2ec2 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/transport/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the Broadcom Bridge Driver +# + +# Each configuration option enables a list of files. +obj-$(CONFIG_SENSORS_SSP_BBD) += bbd_bridge_c.o bbd_engine.o bbd_packet_layer_c.o transport_layer_c.o diff --git a/drivers/sensors/brcm/bbdpl1/transport/bbd_bridge_c.c b/drivers/sensors/brcm/bbdpl1/transport/bbd_bridge_c.c new file mode 100644 index 0000000..a40b1bd0 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/transport/bbd_bridge_c.c @@ -0,0 +1,328 @@ +/****************************************************************************** + ** \file bbd_bridge_c.c BbdBridge is a "mini-RpcEngine" inside the BBD + * + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#include "bbd_bridge_c.h" +#include "bbd_engine.h" +#include "rpc_codec_c.h" +#include "transport_layer_c.h" +#include "bbd_packet_layer_c.h" +#include + +static bool BbdBridge_CheckPacketSanity(struct BbdBridge *p, + unsigned char *pucData, unsigned short usSize); + +static void BbdBridge_OnRpcReceived(struct BbdBridge *p, + unsigned short usRpcId, + unsigned char *pRpcPayload, unsigned short usRpcLen, + unsigned char *pPacket, unsigned short usPacketLen); + +/*------------------------------------------------------------------------------ + * + * Constructor() + * + *------------------------------------------------------------------------------ + */ + +struct BbdBridge *BbdBridge_BbdBridge(struct BbdBridge *p, + struct sITransportLayerCb* rCallbacks) +{ + p->callback = rCallbacks; + TransportLayer_TransportLayer(&p->m_otTL, rCallbacks, "BBDtl"); + p->m_pRpcDecoderHead = 0; + StreamEncoder_StreamEncoder(&p->m_otStreamEncoder, + p->m_ucStreamBuffer, + sizeof(p->m_ucStreamBuffer)); + p->m_uiTransactionPayloadSize = 0; + return p; +} + +void BbdBridge_dtor(struct BbdBridge *p) +{ + TransportLayer_dtor(&p->m_otTL); +} + +void BbdBridge_RegisterRpcDecoder(struct BbdBridge *p, + struct sRpcDecoder* pRpc) +{ + p->m_pRpcDecoderHead = pRpc; +} + +void BbdBridge_UnregisterRpcDecoder(struct BbdBridge *p, + struct sRpcDecoder *pRpcDecoder) +{ + p->m_pRpcDecoderHead = NULL; +} + +/*------------------------------------------------------------------------------ + * + * OnPacketReceived() + * + * Convert the output of normal transactions from the ESW PacketLayer + * into separate RPCs and send the data to the RpcEngine: + * - The only fully decoded RPCs are sensor responses. These are + * sent directly to the SHMD. + * - packet data goes to /dev/bbd_packet + * + *------------------------------------------------------------------------------ + */ + +void BbdBridge_OnPacketReceived(struct sTransportLayer* pp, + unsigned char *pucData, + unsigned short usSize) +{ + struct BbdBridge* p = (struct BbdBridge *) pp; + if (BbdBridge_CheckPacketSanity(p, pucData, usSize)) + { + long lSize = (long)usSize; + + while (lSize > 0) + { + unsigned char* pucDataOrig = pucData; + unsigned short usRpcId = *pucData++; + unsigned short usRpcLen; + lSize--; + + if (usRpcId&0x80) + { + usRpcId &= ~0x80; + usRpcId <<= 8; + usRpcId |= *pucData++; lSize--; + } + + usRpcLen = *pucData++; lSize--; + if (usRpcLen&0x80) + { + usRpcLen &= ~0x80; + usRpcLen <<= 8; + usRpcLen |= *pucData++; lSize--; + } + + BbdBridge_OnRpcReceived(p, usRpcId, pucData, usRpcLen, + pucDataOrig, usRpcLen+pucData-pucDataOrig); + pucData += usRpcLen; + lSize -= usRpcLen; + } + } + else + { + p->callback->OnException(p->callback, __FILE__, __LINE__); + } +} + + +bool BbdBridge_CheckPacketSanity(struct BbdBridge* p, + unsigned char *pucData, unsigned short usSize) +{ + long lSize = (long) usSize; + while (lSize > 0) + { + unsigned short usRpcId = *pucData++; + unsigned short usRpcLen; + lSize--; + + if (usRpcId&0x80) + { + usRpcId &= ~0x80; + usRpcId <<= 8; + usRpcId |= *pucData++; lSize--; + } + + usRpcLen = *pucData++; lSize--; + if (usRpcLen&0x80) + { + usRpcLen &= ~0x80; + usRpcLen <<= 8; + usRpcLen |= *pucData++; lSize--; + } + + pucData += usRpcLen; + lSize -= usRpcLen; + } + + return lSize == 0; +} + + +void BbdBridge_OnRpcReceived(struct BbdBridge *p, + unsigned short usRpcId, + unsigned char *pRpcPayload, + unsigned short usRpcLen, + unsigned char *pPacket, + unsigned short usPacketLen) +{ + struct sStreamDecoder str; + struct sRpcDecoder* pDecoder = p->m_pRpcDecoderHead; + + StreamDecoder_StreamDecoder(&str, pRpcPayload, usRpcLen); + + /* Check our one decoder (sensors) */ + if (pDecoder && pDecoder->cb(pDecoder, usRpcId, &str)) + { + /* ensure the stream has been emptied completely and did not go beyond + */ + if (StreamCodec_GetAvailableSize(&str)) + { + p->callback->OnException(p->callback,__FILE__, __LINE__); + } + else if (StreamCodec_Fail(&str)) + { + p->callback->OnException(p->callback,__FILE__, __LINE__); + } + } + else + { + /* Send this data upwards, to the LHD. */ + p->callback->OnDataToLhd(p->callback, pPacket, usPacketLen); + } +} + +/*------------------------------------------------------------------------------ + * + * SendSensorData() + * + * Convert a raw sensor data stream from the SHMD into and RPC + * and send the data to the packet layer. + * + * This is a "mini RpcEngine" + * + *------------------------------------------------------------------------------ + */ + +bool BbdBridge_SendSensorData(struct BbdBridge* p, + unsigned char* pSensorData, unsigned short size) +{ + if (BbdEngine_Lock(__LINE__)) { + struct sStreamEncoder* str = &p->m_otStreamEncoder; + StreamCodec_Reset (str); + StreamEncoder_PutU16 (str, size); + StreamEncoder_PutBuffer(str, pSensorData, size); + + if (StreamCodec_Fail(str)) + { + BbdEngine_Unlock(); + return false; + } + if (!BbdBridge_AddRpc(p, RPC_DEFINITION(IRpcSensorRequest, Data))) + { + BbdEngine_Unlock(); + return false; + } + TransportLayer_SendPacket(&p->m_otTL, + p->m_aucTransactionPayload, + p->m_uiTransactionPayloadSize); + p->m_uiTransactionPayloadSize = 0; + + BbdEngine_Unlock(); + return true; + } + return false; +} + +bool BbdBridge_AddRpc(struct BbdBridge* p, unsigned short usRpcId) +{ + struct sStreamCodec* str = &p->m_otStreamEncoder; + unsigned short usRpcLen = StreamCodec_GetOffset(str); + unsigned int uiNumBytes = usRpcLen +1 +1; + /* 1 for usRpcId, 1 for usRpcLen*/ + + if (usRpcId > 128) /* large RPC IDs use 2 bytes */ + ++uiNumBytes; + if (usRpcLen > 128) + ++uiNumBytes; + + if ((p->m_uiTransactionPayloadSize+uiNumBytes) >= MAX_OUTGOING_PACKET_SIZE) + { + /* not enough space */ + return false; + /* ASSERT(0); // too many RPCs! doesn't fit inside the TL */ + } + + if (usRpcId >= 0x80) + p->m_aucTransactionPayload[p->m_uiTransactionPayloadSize++] = + ((usRpcId>>8) & 0xFF) | 0x80; + p->m_aucTransactionPayload[p->m_uiTransactionPayloadSize++] = usRpcId&0xFF; + + if (usRpcLen >= 0x80) + p->m_aucTransactionPayload[p->m_uiTransactionPayloadSize++] = + ((usRpcLen>>8) & 0xFF) | 0x80; + p->m_aucTransactionPayload[p->m_uiTransactionPayloadSize++] = usRpcLen&0xFF; + + memcpy(&p->m_aucTransactionPayload[p->m_uiTransactionPayloadSize], + StreamCodec_GetStreamBuffer(str), + StreamCodec_GetOffset (str) ); + p->m_uiTransactionPayloadSize += usRpcLen; + return true; +} + +/* called when LHD has a packet to send */ +void BbdBridge_SendPacket(struct BbdBridge *p, unsigned char* pkt, size_t len) +{ + if (!BbdEngine_Lock(__LINE__)) return; + BbdTransportLayer_SendPacket(&p->m_otTL, pkt, len); + BbdEngine_Unlock(); +} + +bool BbdBridge_SendReliablePacket(struct BbdBridge *p, + struct sBbdReliableTransaction *trans) +{ + bool result = false; + if (BbdEngine_Lock(__LINE__)) { + result = BbdTransportLayer_SendReliablePacket(&p->m_otTL, trans); + BbdEngine_Unlock(); + } + return result; +} + +bool BbdBridge_SetControlMessage(struct BbdBridge *p, char *msg) +{ + bool result = false; + if (BbdEngine_Lock(__LINE__)) { + result = BbdTransportLayer_SetControlMessage(&p->m_otTL, msg); + BbdEngine_Unlock(); + } + return result; +} + +/* called when remote data are received */ + +void BbdBridge_SetData(struct BbdBridge *p, + const unsigned char *pucData, + unsigned short usSize) +{ + if (!BbdEngine_Lock(__LINE__)) return; + if (BbdTransportLayer_IsPassThrough(&p->m_otTL)) + { + p->callback->OnDataToLhd(p->callback, pucData, usSize); + } + else + { + TransportLayer_SetData(&p->m_otTL, pucData, usSize); + } + BbdEngine_Unlock(); +} + +/* Provide a StreamEncoder that can be used when building a transaction */ + +struct sStreamEncoder* BbdBridge_GetStreamEncoder(struct BbdBridge *p) +{ + return &p->m_otStreamEncoder; +} diff --git a/drivers/sensors/brcm/bbdpl1/transport/bbd_bridge_c.h b/drivers/sensors/brcm/bbdpl1/transport/bbd_bridge_c.h new file mode 100644 index 0000000..2632a34 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/transport/bbd_bridge_c.h @@ -0,0 +1,138 @@ +/****************************************************************************** + ** \file bbd_bridge_c.h TransportLayer inside the BBD + * + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#ifndef BBD_BRIDGE_C_H // { +#define BBD_BRIDGE_C_H + +#include + +/* #include "rpc_engine.h" */ +#include "transport_layer_c.h" +#include "../utils/stream_codec_c.h" + +#define MAX_RPC_HEADER_SIZE 4 /* 2 for SeqId, 2 for Size */ +#define MAX_TRANSACTION_SIZE MAX_OUTGOING_PACKET_SIZE /* maximum size per transaction */ +#define MAX_RPC_PAYLOAD (MAX_TRANSACTION_SIZE-MAX_RPC_HEADER_SIZE) /* maximum size per RPC payload */ + +typedef bool (*ProcessRpc) (void* data, unsigned short usRpcId, + struct sStreamDecoder *rStream); + +#ifdef __cplusplus +extern "C" { +#endif + +struct sRpcDecoder +{ + ProcessRpc cb; + void *data; + struct sRpcDecoder *m_pNext; +}; + +/** +* The BbdBridge class is a "mini RPC engine" that resides inside the BBD Linux +* device driver. BBD means "Broadcom Bridge Driver". +* +* The BbdBridge has these features and device channels: +* +* LHD-->BBD +* receives packets from the LHD via /dev/bbd_packet +* receives reliable packets from the LHD via /dev/bbd_reliable +* receives control strings via /dev/bbd_control +* reliable sync request +* enable/disable the packet layer +* +* LHD<--BBD +* sends packets to the LHD via /dev/bbd_packet +* sends reliable packet acknowledgements to the LHD via /dev/bbd_reliable +* sends control results to /dev/bbd_control: +* reliable sync completed +* +* SHMD<--BBD +* supports ONE RPC callback for sensors. This data comes from the shmd device +* driver interface. +* SHMD-->BBD +* Converts the raw sensor data into an RPC suitable for consumption by the ESW +* and transport layer. Only normal transactions are supported. +* +* BBD internals +* Maintains a packet layer that is the peer of the ESW's packet layer. +* Has an option to disable this packet layer (for testing intermediate steps). +* The packet layer is a BbdTransportLayer (slightly modified) TransportLayer +* +* SHMD-->LHD +* Reset request string. So far, doesn't affect BbdBridge. +* +* SHMD<--LHD +* ESW status messages (READY, NOTREADY, CRASHED). +*/ + +struct BbdBridge +{ + struct sTransportLayer m_otTL; /* BbdBridge-->TransportLayer */ + struct sITransportLayerCb* callback; /* BBD<--BbdBridge */ + + /* pointer to the first decoder. If more are registered, then + * they are added to a linked list + */ + struct sRpcDecoder* m_pRpcDecoderHead; + + /* place to hold a (sensor) packet before it is sent */ + struct sStreamEncoder m_otStreamEncoder; + unsigned char m_ucStreamBuffer [MAX_RPC_PAYLOAD ]; + unsigned char m_aucTransactionPayload[MAX_OUTGOING_PACKET_SIZE]; + unsigned int m_uiTransactionPayloadSize; +}; + +struct BbdBridge *BbdBridge_BbdBridge(struct BbdBridge *p, + struct sITransportLayerCb* rCallbacks); +void BbdBridge_dtor(struct BbdBridge *p); + +void BbdBridge_RegisterRpcDecoder (struct BbdBridge *p, struct sRpcDecoder *pRpcDecoder); +void BbdBridge_UnregisterRpcDecoder(struct BbdBridge *p, struct sRpcDecoder *pRpcDecoder); + +bool BbdBridge_AddRpc(struct BbdBridge *p, unsigned short usRpcId); +bool BbdBridge_SendSensorData(struct BbdBridge *p, unsigned char* data, unsigned short size); + +void BbdBridge_SetPassThrough(struct BbdBridge *p, bool passthru); + +/* called when LHD has a packet to send */ +void BbdBridge_SendPacket(struct BbdBridge *p, unsigned char* data, size_t len); +bool BbdBridge_SendReliablePacket(struct BbdBridge *p, struct sBbdReliableTransaction *trans); +bool BbdBridge_SetControlMessage(struct BbdBridge *p, char *msg); + +/* called when remote data are received */ +void BbdBridge_SetData(struct BbdBridge *p, const unsigned char *pData, unsigned short usSize); + +/* called when remote packet is decoded */ +void BbdBridge_OnPacketReceived(struct sTransportLayer* p, + unsigned char *pucData, + unsigned short usSize); + +/* Provide a StreamEncoder that can be used when building a transaction */ +struct sStreamEncoder* BbdBridge_GetStreamEncoder(struct BbdBridge *p); + +#ifdef __cplusplus +} +#endif + +#endif // } BBD_BRIDGE_H + diff --git a/drivers/sensors/brcm/bbdpl1/transport/bbd_engine.c b/drivers/sensors/brcm/bbdpl1/transport/bbd_engine.c new file mode 100644 index 0000000..eeb618b --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/transport/bbd_engine.c @@ -0,0 +1,417 @@ +/****************************************************************************** + ** \file bbd_engine.c Engine to control the BBD bridge + * + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#include "../bbd_internal.h" +#include "bbd_engine.h" +#include "bbd_bridge_c.h" +#include "../utils/stream_codec_c.h" +#include "rpc_codec_c.h" +#include "../utils/bbd_utils.h" +#include "../bbd_ifc.h" + +#include + +void BbdEngine_callback(struct sITransportLayerCb *p, struct BbdEngine *eng); +void BbdEngine_SetUp(struct BbdEngine *p); + +ssize_t bbd_send_packet(unsigned char *pbuff, size_t size); + +//---------------------------------------------------------------------------- +// +// BBD Engine +// +//---------------------------------------------------------------------------- + +bool BbdEngine_OnControlMessageToSend(void* p, const char* pcMsg) +{ + ssize_t len = strlen(pcMsg) + 1; + ssize_t res = bbd_on_read(BBD_MINOR_CONTROL, (unsigned char*)pcMsg, len); + return len == res; +} + +// The synchronization with the remote side is complete, you can start using the +// RpcEngine to communicate with the remote side +void BbdEngine_OnRemoteSyncComplete(void* p) +{ + BbdEngine_OnControlMessageToSend(p, BBD_CTRL_PL_REMOTE_SYNC_COMPLETE); +} + +// GetStats() is needed when the PL is pushed out of the TL into the BBD. +bool BbdEngine_GetStats(void* p, struct stTransportLayerStats *rStats) +{ + return false; +} + +/* Could also use the jiq, but I don't see how to delay the + * scheduling of it. So use a fully-featured work queue. + */ +static struct workqueue_struct *workq; /* = 0; ** Linux code standard: must assume static is cleared to zero */ +static struct delayed_work bbd_tick_work; + +static void BbdEngine_Tick(struct work_struct *work) +{ + printk(KERN_INFO"%s()\n", __func__); + gpbbd_dev->bbd_engine.tickWorking = false; + bbd_on_read(BBD_MINOR_CONTROL, BBD_CTRL_TICK, sizeof(BBD_CTRL_TICK)); +} + +static void BbdEngine_StartWork(struct BbdEngine* p) +{ + if (p && p->constructed && !workq) { + p->tickWorking = false; + workq = create_singlethread_workqueue("BbdTick"); + INIT_DELAYED_WORK(&bbd_tick_work, BbdEngine_Tick); + } +} + +static void BbdEngine_StopWork(struct BbdEngine* p) +{ + const char *workq_message = "No"; + + cancel_delayed_work(&bbd_tick_work); + + if (workq) { + flush_workqueue(workq); + workq_message = "Destroyed"; + destroy_workqueue(workq); + workq = 0; + } +} + +void BbdEngine_Close(struct BbdEngine* p) +{ + if (p && p->constructed && p->open) { + FUNC(); + p->open = false; + BbdEngine_Lock(__LINE__); //should lock BBD here. Otherwise it may crash while handling packet. + BbdBridge_dtor(&p->bridge); + BbdEngine_StopWork(p); + BbdEngine_Unlock(); + } +} + +struct BbdEngine* BbdEngine_Open(struct BbdEngine* p) +{ + if (p && p->constructed && !p->open) { + FUNC(); + p->open = true; + BbdEngine_StartWork(p); + BbdBridge_BbdBridge(&p->bridge, &p->callback); + BbdEngine_SetUp(p); + } + return p; +} + +struct BbdEngine* BbdEngine_BbdEngine(struct BbdEngine* p) +{ + if (p && !p->constructed) { + FUNC(); + mutex_init(&p->lock); + workq = 0; + p->constructed = true; + } + return p; +} + +void BbdEngine_dtor(struct BbdEngine* p) +{ + if (p && p->constructed) { + BbdEngine_Close(p); + p->constructed = false; + } +} + +#define LOCK_FEATURE +#undef DEBUG_LOCK_FEATURE + +bool BbdEngine_Lock(int from) +{ +#ifdef LOCK_FEATURE +#ifdef DEBUG_LOCK_FEATURE + FUNI(from); + if (gpbbd_dev && gpbbd_dev->bbd_engine.constructed) { + if (mutex_is_locked(&gpbbd_dev->bbd_engine.lock)) + FUNS("locked!"); + } + else + FUNS("uninit"); +#endif + if (gpbbd_dev && gpbbd_dev->bbd_engine.constructed) { + mutex_lock(&gpbbd_dev->bbd_engine.lock); +#ifdef DEBUG_LOCK_FEATURE + FUNS("locked{..."); +#endif + return true; + } + return false; +#else + return (gpbbd_dev && gpbbd_dev->bbd_engine.constructed); +#endif +} + +void BbdEngine_Unlock(void) +{ +#ifdef LOCK_FEATURE + if (gpbbd_dev) {// && gpbbd_dev->bbd_engine.constructed) +#ifdef DEBUG_LOCK_FEATURE + FUNS("...} unlocked"); +#endif + mutex_unlock(&gpbbd_dev->bbd_engine.lock); + } +#endif +} + +ssize_t BbdEngine_SendSensorData(struct BbdEngine* p, unsigned char *pbuff, size_t size) +{ + if (BbdBridge_SendSensorData(&p->bridge, pbuff, size)) + return size; + return -EINVAL; +} + +bool BbdEngine_AddRpc(unsigned short usRpcId, struct sStreamCodec *rRpcStream); + +/* send sensor data to the SHMD */ +extern ssize_t bbd_sensor_write_internal(const char *sensorData, size_t size); + + +bool SensorRpc_ProcessRpc(void* p, unsigned short usRpcId, + struct sStreamDecoder *str) +{ + if (usRpcId == RPC_DEFINITION(IRpcSensorResponse, Data)) + { + unsigned short size = StreamDecoder_GetU16(str); + unsigned char* sensorData = StreamDecoder_GetBuffer(str, size); + ssize_t result = bbd_sensor_write_internal(sensorData , size); + return ((short) result == size); + } + return false; +} + +void BbdEngine_SetUp(struct BbdEngine *p) +{ + p->m_rpcSensor.cb = SensorRpc_ProcessRpc; + p->m_rpcSensor.data = p; + p->m_rpcSensor.m_pNext = 0; + + BbdBridge_RegisterRpcDecoder(&p->bridge, + (gpbbd_dev->shmd) ? &p->m_rpcSensor : 0); + BbdEngine_callback(&p->callback, p); +} + +/*------------------------------------------------------------------------------ + * + * BbdEngineCb{} + * + *------------------------------------------------------------------------------ + */ + +static struct BbdEngine *GetEngine(struct sITransportLayerCb *pp) +{ + return (pp) ? ((struct BbdEngine *) pp->m_callbackData) : 0; +} + + +void BbdTransportCB_OnTimerSet(struct sITransportLayerCb *pp, long lTimerMs) +{ + if (gpbbd_dev->bbd_engine.tickWorking) { + cancel_delayed_work(&bbd_tick_work); + gpbbd_dev->bbd_engine.tickWorking = false; + } + if (lTimerMs > 0) { + unsigned long delay_jiffies = msecs_to_jiffies(lTimerMs); + gpbbd_dev->jiffies_to_wake = jiffies + delay_jiffies; + FUNI(lTimerMs); + gpbbd_dev->bbd_engine.tickWorking = true; + queue_delayed_work(workq, &bbd_tick_work, delay_jiffies); + } + else { + FUNC(); + gpbbd_dev->jiffies_to_wake = 0; + } +} + +/*------------------------------------------------------------------------------ + * + * BbdEngine_OnTimer() + * + * Call here periodically to see if the timer should expire. + * + * To avoid a separate thread, send nonsense message "BBD:tick" to + * waken the LHD via bbd_control(). Then bbd_read() will call + * BbdEngine_OnTimer(). + * + *------------------------------------------------------------------------------ + */ + +unsigned long BbdEngine_OnTimer(void) +{ + struct bbd_device *p = gpbbd_dev; + unsigned long result = 0; + if (!workq || mutex_is_locked(&p->bbd_engine.lock)) + return 0; + + if (BbdEngine_Lock(__LINE__)) { + if (p->jiffies_to_wake) + { + if (p->jiffies_to_wake > jiffies) { + result = p->jiffies_to_wake - jiffies; + } + else { + p->jiffies_to_wake = 0; + FUNC(); + TransportLayer_Tick(&p->bbd_engine.bridge.m_otTL); + } + } + BbdEngine_Unlock(); + } + return result; +} + +unsigned long BbdTransportCB_OnTimeMsRequest(struct sITransportLayerCb *pp) +{ + return jiffies_to_msecs(jiffies); +} + + +/*------------------------------------------------------------------------------ + * + * Callbacks + * + * Connect BBD output to the real: + * + * - TTY - bbd_tty.c + * - SSI-SPI - TODO: add code here to connect to the SPI driver: + * + *------------------------------------------------------------------------------ + */ + +extern int bbd_tty_send(unsigned char *buf, int count); +extern int bbd_ssi_spi_send(unsigned char *buf, int count); + +void BbdTransportCB_OnDataToSend_TTY(struct sITransportLayerCb *pp, + unsigned char *data, unsigned long len) +{ + bbd_tty_send(data, len); +} + +void BbdTransportCB_OnDataToSend_SSI_SPI(struct sITransportLayerCb *pp, + unsigned char *data, unsigned long len) +{ + bbd_ssi_spi_send(data, len); +} + +void BbdTransportCB_OnReliableAck(struct sITransportLayerCb *pp, + struct sBbdReliableTransaction* trans) +{ + bbd_on_read(BBD_MINOR_RELIABLE, (unsigned char*) trans, sizeof(*trans)); +} + +bool BbdTransportCB_OnControlMessageToSend(struct sITransportLayerCb *pp, + char* pcMsg) +{ + return BbdEngine_OnControlMessageToSend(pp, pcMsg); +} + +void BbdTransportCB_OnCommunicationError(struct sITransportLayerCb *pp) +{ + /* struct BbdEngine *eng = GetEngine(pp); */ + /* FUNC(); */ + bbd_on_read(BBD_MINOR_CONTROL, + BBD_CTRL_PL_ON_COMMUNICATION_ERROR, + sizeof(BBD_CTRL_PL_ON_COMMUNICATION_ERROR)); +} + + +void BbdTransportCB_OnPacketReceived(struct sITransportLayerCb *pp, + unsigned char *pucData, unsigned short usSize) +{ + struct BbdEngine *eng = GetEngine(pp); + FUNI(usSize); + if (eng) + BbdBridge_OnPacketReceived(&eng->bridge.m_otTL, pucData, usSize); +} + +/* an internal error occured */ +void BbdTransportCB_OnException(struct sITransportLayerCb *pp, + const char filename[], unsigned int uiLine) +{ + char buf[80] = {0}; + char* prefix = ""; + int len = strlen(filename); + + FUNSI(filename, uiLine); + if (len > 30) + { + prefix = "..."; + filename += len - 30; + } + snprintf(buf, sizeof(buf)-1, BBD_CTRL_PL_ON_EXCEPTION + "%s%s/%d", prefix, filename, uiLine); + BbdEngine_OnControlMessageToSend(pp, buf); +} + +void BbdTransportCB_OnRemoteSyncComplete(struct sITransportLayerCb *pp) +{ + FUNC(); + BbdTransportCB_OnControlMessageToSend(pp, BBD_CTRL_PL_REMOTE_SYNC_COMPLETE); +} + +void BbdTransportCB_OnDataToLhd(struct sITransportLayerCb* pp, + const unsigned char *data, unsigned short size) +{ + FUNI(size); + bbd_on_read(BBD_MINOR_PACKET, data, size); +} + +/*------------------------------------------------------------------------------ + * + * Callbacks + * + *------------------------------------------------------------------------------ + */ + +void BbdEngine_callback(struct sITransportLayerCb *p, struct BbdEngine *eng) +{ + p->m_callbackData = eng; + p->OnTimerSet = BbdTransportCB_OnTimerSet; + p->OnTimeMsRequest = BbdTransportCB_OnTimeMsRequest; + p->OnCommunicationError = BbdTransportCB_OnCommunicationError; + p->OnPacketReceived = BbdTransportCB_OnPacketReceived; + p->OnException = BbdTransportCB_OnException; + p->OnRemoteSyncComplete = BbdTransportCB_OnRemoteSyncComplete; + p->OnReliableAck = BbdTransportCB_OnReliableAck; + p->OnDataToLhd = BbdTransportCB_OnDataToLhd; + + p->OnDataToSend = BbdTransportCB_OnDataToSend_SSI_SPI; + switch(gpbbd_dev->sio_type) + { + case BBD_SERIAL_TTY: + p->OnDataToSend = BbdTransportCB_OnDataToSend_TTY; + break; + case BBD_SERIAL_SPI: + p->OnDataToSend = BbdTransportCB_OnDataToSend_SSI_SPI; + break; + default: + p->OnDataToSend = BbdTransportCB_OnDataToSend_SSI_SPI; + break; + } +} diff --git a/drivers/sensors/brcm/bbdpl1/transport/bbd_engine.h b/drivers/sensors/brcm/bbdpl1/transport/bbd_engine.h new file mode 100644 index 0000000..33868d72 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/transport/bbd_engine.h @@ -0,0 +1,60 @@ +/****************************************************************************** + ** \file bbd_engine.h Engine to control the BBD bridge + * + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#ifndef BBD_ENGINE_H_ /* { */ +#define BBD_ENGINE_H_ + +#include "bbd_bridge_c.h" +#include + +//---------------------------------------------------------------------------- +// +// BBD Engine +// +// Thin wrapper for the BbdBridge class to connect it to the bbd device driver. +// +//---------------------------------------------------------------------------- + +struct BbdEngine +{ + struct BbdBridge bridge; + struct mutex lock; + struct sRpcDecoder m_rpcSensor; + unsigned char m_aucTransactionPayload[MAX_OUTGOING_PACKET_SIZE]; + unsigned long m_uiTransactionPayloadSize; + struct sITransportLayerCb callback; + bool tickWorking; + bool constructed; + bool open; +}; + +struct BbdEngine* BbdEngine_BbdEngine(struct BbdEngine* p); + +bool BbdEngine_OnControlMessageToSend(void* p, const char* pcMsg); +void BbdEngine_dtor(struct BbdEngine* p); +ssize_t BbdEngine_SendSensorData(struct BbdEngine* p, unsigned char *pbuff, size_t size); +void BbdEngine_SetUp(struct BbdEngine *p); +unsigned long BbdEngine_OnTimer(void); +bool BbdEngine_Lock (int from); +void BbdEngine_Unlock(void); + +#endif // } BBD_ENGINE_H_ diff --git a/drivers/sensors/brcm/bbdpl1/transport/bbd_packet_layer_c.c b/drivers/sensors/brcm/bbdpl1/transport/bbd_packet_layer_c.c new file mode 100644 index 0000000..a901885 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/transport/bbd_packet_layer_c.c @@ -0,0 +1,124 @@ +/****************************************************************************** + ** \file bbd_packet_layer_c.c TransportLayer inside the BBD + * + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#include "bbd_packet_layer_c.h" +#include + +struct sBbdReliableTransaction* BbdReliableTransaction_init(struct sBbdReliableTransaction* p) +{ + memset(p, 0, sizeof(*p)); + return p; +} + +int bbd_tty_autobaud(void); + +bool BbdTransportLayer_SetControlMessage(struct sTransportLayer* p, + char* pcMessage) +{ + if (strstr(pcMessage, BBD_CTRL_PASSTHRU_ON)) + { + p->m_bPassThrough = true; + return true; + } + if (strstr(pcMessage, BBD_CTRL_PASSTHRU_OFF)) + { + p->m_bPassThrough = false; + return true; + } + if (strstr(pcMessage, BBD_CTRL_PL_START_REMOTE_SYNC)) + { + TransportLayer_StartRemoteSync(p); + return true; + } + return false; +} + +/* The BbdTransportLayer does reliable packet transfers via + * the BbdReliableTransaction structure. + */ + +bool BbdTransportLayer_SendReliablePacket(struct sTransportLayer* p, + struct sBbdReliableTransaction* trans) +{ + if (BbdTransportLayer_IsPassThrough(p)) + { + p->m_callback->OnException(p->m_callback, __FILE__, __LINE__); + return false; + } + return TransportLayer_SendReliablePacket(p, trans->pucData, trans->usSize, + trans->cb, trans->pCbData, trans->ulCbData); +} + +/* Send the reliable ack back to the user layer as we cannot call + * the BBD directly. + */ + +void BbdTransportLayer_DispatchReliableAck(struct sTransportLayer* p, + struct stCallback *resp) +{ + struct sBbdReliableTransaction trans; + if (BbdTransportLayer_IsPassThrough(p)) + { + p->m_callback->OnException(p->m_callback, __FILE__, __LINE__); + return; + } + BbdReliableTransaction_init(&trans); + trans.cb = resp->cb; + trans.pCbData = resp->pCbData; + trans.ulCbData = resp->ulCbData; + + /* notify user the packet was delivered */ + p->m_callback->OnReliableAck(p->m_callback, &trans); +} + +void BbdTransportLayer_SetData(struct sTransportLayer* p, + unsigned char* pucData, unsigned short usSize) +{ + TransportLayer_SetData(p, pucData, usSize); +} + +bool BbdTransportLayer_IsPassThrough(const struct sTransportLayer * const p) +{ + return p->m_bPassThrough; +} + +void BbdTransportLayer_SetPassThrough(struct sTransportLayer *p, + bool bPassThrough) +{ + p->m_bPassThrough = bPassThrough; +} + +/* Send a packet to the remote TransportLayer. */ + +void BbdTransportLayer_SendPacket(struct sTransportLayer *p, + unsigned char *pucData, unsigned short usSize) +{ + if (BbdTransportLayer_IsPassThrough(p)) + { + p->m_callback->OnDataToSend(p->m_callback, pucData, usSize); + } + else + { + TransportLayer_SendPacket(p, pucData, usSize); + } +} + diff --git a/drivers/sensors/brcm/bbdpl1/transport/bbd_packet_layer_c.h b/drivers/sensors/brcm/bbdpl1/transport/bbd_packet_layer_c.h new file mode 100644 index 0000000..cfd6c6b --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/transport/bbd_packet_layer_c.h @@ -0,0 +1,61 @@ +/****************************************************************************** + ** \file bbd_packet_layer_c.h Bridge (or mini-RpcEngine) inside the BBD + * + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#ifndef BBD_PACKET_LAYER_C_H // { +#define BBD_PACKET_LAYER_C_H + +#include "transport_layer_c.h" +#include "../bbd_ifc.h" + +#ifdef __cplusplus +extern "C" { +#endif // } defined __cplusplus + + +/* -------------------- BBD-specific methods ------------------- */ + +bool BbdTransportLayer_IsPassThrough(const struct sTransportLayer * const p); +void BbdTransportLayer_SetPassThrough(struct sTransportLayer *p, + bool bPassThrough); + +/* Send a packet to the remote TransportLayer. */ +void BbdTransportLayer_SendPacket(struct sTransportLayer *p, + unsigned char *pucData, unsigned short usSize); + +/* kernel-space reliable packet API */ +bool BbdTransportLayer_SendReliablePacket(struct sTransportLayer *p, struct sBbdReliableTransaction* trans); + +void BbdTransportLayer_SetData(struct sTransportLayer *p, + unsigned char* pucData, unsigned short usSize); + +bool BbdTransportLayer_SetControlMessage(struct sTransportLayer *p, + char* pcMessage); + +void BbdTransportLayer_DispatchReliableAck(struct sTransportLayer *p, + struct stCallback *resp); + + +#ifdef __cplusplus + } +#endif // } defined __cplusplus + +#endif // } BBD_PACKET_LAYER_H diff --git a/drivers/sensors/brcm/bbdpl1/transport/rpc_codec_c.h b/drivers/sensors/brcm/bbdpl1/transport/rpc_codec_c.h new file mode 100644 index 0000000..d1b27e2 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/transport/rpc_codec_c.h @@ -0,0 +1,81 @@ +/****************************************************************************** + * \file rpc_codec_c.h RPC Codec class declaration + * + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#ifndef RPC_CODEC_C_H +#define RPC_CODEC_C_H + + +/* Enumeration of all the RPCs for the codec. DO NOT CHANGE THE ORDER, + * DELETE, or INSERT anything to keep backward compatibility. + */ +#define RPC_DEFINITION(klass, method) RPC_##klass##_##method +enum +{ + RPC_DEFINITION(IRpcA, A) + ,RPC_DEFINITION(IRpcA, B) + ,RPC_DEFINITION(IRpcA, C) + + ,RPC_DEFINITION(IRpcB, A) + ,RPC_DEFINITION(IRpcB, B) + + ,RPC_DEFINITION(IRpcC, A) + ,RPC_DEFINITION(IRpcC, B) + ,RPC_DEFINITION(IRpcC, C) + + ,RPC_DEFINITION(IRpcD, A) + + ,RPC_DEFINITION(IRpcE, A) + ,RPC_DEFINITION(IRpcE, B) + + ,RPC_DEFINITION(IRpcF, A) + ,RPC_DEFINITION(IRpcF, B) + ,RPC_DEFINITION(IRpcF, C) + + ,RPC_DEFINITION(IRpcG, A) + ,RPC_DEFINITION(IRpcG, B) + ,RPC_DEFINITION(IRpcG, C) + ,RPC_DEFINITION(IRpcG, D) + ,RPC_DEFINITION(IRpcG, E) + + ,RPC_DEFINITION(IRpcH, A) + ,RPC_DEFINITION(IRpcH, B) + ,RPC_DEFINITION(IRpcH, C) + ,RPC_DEFINITION(IRpcH, D) + + ,RPC_DEFINITION(IRpcI, A) + ,RPC_DEFINITION(IRpcI, B) + ,RPC_DEFINITION(IRpcI, C) + + ,RPC_DEFINITION(IRpcJ, A) + ,RPC_DEFINITION(IRpcJ, B) + + ,RPC_DEFINITION(IRpcK, A) + ,RPC_DEFINITION(IRpcK, B) + ,RPC_DEFINITION(IRpcK, C) + + ,RPC_DEFINITION(IRpcL, A) + + ,RPC_DEFINITION(IRpcSensorRequest, Data) + ,RPC_DEFINITION(IRpcSensorResponse, Data) +}; + +#endif /* RPC_CODEC_C_H */ diff --git a/drivers/sensors/brcm/bbdpl1/transport/transport_layer_c.c b/drivers/sensors/brcm/bbdpl1/transport/transport_layer_c.c new file mode 100644 index 0000000..598898f --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/transport/transport_layer_c.c @@ -0,0 +1,1692 @@ +/****************************************************************************** + ** \file transport_layer_c.c TransportLayer struct definition + * + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#include "../utils/bbd_utils.h" +#include "../bbd_internal.h" +#include "bbd_packet_layer_c.h" +#include "../utils/crc8bits_c.h" +#include "../utils/stream_codec_c.h" +#include "bbd_bridge_c.h" + +/* +* The following are used for the software flow control (UART) +*/ +static const unsigned char XON_CHARACTER = 0x11; +static const unsigned char XOFF_CHARACTER = 0x13; + +/* +* The following are used for the protocol. +*/ +static const unsigned char ESCAPE_CHARACTER = 0xB0; +static const unsigned char SOP_CHARACTER = 0x00; +static const unsigned char EOP_CHARACTER = 0x01; +static const unsigned char ESCAPED_ESCAPE_CHARACTER = 0x03; +static const unsigned char ESCAPED_XON_CHARACTER = 0x04; +static const unsigned char ESCAPED_XOFF_CHARACTER = 0x05; + +/* +* The following are the bit field definition for the flags +*/ +static const unsigned short FLAG_PACKET_ACK = (1<<0); // ACK of a received packet. Flag detail contains the ACK SeqId +static const unsigned short FLAG_RELIABLE_PACKET = (1<<1); // Indicates that this is a reliable packet. Flag detail contains the remote reliable seqId +static const unsigned short FLAG_RELIABLE_ACK = (1<<2); // A reliable SeqId was Acked. Flag detail contains the acked reliable seqId +static const unsigned short FLAG_RELIABLE_NACK = (1<<3); // A reliable SeqId error was detected. Flag detail contains the last remote reliable seqId +static const unsigned short FLAG_MSG_LOST = (1<<4); // Remote PacketLayer detected lost packets (jumps in SeqId). The flag details contains the last remote seqId +static const unsigned short FLAG_MSG_GARBAGE = (1<<5); // Garbage bytes detected. The Flag details will contains the number of garbage bytes (capped to 255) +static const unsigned short FLAG_SIZE_EXTENDED = (1<<6); // Size of packet will have one byte extension (MSB), contained in the Flags detail +static const unsigned short FLAG_EXTENDED = (1<<7); // If set, then the flag details will contains a Byte representing the MSB of the 16bit flags +static const unsigned short FLAG_INTERNAL_PACKET = (1<<8); // Packet in that message is internal, and should be processed by the TL +static const unsigned short FLAG_IGNORE_SEQID = (1<<9); // Remote side requested to ignore the seqId, i.e. error should not be accounted for in the stats + +/* + * Internal functions + */ +static void TransportLayer_ParseIncomingData(struct sTransportLayer* p, + const unsigned char *pucData, unsigned short usLen); +static bool TransportLayer_PacketReceived(struct sTransportLayer* p); +static void TransportLayer_ReceivedReliableAck(struct sTransportLayer* p, + unsigned char ucAckedReliableSeqId); +static void TransportLayer_ReceivedReliableNack(struct sTransportLayer* p, + unsigned char ucLastReceivedReliableSeqId, + unsigned char ucAckedSeqId); +static void TransportLayer_SendPacketAck(struct sTransportLayer* p, + unsigned short usAckFlags); +static unsigned long TransportLayer_EscapeDataByte(struct sTransportLayer* p, + unsigned char *pucDestBuff, unsigned char ucData); +static unsigned long TransportLayer_EscapeData(struct sTransportLayer* p, + unsigned char *pucDestBuff, + const unsigned char *pucSrcBuff, + unsigned short usSize); +static unsigned long TransportLayer_EscapeDataAndUpdateCrcByte( + struct sTransportLayer* p, + unsigned char *pucDestBuff, + unsigned char ucData); +static unsigned long TransportLayer_EscapeDataAndUpdateCrc( + struct sTransportLayer* p, + unsigned char *pucDestBuff, + const unsigned char *pucSrcBuff, + unsigned short usSize); +static unsigned char TransportLayer_BuildAndSendPacket( + struct sTransportLayer* p, + unsigned short usFlags, + unsigned char *pucData, + unsigned short usSize, + unsigned char ucReliableTxSeqId); +static unsigned int TransportLayer_GetReliablePacketInTransitCnt( + const struct sTransportLayer* const p); +static void TransportLayer_SendInternalSeqIdRequest(struct sTransportLayer* p); +static void TransportLayer_OnInternalPacket(struct sTransportLayer* p, + unsigned char *pucData, unsigned short usSize); +static void TransportLayer_SendInternalPacket(struct sTransportLayer* p, + unsigned char ucId, + const unsigned char *pucData, + unsigned short usSize); +static void TransportLayer_OnInternalSeqIdRequest(struct sTransportLayer* p, + unsigned char ucReqId); +static void TransportLayer_OnInternalSeqIdResponse(struct sTransportLayer* p, + unsigned char ucReqId, + unsigned char ucLastRxSeqId, + unsigned char ucLastAckSeqId, + unsigned char ucReliableSeqId, + unsigned char ucTxSeqId, + unsigned char ucTxReliableSeqId, + unsigned char ucTxLastAckedSeqId); +static void TransportLayer_OnInternalStatsRequest(struct sTransportLayer* p); +static void TransportLayer_OnInternalStatsResponse(struct sTransportLayer* p, + struct stTransportLayerStats *rStats); +static void TransportLayer_OnInternalSettingsRequest(struct sTransportLayer* p); +static void TransportLayer_OnInternalSettingsResponse(struct sTransportLayer* p, + unsigned short usMaxOutgoingPacketSize, + unsigned short usMaxIncomingPacketSize, + unsigned short usReliableRetryTimeoutMs, + unsigned short usReliableMaxRetry, + unsigned short usReliableMaxPackets); + +/* +* Protocol used to transport a packet from one TransportLayer to a remote TransportLayer +* +* Packet structure: |ESC|SOP||ESC|EOP| +* - ESC : ESCAPE_CHARACTER +* - SOP: SOP_CHARACTER +* - EOP: EOP_CHARACTER +* - ESCAPED_DATA: |SEQ_ID||CRC| +* -- SEQ_ID: Running Sequence Id +* -- CRC: 8 bits CRC +* -- CRCED_DATA: |SIZE|FLAGS|FLAGS_DETAILS|| +* --- SIZE: 1B containing the LSB for the size of the payload. Possible MSB contained in the FLAG_DETAIL +* --- FLAGS: 1B containing different flags for this packet +* --- FLAGS_DETAILS: 1B for each bit set in FLAGS (LSB for LSbit) +* --- PAYLOAD: Packet payload +* +*/ + +/* +* Internal packet types +*/ +enum +{ + INT_PKT_SEQID_REQUEST /* Request to send the Sequence Ids */ + ,INT_PKT_SEQID_RESPONSE /* Sequence Ids responses */ + ,INT_PKT_STATS_REQUEST /* Request for the remote stats to be sent */ + ,INT_PKT_STATS_RESPONSE /* Response with the stats */ + ,INT_PKT_SETTINGS_REQUEST /* Request to get internal settings of remote side */ + ,INT_PKT_SETTINGS_RESPONSE /* Response with the settings */ +}; + + +/// enumeration for the state +enum +{ + WAIT_FOR_ESC_SOP = 0 + ,WAIT_FOR_SOP + ,WAIT_FOR_MESSAGE_COMPLETE + ,WAIT_FOR_EOP +}; + +/// Macro for returning an Exception +#define ASSERT(_test) {if (!(_test)) {p->m_callback->OnException(p->m_callback, __FILE__, __LINE__);}} +#define ASSERT_RET(_test) {if (!(_test)) {p->m_callback->OnException(p->m_callback, __FILE__, __LINE__);return;}} +#define ASSERT_RET_0(_test) {if (!(_test)) {p->m_callback->OnException(p->m_callback, __FILE__, __LINE__);return 0;}} + +// Enables debug (will turn on logging) +#define PL_DEBUG 0 +#if PL_DEBUG + #define PLLOG(fmt, ...) {printf("%s:",(p->m_pDebugName==NULL)?"":p->m_pDebugName);printf(fmt, ##__VA_ARGS__);} +#else + #define PLLOG(fmt,...) +#endif + +struct stReliableElement +{ + unsigned char ucReliableSeqId; + unsigned char ucTxSeqId; + unsigned char ucNumSent; + unsigned char *pucData; + bool bForceResend; + unsigned long ulTimestampMs; + unsigned short usDataSize; + struct stCallback sCallback; + struct stReliableElement *pNext; +}; + +struct stReliableElement* stReliableElement_stReliableElement(struct stReliableElement *p) +{ + memset(p, 0, sizeof(*p)); + /* p->ucReliableSeqId = 0; */ + /* p->ucTxSeqId = 0; */ + /* p->ucNumSent = 0; */ + /* p->pucData = NULL; */ + /* p->bForceResend = false; */ + /* p->ulTimestampMs = 0; */ + /* p->usDataSize = 0; */ + /* p->pNext = NULL; */ + /* p->sCallback.cb = NULL; */ + /* p->sCallback.pCbData = NULL; */ + /* p->sCallback.ulCbData = 0; */ + return p; +} + +struct stTransportLayerStats* stTransportLayerStats_stTransportLayerStats( + struct stTransportLayerStats* p) +{ + memset(p, 0, sizeof(*p)); + /* p->ulRxGarbageBytes = 0; */ + /* p->ulRxPacketLost = 0; */ + /* p->ulRemotePacketLost = 0; */ + /* p->ulRemoteGarbage = 0; */ + /* p->ulPacketSent = 0; */ + /* p->ulPacketReceived = 0; */ + /* p->ulAckReceived = 0; */ + /* p->ulReliablePacketSent = 0; */ + /* p->ulReliableRetransmit = 0; */ + /* p->ulReliablePacketReceived = 0; */ + /* p->ulMaxRetransmitCount = 0; */ + return p; +} + +struct sTransportLayer* TransportLayer_TransportLayer(struct sTransportLayer* p, + struct sITransportLayerCb *rCallback, + const char *pDebugName) +{ + memset(p, 0, sizeof(*p)); + p->m_pDebugName = pDebugName; + p->m_callback = rCallback; + p->m_uiParserState = WAIT_FOR_ESC_SOP; + p->m_ucLastRxSeqId = 0xFF; + p->m_ucReliableSeqId = 0xFF; + p->m_ucTxLastAckedSeqId = 0xFF; + p->m_bPassThrough = true; + + /* p->m_uiRxLen = 0; */ + /* p->m_ulByteCntSinceLastValidPacket = 0; */ + /* p->m_ucLastAckSeqId = 0; */ + /* p->m_ucReliableCrc = 0; */ + /* p->m_usReliableLen = 0; */ + /* p->m_ucDelayAckCount = 0; */ + /* p->m_ucTxSeqId = 0; */ + /* p->m_ucTxReliableSeqId = 0; */ + /* p->m_pFirstReliableEl = NULL; */ + /* p->m_pLastReliableEl = NULL; */ + /* p->m_ulReliableByteCnt = 0; */ + /* p->m_bWait4ReliableAckToProcessNack = false */ + /* p->m_bOngoingSync = false; */ + /* p->m_ulSyncTimestamp = 0; */ + /* p->m_ulRetryCnt = 0; */ + + { + ASSERT_COMPILE(MAX_OUTGOING_PACKET_SIZE > MAX_HEADER_SIZE && MAX_OUTGOING_PACKET_SIZE < USHRT_MAX); + ASSERT_COMPILE(MAX_INCOMING_PACKET_SIZE > MAX_HEADER_SIZE && MAX_INCOMING_PACKET_SIZE < USHRT_MAX); + } + + TransportLayer_Reset(p); + return p; +} + +void TransportLayer_dtor(struct sTransportLayer* p) +{ + while (p->m_pFirstReliableEl != NULL) + { + struct stReliableElement *pToRemove = p->m_pFirstReliableEl; + /* advance to next element */ + p->m_pFirstReliableEl = p->m_pFirstReliableEl->pNext; + + p->m_ulReliableByteCnt -= pToRemove->usDataSize; + + bbd_free(pToRemove->pucData); + bbd_free(pToRemove); + } + /* for now assert, but we should release the memory instead */ + ASSERT(TransportLayer_GetReliableUsage(p) == 0); +} + +void TransportLayer_Reset(struct sTransportLayer* p) +{ + /* we cannot reset if we have ongoing reliable transactions */ + ASSERT_RET(TransportLayer_GetReliablePacketInTransitCnt(p) == 0); + + /* clear ongoing transactions . That means the user better reset + * at the appropriate time, or it will mess up some packets + */ + p->m_uiParserState = WAIT_FOR_ESC_SOP; + p->m_uiRxLen = 0; + p->m_ulByteCntSinceLastValidPacket = 0; + + p->m_ucLastRxSeqId = 0xFF; + p->m_ucLastAckSeqId = 0; + p->m_ucReliableSeqId = 0xFF; + p->m_ucReliableCrc = 0; + p->m_usReliableLen = 0; + p->m_ucDelayAckCount = 0; + + p->m_ucTxSeqId = 0; + p->m_ucTxLastAckedSeqId = 0xFF; + p->m_ucTxReliableSeqId = 0; + + p->m_bWait4ReliableAckToProcessNack = false; +} + +void TransportLayer_StartRemoteSync(struct sTransportLayer* p) +{ + { + ASSERT_RET(TransportLayer_GetReliablePacketInTransitCnt(p) == 0); // we cannot start a sync if we have ongoing reliable transactions + ASSERT_RET(!p->m_bOngoingSync); + } + + PLLOG("TransportLayer_RemoteSync()\n"); + p->m_bOngoingSync = true; + p->m_ulRetryCnt = 0; + + TransportLayer_SendInternalSeqIdRequest(p); + + TransportLayer_Tick(p); // refresh internal timers +} + +void TransportLayer_SendInternalSeqIdRequest(struct sTransportLayer* p) +{ + unsigned short usFlags = FLAG_INTERNAL_PACKET | FLAG_IGNORE_SEQID; + unsigned char ucBuf[2]; + + p->m_ulSyncTimestamp = p->m_callback->OnTimeMsRequest(p->m_callback); + p->m_ulRetryCnt++; + + ucBuf[0] = INT_PKT_SEQID_REQUEST; + ucBuf[1] = (unsigned char)(p->m_ulRetryCnt&0xFF); + TransportLayer_BuildAndSendPacket(p, usFlags, ucBuf, sizeof(ucBuf), 0); + PLLOG("TransportLayer_SendInternalSeqIdRequest(%lu, %lu)\n", + p->m_ulSyncTimestamp, p->m_ulRetryCnt); +} + + +void TransportLayer_SendPacket(struct sTransportLayer* p, + unsigned char *pucData, unsigned short usSize) +{ + ASSERT_RET(usSize <= MAX_OUTGOING_PACKET_SIZE); + ASSERT_RET(!p->m_bOngoingSync); // TL cannot be used during sync + + /* no special flags when sending a regular packet */ + { + unsigned short usFlags = 0; + TransportLayer_BuildAndSendPacket(p, usFlags, pucData, usSize, 0); + ++p->m_otCurrentStats.ulPacketSent; + } +} + + +unsigned char TransportLayer_BuildAndSendPacket(struct sTransportLayer* p, + unsigned short usFlags, unsigned char *pucData, + unsigned short usSize, unsigned char ucReliableTxSeqId) +{ + /* we should never have more than OUTGOING_PACKET_SIZE or we might + * have stack corruption + */ + ASSERT_RET_0(usSize <= MAX_OUTGOING_PACKET_SIZE); + { + unsigned char ucSizeMSB = (usSize>>8)&0xFF; + unsigned char ucSizeLSB = usSize&0xFF; + unsigned char ucTxSeqId = p->m_ucTxSeqId++; + unsigned long ulPacketSize = 0; + unsigned int i = 0; + + if (ucSizeMSB > 0) + { + /* if size is over 255B, mark the size_extension */ + usFlags |= FLAG_SIZE_EXTENDED; + } + + if (usFlags == 0 && p->m_ucDelayAckCount) + { + dprint("Piggyback %u seq %u\n", + p->m_ucDelayAckCount, p->m_ucLastRxSeqId); + // we have a data packet going out anyway, so piggyback + // the ACK onto it. + usFlags = FLAG_PACKET_ACK; // delay no further + p->m_ucDelayAckCount = 0; + } + + if (usFlags > 0xFF) + { + /* if the flags use more than one byte, set the appropriate flag */ + usFlags |= FLAG_EXTENDED; + } + + Crc8Bits_Reset(&p->m_otTxCrc); + + /* update the flags */ + if (p->m_otLastPLUpdatedStats.ulRxGarbageBytes != + p->m_otCurrentStats.ulRxGarbageBytes) + { + usFlags |= FLAG_MSG_GARBAGE; + } + if (p->m_otLastPLUpdatedStats.ulRxPacketLost != + p->m_otCurrentStats.ulRxPacketLost) + { + usFlags |= FLAG_MSG_LOST; + } + + /* Header */ + p->m_aucTxBuffer[ulPacketSize++] = ESCAPE_CHARACTER; + p->m_aucTxBuffer[ulPacketSize++] = SOP_CHARACTER; + ulPacketSize += TransportLayer_EscapeDataByte(p, + &p->m_aucTxBuffer[ulPacketSize], ucTxSeqId); + ulPacketSize += TransportLayer_EscapeDataAndUpdateCrcByte(p, + &p->m_aucTxBuffer[ulPacketSize], ucSizeLSB); + + /* first encode the flags LSB (flags MSB is part of an extension) */ + ulPacketSize += TransportLayer_EscapeDataAndUpdateCrcByte(p, + &p->m_aucTxBuffer[ulPacketSize], (usFlags&0xFF)); + /* and then encode the flags_details */ + for (i = 0; i < 16 && usFlags != 0; ++i) + { + unsigned short usFlagMask = (1<m_aucTxBuffer[ulPacketSize], + p->m_ucLastRxSeqId); + } + /* This is a reliable packet. we need to provide the proper Ack */ + else if (usFlagBit == FLAG_RELIABLE_PACKET) + { + ulPacketSize += TransportLayer_EscapeDataAndUpdateCrcByte(p, + &p->m_aucTxBuffer[ulPacketSize], + ucReliableTxSeqId); + } + else if (usFlagBit == FLAG_RELIABLE_ACK) + { + ulPacketSize += TransportLayer_EscapeDataAndUpdateCrcByte(p, + &p->m_aucTxBuffer[ulPacketSize], + p->m_ucReliableSeqId); + } + else if (usFlagBit == FLAG_RELIABLE_NACK) + { + ulPacketSize += TransportLayer_EscapeDataAndUpdateCrcByte(p, + &p->m_aucTxBuffer[ulPacketSize], + p->m_ucReliableSeqId); + } + else if (usFlagBit == FLAG_MSG_LOST) + { + /* It cannot be more than 255 as we are acking every packet */ + unsigned char ucNumPacketLost = (unsigned char) + (p->m_otCurrentStats.ulRxPacketLost - + p->m_otLastPLUpdatedStats.ulRxPacketLost); + + p->m_otLastPLUpdatedStats.ulRxPacketLost = + p->m_otCurrentStats.ulRxPacketLost; + ulPacketSize += TransportLayer_EscapeDataAndUpdateCrcByte(p, + &p->m_aucTxBuffer[ulPacketSize], ucNumPacketLost); + } + else if (usFlagBit == FLAG_MSG_GARBAGE) + { + unsigned long ulNumGarbageLost = + p->m_otCurrentStats.ulRxGarbageBytes - + p->m_otLastPLUpdatedStats.ulRxGarbageBytes; + if (ulNumGarbageLost > 0xFF) + { + ulNumGarbageLost = 0xFF; /* max we can send is 1B */ + } + p->m_otLastPLUpdatedStats.ulRxGarbageBytes += ulNumGarbageLost; + + ulPacketSize += TransportLayer_EscapeDataAndUpdateCrcByte(p, + &p->m_aucTxBuffer[ulPacketSize], + (unsigned char)ulNumGarbageLost); + } + else if (usFlagBit == FLAG_SIZE_EXTENDED) + { + ulPacketSize += TransportLayer_EscapeDataAndUpdateCrcByte(p, + &p->m_aucTxBuffer[ulPacketSize], ucSizeMSB); + } + else if (usFlagBit == FLAG_EXTENDED) + { + /* encode the MSB of the 16bit flags */ + ulPacketSize += TransportLayer_EscapeDataAndUpdateCrcByte(p, + &p->m_aucTxBuffer[ulPacketSize], ((usFlags>>8)&0xFF)); + } + else if (usFlagBit == FLAG_INTERNAL_PACKET) + { + /* we don't care about the details, but need to encode a + * dummy byte + */ + ulPacketSize += TransportLayer_EscapeDataAndUpdateCrcByte(p, + &p->m_aucTxBuffer[ulPacketSize], 0x0); + } + else if (usFlagBit == FLAG_IGNORE_SEQID) + { + /* we don't care about the details, but need to encode a + * dummy byte + */ + ulPacketSize += TransportLayer_EscapeDataAndUpdateCrcByte(p, + &p->m_aucTxBuffer[ulPacketSize], 0x0); + } + else + { + ASSERT(0); // safe assert, no need to return + break; + } + + usFlags &= ~usFlagBit; + } + + ulPacketSize += TransportLayer_EscapeDataAndUpdateCrc(p, + &p->m_aucTxBuffer[ulPacketSize], pucData, usSize); + + { + unsigned char ucCrc = Crc8Bits_GetState(&p->m_otTxCrc); + ucCrc = ((ucCrc&0x0F)<<4) | ((ucCrc&0xF0)>>4); + + ulPacketSize += TransportLayer_EscapeDataByte(p, + &p->m_aucTxBuffer[ulPacketSize], ucCrc); + + p->m_aucTxBuffer[ulPacketSize++] = ESCAPE_CHARACTER; + p->m_aucTxBuffer[ulPacketSize++] = EOP_CHARACTER; + + p->m_callback->OnDataToSend(p->m_callback, p->m_aucTxBuffer, + ulPacketSize); + return ucTxSeqId; + } +} +} + +unsigned long TransportLayer_EscapeDataByte(struct sTransportLayer* p, + unsigned char *pucDestBuff, unsigned char ucData) +{ + return TransportLayer_EscapeData(p, pucDestBuff, &ucData, 1); +} + + +unsigned long TransportLayer_EscapeData(struct sTransportLayer* p, + unsigned char *pucDestBuff, const unsigned char *pucSrcBuff, + unsigned short usSize) +{ + unsigned long ulCnt = 0; + unsigned int i = 0; + for (i = 0; i < usSize; ++i) + { + if (pucSrcBuff[i] == ESCAPE_CHARACTER) + { + pucDestBuff[ulCnt++] = ESCAPE_CHARACTER; + pucDestBuff[ulCnt++] = ESCAPED_ESCAPE_CHARACTER; + } + else if (pucSrcBuff[i] == XON_CHARACTER) + { + pucDestBuff[ulCnt++] = ESCAPE_CHARACTER; + pucDestBuff[ulCnt++] = ESCAPED_XON_CHARACTER; + } + else if (pucSrcBuff[i] == XOFF_CHARACTER) + { + pucDestBuff[ulCnt++] = ESCAPE_CHARACTER; + pucDestBuff[ulCnt++] = ESCAPED_XOFF_CHARACTER; + } + else // regular data + { + pucDestBuff[ulCnt++] = pucSrcBuff[i]; + } + } + return ulCnt; +} + +unsigned long TransportLayer_EscapeDataAndUpdateCrcByte(struct sTransportLayer* p, + unsigned char *pucDestBuff, + unsigned char ucData) +{ + Crc8Bits_UpdateByte(&p->m_otTxCrc, ucData); + return TransportLayer_EscapeDataByte(p, pucDestBuff, ucData); +} + +unsigned long TransportLayer_EscapeDataAndUpdateCrc(struct sTransportLayer* p, + unsigned char *pucDestBuff, + const unsigned char *pucSrcBuff, + unsigned short usSize) +{ + Crc8Bits_Update(&p->m_otTxCrc, pucSrcBuff, usSize); + return TransportLayer_EscapeData(p, pucDestBuff, pucSrcBuff, usSize); +} + +bool TransportLayer_SendReliablePacket( struct sTransportLayer* p, + unsigned char *pucData, + unsigned short usSize, + PacketDeliveryNotification cb, + void *pCbData, + unsigned long ulCbData) +{ +#if TLCUST_ENABLE_RELIABLE_PL + bool userFail = false; + ASSERT_RET_0(usSize <= MAX_OUTGOING_PACKET_SIZE); + ASSERT_RET_0(TransportLayer_IsReliableAvailable(p)); + ASSERT_RET_0(!p->m_bOngoingSync); // TL cannot be used during sync + + // allocate memory to hold the data + { + struct stReliableElement *pElement = (struct stReliableElement *) bbd_alloc(sizeof(struct stReliableElement)); + if (!pElement) + return false; + stReliableElement_stReliableElement(pElement); + pElement->ucReliableSeqId = p->m_ucTxReliableSeqId; + p->m_ucTxReliableSeqId = (p->m_ucTxReliableSeqId+1)&0xFF; + pElement->ulTimestampMs = p->m_callback->OnTimeMsRequest(p->m_callback); + pElement->ucNumSent = 1; + pElement->pucData = (unsigned char *) bbd_alloc(usSize); + if (!pElement->pucData) { + bbd_free(pElement); + return false; + } + userFail = copy_from_user(pElement->pucData, pucData, usSize); + if (userFail) { + bbd_free(pElement->pucData); + bbd_free(pElement); + return false; + } + pElement->usDataSize = usSize; + pElement->sCallback.cb = cb; + pElement->sCallback.pCbData = pCbData; + pElement->sCallback.ulCbData = ulCbData; + pElement->pNext = NULL; + + p->m_ulReliableByteCnt += usSize; + + // Let's insert that element + if (p->m_pFirstReliableEl == NULL) + { + p->m_pFirstReliableEl = pElement; + p->m_pLastReliableEl = pElement; + } + else + { + // link it to the list + p->m_pLastReliableEl->pNext = pElement; + p->m_pLastReliableEl = pElement; + } + + pElement->ucTxSeqId = TransportLayer_BuildAndSendPacket(p, FLAG_RELIABLE_PACKET, pElement->pucData, pElement->usDataSize, pElement->ucReliableSeqId); + PLLOG("TransportLayer_SendReliablePacket(Size %u, SeqId %u, %lu ms, RelSeqId %u)\n", usSize, pElement->ucTxSeqId, pElement->ulTimestampMs, pElement->ucReliableSeqId); + p->m_otCurrentStats.ulReliablePacketSent++; + + TransportLayer_Tick(p); /* refresh timers */ + } + return true; + +#else /* TLCUST_ENABLE_RELIABLE_PL */ + ASSERT_RET(0); + return false; +#endif +} + +void TransportLayer_SetData(struct sTransportLayer* p, const unsigned char *pucData, + unsigned short usSize) +{ + PLLOG("TransportLayer_SetData(Size %u)\n", usSize); + TransportLayer_ParseIncomingData(p, pucData, usSize); +} + +void TransportLayer_Tick(struct sTransportLayer* p) +{ + // check if there is anything in the reliable layer before bothering the upper layer + unsigned long ulTimeNowMs = p->m_callback->OnTimeMsRequest(p->m_callback); + + long lTimerMs = RELIABLE_RETRY_TIMEOUT_MS; + struct stReliableElement *pElement = p->m_pFirstReliableEl; + if (p->m_bOngoingSync) + { + unsigned long ulDeltaMs = ulTimeNowMs - p->m_ulSyncTimestamp; + if (ulDeltaMs >= RELIABLE_RETRY_TIMEOUT_MS) + { + // we need to restart sync + if (p->m_ulRetryCnt > RELIABLE_MAX_RETRY) + { + p->m_callback->OnCommunicationError(p->m_callback); + } + else + { + TransportLayer_SendInternalSeqIdRequest(p); + } + lTimerMs = RELIABLE_RETRY_TIMEOUT_MS; + } + else + { + lTimerMs = RELIABLE_RETRY_TIMEOUT_MS - ulDeltaMs; + } + } + else if (pElement == NULL) + { + lTimerMs = -1; // TransportLayer is Idle + } + else + { + while (pElement != NULL) + { + // we simply traverse the list, and if we see that there is a timeout expired, we simply resend the packet. If not, we call the OnTimerSet with next timeout. + + unsigned long ulDeltaMs = ulTimeNowMs - pElement->ulTimestampMs; + if (ulDeltaMs >= RELIABLE_RETRY_TIMEOUT_MS || pElement->bForceResend) + { + if (pElement->ucNumSent == RELIABLE_MAX_RETRY) + { + p->m_callback->OnCommunicationError(p->m_callback); + } + else + { + if (p->m_otCurrentStats.ulMaxRetransmitCount < pElement->ucNumSent) + { + p->m_otCurrentStats.ulMaxRetransmitCount = pElement->ucNumSent; + } + pElement->ucNumSent++;// inc its count + pElement->ulTimestampMs = ulTimeNowMs; // update its timestamp to "eat" the system delays by restarting the timeout + /// ****** WARNING ***** + // Known issue if the seqId that is sent again is the same as the previous one. As this is used to detect different NACKs, that could potentially + // create a CommunicationError() + // what we could do is send an empty message to the other side, which would slightly increase the bandwidth but save us from that trouble + // note that this would not occur very often anyway, so that should not be a problem + + pElement->ucTxSeqId = TransportLayer_BuildAndSendPacket(p, FLAG_RELIABLE_PACKET, pElement->pucData, pElement->usDataSize, pElement->ucReliableSeqId); + PLLOG("TransportLayer_Tick(), Resending(Size %u, SeqId %u, RelSeqId %u, %lu ms, sent %u, %s)\n", + pElement->usDataSize, pElement->ucTxSeqId, pElement->ucReliableSeqId, + pElement->ulTimestampMs, pElement->ucNumSent, pElement->bForceResend?"Forced":"Timeout"); + pElement->bForceResend = false; + p->m_otCurrentStats.ulReliableRetransmit++; + } + } + else + { + signed long lTimeoutMs = RELIABLE_RETRY_TIMEOUT_MS - ulDeltaMs; + if (lTimerMs > lTimeoutMs) + { + lTimerMs = lTimeoutMs; + } + } + + pElement = pElement->pNext; + } + } + + p->m_callback->OnTimerSet(p->m_callback, lTimerMs); +} + + +unsigned long TransportLayer_GetReliableUsage(const struct sTransportLayer* const p) +{ + return p->m_ulReliableByteCnt; +} + +unsigned int TransportLayer_GetReliablePacketInTransitCnt(const struct sTransportLayer* const p) +{ + return (unsigned int) ((p->m_ucTxReliableSeqId - p->m_ucTxLastAckedSeqId)&0xFF)-1; +} + +bool TransportLayer_IsReliableAvailable(const struct sTransportLayer* const p) +{ +#if TLCUST_ENABLE_RELIABLE_PL + return TransportLayer_GetReliablePacketInTransitCnt(p) < RELIABLE_MAX_PACKETS; +#else /* TLCUST_ENABLE_RELIABLE_PL */ + return false; +#endif +} + +const struct stTransportLayerStats* const TransportLayer_GetStats(const struct sTransportLayer* const p) +{ + return &p->m_otCurrentStats; +} + +void TransportLayer_ParseIncomingData(struct sTransportLayer* p, + const unsigned char *pucData, unsigned short usLen) +{ + unsigned short usIdx=0; + + while (usIdx != usLen) + { + unsigned char ucData = pucData[usIdx++]; + p->m_ulByteCntSinceLastValidPacket++; + + if (ucData == XON_CHARACTER || ucData == XOFF_CHARACTER) + { + continue; + } + + switch(p->m_uiParserState) + { + case WAIT_FOR_ESC_SOP: + { + if (ucData == ESCAPE_CHARACTER) + { + p->m_uiParserState = WAIT_FOR_SOP; + p->m_otCurrentStats.ulRxGarbageBytes += (p->m_ulByteCntSinceLastValidPacket -1); // if we had only one byte, then there is no garbage, any extra is garbage + p->m_ulByteCntSinceLastValidPacket = 1; + } + } + break; + + case WAIT_FOR_SOP: + { + if (ucData == SOP_CHARACTER) + { + p->m_uiParserState = WAIT_FOR_MESSAGE_COMPLETE; + p->m_uiRxLen = 0; + } + else + { + if (ucData != ESCAPE_CHARACTER) + { + p->m_uiParserState = WAIT_FOR_ESC_SOP; + } + else + { + p->m_otCurrentStats.ulRxGarbageBytes += 2; + p->m_ulByteCntSinceLastValidPacket = 1; + } + + } + } + break; + + case WAIT_FOR_MESSAGE_COMPLETE: + { + if (ucData == ESCAPE_CHARACTER) + { + p->m_uiParserState = WAIT_FOR_EOP; + } + else if (p->m_uiRxLen < sizeof(p->m_aucRxMessageBuf)) + { + p->m_aucRxMessageBuf[p->m_uiRxLen++] = ucData; + } + else + { + p->m_uiParserState = WAIT_FOR_ESC_SOP; + } + } + break; + + case WAIT_FOR_EOP: + { + if (ucData == EOP_CHARACTER) + { + if (TransportLayer_PacketReceived(p)) + { + p->m_ulByteCntSinceLastValidPacket = 0; // we had a valid packet, restart the cnt + } + + p->m_uiParserState = WAIT_FOR_ESC_SOP; + } + else if (ucData == ESCAPED_ESCAPE_CHARACTER) + { + if (p->m_uiRxLen < _DIM(p->m_aucRxMessageBuf)) + { + p->m_aucRxMessageBuf[p->m_uiRxLen++] = ESCAPE_CHARACTER; + p->m_uiParserState = WAIT_FOR_MESSAGE_COMPLETE; + } + else + { + p->m_uiParserState = WAIT_FOR_ESC_SOP; + } + } + else if (ucData == ESCAPED_XON_CHARACTER) + { + if (p->m_uiRxLen < _DIM(p->m_aucRxMessageBuf)) + { + p->m_aucRxMessageBuf[p->m_uiRxLen++] = XON_CHARACTER; + p->m_uiParserState = WAIT_FOR_MESSAGE_COMPLETE; + } + else + { + p->m_uiParserState = WAIT_FOR_ESC_SOP; + } + } + else if (ucData == ESCAPED_XOFF_CHARACTER) + { + if (p->m_uiRxLen < _DIM(p->m_aucRxMessageBuf)) + { + p->m_aucRxMessageBuf[p->m_uiRxLen++] = XOFF_CHARACTER; + p->m_uiParserState = WAIT_FOR_MESSAGE_COMPLETE; + } + else + { + p->m_uiParserState = WAIT_FOR_ESC_SOP; + } + } + else if (ucData == SOP_CHARACTER) + { + // we probably missed the ESC EOP, but we start receiving a new packet! + p->m_uiParserState = WAIT_FOR_MESSAGE_COMPLETE; + // init the parser! + p->m_uiRxLen = 0; + + p->m_otCurrentStats.ulRxGarbageBytes += (p->m_ulByteCntSinceLastValidPacket-2); // if we had only one byte, then there is no garbage, any extra is garbage + p->m_ulByteCntSinceLastValidPacket = 2; + } + else if (ucData == ESCAPE_CHARACTER) + { + p->m_uiParserState = WAIT_FOR_SOP; + } + else + { + p->m_uiParserState = WAIT_FOR_ESC_SOP; + } + } + break; + } + } +} + + +bool TransportLayer_PacketReceived(struct sTransportLayer* p) +{ + struct sCrc8Bits crc; + Crc8Bits_Crc8Bits(&crc); + + /* minimum is seqId, payload size, flags, and Crc */ + if (p->m_uiRxLen >= 4) + { + /* compute CRC */ + /* CRC is not applied on itself, nor on the SeqId */ + unsigned char ucCrc = Crc8Bits_Update(&crc, + &p->m_aucRxMessageBuf[1], p->m_uiRxLen-2); + + /* CRC has its nibble inverted for stronger CRC (as CRC of + * a packet with itself is always 0, if EoP is not detected, + * that always reset the CRC) + */ + ucCrc = ((ucCrc&0x0F)<<4) | ((ucCrc&0xF0)>>4); + if (ucCrc != p->m_aucRxMessageBuf[p->m_uiRxLen-1]) /* CRC is last */ + { + return false; + } + } + else + { + return false; + } + + /* passed CRC check */ + { + unsigned char *pucData = &p->m_aucRxMessageBuf[0]; + unsigned short usLen = p->m_uiRxLen-1; + + unsigned char ucSeqId = *pucData++; /* usLen--; */ + unsigned short usPayloadSize = *pucData++; /* usLen--; */ + unsigned short usFlags = *pucData++; /* usLen--; */ + + bool bReliablePacket = false; + unsigned char ucReliableSeqId=0; + + bool bReliableAckReceived = false; + unsigned char ucReliableAckSeqId=0; + + bool bReliableNackReceived = false; + unsigned char ucReliableNackSeqId=0; + + bool bAckReceived = false; + + bool bInternalPacket = false; + bool bIgnoreSeqId = false; + + unsigned short usAckFlags = 0; + unsigned int i=0; + + usLen -= 3; + + for (i = 0; i < 16 && usFlags != 0 && usLen > 0; ++i) + { + unsigned short usFlagMask = (1 << i); + unsigned short usFlagBit = usFlags & usFlagMask; + unsigned char ucFlagDetail = 0; + + if (usFlagBit == 0) + { + continue; + } + + usFlags &= ~usFlagBit; /* clear the flag */ + ucFlagDetail = *pucData++; /* extract flag details */ + --usLen; + + if (usFlagBit == FLAG_PACKET_ACK) /* acknowledgement */ + { + /* flag detail contain the acknowledged SeqId */ + unsigned char ucReceivedAckSeqId = ucFlagDetail; + + p->m_ucLastAckSeqId = ucReceivedAckSeqId; + bAckReceived = true; + } + else if (usFlagBit == FLAG_RELIABLE_PACKET) + { + /* This is a reliable packet. we need to provide the proper Ack */ + ucReliableSeqId = ucFlagDetail; + bReliablePacket = true; + } + else if (usFlagBit == FLAG_RELIABLE_ACK) + { + bReliableAckReceived = true; + ucReliableAckSeqId = ucFlagDetail; + } + else if (usFlagBit == FLAG_RELIABLE_NACK) + { + bReliableNackReceived = true; + ucReliableNackSeqId = ucFlagDetail; + } + else if (usFlagBit == FLAG_MSG_LOST) + { + /* remote TransportLayer lost had some SeqId jumps */ + p->m_otCurrentStats.ulRemotePacketLost += ucFlagDetail; + } + else if (usFlagBit == FLAG_MSG_GARBAGE) + { + /* remote TransportLayer detected garbage */ + p->m_otCurrentStats.ulRemoteGarbage += ucFlagDetail; + } + else if (usFlagBit == FLAG_SIZE_EXTENDED) + { + /* flag detail contains the MSB of the payload size */ + usPayloadSize |= (ucFlagDetail<<8); + } + else if (usFlagBit == FLAG_EXTENDED) + { + /* the flags are extended, which means that the details + * contains the MSB of the 16bit flags + */ + usFlags |= (ucFlagDetail<<8); + } + else if (usFlagBit == FLAG_INTERNAL_PACKET) + { + /* don't care about details */ + bInternalPacket = true; + } + else if (usFlagBit == FLAG_IGNORE_SEQID) + { + /* don't care about details */ + bIgnoreSeqId = true; + } + else + { + /* we did not process the flag, just put it back + * this is an error, so we can break now, as there is no + * point in continuing + */ + usFlags |= usFlagBit; + break; + } + } + + /* if flag is not garbage, entire packet should all be consumed */ + /* remaining length of the buffer should be the payload size */ + if (usFlags == 0 && + usPayloadSize == usLen) + { + /* we now have a valid packet (at least it passed all our + * validity checks, so we are going to trust it + */ + unsigned char ucExpectedTxSeqId = (p->m_ucLastRxSeqId+1)&0xFF; + if (ucSeqId != ucExpectedTxSeqId + && !bIgnoreSeqId + && !p->m_bOngoingSync) + { + /* Some packets were lost, jump in the RxSeqId */ + p->m_otCurrentStats.ulRxPacketLost += + ((ucSeqId - ucExpectedTxSeqId)&0xFF); + } + p->m_ucLastRxSeqId = ucSeqId; /* increase expected SeqId */ + + if (!bAckReceived || usLen > 0 ) + { + bool bDelayedEnough = (++p->m_ucDelayAckCount > 200); + ++p->m_otCurrentStats.ulPacketReceived; + // if this is a payload packet, we need to acknowledge it. + // But don't clog the wires with power-hungry simple + // acks unless we have too many (200) outstanding. + if (bDelayedEnough) + { + dprint("Skip averted/%d %s %s\n", __LINE__, + (bAckReceived ) ? "ACK" : "!ack", + (bDelayedEnough) ? "ENUF" : "!enuf"); + usAckFlags |= FLAG_PACKET_ACK; + p->m_ucDelayAckCount = 0; + } + else + { + dprint("Skip/%d %s %s %d\n", __LINE__, + (bAckReceived ) ? "ACK" : "!ack", + (bDelayedEnough) ? "ENUF" : "!enuf", + p->m_ucDelayAckCount); + } + } + else + { + ++p->m_otCurrentStats.ulAckReceived; + } + + { + bool bProcessPacket = bReliablePacket || usLen > 0; + if (bReliablePacket) + { + PLLOG("TransportLayer_Received Reliable(Size %u, SeqId %u)\n", usLen, ucReliableSeqId); + /* if this is a reliable message, we need to make sure + * we didn't received it before! + * if we did, the Host probably didn't received the Ack, + * so let's just send the ack + * Reliable seqId is not enough, so we also use CRC and + * Lenght to confirm this was the same message received! + */ + if (ucReliableSeqId == p->m_ucReliableSeqId + && Crc8Bits_GetState(&crc) == p->m_ucReliableCrc + && usLen == p->m_usReliableLen) + { + /* already received that packet, remote TransportLayer + * probably lost the Acknowledgement, send it again + */ + usAckFlags |= FLAG_PACKET_ACK | FLAG_RELIABLE_ACK; + bProcessPacket = false; /* we should not process it again */ + } + else if (ucReliableSeqId == ((p->m_ucReliableSeqId+1)&0xFF)) + { + /* this is a valid message, just do nothing but update + * the reliable info. the message will be processed below + */ + usAckFlags |= FLAG_PACKET_ACK | FLAG_RELIABLE_ACK; + /* already received that packet, remote TransportLayer + * probably lost the Acknowledgement, send it again + */ + p->m_usReliableLen = usLen; + p->m_ucReliableSeqId = ucReliableSeqId; + p->m_ucReliableCrc = Crc8Bits_GetState(&crc); + p->m_otCurrentStats.ulReliablePacketReceived++; + } + else + { + /* we received the wrong reliable SeqId */ + usAckFlags |= FLAG_PACKET_ACK | FLAG_RELIABLE_NACK; + bProcessPacket = false; /* we cannot accept the packet */ + } + } + + /* we have all the flags we need, time to send the acknowledgement */ + if (usAckFlags != 0) + { + if (usAckFlags & FLAG_PACKET_ACK) + { + p->m_ucDelayAckCount = 0; + } + TransportLayer_SendPacketAck(p, usAckFlags); + } + + if (bReliableAckReceived) + { + TransportLayer_ReceivedReliableAck(p, ucReliableAckSeqId); + } + + if (bReliableNackReceived) + { + TransportLayer_ReceivedReliableNack(p, ucReliableNackSeqId, p->m_ucLastAckSeqId); + } + + if (bProcessPacket) + { + if (bInternalPacket) + { + TransportLayer_OnInternalPacket(p, pucData, usLen); + } + else + { + /* everything good, notify upper layer that we have + * the payload of a packet available + */ + BbdBridge_OnPacketReceived(p, pucData, usLen); + } + } + return true; + } + } + else + { + return false; + } + } +} + +void TransportLayer_SendPacketAck(struct sTransportLayer* p, unsigned short usAckFlags) +{ + TransportLayer_BuildAndSendPacket(p, usAckFlags, + NULL /*pucData*/, + 0 /*usDataSize*/, + 0 /*ucReliableTxSeqId*/); +} + +#undef LARGE_FRAME_SIZE + +void TransportLayer_ReceivedReliableAck(struct sTransportLayer* p, + unsigned char ucAckedReliableSeqId) +{ +#if TLCUST_ENABLE_RELIABLE_PL +#ifdef LARGE_FRAME_SIZE + unsigned int i; + unsigned int uiNumToNotify=0; + struct stCallback aCB[RELIABLE_MAX_PACKETS]; +#else + struct stCallback* pCB; +#endif + bool bHasAckedId = false; + struct stReliableElement *pElement = p->m_pFirstReliableEl; + + /* by design, an ack ensure that all packets before the acked Id + * have been received + */ + PLLOG("TransportLayer_ReceivedReliableAck(RelSeqId %u)\n", + ucAckedReliableSeqId); + + /* accumulate the state of all callbacks to call + * (to avoid reentrant call while we are working on our linked list) + */ + while (pElement != NULL) + { + if (pElement->ucReliableSeqId == ucAckedReliableSeqId) + { + bHasAckedId = true; + break; + } + pElement = pElement->pNext; + } + + /* it is possible that we received an ACK previously when we sent + * the packet twice and the ACK took too much time + */ + if (bHasAckedId) + { + while (p->m_pFirstReliableEl != NULL) + { + /* update last acked seqid */ + p->m_ucTxLastAckedSeqId = p->m_pFirstReliableEl->ucReliableSeqId; + /* copy the callback structure */ +#ifdef LARGE_FRAME_SIZE + aCB[uiNumToNotify++] = p->m_pFirstReliableEl->sCallback; +#else + pCB = &p->m_pFirstReliableEl->sCallback; + if (pCB->cb != NULL) + { + /* notify user that the packet was delivered */ + BbdTransportLayer_DispatchReliableAck(p, pCB); + } +#endif + PLLOG("TransportLayer_NotifyReliableTransmittedAck(RelSeqId %u)\n", + p->m_pFirstReliableEl->ucReliableSeqId); + { + struct stReliableElement *pRemoveElement = p->m_pFirstReliableEl; + bool bDone = (p->m_pFirstReliableEl->ucReliableSeqId + == ucAckedReliableSeqId); + + p->m_ulReliableByteCnt -= p->m_pFirstReliableEl->usDataSize; + /* just point to the next on the list */ + p->m_pFirstReliableEl = p->m_pFirstReliableEl->pNext; + + /* we need to free the memory */ + bbd_free(pRemoveElement->pucData); + bbd_free(pRemoveElement); + + if (bDone) /* found our element, we want to stop the loop */ + { + break; + } + } + } + } + + if (p->m_pFirstReliableEl == NULL) /* no more elements */ + { + p->m_pLastReliableEl = NULL; /* set the last to NULL */ + } + + p->m_bWait4ReliableAckToProcessNack = false; // we just got a ack + +#ifdef LARGE_FRAME_SIZE + /* It is safe to notify user, as we are done playing with the pointers */ + for (i = 0; i < uiNumToNotify; ++i) + { + if (aCB[i].cb != NULL) + { + /* notify user that the packet was delivered */ + BbdTransportLayer_DispatchReliableAck(p, aCB + i); + } + } +#endif + + TransportLayer_Tick(p); +#else /* TLCUST_ENABLE_RELIABLE_PL */ + ASSERT_RET(0); +#endif +} + + +void TransportLayer_ReceivedReliableNack(struct sTransportLayer* p, + unsigned char ucLastReceivedReliableSeqId, + unsigned char ucAckedSeqId) +{ +#if TLCUST_ENABLE_RELIABLE_PL + unsigned char ucExpectedReliableSeqId = (ucLastReceivedReliableSeqId+1)&0xFF; +#ifdef LARGE_FRAME_SIZE + unsigned int uiNumToNotify=0; + struct stCallback aCB[RELIABLE_MAX_PACKETS]; +#else + struct stCallback* pCB; +#endif + + PLLOG("TransportLayer_ReceivedReliableNack(Expected RelSeqId %u, AckedSeqid %u) %s\n", ucExpectedReliableSeqId, ucAckedSeqId, p->m_bWait4ReliableAckToProcessNack?"Ignored":""); + + /* if we get a NACK, then we will ignore all the other one until + * we get a ACK. Otherwise, we might be re-sending these too many times + */ + if (p->m_bWait4ReliableAckToProcessNack) + { + return; + } + p->m_bWait4ReliableAckToProcessNack = true; + + /* Let's accumulate on the state all the callbacks to call + * (to avoid reentrant call while we are working on our linked list) + */ + + if (p->m_pFirstReliableEl != NULL) + { + /* if we receive an NACK, we can assume that we were + * acked up to that one! For the rest, we will simply timeout + */ + struct stReliableElement *pElement = p->m_pFirstReliableEl; + + while (pElement != NULL) + { + /* to filter the "late" acks out, we make sure that the + * TxSeqId is matching the one that is acked + */ + if (pElement->ucTxSeqId == ucAckedSeqId) + { + /* we can now pay attention to that NACK. Let's ack all + * the messages until the expectedSeqId, and retransmit + * the other ones + */ + PLLOG("TransportLayer_FoundReliableValidElement(RelSeqId %u, " + "TxSeqId %u)\n", + pElement->ucReliableSeqId, + pElement->ucTxSeqId); + while (p->m_pFirstReliableEl != NULL) + { + if (p->m_pFirstReliableEl->ucReliableSeqId + == ucExpectedReliableSeqId) + { + struct stReliableElement *pElement = p->m_pFirstReliableEl; + while (pElement != NULL) + { + /* we will have them resend now */ + pElement->bForceResend = true; + pElement = pElement->pNext; + } + break; + } + else + { + p->m_ucTxLastAckedSeqId = + p->m_pFirstReliableEl->ucReliableSeqId; + + /* update last acked seqid */ + /* copy the callback structure */ + +#ifdef LARGE_FRAME_SIZE + aCB[uiNumToNotify++] = p->m_pFirstReliableEl->sCallback; +#else + pCB = &p->m_pFirstReliableEl->sCallback; + if (pCB->cb != NULL) + { + /* notify user that the packet was delivered */ + BbdTransportLayer_DispatchReliableAck(p, pCB); + } +#endif + + PLLOG("TransportLayer_NotifyReliableTransmittedAck(RelSeqId %u)\n", p->m_pFirstReliableEl->ucReliableSeqId); + p->m_ulReliableByteCnt -= + p->m_pFirstReliableEl->usDataSize; + + { + struct stReliableElement *pRemoveElement = + p->m_pFirstReliableEl; + p->m_pFirstReliableEl = p->m_pFirstReliableEl->pNext; // just point to the next on the list + + // we need to free the memory + bbd_free(pRemoveElement->pucData); + bbd_free(pRemoveElement); + } + } + } + /* we are done, let's exit the loop */ + break; + } + else + { + pElement = pElement->pNext; + } + } + } + +#ifdef LARGE_FRAME_SIZE + // It is safe to notify user, as we are done playing with the pointers + { + unsigned int i = 0; + for (i = 0; i < uiNumToNotify; ++i) + { + if (aCB[i].cb) + { + /* notify user that the packet was delivered */ + BbdTransportLayer_DispatchReliableAck(p, aCB + i); + } + } + } +#endif + + /* we have changed the reliable list, so we need to update the timers */ + TransportLayer_Tick(p); + +#else /* TLCUST_ENABLE_RELIABLE_PL */ + ASSERT_RET(0); +#endif +} + +/* we have an internal packet. */ + +void TransportLayer_OnInternalPacket(struct sTransportLayer* p, + unsigned char *pucData, unsigned short usSize) +{ + unsigned char ucId = *pucData++; + ASSERT_RET(usSize > 0); + + --usSize; + + switch (ucId) + { + case INT_PKT_SEQID_REQUEST: + { + unsigned char ucReqId = *pucData++; + --usSize; + ASSERT_RET(usSize == 0); + TransportLayer_OnInternalSeqIdRequest(p, ucReqId); + } + break; + + case INT_PKT_SEQID_RESPONSE: + { + struct sStreamDecoder str; + StreamDecoder_StreamDecoder(&str, pucData, usSize); + { + unsigned char ucReqId = StreamDecoder_GetU08(&str); + unsigned char ucLastRxSeqId = StreamDecoder_GetU08(&str); + unsigned char ucLastAckSeqId = StreamDecoder_GetU08(&str); + unsigned char ucReliableSeqId = StreamDecoder_GetU08(&str); + unsigned char ucTxSeqId = StreamDecoder_GetU08(&str); + unsigned char ucTxReliableSeqId = StreamDecoder_GetU08(&str); + unsigned char ucTxLastAckedSeqId = StreamDecoder_GetU08(&str); + + ASSERT_RET(! StreamCodec_Fail(&str) + && StreamCodec_GetAvailableSize(&str) == 0); + TransportLayer_OnInternalSeqIdResponse(p, ucReqId, ucLastRxSeqId, + ucLastAckSeqId, ucReliableSeqId, + ucTxSeqId, ucTxReliableSeqId, + ucTxLastAckedSeqId); + } + } + break; + + case INT_PKT_STATS_REQUEST: + { + ASSERT_RET(usSize == 0); + TransportLayer_OnInternalStatsRequest(p); + } + break; + + case INT_PKT_STATS_RESPONSE: + { + struct sStreamDecoder str; + struct stTransportLayerStats otStats; + StreamDecoder_StreamDecoder(&str, pucData, usSize); + + otStats.ulRxGarbageBytes = StreamDecoder_GetU32(&str); + otStats.ulRxPacketLost = StreamDecoder_GetU32(&str); + otStats.ulRemotePacketLost = StreamDecoder_GetU32(&str); + otStats.ulRemoteGarbage = StreamDecoder_GetU32(&str); + otStats.ulPacketSent = StreamDecoder_GetU32(&str); + otStats.ulPacketReceived = StreamDecoder_GetU32(&str); + otStats.ulAckReceived = StreamDecoder_GetU32(&str); + otStats.ulReliablePacketSent = StreamDecoder_GetU32(&str); + otStats.ulReliableRetransmit = StreamDecoder_GetU32(&str); + otStats.ulReliablePacketReceived = StreamDecoder_GetU32(&str); + otStats.ulMaxRetransmitCount = StreamDecoder_GetU32(&str); + + ASSERT_RET(! StreamCodec_Fail(&str) + && StreamCodec_GetAvailableSize(&str) == 0); + TransportLayer_OnInternalStatsResponse(p, &otStats); + } + break; + + case INT_PKT_SETTINGS_REQUEST: + { + ASSERT_RET(usSize == 0); + TransportLayer_OnInternalSettingsRequest(p); + } + break; + + case INT_PKT_SETTINGS_RESPONSE: + { + struct sStreamDecoder str; + unsigned short usMaxOutgoingPacketSize; + unsigned short usMaxIncomingPacketSize; + unsigned short usReliableRetryTimeoutMs; + unsigned short usReliableMaxRetry; + unsigned short usReliableMaxPackets; + + StreamDecoder_StreamDecoder(&str, pucData, usSize); + usMaxOutgoingPacketSize = StreamDecoder_GetU16(&str); + usMaxIncomingPacketSize = StreamDecoder_GetU16(&str); + usReliableRetryTimeoutMs = StreamDecoder_GetU16(&str); + usReliableMaxRetry = StreamDecoder_GetU16(&str); + usReliableMaxPackets = StreamDecoder_GetU16(&str); + + ASSERT_RET(! StreamCodec_Fail(&str) + && StreamCodec_GetAvailableSize(&str) == 0); + TransportLayer_OnInternalSettingsResponse(p, + usMaxOutgoingPacketSize, + usMaxIncomingPacketSize, + usReliableRetryTimeoutMs, + usReliableMaxRetry, + usReliableMaxPackets); + } + break; + + default: + break; + } +} + +static unsigned char _ucBuf[MAX_OUTGOING_PACKET_SIZE]; +void TransportLayer_SendInternalPacket(struct sTransportLayer* p, + unsigned char ucId, + const unsigned char *pucData, + unsigned short usSize) +{ + unsigned short usFlags = FLAG_INTERNAL_PACKET; + + /* Yes we could have avoided a memcpy if the upper function + * were to encode ucId into the buffer already, + * but code readability is the preffered path, especially + * as the internal packet are rarely used, and little data is exchanged. + */ + _ucBuf[0] = ucId; + ASSERT_RET(usSize < sizeof(_ucBuf)); + memcpy(&_ucBuf[1], pucData, usSize); + + TransportLayer_BuildAndSendPacket(p, usFlags, _ucBuf, + usSize+1, 0/*ucReliableTxSeqId*/); +} + + +void TransportLayer_OnInternalSeqIdRequest(struct sTransportLayer* p, + unsigned char ucReqId) +{ + /* ensure proper packet */ + unsigned char ucBuf[7]; + struct sStreamEncoder otStream; + + StreamEncoder_StreamEncoder(&otStream, ucBuf, sizeof(ucBuf)); + StreamEncoder_PutU08(&otStream, ucReqId); + StreamEncoder_PutU08(&otStream, p->m_ucLastRxSeqId); + StreamEncoder_PutU08(&otStream, p->m_ucLastAckSeqId); + StreamEncoder_PutU08(&otStream, p->m_ucReliableSeqId); + StreamEncoder_PutU08(&otStream, p->m_ucTxSeqId); + StreamEncoder_PutU08(&otStream, p->m_ucTxReliableSeqId); + StreamEncoder_PutU08(&otStream, p->m_ucTxLastAckedSeqId); + + ASSERT_RET(!StreamCodec_Fail(&otStream)); + TransportLayer_SendInternalPacket(p, + INT_PKT_SEQID_RESPONSE, + StreamCodec_GetStreamBuffer(&otStream), + StreamCodec_GetOffset(&otStream)); +} + +void TransportLayer_OnInternalSeqIdResponse(struct sTransportLayer* p, + unsigned char ucReqId, + unsigned char ucLastRxSeqId, + unsigned char ucLastAckSeqId, + unsigned char ucReliableSeqId, + unsigned char ucTxSeqId, + unsigned char ucTxReliableSeqId, + unsigned char ucTxLastAckedSeqId) +{ + /* is there ongoing rquest, and is the reqId matching? */ + if (p->m_bOngoingSync) + { + /* Great, let's set our seqId appropriately, so we can + * start communicating with the remote side, and not + * have any garbage detected + * +1 because the next TxSeqId is expected to be one + * more than last received. + * other +1 because this response triggered an ACK to + * be sent to the remote party, so it indeed already have + * incremented its seqId + */ + p->m_ucTxSeqId = (ucLastRxSeqId+1+1)&0xFF; + p->m_ucTxReliableSeqId = (ucReliableSeqId+1)&0xFF; + /* last acked should be -1 */ + p->m_ucTxLastAckedSeqId = (p->m_ucTxReliableSeqId-1)&0xFF; + p->m_ucLastRxSeqId = (ucTxSeqId)&0xFF; + p->m_ucReliableSeqId = (ucTxReliableSeqId-1)&0xFF; + + /* let's clean the Reliable state */ + p->m_ucReliableCrc = 0; + p->m_usReliableLen = 0; + + p->m_bOngoingSync = false; + TransportLayer_Tick(p); /* refresh internal timers */ + p->m_callback->OnRemoteSyncComplete(p->m_callback); + } +} + + +void TransportLayer_OnInternalStatsRequest(struct sTransportLayer* p) +{ + unsigned char ucBuf[11*4]; + struct sStreamEncoder str; + StreamEncoder_StreamEncoder(&str, ucBuf, sizeof(ucBuf)); + + StreamEncoder_PutU32(&str, p->m_otCurrentStats.ulRxGarbageBytes); + StreamEncoder_PutU32(&str, p->m_otCurrentStats.ulRxPacketLost); + StreamEncoder_PutU32(&str, p->m_otCurrentStats.ulRemotePacketLost); + StreamEncoder_PutU32(&str, p->m_otCurrentStats.ulRemoteGarbage); + StreamEncoder_PutU32(&str, p->m_otCurrentStats.ulPacketSent); + StreamEncoder_PutU32(&str, p->m_otCurrentStats.ulPacketReceived); + StreamEncoder_PutU32(&str, p->m_otCurrentStats.ulAckReceived); + StreamEncoder_PutU32(&str, p->m_otCurrentStats.ulReliablePacketSent); + StreamEncoder_PutU32(&str, p->m_otCurrentStats.ulReliableRetransmit); + StreamEncoder_PutU32(&str, p->m_otCurrentStats.ulReliablePacketReceived); + StreamEncoder_PutU32(&str, p->m_otCurrentStats.ulMaxRetransmitCount); + + ASSERT_RET(!StreamCodec_Fail(&str)); + + TransportLayer_SendInternalPacket(p, + INT_PKT_STATS_RESPONSE, + StreamCodec_GetStreamBuffer(&str), + StreamCodec_GetOffset (&str)); +} + +void TransportLayer_OnInternalStatsResponse(struct sTransportLayer* p, + struct stTransportLayerStats* rStats) +{ + /* what do we want to do with it? + * Save it locally for access via sysfs + */ +} + +void TransportLayer_OnInternalSettingsRequest(struct sTransportLayer* p) +{ + unsigned char ucBuf[5*2]; + struct sStreamEncoder str; + StreamEncoder_StreamEncoder(&str, ucBuf, sizeof(ucBuf)); + + StreamEncoder_PutU16(&str, MAX_OUTGOING_PACKET_SIZE); + StreamEncoder_PutU16(&str, MAX_INCOMING_PACKET_SIZE); + StreamEncoder_PutU16(&str, RELIABLE_RETRY_TIMEOUT_MS); + StreamEncoder_PutU16(&str, RELIABLE_MAX_RETRY); + StreamEncoder_PutU16(&str, RELIABLE_MAX_PACKETS); + + ASSERT_RET(!StreamCodec_Fail(&str)); + + TransportLayer_SendInternalPacket(p, + INT_PKT_SETTINGS_RESPONSE, + StreamCodec_GetStreamBuffer(&str), + StreamCodec_GetOffset (&str)); +} + +void TransportLayer_OnInternalSettingsResponse(struct sTransportLayer* p, + unsigned short usMaxOutgoingPacketSize, + unsigned short usMaxIncomingPacketSize, + unsigned short usReliableRetryTimeoutMs, + unsigned short usReliableMaxRetry, + unsigned short usReliableMaxPackets) +{ + /* what do we want to do with it? + * Save it locally for access via sysfs + */ +} diff --git a/drivers/sensors/brcm/bbdpl1/transport/transport_layer_c.h b/drivers/sensors/brcm/bbdpl1/transport/transport_layer_c.h new file mode 100644 index 0000000..48671bc --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/transport/transport_layer_c.h @@ -0,0 +1,259 @@ +/****************************************************************************** + ** \file transport_layer_c.h TransportLayer struct declaration + * + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#ifndef TRANSPORT_LAYER_C_H +#define TRANSPORT_LAYER_C_H + +/* This is the core transport layer for the Broadcom Bridge Driver + * It is a straightforward migration of C++ embedded code to C + * The BBD-specific aspects are: + * - bool m_bPassThrough + * - some extra callbacks + * - some extra function with BbdTransportLayer_ prefix. + */ + +#include "../utils/bbd_utils.h" +#include "../transport/transport_layer_custom.h" +#include "../utils/crc8bits_c.h" + +#ifndef TRANSPORT_LAYER_H +/* prototype for callback associated with a packet for reliable transactions */ +typedef void (*PacketDeliveryNotification)(void* pCbData, unsigned long ulCbData); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct sBbdReliableTransaction +{ + unsigned char* pucData; + unsigned short usSize; + PacketDeliveryNotification cb; + void* pCbData; + unsigned long ulCbData; +}; + +struct sBbdReliableTransaction* BbdReliableTransaction_init(struct sBbdReliableTransaction* p); + +struct stCallback +{ + PacketDeliveryNotification cb; + void *pCbData; + unsigned long ulCbData; +}; + +struct sITransportLayerCb; + +/* + * Callbacks needed by the TransportLayer + */ +typedef void (*ITransportCB_OnTimerSet)(struct sITransportLayerCb*, long lTimerMs); +typedef unsigned long (*ITransportCB_OnTimeMsRequest)(struct sITransportLayerCb*); +typedef void (*ITransportCB_OnDataToSend)(struct sITransportLayerCb*, unsigned char *pucData, unsigned long ulSize); +typedef void (*ITransportCB_OnCommunicationError)(struct sITransportLayerCb*); +typedef void (*ITransportCB_OnPacketReceived)(struct sITransportLayerCb*, unsigned char *pucData, unsigned short usSize); +typedef void (*ITransportCB_OnException)(struct sITransportLayerCb*, const char filename[], unsigned int uiLine); +typedef void (*ITransportCB_OnRemoteSyncComplete)(struct sITransportLayerCb*); +typedef void (*ITransportCB_OnReliableAck)(struct sITransportLayerCb*, struct sBbdReliableTransaction*); +typedef void (*ITransportCB_OnDataToLhd)(struct sITransportLayerCb*, const unsigned char *pucData, unsigned short usSize); + +struct sITransportLayerCb +{ + void *m_callbackData; + + /* TransportLayer can sleep for lTimerMs. -1 means the TransportLayer + * is idle + */ + ITransportCB_OnTimerSet OnTimerSet; + + /* TransportLayer needs to know about the platform time (32bit ms timer + * monotically increasing) + */ + ITransportCB_OnTimeMsRequest OnTimeMsRequest; + + /* Data must be sent to the remote TransportLayer */ + ITransportCB_OnDataToSend OnDataToSend; + + /* TransportLayer was not able to recover due to communication errors */ + ITransportCB_OnCommunicationError OnCommunicationError; + + /* The payload from a packet was received from the remote TransportLayer */ + ITransportCB_OnPacketReceived OnPacketReceived; + + /* an internal error occured */ + ITransportCB_OnException OnException; + + /* The synchronization with the remote side is complete, you can + * start using the Transport Layer to communicate with the remote side + */ + ITransportCB_OnRemoteSyncComplete OnRemoteSyncComplete; + + ITransportCB_OnReliableAck OnReliableAck; + ITransportCB_OnDataToLhd OnDataToLhd; +}; + +/* + * statistics of the TransportLayer + */ +struct stTransportLayerStats +{ + unsigned long ulRxGarbageBytes; + unsigned long ulRxPacketLost; + /* this is approximate as it is reported and the report could be lost */ + unsigned long ulRemotePacketLost; + /* this is approximate as it is reported and the report could be lost */ + unsigned long ulRemoteGarbage; + unsigned long ulPacketSent; /* number of normal packets sent */ + unsigned long ulPacketReceived; + unsigned long ulAckReceived; + unsigned long ulReliablePacketSent; + unsigned long ulReliableRetransmit; + unsigned long ulReliablePacketReceived; + unsigned long ulMaxRetransmitCount; +}; + +struct stTransportLayerStats* stTransportLayerStats_stTransportLayerStats( + struct stTransportLayerStats* p); + + +/* public constants that are built from the customization file */ +#define MAX_OUTGOING_PACKET_SIZE TLCUST_MAX_OUTGOING_PACKET_SIZE +#define MAX_INCOMING_PACKET_SIZE TLCUST_MAX_INCOMING_PACKET_SIZE +#define RELIABLE_RETRY_TIMEOUT_MS TLCUST_RELIABLE_RETRY_TIMEOUT_MS +#define RELIABLE_MAX_RETRY TLCUST_RELIABLE_MAX_RETRY +#define RELIABLE_MAX_PACKETS TLCUST_RELIABLE_MAX_PACKETS + +#define MAX_HEADER_SIZE 14 + +/* + * The TransportLayer struct exchanges packets with a remote TransportLayer. + * The data is a stream of bytes. + * The TransportLayer is agnostic to the payload. It supports a reliable + * channel that will retransmit packets until the other side acknowledges it + * to ensure delivery and order is maintained. + */ + +struct sTransportLayer +{ + struct sITransportLayerCb *m_callback; + + unsigned int m_uiParserState; + unsigned char m_aucRxMessageBuf[MAX_INCOMING_PACKET_SIZE+MAX_HEADER_SIZE]; + unsigned int m_uiRxLen; + unsigned char m_ucLastRxSeqId; + unsigned long m_ulByteCntSinceLastValidPacket; + + unsigned char m_ucLastAckSeqId; + unsigned char m_ucReliableSeqId; + unsigned char m_ucReliableCrc; + unsigned char m_ucDelayAckCount; + unsigned short m_usReliableLen; + + unsigned char m_aucTxBuffer[(MAX_OUTGOING_PACKET_SIZE+MAX_HEADER_SIZE+2)*2]; + unsigned char m_ucTxSeqId; + struct sCrc8Bits m_otTxCrc; /* tx CRC */ + + struct stTransportLayerStats m_otCurrentStats; + struct stTransportLayerStats m_otLastPLUpdatedStats; + + unsigned char m_ucTxReliableSeqId; + unsigned char m_ucTxLastAckedSeqId; + struct stReliableElement *m_pFirstReliableEl; + struct stReliableElement *m_pLastReliableEl; + unsigned long m_ulReliableByteCnt; + + bool m_bWait4ReliableAckToProcessNack; + + bool m_bOngoingSync; + unsigned long m_ulSyncTimestamp; + unsigned long m_ulRetryCnt; + + const char* m_pDebugName; + + /* Here are the BBD-specific items */ + bool m_bPassThrough; +}; + +/* constructor. Need a reference to the IPacketLayerCb for the + * callback mechanism. + */ +struct sTransportLayer* TransportLayer_TransportLayer(struct sTransportLayer* p, + struct sITransportLayerCb* rCallback, const char *pDebugName); +void TransportLayer_dtor(struct sTransportLayer* p); + +/* Sends a packet to the remote TransportLayer. */ +void TransportLayer_SendPacket(struct sTransportLayer* p, unsigned char *pucData, + unsigned short usSize); + +/* Sends a reliable packet to the remote TransportLayer. Delivery is + * ensured, and PacketDeliveryNotification callback will be called + * (if not NULL) when delivery is confirmed + */ +bool TransportLayer_SendReliablePacket(struct sTransportLayer* p, + unsigned char *pucData, unsigned short usSize, + PacketDeliveryNotification cb, void *pCbData, + unsigned long ulCbData); + +/* Push data from remote TransportLayer */ +void TransportLayer_SetData(struct sTransportLayer* p, const unsigned char *pucData, + unsigned short usSize); + +/* Called to refresh internal timers. Should be called when timer + * returned by OnTimerSet callback expires + */ +void TransportLayer_Tick(struct sTransportLayer* p); + +/* Reset the TL seqId. That should be used if the Remote party was reset, + * otherwise, the reliable transport will break down, and the statistics + * will show inconsistent data + */ +void TransportLayer_Reset(struct sTransportLayer* p); + +/* The TL layer will start a synchronization with the remote side. That + * could be used when started a TL and not sure about the other side state. + * A remote sync should happen without any other transactions (i.e. no send + * packets!). + * OnRemoteSyncComplete is called once this is complete + */ +void TransportLayer_StartRemoteSync(struct sTransportLayer* p); + +/* Return the size of the data in the reliable buffer (i.e. which has + * not been acked yet) + */ +unsigned long TransportLayer_GetReliableUsage(const struct sTransportLayer* const p); + +/* Return true if it is possible to send a reliable packet + * (limited by RELIABLE_MAX_PACKETS packets, and the intrinsic max is 255) + */ +bool TransportLayer_IsReliableAvailable(const struct sTransportLayer* const p); + +/* Get a reference to the TL statistics */ +const struct stTransportLayerStats* const TransportLayer_GetStats( + const struct sTransportLayer* const p); + +#ifdef __cplusplus +} +#endif + +#endif /* TRANSPORT_LAYER_C_H */ + diff --git a/drivers/sensors/brcm/bbdpl1/transport/transport_layer_custom.h b/drivers/sensors/brcm/bbdpl1/transport/transport_layer_custom.h new file mode 100644 index 0000000..5a47e60 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/transport/transport_layer_custom.h @@ -0,0 +1,64 @@ +/******************************************************************************* + ** \file transport_layer_custom.h customization file for the TransportLayer + * + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#ifndef TRANSPORT_LAYER_CUSTOM_H +#define TRANSPORT_LAYER_CUSTOM_H + +/* +******************* CUSTOMIZATION FILE ******************** +* This file can be edited for your project, and you can customize your transport layer by changing some of the default value +* Note that you can also define these value from your makefile directly +*/ + +/// if defined to 1, the reliable channel will be enabled. If defined to 0, it will be disabled and compiled out +#ifndef TLCUST_ENABLE_RELIABLE_PL + #define TLCUST_ENABLE_RELIABLE_PL 1 +#endif + +// Defines the max size of outgoing packet. That should match the max size of incoming packet from the remote TL +#ifndef TLCUST_MAX_OUTGOING_PACKET_SIZE + #define TLCUST_MAX_OUTGOING_PACKET_SIZE 2048 +#endif + +// Defines the max size of incoming packet. That should match the max size of outgoing packet from the remote TL +#ifndef TLCUST_MAX_INCOMING_PACKET_SIZE + #define TLCUST_MAX_INCOMING_PACKET_SIZE 2048 +#endif + +// Defines the number of millisecond before retrying a reliable packet when no acknowledgement is received. +#ifndef TLCUST_RELIABLE_RETRY_TIMEOUT_MS + #define TLCUST_RELIABLE_RETRY_TIMEOUT_MS 1000 +#endif + +// Defines the number of retries before declaring a communication error +#ifndef TLCUST_RELIABLE_MAX_RETRY + #define TLCUST_RELIABLE_MAX_RETRY 10 +#endif + + +// Defines the maximum number of reliable packets that can be in transit(MAX is 255) +#ifndef TLCUST_RELIABLE_MAX_PACKETS + #define TLCUST_RELIABLE_MAX_PACKETS 150 +#endif + +#endif /* TRANSPORT_LAYER_CUSTOM_H */ + diff --git a/drivers/sensors/brcm/bbdpl1/utils/Makefile b/drivers/sensors/brcm/bbdpl1/utils/Makefile new file mode 100644 index 0000000..e7d5b69 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/utils/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the Broadcom Bridge Driver +# + +# Each configuration option enables a list of files. +obj-$(CONFIG_SENSORS_SSP_BBD) += crc8bits_c.o ring_buffer_c.o stream_codec_c.o diff --git a/drivers/sensors/brcm/bbdpl1/utils/bbd_utils.h b/drivers/sensors/brcm/bbdpl1/utils/bbd_utils.h new file mode 100644 index 0000000..43636c2 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/utils/bbd_utils.h @@ -0,0 +1,38 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#ifndef BBD_UTILS_H_ /* { */ +#define BBD_UTILS_H_ + +#include + +#include +#include + +#define ASSERT_COMPILE(test_) \ + int __static_assert(int assert[(test_) ? 1 : -1]) + +#ifndef _DIM +/** Array dimension +*/ +#define _DIM(x) ((unsigned int)(sizeof(x)/sizeof(*(x)))) +#endif + +#endif /* } BBD_UTILS_H_ */ diff --git a/drivers/sensors/brcm/bbdpl1/utils/crc8bits_c.c b/drivers/sensors/brcm/bbdpl1/utils/crc8bits_c.c new file mode 100644 index 0000000..8cc9938 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/utils/crc8bits_c.c @@ -0,0 +1,84 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + * \file crc8bits_c.c 8 bits CRC generator + */ + +#include "crc8bits_c.h" + +static const unsigned char Crc8Bits_aucCrcTable[] = +{ + 0x00,0x4d,0x9a,0xd7,0x79,0x34,0xe3,0xae,0xf2,0xbf,0x68,0x25,0x8b,0xc6,0x11,0x5c, + 0xa9,0xe4,0x33,0x7e,0xd0,0x9d,0x4a,0x07,0x5b,0x16,0xc1,0x8c,0x22,0x6f,0xb8,0xf5, + 0x1f,0x52,0x85,0xc8,0x66,0x2b,0xfc,0xb1,0xed,0xa0,0x77,0x3a,0x94,0xd9,0x0e,0x43, + 0xb6,0xfb,0x2c,0x61,0xcf,0x82,0x55,0x18,0x44,0x09,0xde,0x93,0x3d,0x70,0xa7,0xea, + 0x3e,0x73,0xa4,0xe9,0x47,0x0a,0xdd,0x90,0xcc,0x81,0x56,0x1b,0xb5,0xf8,0x2f,0x62, + 0x97,0xda,0x0d,0x40,0xee,0xa3,0x74,0x39,0x65,0x28,0xff,0xb2,0x1c,0x51,0x86,0xcb, + 0x21,0x6c,0xbb,0xf6,0x58,0x15,0xc2,0x8f,0xd3,0x9e,0x49,0x04,0xaa,0xe7,0x30,0x7d, + 0x88,0xc5,0x12,0x5f,0xf1,0xbc,0x6b,0x26,0x7a,0x37,0xe0,0xad,0x03,0x4e,0x99,0xd4, + 0x7c,0x31,0xe6,0xab,0x05,0x48,0x9f,0xd2,0x8e,0xc3,0x14,0x59,0xf7,0xba,0x6d,0x20, + 0xd5,0x98,0x4f,0x02,0xac,0xe1,0x36,0x7b,0x27,0x6a,0xbd,0xf0,0x5e,0x13,0xc4,0x89, + 0x63,0x2e,0xf9,0xb4,0x1a,0x57,0x80,0xcd,0x91,0xdc,0x0b,0x46,0xe8,0xa5,0x72,0x3f, + 0xca,0x87,0x50,0x1d,0xb3,0xfe,0x29,0x64,0x38,0x75,0xa2,0xef,0x41,0x0c,0xdb,0x96, + 0x42,0x0f,0xd8,0x95,0x3b,0x76,0xa1,0xec,0xb0,0xfd,0x2a,0x67,0xc9,0x84,0x53,0x1e, + 0xeb,0xa6,0x71,0x3c,0x92,0xdf,0x08,0x45,0x19,0x54,0x83,0xce,0x60,0x2d,0xfa,0xb7, + 0x5d,0x10,0xc7,0x8a,0x24,0x69,0xbe,0xf3,0xaf,0xe2,0x35,0x78,0xd6,0x9b,0x4c,0x01, + 0xf4,0xb9,0x6e,0x23,0x8d,0xc0,0x17,0x5a,0x06,0x4b,0x9c,0xd1,0x7f,0x32,0xe5,0xa8 +}; + +// C function to compute the CRC of a memory region +unsigned char cGet8bitCrc(const unsigned char *pucData, unsigned long ulLen) +{ + struct sCrc8Bits otCrc; + Crc8Bits_Crc8Bits(&otCrc); + Crc8Bits_Update(&otCrc, pucData, ulLen); + return Crc8Bits_GetState(&otCrc); +} + +void Crc8Bits_Crc8Bits(struct sCrc8Bits* p) +{ + p->m_ucCrcState = 0; +} + +unsigned char Crc8Bits_UpdateByte(struct sCrc8Bits* p, unsigned char ucData) +{ + p->m_ucCrcState = Crc8Bits_aucCrcTable[p->m_ucCrcState ^ ucData]; + return p->m_ucCrcState; +} + +unsigned char Crc8Bits_Update(struct sCrc8Bits* p, + const unsigned char *pucData, unsigned long ulLen) +{ + while (ulLen--) + { + p->m_ucCrcState = Crc8Bits_aucCrcTable[p->m_ucCrcState ^ (*pucData++)]; + } + return p->m_ucCrcState; +} + +unsigned char Crc8Bits_GetState(const struct sCrc8Bits* p) +{ + return p->m_ucCrcState; +} + +void Crc8Bits_Reset(struct sCrc8Bits* p) +{ + p->m_ucCrcState = 0; +} + + diff --git a/drivers/sensors/brcm/bbdpl1/utils/crc8bits_c.h b/drivers/sensors/brcm/bbdpl1/utils/crc8bits_c.h new file mode 100644 index 0000000..99f063a --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/utils/crc8bits_c.h @@ -0,0 +1,45 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#ifndef CRC_8BITS_C_H +#define CRC_8BITS_C_H + +struct sCrc8Bits +{ + unsigned char m_ucCrcState; +}; + +void Crc8Bits_Crc8Bits(struct sCrc8Bits *p); + +void Crc8Bits_Reset(struct sCrc8Bits *p); // Reset the CRC + +// Update CRC with array of bytes +unsigned char Crc8Bits_Update(struct sCrc8Bits *p, const unsigned char *pacData, unsigned long ulLen); + +// Update CRC with a single byte +unsigned char Crc8Bits_UpdateByte(struct sCrc8Bits *p, unsigned char ucData); + +// Get the CRC +unsigned char Crc8Bits_GetState(const struct sCrc8Bits *p); + +// C function to compute the CRC of a memory region +unsigned char cGet8bitCrc(const unsigned char *pacData, unsigned long ulLen); + +#endif /* CRC_8BITS_C_H */ diff --git a/drivers/sensors/brcm/bbdpl1/utils/ring_buffer_c.c b/drivers/sensors/brcm/bbdpl1/utils/ring_buffer_c.c new file mode 100644 index 0000000..efd2e3d --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/utils/ring_buffer_c.c @@ -0,0 +1,156 @@ +/****************************************************************************** + ** \file ring_buffer_c.c RingBuffer struct definition + * + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#include +#include "ring_buffer_c.h" +#include "../bbd_internal.h" + +void RingBuffer_RingBuffer(struct sRingBuffer* p, + unsigned long ulSizeRingBuffer) +{ + p->m_pBase = NULL; + p->m_pEnd = NULL; + p->m_pTail = NULL; + p->m_pHead = NULL; + p->m_ulSize = ulSizeRingBuffer; + p->m_ulUsed = 0; + p->m_bFull = false; + p->m_pBase = (unsigned char *) bbd_calloc(ulSizeRingBuffer); + if (p->m_pBase != NULL) + { + p->m_pTail = p->m_pBase; + p->m_pHead = p->m_pBase; + p->m_pEnd = p->m_pBase + p->m_ulSize; + } +} + +void RingBuffer_dtor(struct sRingBuffer* p) +{ + if (p->m_pBase) + bbd_free(p->m_pBase); + p->m_pBase = 0; +} + +void RingBuffer_SetData(struct sRingBuffer* p, unsigned char raucData[], unsigned long ulSize) +{ + if (p->m_bFull || ulSize == 0) + { + return; + } + + /* Copy what we can until the end of the buffer, and handle rollovers */ + if (p->m_pHead >= p->m_pTail) + { + unsigned long ulSizeTillEnd = (unsigned long )(p->m_pEnd-p->m_pHead); + if (ulSizeTillEnd > ulSize) + { + ulSizeTillEnd = ulSize; + } + memcpy(p->m_pHead, raucData, ulSizeTillEnd); + p->m_ulUsed += ulSizeTillEnd; + p->m_pHead += ulSizeTillEnd; + ulSize -= ulSizeTillEnd; + raucData += ulSizeTillEnd; + if (p->m_pHead == p->m_pEnd) + { + p->m_pHead = p->m_pBase; + if (p->m_pTail == p->m_pBase) + { + p->m_bFull = true; + } + } + if (ulSize == 0 || p->m_bFull) + { + return; + } + } + + /* at that point, the head is before the tail */ + { + unsigned long ulSizeAvailable = RingBuffer_GetAvailableSize(p); + if (ulSizeAvailable > ulSize) + { + ulSizeAvailable = ulSize; + } + + memcpy(p->m_pHead, raucData, ulSizeAvailable); + p->m_ulUsed += ulSizeAvailable; + p->m_pHead += ulSizeAvailable; + ulSize -= ulSizeAvailable; + + if (p->m_pHead == p->m_pTail) + { + p->m_bFull = true; + } + } +} + +unsigned long RingBuffer_GetAvailableSize(const struct sRingBuffer* const p) +{ + return p->m_ulSize-p->m_ulUsed; +} + + +unsigned long RingBuffer_GetDataSize(const struct sRingBuffer* const p) +{ + return p->m_ulUsed; +} + + +unsigned long RingBuffer_GetData(struct sRingBuffer* p, unsigned char raucData[], unsigned long ulMaxSize) +{ + unsigned long ulAvail = RingBuffer_GetDataSize(p); + if (ulMaxSize == 0 || ulAvail == 0) + { + return 0; + } + + { + unsigned long ulSizeTillEnd = (unsigned long)(p->m_pEnd-p->m_pTail); + + + if (ulSizeTillEnd > ulMaxSize) + { + ulSizeTillEnd = ulMaxSize; + } + if (ulSizeTillEnd > ulAvail) + { + ulSizeTillEnd = ulAvail; + } + + memcpy(raucData, p->m_pTail, ulSizeTillEnd); + p->m_ulUsed -= ulSizeTillEnd; + ulMaxSize -= ulSizeTillEnd; + p->m_pTail += ulSizeTillEnd; + raucData += ulSizeTillEnd; + if (p->m_pTail == p->m_pEnd) + { + p->m_pTail = p->m_pBase; + } + p->m_bFull = false; + /* we have at least copied one B, so it is not full anymore */ + + /* recursive call on the rest of the array */ + return ulSizeTillEnd + RingBuffer_GetData(p, raucData, ulMaxSize); + } +} + diff --git a/drivers/sensors/brcm/bbdpl1/utils/ring_buffer_c.h b/drivers/sensors/brcm/bbdpl1/utils/ring_buffer_c.h new file mode 100644 index 0000000..2e40440 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/utils/ring_buffer_c.h @@ -0,0 +1,68 @@ +/******************************************************************************* + ** \file ring_buffer.h RingBuffer class declaration + * + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#ifndef RING_BUFFER__C_H +#define RING_BUFFER__C_H + +#include "bbd_utils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct sRingBuffer +{ + unsigned char *m_pBase; + unsigned char *m_pEnd; + unsigned char *m_pTail; + unsigned char *m_pHead; + unsigned long m_ulSize; + unsigned long m_ulUsed; + bool m_bFull; +}; + +/* Dynamic memroy will be allocated */ +void RingBuffer_RingBuffer(struct sRingBuffer *p, unsigned long ulSizeRingBuffer); +void RingBuffer_dtor(struct sRingBuffer *p); + +/* push data to the ring buffer. Bytes will be dropped if space is not available */ +void RingBuffer_SetData(struct sRingBuffer *p, + unsigned char raucData[], unsigned long ulSize); + +/* request the available size */ +unsigned long RingBuffer_GetAvailableSize(const struct sRingBuffer *const p); + +/* request the size of the data available */ +unsigned long RingBuffer_GetDataSize(const struct sRingBuffer *const p); + +/* get data from the ring buffer. Returns the number of + * bytes copied to raucData + */ + +unsigned long RingBuffer_GetData(struct sRingBuffer *p, + unsigned char raucData[], unsigned long ulMaxSize); + +#ifdef __cplusplus +} +#endif + +#endif /* RING_BUFFER_H */ diff --git a/drivers/sensors/brcm/bbdpl1/utils/stream_codec_c.c b/drivers/sensors/brcm/bbdpl1/utils/stream_codec_c.c new file mode 100644 index 0000000..aef6721 --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/utils/stream_codec_c.c @@ -0,0 +1,209 @@ +/****************************************************************************** + ** \file stream_codec_c.c StreamCodec class definition + * + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#include "bbd_utils.h" +#include "stream_codec_c.h" + +#define FAIL_CHECK(_test) {if(!(_test)){p->m_bFail = true; return; }} +#define FAIL_CHECK_RET0(_test) {if(!(_test)){p->m_bFail = true; return 0;}} + +void StreamCodec_StreamCodec(struct sStreamCodec* p, unsigned char* buf, + unsigned int bufsize) +{ + p->m_uiOffset = 0; + p->m_pBuf = buf; + p->m_uiMaxOffset = bufsize; + p->m_uiBitOffset = 0; + p->m_bFail = false; +} + +unsigned int StreamCodec_GetOffset(struct sStreamCodec* p) +{ + // When using bit packing, no other methods should be + // called until it's aligned to a byte + FAIL_CHECK_RET0(p->m_uiBitOffset == 0); + + return p->m_uiOffset; +} + +unsigned int StreamCodec_GetAvailableSize(struct sStreamCodec* p) +{ + FAIL_CHECK_RET0(p->m_uiBitOffset == 0); + return (p->m_uiMaxOffset-p->m_uiOffset); +} + +const unsigned char* StreamCodec_GetStreamBuffer(struct sStreamCodec* p) +{ + return p->m_pBuf; +} + +void StreamCodec_Reset(struct sStreamCodec* p) +{ + p->m_uiOffset = 0; + p->m_uiBitOffset = 0; + p->m_bFail = false; +} + +bool StreamCodec_Fail(const struct sStreamCodec* const p) +{ + return p->m_bFail; +} + +void StreamDecoder_StreamDecoder(struct sStreamCodec* p, + unsigned char* buf, unsigned int bufsize) +{ + StreamCodec_StreamCodec((struct sStreamCodec *) p, buf, bufsize); +} + +unsigned char StreamDecoder_GetU08(struct sStreamCodec* p) +{ + FAIL_CHECK_RET0(p->m_uiBitOffset == 0); + FAIL_CHECK_RET0(p->m_uiOffset+1<=p->m_uiMaxOffset); + + return p->m_pBuf[p->m_uiOffset++]; +} + +signed char StreamDecoder_GetS08(struct sStreamCodec* p) +{ + return (signed char)StreamDecoder_GetU08(p); +} + +unsigned short StreamDecoder_GetU16(struct sStreamCodec* p) +{ + FAIL_CHECK_RET0(p->m_uiBitOffset == 0); + FAIL_CHECK_RET0(p->m_uiOffset+2<=p->m_uiMaxOffset); + { + unsigned short usValue = p->m_pBuf[p->m_uiOffset++]; + usValue |= p->m_pBuf[p->m_uiOffset++]<<8; + return usValue; + } +} + +signed short StreamDecoder_GetS16(struct sStreamCodec* p) +{ + return (signed short)StreamDecoder_GetU16(p); +} + +unsigned long StreamDecoder_GetU32(struct sStreamCodec* p) +{ + FAIL_CHECK_RET0(p->m_uiBitOffset == 0); + FAIL_CHECK_RET0(p->m_uiOffset+4<=p->m_uiMaxOffset); + + { + unsigned long ulValue; + ulValue = p->m_pBuf[p->m_uiOffset++]; + ulValue |= p->m_pBuf[p->m_uiOffset++]<<8; + ulValue |= p->m_pBuf[p->m_uiOffset++]<<16; + ulValue |= p->m_pBuf[p->m_uiOffset++]<<24; + + return ulValue; + } +} + + +signed long StreamDecoder_GetS32(struct sStreamCodec* p) +{ + return (signed long)StreamDecoder_GetU32(p); +} + +unsigned char *StreamDecoder_GetBuffer (struct sStreamCodec* p, unsigned long size) +{ + unsigned char* buf = &p->m_pBuf[p->m_uiOffset]; + if (p->m_uiBitOffset != 0 || p->m_uiOffset+size>p->m_uiMaxOffset) + { + p->m_bFail = true; + } + else + { + p->m_uiOffset+=size; + } + return buf; +} + + + +void StreamEncoder_StreamEncoder(struct sStreamCodec* p, + unsigned char* buf, unsigned int bufsize) +{ + StreamCodec_StreamCodec(p, buf, bufsize); +} + +void StreamEncoder_PutU08(struct sStreamCodec* p, unsigned char u8) +{ + FAIL_CHECK(p->m_uiBitOffset == 0); + FAIL_CHECK(p->m_uiOffset+1<=p->m_uiMaxOffset); + + p->m_pBuf[p->m_uiOffset++] = u8; +} + +void StreamEncoder_PutS08 (struct sStreamCodec* p, signed char s8) +{ + StreamEncoder_PutU08(p, (unsigned char)s8); +} + +void StreamEncoder_PutU16 (struct sStreamCodec* p, unsigned short u16) +{ + FAIL_CHECK(p->m_uiBitOffset == 0); + FAIL_CHECK(p->m_uiOffset+2<=p->m_uiMaxOffset); + + { + /* Serialize unsigned short value */ + p->m_pBuf[p->m_uiOffset++] = (u16)&0xFF; + p->m_pBuf[p->m_uiOffset++] = (u16>>8)&0xFF; + } +} + +void StreamEncoder_PutS16 (struct sStreamCodec* p, signed short s16) +{ + StreamEncoder_PutU16(p, (unsigned short)s16); +} + +void StreamEncoder_PutU32 (struct sStreamCodec* p, unsigned long u32) +{ + FAIL_CHECK(p->m_uiBitOffset == 0); + FAIL_CHECK(p->m_uiOffset+4<=p->m_uiMaxOffset); + + /* Serialize unsigned long value */ + { + p->m_pBuf[p->m_uiOffset++] = (unsigned char)((u32)&0xFF); + p->m_pBuf[p->m_uiOffset++] = (unsigned char)((u32>>8)&0xFF); + p->m_pBuf[p->m_uiOffset++] = (unsigned char)((u32>>16)&0xFF); + p->m_pBuf[p->m_uiOffset++] = (unsigned char)((u32>>24)&0xFF); + } +} + +void StreamEncoder_PutS32 (struct sStreamCodec* p, signed long s32) +{ + StreamEncoder_PutU32(p, (unsigned long)s32); +} + +void StreamEncoder_PutBuffer(struct sStreamCodec* p, const unsigned char* buf, unsigned long size) +{ + FAIL_CHECK(p->m_uiBitOffset == 0); + FAIL_CHECK(p->m_uiOffset+size<=p->m_uiMaxOffset); + + { + memcpy(&p->m_pBuf[p->m_uiOffset], buf, size); + p->m_uiOffset += size; + } +} + diff --git a/drivers/sensors/brcm/bbdpl1/utils/stream_codec_c.h b/drivers/sensors/brcm/bbdpl1/utils/stream_codec_c.h new file mode 100644 index 0000000..03586da --- /dev/null +++ b/drivers/sensors/brcm/bbdpl1/utils/stream_codec_c.h @@ -0,0 +1,86 @@ +/******************************************************************************* + ** \file stream_codec_c.h StreamCodec struct declaration + * + * Copyright 2014 Broadcom Corporation + * + * This program 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 (the "GPL"). + * + * 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. + * + * A copy of the GPL is available at + * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The BBD (Broadcom Bridge Driver) + */ + +#ifndef STREAM_CODEC_C_H /* { */ +#define STREAM_CODEC_C_H + +#include "bbd_utils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct sStreamCodec +{ + unsigned int m_uiOffset; + unsigned char* m_pBuf; + unsigned int m_uiMaxOffset; + bool m_bBigEndian; + unsigned int m_uiBitOffset; + bool m_bFail; +}; + +#define sStreamEncoder sStreamCodec +#define sStreamDecoder sStreamCodec + +void StreamCodec_StreamCodec(struct sStreamCodec* p, unsigned char* buf, + unsigned int bufsize); +/* get size of buffer used */ +unsigned int StreamCodec_GetOffset(struct sStreamCodec *p); + +/* get available size in buffer */ +unsigned int StreamCodec_GetAvailableSize(struct sStreamCodec *p); + +/* get pointer to the buffer of the stream */ +const unsigned char* StreamCodec_GetStreamBuffer(struct sStreamCodec *p); + +/* clears the buffer. Ready to be used again on same memory buffer */ +void StreamCodec_Reset(struct sStreamCodec *p); + +// Fail returns true if an error occured. +bool StreamCodec_Fail(const struct sStreamCodec * const p); + +void StreamEncoder_StreamEncoder(struct sStreamCodec *p,unsigned char* buf, unsigned int bufsize); + +void StreamEncoder_PutU08(struct sStreamCodec *p,unsigned char); +void StreamEncoder_PutS08(struct sStreamCodec *p,signed char); +void StreamEncoder_PutU16(struct sStreamCodec *p,unsigned short); +void StreamEncoder_PutS16(struct sStreamCodec *p,signed short); +void StreamEncoder_PutU32(struct sStreamCodec *p,unsigned long); +void StreamEncoder_PutS32(struct sStreamCodec *p,signed long); +void StreamEncoder_PutBuffer(struct sStreamCodec *p,const unsigned char*, unsigned long); + +void StreamDecoder_StreamDecoder(struct sStreamCodec *p, + unsigned char* buf, unsigned int bufsize); + unsigned char StreamDecoder_GetU08(struct sStreamCodec *p); + signed char StreamDecoder_GetS08(struct sStreamCodec *p); + unsigned short StreamDecoder_GetU16(struct sStreamCodec *p); + signed short StreamDecoder_GetS16(struct sStreamCodec *p); + unsigned long StreamDecoder_GetU32(struct sStreamCodec *p); + signed long StreamDecoder_GetS32(struct sStreamCodec *p); + unsigned char* StreamDecoder_GetBuffer(struct sStreamCodec *p, unsigned long); + +#ifdef __cplusplus +} +#endif + +#endif /* } STREAM_CODEC_C_H */ diff --git a/drivers/sensors/brcm/factory/accel_icm20610.c b/drivers/sensors/brcm/factory/accel_icm20610.c new file mode 100644 index 0000000..f3a443ac --- /dev/null +++ b/drivers/sensors/brcm/factory/accel_icm20610.c @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ +#include "../ssp.h" + +/*************************************************************************/ +/* factory Sysfs */ +/*************************************************************************/ + +#define VENDOR "INVENSENSE" +#define CHIP_ID "ICM20610" + +#define CALIBRATION_FILE_PATH "/efs/calibration_data" +#define CALIBRATION_DATA_AMOUNT 20 + +/* accel range : 4g */ +#define MAX_ACCEL_1G 8192 +#define MAX_ACCEL_2G 16384 +#define MIN_ACCEL_2G -16383 +#define MAX_ACCEL_4G 32768 + +static ssize_t accel_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", VENDOR); +} + +static ssize_t accel_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", CHIP_ID); +} + +int accel_open_calibration(struct ssp_data *data) +{ + int iRet = 0; + mm_segment_t old_fs; + struct file *cal_filp = NULL; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + cal_filp = filp_open(CALIBRATION_FILE_PATH, O_RDONLY, 0666); + if (IS_ERR(cal_filp)) { + set_fs(old_fs); + iRet = PTR_ERR(cal_filp); + + data->accelcal.x = 0; + data->accelcal.y = 0; + data->accelcal.z = 0; + + return iRet; + } + + iRet = cal_filp->f_op->read(cal_filp, (char *)&data->accelcal, + 3 * sizeof(int), &cal_filp->f_pos); + if (iRet != 3 * sizeof(int)) + iRet = -EIO; + + filp_close(cal_filp, current->files); + set_fs(old_fs); + + ssp_dbg("[SSP]: open accel calibration %d, %d, %d\n", + data->accelcal.x, data->accelcal.y, data->accelcal.z); + + if ((data->accelcal.x == 0) && (data->accelcal.y == 0) + && (data->accelcal.z == 0)) + return ERROR; + + return iRet; +} + +int set_accel_cal(struct ssp_data *data) +{ + int iRet = 0; + struct ssp_msg *msg; + s16 accel_cal[3]; + + if (!(data->uSensorState & (1 << ACCELEROMETER_SENSOR))) { + pr_info("[SSP]: %s - Skip this function!!!"\ + ", accel sensor is not connected(0x%x)\n", + __func__, data->uSensorState); + return iRet; + } + accel_cal[0] = data->accelcal.x; + accel_cal[1] = data->accelcal.y; + accel_cal[2] = data->accelcal.z; + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + return -ENOMEM; + } + msg->cmd = MSG2SSP_AP_MCU_SET_ACCEL_CAL; + msg->length = 6; + msg->options = AP2HUB_WRITE; + msg->buffer = (char*) kzalloc(6, GFP_KERNEL); + + msg->free_buffer = 1; + memcpy(msg->buffer, accel_cal, 6); + + iRet = ssp_spi_async(data, msg); + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - i2c fail %d\n", __func__, iRet); + iRet = ERROR; + } + + pr_info("[SSP] Set accel cal data %d, %d, %d\n", accel_cal[0], accel_cal[1], accel_cal[2]); + return iRet; +} + +static int enable_accel_for_cal(struct ssp_data *data) +{ + u8 uBuf[4] = { 0, }; + s32 dMsDelay = get_msdelay(data->adDelayBuf[ACCELEROMETER_SENSOR]); + memcpy(&uBuf[0], &dMsDelay, 4); + + if (atomic_read(&data->aSensorEnable) & (1 << ACCELEROMETER_SENSOR)) { + if (get_msdelay(data->adDelayBuf[ACCELEROMETER_SENSOR]) != 10) { + send_instruction(data, CHANGE_DELAY, + ACCELEROMETER_SENSOR, uBuf, 4); + return SUCCESS; + } + } else { + send_instruction(data, ADD_SENSOR, + ACCELEROMETER_SENSOR, uBuf, 4); + } + + return FAIL; +} + +static void disable_accel_for_cal(struct ssp_data *data, int iDelayChanged) +{ + u8 uBuf[4] = { 0, }; + s32 dMsDelay = get_msdelay(data->adDelayBuf[ACCELEROMETER_SENSOR]); + memcpy(&uBuf[0], &dMsDelay, 4); + + if (atomic_read(&data->aSensorEnable) & (1 << ACCELEROMETER_SENSOR)) { + if (iDelayChanged) + send_instruction(data, CHANGE_DELAY, + ACCELEROMETER_SENSOR, uBuf, 4); + } else { + send_instruction(data, REMOVE_SENSOR, + ACCELEROMETER_SENSOR, uBuf, 4); + } +} + +static int accel_do_calibrate(struct ssp_data *data, int iEnable) +{ + int iSum[3] = { 0, }; + int iRet = 0, iCount; + struct file *cal_filp = NULL; + mm_segment_t old_fs; + + if (iEnable) { + data->accelcal.x = 0; + data->accelcal.y = 0; + data->accelcal.z = 0; + set_accel_cal(data); + iRet = enable_accel_for_cal(data); + msleep(300); + + for (iCount = 0; iCount < CALIBRATION_DATA_AMOUNT; iCount++) { + iSum[0] += data->buf[ACCELEROMETER_SENSOR].x; + iSum[1] += data->buf[ACCELEROMETER_SENSOR].y; + iSum[2] += data->buf[ACCELEROMETER_SENSOR].z; + mdelay(10); + } + disable_accel_for_cal(data, iRet); + + data->accelcal.x = (iSum[0] / CALIBRATION_DATA_AMOUNT); + data->accelcal.y = (iSum[1] / CALIBRATION_DATA_AMOUNT); + data->accelcal.z = (iSum[2] / CALIBRATION_DATA_AMOUNT); + + if (data->accelcal.z > 0) + data->accelcal.z -= MAX_ACCEL_1G; + else if (data->accelcal.z < 0) + data->accelcal.z += MAX_ACCEL_1G; + } else { + data->accelcal.x = 0; + data->accelcal.y = 0; + data->accelcal.z = 0; + } + + ssp_dbg("[SSP]: do accel calibrate %d, %d, %d\n", + data->accelcal.x, data->accelcal.y, data->accelcal.z); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + cal_filp = filp_open(CALIBRATION_FILE_PATH, + O_CREAT | O_TRUNC | O_WRONLY, 0666); + if (IS_ERR(cal_filp)) { + pr_err("[SSP]: %s - Can't open calibration file\n", __func__); + set_fs(old_fs); + iRet = PTR_ERR(cal_filp); + return iRet; + } + + iRet = cal_filp->f_op->write(cal_filp, (char *)&data->accelcal, + 3 * sizeof(int), &cal_filp->f_pos); + if (iRet != 3 * sizeof(int)) { + pr_err("[SSP]: %s - Can't write the accelcal to file\n", + __func__); + iRet = -EIO; + } + + filp_close(cal_filp, current->files); + set_fs(old_fs); + set_accel_cal(data); + return iRet; +} + +static ssize_t accel_calibration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int iRet; + int iCount = 0; + struct ssp_data *data = dev_get_drvdata(dev); + + iRet = accel_open_calibration(data); + if (iRet < 0) + pr_err("[SSP]: %s - calibration open failed(%d)\n", __func__, iRet); + + ssp_dbg("[SSP] Cal data : %d %d %d - %d\n", + data->accelcal.x, data->accelcal.y, data->accelcal.z, iRet); + + iCount = sprintf(buf, "%d %d %d %d\n", iRet, data->accelcal.x, + data->accelcal.y, data->accelcal.z); + return iCount; +} + +static ssize_t accel_calibration_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int iRet; + int64_t dEnable; + struct ssp_data *data = dev_get_drvdata(dev); + + iRet = kstrtoll(buf, 10, &dEnable); + if (iRet < 0) + return iRet; + + iRet = accel_do_calibrate(data, (int)dEnable); + if (iRet < 0) + pr_err("[SSP]: %s - accel_do_calibrate() failed\n", __func__); + + return size; +} + +static ssize_t raw_data_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", + data->buf[ACCELEROMETER_SENSOR].x, + data->buf[ACCELEROMETER_SENSOR].y, + data->buf[ACCELEROMETER_SENSOR].z); +} + +static ssize_t accel_reactive_alert_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int iRet = 0; + char chTempBuf = 1; + struct ssp_data *data = dev_get_drvdata(dev); + + struct ssp_msg *msg; + + if (sysfs_streq(buf, "1")) + ssp_dbg("[SSP]: %s - on\n", __func__); + else if (sysfs_streq(buf, "0")) + ssp_dbg("[SSP]: %s - off\n", __func__); + else if (sysfs_streq(buf, "2")) { + ssp_dbg("[SSP]: %s - factory\n", __func__); + + data->bAccelAlert = 0; + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + return -ENOMEM; + } + msg->cmd = ACCELEROMETER_FACTORY; + msg->length = 1; + msg->options = AP2HUB_READ; + msg->data = chTempBuf; + msg->buffer = &chTempBuf; + msg->free_buffer = 0; + + iRet = ssp_spi_sync(data, msg, 3000); + data->bAccelAlert = chTempBuf; + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - accel Selftest Timeout!!\n", __func__); + goto exit; + } + + ssp_dbg("[SSP]: %s factory test success!\n", __func__); + } else { + pr_err("[SSP]: %s - invalid value %d\n", __func__, *buf); + return -EINVAL; + } +exit: + return size; +} + +static ssize_t accel_reactive_alert_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + bool bSuccess = false; + struct ssp_data *data = dev_get_drvdata(dev); + + if (data->bAccelAlert == true) + bSuccess = true; + else + bSuccess = false; + + data->bAccelAlert = false; + return sprintf(buf, "%u\n", bSuccess); +} + +static ssize_t accel_hw_selftest_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char chTempBuf[8] = { 2, 0, }; + s8 init_status = 0, result = -1; + s16 shift_ratio[3] = { 0, }; + int iRet; + struct ssp_data *data = dev_get_drvdata(dev); + struct ssp_msg *msg; + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + goto exit; + } + msg->cmd = ACCELEROMETER_FACTORY; + msg->length = 8; + msg->options = AP2HUB_READ; + msg->data = chTempBuf[0]; + msg->buffer = chTempBuf; + msg->free_buffer = 0; + + iRet = ssp_spi_sync(data, msg, 3000); + if (iRet != SUCCESS) { + pr_err("[SSP] %s - accel hw selftest Timeout!!\n", __func__); + goto exit; + } + + init_status = chTempBuf[0]; + shift_ratio[0] = (s16)((chTempBuf[2] << 8) + chTempBuf[1]); + shift_ratio[1] = (s16)((chTempBuf[4] << 8) + chTempBuf[3]); + shift_ratio[2] = (s16)((chTempBuf[6] << 8) + chTempBuf[5]); + result = chTempBuf[7]; + + pr_info("[SSP] %s - %d, %d, %d, %d, %d\n", __func__, + init_status, result, shift_ratio[0], shift_ratio[1], shift_ratio[2]); + + return sprintf(buf, "%d,%d.%d,%d.%d,%d.%d\n", result, + shift_ratio[0] / 10, shift_ratio[0] % 10, + shift_ratio[1] / 10, shift_ratio[1] % 10, + shift_ratio[2] / 10, shift_ratio[2] % 10); +exit: + return sprintf(buf, "%d,%d,%d,%d\n", -5, 0, 0, 0); +} + +static ssize_t accel_lowpassfilter_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int iRet = 0, new_enable = 1; + struct ssp_data *data = dev_get_drvdata(dev); + struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory\n", __func__); + goto exit; + } + + if (sysfs_streq(buf, "1")) + new_enable = 1; + else if (sysfs_streq(buf, "0")) + new_enable = 0; + else + ssp_dbg("[SSP]: %s - invalid value!\n", __func__); + + msg->cmd = MSG2SSP_AP_SENSOR_LPF; + msg->length = 1; + msg->options = AP2HUB_WRITE; + msg->buffer = (char*) kzalloc(1, GFP_KERNEL); + if (msg->buffer == NULL) { + pr_err("[SSP] %s, failed to alloc memory\n", __func__); + kfree(msg); + goto exit; + } + + *msg->buffer = new_enable; + msg->free_buffer = 1; + + iRet = ssp_spi_async(data, msg); + if (iRet != SUCCESS) + pr_err("[SSP] %s - fail %d\n", __func__, iRet); + else + pr_info("[SSP] %s - %d\n", __func__, new_enable); + +exit: + return size; +} + +static DEVICE_ATTR(name, S_IRUSR | S_IRGRP, accel_name_show, NULL); +static DEVICE_ATTR(vendor, S_IRUSR | S_IRGRP, accel_vendor_show, NULL); +static DEVICE_ATTR(calibration, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + accel_calibration_show, accel_calibration_store); +static DEVICE_ATTR(raw_data, S_IRUSR | S_IRGRP, raw_data_read, NULL); +static DEVICE_ATTR(reactive_alert, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + accel_reactive_alert_show, accel_reactive_alert_store); +static DEVICE_ATTR(selftest, S_IRUSR | S_IRGRP, accel_hw_selftest_show, NULL); +static DEVICE_ATTR(lowpassfilter, S_IWUSR | S_IWGRP, + NULL, accel_lowpassfilter_store); + +static struct device_attribute *acc_attrs[] = { + &dev_attr_name, + &dev_attr_vendor, + &dev_attr_calibration, + &dev_attr_raw_data, + &dev_attr_reactive_alert, + &dev_attr_selftest, + &dev_attr_lowpassfilter, + NULL, +}; + +void initialize_accel_factorytest(struct ssp_data *data) +{ + sensors_register(data->acc_device, data, acc_attrs, + "accelerometer_sensor"); +} + +void remove_accel_factorytest(struct ssp_data *data) +{ + sensors_unregister(data->acc_device, acc_attrs); +} diff --git a/drivers/sensors/brcm/factory/barcode_emul_tmg3992.c b/drivers/sensors/brcm/factory/barcode_emul_tmg3992.c new file mode 100644 index 0000000..9a8597e --- /dev/null +++ b/drivers/sensors/brcm/factory/barcode_emul_tmg3992.c @@ -0,0 +1,310 @@ +#include "../ssp.h" +#include + +#define VENDOR "AMS" +#define CHIP_ID "TMG399x" + +u8 hop_count; +u8 is_beaming; + +static struct reg_index_table reg_id_table[15] = { + {0x81, 0}, {0x88, 1}, {0x8F, 2}, {0x96, 3}, {0x9D, 4}, + {0xA4, 5}, {0xAB, 6}, {0xB2, 7}, {0xB9, 8}, {0xC0, 9}, + {0xC7, 10}, {0xCE, 11}, {0xD5, 12}, {0xDC, 13}, {0xE3, 14} +}; + +#define BEAMING_ON 1 +#define BEAMING_OFF 0 +#define STOP_BEAMING 0 + +#define COUNT_TEST 48 /* 0 */ +#define REGISTER_TEST 49 /* 1 */ +#define DATA_TEST 50 /* 2 */ + +enum { + dataset = 0, + registerset, + countset, + start, +}; + +enum { + reg, + index, +}; + +void mobeam_write(struct ssp_data *data, int type, u8 *u_buf) +{ + int iRet = 0; + u8 command = -1; + u8 data_length = 0; + + struct ssp_msg *msg; + + if (!(data->uSensorState & (1 << PROXIMITY_SENSOR))) { + pr_info("[SSP]: %s - Skip this function!!!"\ + ", proximity sensor is not connected(0x%x)\n", + __func__, data->uSensorState); + return; + } + + pr_info("[SSP] %s start, command_type = %d\n", __func__, type); + switch (type) { + case dataset: + command = MSG2SSP_AP_MOBEAM_DATA_SET; + data_length = 128; + break; + case registerset: + command = MSG2SSP_AP_MOBEAM_REGISTER_SET; + data_length = 6; + break; + case countset: + command = MSG2SSP_AP_MOBEAM_COUNT_SET; + data_length = 1; + break; + case start: + command = MSG2SSP_AP_MOBEAM_START; + data_length = 1; + is_beaming = BEAMING_ON; + break; + default: + pr_info("[SSP] %s - unknown cmd type\n", __func__); + break; + } + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP]: %s - failed to allocate memory\n", __func__); + return; + } + msg->cmd = command; + msg->length = data_length; + msg->options = AP2HUB_WRITE; + msg->buffer = (char *) kzalloc(data_length, GFP_KERNEL); + if ((msg->buffer) == NULL) { + pr_err("[SSP]: %s - failed to allocate memory\n", __func__); + kfree(msg); + return; + } + msg->free_buffer = 1; + + memcpy(msg->buffer, u_buf, data_length); + + iRet = ssp_spi_async(data, msg); + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - MOBEAM CMD fail %d\n", __func__, iRet); + return; + } + + pr_info("[SSP] %s command = 0x%X\n", __func__, command); + return; +} + +void mobeam_stop_set(struct ssp_data *data) +{ + int iRet, iReties = 0; + struct ssp_msg *msg; + u8 buffer = 0; + +retries: + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP]: %s - failed to allocate memory\n", __func__); + return; + } + msg->cmd = MSG2SSP_AP_MOBEAM_STOP; + msg->length = 1; + msg->options = AP2HUB_READ; + msg->buffer = &buffer; + msg->free_buffer = 0; + + iRet = ssp_spi_sync(data, msg, 1000); + if (iRet != SUCCESS) { + pr_err("[SSP] %s fail %d\n", __func__, iRet); + + if (iReties++ < 2) { + pr_err("[SSP] %s fail, retry\n", __func__); + mdelay(5); + goto retries; + } + } else { + is_beaming = BEAMING_OFF; + pr_info("[SSP] %s - success(%u)\n", __func__, is_beaming); + } + return; +} + +static ssize_t mobeam_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s\n", VENDOR); +} + +static ssize_t mobeam_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID); +} + +static ssize_t barcode_emul_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct ssp_data *data = dev_get_drvdata(dev); + u8 send_buf[128] = { 0, }; + int i; + + memset(send_buf, 0xFF, 128); + if (buf[0] == 0xFF && buf[1] != STOP_BEAMING) { + pr_info("[SSP] %s - START BEAMING(0x%X, 0x%X)\n", __func__, + buf[0], buf[1]); + send_buf[1] = buf[1]; + mobeam_write(data, start, &send_buf[1]); + } else if (buf[0] == 0xFF && buf[1] == STOP_BEAMING) { + pr_info("[SSP] %s - STOP BEAMING(0x%X, 0x%X)\n", __func__, + buf[0], buf[1]); + if (is_beaming == BEAMING_ON) + mobeam_stop_set(data); + else + pr_info("[SSP] %s - skip stop command\n", __func__); + } else if (buf[0] == 0x00) { + pr_info("[SSP] %s - DATA SET(0x%X, 0x%X)\n", __func__, + buf[0], buf[1]); + memcpy(send_buf, &buf[2], 128); + pr_info("[SSP] %s - %u %u %u %u %u %u\n", __func__, + send_buf[0], send_buf[1], send_buf[2], + send_buf[3], send_buf[4], send_buf[5]); + mobeam_write(data, dataset, send_buf); + } else if (buf[0] == 0x80) { + pr_info("[SSP] %s - HOP COUNT SET(0x%X, 0x%X)\n", __func__, + buf[0], buf[1]); + hop_count = buf[1]; + mobeam_write(data, countset, &hop_count); + } else { + pr_info("[SSP] %s - REGISTER SET(0x%X)\n", __func__, buf[0]); + for (i = 0; i < 15; i++) { + if (reg_id_table[i].reg == buf[0]) + send_buf[0] = reg_id_table[i].index; + } + send_buf[1] = buf[1]; + send_buf[2] = buf[2]; + send_buf[3] = buf[4]; + send_buf[4] = buf[5]; + send_buf[5] = buf[7]; + mobeam_write(data, registerset, send_buf); + } + return size; +} + +static ssize_t barcode_emul_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return strlen(buf); +} + +static ssize_t barcode_led_status_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%u\n", is_beaming); +} + +static ssize_t barcode_ver_check_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%u\n", 15); +} + +static ssize_t barcode_emul_test_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct ssp_data *data = dev_get_drvdata(dev); + u8 barcode_data[14] = {0xFF, 0xAC, 0xDB, 0x36, 0x42, 0x85, + 0x0A, 0xA8, 0xD1, 0xA3, 0x46, 0xC5, 0xDA, 0xFF}; + u8 test_data[128] = { 0, }; + + memset(test_data, 0xFF, 128); + if (buf[0] == COUNT_TEST) { + test_data[0] = 0x80; + test_data[1] = 1; + pr_info("[SSP] %s, COUNT_TEST - 0x%X, %u\n", __func__, + test_data[0], test_data[1]); + mobeam_write(data, countset, &test_data[1]); + } else if (buf[0] == REGISTER_TEST) { + test_data[0] = 0; + test_data[1] = 10; + test_data[2] = 20; + test_data[3] = 30; + test_data[4] = 40; + test_data[5] = 50; + pr_info("[SSP] %s, REGISTER_TEST - %u: %u %u %u %u %u\n", + __func__, test_data[0], test_data[1], test_data[2], + test_data[3], test_data[4], test_data[5]); + mobeam_write(data, registerset, test_data); + } else if (buf[0] == DATA_TEST) { + memcpy(test_data, &barcode_data[1], 13); + pr_info("[SSP] %s, DATA_TEST - 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n", + __func__, test_data[0], test_data[1], test_data[2], + test_data[3], test_data[4], test_data[5]); + mobeam_write(data, dataset, test_data); + } + return size; +} +static DEVICE_ATTR(vendor, S_IRUSR | S_IRGRP, mobeam_vendor_show, NULL); +static DEVICE_ATTR(name, S_IRUSR | S_IRGRP, mobeam_name_show, NULL); +static DEVICE_ATTR(barcode_send, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + barcode_emul_show, barcode_emul_store); +static DEVICE_ATTR(barcode_led_status, S_IRUSR | S_IRGRP, barcode_led_status_show, NULL); +static DEVICE_ATTR(barcode_ver_check, S_IRUSR | S_IRGRP, barcode_ver_check_show, NULL); +static DEVICE_ATTR(barcode_test_send, S_IWUSR | S_IWGRP, + NULL, barcode_emul_test_store); + +void initialize_mobeam(struct ssp_data *data) +{ + pr_info("[SSP] %s\n", __func__); + data->mobeam_device = sec_device_create(data, "sec_barcode_emul"); + + if (IS_ERR(data->mobeam_device)) + pr_err("[SSP] Failed to create mobeam_dev device\n"); + + if (device_create_file(data->mobeam_device, &dev_attr_vendor) < 0) + pr_err("[SSP] Failed to create device file(%s)!\n", + dev_attr_vendor.attr.name); + + if (device_create_file(data->mobeam_device, &dev_attr_name) < 0) + pr_err("[SSP] Failed to create device file(%s)!\n", + dev_attr_name.attr.name); + + if (device_create_file(data->mobeam_device, &dev_attr_barcode_send) < 0) + pr_err("[SSP] Failed to create device file(%s)!\n", + dev_attr_barcode_send.attr.name); + + if (device_create_file(data->mobeam_device, &dev_attr_barcode_led_status) < 0) + pr_err("[SSP] Failed to create device file(%s)!\n", + dev_attr_barcode_led_status.attr.name); + + if (device_create_file(data->mobeam_device, &dev_attr_barcode_ver_check) < 0) + pr_err("[SSP] Failed to create device file(%s)!\n", + dev_attr_barcode_ver_check.attr.name); + + if (device_create_file(data->mobeam_device, &dev_attr_barcode_test_send) < 0) + pr_err("[SSP] Failed to create device file(%s)!\n", + dev_attr_barcode_test_send.attr.name); + is_beaming = BEAMING_OFF; +} + +void remove_mobeam(struct ssp_data *data) +{ + pr_info("[SSP] %s\n", __func__); + + device_remove_file(data->mobeam_device, &dev_attr_barcode_test_send); + device_remove_file(data->mobeam_device, &dev_attr_barcode_ver_check); + device_remove_file(data->mobeam_device, &dev_attr_barcode_led_status); + device_remove_file(data->mobeam_device, &dev_attr_barcode_send); + device_remove_file(data->mobeam_device, &dev_attr_name); + device_remove_file(data->mobeam_device, &dev_attr_vendor); +} diff --git a/drivers/sensors/brcm/factory/gesture_tmg399x.c b/drivers/sensors/brcm/factory/gesture_tmg399x.c new file mode 100644 index 0000000..e0f1d82 --- /dev/null +++ b/drivers/sensors/brcm/factory/gesture_tmg399x.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ +#include "../ssp.h" + +#define VENDOR "AMS" +#define CHIP_ID "TMG3992" + + +static ssize_t gestrue_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR); +} + +static ssize_t gestrue_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID); +} + +static ssize_t raw_data_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d\n", + data->buf[GESTURE_SENSOR].data[3], + data->buf[GESTURE_SENSOR].data[4], + data->buf[GESTURE_SENSOR].data[5], + data->buf[GESTURE_SENSOR].data[6]); +} + +static ssize_t gesture_get_selftest_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + s16 raw_A = 0, raw_B = 0, raw_C = 0, raw_D = 0; + int iRet = 0; + char chTempBuf[8] = { 0, }; + struct ssp_data *data = dev_get_drvdata(dev); + + struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + msg->cmd = GESTURE_FACTORY; + msg->length = 4; + msg->options = AP2HUB_READ; + msg->buffer = chTempBuf; + msg->free_buffer = 0; + + iRet = ssp_spi_sync(data, msg, 2000); + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - Gesture Selftest Timeout!!\n", __func__); + goto exit; + } + + ssp_dbg("[SSP]: %s %d %d %d %d\n", + __func__, chTempBuf[0], chTempBuf[1], chTempBuf[2], chTempBuf[3]); + + raw_A = chTempBuf[0]; + raw_B = chTempBuf[1]; + raw_C = chTempBuf[2]; + raw_D = chTempBuf[3]; + + pr_info("[SSP] %s: self test A = %d, B = %d, C = %d, D = %d\n", + __func__, raw_A, raw_B, raw_C, raw_D); + +exit: + return sprintf(buf, "%d,%d,%d,%d\n", + raw_A, raw_B, raw_C, raw_D); +} + +static ssize_t ir_current_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + if(data->uIr_Current == 0) + data->uIr_Current = DEFUALT_IR_CURRENT; + + ssp_dbg("[SSP]: %s - Ir_Current Setting = %d\n", + __func__, data->uIr_Current); + + return sprintf(buf, "%d\n", data->uIr_Current); +} + +static ssize_t ir_current_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + u16 uNewIrCurrent = DEFUALT_IR_CURRENT; + int iRet = 0; + u16 current_index = 0; + struct ssp_data *data = dev_get_drvdata(dev); + static u16 set_current[2][4] = { {12, 25, 50, 100}, + {24, 16, 8, 0} }; + + iRet = kstrtou16(buf, 10, &uNewIrCurrent); + + if (iRet < 0) + pr_err("[SSP]: %s - kstrtoint failed.(%d)\n", __func__, iRet); + else { + for(current_index = 0; current_index < 4; current_index++) { + if (set_current[0][current_index] == uNewIrCurrent) { + data->uIr_Current = set_current[1][current_index]; + break; + } + } + if(current_index == 4) // current setting value wrong. + { + return ERROR; + } + set_gesture_current(data, data->uIr_Current); + data->uIr_Current= uNewIrCurrent; + } + + ssp_dbg("[SSP]: %s - new Ir_Current Setting : %d\n", + __func__, data->uIr_Current); + + return size; +} + +static DEVICE_ATTR(vendor, S_IRUSR | S_IRGRP, gestrue_vendor_show, NULL); +static DEVICE_ATTR(name, S_IRUSR | S_IRGRP, gestrue_name_show, NULL); +static DEVICE_ATTR(raw_data, S_IRUSR | S_IRGRP, raw_data_read, NULL); +static DEVICE_ATTR(selftest, S_IRUSR | S_IRGRP, gesture_get_selftest_show, NULL); +static DEVICE_ATTR(ir_current, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + ir_current_show, ir_current_store); + +static struct device_attribute *gesture_attrs[] = { + &dev_attr_vendor, + &dev_attr_name, + &dev_attr_raw_data, + &dev_attr_selftest, + &dev_attr_ir_current, + NULL, +}; + +void initialize_gesture_factorytest(struct ssp_data *data) +{ + sensors_register(data->ges_device, data, + gesture_attrs, "gesture_sensor"); +} + +void remove_gesture_factorytest(struct ssp_data *data) +{ + sensors_unregister(data->ges_device, gesture_attrs); +} diff --git a/drivers/sensors/brcm/factory/gyro_icm20610.c b/drivers/sensors/brcm/factory/gyro_icm20610.c new file mode 100644 index 0000000..7d5eb1d --- /dev/null +++ b/drivers/sensors/brcm/factory/gyro_icm20610.c @@ -0,0 +1,615 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ +#include +#include "../ssp.h" + +/*************************************************************************/ +/* factory Sysfs */ +/*************************************************************************/ + +#define VENDOR "INVENSENSE" +#define CHIP_ID "ICM20610" + +#define CALIBRATION_FILE_PATH "/efs/gyro_cal_data" +#define VERBOSE_OUT 1 +#define CALIBRATION_DATA_AMOUNT 20 +#define DEF_GYRO_FULLSCALE 2000 +#define DEF_GYRO_SENS (32768 / DEF_GYRO_FULLSCALE) +#define DEF_BIAS_LSB_THRESH_SELF (40 * DEF_GYRO_SENS) +#define DEF_RMS_LSB_TH_SELF (5 * DEF_GYRO_SENS) +#define DEF_RMS_THRESH ((DEF_RMS_LSB_TH_SELF) * (DEF_RMS_LSB_TH_SELF)) +#define DEF_SCALE_FOR_FLOAT (1000) +#define DEF_RMS_SCALE_FOR_RMS (10000) +#define DEF_SQRT_SCALE_FOR_RMS (100) + +static ssize_t gyro_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", VENDOR); +} + +static ssize_t gyro_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", CHIP_ID); +} + +int gyro_open_calibration(struct ssp_data *data) +{ + int iRet = 0; + mm_segment_t old_fs; + struct file *cal_filp = NULL; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + cal_filp = filp_open(CALIBRATION_FILE_PATH, O_RDONLY, 0666); + if (IS_ERR(cal_filp)) { + set_fs(old_fs); + iRet = PTR_ERR(cal_filp); + + data->gyrocal.x = 0; + data->gyrocal.y = 0; + data->gyrocal.z = 0; + + return iRet; + } + + iRet = cal_filp->f_op->read(cal_filp, (char *)&data->gyrocal, + 3 * sizeof(int), &cal_filp->f_pos); + if (iRet != 3 * sizeof(int)) + iRet = -EIO; + + filp_close(cal_filp, current->files); + set_fs(old_fs); + + ssp_dbg("[SSP]: open gyro calibration %d, %d, %d\n", + data->gyrocal.x, data->gyrocal.y, data->gyrocal.z); + return iRet; +} + +static int save_gyro_caldata(struct ssp_data *data, s16 *iCalData) +{ + int iRet = 0; + struct file *cal_filp = NULL; + mm_segment_t old_fs; + + data->gyrocal.x = iCalData[0]; + data->gyrocal.y = iCalData[1]; + data->gyrocal.z = iCalData[2]; + + ssp_dbg("[SSP]: do gyro calibrate %d, %d, %d\n", + data->gyrocal.x, data->gyrocal.y, data->gyrocal.z); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + cal_filp = filp_open(CALIBRATION_FILE_PATH, + O_CREAT | O_TRUNC | O_WRONLY, 0666); + if (IS_ERR(cal_filp)) { + pr_err("[SSP]: %s - Can't open calibration file\n", __func__); + set_fs(old_fs); + iRet = PTR_ERR(cal_filp); + return -EIO; + } + + iRet = cal_filp->f_op->write(cal_filp, (char *)&data->gyrocal, + 3 * sizeof(int), &cal_filp->f_pos); + if (iRet != 3 * sizeof(int)) { + pr_err("[SSP]: %s - Can't write gyro cal to file\n", __func__); + iRet = -EIO; + } + + filp_close(cal_filp, current->files); + set_fs(old_fs); + + return iRet; +} + +int set_gyro_cal(struct ssp_data *data) +{ + int iRet = 0; + struct ssp_msg *msg; + s16 gyro_cal[3]; + if (!(data->uSensorState & (1 << GYROSCOPE_SENSOR))) { + pr_info("[SSP]: %s - Skip this function!!!"\ + ", gyro sensor is not connected(0x%x)\n", + __func__, data->uSensorState); + return iRet; + } + + gyro_cal[0] = data->gyrocal.x; + gyro_cal[1] = data->gyrocal.y; + gyro_cal[2] = data->gyrocal.z; + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + return -ENOMEM; + } + msg->cmd = MSG2SSP_AP_MCU_SET_GYRO_CAL; + msg->length = 6; + msg->options = AP2HUB_WRITE; + msg->buffer = (char*) kzalloc(6, GFP_KERNEL); + + msg->free_buffer = 1; + memcpy(msg->buffer, gyro_cal, 6); + + iRet = ssp_spi_async(data, msg); + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - i2c fail %d\n", __func__, iRet); + iRet = ERROR; + } + + pr_info("[SSP] Set gyro cal data %d, %d, %d\n", gyro_cal[0], gyro_cal[1], gyro_cal[2]); + return iRet; +} + +static ssize_t gyro_power_off(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssp_dbg("[SSP]: %s\n", __func__); + + return sprintf(buf, "%d\n", 1); +} + +static ssize_t gyro_power_on(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssp_dbg("[SSP]: %s\n", __func__); + + return sprintf(buf, "%d\n", 1); +} + +short ICM20610_gyro_get_temp(struct ssp_data *data) +{ + char chTempBuf[2] = { 0}; + unsigned char reg[2]; + short temperature = 0; + int iRet = 0; + + struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + goto exit; + } + msg->cmd = GYROSCOPE_TEMP_FACTORY; + msg->length = 2; + msg->options = AP2HUB_READ; + msg->buffer = chTempBuf; + msg->free_buffer = 0; + + iRet = ssp_spi_sync(data, msg, 3000); + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - Gyro Temp Timeout!!\n", __func__); + goto exit; + } + + reg[0] = chTempBuf[1]; + reg[1] = chTempBuf[0]; + temperature = (short) (((reg[0]) << 8) | reg[1]); + ssp_dbg("[SSP]: %s - %d\n", __func__, temperature); + + exit: + return temperature; +} + + +static ssize_t gyro_get_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + short temperature = 0; + struct ssp_data *data = dev_get_drvdata(dev); + + temperature = ICM20610_gyro_get_temp(data); + return sprintf(buf, "%d\n", temperature); +} + +static u32 icm20610_selftest_sqrt(u32 sqsum) +{ + u32 sq_rt; + u32 g0, g1, g2, g3, g4; + u32 seed; + u32 next; + u32 step; + + g4 = sqsum / 100000000; + g3 = (sqsum - g4 * 100000000) / 1000000; + g2 = (sqsum - g4 * 100000000 - g3 * 1000000) / 10000; + g1 = (sqsum - g4 * 100000000 - g3 * 1000000 - g2 * 10000) / 100; + g0 = (sqsum - g4 * 100000000 - g3 * 1000000 - g2 * 10000 - g1 * 100); + + next = g4; + step = 0; + seed = 0; + while (((seed + 1) * (step + 1)) <= next) { + step++; + seed++; + } + + sq_rt = seed * 10000; + next = (next - (seed * step)) * 100 + g3; + + step = 0; + seed = 2 * seed * 10; + while (((seed + 1) * (step + 1)) <= next) { + step++; + seed++; + } + + sq_rt = sq_rt + step * 1000; + next = (next - seed * step) * 100 + g2; + seed = (seed + step) * 10; + step = 0; + while (((seed + 1) * (step + 1)) <= next) { + step++; + seed++; + } + + sq_rt = sq_rt + step * 100; + next = (next - seed * step) * 100 + g1; + seed = (seed + step) * 10; + step = 0; + + while (((seed + 1) * (step + 1)) <= next) { + step++; + seed++; + } + + sq_rt = sq_rt + step * 10; + next = (next - seed * step) * 100 + g0; + seed = (seed + step) * 10; + step = 0; + + while (((seed + 1) * (step + 1)) <= next) { + step++; + seed++; + } + + sq_rt = sq_rt + step; + + return sq_rt; +} + +static ssize_t gyro_selftest_store(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char chTempBuf[36] = { 0,}; + u8 initialized = 0; + s8 hw_result = 0; + int i = 0, j = 0, total_count = 0, ret_val = 0; + long avg[3] = {0,}, rms[3] = {0,}; + int gyro_bias[3] = {0,}, gyro_rms[3] = {0,}; + s16 shift_ratio[3] = {0,}; + s16 iCalData[3] = {0,}; + char a_name[3][2] = { "X", "Y", "Z" }; + int iRet = 0; + int dps_rms[3] = { 0, }; + u32 temp = 0; + int bias_thresh = DEF_BIAS_LSB_THRESH_SELF; + struct ssp_data *data = dev_get_drvdata(dev); + + struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + goto exit; + } + msg->cmd = GYROSCOPE_FACTORY; + msg->length = 36; + msg->options = AP2HUB_READ; + msg->buffer = chTempBuf; + msg->free_buffer = 0; + + iRet = ssp_spi_sync(data, msg, 7000); + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - Gyro Selftest Timeout!!\n", __func__); + ret_val = 1; + goto exit; + } + + data->uTimeOutCnt = 0; + + pr_err("[SSP]%d %d %d %d %d %d %d %d %d %d %d %d\n", chTempBuf[0], + chTempBuf[1], chTempBuf[2], chTempBuf[3], chTempBuf[4], + chTempBuf[5], chTempBuf[6], chTempBuf[7], chTempBuf[8], + chTempBuf[9], chTempBuf[10], chTempBuf[11]); + + initialized = chTempBuf[0]; + shift_ratio[0] = (s16)((chTempBuf[2] << 8) + + chTempBuf[1]); + shift_ratio[1] = (s16)((chTempBuf[4] << 8) + + chTempBuf[3]); + shift_ratio[2] = (s16)((chTempBuf[6] << 8) + + chTempBuf[5]); + hw_result = (s8)chTempBuf[7]; + total_count = (int)((chTempBuf[11] << 24) + + (chTempBuf[10] << 16) + + (chTempBuf[9] << 8) + + chTempBuf[8]); + avg[0] = (long)((chTempBuf[15] << 24) + + (chTempBuf[14] << 16) + + (chTempBuf[13] << 8) + + chTempBuf[12]); + avg[1] = (long)((chTempBuf[19] << 24) + + (chTempBuf[18] << 16) + + (chTempBuf[17] << 8) + + chTempBuf[16]); + avg[2] = (long)((chTempBuf[23] << 24) + + (chTempBuf[22] << 16) + + (chTempBuf[21] << 8) + + chTempBuf[20]); + rms[0] = (long)((chTempBuf[27] << 24) + + (chTempBuf[26] << 16) + + (chTempBuf[25] << 8) + + chTempBuf[24]); + rms[1] = (long)((chTempBuf[31] << 24) + + (chTempBuf[30] << 16) + + (chTempBuf[29] << 8) + + chTempBuf[28]); + rms[2] = (long)((chTempBuf[35] << 24) + + (chTempBuf[34] << 16) + + (chTempBuf[33] << 8) + + chTempBuf[32]); + pr_info("[SSP] init: %d, total cnt: %d\n", initialized, total_count); + pr_info("[SSP] hw_result: %d, %d, %d, %d\n", hw_result, + shift_ratio[0], shift_ratio[1], shift_ratio[2]); + pr_info("[SSP] avg %+8ld %+8ld %+8ld (LSB)\n", avg[0], avg[1], avg[2]); + pr_info("[SSP] rms %+8ld %+8ld %+8ld (LSB)\n", rms[0], rms[1], rms[2]); + + if (total_count == 0) { + pr_err("[SSP] %s, total_count is 0. goto exit\n", __func__); + ret_val = 2; + goto exit; + } + + if (hw_result < 0) { + pr_err("[SSP] %s - hw selftest fail(%d), sw selftest skip\n", + __func__, hw_result); + return sprintf(buf, "-1,0,0,0,0,0,0,%d.%d,%d.%d,%d.%d,0,0,0\n", + shift_ratio[0] / 10, shift_ratio[0] % 10, + shift_ratio[1] / 10, shift_ratio[1] % 10, + shift_ratio[2] / 10, shift_ratio[2] % 10); + } + gyro_bias[0] = (avg[0] * DEF_SCALE_FOR_FLOAT) / DEF_GYRO_SENS; + gyro_bias[1] = (avg[1] * DEF_SCALE_FOR_FLOAT) / DEF_GYRO_SENS; + gyro_bias[2] = (avg[2] * DEF_SCALE_FOR_FLOAT) / DEF_GYRO_SENS; + iCalData[0] = (s16)avg[0]; + iCalData[1] = (s16)avg[1]; + iCalData[2] = (s16)avg[2]; + + if (VERBOSE_OUT) { + pr_info("[SSP] abs bias : %+8d.%03d %+8d.%03d %+8d.%03d (dps)\n", + (int)abs(gyro_bias[0]) / DEF_SCALE_FOR_FLOAT, + (int)abs(gyro_bias[0]) % DEF_SCALE_FOR_FLOAT, + (int)abs(gyro_bias[1]) / DEF_SCALE_FOR_FLOAT, + (int)abs(gyro_bias[1]) % DEF_SCALE_FOR_FLOAT, + (int)abs(gyro_bias[2]) / DEF_SCALE_FOR_FLOAT, + (int)abs(gyro_bias[2]) % DEF_SCALE_FOR_FLOAT); + } + + for (j = 0; j < 3; j++) { + if (unlikely(abs(avg[j]) > bias_thresh)) { + pr_err("[SSP] %s-Gyro bias (%ld) exceeded threshold " + "(threshold = %d LSB)\n", a_name[j], + avg[j], bias_thresh); + ret_val |= 1 << (3 + j); + } + } + /* 3rd, check RMS for dead gyros + If any of the RMS noise value returns zero, + then we might have dead gyro or FIFO/register failure, + the part is sleeping, or the part is not responsive */ + if (rms[0] == 0 || rms[1] == 0 || rms[2] == 0) + ret_val |= 1 << 6; + + if (VERBOSE_OUT) { + pr_info("[SSP] RMS ^ 2 : %+8ld %+8ld %+8ld\n", + (long)rms[0] / total_count, + (long)rms[1] / total_count, (long)rms[2] / total_count); + } + + for (j = 0; j < 3; j++) { + if (unlikely(rms[j] / total_count > DEF_RMS_THRESH)) { + pr_err("[SSP] %s-Gyro rms (%ld) exceeded threshold " + "(threshold = %d LSB)\n", a_name[j], + rms[j] / total_count, DEF_RMS_THRESH); + ret_val |= 1 << (7 + j); + } + } + + for (i = 0; i < 3; i++) { + if (rms[i] > 10000) { + temp = + ((u32) (rms[i] / total_count)) * + DEF_RMS_SCALE_FOR_RMS; + } else { + temp = + ((u32) (rms[i] * DEF_RMS_SCALE_FOR_RMS)) / + total_count; + } + if (rms[i] < 0) + temp = 1 << 31; + + dps_rms[i] = icm20610_selftest_sqrt(temp) / DEF_GYRO_SENS; + + gyro_rms[i] = + dps_rms[i] * DEF_SCALE_FOR_FLOAT / DEF_SQRT_SCALE_FOR_RMS; + } + + pr_info("[SSP] RMS : %+8d.%03d %+8d.%03d %+8d.%03d (dps)\n", + (int)abs(gyro_rms[0]) / DEF_SCALE_FOR_FLOAT, + (int)abs(gyro_rms[0]) % DEF_SCALE_FOR_FLOAT, + (int)abs(gyro_rms[1]) / DEF_SCALE_FOR_FLOAT, + (int)abs(gyro_rms[1]) % DEF_SCALE_FOR_FLOAT, + (int)abs(gyro_rms[2]) / DEF_SCALE_FOR_FLOAT, + (int)abs(gyro_rms[2]) % DEF_SCALE_FOR_FLOAT); + + if (likely(!ret_val)) { + save_gyro_caldata(data, iCalData); + } else { + pr_err("[SSP] ret_val != 0, gyrocal is 0 at all axis\n"); + data->gyrocal.x = 0; + data->gyrocal.y = 0; + data->gyrocal.z = 0; + } + +exit: + ssp_dbg("[SSP]: %s - %d," + "%d.%03d,%d.%03d,%d.%03d," + "%d.%03d,%d.%03d,%d.%03d," + "%d.%d,%d.%d,%d.%d," + "%d,%d,%d\n", + __func__, ret_val, + gyro_bias[0]/1000, + (int)abs(gyro_bias[0])%1000, + gyro_bias[1]/1000, + (int)abs(gyro_bias[1])%1000, + gyro_bias[2]/1000, + (int)abs(gyro_bias[2])%1000, + gyro_rms[0]/1000, + (int)abs(gyro_rms[0])%1000, + gyro_rms[1]/1000, + (int)abs(gyro_rms[1])%1000, + gyro_rms[2]/1000, + (int)abs(gyro_rms[2])%1000, + shift_ratio[0] / 10, shift_ratio[0] % 10, + shift_ratio[1] / 10, shift_ratio[1] % 10, + shift_ratio[2] / 10, shift_ratio[2] % 10, + (int)(total_count/3), + (int)(total_count/3), + (int)(total_count/3)); + + return sprintf(buf, "%d," + "%d.%03d,%d.%03d,%d.%03d," + "%d.%03d,%d.%03d,%d.%03d," + "%d.%d,%d.%d,%d.%d," + "%d,%d,%d\n", + ret_val, + gyro_bias[0]/1000, + (int)abs(gyro_bias[0])%1000, + gyro_bias[1]/1000, + (int)abs(gyro_bias[1])%1000, + gyro_bias[2]/1000, + (int)abs(gyro_bias[2])%1000, + gyro_rms[0]/1000, + (int)abs(gyro_rms[0])%1000, + gyro_rms[1]/1000, + (int)abs(gyro_rms[1])%1000, + gyro_rms[2]/1000, + (int)abs(gyro_rms[2])%1000, + shift_ratio[0] / 10, shift_ratio[0] % 10, + shift_ratio[1] / 10, shift_ratio[1] % 10, + shift_ratio[2] / 10, shift_ratio[2] % 10, + (int)(total_count/3), + (int)(total_count/3), + (int)(total_count/3)); +} + +static ssize_t gyro_selftest_dps_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int iNewDps = 0; + int iRet = 0; + char chTempBuf = 0; + + struct ssp_data *data = dev_get_drvdata(dev); + + struct ssp_msg *msg; + + if (!(data->uSensorState & (1 << GYROSCOPE_SENSOR))) + goto exit; + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + goto exit; + } + msg->cmd = GYROSCOPE_DPS_FACTORY; + msg->length = 1; + msg->options = AP2HUB_READ; + msg->buffer = &chTempBuf; + msg->free_buffer = 0; + + sscanf(buf, "%d", &iNewDps); + + if (iNewDps == GYROSCOPE_DPS250) + msg->options |= 0 << SSP_GYRO_DPS; + else if (iNewDps == GYROSCOPE_DPS500) + msg->options |= 1 << SSP_GYRO_DPS; + else if (iNewDps == GYROSCOPE_DPS2000) + msg->options |= 2 << SSP_GYRO_DPS; + else { + msg->options |= 1 << SSP_GYRO_DPS; + iNewDps = GYROSCOPE_DPS500; + } + + iRet = ssp_spi_sync(data, msg, 3000); + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - Gyro Selftest DPS Timeout!!\n", __func__); + goto exit; + } + + if (chTempBuf != SUCCESS) { + pr_err("[SSP]: %s - Gyro Selftest DPS Error!!\n", __func__); + goto exit; + } + + data->uGyroDps = (unsigned int)iNewDps; + pr_err("[SSP]: %s - %u dps stored\n", __func__, data->uGyroDps); +exit: + return count; +} + +static ssize_t gyro_selftest_dps_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", data->uGyroDps); +} + +static DEVICE_ATTR(name, S_IRUSR | S_IRGRP, gyro_name_show, NULL); +static DEVICE_ATTR(vendor, S_IRUSR | S_IRGRP, gyro_vendor_show, NULL); +static DEVICE_ATTR(power_off, S_IRUSR | S_IRGRP, gyro_power_off, NULL); +static DEVICE_ATTR(power_on, S_IRUSR | S_IRGRP, gyro_power_on, NULL); +static DEVICE_ATTR(temperature, S_IRUSR | S_IRGRP, gyro_get_temp, NULL); +static DEVICE_ATTR(selftest, S_IRUSR | S_IRGRP, gyro_selftest_store, NULL); +static DEVICE_ATTR(selftest_dps, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + gyro_selftest_dps_show, gyro_selftest_dps_store); + +static struct device_attribute *gyro_attrs[] = { + &dev_attr_name, + &dev_attr_vendor, + &dev_attr_selftest, + &dev_attr_power_on, + &dev_attr_power_off, + &dev_attr_temperature, + &dev_attr_selftest_dps, + NULL, +}; + +void initialize_gyro_factorytest(struct ssp_data *data) +{ + sensors_register(data->gyro_device, data, gyro_attrs, "gyro_sensor"); +} + +void remove_gyro_factorytest(struct ssp_data *data) +{ + sensors_unregister(data->gyro_device, gyro_attrs); +} diff --git a/drivers/sensors/brcm/factory/light_tmg399x.c b/drivers/sensors/brcm/factory/light_tmg399x.c new file mode 100644 index 0000000..37f257a --- /dev/null +++ b/drivers/sensors/brcm/factory/light_tmg399x.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ +#include "../ssp.h" + +#define VENDOR "AMS" +#define CHIP_ID "TMG399X" + +/*************************************************************************/ +/* factory Sysfs */ +/*************************************************************************/ +static ssize_t light_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", VENDOR); +} + +static ssize_t light_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", CHIP_ID); +} + +static ssize_t light_lux_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%u,%u,%u,%u,%u,%u\n", + data->buf[LIGHT_SENSOR].r, data->buf[LIGHT_SENSOR].g, + data->buf[LIGHT_SENSOR].b, data->buf[LIGHT_SENSOR].w, + data->buf[LIGHT_SENSOR].a_time, data->buf[LIGHT_SENSOR].a_gain); +} + +static ssize_t light_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%u,%u,%u,%u,%u,%u\n", + data->buf[LIGHT_SENSOR].r, data->buf[LIGHT_SENSOR].g, + data->buf[LIGHT_SENSOR].b, data->buf[LIGHT_SENSOR].w, + data->buf[LIGHT_SENSOR].a_time, data->buf[LIGHT_SENSOR].a_gain); +} + +static DEVICE_ATTR(vendor, S_IRUSR | S_IRGRP, light_vendor_show, NULL); +static DEVICE_ATTR(name, S_IRUSR | S_IRGRP, light_name_show, NULL); +static DEVICE_ATTR(lux, S_IRUSR | S_IRGRP, light_lux_show, NULL); +static DEVICE_ATTR(raw_data, S_IRUSR | S_IRGRP, light_data_show, NULL); + +static struct device_attribute *light_attrs[] = { + &dev_attr_vendor, + &dev_attr_name, + &dev_attr_lux, + &dev_attr_raw_data, + NULL, +}; + +void initialize_light_factorytest(struct ssp_data *data) +{ + sensors_register(data->light_device, data, light_attrs, "light_sensor"); +} + +void remove_light_factorytest(struct ssp_data *data) +{ + sensors_unregister(data->light_device, light_attrs); +} diff --git a/drivers/sensors/brcm/factory/magnetic_yas532.c b/drivers/sensors/brcm/factory/magnetic_yas532.c new file mode 100644 index 0000000..c6c5ddf --- /dev/null +++ b/drivers/sensors/brcm/factory/magnetic_yas532.c @@ -0,0 +1,522 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ +#include "../ssp.h" +#include "magnetic_yas532.h" + +/*************************************************************************/ +/* factory Sysfs */ +/*************************************************************************/ + +#define VENDOR "YAMAHA" +#define CHIP_ID "YAS532" +#define MAG_HW_OFFSET_FILE_PATH "/efs/hw_offset" + +int mag_open_hwoffset(struct ssp_data *data) +{ + int iRet = 0; + mm_segment_t old_fs; + struct file *cal_filp = NULL; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + cal_filp = filp_open(MAG_HW_OFFSET_FILE_PATH, O_RDONLY, 0666); + if (IS_ERR(cal_filp)) { + pr_err("[SSP] %s: filp_open failed\n", __func__); + set_fs(old_fs); + iRet = PTR_ERR(cal_filp); + + data->magoffset.x = 0; + data->magoffset.y = 0; + data->magoffset.z = 0; + + return iRet; + } + + iRet = cal_filp->f_op->read(cal_filp, (char *)&data->magoffset, + 3 * sizeof(char), &cal_filp->f_pos); + if (iRet != 3 * sizeof(char)) { + pr_err("[SSP] %s: filp_open failed\n", __func__); + iRet = -EIO; + } + + filp_close(cal_filp, current->files); + set_fs(old_fs); + + ssp_dbg("[SSP]: %s: %d, %d, %d\n", __func__, + (s8)data->magoffset.x, + (s8)data->magoffset.y, + (s8)data->magoffset.z); + + if ((data->magoffset.x == 0) && (data->magoffset.y == 0) + && (data->magoffset.z == 0)) + return ERROR; + + return iRet; +} + +int mag_store_hwoffset(struct ssp_data *data) +{ + int iRet = 0; + struct file *cal_filp = NULL; + mm_segment_t old_fs; + + if (get_hw_offset(data) < 0) { + pr_err("[SSP]: %s - get_hw_offset failed\n", __func__); + return ERROR; + } else { + old_fs = get_fs(); + set_fs(KERNEL_DS); + + cal_filp = filp_open(MAG_HW_OFFSET_FILE_PATH, + O_CREAT | O_TRUNC | O_WRONLY, 0666); + if (IS_ERR(cal_filp)) { + pr_err("[SSP]: %s - Can't open hw_offset file\n", + __func__); + set_fs(old_fs); + iRet = PTR_ERR(cal_filp); + return iRet; + } + iRet = cal_filp->f_op->write(cal_filp, + (char *)&data->magoffset, + 3 * sizeof(char), &cal_filp->f_pos); + if (iRet != 3 * sizeof(char)) { + pr_err("[SSP]: %s - Can't write the hw_offset" + " to file\n", __func__); + iRet = -EIO; + } + filp_close(cal_filp, current->files); + set_fs(old_fs); + return iRet; + } +} + +int set_hw_offset(struct ssp_data *data) +{ + int iRet = 0; + struct ssp_msg *msg; + + if (!(data->uSensorState & 0x04)) { + pr_info("[SSP]: %s - Skip this function!!!"\ + ", magnetic sensor is not connected(0x%x)\n", + __func__, data->uSensorState); + return iRet; + } + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + return -ENOMEM; + } + msg->cmd = MSG2SSP_AP_SET_MAGNETIC_HWOFFSET; + msg->length = 3; + msg->options = AP2HUB_WRITE; + msg->buffer = (char*) kzalloc(3, GFP_KERNEL); + msg->free_buffer = 1; + + msg->buffer[0] = data->magoffset.x; + msg->buffer[1] = data->magoffset.y; + msg->buffer[2] = data->magoffset.z; + + iRet = ssp_spi_async(data, msg); + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - i2c fail %d\n", __func__, iRet); + iRet = ERROR; + } + + pr_info("[SSP]: %s: x: %d, y: %d, z: %d\n", __func__, + (s8)data->magoffset.x, (s8)data->magoffset.y, (s8)data->magoffset.z); + return iRet; +} + +int set_static_matrix(struct ssp_data *data) +{ + int iRet = 0; + struct ssp_msg *msg; + s16 static_matrix[9] = YAS_STATIC_ELLIPSOID_MATRIX; + + if (!(data->uSensorState & 0x04)) { + pr_info("[SSP]: %s - Skip this function!!!"\ + ", magnetic sensor is not connected(0x%x)\n", + __func__, data->uSensorState); + return iRet; + } + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + return -ENOMEM; + } + msg->cmd = MSG2SSP_AP_SET_MAGNETIC_STATIC_MATRIX; + msg->length = 18; + msg->options = AP2HUB_WRITE; + msg->buffer = (char*) kzalloc(18, GFP_KERNEL); + + msg->free_buffer = 1; + if (data->static_matrix == NULL) + memcpy(msg->buffer, static_matrix, 18); + else + memcpy(msg->buffer, data->static_matrix, 18); + + iRet = ssp_spi_async(data, msg); + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - i2c fail %d\n", __func__, iRet); + iRet = ERROR; + } + pr_info("[SSP]: %s: finished \n", __func__); + + return iRet; +} + +int get_hw_offset(struct ssp_data *data) +{ + int iRet = 0; + char buffer[3] = { 0, }; + + struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + return -ENOMEM; + } + msg->cmd = MSG2SSP_AP_GET_MAGNETIC_HWOFFSET; + msg->length = 3; + msg->options = AP2HUB_READ; + msg->buffer = buffer; + msg->free_buffer = 0; + + data->magoffset.x = 0; + data->magoffset.y = 0; + data->magoffset.z = 0; + + iRet = ssp_spi_sync(data, msg, 1000); + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - i2c fail %d\n", __func__, iRet); + iRet = ERROR; + } + + data->magoffset.x = buffer[0]; + data->magoffset.y = buffer[1]; + data->magoffset.z = buffer[2]; + + pr_info("[SSP]: %s: x: %d, y: %d, z: %d\n", __func__, + (s8)data->magoffset.x, + (s8)data->magoffset.y, + (s8)data->magoffset.z); + return iRet; +} + +static ssize_t magnetic_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", VENDOR); +} + +static ssize_t magnetic_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", CHIP_ID); +} + +static int check_rawdata_spec(struct ssp_data *data) +{ + if ((data->buf[GEOMAGNETIC_RAW].x == 0) && + (data->buf[GEOMAGNETIC_RAW].y == 0) && + (data->buf[GEOMAGNETIC_RAW].z == 0)) + return FAIL; + else + return SUCCESS; +} + +static ssize_t raw_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + pr_info("[SSP] %s - %d,%d,%d\n", __func__, + data->buf[GEOMAGNETIC_RAW].x, + data->buf[GEOMAGNETIC_RAW].y, + data->buf[GEOMAGNETIC_RAW].z); + + if (data->bGeomagneticRawEnabled == false) { + data->buf[GEOMAGNETIC_RAW].x = -1; + data->buf[GEOMAGNETIC_RAW].y = -1; + data->buf[GEOMAGNETIC_RAW].z = -1; + } else { + if (data->buf[GEOMAGNETIC_RAW].x > 18000) + data->buf[GEOMAGNETIC_RAW].x = 18000; + else if (data->buf[GEOMAGNETIC_RAW].x < -18000) + data->buf[GEOMAGNETIC_RAW].x = -18000; + if (data->buf[GEOMAGNETIC_RAW].y > 18000) + data->buf[GEOMAGNETIC_RAW].y = 18000; + else if (data->buf[GEOMAGNETIC_RAW].y < -18000) + data->buf[GEOMAGNETIC_RAW].y = -18000; + if (data->buf[GEOMAGNETIC_RAW].z > 18000) + data->buf[GEOMAGNETIC_RAW].z = 18000; + else if (data->buf[GEOMAGNETIC_RAW].z < -18000) + data->buf[GEOMAGNETIC_RAW].z = -18000; + } + return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", + data->buf[GEOMAGNETIC_RAW].x, + data->buf[GEOMAGNETIC_RAW].y, + data->buf[GEOMAGNETIC_RAW].z); +} + +static ssize_t raw_data_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + char chTempbuf[4] = { 0 }; + int iRet; + int64_t dEnable; + int iRetries = 50; + struct ssp_data *data = dev_get_drvdata(dev); + s32 dMsDelay = 20; + memcpy(&chTempbuf[0], &dMsDelay, 4); + + iRet = kstrtoll(buf, 10, &dEnable); + if (iRet < 0) + return iRet; + + if (dEnable) { + data->buf[GEOMAGNETIC_RAW].x = 0; + data->buf[GEOMAGNETIC_RAW].y = 0; + data->buf[GEOMAGNETIC_RAW].z = 0; + + send_instruction(data, ADD_SENSOR, GEOMAGNETIC_RAW, + chTempbuf, 4); + + do { + msleep(20); + if (check_rawdata_spec(data) == SUCCESS) + break; + } while (--iRetries); + + if (iRetries > 0) { + pr_info("[SSP] %s - success, %d\n", __func__, iRetries); + data->bGeomagneticRawEnabled = true; + } else { + pr_err("[SSP] %s - wait timeout, %d\n", __func__, + iRetries); + data->bGeomagneticRawEnabled = false; + } + } else { + send_instruction(data, REMOVE_SENSOR, GEOMAGNETIC_RAW, + chTempbuf, 4); + data->bGeomagneticRawEnabled = false; + } + + return size; +} + +static int check_data_spec(struct ssp_data *data) +{ + if ((data->buf[GEOMAGNETIC_SENSOR].x == 0) && + (data->buf[GEOMAGNETIC_SENSOR].y == 0) && + (data->buf[GEOMAGNETIC_SENSOR].z == 0)) + return FAIL; + else if ((data->buf[GEOMAGNETIC_SENSOR].x > 6500) || + (data->buf[GEOMAGNETIC_SENSOR].x < -6500) || + (data->buf[GEOMAGNETIC_SENSOR].y > 6500) || + (data->buf[GEOMAGNETIC_SENSOR].y < -6500) || + (data->buf[GEOMAGNETIC_SENSOR].z > 6500) || + (data->buf[GEOMAGNETIC_SENSOR].z < -6500)) + return FAIL; + else + return SUCCESS; +} + +static ssize_t adc_data_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + bool bSuccess = false; + u8 chTempbuf[4] = { 0 }; + s16 iSensorBuf[3] = {0, }; + int iRetries = 10; + struct ssp_data *data = dev_get_drvdata(dev); + s32 dMsDelay = 20; + memcpy(&chTempbuf[0], &dMsDelay, 4); + + data->buf[GEOMAGNETIC_SENSOR].x = 0; + data->buf[GEOMAGNETIC_SENSOR].y = 0; + data->buf[GEOMAGNETIC_SENSOR].z = 0; + + if (!(atomic_read(&data->aSensorEnable) & (1 << GEOMAGNETIC_SENSOR))) + send_instruction(data, ADD_SENSOR, GEOMAGNETIC_SENSOR, + chTempbuf, 4); + + do { + msleep(60); + if (check_data_spec(data) == SUCCESS) + break; + } while (--iRetries); + + if (iRetries > 0) + bSuccess = true; + + iSensorBuf[0] = data->buf[GEOMAGNETIC_SENSOR].x; + iSensorBuf[1] = data->buf[GEOMAGNETIC_SENSOR].y; + iSensorBuf[2] = data->buf[GEOMAGNETIC_SENSOR].z; + + if (!(atomic_read(&data->aSensorEnable) & (1 << GEOMAGNETIC_SENSOR))) + send_instruction(data, REMOVE_SENSOR, GEOMAGNETIC_SENSOR, + chTempbuf, 4); + + pr_info("[SSP]: %s - x = %d, y = %d, z = %d\n", __func__, + iSensorBuf[0], iSensorBuf[1], iSensorBuf[2]); + + return sprintf(buf, "%s,%d,%d,%d\n", (bSuccess ? "OK" : "NG"), + iSensorBuf[0], iSensorBuf[1], iSensorBuf[2]); +} + +static ssize_t magnetic_get_selftest(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char chTempBuf[22] = { 0, }; + int iRet = 0; + s8 id = 0, x = 0, y1 = 0, y2 = 0, dir = 0; + s16 sx = 0, sy = 0, ohx = 0, ohy = 0, ohz = 0; + s8 err[7] = {-1, }; + struct ssp_data *data = dev_get_drvdata(dev); + + struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + goto exit; + } + msg->cmd = GEOMAGNETIC_FACTORY; + msg->length = 22; + msg->options = AP2HUB_READ; + msg->buffer = chTempBuf; + msg->free_buffer = 0; + + iRet = ssp_spi_sync(data, msg, 1000); + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - Magnetic Selftest Timeout!! %d\n", __func__, iRet); + goto exit; + } + + id = (s8)(chTempBuf[0]); + err[0] = (s8)(chTempBuf[1]); + err[1] = (s8)(chTempBuf[2]); + err[2] = (s8)(chTempBuf[3]); + x = (s8)(chTempBuf[4]); + y1 = (s8)(chTempBuf[5]); + y2 = (s8)(chTempBuf[6]); + err[3] = (s8)(chTempBuf[7]); + dir = (s8)(chTempBuf[8]); + err[4] = (s8)(chTempBuf[9]); + ohx = (s16)((chTempBuf[10] << 8) + chTempBuf[11]); + ohy = (s16)((chTempBuf[12] << 8) + chTempBuf[13]); + ohz = (s16)((chTempBuf[14] << 8) + chTempBuf[15]); + err[6] = (s8)(chTempBuf[16]); + sx = (s16)((chTempBuf[17] << 8) + chTempBuf[18]); + sy = (s16)((chTempBuf[19] << 8) + chTempBuf[20]); + err[5] = (s8)(chTempBuf[21]); + + if (unlikely(id != 0x2)) + err[0] = -1; + if (unlikely(x < -30 || x > 30)) + err[3] = -1; + if (unlikely(y1 < -30 || y1 > 30)) + err[3] = -1; + if (unlikely(y2 < -30 || y2 > 30)) + err[3] = -1; + if (unlikely(sx < 17 || sy < 22)) + err[5] = -1; + if (unlikely(ohx < -600 || ohx > 600)) + err[6] = -1; + if (unlikely(ohy < -600 || ohy > 600)) + err[6] = -1; + if (unlikely(ohz < -600 || ohz > 600)) + err[6] = -1; + + pr_info("[SSP] %s\n" + "[SSP] Test1 - err = %d, id = %d\n" + "[SSP] Test3 - err = %d\n" + "[SSP] Test4 - err = %d, offset = %d,%d,%d\n" + "[SSP] Test5 - err = %d, direction = %d\n" + "[SSP] Test6 - err = %d, sensitivity = %d,%d\n" + "[SSP] Test7 - err = %d, offset = %d,%d,%d\n" + "[SSP] Test2 - err = %d\n", + __func__, err[0], id, err[2], err[3], x, y1, y2, err[4], dir, + err[5], sx, sy, err[6], ohx, ohy, ohz, err[1]); + +exit: + return sprintf(buf, + "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + err[0], id, err[2], err[3], x, y1, y2, err[4], dir, + err[5], sx, sy, err[6], ohx, ohy, ohz, err[1]); +} + +static ssize_t hw_offset_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + mag_open_hwoffset(data); + + pr_info("[SSP] %s: %d %d %d\n", __func__, + (s8)data->magoffset.x, + (s8)data->magoffset.y, + (s8)data->magoffset.z); + + return sprintf(buf, "%d %d %d\n", + (s8)data->magoffset.x, + (s8)data->magoffset.y, + (s8)data->magoffset.z); +} + +static DEVICE_ATTR(name, S_IRUSR | S_IRGRP, magnetic_name_show, NULL); +static DEVICE_ATTR(vendor, S_IRUSR | S_IRGRP, magnetic_vendor_show, NULL); +static DEVICE_ATTR(raw_data, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + raw_data_show, raw_data_store); +static DEVICE_ATTR(adc, S_IRUSR | S_IRGRP, adc_data_read, NULL); +static DEVICE_ATTR(selftest, S_IRUSR | S_IRGRP, magnetic_get_selftest, NULL); +static DEVICE_ATTR(hw_offset, S_IRUSR | S_IRGRP, hw_offset_show, NULL); + +static struct device_attribute *mag_attrs[] = { + &dev_attr_name, + &dev_attr_vendor, + &dev_attr_adc, + &dev_attr_raw_data, + &dev_attr_selftest, + &dev_attr_hw_offset, + NULL, +}; + +int initialize_magnetic_sensor(struct ssp_data *data) +{ + int ret; + + ret = set_static_matrix(data); + if (ret < 0) + pr_err("[SSP]: %s - set_magnetic_static_matrix failed %d\n", + __func__, ret); + + return ret; +} + +void initialize_magnetic_factorytest(struct ssp_data *data) +{ + sensors_register(data->mag_device, data, mag_attrs, "magnetic_sensor"); +} + +void remove_magnetic_factorytest(struct ssp_data *data) +{ + sensors_unregister(data->mag_device, mag_attrs); +} diff --git a/drivers/sensors/brcm/factory/magnetic_yas532.h b/drivers/sensors/brcm/factory/magnetic_yas532.h new file mode 100644 index 0000000..4cb765e --- /dev/null +++ b/drivers/sensors/brcm/factory/magnetic_yas532.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ + +#define YAS_STATIC_ELLIPSOID_MATRIX {10000, 0, 0, 0, 10000, 0, 0, 0, 10000} diff --git a/drivers/sensors/brcm/factory/mcu_bcm4773.c b/drivers/sensors/brcm/factory/mcu_bcm4773.c new file mode 100644 index 0000000..a65e0d9 --- /dev/null +++ b/drivers/sensors/brcm/factory/mcu_bcm4773.c @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ +#include "../ssp.h" + +/*************************************************************************/ +/* factory Sysfs */ +/*************************************************************************/ + +#define MODEL_NAME "BCM4773IUB2G" + +ssize_t mcu_revision_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "BR01%u,BR01%u\n", data->uCurFirmRev, + get_module_rev(data)); +} + +ssize_t mcu_model_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", MODEL_NAME); +} + +ssize_t mcu_update_kernel_bin_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssp_dbg("[SSPBBD]: %s:%d: Ignored some code section.\n", + __func__, __LINE__); + return sprintf(buf, "NG\n"); +} + +ssize_t mcu_update_kernel_crashed_bin_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssp_dbg("[SSPBBD]: %s:%d: Ignored some code section.\n", + __func__, __LINE__); + return sprintf(buf, "NG\n"); +} + +ssize_t mcu_update_ums_bin_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssp_dbg("[SSPBBD]: %s:%d: Ignored some code section.\n", + __func__, __LINE__); + return sprintf(buf, "NG\n"); +} + +ssize_t mcu_reset_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + reset_mcu(data); + + return sprintf(buf, "OK\n"); +} + +ssize_t mcu_dump_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + int status = 1, iDelaycnt = 0; + + data->bDumping = true; + set_big_data_start(data, BIG_TYPE_DUMP, 0); + msleep(300); + while (data->bDumping) { + mdelay(10); + if (iDelaycnt++ > 1000) { + status = 0; + break; + } + } + return sprintf(buf, "%s\n", status ? "OK" : "NG"); +} + +static char buffer[FACTORY_DATA_MAX]; + +ssize_t mcu_factorytest_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct ssp_data *data = dev_get_drvdata(dev); + int iRet = 0; + struct ssp_msg *msg; + + if (sysfs_streq(buf, "1")) { + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", + __func__); + return -ENOMEM; + } + msg->cmd = MCU_FACTORY; + msg->length = 5; + msg->options = AP2HUB_READ; + msg->buffer = buffer; + msg->free_buffer = 0; + + memset(msg->buffer, 0, 5); + + iRet = ssp_spi_async(data, msg); + + } else { + pr_err("[SSP]: %s - invalid value %d\n", __func__, *buf); + return -EINVAL; + } + + ssp_dbg("[SSP]: MCU Factory Test Start! - %d\n", iRet); + + return size; +} + +ssize_t mcu_factorytest_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + bool bMcuTestSuccessed = false; + struct ssp_data *data = dev_get_drvdata(dev); + + if (data->bSspShutdown == true) { + ssp_dbg("[SSP]: %s - MCU Bin is crashed\n", __func__); + return sprintf(buf, "NG,NG,NG\n"); + } + + ssp_dbg("[SSP] MCU Factory Test Data : %u, %u, %u, %u, %u\n", buffer[0], + buffer[1], buffer[2], buffer[3], buffer[4]); + + /* system clock, RTC, I2C Master, I2C Slave, externel pin */ + if ((buffer[0] == SUCCESS) + && (buffer[1] == SUCCESS) + && (buffer[2] == SUCCESS) + && (buffer[3] == SUCCESS) + && (buffer[4] == SUCCESS)) + bMcuTestSuccessed = true; + + ssp_dbg("[SSP]: MCU Factory Test Result - %s, %s, %s\n", MODEL_NAME, + (bMcuTestSuccessed ? "OK" : "NG"), "OK"); + + return sprintf(buf, "%s,%s,%s\n", MODEL_NAME, + (bMcuTestSuccessed ? "OK" : "NG"), "OK"); +} + +ssize_t mcu_sleep_factorytest_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct ssp_data *data = dev_get_drvdata(dev); + int iRet = 0; + struct ssp_msg *msg; + + if (sysfs_streq(buf, "1")) { + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", + __func__); + return -ENOMEM; + } + msg->cmd = MCU_SLEEP_FACTORY; + msg->length = FACTORY_DATA_MAX; + msg->options = AP2HUB_READ; + msg->buffer = buffer; + msg->free_buffer = 0; + + iRet = ssp_spi_sync(data, msg, 10000); + + } else { + pr_err("[SSP]: %s - invalid value %d\n", __func__, *buf); + return -EINVAL; + } + + ssp_dbg("[SSP]: MCU Sleep Factory Test Start! - %d\n", iRet); + + return size; +} + +ssize_t mcu_sleep_factorytest_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int iDataIdx, iSensorData = 0; + struct ssp_data *data = dev_get_drvdata(dev); + struct sensor_value fsb[SENSOR_MAX]; + u16 chLength = 0; + + memcpy(&chLength, buffer, 2); + memset(fsb, 0, sizeof(struct sensor_value) * SENSOR_MAX); + + for (iDataIdx = 2; iDataIdx < chLength + 2;) { + iSensorData = (int)buffer[iDataIdx++]; + + if ((iSensorData < 0) || + (iSensorData >= (SENSOR_MAX - 1))) { + pr_err("[SSP]: %s - Mcu data frame error %d\n", + __func__, iSensorData); + goto exit; + } + + data->get_sensor_data[iSensorData]((char *)buffer, + &iDataIdx, &(fsb[iSensorData])); + } + + fsb[PRESSURE_SENSOR].pressure[0] -= data->iPressureCal; + +exit: + ssp_dbg("[SSP]: %s Result\n" + "[SSP]: accel %d,%d,%d\n" + "[SSP]: gyro %d,%d,%d\n" + "[SSP]: mag %d,%d,%d\n" + "[SSP]: baro %d,%d\n" + "[SSP]: ges %d,%d,%d,%d\n" + "[SSP]: prox %u,%u\n" + "[SSP]: temp %d,%d,%d\n" +#if defined(CONFIG_SENSORS_SSP_TMG399X) + "[SSP]: light %u,%u,%u,%u,%u,%u\n", __func__, +#else + "[SSP]: light %u,%u,%u,%u\n", __func__, +#endif + fsb[ACCELEROMETER_SENSOR].x, fsb[ACCELEROMETER_SENSOR].y, + fsb[ACCELEROMETER_SENSOR].z, fsb[GYROSCOPE_SENSOR].x, + fsb[GYROSCOPE_SENSOR].y, fsb[GYROSCOPE_SENSOR].z, + fsb[GEOMAGNETIC_SENSOR].cal_x, fsb[GEOMAGNETIC_SENSOR].cal_y, + fsb[GEOMAGNETIC_SENSOR].cal_z, fsb[PRESSURE_SENSOR].pressure[0], + fsb[PRESSURE_SENSOR].pressure[1], + fsb[GESTURE_SENSOR].data[0], fsb[GESTURE_SENSOR].data[1], + fsb[GESTURE_SENSOR].data[2], fsb[GESTURE_SENSOR].data[3], + fsb[PROXIMITY_SENSOR].prox[0], fsb[PROXIMITY_SENSOR].prox[1], + fsb[TEMPERATURE_HUMIDITY_SENSOR].x, + fsb[TEMPERATURE_HUMIDITY_SENSOR].y, + fsb[TEMPERATURE_HUMIDITY_SENSOR].z, + fsb[LIGHT_SENSOR].r, fsb[LIGHT_SENSOR].g, fsb[LIGHT_SENSOR].b, + fsb[LIGHT_SENSOR].w +#if defined(CONFIG_SENSORS_SSP_TMG399X) + ,fsb[LIGHT_SENSOR].a_time, fsb[LIGHT_SENSOR].a_gain +#endif + ); + + return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%u," +#if defined(CONFIG_SENSORS_SSP_TMG399X) + "%u,%u,%u,%u,%u,%u,%d,%d,%d,%d,%d,%d\n", +#else + "%u,%u,%u,%u,%d,%d,%d,%d,%d,%d\n", +#endif + fsb[ACCELEROMETER_SENSOR].x, fsb[ACCELEROMETER_SENSOR].y, + fsb[ACCELEROMETER_SENSOR].z, fsb[GYROSCOPE_SENSOR].x, + fsb[GYROSCOPE_SENSOR].y, fsb[GYROSCOPE_SENSOR].z, + fsb[GEOMAGNETIC_SENSOR].cal_x, fsb[GEOMAGNETIC_SENSOR].cal_y, + fsb[GEOMAGNETIC_SENSOR].cal_z, fsb[PRESSURE_SENSOR].pressure[0], + fsb[PRESSURE_SENSOR].pressure[1], fsb[PROXIMITY_SENSOR].prox[1], + fsb[LIGHT_SENSOR].r, fsb[LIGHT_SENSOR].g, fsb[LIGHT_SENSOR].b, + fsb[LIGHT_SENSOR].w, +#if defined(CONFIG_SENSORS_SSP_TMG399X) + fsb[LIGHT_SENSOR].a_time, fsb[LIGHT_SENSOR].a_gain, +#endif + fsb[GESTURE_SENSOR].data[0], fsb[GESTURE_SENSOR].data[1], + fsb[GESTURE_SENSOR].data[2], fsb[GESTURE_SENSOR].data[3], + fsb[TEMPERATURE_HUMIDITY_SENSOR].x, + fsb[TEMPERATURE_HUMIDITY_SENSOR].y); +} diff --git a/drivers/sensors/brcm/factory/pressure_bmp182.c b/drivers/sensors/brcm/factory/pressure_bmp182.c new file mode 100644 index 0000000..d15f8c6 --- /dev/null +++ b/drivers/sensors/brcm/factory/pressure_bmp182.c @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ +#include "../ssp.h" + +#define LPS25H_REV 6 +#define VENDOR_BOSCH "BOSCH" +#define CHIP_ID_BOSCH "BMP180" +#define VENDOR_STM "STM" +#define CHIP_ID_LPS25H "LPS25H" + +#define CALIBRATION_FILE_PATH "/efs/FactoryApp/baro_delta" + +#define PR_ABS_MAX 8388607 /* 24 bit 2'compl */ +#define PR_ABS_MIN -8388608 + +/*************************************************************************/ +/* factory Sysfs */ +/*************************************************************************/ + +static ssize_t sea_level_pressure_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + sscanf(buf, "%d", &data->sealevelpressure); + + if (data->sealevelpressure == 0) { + pr_info("%s, our->temperature = 0\n", __func__); + data->sealevelpressure = -1; + } + + pr_info("[SSP] %s sea_level_pressure = %d\n", + __func__, data->sealevelpressure); + return size; +} + +int pressure_open_calibration(struct ssp_data *data) +{ + char chBuf[10] = {0,}; + int iErr = 0; + mm_segment_t old_fs; + struct file *cal_filp = NULL; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + cal_filp = filp_open(CALIBRATION_FILE_PATH, O_RDONLY, 0666); + if (IS_ERR(cal_filp)) { + iErr = PTR_ERR(cal_filp); + if (iErr != -ENOENT) + pr_err("[SSP]: %s - Can't open calibration file(%d)\n", + __func__, iErr); + set_fs(old_fs); + return iErr; + } + iErr = cal_filp->f_op->read(cal_filp, + chBuf, 10 * sizeof(char), &cal_filp->f_pos); + if (iErr < 0) { + pr_err("[SSP]: %s - Can't read the cal data from file (%d)\n", + __func__, iErr); + return iErr; + } + filp_close(cal_filp, current->files); + set_fs(old_fs); + + iErr = kstrtoint(chBuf, 10, &data->iPressureCal); + if (iErr < 0) { + pr_err("[SSP]: %s - kstrtoint failed. %d", __func__, iErr); + return iErr; + } + + ssp_dbg("[SSP]: open barometer calibration %d\n", data->iPressureCal); + + if (data->iPressureCal < PR_ABS_MIN || data->iPressureCal > PR_ABS_MAX) + pr_err("[SSP]: %s - wrong offset value!!!\n", __func__); + + return iErr; +} + +static ssize_t pressure_cabratioin_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct ssp_data *data = dev_get_drvdata(dev); + int iPressureCal = 0, iErr = 0; + + iErr = kstrtoint(buf, 10, &iPressureCal); + if (iErr < 0) { + pr_err("[SSP]: %s - kstrtoint failed.(%d)", __func__, iErr); + return iErr; + } + + if (iPressureCal < PR_ABS_MIN || iPressureCal > PR_ABS_MAX) + return -EINVAL; + + data->iPressureCal = (s32)iPressureCal; + + return size; +} + +static ssize_t pressure_cabratioin_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + pressure_open_calibration(data); + + return sprintf(buf, "%d\n", data->iPressureCal); +} + +static ssize_t eeprom_check_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char chTempBuf = 0; + int iRet = 0; + struct ssp_data *data = dev_get_drvdata(dev); + + struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + msg->cmd = PRESSURE_FACTORY; + msg->length = 1; + msg->options = AP2HUB_READ; + msg->buffer = &chTempBuf; + msg->free_buffer = 0; + + iRet = ssp_spi_sync(data, msg, 3000); + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - Pressure Selftest Timeout!!\n", __func__); + goto exit; + } + + ssp_dbg("[SSP]: %s - %u\n", __func__, chTempBuf); + + exit: + return snprintf(buf, PAGE_SIZE, "%d", chTempBuf); +} + +/* sysfs for vendor & name */ +static ssize_t pressure_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#if defined(LPS25H_REV) + struct ssp_data *data = dev_get_drvdata(dev); + + if(data->ap_rev >= LPS25H_REV) + return sprintf(buf, "%s\n", VENDOR_STM); + else + return sprintf(buf, "%s\n", VENDOR_BOSCH); +#else + return sprintf(buf, "%s\n", VENDOR_BOSCH); +#endif +} + +static ssize_t pressure_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#if defined (LPS25H_REV) + struct ssp_data *data = dev_get_drvdata(dev); + + if(data->ap_rev >= LPS25H_REV) + return sprintf(buf, "%s\n", CHIP_ID_LPS25H); + else + return sprintf(buf, "%s\n", CHIP_ID_BOSCH); +#else + return sprintf(buf, "%s\n", CHIP_ID_BOSCH); +#endif +} + +static DEVICE_ATTR(vendor, S_IRUSR | S_IRGRP, pressure_vendor_show, NULL); +static DEVICE_ATTR(name, S_IRUSR | S_IRGRP, pressure_name_show, NULL); +static DEVICE_ATTR(eeprom_check, S_IRUSR | S_IRGRP, eeprom_check_show, NULL); +static DEVICE_ATTR(calibration, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + pressure_cabratioin_show, pressure_cabratioin_store); +static DEVICE_ATTR(sea_level_pressure, /*S_IRUGO |*/ S_IWUSR | S_IWGRP, + NULL, sea_level_pressure_store); + +static struct device_attribute *pressure_attrs[] = { + &dev_attr_vendor, + &dev_attr_name, + &dev_attr_calibration, + &dev_attr_sea_level_pressure, + &dev_attr_eeprom_check, + NULL, +}; + +#if defined (LPS25H_REV) +static struct device_attribute *pressure_attrs_lps25h[] = { + &dev_attr_vendor, + &dev_attr_name, + &dev_attr_calibration, + &dev_attr_sea_level_pressure, + NULL, +}; +#endif + +void initialize_pressure_factorytest(struct ssp_data *data) +{ +#if defined (LPS25H_REV) + if(data->ap_rev >= LPS25H_REV) + sensors_register(data->prs_device, data, pressure_attrs_lps25h, + "barometer_sensor"); + else + sensors_register(data->prs_device, data, pressure_attrs, + "barometer_sensor"); +#else + sensors_register(data->prs_device, data, pressure_attrs, + "barometer_sensor"); +#endif +} + +void remove_pressure_factorytest(struct ssp_data *data) +{ +#if defined (LPS25H_REV) + if(data->ap_rev >= LPS25H_REV) + sensors_unregister(data->prs_device, pressure_attrs_lps25h); + else + sensors_unregister(data->prs_device, pressure_attrs); +#else + sensors_unregister(data->prs_device, pressure_attrs); +#endif +} diff --git a/drivers/sensors/brcm/factory/prox_tmg399x.c b/drivers/sensors/brcm/factory/prox_tmg399x.c new file mode 100644 index 0000000..fbd9c7c --- /dev/null +++ b/drivers/sensors/brcm/factory/prox_tmg399x.c @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ +#include "../ssp.h" + +#define VENDOR "AMS" +#define CHIP_ID "TMG399X" + +#define CANCELATION_FILE_PATH "/efs/prox_cal" +#define LCD_LDI_FILE_PATH "/sys/class/lcd/panel/window_type" + +#define LINE_1 '4' +#define LINE_2 '2' + +#define LDI_OTHERS '0' +#define LDI_GRAY '1' +#define LDI_WHITE '2' + +#define TBD_HIGH_THRESHOLD 185 +#define TBD_LOW_THRESHOLD 145 +#define WHITE_HIGH_THRESHOLD 185 +#define WHITE_LOW_THRESHOLD 145 + +/*************************************************************************/ +/* factory Sysfs */ +/*************************************************************************/ + +static ssize_t prox_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", VENDOR); +} + +static ssize_t prox_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", CHIP_ID); +} + +static ssize_t proximity_avg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", + data->buf[PROXIMITY_RAW].prox[1], + data->buf[PROXIMITY_RAW].prox[2], + data->buf[PROXIMITY_RAW].prox[3]); +} + +static ssize_t proximity_avg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + char chTempbuf[4] = { 0 }; + int iRet; + int64_t dEnable; + struct ssp_data *data = dev_get_drvdata(dev); + + s32 dMsDelay = 20; + memcpy(&chTempbuf[0], &dMsDelay, 4); + + iRet = kstrtoll(buf, 10, &dEnable); + if (iRet < 0) + return iRet; + + if (dEnable) { + send_instruction(data, ADD_SENSOR, PROXIMITY_RAW, chTempbuf, 4); + data->bProximityRawEnabled = true; + } else { + send_instruction(data, REMOVE_SENSOR, PROXIMITY_RAW, + chTempbuf, 4); + data->bProximityRawEnabled = false; + } + + return size; +} + +static u16 get_proximity_rawdata(struct ssp_data *data) +{ + u16 uRowdata = 0; + char chTempbuf[4] = { 0 }; + + s32 dMsDelay = 20; + memcpy(&chTempbuf[0], &dMsDelay, 4); + + if (data->bProximityRawEnabled == false) { + send_instruction(data, ADD_SENSOR, PROXIMITY_RAW, chTempbuf, 4); + msleep(200); + uRowdata = data->buf[PROXIMITY_RAW].prox[0]; + send_instruction(data, REMOVE_SENSOR, PROXIMITY_RAW, + chTempbuf, 4); + } else { + uRowdata = data->buf[PROXIMITY_RAW].prox[0]; + } + + return uRowdata; +} + +static ssize_t proximity_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", get_proximity_rawdata(data)); +} + +static ssize_t proximity_raw_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", get_proximity_rawdata(data)); +} + +void get_proximity_threshold(struct ssp_data *data) +{ + data->uProxHiThresh = data->uProxHiThresh_default; + data->uProxLoThresh = data->uProxLoThresh_default; +} + +int proximity_open_calibration(struct ssp_data *data) +{ + int iRet = 0; + mm_segment_t old_fs; + struct file *cancel_filp = NULL; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + cancel_filp = filp_open(CANCELATION_FILE_PATH, O_RDONLY, 0666); + if (IS_ERR(cancel_filp)) { + iRet = PTR_ERR(cancel_filp); + if (iRet != -ENOENT) + pr_err("[SSP]: %s - Can't open cancelation file\n", + __func__); + set_fs(old_fs); + goto exit; + } + + iRet = cancel_filp->f_op->read(cancel_filp, + (u8 *)&data->uProxCanc, sizeof(unsigned int), &cancel_filp->f_pos); + if (iRet != sizeof(u8)) { + pr_err("[SSP]: %s - Can't read the cancel data\n", __func__); + iRet = -EIO; + } + + if (data->uProxCanc != 0) { + /*If there is an offset cal data. */ + data->uProxHiThresh = + data->uProxHiThresh_default + data->uProxCanc; + data->uProxLoThresh = + data->uProxLoThresh_default + data->uProxCanc; + } + + pr_info("[SSP] %s: proximity ps_canc = %d, ps_thresh hi - %d lo - %d\n", + __func__, data->uProxCanc, data->uProxHiThresh, + data->uProxLoThresh); + + filp_close(cancel_filp, current->files); + set_fs(old_fs); + +exit: + return iRet; +} + +static int calculate_proximity_threshold(struct ssp_data *data) +{ + if (data->uCrosstalk < 55) { + data->uProxCanc = 0; + data->uProxCalResult = 2; + } else if (data->uCrosstalk <= 120) { + data->uProxCanc = data->uCrosstalk * 5 / 10; + data->uProxCalResult = 1; + } else { + data->uProxCanc = 0; + data->uProxCalResult = 0; + pr_info("[SSP] crosstalk > 120, calibration failed\n"); + return ERROR; + } + data->uProxHiThresh = data->uProxHiThresh_default + data->uProxCanc; + data->uProxLoThresh = data->uProxLoThresh_default + data->uProxCanc; + + pr_info("[SSP] %s - crosstalk_offset = %u(%u), HI_THD = %u, LOW_THD = %u\n", + __func__, data->uProxCanc, data->uCrosstalk, + data->uProxHiThresh, data->uProxLoThresh); + + return SUCCESS; +} + +static int proximity_store_cancelation(struct ssp_data *data, int iCalCMD) +{ + int iRet = 0; + mm_segment_t old_fs; + struct file *cancel_filp = NULL; + + if (iCalCMD) { + data->uCrosstalk = get_proximity_rawdata(data); + iRet = calculate_proximity_threshold(data); + } else { + data->uProxHiThresh = data->uProxHiThresh_default; + data->uProxLoThresh = data->uProxLoThresh_default; + data->uProxCanc = 0; + } + + if (iRet != ERROR) + set_proximity_threshold(data, data->uProxHiThresh, + data->uProxLoThresh); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + cancel_filp = filp_open(CANCELATION_FILE_PATH, + O_CREAT | O_TRUNC | O_WRONLY | O_SYNC, 0666); + if (IS_ERR(cancel_filp)) { + pr_err("%s: Can't open cancelation file\n", __func__); + set_fs(old_fs); + iRet = PTR_ERR(cancel_filp); + return iRet; + } + + iRet = cancel_filp->f_op->write(cancel_filp, (u8 *)&data->uProxCanc, + sizeof(unsigned int), &cancel_filp->f_pos); + if (iRet != sizeof(unsigned int)) { + pr_err("%s: Can't write the cancel data to file\n", __func__); + iRet = -EIO; + } + + filp_close(cancel_filp, current->files); + set_fs(old_fs); + + return iRet; +} + +static ssize_t proximity_cancel_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + ssp_dbg("[SSP]: uProxThresh : hi : %u lo : %u, uProxCanc = %u\n", + data->uProxHiThresh, data->uProxLoThresh, data->uProxCanc); + + return sprintf(buf, "%u,%u,%u\n", data->uProxCanc, + data->uProxHiThresh, data->uProxLoThresh); +} + +static ssize_t proximity_cancel_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int iCalCMD = 0, iRet = 0; + struct ssp_data *data = dev_get_drvdata(dev); + + if (sysfs_streq(buf, "1")) /* calibrate cancelation value */ + iCalCMD = 1; + else if (sysfs_streq(buf, "0")) /* reset cancelation value */ + iCalCMD = 0; + else { + pr_debug("%s: invalid value %d\n", __func__, *buf); + return -EINVAL; + } + + iRet = proximity_store_cancelation(data, iCalCMD); + if (iRet < 0) { + pr_err("[SSP]: - %s proximity_store_cancelation() failed\n", + __func__); + return iRet; + } + + ssp_dbg("[SSP]: %s - %u\n", __func__, iCalCMD); + return size; +} + +static ssize_t proximity_thresh_high_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + ssp_dbg("[SSP]: uProxThresh = hi - %u, lo - %u\n", + data->uProxHiThresh, data->uProxLoThresh); + + return sprintf(buf, "%u,%u\n", data->uProxHiThresh, + data->uProxLoThresh); +} + +static ssize_t proximity_thresh_high_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + u16 uNewThresh; + int iRet = 0; + struct ssp_data *data = dev_get_drvdata(dev); + + iRet = kstrtou16(buf, 10, &uNewThresh); + if (iRet < 0) + pr_err("[SSP]: %s - kstrtoint failed.(%d)\n", __func__, iRet); + else { + if(uNewThresh & 0xfc00) + pr_err("[SSP]: %s - allow 10bits.(%d)\n", __func__, uNewThresh); + else { + uNewThresh &= 0x03ff; + data->uProxHiThresh = data->uProxHiThresh_default = uNewThresh; + set_proximity_threshold(data, data->uProxHiThresh, + data->uProxLoThresh); + } + } + + ssp_dbg("[SSP]: %s - new prox threshold : hi - %u, lo - %u\n", + __func__, data->uProxHiThresh, data->uProxLoThresh); + + return size; +} + +static ssize_t proximity_thresh_low_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + ssp_dbg("[SSP]: uProxThresh = hi - %u, lo - %u\n", + data->uProxHiThresh, data->uProxLoThresh); + + return sprintf(buf, "%u,%u\n", data->uProxHiThresh, + data->uProxLoThresh); +} + +static ssize_t proximity_thresh_low_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + u16 uNewThresh; + int iRet = 0; + struct ssp_data *data = dev_get_drvdata(dev); + + iRet = kstrtou16(buf, 10, &uNewThresh); + if (iRet < 0) + pr_err("[SSP]: %s - kstrtoint failed.(%d)\n", __func__, iRet); + else { + if(uNewThresh & 0xfc00) + pr_err("[SSP]: %s - allow 10bits.(%d)\n", __func__, uNewThresh); + else { + uNewThresh &= 0x03ff; + data->uProxLoThresh = data->uProxLoThresh_default = uNewThresh; + set_proximity_threshold(data, data->uProxHiThresh, + data->uProxLoThresh); + } + } + + ssp_dbg("[SSP]: %s - new prox threshold : hi - %u, lo - %u\n", + __func__, data->uProxHiThresh, data->uProxLoThresh); + + return size; +} + +static ssize_t proximity_cancel_pass_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + pr_info("[SSP] %s, %u\n", __func__, data->uProxCalResult); + return snprintf(buf, PAGE_SIZE, "%u\n", data->uProxCalResult); +} + +static ssize_t barcode_emul_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", data->bBarcodeEnabled); +} + +static ssize_t barcode_emul_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int iRet; + int64_t dEnable; + struct ssp_data *data = dev_get_drvdata(dev); + + iRet = kstrtoll(buf, 10, &dEnable); + if (iRet < 0) + return iRet; + + if (dEnable) + set_proximity_barcode_enable(data, true); + else + set_proximity_barcode_enable(data, false); + + return size; +} + +static DEVICE_ATTR(vendor, S_IRUSR | S_IRGRP, prox_vendor_show, NULL); +static DEVICE_ATTR(name, S_IRUSR | S_IRGRP, prox_name_show, NULL); +static DEVICE_ATTR(state, S_IRUSR | S_IRGRP, proximity_state_show, NULL); +static DEVICE_ATTR(raw_data, S_IRUSR | S_IRGRP, proximity_raw_data_show, NULL); +static DEVICE_ATTR(barcode_emul_en, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + barcode_emul_enable_show, barcode_emul_enable_store); +static DEVICE_ATTR(prox_avg, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + proximity_avg_show, proximity_avg_store); +static DEVICE_ATTR(prox_cal, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + proximity_cancel_show, proximity_cancel_store); +static DEVICE_ATTR(thresh_high, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + proximity_thresh_high_show, proximity_thresh_high_store); +static DEVICE_ATTR(thresh_low, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + proximity_thresh_low_show, proximity_thresh_low_store); +static DEVICE_ATTR(prox_offset_pass, S_IRUSR | S_IRGRP, + proximity_cancel_pass_show, NULL); + +static struct device_attribute *prox_attrs[] = { + &dev_attr_vendor, + &dev_attr_name, + &dev_attr_state, + &dev_attr_raw_data, + &dev_attr_prox_avg, + &dev_attr_prox_cal, + &dev_attr_thresh_high, + &dev_attr_thresh_low, + &dev_attr_barcode_emul_en, + &dev_attr_prox_offset_pass, + NULL, +}; + +void initialize_prox_factorytest(struct ssp_data *data) +{ + sensors_register(data->prox_device, data, + prox_attrs, "proximity_sensor"); +} + +void remove_prox_factorytest(struct ssp_data *data) +{ + sensors_unregister(data->prox_device, prox_attrs); +} diff --git a/drivers/sensors/brcm/gps/Kconfig b/drivers/sensors/brcm/gps/Kconfig new file mode 100644 index 0000000..2da40a9 --- /dev/null +++ b/drivers/sensors/brcm/gps/Kconfig @@ -0,0 +1,7 @@ +menu "Broadcom GPS Drivers" + +config GPS_BCM47531 + bool "BROADCOM_GPS_CHIPSET_47531" + select SEC_SYSFS + default n +endmenu diff --git a/drivers/sensors/brcm/gps/Makefile b/drivers/sensors/brcm/gps/Makefile new file mode 100644 index 0000000..2b8fedb --- /dev/null +++ b/drivers/sensors/brcm/gps/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for gps devices +# + +obj-$(CONFIG_GPS_BCM47531) += sec_gps_bcm47531.o diff --git a/drivers/sensors/brcm/gps/sec_gps_bcm47531.c b/drivers/sensors/brcm/gps/sec_gps_bcm47531.c new file mode 100644 index 0000000..2ec596b --- /dev/null +++ b/drivers/sensors/brcm/gps/sec_gps_bcm47531.c @@ -0,0 +1,255 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static struct device *gps_dev; +static wait_queue_head_t *p_geofence_wait; +static unsigned int gps_host_wake_up; +static unsigned int gps_pwr_on; + +/* BRCM + * GPS geofence wakeup device implementation + * to support waking up gpsd upon arrival of the interrupt + * gpsd will select on this device to be notified of fence crossing event + */ + +struct gps_geofence_wake { + wait_queue_head_t wait; + int irq; + int host_req_pin; + struct miscdevice misc; +}; + +static int gps_geofence_wake_open(struct inode *inode, struct file *filp) +{ + struct gps_geofence_wake *ac_data = container_of(filp->private_data, + struct gps_geofence_wake, + misc); + + filp->private_data = ac_data; + return 0; +} + +static int gps_geofence_wake_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +static unsigned int gps_geofence_wake_poll(struct file *file, poll_table *wait) +{ + int gpio_value; + struct gps_geofence_wake *ac_data = file->private_data; + + BUG_ON(!ac_data); + + poll_wait(file, &ac_data->wait, wait); + + gpio_value = gpio_get_value(ac_data->host_req_pin); + + /*printk(KERN_INFO "gpio geofence wake host_req=%d\n",gpio_value);*/ + + if (gpio_value) + return POLLIN | POLLRDNORM; + + return 0; +} + + +static long gps_geofence_wake_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + /* TODO + * Fill in useful commands + * (like test helper) + */ + /* + switch(cmd) { + default: + } + */ + return 0; +} + + +static const struct file_operations gps_geofence_wake_fops = { + .owner = THIS_MODULE, + .open = gps_geofence_wake_open, + .release = gps_geofence_wake_release, + .poll = gps_geofence_wake_poll, + /*.read = gps_geofence_wake_read, + .write = gps_geofence_wake_write,*/ + .unlocked_ioctl = gps_geofence_wake_ioctl +}; + +static struct gps_geofence_wake geofence_wake; + +static int gps_geofence_wake_init(int irq, int host_req_pin) +{ + int ret; + + struct gps_geofence_wake *ac_data = &geofence_wake; + memset(ac_data, 0, sizeof(struct gps_geofence_wake)); + + /* misc device setup */ + /* gps daemon will access "/dev/gps_geofence_wake" */ + ac_data->misc.minor = MISC_DYNAMIC_MINOR; + ac_data->misc.name = "gps_geofence_wake"; + ac_data->misc.fops = &gps_geofence_wake_fops; + + /* information that be used later */ + ac_data->irq = irq; + ac_data->host_req_pin = host_req_pin; + + /* initialize wait queue : */ + /* which will be used by poll or select system call */ + init_waitqueue_head(&ac_data->wait); + + printk(KERN_INFO "%s misc register, name %s, irq %d, host req pin num %d\n", + __func__, ac_data->misc.name, irq, host_req_pin); + + ret = misc_register(&ac_data->misc); + if (ret != 0) { + printk(KERN_ERR "cannot register gps geofence wake miscdev on minor=%d (%d)\n", + MISC_DYNAMIC_MINOR, ret); + return -ENODEV; + } + + p_geofence_wait = &ac_data->wait; + + return 0; +} + +/* --------------- */ + + +/*EXPORT_SYMBOL(p_geofence_wait);*/ /*- BRCM -*/ + +static irqreturn_t gps_host_wake_isr(int irq, void *dev) +{ + + int gps_host_wake; + + gps_host_wake = gpio_get_value(gps_host_wake_up); + irq_set_irq_type(irq, + gps_host_wake ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING); + + if (p_geofence_wait && gps_host_wake) { + printk(KERN_ERR "%s Wake-up GPS daemon[TRUE]\n", __func__); + wake_up_interruptible(p_geofence_wait); + } + + return IRQ_HANDLED; +} + +int check_gps_op(void) +{ + /* This pin is high when gps is working */ + int gps_is_running = gpio_get_value(gps_pwr_on); + + pr_debug("LPA : check_gps_op(%d)\n", gps_is_running); + + return gps_is_running; +} +EXPORT_SYMBOL(check_gps_op); + +static int __init gps_bcm47531_init(void) +{ + int irq = 0, ret = 0; + const char *gps_node = "samsung,exynos54xx-bcm4753"; + + struct device_node *root_node = NULL; + + gps_dev = sec_device_create(NULL, "gps"); + BUG_ON(!gps_dev); + + root_node = of_find_compatible_node(NULL, NULL, gps_node); + if (!root_node) { + WARN(1, "failed to get device node of bcm4753\n"); + ret = -ENODEV; + goto err_sec_device_create; + } + + //========== GPS_PWR_EN ============// + gps_pwr_on = of_get_gpio(root_node, 0); + if (!gpio_is_valid(gps_pwr_on)) { + WARN(1, "Invalied gpio pin : %d\n", gps_pwr_on); + ret = -ENODEV; + goto err_sec_device_create; + } + + if (gpio_request(gps_pwr_on, "GPS_PWR_EN")) { + WARN(1, "fail to request gpio(GPS_PWR_EN)\n"); + ret = -ENODEV; + goto err_sec_device_create; + } + gpio_direction_output(gps_pwr_on, 0); + gpio_export(gps_pwr_on, 1); + gpio_export_link(gps_dev, "GPS_PWR_EN", gps_pwr_on); + + //========== GPS_HOST_WAKE ============// + gps_host_wake_up = of_get_gpio(root_node, 1); + if (!gpio_is_valid(gps_host_wake_up)) { + WARN(1, "Invalied gpio pin : %d\n", gps_host_wake_up); + ret = -ENODEV; + goto err_sec_device_create; + } + + if (gpio_request(gps_host_wake_up, "GPS_HOST_WAKE")) { + WARN(1, "fail to request gpio(GPS_HOST_WAKE)\n"); + ret = -ENODEV; + goto err_sec_device_create; + } + gpio_direction_input(gps_host_wake_up); + gpio_export(gps_host_wake_up, 1); + gpio_export_link(gps_dev, "GPS_HOST_WAKE", gps_host_wake_up); + + irq = gpio_to_irq(gps_host_wake_up); + ret = gps_geofence_wake_init(irq, gps_host_wake_up); + if (ret) { + pr_err("[GPS] gps_geofence_wake_init failed.\n"); + ret = -ENODEV; + goto err_sec_device_create; + } + + ret = request_irq(irq, gps_host_wake_isr, + IRQF_TRIGGER_RISING, "gps_host_wake", NULL); + if (ret) { + pr_err("[GPS] Request_host wake irq failed.\n"); + ret = -ENODEV; + goto err_free_irq; + } + + ret = irq_set_irq_wake(irq, 1); + if (ret) { + pr_err("[GPS] Set_irq_wake failed.\n"); + ret = -ENODEV; + goto err_free_irq; + } + + return 0; + +err_free_irq: + free_irq(irq, NULL); +err_sec_device_create: + sec_device_destroy(gps_dev->devt); + return ret; +} + +device_initcall(gps_bcm47531_init); diff --git a/drivers/sensors/brcm/platform/bbd_s333_patch_file.h b/drivers/sensors/brcm/platform/bbd_s333_patch_file.h new file mode 100644 index 0000000..fad7584 --- /dev/null +++ b/drivers/sensors/brcm/platform/bbd_s333_patch_file.h @@ -0,0 +1,12881 @@ +"\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"\n" diff --git a/drivers/sensors/brcm/platform/bbd_tm2_patch_file.h b/drivers/sensors/brcm/platform/bbd_tm2_patch_file.h new file mode 100644 index 0000000..b26f196 --- /dev/null +++ b/drivers/sensors/brcm/platform/bbd_tm2_patch_file.h @@ -0,0 +1,12881 @@ +"\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"\n" diff --git a/drivers/sensors/brcm/sensors_core.c b/drivers/sensors/brcm/sensors_core.c new file mode 100644 index 0000000..a3dd9cf --- /dev/null +++ b/drivers/sensors/brcm/sensors_core.c @@ -0,0 +1,173 @@ +/* + * Universal sensors core class + * + * Author : Ryunkyun Park + */ + +#include +#include +#include +#include +#include +#include +#include + +struct class *sensors_class; +EXPORT_SYMBOL_GPL(sensors_class); +struct class *sensors_event_class; +EXPORT_SYMBOL_GPL(sensors_event_class); +static atomic_t sensor_count; +static struct device *symlink_dev; + +/* + * Create sysfs interface + */ +static void set_sensor_attr(struct device *dev, + struct device_attribute *attributes[]) +{ + int i; + + for (i = 0; attributes[i] != NULL; i++) + if ((device_create_file(dev, attributes[i])) < 0) + pr_err("[SENSOR CORE] fail device_create_file"\ + "(dev, attributes[%d])\n", i); +} + +int sensors_create_symlink(struct input_dev *inputdev) +{ + int err = 0; + + if (symlink_dev == NULL) { + pr_err("%s, symlink_dev is NULL!!!\n", __func__); + return -ENODEV; + } + + err = sysfs_create_link(&symlink_dev->kobj, &inputdev->dev.kobj, inputdev->name); + + if (err < 0) { + pr_err("%s, %s failed!(%d)\n", __func__, inputdev->name, err); + return err; + } + + return err; +} +EXPORT_SYMBOL_GPL(sensors_create_symlink); + +void sensors_remove_symlink(struct input_dev *inputdev) +{ + + if (symlink_dev == NULL) { + pr_err("%s, symlink_dev is NULL!!!\n", __func__); + return; + } + + sysfs_delete_link(&symlink_dev->kobj, &inputdev->dev.kobj, inputdev->name); +} +EXPORT_SYMBOL_GPL(sensors_remove_symlink); + + +int sensors_register(struct device *dev, void *drvdata, + struct device_attribute *attributes[], char *name) +{ + int ret = 0; + + if (!sensors_class) { + sensors_class = class_create(THIS_MODULE, "sensors"); + if (IS_ERR(sensors_class)) + return PTR_ERR(sensors_class); + } + + dev = device_create(sensors_class, NULL, 0, drvdata, "%s", name); + + if (IS_ERR(dev)) { + ret = PTR_ERR(dev); + pr_err("[SENSORS CORE] device_create failed!"\ + "[%d]\n", ret); + return ret; + } + + set_sensor_attr(dev, attributes); + atomic_inc(&sensor_count); + + return 0; +} +EXPORT_SYMBOL_GPL(sensors_register); + +void sensors_unregister(struct device *dev, + struct device_attribute *attributes[]) +{ + int i; + + for (i = 0; attributes[i] != NULL; i++) + device_remove_file(dev, attributes[i]); +} +EXPORT_SYMBOL_GPL(sensors_unregister); + +void destroy_sensor_class(void) +{ + if (sensors_class) { + class_destroy(sensors_class); + sensors_class = NULL; + } + + if (sensors_event_class) { + device_destroy(sensors_event_class, symlink_dev->devt); + class_destroy(sensors_event_class); + symlink_dev = NULL; + sensors_event_class = NULL; + } +} +EXPORT_SYMBOL_GPL(destroy_sensor_class); + +static int __init sensors_class_init(void) +{ + pr_info("[SENSORS CORE] sensors_class_init\n"); + sensors_class = class_create(THIS_MODULE, "sensors"); + if (IS_ERR(sensors_class)) { + pr_err("%s, create sensors_class is failed.(err=%ld)\n", + __func__, IS_ERR(sensors_class)); + return PTR_ERR(sensors_class); + } + + /* For symbolic link */ + sensors_event_class = class_create(THIS_MODULE, "sensor_event"); + if (IS_ERR(sensors_event_class)) { + pr_err("%s, create sensors_class is failed.(err=%ld)\n", + __func__, IS_ERR(sensors_event_class)); + class_destroy(sensors_class); + return PTR_ERR(sensors_event_class); + } + + symlink_dev = device_create(sensors_event_class, NULL, 0, NULL, + "%s", "symlink"); + if (IS_ERR(symlink_dev)) { + pr_err("[SENSORS CORE] symlink_dev create failed!"\ + "[%ld]\n", IS_ERR(symlink_dev)); + class_destroy(sensors_class); + class_destroy(sensors_event_class); + return PTR_ERR(symlink_dev); + } + + atomic_set(&sensor_count, 0); + sensors_class->dev_uevent = NULL; + pr_info("[SENSORS CORE] sensors_class_init succcess\n"); + + return 0; +} + +static void __exit sensors_class_exit(void) +{ + if (sensors_class || sensors_event_class) { + class_destroy(sensors_class); + sensors_class = NULL; + class_destroy(sensors_event_class); + sensors_event_class = NULL; + } +} + +subsys_initcall(sensors_class_init); +module_exit(sensors_class_exit); + +MODULE_DESCRIPTION("Universal sensors core class"); +MODULE_AUTHOR("Ryunkyun Park "); +MODULE_LICENSE("GPL"); diff --git a/drivers/sensors/brcm/ssp.h b/drivers/sensors/brcm/ssp.h new file mode 100644 index 0000000..fba7656 --- /dev/null +++ b/drivers/sensors/brcm/ssp.h @@ -0,0 +1,711 @@ +/* + * Copyright (C) 2011, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ + +#ifndef __SSP_PRJ_H__ +#define __SSP_PRJ_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bbdpl1/bbd.h" + +#ifdef CONFIG_SENSORS_SSP_SENSORHUB +#include "ssp_sensorhub.h" +#endif + +#ifdef CONFIG_HAS_EARLYSUSPEND +#undef CONFIG_HAS_EARLYSUSPEND +#endif + +#define SSP_DBG 1 + +#define SUCCESS 1 +#define FAIL 0 +#define ERROR -1 + +#define FACTORY_DATA_MAX 99 + +#if SSP_DBG +#define SSP_FUNC_DBG 1 +#define SSP_DATA_DBG 0 + +/* ssp mcu device ID */ +#define DEVICE_ID 0x55 + +#define ssp_dbg(format, ...) do { \ + printk(KERN_INFO format, ##__VA_ARGS__); \ + } while (0) +#else +#define ssp_dbg(format, ...) +#endif + +#if SSP_FUNC_DBG +#define func_dbg() do { \ + printk(KERN_INFO "[SSP]: %s\n", __func__); \ + } while (0) +#else +#define func_dbg() +#endif + +#if SSP_DATA_DBG +#define data_dbg(format, ...) do { \ + printk(KERN_INFO format, ##__VA_ARGS__); \ + } while (0) +#else +#define data_dbg(format, ...) +#endif + +#define SSP_SW_RESET_TIME 3000 +#define DEFUALT_POLLING_DELAY (200 * NSEC_PER_MSEC) +#define PROX_AVG_READ_NUM 80 +#define DEFAULT_RETRIES 3 +#define DATA_PACKET_SIZE 960 + +/* SSP Binary Type */ +enum { + KERNEL_BINARY = 0, + KERNEL_CRASHED_BINARY, + UMS_BINARY, +}; + +/* + * SENSOR_DELAY_SET_STATE + * Check delay set to avoid sending ADD instruction twice + */ +enum { + INITIALIZATION_STATE = 0, + NO_SENSOR_STATE, + ADD_SENSOR_STATE, + RUNNING_SENSOR_STATE, +}; + +/* for MSG2SSP_AP_GET_THERM */ +enum { + ADC_BATT = 0, + ADC_CHG, +}; + +enum { + SENSORS_BATCH_DRY_RUN = 0x00000001, + SENSORS_BATCH_WAKE_UPON_FIFO_FULL = 0x00000002 +}; + +enum { + META_DATA_FLUSH_COMPLETE = 1, +}; + +#define SSP_INVALID_REVISION 99999 +#define SSP_INVALID_REVISION2 0xFFFFFF + +/* Gyroscope DPS */ +#define GYROSCOPE_DPS250 250 +#define GYROSCOPE_DPS500 500 +#define GYROSCOPE_DPS2000 2000 + +/* Gesture Sensor Current */ +#define DEFUALT_IR_CURRENT 100 + +/* kernel -> ssp manager cmd*/ +#define SSP_LIBRARY_SLEEP_CMD (1 << 5) +#define SSP_LIBRARY_LARGE_DATA_CMD (1 << 6) +#define SSP_LIBRARY_WAKEUP_CMD (1 << 7) + +/* AP -> SSP Instruction */ +#define MSG2SSP_INST_BYPASS_SENSOR_ADD 0xA1 +#define MSG2SSP_INST_BYPASS_SENSOR_REMOVE 0xA2 +#define MSG2SSP_INST_REMOVE_ALL 0xA3 +#define MSG2SSP_INST_CHANGE_DELAY 0xA4 +#define MSG2SSP_INST_LIBRARY_ADD 0xB1 +#define MSG2SSP_INST_LIBRARY_REMOVE 0xB2 +#define MSG2SSP_INST_LIB_NOTI 0xB4 +#define MSG2SSP_INST_LIB_DATA 0xC1 + +#define MSG2SSP_AP_MCU_SET_GYRO_CAL 0xCD +#define MSG2SSP_AP_MCU_SET_ACCEL_CAL 0xCE +#define MSG2SSP_AP_STATUS_SHUTDOWN 0xD0 +#define MSG2SSP_AP_STATUS_WAKEUP 0xD1 +#define MSG2SSP_AP_STATUS_SLEEP 0xD2 +#define MSG2SSP_AP_STATUS_RESUME 0xD3 +#define MSG2SSP_AP_STATUS_SUSPEND 0xD4 +#define MSG2SSP_AP_STATUS_RESET 0xD5 +#define MSG2SSP_AP_STATUS_POW_CONNECTED 0xD6 +#define MSG2SSP_AP_STATUS_POW_DISCONNECTED 0xD7 +#define MSG2SSP_AP_TEMPHUMIDITY_CAL_DONE 0xDA +#define MSG2SSP_AP_MCU_SET_DUMPMODE 0xDB +#define MSG2SSP_AP_MCU_DUMP_CHECK 0xDC +#define MSG2SSP_AP_MCU_BATCH_FLUSH 0xDD +#define MSG2SSP_AP_MCU_BATCH_COUNT 0xDF + +#define MSG2SSP_AP_WHOAMI 0x0F +#define MSG2SSP_AP_FIRMWARE_REV 0xF0 +#define MSG2SSP_AP_SENSOR_FORMATION 0xF1 +#define MSG2SSP_AP_SENSOR_PROXTHRESHOLD 0xF2 +#define MSG2SSP_AP_SENSOR_BARCODE_EMUL 0xF3 +#define MSG2SSP_AP_SENSOR_SCANNING 0xF4 +#define MSG2SSP_AP_SET_MAGNETIC_HWOFFSET 0xF5 +#define MSG2SSP_AP_GET_MAGNETIC_HWOFFSET 0xF6 +#define MSG2SSP_AP_SENSOR_GESTURE_CURRENT 0xF7 +#define MSG2SSP_AP_GET_THERM 0xF8 +#define MSG2SSP_AP_GET_BIG_DATA 0xF9 +#define MSG2SSP_AP_SET_BIG_DATA 0xFA +#define MSG2SSP_AP_START_BIG_DATA 0xFB +#define MSG2SSP_AP_SET_MAGNETIC_STATIC_MATRIX 0xFD +#define MSG2SSP_AP_SENSOR_TILT 0xEA +#define MSG2SSP_AP_MCU_SET_TIME 0xFE +#define MSG2SSP_AP_MCU_GET_TIME 0xFF +#define MSG2SSP_AP_MOBEAM_DATA_SET 0x31 +#define MSG2SSP_AP_MOBEAM_REGISTER_SET 0x32 +#define MSG2SSP_AP_MOBEAM_COUNT_SET 0x33 +#define MSG2SSP_AP_MOBEAM_START 0x34 +#define MSG2SSP_AP_MOBEAM_STOP 0x35 +#define MSG2SSP_AP_SENSOR_LPF 0x37 + +#define MSG2SSP_AP_FUSEROM 0X01 + +/* voice data */ +#define TYPE_WAKE_UP_VOICE_SERVICE 0x01 +#define TYPE_WAKE_UP_VOICE_SOUND_SOURCE_AM 0x01 +#define TYPE_WAKE_UP_VOICE_SOUND_SOURCE_GRAMMER 0x02 + +/* Factory Test */ +#define ACCELEROMETER_FACTORY 0x80 +#define GYROSCOPE_FACTORY 0x81 +#define GEOMAGNETIC_FACTORY 0x82 +#define PRESSURE_FACTORY 0x85 +#define GESTURE_FACTORY 0x86 +#define TEMPHUMIDITY_CRC_FACTORY 0x88 +#define GYROSCOPE_TEMP_FACTORY 0x8A +#define GYROSCOPE_DPS_FACTORY 0x8B +#define MCU_FACTORY 0x8C +#define MCU_SLEEP_FACTORY 0x8D + +/* Factory data length */ +#define ACCEL_FACTORY_DATA_LENGTH 1 +#define GYRO_FACTORY_DATA_LENGTH 36 +#define MAGNETIC_FACTORY_DATA_LENGTH 26 +#define PRESSURE_FACTORY_DATA_LENGTH 1 +#define MCU_FACTORY_DATA_LENGTH 5 +#define GYRO_TEMP_FACTORY_DATA_LENGTH 2 +#define GYRO_DPS_FACTORY_DATA_LENGTH 1 +#define TEMPHUMIDITY_FACTORY_DATA_LENGTH 1 +#define MCU_SLEEP_FACTORY_DATA_LENGTH FACTORY_DATA_MAX +#define GESTURE_FACTORY_DATA_LENGTH 4 + +#define DEFUALT_HIGH_THRESHOLD 185 +#define DEFUALT_LOW_THRESHOLD 145 + +/* SSP -> AP ACK about write CMD */ +#define MSG_ACK 0x80 /* ACK from SSP to AP */ +#define MSG_NAK 0x70 /* NAK from SSP to AP */ + +#define MAX_GYRO 32767 +#define MIN_GYRO -32768 + +#define MAX_COMP_BUFF 60 + +/* temphumidity sensor*/ +struct shtc1_buffer { + u16 batt[MAX_COMP_BUFF]; + u16 chg[MAX_COMP_BUFF]; + s16 temp[MAX_COMP_BUFF]; + u16 humidity[MAX_COMP_BUFF]; + u16 baro[MAX_COMP_BUFF]; + u16 gyro[MAX_COMP_BUFF]; + char len; +}; + +/* SSP_INSTRUCTION_CMD */ +enum { + REMOVE_SENSOR = 0, + ADD_SENSOR, + CHANGE_DELAY, + GO_SLEEP, + REMOVE_LIBRARY, + ADD_LIBRARY, +}; + +/* SENSOR_TYPE */ +enum { + ACCELEROMETER_SENSOR = 0, + GYROSCOPE_SENSOR, + GEOMAGNETIC_UNCALIB_SENSOR, + GEOMAGNETIC_RAW, + GEOMAGNETIC_SENSOR, + PRESSURE_SENSOR, + GESTURE_SENSOR, + PROXIMITY_SENSOR, + TEMPERATURE_HUMIDITY_SENSOR, + LIGHT_SENSOR, + PROXIMITY_RAW, + ORIENTATION_SENSOR, + STEP_DETECTOR = 12, + SIG_MOTION_SENSOR, + GYRO_UNCALIB_SENSOR, + GAME_ROTATION_VECTOR = 15, + ROTATION_VECTOR, + STEP_COUNTER, + BIO_HRM_RAW, + BIO_HRM_RAW_FAC, + BIO_HRM_LIB, + SHAKE_CAM = 22, + SENSOR_MAX, +}; + +struct meta_data_event { + s32 what; + s32 sensor; +} __attribute__((__packed__)); + +struct sensor_value { + union { + struct { + s16 x; + s16 y; + s16 z; + }; + struct { /*calibrated mag, gyro*/ + s16 cal_x; + s16 cal_y; + s16 cal_z; + u8 accuracy; + }; + struct { /*uncalibrated mag, gyro*/ + s16 uncal_x; + s16 uncal_y; + s16 uncal_z; + s16 offset_x; + s16 offset_y; + s16 offset_z; + }; + struct { /* rotation vector */ + s32 quat_a; + s32 quat_b; + s32 quat_c; + s32 quat_d; + u8 acc_rot; + }; + struct { + u16 r; + u16 g; + u16 b; + u16 w; +#if defined(CONFIG_SENSORS_SSP_TMG399X) + u8 a_time; + u8 a_gain; +#endif + }; + u8 step_det; + u8 sig_motion; + u8 prox[4]; + u8 data[20]; + s32 pressure[3]; + u32 step_diff; + struct meta_data_event meta_data; + }; + u64 timestamp; +} __attribute__((__packed__)); + +extern struct class *sensors_event_class; + +struct calibraion_data { + s16 x; + s16 y; + s16 z; +}; + +struct hw_offset_data { + char x; + char y; + char z; +}; + +/* ssp_msg options bit*/ +#define SSP_SPI 0 /* read write mask */ +#define SSP_RETURN 2 /* write and read option */ +#define SSP_GYRO_DPS 3 /* gyro dps mask */ +#define SSP_INDEX 3 /* data index mask */ + +#define SSP_SPI_MASK (3 << SSP_SPI) /* read write mask */ +#define SSP_GYRO_DPS_MASK (3 << SSP_GYRO_DPS) +#define SSP_INDEX_MASK (8191 << SSP_INDEX) /* dump index mask. Index is up to 8191 */ + +struct ssp_msg { + u8 cmd; + u16 length; + u16 options; + u32 data; + + struct list_head list; + struct completion *done; + char *buffer; + u8 free_buffer; + bool *dead_hook; + bool dead; +} __attribute__((__packed__)); + +enum { + AP2HUB_READ = 0, + AP2HUB_WRITE, + HUB2AP_WRITE, + AP2HUB_READY, + AP2HUB_RETURN +}; + +enum { + BIG_TYPE_DUMP = 0, + BIG_TYPE_READ_LIB, + /*+snamy.jeong 0706 for voice model download & pcm dump*/ + BIG_TYPE_VOICE_NET, + BIG_TYPE_VOICE_GRAM, + BIG_TYPE_VOICE_PCM, + /*-snamy.jeong 0706 for voice model download & pcm dump*/ + BIG_TYPE_TEMP, + BIG_TYPE_MAX, +}; + +enum { + BATCH_MODE_NONE = 0, + BATCH_MODE_RUN, +}; + +struct ssp_time_diff { + u16 batch_count; + u16 batch_mode; + u64 time_diff; + u64 irq_diff; + u16 batch_count_fixed; +}; + +struct ssp_data { + struct iio_dev *accel_indio_dev; + struct iio_dev *gyro_indio_dev; + struct iio_dev *uncal_gyro_indio_dev; + struct iio_dev *mag_indio_dev; + struct iio_dev *uncal_mag_indio_dev; + struct iio_dev *rot_indio_dev; + struct iio_dev *game_rot_indio_dev; + struct iio_dev *step_det_indio_dev; + struct iio_dev *pressure_indio_dev; + struct iio_trigger *accel_trig; + struct iio_trigger *gyro_trig; + struct iio_trigger *rot_trig; + struct iio_trigger *game_rot_trig; + struct iio_trigger *step_det_trig; + struct iio_trigger *pressure_det_trig; + + struct input_dev *light_input_dev; + struct input_dev *prox_input_dev; + struct input_dev *temp_humi_input_dev; + struct input_dev *gesture_input_dev; + + struct input_dev *sig_motion_input_dev; + struct input_dev *step_cnt_input_dev; + struct input_dev *meta_input_dev; + struct spi_device *spi; + struct workqueue_struct *bbd_on_packet_wq; + struct work_struct work_bbd_on_packet; + struct workqueue_struct *bbd_mcu_ready_wq; + struct work_struct work_bbd_mcu_ready; + +#ifdef SSP_BBD_USE_SEND_WORK + struct workqueue_struct *bbd_send_packet_wq; + struct work_struct work_bbd_send_packet; + struct ssp_msg *bbd_send_msg; + unsigned short bbd_msg_options; +#endif /* SSP_BBD_USE_SEND_WORK */ + struct i2c_client *client; + struct wake_lock ssp_wake_lock; + struct timer_list debug_timer; + struct workqueue_struct *debug_wq; + struct work_struct work_debug; + struct calibraion_data accelcal; + struct calibraion_data gyrocal; + struct hw_offset_data magoffset; + struct sensor_value buf[SENSOR_MAX]; + struct device *sen_dev; + struct device *mcu_device; + struct device *acc_device; + struct device *gyro_device; + struct device *mag_device; + struct device *prs_device; + struct device *prox_device; + struct device *light_device; + struct device *ges_device; + struct device *temphumidity_device; +#ifdef CONFIG_SENSORS_SSP_MOBEAM + struct device *mobeam_device; +#endif + struct miscdevice batch_io_device; +/*snamy.jeong@samsung.com temporary code for voice data sending to mcu*/ + struct device *voice_device; + + bool bSspShutdown; + bool bAccelAlert; + bool bProximityRawEnabled; + bool bGeomagneticRawEnabled; + bool bGeomagneticLogged; + bool bBarcodeEnabled; + bool bMcuDumpMode; + bool bBinaryChashed; + bool bProbeIsDone; + bool bDumping; + bool bTimeSyncing; + bool bHandlingIrq; + + unsigned int uProxCanc; + unsigned int uCrosstalk; + unsigned int uProxCalResult; + unsigned int uProxHiThresh; + unsigned int uProxLoThresh; + unsigned int uProxHiThresh_default; + unsigned int uProxLoThresh_default; + unsigned int uIr_Current; + unsigned char uFuseRomData[3]; + unsigned char uMagCntlRegData; + char *pchLibraryBuf; + char chLcdLdi[2]; + int iLibraryLength; + int aiCheckStatus[SENSOR_MAX]; + + unsigned int uComFailCnt; + unsigned int uResetCnt; + unsigned int uTimeOutCnt; + unsigned int uIrqCnt; + unsigned int uDumpCnt; + + int sealevelpressure; + unsigned int uGyroDps; + unsigned int uSensorState; + unsigned int uCurFirmRev; + unsigned int uFactoryProxAvg[4]; + char uLastResumeState; + char uLastAPState; + s32 iPressureCal; + u64 step_count_total; + + atomic_t aSensorEnable; + int64_t adDelayBuf[SENSOR_MAX]; + u64 lastTimestamp[SENSOR_MAX]; + s32 batchLatencyBuf[SENSOR_MAX]; + s8 batchOptBuf[SENSOR_MAX]; + bool reportedData[SENSOR_MAX]; + + int (*wakeup_mcu)(void); + int (*set_mcu_reset)(int); + void (*get_sensor_data[SENSOR_MAX])(char *, int *, + struct sensor_value *); + void (*report_sensor_data[SENSOR_MAX])(struct ssp_data *, + struct sensor_value *); + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif + +#ifdef CONFIG_SENSORS_SSP_SENSORHUB + struct ssp_sensorhub_data *hub_data; +#endif + int ap_rev; + int accel_position; + int mag_position; + u8 mag_matrix_size; + u8 *mag_matrix; + struct mutex comm_mutex; + struct mutex pending_mutex; + struct mutex enable_mutex; + +#if defined(CONFIG_SENSORS_SSP_YAS532) + s16 *static_matrix; +#endif + struct list_head pending_list; + void (*ssp_big_task[BIG_TYPE_MAX])(struct work_struct *); + u64 timestamp; +}; + +struct ssp_big { + struct ssp_data* data; + struct work_struct work; + u32 length; + u32 addr; +}; + +#ifdef CONFIG_SENSORS_SSP_MOBEAM +struct reg_index_table { + unsigned char reg; + unsigned char index; +}; +#endif + +int ssp_iio_configure_ring(struct iio_dev *); +void ssp_iio_unconfigure_ring(struct iio_dev *); +int ssp_iio_probe_trigger(struct ssp_data *, struct iio_dev *, struct iio_trigger *); +void ssp_iio_remove_trigger(struct iio_trigger *); + +void ssp_enable(struct ssp_data *, bool); +int ssp_spi_async(struct ssp_data *, struct ssp_msg *); +int ssp_spi_sync(struct ssp_data *, struct ssp_msg *, int); +void clean_pending_list(struct ssp_data *); +void toggle_mcu_reset(struct ssp_data *); +int initialize_mcu(struct ssp_data *); +int initialize_input_dev(struct ssp_data *); +int initialize_sysfs(struct ssp_data *); +void initialize_function_pointer(struct ssp_data *); +void initialize_accel_factorytest(struct ssp_data *); +void initialize_prox_factorytest(struct ssp_data *); +void initialize_light_factorytest(struct ssp_data *); +void initialize_gyro_factorytest(struct ssp_data *); +void initialize_pressure_factorytest(struct ssp_data *); +void initialize_magnetic_factorytest(struct ssp_data *); +void initialize_gesture_factorytest(struct ssp_data *data); +void initialize_temphumidity_factorytest(struct ssp_data *data); +void remove_accel_factorytest(struct ssp_data *); +void remove_gyro_factorytest(struct ssp_data *); +void remove_prox_factorytest(struct ssp_data *); +void remove_light_factorytest(struct ssp_data *); +void remove_pressure_factorytest(struct ssp_data *); +void remove_magnetic_factorytest(struct ssp_data *); +void remove_gesture_factorytest(struct ssp_data *data); +void remove_temphumidity_factorytest(struct ssp_data *data); +#ifdef CONFIG_SENSORS_SSP_MOBEAM +void initialize_mobeam(struct ssp_data *data); +void remove_mobeam(struct ssp_data *data); +#endif +void sensors_remove_symlink(struct input_dev *); +void destroy_sensor_class(void); +int initialize_event_symlink(struct ssp_data *); +int sensors_create_symlink(struct input_dev *); +int accel_open_calibration(struct ssp_data *); +int gyro_open_calibration(struct ssp_data *); +int pressure_open_calibration(struct ssp_data *); +int proximity_open_calibration(struct ssp_data *); +int check_fwbl(struct ssp_data *); +void remove_input_dev(struct ssp_data *); +void remove_sysfs(struct ssp_data *); +void remove_event_symlink(struct ssp_data *); +int ssp_send_cmd(struct ssp_data *, char, int); +int send_instruction(struct ssp_data *, u8, u8, u8 *, u8); +int send_instruction_sync(struct ssp_data *, u8, u8, u8 *, u8); +int flush(struct ssp_data *, u8); +int get_batch_count(struct ssp_data *, u8); +int select_irq_msg(struct ssp_data *); +int get_chipid(struct ssp_data *); +int set_big_data_start(struct ssp_data *, u8 , u32); +int mag_open_hwoffset(struct ssp_data *); +int mag_store_hwoffset(struct ssp_data *); +int set_hw_offset(struct ssp_data *); +int get_hw_offset(struct ssp_data *); +int set_gyro_cal(struct ssp_data *); +int set_accel_cal(struct ssp_data *); +int initialize_magnetic_sensor(struct ssp_data *data); +int set_sensor_position(struct ssp_data *); +int set_magnetic_static_matrix(struct ssp_data *); +void sync_sensor_state(struct ssp_data *); +void set_proximity_threshold(struct ssp_data *, unsigned int, unsigned int); +void set_proximity_barcode_enable(struct ssp_data *, bool); +void set_gesture_current(struct ssp_data *, unsigned char); +int get_msdelay(int64_t); +unsigned int get_sensor_scanning_info(struct ssp_data *); +unsigned int get_firmware_rev(struct ssp_data *); +int forced_to_download_binary(struct ssp_data *, int); +int parse_dataframe(struct ssp_data *, char *, int); +void enable_debug_timer(struct ssp_data *); +void disable_debug_timer(struct ssp_data *); +int initialize_debug_timer(struct ssp_data *); +void get_proximity_threshold(struct ssp_data *); +void report_meta_data(struct ssp_data *, struct sensor_value *); +void report_acc_data(struct ssp_data *, struct sensor_value *); +void report_gyro_data(struct ssp_data *, struct sensor_value *); +void report_mag_data(struct ssp_data *, struct sensor_value *); +void report_mag_uncaldata(struct ssp_data *, struct sensor_value *); +void report_rot_data(struct ssp_data *, struct sensor_value *); +void report_game_rot_data(struct ssp_data *, struct sensor_value *); +void report_step_det_data(struct ssp_data *, struct sensor_value *); +void report_gesture_data(struct ssp_data *, struct sensor_value *); +void report_pressure_data(struct ssp_data *, struct sensor_value *); +void report_light_data(struct ssp_data *, struct sensor_value *); +void report_prox_data(struct ssp_data *, struct sensor_value *); +void report_prox_raw_data(struct ssp_data *, struct sensor_value *); +void report_geomagnetic_raw_data(struct ssp_data *, struct sensor_value *); +void report_sig_motion_data(struct ssp_data *, struct sensor_value *); +void report_uncalib_gyro_data(struct ssp_data *, struct sensor_value *); +void report_step_cnt_data(struct ssp_data *, struct sensor_value *); +int print_mcu_debug(char *, int *, int); +void report_temp_humidity_data(struct ssp_data *, struct sensor_value *); +void report_shake_cam_data(struct ssp_data *, struct sensor_value *); +void report_bulk_comp_data(struct ssp_data *data); +unsigned int get_module_rev(struct ssp_data *data); +void reset_mcu(struct ssp_data *); +void convert_acc_data(s16 *); +int sensors_register(struct device *, void *, + struct device_attribute*[], char *); +void sensors_unregister(struct device *, + struct device_attribute*[]); +ssize_t mcu_reset_show(struct device *, struct device_attribute *, char *); +ssize_t mcu_dump_show(struct device *, struct device_attribute *, char *); +ssize_t mcu_revision_show(struct device *, struct device_attribute *, char *); +ssize_t mcu_update_ums_bin_show(struct device *, + struct device_attribute *, char *); +ssize_t mcu_update_kernel_bin_show(struct device *, + struct device_attribute *, char *); +ssize_t mcu_update_kernel_crashed_bin_show(struct device *, + struct device_attribute *, char *); +ssize_t mcu_factorytest_store(struct device *, struct device_attribute *, + const char *, size_t); +ssize_t mcu_factorytest_show(struct device *, + struct device_attribute *, char *); +ssize_t mcu_model_name_show(struct device *, + struct device_attribute *, char *); +ssize_t mcu_sleep_factorytest_show(struct device *, + struct device_attribute *, char *); +ssize_t mcu_sleep_factorytest_store(struct device *, + struct device_attribute *, const char *, size_t); +unsigned int ssp_check_sec_dump_mode(void); + +void ssp_dump_task(struct work_struct *work); +void ssp_read_big_library_task(struct work_struct *work); +void ssp_send_big_library_task(struct work_struct *work); +void ssp_pcm_dump_task(struct work_struct *work); +void ssp_temp_task(struct work_struct *work); + +int callback_bbd_on_control(void *ssh_data, char *str_ctrl); +int callback_bbd_on_mcu_ready(void *ssh_data, bool ready); +int callback_bbd_on_packet_alarm(void *ssh_data); +void bbd_on_packet_work_func(struct work_struct *work); +void bbd_mcu_ready_work_func(struct work_struct *work); +#ifdef SSP_BBD_USE_SEND_WORK +void bbd_send_packet_work_func(struct work_struct *work); +#endif /* SSP_BBD_USE_SEND_WORK */ +int set_time(struct ssp_data *); +int get_time(struct ssp_data *); +#endif diff --git a/drivers/sensors/brcm/ssp_bbd.c b/drivers/sensors/brcm/ssp_bbd.c new file mode 100644 index 0000000..8af8ddf --- /dev/null +++ b/drivers/sensors/brcm/ssp_bbd.c @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ + +#include "ssp.h" + + +bool ssp_dbg; +bool ssp_pkt_dbg; + +#define dprint(fmt, args...) \ + if (unlikely(ssp_dbg)) printk(KERN_INFO "[SSPBBD]:(%s:%d): " fmt, __func__,__LINE__, ##args) + +#define DEBUG_SHOW_HEX_SEND(msg, len) \ + if (unlikely(ssp_pkt_dbg)) print_hex_dump(KERN_INFO, "SSP->MCU: ", DUMP_PREFIX_NONE, 16, 1, (msg), (len), true); +#define DEBUG_SHOW_HEX_RECV(msg, len) \ + if (unlikely(ssp_pkt_dbg)) print_hex_dump(KERN_INFO, "SSP<-MCU: ", DUMP_PREFIX_NONE, 16, 1, (msg), (len), true); + +extern void clean_msg(struct ssp_msg *msg); + +/** + * transfer ssp data to mcu thru bbd driver. + * + * @data: ssp data pointer + * @msg: ssp message + * @done: completion + * @timeout: wait response time (ms) + * + * @return: 1 = success, -1 = failed to send data to bbd + * -2 = failed to get response from mcu + */ +int bbd_do_transfer(struct ssp_data *data, struct ssp_msg *msg, + struct completion *done, int timeout) { + int status = 0; + bool msg_dead = false, ssp_down = false; + bool use_no_irq = false; + + if(data == NULL || msg == NULL) { + pr_err("%s():[SSPBBD] data or msg is NULL\n", __func__); + return -1; + } + + ssp_down = data->bSspShutdown; + + if (ssp_down) { + pr_err("[SSPBBD]: ssp_down == true. returning\n"); + clean_msg(msg); + mdelay(5); + return -1; + } + + msg->dead_hook = &msg_dead; + msg->dead = false; + msg->done = done; + use_no_irq = (msg->length == 0); + + mutex_lock(&data->comm_mutex); + + mutex_lock(&data->pending_mutex); + + if (bbd_send_packet((unsigned char *)msg, 9) > 0) { + status = 1; + DEBUG_SHOW_HEX_SEND(msg, 9) + } else { + pr_err("[SSP]: %s bbd_send_packet fail!!\n", __func__); + data->uTimeOutCnt++; + clean_msg(msg); + mutex_unlock(&data->pending_mutex); + mutex_unlock(&data->comm_mutex); + return -1; + } + + if (!use_no_irq) { + list_add_tail(&msg->list, &data->pending_list); + } + + mutex_unlock(&data->pending_mutex); + + if (status == 1 && done != NULL) { + dprint("waiting completion ...\n"); + if (wait_for_completion_timeout(done, msecs_to_jiffies(timeout)) == 0) { + pr_err("[SSPBBD] : %s() : completion is timeout !\n", __func__); + status = -2; + if (!use_no_irq && !msg_dead) { + mutex_lock(&data->pending_mutex); + if (msg->list.next!=NULL && msg->list.next!=LIST_POISON1) + list_del(&msg->list); + mutex_unlock(&data->pending_mutex); + } + }else{ + dprint("completion is cleared !\n"); + } + } + + mutex_lock(&data->pending_mutex); + if (msg != NULL && !msg_dead) { + msg->done = NULL; + msg->dead_hook = NULL; + + if (status != 1) + msg->dead = true; + if (status == -2) + data->uTimeOutCnt++; + } + mutex_unlock(&data->pending_mutex); + + if (use_no_irq) + clean_msg(msg); + + mutex_unlock(&data->comm_mutex); + + return status; +} + +/**************************************************************** + * + * Callback fucntios + * + ****************************************************************/ + +/** + * callback function: + * called this function by bbdpl when packet comes from MCU + * + * @ssh_data: ssh data pointer + * + * @return: 0 = success, -1 = failure + * + */ +int callback_bbd_on_packet_alarm(void *ssh_data) +{ + struct ssp_data *data = (struct ssp_data *)ssh_data; + + if(ssh_data == NULL) + return -1; + if(queue_work(data->bbd_on_packet_wq, &data->work_bbd_on_packet)){ + /* in case of adding the work to queue */ + dprint("queue_work ok!!\n"); + return 0; + } + + return 0; +} + +/** + * callback function + * called this function by bbdpl whenever mcu is(not) ready + * + * @ssh_data: ssh data pointer + * @ready: true = ready, false = not ready + * + * @return: 0 = success, -1 = failure + */ +int callback_bbd_on_mcu_ready(void *ssh_data, bool ready) +{ + struct ssp_data *data = (struct ssp_data *)ssh_data; + + if(data == NULL) + return -1; + + if(ready == true) { + /* Start queue work for initializing MCU */ + queue_work(data->bbd_mcu_ready_wq, &data->work_bbd_mcu_ready); + }else{ + /* Disable SSP */ + ssp_enable(data, false); + dprint("MCU is not ready and disabled SSP\n"); + } + + return 0; +} + +/** + * callback function + * called this function by bbdpl when received control command from LHD + * + * @ssh_data: ssh data pointer + * @str_ctrl: string type control command + * + * @return: 0 = success, -1 = failure + */ +int callback_bbd_on_control(void *ssh_data, char *str_ctrl) +{ + if(!ssh_data || !str_ctrl) + return -1; + + dprint("Received string command from LHD(=%s)\n", str_ctrl); + + return 0; +} + +/**************************************************************** + * + * Work queue fucntios + * + ****************************************************************/ + +/** + * Work queue function for MCU ready + * This is called by bbdpl when MCU is ready and then + * initialize MCU + + * @work: work structure + * + * @return: none + */ +void bbd_mcu_ready_work_func(struct work_struct *work) +{ + struct ssp_data *data = container_of(work, struct ssp_data, work_bbd_mcu_ready); + int ret = 0; + int retries = 0; + + msleep(1000); + dprint("MCU is ready.(work_queue)\n"); + + clean_pending_list(data); + + ssp_enable(data, true); +retries: + ret = initialize_mcu(data); + if (ret != SUCCESS) { + data->uResetCnt++; + mdelay(100); + if(++retries > 3) { + pr_err("[SSPBBD] fail to initialize mcu\n"); + ssp_enable(data, false); + return; + } + goto retries; + } + dprint("mcu is initiialized (retries=%d)\n", retries); + + /* recover previous state */ + sync_sensor_state(data); + ssp_sensorhub_report_notice(data, MSG2SSP_AP_STATUS_RESET); + + if (data->uLastAPState != 0) + ssp_send_cmd(data, data->uLastAPState, 0); + if (data->uLastResumeState != 0) + ssp_send_cmd(data, data->uLastResumeState, 0); +} + +/** + * This work queue fucntion starts from alarm callback function + * + * @work: work structure + * + * @return: none + */ +#define BBD_PULL_TIMEOUT 1000 /* the timeout for getting data from bbdpl */ + +unsigned char rBuff[BBD_MAX_DATA_SIZE] = {-1}; + +void bbd_on_packet_work_func(struct work_struct *work) +{ + struct ssp_data *data = container_of(work, struct ssp_data, work_bbd_on_packet); + unsigned short chLength = 0, msg_options = 0; + unsigned char msg_type = 0; + int iRet = 0; + unsigned char *pData = NULL, *p, *q; + int nDataLen = 0; + struct timespec ts; + ts = ktime_to_timespec(ktime_get_boottime()); + data->timestamp = ts.tv_sec * 1000000000ULL + ts.tv_nsec; + + iRet = bbd_pull_packet(rBuff, sizeof(rBuff), BBD_PULL_TIMEOUT); + if (iRet <= 0) { + pr_err("[SSP]: %s bbd_pull_packet fail!! (iRet=%d)\n", __func__, iRet); + return; + } + + p = rBuff; + q = rBuff + iRet; + +process_one: + DEBUG_SHOW_HEX_RECV(p, 4) + + memcpy(&msg_options, p, 2); p+=2; + msg_type = msg_options & SSP_SPI_MASK; + memcpy(&chLength, p, 2); p+=2; + pData = p; /* pData points current frame's data */ + if (msg_type == AP2HUB_READ || msg_type == HUB2AP_WRITE) + p += chLength; /* now p points next frame */ + nDataLen = q - pData; + + switch (msg_type) { + case AP2HUB_READ: + case AP2HUB_WRITE: + mutex_lock(&data->pending_mutex); + if (!list_empty(&data->pending_list)) { + struct ssp_msg *msg, *n; + bool found = false; + + list_for_each_entry_safe(msg, n, &data->pending_list, list) + { + if (msg->options == msg_options) { + list_del(&msg->list); + found = true; + break; + } + } + + if (!found) { + pr_err("[SSP]: %s %d - Not match error\n", __func__, msg_options); + goto exit; + } + + if (msg->dead && !msg->free_buffer) { + msg->buffer = (char*) kzalloc(msg->length, GFP_KERNEL); + msg->free_buffer = 1; + } /* For dead msg, make a temporary buffer to read. */ + + if(msg->buffer == NULL) { + pr_err("[SSPBBD]: %s() : msg->buffer is NULL\n", __func__); + goto exit; + } + if (msg_type == AP2HUB_READ) { + if(nDataLen <= 0) { + dprint("Waiting 2nd message...(msg=%p, length=%d)\n", + msg, msg->length); + iRet = bbd_pull_packet(msg->buffer, msg->length, + BBD_PULL_TIMEOUT); + dprint("Received 2nd message. (iRet=%d)\n", iRet); + }else{ + memcpy(msg->buffer, pData, msg->length); + nDataLen -= msg->length; + } + DEBUG_SHOW_HEX_RECV(msg->buffer, msg->length) + } + if (msg_type == AP2HUB_WRITE) { + iRet = bbd_send_packet(msg->buffer, msg->length); + if(iRet <= 0) { + pr_err("[SSP]: %s bbd_send_packet fail!!(AP2HUB_WRITE)\n", + __func__); + goto exit; + } + + DEBUG_SHOW_HEX_SEND(msg->buffer, msg->length) + + if (msg_options & AP2HUB_RETURN) { + msg->options = AP2HUB_READ | AP2HUB_RETURN; + msg->length = 1; + list_add_tail(&msg->list, &data->pending_list); + goto exit; + } + } + if (msg->done != NULL && !completion_done(msg->done)){ + dprint("complete(mg->done)\n"); + complete(msg->done); + } + if (msg->dead_hook != NULL) + *(msg->dead_hook) = true; + + clean_msg(msg); + } else + pr_err("[SSP]List empty error(%d)\n", msg_type); +exit: + mutex_unlock(&data->pending_mutex); + break; + case HUB2AP_WRITE: + { + char* buffer = (char*) kzalloc(chLength, GFP_KERNEL); + if (buffer == NULL) { + pr_err("[SSP] %s, failed to alloc memory for buffer\n", __func__); + iRet = -ENOMEM; + break; + } + if(nDataLen <= 0) { + dprint("Waiting 2nd message...(chLength=%d)\n", chLength); + iRet = bbd_pull_packet(buffer, chLength, BBD_PULL_TIMEOUT); + dprint("Received 2nd message. (iRet=%d)\n", iRet); + }else{ + memcpy(buffer, pData, chLength); + iRet = chLength; + } + DEBUG_SHOW_HEX_RECV(buffer, chLength) + if (iRet < 0) + pr_err("[SSP] %s bbd_pull_packet fail.(iRet=%d)\n", __func__,iRet); + else + parse_dataframe(data, buffer, chLength); + kfree(buffer); + break; + } + default: + pr_err("[SSP]No type error(%d)\n", msg_type); + break; + } + + if (iRet < 0) { + pr_err("[SSP]: %s - MSG2SSP_SSD error %d\n", __func__, iRet); + } + + if (p +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ssp.h" + +#define VENDOR_NAME "GENICOM" +#define CHIP_NAME "GUVA-C12SD" + +#ifdef CONFIG_HAS_EARLYSUSPEND +/* early suspend */ +static void ssp_early_suspend(struct early_suspend *handler); +static void ssp_late_resume(struct early_suspend *handler); +#endif + +struct uv_info { + struct uv_platform_data *pdata; + struct workqueue_struct *uv_wq; + struct work_struct work_uv; + struct device *uv_dev; + struct hrtimer uv_timer; + struct mutex power_lock; + struct mutex read_lock; + struct input_dev *uv_input_dev; + struct platform_device *pdev_uv_adc; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif + ktime_t uv_poll_delay; + int uv_raw_data; + bool onoff; +}; + +/* sysfs for input device */ +static ssize_t uv_poll_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uv_info *uv = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%lld\n", + ktime_to_ns(uv->uv_poll_delay)); +} + +static ssize_t uv_poll_delay_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct uv_info *uv = dev_get_drvdata(dev); + int64_t new_delay; + int err; + + err = kstrtoll(buf, 10, &new_delay); + if (err < 0) + return err; + + mutex_lock(&uv->power_lock); + if (uv->onoff) { + hrtimer_cancel(&uv->uv_timer); + cancel_work_sync(&uv->work_uv); + } + + if (new_delay != ktime_to_ns(uv->uv_poll_delay)) { + uv->uv_poll_delay = ns_to_ktime(new_delay); + pr_info("%s, poll_delay = %lld\n", __func__, new_delay); + } + + if (uv->onoff) + hrtimer_start(&uv->uv_timer, uv->uv_poll_delay, + HRTIMER_MODE_REL); + mutex_unlock(&uv->power_lock); + + return size; +} + +static ssize_t uv_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct uv_info *uv = dev_get_drvdata(dev); + bool new_value; + + if (sysfs_streq(buf, "1")) { + new_value = true; + } else if (sysfs_streq(buf, "0")) { + new_value = false; + } else { + pr_err("%s: invalid value %d\n", __func__, *buf); + return -EINVAL; + } + pr_info("%s, old = %d, new = %d\n", __func__, uv->onoff, new_value); + mutex_lock(&uv->power_lock); + if (new_value && !uv->onoff) { /* Enable */ + uv->onoff = new_value; + if (uv->pdata->power_on) + uv->pdata->power_on(uv->onoff); + hrtimer_start(&uv->uv_timer, uv->uv_poll_delay, + HRTIMER_MODE_REL); + } else if (!new_value && uv->onoff) { /* Disable */ + hrtimer_cancel(&uv->uv_timer); + uv->onoff = new_value; + if (uv->pdata->power_on) + uv->pdata->power_on(uv->onoff); + } else + pr_err("%s, new_enable = %d\n", __func__, new_value); + mutex_unlock(&uv->power_lock); + + return size; +} + +static ssize_t uv_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uv_info *uv = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", uv->onoff); +} + +static DEVICE_ATTR(poll_delay, S_IRUGO | S_IWUSR | S_IWGRP, + uv_poll_delay_show, uv_poll_delay_store); +static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP, + uv_enable_show, uv_enable_store); + +static struct attribute *uv_sysfs_attrs[] = { + &dev_attr_enable.attr, + &dev_attr_poll_delay.attr, + NULL +}; + +static struct attribute_group uv_attribute_group = { + .attrs = uv_sysfs_attrs, +}; + +/* sysfs for uv sensor class */ +static ssize_t get_vendor_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR_NAME); +} + +static ssize_t get_chip_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_NAME); +} + +static ssize_t get_adc_value(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uv_info *uv = dev_get_drvdata(dev); + int adc = 0; + /* If power is not turned on, it won't work. */ + if (!uv->onoff) { + mutex_lock(&uv->read_lock); + if (uv->pdata->power_on) + uv->pdata->power_on(true); + mdelay(20); + adc = uv->pdata->get_adc_value(); + if (uv->pdata->power_on) + uv->pdata->power_on(false); + mutex_unlock(&uv->read_lock); + } else + adc = uv->uv_raw_data; + pr_info("%s: uv_adc = 0x%x, (%dmV)\n", __func__, adc, adc / 1000); + + return snprintf(buf, PAGE_SIZE, "%d\n", adc); +} + +static ssize_t uv_power_on(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uv_info *uv = dev_get_drvdata(dev); + + if (!uv->onoff && uv->pdata->power_on) + uv->pdata->power_on(true); + pr_info("%s\n", __func__); + + return sprintf(buf, "%d\n", 1); +} + +static ssize_t uv_power_off(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uv_info *uv = dev_get_drvdata(dev); + + if (!uv->onoff && uv->pdata->power_on) + uv->pdata->power_on(false); + pr_info("%s\n", __func__); + + return sprintf(buf, "%d\n", 1); +} + +static DEVICE_ATTR(vendor, S_IRUSR | S_IRGRP, get_vendor_name, NULL); +static DEVICE_ATTR(name, S_IRUSR | S_IRGRP, get_chip_name, NULL); +static DEVICE_ATTR(raw_data, S_IRUSR | S_IRGRP, get_adc_value, NULL); +static DEVICE_ATTR(power_on, S_IRUSR | S_IRGRP, uv_power_on, NULL); +static DEVICE_ATTR(power_off, S_IRUSR | S_IRGRP, uv_power_off, NULL); + +static struct device_attribute *uv_sensor_attrs[] = { + &dev_attr_vendor, + &dev_attr_name, + &dev_attr_raw_data, + &dev_attr_power_on, + &dev_attr_power_off, + NULL +}; + +/* timer function for uv sensor polling */ +static enum hrtimer_restart uv_timer_func(struct hrtimer *timer) +{ + struct uv_info *uv + = container_of(timer, struct uv_info, uv_timer); + queue_work(uv->uv_wq, &uv->work_uv); + hrtimer_forward_now(&uv->uv_timer, uv->uv_poll_delay); + return HRTIMER_RESTART; +} + +static void work_func_uv(struct work_struct *work) +{ + struct uv_info *uv + = container_of(work, struct uv_info, work_uv); + + mutex_lock(&uv->read_lock); + uv->uv_raw_data = uv->pdata->get_adc_value(); + mutex_unlock(&uv->read_lock); + + input_report_rel(uv->uv_input_dev, REL_MISC, uv->uv_raw_data+1); + input_sync(uv->uv_input_dev); +} + +static int uv_probe(struct platform_device *pdev) +{ + struct uv_info *uv; + struct uv_platform_data *pdata = pdev->dev.platform_data; + int ret = 0; + + pr_info("%s: is started\n", __func__); + if (!pdata) { + pr_err("%s: pdata is NULL\n", __func__); + return -ENODEV; + } + + /* allocate memory */ + uv = kzalloc(sizeof(struct uv_info), GFP_KERNEL); + if (uv == NULL) { + pr_err("%s: Failed to allocate memory\n", __func__); + return -ENOMEM; + } + + uv->pdata = pdata; + + /* Setup for ADC */ + /* alloc platform device for adc client */ + uv->pdev_uv_adc = platform_device_alloc("uv-adc", -1); + if (!uv->pdev_uv_adc) { + pr_err("%s: could not allocation uv-adc\n", __func__); + goto err_alloc_pdev; + } + + if (pdata->adc_ap_init && pdata->adc_ap_exit) { + uv->pdata->adc_ap_init = pdata->adc_ap_init; + uv->pdata->adc_ap_exit = pdata->adc_ap_exit; + ret = pdata->adc_ap_init(uv->pdev_uv_adc); + if (!ret) { + ret = -1; + goto err_setup_adc; + } + } + + if (pdata->power_on) { + uv->pdata->power_on = pdata->power_on; + uv->pdata->power_on(false); + } + uv->onoff = false; + + if (pdata->get_adc_value) + uv->pdata->get_adc_value = pdata->get_adc_value; + + mutex_init(&uv->power_lock); + mutex_init(&uv->read_lock); + + /* allocate uv input device */ + uv->uv_input_dev = input_allocate_device(); + if (!uv->uv_input_dev) { + pr_err("%s: could not allocate input device\n", + __func__); + goto err_input_allocate_device_uv; + } + + input_set_drvdata(uv->uv_input_dev, uv); + uv->uv_input_dev->name = "uv_sensor"; + input_set_capability(uv->uv_input_dev, EV_REL, REL_MISC); + + ret = input_register_device(uv->uv_input_dev); + if (ret < 0) { + pr_err("%s: could not register input device\n", + __func__); + input_free_device(uv->uv_input_dev); + goto err_input_register_device_uv; + } + + ret = sysfs_create_group(&uv->uv_input_dev->dev.kobj, + &uv_attribute_group); + if (ret) { + pr_err("%s: could not create sysfs group\n", + __func__); + goto err_sysfs_create_group_uv; + } + + /* timer init */ + hrtimer_init(&uv->uv_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + uv->uv_poll_delay = ns_to_ktime(200 * NSEC_PER_MSEC); + uv->uv_timer.function = uv_timer_func; + + /* workqueue init */ + uv->uv_wq = create_singlethread_workqueue("uv_wq"); + if (!uv->uv_wq) { + ret = -ENOMEM; + pr_err("%s: could not create uv workqueue\n", + __func__); + goto err_create_uv_workqueue; + } + INIT_WORK(&uv->work_uv, work_func_uv); + + /* sysfs for uv sensor */ + ret = sensors_register(uv->uv_dev, uv, uv_sensor_attrs, + "uv_sensor"); + if (ret) { + pr_err("%s: could not register uv device(%d)\n", + __func__, ret); + goto err_sensor_register_failed; + } + + ret = sensors_create_symlink(uv->uv_input_dev); + if (ret < 0) { + pr_err("%s, sensors_create_symlinks failed!(%d)\n", + __func__, ret); + goto err_uv_input__sysfs_create_link; + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + uv->early_suspend.suspend = ssp_early_suspend; + uv->early_suspend.resume = ssp_late_resume; + register_early_suspend(&uv->early_suspend); +#endif + + platform_set_drvdata(pdev, uv); + + pr_info("%s, success\n", __func__); + return 0; +err_uv_input__sysfs_create_link: + sensors_unregister(uv->uv_dev, uv_sensor_attrs); +err_sensor_register_failed: + destroy_workqueue(uv->uv_wq); +err_create_uv_workqueue: + sysfs_remove_group(&uv->uv_input_dev->dev.kobj, + &uv_attribute_group); +err_sysfs_create_group_uv: + input_unregister_device(uv->uv_input_dev); +err_input_register_device_uv: +err_input_allocate_device_uv: + mutex_destroy(&uv->read_lock); + mutex_destroy(&uv->power_lock); + if (uv->pdata->adc_ap_exit) + uv->pdata->adc_ap_exit(uv->pdev_uv_adc); +err_setup_adc: + if (uv->pdev_uv_adc) + platform_device_put(uv->pdev_uv_adc); +err_alloc_pdev: + kfree(uv); + return ret; +} + +static int uv_remove(struct platform_device *pdev) +{ + struct uv_info *uv = dev_get_drvdata(&pdev->dev); + + pr_info("%s+\n", __func__); + if (uv->onoff) { + hrtimer_cancel(&uv->uv_timer); + cancel_work_sync(&uv->work_uv); + } + destroy_workqueue(uv->uv_wq); + sensors_remove_symlink(uv->uv_input_dev); + +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&uv->early_suspend); +#endif + sensors_unregister(uv->uv_dev, uv_sensor_attrs); + sysfs_remove_group(&uv->uv_input_dev->dev.kobj, + &uv_attribute_group); + input_unregister_device(uv->uv_input_dev); + + if (uv->onoff && uv->pdata->power_on) + uv->pdata->power_on(false); + if (uv->pdata->adc_ap_exit) + uv->pdata->adc_ap_exit(uv->pdev_uv_adc); + if (uv->pdev_uv_adc) + platform_device_put(uv->pdev_uv_adc); + mutex_destroy(&uv->read_lock); + mutex_destroy(&uv->power_lock); + kfree(uv); + pr_info("%s-\n", __func__); + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +/* early suspend */ +static void ssp_early_suspend(struct early_suspend *handler) +{ + struct uv_info *uv; + uv = container_of(handler, struct uv_info, early_suspend); + + if (uv->onoff) { + hrtimer_cancel(&uv->uv_timer); + cancel_work_sync(&uv->work_uv); + if (uv->pdata->power_on) + uv->pdata->power_on(false); + } + pr_err("%s, enabled = %d\n", __func__, uv->onoff); +} + +static void ssp_late_resume(struct early_suspend *handler) +{ + struct uv_info *uv; + uv = container_of(handler, struct uv_info, early_suspend); + + if (uv->onoff) { + if (uv->pdata->power_on) + uv->pdata->power_on(true); + hrtimer_start(&uv->uv_timer, + uv->uv_poll_delay, HRTIMER_MODE_REL); + } + pr_err("%s, enabled = %d\n", __func__, uv->onoff); +} +#else +static int uv_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct uv_info *uv = dev_get_drvdata(&pdev->dev); + + if (uv->onoff) { + hrtimer_cancel(&uv->uv_timer); + cancel_work_sync(&uv->work_uv); + if (uv->pdata->power_on) + uv->pdata->power_on(false); + } + pr_err("%s, enabled = %d\n", __func__, uv->onoff); + return 0; +} + +static int uv_resume(struct platform_device *pdev) +{ + struct uv_info *uv = dev_get_drvdata(&pdev->dev); + + if (uv->onoff) { + if (uv->pdata->power_on) + uv->pdata->power_on(true); + hrtimer_start(&uv->uv_timer, + uv->uv_poll_delay, HRTIMER_MODE_REL); + } + pr_err("%s, enabled = %d\n", __func__, uv->onoff); + return 0; +} +#endif + +static void uv_shutdown(struct platform_device *pdev) +{ + struct uv_info *uv = dev_get_drvdata(&pdev->dev); + + pr_info("%s+\n", __func__); + if (uv->onoff) { + hrtimer_cancel(&uv->uv_timer); + cancel_work_sync(&uv->work_uv); + } + destroy_workqueue(uv->uv_wq); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&uv->early_suspend); +#endif + sensors_unregister(uv->uv_dev, uv_sensor_attrs); + sysfs_remove_group(&uv->uv_input_dev->dev.kobj, + &uv_attribute_group); + input_unregister_device(uv->uv_input_dev); + + if (uv->onoff && uv->pdata->power_on) + uv->pdata->power_on(false); + if (uv->pdata->adc_ap_exit) + uv->pdata->adc_ap_exit(uv->pdev_uv_adc); + if (uv->pdev_uv_adc) + platform_device_put(uv->pdev_uv_adc); + mutex_destroy(&uv->read_lock); + mutex_destroy(&uv->power_lock); + kfree(uv); + pr_info("%s-\n", __func__); +} + +static struct platform_driver uv_driver = { + .probe = uv_probe, + .remove = uv_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = uv_suspend, + .resume = uv_resume, +#endif + .shutdown = uv_shutdown, + .driver = { + .name = "uv_sensor", + .owner = THIS_MODULE, + }, +}; + +static int __init uv_init(void) +{ + return platform_driver_register(&uv_driver); +} + +static void __exit uv_exit(void) +{ + platform_driver_unregister(&uv_driver); +} + +module_init(uv_init); +module_exit(uv_exit); + +MODULE_DESCRIPTION("UV sensor device driver"); +MODULE_AUTHOR("OHeon Kwon "); +MODULE_LICENSE("GPL"); diff --git a/drivers/sensors/brcm/ssp_data.c b/drivers/sensors/brcm/ssp_data.c new file mode 100644 index 0000000..64bed04f --- /dev/null +++ b/drivers/sensors/brcm/ssp_data.c @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ +#include "ssp.h" +#include +#include + +/* SSP -> AP Instruction */ +#define MSG2AP_INST_BYPASS_DATA 0x37 +#define MSG2AP_INST_LIBRARY_DATA 0x01 +#define MSG2AP_INST_DEBUG_DATA 0x03 +#define MSG2AP_INST_BIG_DATA 0x04 +#define MSG2AP_INST_META_DATA 0x05 +#define MSG2AP_INST_TIME_SYNC 0x06 +#define MSG2AP_INST_RESET 0x07 + +/*************************************************************************/ +/* SSP parsing the dataframe */ +/*************************************************************************/ + +static void generate_data(struct ssp_data *data, + struct sensor_value *sensorsdata, int iSensorData, u64 timestamp) +{ + u64 move_timestamp = data->lastTimestamp[iSensorData]; + if ((iSensorData != PROXIMITY_SENSOR) && (iSensorData != GESTURE_SENSOR) + && (iSensorData != STEP_DETECTOR) && (iSensorData != SIG_MOTION_SENSOR) + && (iSensorData != STEP_COUNTER)) { + while ((move_timestamp * 10 + data->adDelayBuf[iSensorData] * 15) < (timestamp * 10)) { + move_timestamp += data->adDelayBuf[iSensorData]; + sensorsdata->timestamp = move_timestamp; + data->report_sensor_data[iSensorData](data, sensorsdata); + } + } +} + +static void get_timestamp(struct ssp_data *data, char *pchRcvDataFrame, + int *iDataIdx, struct sensor_value *sensorsdata, + struct ssp_time_diff *sensortime, int iSensorData) +{ + if (sensortime->batch_mode == BATCH_MODE_RUN) { + if (sensortime->batch_count == sensortime->batch_count_fixed) { + if (sensortime->time_diff == data->adDelayBuf[iSensorData]) { + generate_data(data, sensorsdata, iSensorData, + (data->timestamp - data->adDelayBuf[iSensorData] * (sensortime->batch_count_fixed - 1))); + } + sensorsdata->timestamp = data->timestamp - ((sensortime->batch_count - 1) * sensortime->time_diff); + } else { + if (sensortime->batch_count > 1) + sensorsdata->timestamp = data->timestamp - ((sensortime->batch_count - 1) * sensortime->time_diff); + else + sensorsdata->timestamp = data->timestamp; + } + } else { + if (((sensortime->irq_diff * 10) > (data->adDelayBuf[iSensorData] * 18)) + && ((sensortime->irq_diff * 10) < (data->adDelayBuf[iSensorData] * 100))) { + generate_data(data, sensorsdata, iSensorData, data->timestamp); + } + sensorsdata->timestamp = data->timestamp; + } + *iDataIdx += 4; +} + +static void get_3axis_sensordata(char *pchRcvDataFrame, int *iDataIdx, + struct sensor_value *sensorsdata) +{ + memcpy(sensorsdata, pchRcvDataFrame + *iDataIdx, 6); + *iDataIdx += 6; +} + +static void get_uncalib_sensordata(char *pchRcvDataFrame, int *iDataIdx, + struct sensor_value *sensorsdata) +{ + memcpy(sensorsdata, pchRcvDataFrame + *iDataIdx, 12); + *iDataIdx += 12; +} + +static void get_geomagnetic_uncaldata(char *pchRcvDataFrame, int *iDataIdx, + struct sensor_value *sensorsdata) +{ + memcpy(sensorsdata, pchRcvDataFrame + *iDataIdx, 12); + *iDataIdx += 12; +} + +static void get_geomagnetic_rawdata(char *pchRcvDataFrame, int *iDataIdx, + struct sensor_value *sensorsdata) +{ + memcpy(sensorsdata, pchRcvDataFrame + *iDataIdx, 6); + *iDataIdx += 6; +} + +static void get_geomagnetic_caldata(char *pchRcvDataFrame, int *iDataIdx, + struct sensor_value *sensorsdata) +{ + memcpy(sensorsdata, pchRcvDataFrame + *iDataIdx, 7); + *iDataIdx += 7; +} + +static void get_rot_sensordata(char *pchRcvDataFrame, int *iDataIdx, + struct sensor_value *sensorsdata) +{ + memcpy(sensorsdata, pchRcvDataFrame + *iDataIdx, 17); + *iDataIdx += 17; +} + +static void get_step_det_sensordata(char *pchRcvDataFrame, int *iDataIdx, + struct sensor_value *sensorsdata) +{ + memcpy(sensorsdata, pchRcvDataFrame + *iDataIdx, 1); + *iDataIdx += 1; +} + +static void get_light_sensordata(char *pchRcvDataFrame, int *iDataIdx, + struct sensor_value *sensorsdata) +{ +#if defined(CONFIG_SENSORS_SSP_TMG399X) + memcpy(sensorsdata, pchRcvDataFrame + *iDataIdx, 10); + *iDataIdx += 10; +#else + memcpy(sensorsdata, pchRcvDataFrame + *iDataIdx, 8); + *iDataIdx += 8; +#endif +} + +static void get_pressure_sensordata(char *pchRcvDataFrame, int *iDataIdx, + struct sensor_value *sensorsdata) +{ + s16 temperature = 0; + memcpy(&sensorsdata->pressure[0], pchRcvDataFrame + *iDataIdx, 4); + memcpy(&temperature, pchRcvDataFrame + *iDataIdx + 4, 2); + sensorsdata->pressure[1] = temperature; + *iDataIdx += 6; +} + +static void get_gesture_sensordata(char *pchRcvDataFrame, int *iDataIdx, + struct sensor_value *sensorsdata) +{ + memcpy(sensorsdata, pchRcvDataFrame + *iDataIdx, 20); + *iDataIdx += 20; +} + +static void get_proximity_sensordata(char *pchRcvDataFrame, int *iDataIdx, + struct sensor_value *sensorsdata) +{ + memset(&sensorsdata->prox[0], 0, 1); + memcpy(&sensorsdata->prox[0], pchRcvDataFrame + *iDataIdx, 2); + *iDataIdx += 2; +} + +static void get_proximity_rawdata(char *pchRcvDataFrame, int *iDataIdx, + struct sensor_value *sensorsdata) +{ + memcpy(&sensorsdata->prox[0], pchRcvDataFrame + *iDataIdx, 1); + *iDataIdx += 1; +} + +static void get_temp_humidity_sensordata(char *pchRcvDataFrame, int *iDataIdx, + struct sensor_value *sensorsdata) +{ + memset(&sensorsdata->data[2], 0, 2); + memcpy(sensorsdata, pchRcvDataFrame + *iDataIdx, 5); + *iDataIdx += 5; +} + +static void get_sig_motion_sensordata(char *pchRcvDataFrame, int *iDataIdx, + struct sensor_value *sensorsdata) +{ + memcpy(sensorsdata, pchRcvDataFrame + *iDataIdx, 1); + *iDataIdx += 1; +} + +static void get_step_cnt_sensordata(char *pchRcvDataFrame, int *iDataIdx, + struct sensor_value *sensorsdata) +{ + memcpy(&sensorsdata->step_diff, pchRcvDataFrame + *iDataIdx, 4); + *iDataIdx += 4; +} + +static void get_shake_cam_sensordata(char *pchRcvDataFrame, int *iDataIdx, + struct sensor_value *sensorsdata) +{ + memcpy(sensorsdata, pchRcvDataFrame + *iDataIdx, 1); + *iDataIdx += 1; +} + +int handle_big_data(struct ssp_data *data, char *pchRcvDataFrame, int *pDataIdx) { + u8 bigType = 0; + struct ssp_big *big = kzalloc(sizeof(*big), GFP_KERNEL); + big->data = data; + bigType = pchRcvDataFrame[(*pDataIdx)++]; + memcpy(&big->length, pchRcvDataFrame + *pDataIdx, 4); + *pDataIdx += 4; + memcpy(&big->addr, pchRcvDataFrame + *pDataIdx, 4); + *pDataIdx += 4; + + if (bigType >= BIG_TYPE_MAX) { + kfree(big); + return FAIL; + } + + INIT_WORK(&big->work, data->ssp_big_task[bigType]); + queue_work(data->debug_wq, &big->work); + return SUCCESS; +} + +int parse_dataframe(struct ssp_data *data, char *pchRcvDataFrame, int iLength) +{ + int iDataIdx, iSensorData; + u16 length = 0; + struct sensor_value sensorsdata; + struct ssp_time_diff sensortime; + + sensortime.time_diff = 0; + data->uIrqCnt++; + + for (iDataIdx = 0; iDataIdx < iLength;) { + switch (pchRcvDataFrame[iDataIdx++]) { + case MSG2AP_INST_BYPASS_DATA: + iSensorData = pchRcvDataFrame[iDataIdx++]; + if ((iSensorData < 0) || (iSensorData >= SENSOR_MAX)) { + pr_err("[SSP]: %s - Mcu data frame1 error %d\n", __func__, + iSensorData); + return ERROR; + } + + memcpy(&length, pchRcvDataFrame + iDataIdx, 2); + iDataIdx += 2; + sensortime.batch_count = sensortime.batch_count_fixed = length; + sensortime.batch_mode = length > 1 ? BATCH_MODE_RUN : BATCH_MODE_NONE; + sensortime.irq_diff = data->timestamp - data->lastTimestamp[iSensorData]; + + if (sensortime.batch_mode == BATCH_MODE_RUN) { + if (data->reportedData[iSensorData] == true) { + u64 time; + sensortime.time_diff = div64_long((s64)(data->timestamp - data->lastTimestamp[iSensorData]), (s64)length); + if (length > 8) + time = data->adDelayBuf[iSensorData] * 18; + else if (length > 4) + time = data->adDelayBuf[iSensorData] * 25; + else if (length > 2) + time = data->adDelayBuf[iSensorData] * 50; + else + time = data->adDelayBuf[iSensorData] * 100; + if ((sensortime.time_diff * 10) > time) { + data->lastTimestamp[iSensorData] = data->timestamp - (data->adDelayBuf[iSensorData] * length); + sensortime.time_diff = data->adDelayBuf[iSensorData]; + } else { + time = data->adDelayBuf[iSensorData] * 18; + if ((sensortime.time_diff * 10) > time) + sensortime.time_diff = data->adDelayBuf[iSensorData]; + } + } else { + if (data->lastTimestamp[iSensorData] < (data->timestamp - (data->adDelayBuf[iSensorData] * length))) { + data->lastTimestamp[iSensorData] = data->timestamp - (data->adDelayBuf[iSensorData] * length); + sensortime.time_diff = data->adDelayBuf[iSensorData]; + } else + sensortime.time_diff = div64_long((s64)(data->timestamp - data->lastTimestamp[iSensorData]), (s64)length); + } + } else { + if (data->reportedData[iSensorData] == false) + sensortime.irq_diff = data->adDelayBuf[iSensorData]; + } + + do { + data->get_sensor_data[iSensorData](pchRcvDataFrame, &iDataIdx, &sensorsdata); + get_timestamp(data, pchRcvDataFrame, &iDataIdx, &sensorsdata, &sensortime, iSensorData); + if (sensortime.irq_diff > 1000000) + data->report_sensor_data[iSensorData](data, &sensorsdata); + else if ((iSensorData == PROXIMITY_SENSOR) || (iSensorData == PROXIMITY_RAW) + || (iSensorData == GESTURE_SENSOR) || (iSensorData == SIG_MOTION_SENSOR)) + data->report_sensor_data[iSensorData](data, &sensorsdata); + else + pr_err("[SSP]: %s irq_diff is under 1msec (%d)\n", __func__, iSensorData); + sensortime.batch_count--; + } while ((sensortime.batch_count > 0) && (iDataIdx < iLength)); + + if (sensortime.batch_count > 0) + pr_err("[SSP]: %s batch count error (%d)\n", __func__, sensortime.batch_count); + + data->lastTimestamp[iSensorData] = data->timestamp; + data->reportedData[iSensorData] = true; + break; + case MSG2AP_INST_DEBUG_DATA: + iSensorData = print_mcu_debug(pchRcvDataFrame, &iDataIdx, iLength); + if (iSensorData) { + pr_err("[SSP]: %s - Mcu data frame3 error %d\n", __func__, + iSensorData); + return ERROR; + } + break; + case MSG2AP_INST_LIBRARY_DATA: + memcpy(&length, pchRcvDataFrame + iDataIdx, 2); + iDataIdx += 2; + ssp_sensorhub_handle_data(data, pchRcvDataFrame, iDataIdx, + iDataIdx + length); + iDataIdx += length; + break; + case MSG2AP_INST_BIG_DATA: + handle_big_data(data, pchRcvDataFrame, &iDataIdx); + break; + case MSG2AP_INST_META_DATA: + sensorsdata.meta_data.what = pchRcvDataFrame[iDataIdx++]; + sensorsdata.meta_data.sensor = pchRcvDataFrame[iDataIdx++]; + report_meta_data(data, &sensorsdata); + break; + case MSG2AP_INST_TIME_SYNC: + data->bTimeSyncing = true; + break; + } + } + + return SUCCESS; +} + +void initialize_function_pointer(struct ssp_data *data) +{ + data->get_sensor_data[ACCELEROMETER_SENSOR] = get_3axis_sensordata; + data->get_sensor_data[GYROSCOPE_SENSOR] = get_3axis_sensordata; + data->get_sensor_data[GEOMAGNETIC_UNCALIB_SENSOR] = + get_geomagnetic_uncaldata; + data->get_sensor_data[GEOMAGNETIC_RAW] = get_geomagnetic_rawdata; + data->get_sensor_data[GEOMAGNETIC_SENSOR] = + get_geomagnetic_caldata; + data->get_sensor_data[PRESSURE_SENSOR] = get_pressure_sensordata; + data->get_sensor_data[GESTURE_SENSOR] = get_gesture_sensordata; + data->get_sensor_data[PROXIMITY_SENSOR] = get_proximity_sensordata; + data->get_sensor_data[PROXIMITY_RAW] = get_proximity_rawdata; + data->get_sensor_data[LIGHT_SENSOR] = get_light_sensordata; + data->get_sensor_data[TEMPERATURE_HUMIDITY_SENSOR] = + get_temp_humidity_sensordata; + data->get_sensor_data[ROTATION_VECTOR] = get_rot_sensordata; + data->get_sensor_data[GAME_ROTATION_VECTOR] = get_rot_sensordata; + data->get_sensor_data[STEP_DETECTOR] = get_step_det_sensordata; + data->get_sensor_data[SIG_MOTION_SENSOR] = get_sig_motion_sensordata; + data->get_sensor_data[GYRO_UNCALIB_SENSOR] = get_uncalib_sensordata; + data->get_sensor_data[STEP_COUNTER] = get_step_cnt_sensordata; + data->get_sensor_data[SHAKE_CAM] = get_shake_cam_sensordata; + + data->report_sensor_data[ACCELEROMETER_SENSOR] = report_acc_data; + data->report_sensor_data[GYROSCOPE_SENSOR] = report_gyro_data; + data->report_sensor_data[GEOMAGNETIC_UNCALIB_SENSOR] = + report_mag_uncaldata; + data->report_sensor_data[GEOMAGNETIC_RAW] = report_geomagnetic_raw_data; + data->report_sensor_data[GEOMAGNETIC_SENSOR] = + report_mag_data; + data->report_sensor_data[PRESSURE_SENSOR] = report_pressure_data; + data->report_sensor_data[GESTURE_SENSOR] = report_gesture_data; + data->report_sensor_data[PROXIMITY_SENSOR] = report_prox_data; + data->report_sensor_data[PROXIMITY_RAW] = report_prox_raw_data; + data->report_sensor_data[LIGHT_SENSOR] = report_light_data; + data->report_sensor_data[TEMPERATURE_HUMIDITY_SENSOR] = + report_temp_humidity_data; + data->report_sensor_data[ROTATION_VECTOR] = report_rot_data; + data->report_sensor_data[GAME_ROTATION_VECTOR] = report_game_rot_data; + data->report_sensor_data[STEP_DETECTOR] = report_step_det_data; + data->report_sensor_data[SIG_MOTION_SENSOR] = report_sig_motion_data; + data->report_sensor_data[GYRO_UNCALIB_SENSOR] = report_uncalib_gyro_data; + data->report_sensor_data[STEP_COUNTER] = report_step_cnt_data; + data->report_sensor_data[SHAKE_CAM] = report_shake_cam_data; + + data->ssp_big_task[BIG_TYPE_DUMP] = ssp_dump_task; + data->ssp_big_task[BIG_TYPE_READ_LIB] = ssp_read_big_library_task; + data->ssp_big_task[BIG_TYPE_VOICE_NET] = ssp_send_big_library_task; + data->ssp_big_task[BIG_TYPE_VOICE_GRAM] = ssp_send_big_library_task; + data->ssp_big_task[BIG_TYPE_VOICE_PCM] = ssp_pcm_dump_task; + data->ssp_big_task[BIG_TYPE_TEMP] = ssp_temp_task; +} diff --git a/drivers/sensors/brcm/ssp_debug.c b/drivers/sensors/brcm/ssp_debug.c new file mode 100644 index 0000000..1b6d000e --- /dev/null +++ b/drivers/sensors/brcm/ssp_debug.c @@ -0,0 +1,416 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ +#include "ssp.h" +#include + + + +#define SSP_DEBUG_TIMER_SEC (10 * HZ) + +#define LIMIT_RESET_CNT 40 +#define LIMIT_TIMEOUT_CNT 3 + +#define DUMP_FILE_PATH "/data/log/MCU_DUMP" + +void ssp_dump_task(struct work_struct *work) { +#ifdef CONFIG_SENSORS_SSP_BBD + pr_err("[SSPBBD]:TODO:%s()\n", __func__); +#else + + struct ssp_big *big; + struct file *dump_file; + struct ssp_msg *msg; + char *buffer; + char strFilePath[60]; + struct timeval cur_time; + int iTimeTemp; + mm_segment_t fs; + int buf_len, packet_len, residue, iRet = 0, index = 0 ,iRetTrans=0 ,iRetWrite=0; + + big = container_of(work, struct ssp_big, work); + pr_err("[SSP]: %s - start ssp dumping (%d)(%d)\n", __func__,big->data->bMcuDumpMode,big->data->uDumpCnt); + big->data->uDumpCnt++; + wake_lock(&big->data->ssp_wake_lock); + + fs = get_fs(); + set_fs(get_ds()); + + if(big->data->bMcuDumpMode == true) + { + do_gettimeofday(&cur_time); + iTimeTemp = (int) cur_time.tv_sec; + + sprintf(strFilePath, "%s%d.txt", DUMP_FILE_PATH, iTimeTemp); + + dump_file = filp_open(strFilePath, O_RDWR | O_CREAT | O_APPEND, 0666); + if (IS_ERR(dump_file)) { + pr_err("[SSP]: %s - Can't open dump file\n", __func__); + set_fs(fs); + iRet = PTR_ERR(dump_file); + wake_unlock(&big->data->ssp_wake_lock); + kfree(big); + return; + } + } + else + dump_file = NULL; + + buf_len = big->length > DATA_PACKET_SIZE ? DATA_PACKET_SIZE : big->length; + buffer = kzalloc(buf_len, GFP_KERNEL); + residue = big->length; + + while (residue > 0) { + packet_len = residue > DATA_PACKET_SIZE ? DATA_PACKET_SIZE : residue; + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + msg->cmd = MSG2SSP_AP_GET_BIG_DATA; + msg->length = packet_len; + msg->options = AP2HUB_READ | (index++ << SSP_INDEX); + msg->data = big->addr; + msg->buffer = buffer; + msg->free_buffer = 0; + + iRetTrans = ssp_spi_sync(big->data, msg, 1000); + if (iRetTrans != SUCCESS) { + pr_err("[SSP]: %s - Fail to receive data %d (%d)\n", __func__, iRetTrans,residue); + break; + } + if(big->data->bMcuDumpMode == true) + { + iRetWrite = vfs_write(dump_file, (char __user *) buffer, packet_len, + &dump_file->f_pos); + if (iRetWrite < 0) { + pr_err("[SSP]: %s - Can't write dump to file\n", __func__); + break; + } + } + residue -= packet_len; + } + + if(big->data->bMcuDumpMode == true && (iRetTrans != SUCCESS || iRetWrite < 0) ) + { + char FAILSTRING[100]; + sprintf(FAILSTRING,"FAIL OCCURED(%d)(%d)(%d)",iRetTrans,iRetWrite,big->length); + vfs_write(dump_file, (char __user *) FAILSTRING, strlen(FAILSTRING),&dump_file->f_pos); + } + + big->data->bDumping = false; + if(big->data->bMcuDumpMode == true) + filp_close(dump_file, current->files); + + set_fs(fs); + + wake_unlock(&big->data->ssp_wake_lock); + kfree(buffer); + kfree(big); + + pr_err("[SSP]: %s done\n", __func__); +#endif +} + +void ssp_temp_task(struct work_struct *work) { +#ifdef CONFIG_SENSORS_SSP_BBD + pr_err("[SSPBBD]:TODO:%s()\n", __func__); +#else + struct ssp_big *big; + struct ssp_msg *msg; + char *buffer; + int buf_len, packet_len, residue, iRet = 0, index = 0, i = 0, buffindex = 0; + + big = container_of(work, struct ssp_big, work); + buf_len = big->length > DATA_PACKET_SIZE ? DATA_PACKET_SIZE : big->length; + buffer = kzalloc(buf_len, GFP_KERNEL); + residue = big->length; + + while (residue > 0) { + packet_len = residue > DATA_PACKET_SIZE ? DATA_PACKET_SIZE : residue; + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + msg->cmd = MSG2SSP_AP_GET_BIG_DATA; + msg->length = packet_len; + msg->options = AP2HUB_READ | (index++ << SSP_INDEX); + msg->data = big->addr; + msg->buffer = buffer; + msg->free_buffer = 0; + + iRet = ssp_spi_sync(big->data, msg, 1000); + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - Fail to receive data %d\n", __func__, iRet); + break; + } + + i = 0; + while (packet_len - i >= 12) { + ssp_dbg("[SSP]: %s %d %d %d %d %d %d", __func__, + *((s16 *) (buffer + i + 0)), *((s16 *) (buffer + i + 2)), + *((s16 *) (buffer + i + 4)), *((s16 *) (buffer + i + 6)), + *((s16 *) (buffer + i + 8)), *((s16 *) (buffer +i + 10))); + buffindex++; + i += 12; + } + + residue -= packet_len; + } + kfree(buffer); + kfree(big); + ssp_dbg("[SSP]: %s done\n", __func__); +#endif +} + +/*************************************************************************/ +/* SSP Debug timer function */ +/*************************************************************************/ +int print_mcu_debug(char *pchRcvDataFrame, int *pDataIdx, + int iRcvDataFrameLength) +{ + int iLength = pchRcvDataFrame[(*pDataIdx)++]; + int cur = *pDataIdx; + + if (iLength > iRcvDataFrameLength - *pDataIdx || iLength <= 0) { + ssp_dbg("[SSP]: MSG From MCU - invalid debug length(%d/%d/%d)\n", + iLength, iRcvDataFrameLength, cur); + return iLength ? iLength : ERROR; + } + + ssp_dbg("[SSP]: MSG From MCU - %s\n", &pchRcvDataFrame[*pDataIdx]); + *pDataIdx += iLength; + return 0; +} + +void reset_mcu(struct ssp_data *data) +{ + func_dbg(); + data->uResetCnt++; + + ssp_enable(data, false); + clean_pending_list(data); + + bbd_mcu_reset(); +} + +void sync_sensor_state(struct ssp_data *data) +{ + unsigned char uBuf[9] = {0,}; + unsigned int uSensorCnt; + int iRet = 0; + + iRet = set_gyro_cal(data); + if (iRet < 0) + pr_err("[SSP]: %s - set_gyro_cal failed\n", __func__); + + iRet = set_accel_cal(data); + if (iRet < 0) + pr_err("[SSP]: %s - set_accel_cal failed\n", __func__); + + udelay(10); + + for (uSensorCnt = 0; uSensorCnt < SENSOR_MAX; uSensorCnt++) { + mutex_lock(&data->enable_mutex); + if (atomic_read(&data->aSensorEnable) & (1 << uSensorCnt)) { + s32 dMsDelay = + get_msdelay(data->adDelayBuf[uSensorCnt]); + memcpy(&uBuf[0], &dMsDelay, 4); + memcpy(&uBuf[4], &data->batchLatencyBuf[uSensorCnt], 4); + uBuf[8] = data->batchOptBuf[uSensorCnt]; + send_instruction(data, ADD_SENSOR, uSensorCnt, uBuf, 9); + udelay(10); + } + mutex_unlock(&data->enable_mutex); + } + + if (data->bProximityRawEnabled == true) { + s32 dMsDelay = 20; + memcpy(&uBuf[0], &dMsDelay, 4); + send_instruction(data, ADD_SENSOR, PROXIMITY_RAW, uBuf, 4); + } + + set_proximity_threshold(data, data->uProxHiThresh,data->uProxLoThresh); + + data->bMcuDumpMode = ssp_check_sec_dump_mode(); + iRet = ssp_send_cmd(data, MSG2SSP_AP_MCU_SET_DUMPMODE, + data->bMcuDumpMode); + if (iRet < 0) + pr_err("[SSP]: %s - MSG2SSP_AP_MCU_SET_DUMPMODE failed\n", + __func__); +} + +static void print_sensordata(struct ssp_data *data, unsigned int uSensor) +{ + switch (uSensor) { + case ACCELEROMETER_SENSOR: + case GYROSCOPE_SENSOR: + ssp_dbg("[SSP] %u : %d, %d, %d (%ums, %dms)\n", uSensor, + data->buf[uSensor].x, data->buf[uSensor].y, + data->buf[uSensor].z, + get_msdelay(data->adDelayBuf[uSensor]), + data->batchLatencyBuf[uSensor]); + break; + case GEOMAGNETIC_SENSOR: + ssp_dbg("[SSP] %u : %d, %d, %d, %d (%ums)\n", uSensor, + data->buf[uSensor].cal_x, data->buf[uSensor].cal_y, + data->buf[uSensor].cal_y, data->buf[uSensor].accuracy, + get_msdelay(data->adDelayBuf[uSensor])); + break; + case GEOMAGNETIC_UNCALIB_SENSOR: + ssp_dbg("[SSP] %u : %d, %d, %d, %d, %d, %d (%ums)\n", uSensor, + data->buf[uSensor].uncal_x, data->buf[uSensor].uncal_y, + data->buf[uSensor].uncal_z, data->buf[uSensor].offset_x, + data->buf[uSensor].offset_y, data->buf[uSensor].offset_z, + get_msdelay(data->adDelayBuf[uSensor])); + break; + case PRESSURE_SENSOR: + ssp_dbg("[SSP] %u : %d, %d (%ums, %dms)\n", uSensor, + data->buf[uSensor].pressure[0], + data->buf[uSensor].pressure[1], + get_msdelay(data->adDelayBuf[uSensor]), + data->batchLatencyBuf[uSensor]); + break; + case GESTURE_SENSOR: + ssp_dbg("[SSP] %u : %d, %d, %d, %d (%ums)\n", uSensor, + data->buf[uSensor].data[3], data->buf[uSensor].data[4], + data->buf[uSensor].data[5], data->buf[uSensor].data[6], + get_msdelay(data->adDelayBuf[uSensor])); + break; + case TEMPERATURE_HUMIDITY_SENSOR: + ssp_dbg("[SSP] %u : %d, %d, %d (%ums)\n", uSensor, + data->buf[uSensor].x, data->buf[uSensor].y, + data->buf[uSensor].z, + get_msdelay(data->adDelayBuf[uSensor])); + break; + case LIGHT_SENSOR: +#if defined(CONFIG_SENSORS_SSP_TMG399X) + ssp_dbg("[SSP] %u : %u, %u, %u, %u, %u, %u (%ums)\n", uSensor, + data->buf[uSensor].r, data->buf[uSensor].g, + data->buf[uSensor].b, data->buf[uSensor].w, + data->buf[uSensor].a_time, data->buf[uSensor].a_gain, + get_msdelay(data->adDelayBuf[uSensor])); +#endif + break; + case PROXIMITY_SENSOR: + ssp_dbg("[SSP] %u : %d, %d (%ums)\n", uSensor, + data->buf[uSensor].prox[0], data->buf[uSensor].prox[1], + get_msdelay(data->adDelayBuf[uSensor])); + break; + case STEP_DETECTOR: + ssp_dbg("[SSP] %u : %u (%ums, %dms)\n", uSensor, + data->buf[uSensor].step_det, + get_msdelay(data->adDelayBuf[uSensor]), + data->batchLatencyBuf[uSensor]); + break; + case GAME_ROTATION_VECTOR: + case ROTATION_VECTOR: + ssp_dbg("[SSP] %u : %d, %d, %d, %d, %d (%ums, %dms)\n", uSensor, + data->buf[uSensor].quat_a, data->buf[uSensor].quat_b, + data->buf[uSensor].quat_c, data->buf[uSensor].quat_d, + data->buf[uSensor].acc_rot, + get_msdelay(data->adDelayBuf[uSensor]), + data->batchLatencyBuf[uSensor]); + break; + case SIG_MOTION_SENSOR: + ssp_dbg("[SSP] %u : %u(%ums)\n", uSensor, + data->buf[uSensor].sig_motion, + get_msdelay(data->adDelayBuf[uSensor])); + break; + case GYRO_UNCALIB_SENSOR: + ssp_dbg("[SSP] %u : %d, %d, %d, %d, %d, %d (%ums)\n", uSensor, + data->buf[uSensor].uncal_x, data->buf[uSensor].uncal_y, + data->buf[uSensor].uncal_z, data->buf[uSensor].offset_x, + data->buf[uSensor].offset_y, + data->buf[uSensor].offset_z, + get_msdelay(data->adDelayBuf[uSensor])); + break; + case STEP_COUNTER: + ssp_dbg("[SSP] %u : %u(%ums)\n", uSensor, + data->buf[uSensor].step_diff, + get_msdelay(data->adDelayBuf[uSensor])); + break; + default: + ssp_dbg("[SSP] Wrong sensorCnt: %u\n", uSensor); + break; + } +} + +static void debug_work_func(struct work_struct *work) +{ + unsigned int uSensorCnt; + struct ssp_data *data = container_of(work, struct ssp_data, work_debug); + + for (uSensorCnt = 0; uSensorCnt < SENSOR_MAX; uSensorCnt++) + if ((atomic_read(&data->aSensorEnable) & (1 << uSensorCnt)) + || data->batchLatencyBuf[uSensorCnt]) + print_sensordata(data, uSensorCnt); + + if (((atomic_read(&data->aSensorEnable) & (1 << ACCELEROMETER_SENSOR)) + && (data->batchLatencyBuf[ACCELEROMETER_SENSOR] == 0) + && (data->uIrqCnt == 0) && (data->uTimeOutCnt > 0)) + || (data->uTimeOutCnt > LIMIT_TIMEOUT_CNT)) { + + if (data->uResetCnt < LIMIT_RESET_CNT) { + pr_debug("[SSP] : %s - uTimeOutCnt(%u), pending(%u)\n", + __func__, data->uTimeOutCnt, + !list_empty(&data->pending_list)); + reset_mcu(data); + } else + ssp_enable(data, false); + + data->uTimeOutCnt = 0; + data->uComFailCnt = 0; + } + + data->uIrqCnt = 0; +} + +static void debug_timer_func(unsigned long ptr) +{ + struct ssp_data *data = (struct ssp_data *)ptr; + + queue_work(data->debug_wq, &data->work_debug); + mod_timer(&data->debug_timer, + round_jiffies_up(jiffies + SSP_DEBUG_TIMER_SEC)); +} + +void enable_debug_timer(struct ssp_data *data) +{ + mod_timer(&data->debug_timer, + round_jiffies_up(jiffies + SSP_DEBUG_TIMER_SEC)); +} + +void disable_debug_timer(struct ssp_data *data) +{ + del_timer_sync(&data->debug_timer); + cancel_work_sync(&data->work_debug); +} + +int initialize_debug_timer(struct ssp_data *data) +{ + setup_timer(&data->debug_timer, debug_timer_func, (unsigned long)data); + + data->debug_wq = create_singlethread_workqueue("ssp_debug_wq"); + if (!data->debug_wq) + return ERROR; + + INIT_WORK(&data->work_debug, debug_work_func); + return SUCCESS; +} + +unsigned int ssp_check_sec_dump_mode() /* if returns true dump mode on */ +{ +#ifdef CONFIG_SEC_DEBUG + if (sec_debug_level.en.kernel_fault == 1) + return 1; + else +#endif + return 0; +} diff --git a/drivers/sensors/brcm/ssp_dev.c b/drivers/sensors/brcm/ssp_dev.c new file mode 100644 index 0000000..3aab02f --- /dev/null +++ b/drivers/sensors/brcm/ssp_dev.c @@ -0,0 +1,555 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ +#include "ssp.h" +#ifdef CONFIG_OF +#include +#endif + +#define DEBUG + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void ssp_early_suspend(struct early_suspend *handler); +static void ssp_late_resume(struct early_suspend *handler); +#endif +#define NORMAL_SENSOR_STATE_K 0x3FEFF + +unsigned int bootmode; +EXPORT_SYMBOL(bootmode); +static int __init bootmode_setup(char *str) +{ + get_option(&str, &bootmode); + pr_info("[SSP] bootmode_setup = %d\n",bootmode); + return 1; +} +__setup("bootmode=", bootmode_setup); + +void ssp_enable(struct ssp_data *data, bool enable) +{ + if (enable == !data->bSspShutdown) + return; + + pr_info("[SSP] %s, new enable = %d, old enable = %d\n", + __func__, enable, !data->bSspShutdown); + + if (enable && data->bSspShutdown) + data->bSspShutdown = false; + else if (!enable && !data->bSspShutdown) + data->bSspShutdown = true; +} + +/*************************************************************************/ +/* initialize sensor hub */ +/*************************************************************************/ + +static void initialize_variable(struct ssp_data *data) +{ + int iSensorIndex; + + for (iSensorIndex = 0; iSensorIndex < SENSOR_MAX; iSensorIndex++) { + data->adDelayBuf[iSensorIndex] = DEFUALT_POLLING_DELAY; + data->batchLatencyBuf[iSensorIndex] = 0; + data->batchOptBuf[iSensorIndex] = 0; + data->aiCheckStatus[iSensorIndex] = INITIALIZATION_STATE; + data->lastTimestamp[iSensorIndex] = 0; + data->reportedData[iSensorIndex] = false; + } + + atomic_set(&data->aSensorEnable, 0); + data->iLibraryLength = 0; + data->uSensorState = NORMAL_SENSOR_STATE_K; + data->uFactoryProxAvg[0] = 0; + data->uMagCntlRegData = 1; + + data->uResetCnt = 0; + data->uTimeOutCnt = 0; + data->uComFailCnt = 0; + data->uIrqCnt = 0; + + data->bSspShutdown = true; + data->bProximityRawEnabled = false; + data->bGeomagneticRawEnabled = false; + data->bBarcodeEnabled = false; + data->bAccelAlert = false; + data->bTimeSyncing = true; + data->bHandlingIrq = false; + + data->accelcal.x = 0; + data->accelcal.y = 0; + data->accelcal.z = 0; + + data->gyrocal.x = 0; + data->gyrocal.y = 0; + data->gyrocal.z = 0; + + data->magoffset.x = 0; + data->magoffset.y = 0; + data->magoffset.z = 0; + + data->iPressureCal = 0; + data->uProxCanc = 0; + data->uProxHiThresh = data->uProxHiThresh_default; + data->uProxLoThresh = data->uProxLoThresh_default; + data->uGyroDps = GYROSCOPE_DPS2000; + data->uIr_Current = DEFUALT_IR_CURRENT; + + data->mcu_device = NULL; + data->acc_device = NULL; + data->gyro_device = NULL; + data->mag_device = NULL; + data->prs_device = NULL; + data->prox_device = NULL; + data->light_device = NULL; + data->ges_device = NULL; + + data->voice_device = NULL; + data->bMcuDumpMode = ssp_check_sec_dump_mode(); + INIT_LIST_HEAD(&data->pending_list); + + data->bbd_on_packet_wq = + create_singlethread_workqueue("ssp_bbd_on_packet_wq"); + INIT_WORK(&data->work_bbd_on_packet, bbd_on_packet_work_func); + + data->bbd_mcu_ready_wq = + create_singlethread_workqueue("ssp_bbd_mcu_ready_wq"); + INIT_WORK(&data->work_bbd_mcu_ready, bbd_mcu_ready_work_func); + +#ifdef SSP_BBD_USE_SEND_WORK + data->bbd_send_packet_wq = + create_singlethread_workqueue("ssp_bbd_send_packet_wq"); + INIT_WORK(&data->work_bbd_send_packet, bbd_send_packet_work_func); +#endif /* SSP_BBD_USE_SEND_WORK */ + + data->step_count_total = 0; + data->sealevelpressure = 0; + initialize_function_pointer(data); +} + +int initialize_mcu(struct ssp_data *data) +{ + int iRet = 0; + + clean_pending_list(data); + + iRet = get_chipid(data); + pr_info("[SSP] MCU device ID = %d, reading ID = %d\n", DEVICE_ID, iRet); + if (iRet != DEVICE_ID) { + if (iRet < 0) { + pr_err("[SSP]: %s - MCU is not working : 0x%x\n", + __func__, iRet); + } else { + pr_err("[SSP]: %s - MCU identification failed\n", + __func__); + iRet = -ENODEV; + } + goto out; + } + + iRet = set_sensor_position(data); + if (iRet < 0) { + pr_err("[SSP]: %s - set_sensor_position failed\n", __func__); + goto out; + } + + data->uSensorState = get_sensor_scanning_info(data); + if (data->uSensorState == 0) { + pr_err("[SSP]: %s - get_sensor_scanning_info failed\n", + __func__); + iRet = ERROR; + goto out; + } + + iRet = initialize_magnetic_sensor(data); + if (iRet < 0) + pr_err("[SSP]: %s - initialize magnetic sensor failed\n", + __func__); + + data->uCurFirmRev = get_firmware_rev(data); + pr_info("[SSP] MCU Firm Rev : New = %8u\n", + data->uCurFirmRev); + +#ifndef CONFIG_SENSORS_SSP_BBD + iRet = ssp_send_cmd(data, MSG2SSP_AP_MCU_DUMP_CHECK, 0); +#endif +out: + return iRet; +} + +static bbd_callbacks ssp_bbd_callbacks = { + .on_packet = NULL, + .on_packet_alarm = callback_bbd_on_packet_alarm, + .on_control = callback_bbd_on_control, + .on_mcu_ready = callback_bbd_on_mcu_ready, +}; + +static int ssp_parse_dt(struct device *dev,struct ssp_data *data) +{ + struct device_node *np = dev->of_node; + int errorno = 0; +#if defined(CONFIG_SENSORS_SSP_YAS532) + u32 len, temp; + int i; +#endif + + if (!np) { + pr_err("[SSP] NO dt node!!\n"); + return errorno; + } + + if (of_property_read_u32(np, "ssp-acc-position", &data->accel_position)) + data->accel_position = 0; + if (of_property_read_u32(np, "ssp-mag-position", &data->mag_position)) + data->mag_position = 0; + + pr_info("[SSP] acc-posi[%d] mag-posi[%d]\n", + data->accel_position, data->mag_position); + + if (of_property_read_u32(np, "ssp-ap-rev", &data->ap_rev)) + data->ap_rev = 0; + + if (of_property_read_u32(np, "ssp,prox-hi_thresh", + &data->uProxHiThresh_default)) + data->uProxHiThresh_default = DEFUALT_HIGH_THRESHOLD; + + if (of_property_read_u32(np, "ssp,prox-low_thresh", + &data->uProxLoThresh_default)) + data->uProxLoThresh_default = DEFUALT_LOW_THRESHOLD; + + pr_info("[SSP] hi-thresh[%u] low-thresh[%u]\n", + data->uProxHiThresh_default, data->uProxLoThresh_default); + +#if defined(CONFIG_SENSORS_SSP_YAS532) + if (!of_get_property(np, "ssp-mag-array", &len)) { + pr_info("[SSP] No static matrix at DT for YAS532!(%p)\n", data->static_matrix); + goto dt_exit; + } + if (len/4 != 9) { + pr_err("[SSP] Length/4:%d should be 9 for YAS532!\n", len/4); + goto dt_exit; + } + data->static_matrix = kzalloc(9*sizeof(s16), GFP_KERNEL); + pr_info("[SSP] static matrix Length:%d, Len/4=%d\n", len, len/4); + + for (i = 0; i < 9; i++) { + if (of_property_read_u32_index(np, "ssp-mag-array", i, &temp)) { + pr_err("[SSP] %s cannot get u32 of array[%d]!\n", __func__, i); + goto dt_exit; + } + *(data->static_matrix+i) = (int)temp; + } +#endif + +dt_exit: + return errorno; +} + +static int ssp_probe(struct spi_device *spi) +{ + struct ssp_data *data; + struct ssp_platform_data *pdata; + int iRet = 0; + + pr_info("[SSP] %s, is called\n", __func__); + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) { + pr_err("[SSP] %s, failed to allocate memory for data\n", __func__); + iRet = -ENOMEM; + goto exit; + } + + if (spi->dev.of_node) { + iRet = ssp_parse_dt(&spi->dev, data); + if (iRet) { + pr_err("[SSP]: %s - Failed to parse DT\n", __func__); + goto err_setup; + } + } else { + pdata = spi->dev.platform_data; + if (pdata == NULL) { + pr_err("[SSP] %s, platform_data is null\n", __func__); + iRet = -ENOMEM; + goto err_setup; + } + + /* AP system_rev */ + if (pdata->check_ap_rev) + data->ap_rev = pdata->check_ap_rev(); + else + data->ap_rev = 0; + + /* Get sensor positions */ + if (pdata->get_positions) { + pdata->get_positions(&data->accel_position, + &data->mag_position); + } else { + data->accel_position = 0; + data->mag_position = 0; + } + if (pdata->mag_matrix) { + data->mag_matrix_size = pdata->mag_matrix_size; + data->mag_matrix = pdata->mag_matrix; + } + } + + spi->mode = SPI_MODE_3; + if (spi_setup(spi)) { + pr_err("[SSP] %s, failed to setup spi\n", __func__); + iRet = -ENODEV; + goto err_setup; + } + + data->bProbeIsDone = false; + data->spi = spi; + spi_set_drvdata(spi, data); + + mutex_init(&data->comm_mutex); + mutex_init(&data->pending_mutex); + mutex_init(&data->enable_mutex); + + if (spi->dev.of_node == NULL) { + pr_err("[SSP] %s, function callback is null\n", __func__); + iRet = -EIO; + goto err_reset_null; + } + + pr_info("\n#####################################################\n"); + + initialize_variable(data); + wake_lock_init(&data->ssp_wake_lock, + WAKE_LOCK_SUSPEND, "ssp_wake_lock"); + + iRet = initialize_input_dev(data); + if (iRet < 0) { + pr_err("[SSP]: %s - could not create input device\n", __func__); + goto err_input_register_device; + } + + iRet = initialize_debug_timer(data); + if (iRet < 0) { + pr_err("[SSP]: %s - could not create workqueue\n", __func__); + goto err_create_workqueue; + } + + iRet = initialize_sysfs(data); + if (iRet < 0) { + pr_err("[SSP]: %s - could not create sysfs\n", __func__); + goto err_sysfs_create; + } + + iRet = initialize_event_symlink(data); + if (iRet < 0) { + pr_err("[SSP]: %s - could not create symlink\n", __func__); + goto err_symlink_create; + } + + msleep(70); + +#ifdef CONFIG_SENSORS_SSP_SENSORHUB + /* init sensorhub device */ + iRet = ssp_sensorhub_initialize(data); + if (iRet < 0) { + pr_err("%s: ssp_sensorhub_initialize err(%d)", __func__, iRet); + ssp_sensorhub_remove(data); + } +#endif + + bbd_register(data, &ssp_bbd_callbacks); + +#ifdef CONFIG_HAS_EARLYSUSPEND + data->early_suspend.suspend = ssp_early_suspend; + data->early_suspend.resume = ssp_late_resume; + register_early_suspend(&data->early_suspend); +#endif + + pr_info("[SSP]: %s - probe success!\n", __func__); + + enable_debug_timer(data); + data->bProbeIsDone = true; + iRet = 0; + + goto exit; + +err_symlink_create: + remove_sysfs(data); +err_sysfs_create: + destroy_workqueue(data->debug_wq); +err_create_workqueue: + remove_input_dev(data); +err_input_register_device: + wake_lock_destroy(&data->ssp_wake_lock); +err_reset_null: + mutex_destroy(&data->comm_mutex); + mutex_destroy(&data->pending_mutex); + +err_setup: + kfree(data); + pr_err("[SSP] %s, probe failed!\n", __func__); +exit: + pr_info("#####################################################\n\n"); + return iRet; +} + +static void ssp_shutdown(struct spi_device *spi) +{ + struct ssp_data *data = spi_get_drvdata(spi); + + pr_err("[SSP] lpm recovery\n"); + func_dbg(); + if (data->bProbeIsDone == false) + goto exit; + + disable_debug_timer(data); + + if (SUCCESS != ssp_send_cmd(data, MSG2SSP_AP_STATUS_SHUTDOWN, 0)) + pr_err("[SSP]: %s MSG2SSP_AP_STATUS_SHUTDOWN failed\n", + __func__); + + ssp_enable(data, false); + clean_pending_list(data); + +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&data->early_suspend); +#endif + + bbd_register(NULL, NULL); + + remove_event_symlink(data); + remove_sysfs(data); + remove_input_dev(data); + +#ifdef CONFIG_SENSORS_SSP_SENSORHUB + ssp_sensorhub_remove(data); +#endif + + del_timer_sync(&data->debug_timer); + cancel_work_sync(&data->work_debug); + cancel_work_sync(&data->work_bbd_mcu_ready); + destroy_workqueue(data->debug_wq); + wake_lock_destroy(&data->ssp_wake_lock); + mutex_destroy(&data->comm_mutex); + mutex_destroy(&data->pending_mutex); + pr_info("[SSP] %s done\n", __func__); +exit: + kfree(data); +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void ssp_early_suspend(struct early_suspend *handler) +{ + struct ssp_data *data; + data = container_of(handler, struct ssp_data, early_suspend); + + func_dbg(); + disable_debug_timer(data); + +#ifdef CONFIG_SENSORS_SSP_SENSORHUB + /* give notice to user that AP goes to sleep */ + ssp_sensorhub_report_notice(data, MSG2SSP_AP_STATUS_SLEEP); + ssp_sleep_mode(data); + data->uLastAPState = MSG2SSP_AP_STATUS_SLEEP; +#else + if (atomic_read(&data->aSensorEnable) > 0) + ssp_sleep_mode(data); +#endif +} + +static void ssp_late_resume(struct early_suspend *handler) +{ + struct ssp_data *data; + data = container_of(handler, struct ssp_data, early_suspend); + + func_dbg(); + enable_debug_timer(data); + +#ifdef CONFIG_SENSORS_SSP_SENSORHUB + /* give notice to user that AP goes to sleep */ + ssp_sensorhub_report_notice(data, MSG2SSP_AP_STATUS_WAKEUP); + ssp_resume_mode(data); + data->uLastAPState = MSG2SSP_AP_STATUS_WAKEUP; +#else + if (atomic_read(&data->aSensorEnable) > 0) + ssp_resume_mode(data); +#endif +} + +#else /* no early suspend */ + +static int ssp_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct ssp_data *data = spi_get_drvdata(spi); + + func_dbg(); + + if (SUCCESS != ssp_send_cmd(data, MSG2SSP_AP_STATUS_SUSPEND, 0)) + pr_err("[SSP]: %s MSG2SSP_AP_STATUS_SUSPEND failed\n", + __func__); + + data->uLastResumeState = MSG2SSP_AP_STATUS_SUSPEND; + disable_debug_timer(data); + + data->bTimeSyncing = false; + pr_info("[SSP]: isHandlingIrq:%d\n", data->bHandlingIrq); + + return 0; +} + +static int ssp_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct ssp_data *data = spi_get_drvdata(spi); + + func_dbg(); + enable_debug_timer(data); + + if (SUCCESS != ssp_send_cmd(data, MSG2SSP_AP_STATUS_RESUME, 0)) + pr_err("[SSP]: %s MSG2SSP_AP_STATUS_RESUME failed\n", + __func__); + data->uLastResumeState = MSG2SSP_AP_STATUS_RESUME; + + return 0; +} + +static const struct dev_pm_ops ssp_pm_ops = { + .suspend = ssp_suspend, + .resume = ssp_resume +}; +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +static const struct spi_device_id ssp_id[] = { + {"ssp-spi", 0}, + {} +}; + +MODULE_DEVICE_TABLE(spi, ssp_id); + +static struct spi_driver ssp_driver = { + .probe = ssp_probe, + .shutdown = ssp_shutdown, + .id_table = ssp_id, + .driver = { +#ifndef CONFIG_HAS_EARLYSUSPEND + .pm = &ssp_pm_ops, +#endif + .owner = THIS_MODULE, + .name = "ssp-spi", + }, +}; + +struct spi_driver *pssp_driver = &ssp_driver; +module_spi_driver(ssp_driver); +MODULE_DESCRIPTION("ssp spi driver"); +MODULE_AUTHOR("Samsung Electronics"); +MODULE_LICENSE("GPL"); diff --git a/drivers/sensors/brcm/ssp_firmware.c b/drivers/sensors/brcm/ssp_firmware.c new file mode 100644 index 0000000..bf93470 --- /dev/null +++ b/drivers/sensors/brcm/ssp_firmware.c @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ +#include "ssp.h" + +#if defined(CONFIG_SENSORHUB_S333) +#define SSP_FIRMWARE_REVISION_BCM 15011301 +#else +#define SSP_FIRMWARE_REVISION_BCM 15011300 +#endif + +unsigned int get_module_rev(struct ssp_data *data) +{ + return SSP_FIRMWARE_REVISION_BCM; +} diff --git a/drivers/sensors/brcm/ssp_i2c.c b/drivers/sensors/brcm/ssp_i2c.c new file mode 100644 index 0000000..4cf68ed --- /dev/null +++ b/drivers/sensors/brcm/ssp_i2c.c @@ -0,0 +1,758 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ +#include "ssp.h" + +#define LIMIT_DELAY_CNT 200 +#define RECEIVEBUFFERSIZE 12 +#define DEBUG_SHOW_DATA 0 + +int bbd_do_transfer(struct ssp_data *data, struct ssp_msg *msg, + struct completion *done, int timeout); + +void clean_msg(struct ssp_msg *msg) { + if (msg->free_buffer) + kfree(msg->buffer); + kfree(msg); +} + +static int do_transfer(struct ssp_data *data, struct ssp_msg *msg, + struct completion *done, int timeout) +{ + if(timeout) + wake_lock_timeout(&data->ssp_wake_lock, ((timeout/1000)+1)*HZ); + + return bbd_do_transfer(data, msg, done, timeout); +} + +int ssp_spi_async(struct ssp_data *data, struct ssp_msg *msg) +{ + int status = 0; + + if (msg->length) + return ssp_spi_sync(data, msg, 1000); + + status = do_transfer(data, msg, NULL, 0); + + return status; +} + +int ssp_spi_sync(struct ssp_data *data, struct ssp_msg *msg, int timeout) { + DECLARE_COMPLETION_ONSTACK(done); + int status = 0; + + if (msg->length == 0) { + pr_err("[SSP]: %s length must not be 0\n", __func__); + clean_msg(msg); + return status; + } + + status = do_transfer(data, msg, &done, timeout); + + return status; +} + +int select_irq_msg(struct ssp_data *data) { + struct ssp_msg *msg, *n; + bool found = false; + u16 chLength = 0, msg_options = 0; + u8 msg_type = 0; + int iRet = 0; + char* buffer; + char chTempBuf[4] = { -1 }; + + data->bHandlingIrq = true; + iRet = spi_read(data->spi, chTempBuf, sizeof(chTempBuf)); + if (iRet < 0) { + pr_err("[SSP] %s spi_read fail, ret = %d\n", __func__, iRet); + data->bHandlingIrq = false; + return iRet; + } + + memcpy(&msg_options, &chTempBuf[0], 2); + msg_type = msg_options & SSP_SPI_MASK; + memcpy(&chLength, &chTempBuf[2], 2); + + switch (msg_type) { + case AP2HUB_READ: + case AP2HUB_WRITE: + mutex_lock(&data->pending_mutex); + if (!list_empty(&data->pending_list)) { + list_for_each_entry_safe(msg, n, &data->pending_list, list) + { + if (msg->options == msg_options) { + list_del(&msg->list); + found = true; + break; + } + } + + if (!found) { + pr_err("[SSP]: %s %d - Not match error\n", __func__, msg_options); + goto exit; + } + + if (msg->dead && !msg->free_buffer) { + msg->buffer = (char*) kzalloc(msg->length, GFP_KERNEL); + msg->free_buffer = 1; + } /* For dead msg, make a temporary buffer to read. */ + + if (msg_type == AP2HUB_READ) + iRet = spi_read(data->spi, msg->buffer, msg->length); + if (msg_type == AP2HUB_WRITE) { + iRet = spi_write(data->spi, msg->buffer, msg->length); + + if (msg_options & AP2HUB_RETURN) { + msg->options = AP2HUB_READ | AP2HUB_RETURN; + msg->length = 1; + list_add_tail(&msg->list, &data->pending_list); + goto exit; + } + } + + if (msg->done != NULL && !completion_done(msg->done)) + complete(msg->done); + if (msg->dead_hook != NULL) + *(msg->dead_hook) = true; + + clean_msg(msg); + } else + pr_err("[SSP]List empty error(%d)\n", msg_type); + exit: + mutex_unlock(&data->pending_mutex); + break; + case HUB2AP_WRITE: + buffer = (char*) kzalloc(chLength, GFP_KERNEL); + if (buffer == NULL) { + pr_err("[SSP] %s, failed to alloc memory for buffer\n", __func__); + iRet = -ENOMEM; + break; + } + iRet = spi_read(data->spi, buffer, chLength); + if (iRet < 0) + pr_err("[SSP] %s spi_read fail\n", __func__); + else + parse_dataframe(data, buffer, chLength); + kfree(buffer); + break; + default: + pr_err("[SSP]No type error(%d)\n", msg_type); + break; + } + + if (iRet < 0) { + pr_err("[SSP]: %s - MSG2SSP_SSD error %d\n", __func__, iRet); + data->bHandlingIrq = false; + return ERROR; + } + + data->bHandlingIrq = false; + return SUCCESS; +} + +void clean_pending_list(struct ssp_data *data) { + struct ssp_msg *msg, *n; + + mutex_lock(&data->pending_mutex); + + list_for_each_entry_safe(msg, n, &data->pending_list, list) + { + list_del(&msg->list); + if (msg->done != NULL && !completion_done(msg->done)) + complete(msg->done); + if (msg->dead_hook != NULL) + *(msg->dead_hook) = true; + + clean_msg(msg); + } + mutex_unlock(&data->pending_mutex); +} + +int ssp_send_cmd(struct ssp_data *data, char command, int arg) +{ + int iRet = 0; + + struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + return -ENOMEM; + } + msg->cmd = command; + msg->length = 0; + msg->options = AP2HUB_WRITE; + msg->data = arg; + msg->free_buffer = 0; + + iRet = ssp_spi_async(data, msg); + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - command 0x%x failed %d\n", + __func__, command, iRet); + return ERROR; + } + + ssp_dbg("[SSP]: %s - command 0x%x %d\n", __func__, command, arg); + + return SUCCESS; +} + +int send_instruction(struct ssp_data *data, u8 uInst, + u8 uSensorType, u8 *uSendBuf, u8 uLength) +{ + char command; + int iRet = 0; + struct ssp_msg *msg; + + if ((!(data->uSensorState & (1 << uSensorType))) + && (uInst <= CHANGE_DELAY)) { + pr_err("[SSP]: %s - Bypass Inst Skip! - %u\n", + __func__, uSensorType); + return FAIL; + } + + switch (uInst) { + case REMOVE_SENSOR: + command = MSG2SSP_INST_BYPASS_SENSOR_REMOVE; + break; + case ADD_SENSOR: + command = MSG2SSP_INST_BYPASS_SENSOR_ADD; + break; + case CHANGE_DELAY: + command = MSG2SSP_INST_CHANGE_DELAY; + break; + case GO_SLEEP: + command = MSG2SSP_AP_STATUS_SLEEP; + data->uLastAPState = MSG2SSP_AP_STATUS_SLEEP; + break; + case REMOVE_LIBRARY: + command = MSG2SSP_INST_LIBRARY_REMOVE; + break; + case ADD_LIBRARY: + command = MSG2SSP_INST_LIBRARY_ADD; + break; + default: + command = uInst; + break; + } + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + iRet = -ENOMEM; + return iRet; + } + msg->cmd = command; + msg->length = uLength + 1; + msg->options = AP2HUB_WRITE; + msg->buffer = (char*) kzalloc(uLength + 1, GFP_KERNEL); + msg->free_buffer = 1; + + msg->buffer[0] = uSensorType; + memcpy(&msg->buffer[1], uSendBuf, uLength); + + ssp_dbg("[SSP]: %s - Inst = 0x%x, Sensor Type = 0x%x, data = %u\n", + __func__, command, uSensorType, msg->buffer[1]); + + iRet = ssp_spi_async(data, msg); + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - Instruction CMD Fail %d\n", __func__, iRet); + return ERROR; + } + + return iRet; +} + +int send_instruction_sync(struct ssp_data *data, u8 uInst, + u8 uSensorType, u8 *uSendBuf, u8 uLength) +{ + char command; + int iRet = 0; + char buffer[10] = { 0, }; + struct ssp_msg *msg; + + if ((!(data->uSensorState & (1 << uSensorType))) + && (uInst <= CHANGE_DELAY)) { + pr_err("[SSP]: %s - Bypass Inst Skip! - %u\n", + __func__, uSensorType); + return FAIL; + } + + switch (uInst) { + case REMOVE_SENSOR: + command = MSG2SSP_INST_BYPASS_SENSOR_REMOVE; + break; + case ADD_SENSOR: + command = MSG2SSP_INST_BYPASS_SENSOR_ADD; + break; + case CHANGE_DELAY: + command = MSG2SSP_INST_CHANGE_DELAY; + break; + case GO_SLEEP: + command = MSG2SSP_AP_STATUS_SLEEP; + data->uLastAPState = MSG2SSP_AP_STATUS_SLEEP; + break; + case REMOVE_LIBRARY: + command = MSG2SSP_INST_LIBRARY_REMOVE; + break; + case ADD_LIBRARY: + command = MSG2SSP_INST_LIBRARY_ADD; + break; + default: + command = uInst; + break; + } + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + return -ENOMEM; + } + msg->cmd = command; + msg->length = uLength + 1; + msg->options = AP2HUB_WRITE | AP2HUB_RETURN; + msg->buffer = buffer; + msg->free_buffer = 0; + + msg->buffer[0] = uSensorType; + memcpy(&msg->buffer[1], uSendBuf, uLength); + + ssp_dbg("[SSP]: %s - Inst Sync = 0x%x, Sensor Type = %u, data = %u\n", + __func__, command, uSensorType, msg->buffer[0]); + + iRet = ssp_spi_sync(data, msg, 1000); + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - Instruction CMD Fail %d\n", __func__, iRet); + return ERROR; + } + + return buffer[0]; +} + +int flush(struct ssp_data *data, u8 uSensorType) { + int iRet = 0; + char buffer = 0; + + struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + return -ENOMEM; + } + msg->cmd = MSG2SSP_AP_MCU_BATCH_FLUSH; + msg->length = 1; + msg->options = AP2HUB_READ; + msg->data = uSensorType; + msg->buffer = &buffer; + msg->free_buffer = 0; + + iRet = ssp_spi_sync(data, msg, 1000); + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - fail %d\n", __func__, iRet); + return ERROR; + } + + ssp_dbg("[SSP]: %s Sensor Type = 0x%x, data = %u\n", __func__, uSensorType, + buffer); + + return buffer ? 0 : -1; +} + +int get_batch_count(struct ssp_data *data, u8 uSensorType) { + int iRet = 0; + s32 result = 0; + char buffer[4] = { 0, }; + + struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + return -ENOMEM; + } + msg->cmd = MSG2SSP_AP_MCU_BATCH_COUNT; + msg->length = 4; + msg->options = AP2HUB_READ; + msg->data = uSensorType; + msg->buffer = buffer; + msg->free_buffer = 0; + + iRet = ssp_spi_sync(data, msg, 1000); + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - fail %d\n", __func__, iRet); + return ERROR; + } + + memcpy(&result, buffer, 4); + + ssp_dbg("[SSP]: %s Sensor Type = 0x%x, data = %u\n", __func__, uSensorType, + result); + + return result; +} + +int get_chipid(struct ssp_data *data) +{ + int iRet, iReties = 0; + char buffer = 0; + struct ssp_msg *msg; + +retries: + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + return -ENOMEM; + } + msg->cmd = MSG2SSP_AP_WHOAMI; + msg->length = 1; + msg->options = AP2HUB_READ; + msg->buffer = &buffer; + msg->free_buffer = 0; + + iRet = ssp_spi_sync(data, msg, 1000); + + if (buffer != DEVICE_ID && iReties++ < 2) { + mdelay(5); + pr_err("[SSP] %s - get chip ID retry\n", __func__); + goto retries; + } + + if (iRet == SUCCESS) + return buffer; + + pr_err("[SSP] %s - get chip ID failed %d\n", __func__, iRet); + return ERROR; +} + +int set_sensor_position(struct ssp_data *data) +{ + int iRet = 0; + + struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + return -ENOMEM; + } + msg->cmd = MSG2SSP_AP_SENSOR_FORMATION; + msg->length = 3; + msg->options = AP2HUB_WRITE; + msg->buffer = (char*) kzalloc(3, GFP_KERNEL); + msg->free_buffer = 1; + + msg->buffer[0] = data->accel_position; + msg->buffer[1] = data->accel_position; + msg->buffer[2] = data->mag_position; + + iRet = ssp_spi_async(data, msg); + + pr_info("[SSP] Sensor Posision A : %u, G : %u, M: %u, P: %u\n", + data->accel_position, data->accel_position, data->mag_position, 0); + + if (iRet != SUCCESS) { + pr_err("[SSP] %s -fail to set_sensor_position %d\n", __func__, iRet); + iRet = ERROR; + } + + return iRet; +} + +int set_magnetic_static_matrix(struct ssp_data *data) +{ + int iRet = 0; + + struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + return -ENOMEM; + } + msg->cmd = MSG2SSP_AP_SET_MAGNETIC_STATIC_MATRIX; + msg->length = data->mag_matrix_size; + msg->options = AP2HUB_WRITE; + msg->buffer = (char*) kzalloc(data->mag_matrix_size, GFP_KERNEL); + msg->free_buffer = 1; + + memcpy(msg->buffer, data->mag_matrix, data->mag_matrix_size); + + iRet = ssp_spi_async(data, msg); + if (iRet != SUCCESS) { + pr_err("[SSP] %s -fail to set_magnetic_static_matrix %d\n", + __func__, iRet); + iRet = ERROR; + } + + return iRet; +} + +void set_proximity_threshold(struct ssp_data *data, + unsigned int uData1, unsigned int uData2) +{ + int iRet = 0; + + struct ssp_msg *msg; + + if (!(data->uSensorState & (1 << PROXIMITY_SENSOR))) { + pr_info("[SSP]: %s - Skip this function!!!"\ + ", proximity sensor is not connected(0x%x)\n", + __func__, data->uSensorState); + return; + } + + msg= kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", + __func__); + return; + } + msg->cmd = MSG2SSP_AP_SENSOR_PROXTHRESHOLD; + msg->length = 2; + msg->options = AP2HUB_WRITE; + msg->buffer = (char*) kzalloc(4, GFP_KERNEL); + msg->free_buffer = 1; + + msg->buffer[0] = (char)uData1; + msg->buffer[1] = (char)uData2; + + iRet = ssp_spi_async(data, msg); + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - SENSOR_PROXTHRESHOLD CMD fail %d\n", + __func__, iRet); + return; + } + + pr_info("[SSP]: Proximity Threshold - %u, %u\n", uData1, uData2); +} + +void set_proximity_barcode_enable(struct ssp_data *data, bool bEnable) +{ + int iRet = 0; + + struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + return; + } + msg->cmd = MSG2SSP_AP_SENSOR_BARCODE_EMUL; + msg->length = 1; + msg->options = AP2HUB_WRITE; + msg->buffer = (char*) kzalloc(1, GFP_KERNEL); + msg->free_buffer = 1; + + data->bBarcodeEnabled = bEnable; + msg->buffer[0] = bEnable; + + iRet = ssp_spi_async(data, msg); + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - SENSOR_BARCODE_EMUL CMD fail %d\n", + __func__, iRet); + return; + } + + pr_info("[SSP] Proximity Barcode En : %u\n", bEnable); +} + +void set_gesture_current(struct ssp_data *data, unsigned char uData1) +{ + int iRet = 0; + + struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + return; + } + msg->cmd = MSG2SSP_AP_SENSOR_GESTURE_CURRENT; + msg->length = 1; + msg->options = AP2HUB_WRITE; + msg->buffer = (char*) kzalloc(1, GFP_KERNEL); + msg->free_buffer = 1; + + msg->buffer[0] = uData1; + iRet = ssp_spi_async(data, msg); + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - SENSOR_GESTURE_CURRENT CMD fail %d\n", __func__, + iRet); + return; + } + + pr_info("[SSP]: Gesture Current Setting - %u\n", uData1); +} + +unsigned int get_sensor_scanning_info(struct ssp_data *data) { + int iRet = 0, z = 0; + u32 result = 0; + char bin[SENSOR_MAX + 1]; + + struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + return -ENOMEM; + } + msg->cmd = MSG2SSP_AP_SENSOR_SCANNING; + msg->length = 4; + msg->options = AP2HUB_READ; + msg->buffer = (char*) &result; + msg->free_buffer = 0; + + iRet = ssp_spi_sync(data, msg, 1000); + + if (iRet != SUCCESS) + pr_err("[SSP]: %s - i2c fail %d\n", __func__, iRet); + + bin[SENSOR_MAX] = '\0'; + for (z = 0; z < SENSOR_MAX; z++) + bin[SENSOR_MAX - 1 - z] = (result & (1 << z)) ? '1' : '0'; + pr_err("[SSP]: state: %s\n", bin); + + return result; +} + +unsigned int get_firmware_rev(struct ssp_data *data) { + int iRet; + u32 result = SSP_INVALID_REVISION; + + struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + return -ENOMEM; + } + msg->cmd = MSG2SSP_AP_FIRMWARE_REV; + msg->length = 4; + msg->options = AP2HUB_READ; + msg->buffer = (char*) &result; + msg->free_buffer = 0; + + iRet = ssp_spi_sync(data, msg, 1000); + if (iRet != SUCCESS) + pr_err("[SSP]: %s - transfer fail %d\n", __func__, iRet); + + return result; +} + +int set_big_data_start(struct ssp_data *data, u8 type, u32 length) { + int iRet = 0; + + struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + return -ENOMEM; + } + msg->cmd = MSG2SSP_AP_START_BIG_DATA; + msg->length = 5; + msg->options = AP2HUB_WRITE; + msg->buffer = (char*) kzalloc(5, GFP_KERNEL); + msg->free_buffer = 1; + + msg->buffer[0] = type; + memcpy(&msg->buffer[1], &length, 4); + + iRet = ssp_spi_async(data, msg); + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - i2c fail %d\n", __func__, iRet); + iRet = ERROR; + } + + return iRet; +} + +int set_time(struct ssp_data *data) { + int iRet; + struct ssp_msg *msg; + struct timespec ts; + struct rtc_time tm; + + getnstimeofday(&ts); + rtc_time_to_tm(ts.tv_sec, &tm); + pr_info("[SSP]: %s %d-%02d-%02d %02d:%02d:%02d.%09lu UTC\n", __func__, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, + tm.tm_sec, ts.tv_nsec); + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + return -ENOMEM; + } + msg->cmd = MSG2SSP_AP_MCU_SET_TIME; + msg->length = 12; + msg->options = AP2HUB_WRITE; + msg->buffer = (char*) kzalloc(12, GFP_KERNEL); + msg->free_buffer = 1; + + msg->buffer[0] = tm.tm_hour; + msg->buffer[1] = tm.tm_min; + msg->buffer[2] = tm.tm_sec; + msg->buffer[3] = tm.tm_hour > 11 ? 64 : 0; + msg->buffer[4] = tm.tm_wday; + msg->buffer[5] = tm.tm_mon + 1; + msg->buffer[6] = tm.tm_mday; + msg->buffer[7] = tm.tm_year % 100; + memcpy(&msg->buffer[8], &ts.tv_nsec, 4); + + iRet = ssp_spi_async(data, msg); + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - i2c fail %d\n", __func__, iRet); + iRet = ERROR; + } + + return iRet; +} + +int get_time(struct ssp_data *data) { + int iRet; + char buffer[12] = { 0, }; + struct ssp_msg *msg; + struct timespec ts; + struct rtc_time tm; + + getnstimeofday(&ts); + rtc_time_to_tm(ts.tv_sec, &tm); + pr_info("[SSP]: %s ap %d-%02d-%02d %02d:%02d:%02d.%09lu UTC\n", __func__, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, + tm.tm_sec, ts.tv_nsec); + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + pr_err("[SSP] %s, failed to alloc memory for ssp_msg\n", __func__); + return -ENOMEM; + } + msg->cmd = MSG2SSP_AP_MCU_GET_TIME; + msg->length = 12; + msg->options = AP2HUB_READ; + msg->buffer = buffer; + msg->free_buffer = 0; + + iRet = ssp_spi_sync(data, msg, 1000); + + if (iRet != SUCCESS) { + pr_err("[SSP]: %s - i2c failed %d\n", __func__, iRet); + return 0; + } + + tm.tm_hour = buffer[0]; + tm.tm_min = buffer[1]; + tm.tm_sec = buffer[2]; + tm.tm_mon = msg->buffer[5] - 1; + tm.tm_mday = buffer[6]; + tm.tm_year = buffer[7] + 100; + rtc_tm_to_time(&tm, &ts.tv_sec); + memcpy(&ts.tv_nsec, &msg->buffer[8], 4); + + rtc_time_to_tm(ts.tv_sec, &tm); + pr_info("[SSP]: %s mcu %d-%02d-%02d %02d:%02d:%02d.%09lu UTC\n", __func__, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, + tm.tm_sec, ts.tv_nsec); + + return iRet; +} + diff --git a/drivers/sensors/brcm/ssp_iio_ring.c b/drivers/sensors/brcm/ssp_iio_ring.c new file mode 100644 index 0000000..3081cc9 --- /dev/null +++ b/drivers/sensors/brcm/ssp_iio_ring.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2011, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ssp.h" +#include +#include +#include +#include + +void ssp_iio_unconfigure_ring(struct iio_dev *indio_dev) +{ + iio_kfifo_free(indio_dev->buffer); +}; + +static const struct iio_buffer_setup_ops ssp_iio_ring_setup_ops; + +int ssp_iio_configure_ring(struct iio_dev *indio_dev) +{ + struct iio_buffer *ring; + + ring = iio_kfifo_allocate(); + if (!ring) + return -ENOMEM; + + ring->bytes_per_datum = 8; + indio_dev->buffer = ring; + /* setup ring buffer */ + ring->scan_timestamp = true; + indio_dev->setup_ops = &ssp_iio_ring_setup_ops; + /* scan count double count timestamp. should subtract 1. but + * number of channels still includes timestamp + */ + + indio_dev->modes |= INDIO_BUFFER_HARDWARE; + + return 0; +} + diff --git a/drivers/sensors/brcm/ssp_iio_trigger.c b/drivers/sensors/brcm/ssp_iio_trigger.c new file mode 100644 index 0000000..ce11232 --- /dev/null +++ b/drivers/sensors/brcm/ssp_iio_trigger.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2011, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ + +#include "ssp.h" +#include +#include +/* + * ssp_iio_data_rdy_trigger_set_state() set data ready interrupt state + */ +static const struct iio_trigger_ops ssp_iio_trigger_ops; + +int ssp_iio_probe_trigger(struct ssp_data *data, struct iio_dev *indio_dev, struct iio_trigger *trig) +{ + int ret; + + trig = iio_trigger_alloc("%s-dev%d", + indio_dev->name, + indio_dev->id); + if (trig == NULL) + return -ENOMEM; + trig->dev.parent = &data->client->dev; + trig->ops = &ssp_iio_trigger_ops; + ret = iio_trigger_register(trig); + + if (ret) { + iio_trigger_free(trig); + return -EPERM; + } + indio_dev->trig = trig; + + return 0; +} + +void ssp_iio_remove_trigger(struct iio_trigger *trig) +{ + iio_trigger_unregister(trig); + iio_trigger_free(trig); +} diff --git a/drivers/sensors/brcm/ssp_input.c b/drivers/sensors/brcm/ssp_input.c new file mode 100644 index 0000000..82c6c17 --- /dev/null +++ b/drivers/sensors/brcm/ssp_input.c @@ -0,0 +1,1145 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ +#include "ssp.h" + +#include +#include +#include +#include +#include + +/*************************************************************************/ +/* SSP Kernel -> HAL input evnet function */ +/*************************************************************************/ +#define IIO_BUFFER_12_BYTES 20 /* 12 + timestamp 8*/ +#define IIO_BUFFER_6_BYTES 14 +#define IIO_BUFFER_1_BYTES 9 +#define IIO_BUFFER_17_BYTES 25 +#define IIO_BUFFER_24_BYTES 20 +#define IIO_BUFFER_7_BYTES 15 + +#define IIO_ST(si, rb, sb, sh) \ + { .sign = si, .realbits = rb, .storagebits = sb, .shift = sh } + +/* data header defines */ +static int ssp_push_17bytes_buffer(struct iio_dev *indio_dev, u64 t, int *q) +{ + u8 buf[IIO_BUFFER_17_BYTES]; + int i; + + for (i = 0; i < 4; i++) + memcpy(buf + 4 * i, &q[i], sizeof(q[i])); + buf[16] = (u8)q[4]; + memcpy(buf + 17, &t, sizeof(t)); + mutex_lock(&indio_dev->mlock); + iio_push_to_buffers(indio_dev, buf); + mutex_unlock(&indio_dev->mlock); + + return 0; +} + +static int ssp_push_12bytes_buffer(struct iio_dev *indio_dev, u64 t, int *q) +{ + u8 buf[IIO_BUFFER_12_BYTES]; + int i; + + for (i = 0; i < 3; i++) + memcpy(buf + 4 * i, &q[i], sizeof(q[i])); + memcpy(buf + 12, &t, sizeof(t)); + mutex_lock(&indio_dev->mlock); + iio_push_to_buffers(indio_dev, buf); + mutex_unlock(&indio_dev->mlock); + + return 0; +} + +static int ssp_push_6bytes_buffer(struct iio_dev *indio_dev, u64 t, s16 *d) +{ + u8 buf[IIO_BUFFER_6_BYTES]; + int i; + + for (i = 0; i < 3; i++) + memcpy(buf + i * 2, &d[i], sizeof(d[i])); + + memcpy(buf + 6, &t, sizeof(t)); + mutex_lock(&indio_dev->mlock); + iio_push_to_buffers(indio_dev, buf); + mutex_unlock(&indio_dev->mlock); + + return 0; +} + +static int ssp_push_1bytes_buffer(struct iio_dev *indio_dev, u64 t, u8 *d) +{ + u8 buf[IIO_BUFFER_1_BYTES]; + + memcpy(buf, d, sizeof(u8)); + memcpy(buf + 1, &t, sizeof(t)); + mutex_lock(&indio_dev->mlock); + iio_push_to_buffers(indio_dev, buf); + mutex_unlock(&indio_dev->mlock); + + return 0; +} + +static int ssp_push_24bytes_buffer(struct iio_dev *indio_dev, u64 t, s16 *q) +{ + u8 buf[IIO_BUFFER_24_BYTES]; + int i; + + for (i = 0; i < 6; i++) + memcpy(buf + 2 * i, &q[i], sizeof(q[i])); + memcpy(buf + 12, &t, sizeof(t)); + mutex_lock(&indio_dev->mlock); + iio_push_to_buffers(indio_dev, buf); + mutex_unlock(&indio_dev->mlock); + + return 0; +} + +static int ssp_push_7bytes_buffer(struct iio_dev *indio_dev, u64 t, s16 *d, + u8 status) +{ + u8 buf[IIO_BUFFER_7_BYTES]; + int i; + + for (i = 0; i < 3; i++) + memcpy(buf + i * 2, &d[i], sizeof(d[i])); + buf[6] = status; + memcpy(buf + 7, &t, sizeof(t)); + mutex_lock(&indio_dev->mlock); + iio_push_to_buffers(indio_dev, buf); + mutex_unlock(&indio_dev->mlock); + + return 0; +} + +void report_meta_data(struct ssp_data *data, struct sensor_value *s) +{ + pr_info("[SSP]: %s - what: %d, sensor: %d\n", __func__, + s->meta_data.what, s->meta_data.sensor); + + if (s->meta_data.sensor == ACCELEROMETER_SENSOR) { + s16 accel_buf[3]; + memset(accel_buf, 0, sizeof(s16) * 3); + ssp_push_6bytes_buffer(data->accel_indio_dev, 0, accel_buf); + } else if (s->meta_data.sensor == GYROSCOPE_SENSOR) { + int gyro_buf[3]; + memset(gyro_buf, 0, sizeof(int) * 3); + ssp_push_12bytes_buffer(data->gyro_indio_dev, 0, gyro_buf); + } else if (s->meta_data.sensor == GYRO_UNCALIB_SENSOR) { + s16 uncal_gyro_buf[6]; + memset(uncal_gyro_buf, 0, sizeof(s16) * 6); + ssp_push_24bytes_buffer(data->uncal_gyro_indio_dev, + 0, uncal_gyro_buf); + } else if (s->meta_data.sensor == GEOMAGNETIC_SENSOR) { + s16 mag_buf[3]; + memset(mag_buf, 0, sizeof(s16) * 3); + ssp_push_7bytes_buffer(data->mag_indio_dev, 0, mag_buf, 0); + } else if (s->meta_data.sensor == GEOMAGNETIC_UNCALIB_SENSOR) { + s16 uncal_mag_buf[6]; + memset(uncal_mag_buf, 0, sizeof(s16) * 6); + ssp_push_24bytes_buffer(data->uncal_mag_indio_dev, + 0, uncal_mag_buf); + } else if (s->meta_data.sensor == PRESSURE_SENSOR) { + int pressure_buf[3]; + memset(pressure_buf, 0, sizeof(int) * 3); + ssp_push_12bytes_buffer(data->pressure_indio_dev, 0, + pressure_buf); + } else if (s->meta_data.sensor == ROTATION_VECTOR) { + int rot_buf[5]; + memset(rot_buf, 0, sizeof(int) * 5); + ssp_push_17bytes_buffer(data->rot_indio_dev, 0, rot_buf); + } else if (s->meta_data.sensor == GAME_ROTATION_VECTOR) { + int grot_buf[5]; + memset(grot_buf, 0, sizeof(int) * 5); + ssp_push_17bytes_buffer(data->game_rot_indio_dev, 0, grot_buf); + } else if (s->meta_data.sensor == STEP_DETECTOR) { + u8 step_buf[1] = {0}; + ssp_push_1bytes_buffer(data->step_det_indio_dev, 0, step_buf); + } else { + input_report_rel(data->meta_input_dev, REL_DIAL, + s->meta_data.what); + input_report_rel(data->meta_input_dev, REL_HWHEEL, + s->meta_data.sensor + 1); + input_sync(data->meta_input_dev); + } +} + +void report_acc_data(struct ssp_data *data, struct sensor_value *accdata) +{ + s16 accel_buf[3]; + + data->buf[ACCELEROMETER_SENSOR].x = accdata->x; + data->buf[ACCELEROMETER_SENSOR].y = accdata->y; + data->buf[ACCELEROMETER_SENSOR].z = accdata->z; + + accel_buf[0] = data->buf[ACCELEROMETER_SENSOR].x; + accel_buf[1] = data->buf[ACCELEROMETER_SENSOR].y; + accel_buf[2] = data->buf[ACCELEROMETER_SENSOR].z; + + ssp_push_6bytes_buffer(data->accel_indio_dev, accdata->timestamp, + accel_buf); +} + +void report_gyro_data(struct ssp_data *data, struct sensor_value *gyrodata) +{ + int lTemp[3] = {0,}; + + data->buf[GYROSCOPE_SENSOR].x = gyrodata->x; + data->buf[GYROSCOPE_SENSOR].y = gyrodata->y; + data->buf[GYROSCOPE_SENSOR].z = gyrodata->z; + + if (data->uGyroDps == GYROSCOPE_DPS500) { + lTemp[0] = (int)data->buf[GYROSCOPE_SENSOR].x >> 2; + lTemp[1] = (int)data->buf[GYROSCOPE_SENSOR].y >> 2; + lTemp[2] = (int)data->buf[GYROSCOPE_SENSOR].z >> 2; + } else if (data->uGyroDps == GYROSCOPE_DPS250) { + lTemp[0] = (int)data->buf[GYROSCOPE_SENSOR].x >> 3; + lTemp[1] = (int)data->buf[GYROSCOPE_SENSOR].y >> 3; + lTemp[2] = (int)data->buf[GYROSCOPE_SENSOR].z >> 3; + } else { + lTemp[0] = (int)data->buf[GYROSCOPE_SENSOR].x; + lTemp[1] = (int)data->buf[GYROSCOPE_SENSOR].y; + lTemp[2] = (int)data->buf[GYROSCOPE_SENSOR].z; + } + + ssp_push_12bytes_buffer(data->gyro_indio_dev, gyrodata->timestamp, + lTemp); +} + +void report_geomagnetic_raw_data(struct ssp_data *data, + struct sensor_value *magrawdata) +{ + data->buf[GEOMAGNETIC_RAW].x = magrawdata->x; + data->buf[GEOMAGNETIC_RAW].y = magrawdata->y; + data->buf[GEOMAGNETIC_RAW].z = magrawdata->z; +} + +void report_mag_data(struct ssp_data *data, struct sensor_value *magdata) +{ + s16 lTemp[3] = { 0, }; + data->buf[GEOMAGNETIC_SENSOR].cal_x = magdata->cal_x; + data->buf[GEOMAGNETIC_SENSOR].cal_y = magdata->cal_y; + data->buf[GEOMAGNETIC_SENSOR].cal_z = magdata->cal_z; + data->buf[GEOMAGNETIC_SENSOR].accuracy = magdata->accuracy; + + lTemp[0] = data->buf[GEOMAGNETIC_SENSOR].cal_x; + lTemp[1] = data->buf[GEOMAGNETIC_SENSOR].cal_y; + lTemp[2] = data->buf[GEOMAGNETIC_SENSOR].cal_z; + + ssp_push_7bytes_buffer(data->mag_indio_dev, magdata->timestamp, + lTemp, data->buf[GEOMAGNETIC_SENSOR].accuracy); +} + +void report_mag_uncaldata(struct ssp_data *data, struct sensor_value *magdata) +{ + s16 lTemp[6] = {0,}; + data->buf[GEOMAGNETIC_UNCALIB_SENSOR].uncal_x = magdata->uncal_x; + data->buf[GEOMAGNETIC_UNCALIB_SENSOR].uncal_y = magdata->uncal_y; + data->buf[GEOMAGNETIC_UNCALIB_SENSOR].uncal_z = magdata->uncal_z; + data->buf[GEOMAGNETIC_UNCALIB_SENSOR].offset_x= magdata->offset_x; + data->buf[GEOMAGNETIC_UNCALIB_SENSOR].offset_y= magdata->offset_y; + data->buf[GEOMAGNETIC_UNCALIB_SENSOR].offset_z= magdata->offset_z; + + lTemp[0] = data->buf[GEOMAGNETIC_UNCALIB_SENSOR].uncal_x; + lTemp[1] = data->buf[GEOMAGNETIC_UNCALIB_SENSOR].uncal_y; + lTemp[2] = data->buf[GEOMAGNETIC_UNCALIB_SENSOR].uncal_z; + lTemp[3] = data->buf[GEOMAGNETIC_UNCALIB_SENSOR].offset_x; + lTemp[4] = data->buf[GEOMAGNETIC_UNCALIB_SENSOR].offset_y; + lTemp[5] = data->buf[GEOMAGNETIC_UNCALIB_SENSOR].offset_z; + + ssp_push_24bytes_buffer(data->uncal_mag_indio_dev, + magdata->timestamp, lTemp); +} + +void report_uncalib_gyro_data(struct ssp_data *data, struct sensor_value *gyrodata) +{ + s16 lTemp[6] = {0,}; + data->buf[GYRO_UNCALIB_SENSOR].uncal_x = gyrodata->uncal_x; + data->buf[GYRO_UNCALIB_SENSOR].uncal_y = gyrodata->uncal_y; + data->buf[GYRO_UNCALIB_SENSOR].uncal_z = gyrodata->uncal_z; + data->buf[GYRO_UNCALIB_SENSOR].offset_x = gyrodata->offset_x; + data->buf[GYRO_UNCALIB_SENSOR].offset_y = gyrodata->offset_y; + data->buf[GYRO_UNCALIB_SENSOR].offset_z = gyrodata->offset_z; + + lTemp[0] = gyrodata->uncal_x; + lTemp[1] = gyrodata->uncal_y; + lTemp[2] = gyrodata->uncal_z; + lTemp[3] = gyrodata->offset_x; + lTemp[4] = gyrodata->offset_y; + lTemp[5] = gyrodata->offset_z; + + ssp_push_24bytes_buffer(data->uncal_gyro_indio_dev, + gyrodata->timestamp, lTemp); +} + +void report_sig_motion_data(struct ssp_data *data, + struct sensor_value *sig_motion_data) +{ + data->buf[SIG_MOTION_SENSOR].sig_motion = sig_motion_data->sig_motion; + + input_report_rel(data->sig_motion_input_dev, REL_MISC, + data->buf[SIG_MOTION_SENSOR].sig_motion); + input_sync(data->sig_motion_input_dev); +} + +void report_rot_data(struct ssp_data *data, struct sensor_value *rotdata) +{ + int rot_buf[5]; + + data->buf[ROTATION_VECTOR].quat_a = rotdata->quat_a; + data->buf[ROTATION_VECTOR].quat_b = rotdata->quat_b; + data->buf[ROTATION_VECTOR].quat_c = rotdata->quat_c; + data->buf[ROTATION_VECTOR].quat_d = rotdata->quat_d; + data->buf[ROTATION_VECTOR].acc_rot = rotdata->acc_rot; + + rot_buf[0] = rotdata->quat_a; + rot_buf[1] = rotdata->quat_b; + rot_buf[2] = rotdata->quat_c; + rot_buf[3] = rotdata->quat_d; + rot_buf[4] = rotdata->acc_rot; + + ssp_push_17bytes_buffer(data->rot_indio_dev, rotdata->timestamp, + rot_buf); +} + +void report_game_rot_data(struct ssp_data *data, struct sensor_value *grvec_data) +{ + int grot_buf[5]; + + data->buf[GAME_ROTATION_VECTOR].quat_a = grvec_data->quat_a; + data->buf[GAME_ROTATION_VECTOR].quat_b = grvec_data->quat_b; + data->buf[GAME_ROTATION_VECTOR].quat_c = grvec_data->quat_c; + data->buf[GAME_ROTATION_VECTOR].quat_d = grvec_data->quat_d; + data->buf[GAME_ROTATION_VECTOR].acc_rot = grvec_data->acc_rot; + + grot_buf[0] = grvec_data->quat_a; + grot_buf[1] = grvec_data->quat_b; + grot_buf[2] = grvec_data->quat_c; + grot_buf[3] = grvec_data->quat_d; + grot_buf[4] = grvec_data->acc_rot; + + ssp_push_17bytes_buffer(data->game_rot_indio_dev, grvec_data->timestamp, + grot_buf); +} + +void report_gesture_data(struct ssp_data *data, struct sensor_value *gesdata) +{ + int i = 0; + for (i=0; i<20; i++) { + data->buf[GESTURE_SENSOR].data[i] = gesdata->data[i]; + } + + input_report_abs(data->gesture_input_dev, + ABS_X, data->buf[GESTURE_SENSOR].data[0]); + input_report_abs(data->gesture_input_dev, + ABS_Y, data->buf[GESTURE_SENSOR].data[1]); + input_report_abs(data->gesture_input_dev, + ABS_Z, data->buf[GESTURE_SENSOR].data[2]); + input_report_abs(data->gesture_input_dev, + ABS_RX, data->buf[GESTURE_SENSOR].data[3]); + input_report_abs(data->gesture_input_dev, + ABS_RY, data->buf[GESTURE_SENSOR].data[4]); + input_report_abs(data->gesture_input_dev, + ABS_RZ, data->buf[GESTURE_SENSOR].data[5]); + input_report_abs(data->gesture_input_dev, + ABS_THROTTLE, data->buf[GESTURE_SENSOR].data[6]); + input_report_abs(data->gesture_input_dev, + ABS_RUDDER, data->buf[GESTURE_SENSOR].data[7]); + input_report_abs(data->gesture_input_dev, + ABS_WHEEL, data->buf[GESTURE_SENSOR].data[8]); + input_report_abs(data->gesture_input_dev, + ABS_GAS, data->buf[GESTURE_SENSOR].data[9]); + input_report_abs(data->gesture_input_dev, + ABS_BRAKE, data->buf[GESTURE_SENSOR].data[10]); + input_report_abs(data->gesture_input_dev, + ABS_HAT0X, data->buf[GESTURE_SENSOR].data[11]); + input_report_abs(data->gesture_input_dev, + ABS_HAT0Y, data->buf[GESTURE_SENSOR].data[12]); + input_report_abs(data->gesture_input_dev, + ABS_HAT1X, data->buf[GESTURE_SENSOR].data[13]); + input_report_abs(data->gesture_input_dev, + ABS_HAT1Y, data->buf[GESTURE_SENSOR].data[14]); + input_report_abs(data->gesture_input_dev, + ABS_HAT2X, data->buf[GESTURE_SENSOR].data[15]); + input_report_abs(data->gesture_input_dev, + ABS_HAT2Y, data->buf[GESTURE_SENSOR].data[16]); + input_report_abs(data->gesture_input_dev, + ABS_HAT3X, data->buf[GESTURE_SENSOR].data[17]); + input_report_abs(data->gesture_input_dev, + ABS_HAT3Y, data->buf[GESTURE_SENSOR].data[18]); + input_report_abs(data->gesture_input_dev, + ABS_PRESSURE, data->buf[GESTURE_SENSOR].data[19]); + + input_sync(data->gesture_input_dev); +} + +void report_pressure_data(struct ssp_data *data, struct sensor_value *predata) +{ + int temp[3] = {0, }; + data->buf[PRESSURE_SENSOR].pressure[0] = + predata->pressure[0] - data->iPressureCal; + data->buf[PRESSURE_SENSOR].pressure[1] = predata->pressure[1]; + + temp[0] = data->buf[PRESSURE_SENSOR].pressure[0]; + temp[1] = data->buf[PRESSURE_SENSOR].pressure[1]; + temp[2] = data->sealevelpressure; + + ssp_push_12bytes_buffer(data->pressure_indio_dev, predata->timestamp, + temp); +} + +void report_light_data(struct ssp_data *data, struct sensor_value *lightdata) +{ + data->buf[LIGHT_SENSOR].r = lightdata->r; + data->buf[LIGHT_SENSOR].g = lightdata->g; + data->buf[LIGHT_SENSOR].b = lightdata->b; + data->buf[LIGHT_SENSOR].w = lightdata->w; +#if defined(CONFIG_SENSORS_SSP_TMG399X) + data->buf[LIGHT_SENSOR].a_time = lightdata->a_time; + data->buf[LIGHT_SENSOR].a_gain = (0x03) & (lightdata->a_gain); +#endif + + input_report_rel(data->light_input_dev, REL_HWHEEL, + data->buf[LIGHT_SENSOR].r + 1); + input_report_rel(data->light_input_dev, REL_DIAL, + data->buf[LIGHT_SENSOR].g + 1); + input_report_rel(data->light_input_dev, REL_WHEEL, + data->buf[LIGHT_SENSOR].b + 1); + input_report_rel(data->light_input_dev, REL_MISC, + data->buf[LIGHT_SENSOR].w + 1); + +#if defined(CONFIG_SENSORS_SSP_TMG399X) + input_report_rel(data->light_input_dev, REL_RY, + data->buf[LIGHT_SENSOR].a_time + 1); + input_report_rel(data->light_input_dev, REL_RZ, + data->buf[LIGHT_SENSOR].a_gain + 1); +#endif + input_sync(data->light_input_dev); +} + +void report_prox_data(struct ssp_data *data, struct sensor_value *proxdata) +{ + ssp_dbg("[SSP] Proximity Sensor Detect : %u, raw : %u\n", + proxdata->prox[0], proxdata->prox[1]); + + data->buf[PROXIMITY_SENSOR].prox[0] = proxdata->prox[0]; + data->buf[PROXIMITY_SENSOR].prox[1] = proxdata->prox[1]; + + input_report_abs(data->prox_input_dev, ABS_DISTANCE, + (!proxdata->prox[0])); + input_sync(data->prox_input_dev); + + wake_lock_timeout(&data->ssp_wake_lock, 3 * HZ); +} + +void report_prox_raw_data(struct ssp_data *data, + struct sensor_value *proxrawdata) +{ + if (data->uFactoryProxAvg[0]++ >= PROX_AVG_READ_NUM) { + data->uFactoryProxAvg[2] /= PROX_AVG_READ_NUM; + data->buf[PROXIMITY_RAW].prox[1] = (u16)data->uFactoryProxAvg[1]; + data->buf[PROXIMITY_RAW].prox[2] = (u16)data->uFactoryProxAvg[2]; + data->buf[PROXIMITY_RAW].prox[3] = (u16)data->uFactoryProxAvg[3]; + + data->uFactoryProxAvg[0] = 0; + data->uFactoryProxAvg[1] = 0; + data->uFactoryProxAvg[2] = 0; + data->uFactoryProxAvg[3] = 0; + } else { + data->uFactoryProxAvg[2] += proxrawdata->prox[0]; + + if (data->uFactoryProxAvg[0] == 1) + data->uFactoryProxAvg[1] = proxrawdata->prox[0]; + else if (proxrawdata->prox[0] < data->uFactoryProxAvg[1]) + data->uFactoryProxAvg[1] = proxrawdata->prox[0]; + + if (proxrawdata->prox[0] > data->uFactoryProxAvg[3]) + data->uFactoryProxAvg[3] = proxrawdata->prox[0]; + } + + data->buf[PROXIMITY_RAW].prox[0] = proxrawdata->prox[0]; +} + +void report_step_det_data(struct ssp_data *data, + struct sensor_value *stepdet_data) +{ + data->buf[STEP_DETECTOR].step_det = stepdet_data->step_det; + ssp_push_1bytes_buffer(data->step_det_indio_dev, stepdet_data->timestamp, + &stepdet_data->step_det); +} + +void report_step_cnt_data(struct ssp_data *data, + struct sensor_value *sig_motion_data) +{ + data->buf[STEP_COUNTER].step_diff = sig_motion_data->step_diff; + + data->step_count_total += data->buf[STEP_COUNTER].step_diff; + + input_report_rel(data->step_cnt_input_dev, REL_MISC, + data->step_count_total + 1); + input_sync(data->step_cnt_input_dev); +} + +void report_temp_humidity_data(struct ssp_data *data, + struct sensor_value *temp_humi_data) +{ + data->buf[TEMPERATURE_HUMIDITY_SENSOR].x = temp_humi_data->x; + data->buf[TEMPERATURE_HUMIDITY_SENSOR].y = temp_humi_data->y; + data->buf[TEMPERATURE_HUMIDITY_SENSOR].z = temp_humi_data->z; + + /* Temperature */ + input_report_rel(data->temp_humi_input_dev, REL_HWHEEL, + data->buf[TEMPERATURE_HUMIDITY_SENSOR].x); + /* Humidity */ + input_report_rel(data->temp_humi_input_dev, REL_DIAL, + data->buf[TEMPERATURE_HUMIDITY_SENSOR].y); + input_sync(data->temp_humi_input_dev); + if (data->buf[TEMPERATURE_HUMIDITY_SENSOR].z) + wake_lock_timeout(&data->ssp_wake_lock, 2 * HZ); +} + +int initialize_event_symlink(struct ssp_data *data) +{ + int iRet = 0; + + iRet = sensors_create_symlink(data->gesture_input_dev); + if (iRet < 0) + goto iRet_gesture_sysfs_create_link; + + iRet = sensors_create_symlink(data->light_input_dev); + if (iRet < 0) + goto iRet_light_sysfs_create_link; + + iRet = sensors_create_symlink(data->prox_input_dev); + if (iRet < 0) + goto iRet_prox_sysfs_create_link; + + iRet = sensors_create_symlink(data->temp_humi_input_dev); + if (iRet < 0) + goto iRet_temp_humi_sysfs_create_link; + + iRet = sensors_create_symlink(data->sig_motion_input_dev); + if (iRet < 0) + goto iRet_sig_motion_sysfs_create_link; + + iRet = sensors_create_symlink(data->step_cnt_input_dev); + if (iRet < 0) + goto iRet_step_cnt_sysfs_create_link; + + iRet = sensors_create_symlink(data->meta_input_dev); + if (iRet < 0) + goto iRet_meta_sysfs_create_link; + + return SUCCESS; + +iRet_meta_sysfs_create_link: + sensors_remove_symlink(data->step_cnt_input_dev); +iRet_step_cnt_sysfs_create_link: + sensors_remove_symlink(data->sig_motion_input_dev); +iRet_sig_motion_sysfs_create_link: + sensors_remove_symlink(data->temp_humi_input_dev); +iRet_temp_humi_sysfs_create_link: + sensors_remove_symlink(data->prox_input_dev); +iRet_prox_sysfs_create_link: + sensors_remove_symlink(data->light_input_dev); +iRet_light_sysfs_create_link: + sensors_remove_symlink(data->gesture_input_dev); +iRet_gesture_sysfs_create_link: + pr_err("[SSP]: %s - could not create event symlink\n", __func__); + + return FAIL; +} + +void remove_event_symlink(struct ssp_data *data) +{ + sensors_remove_symlink(data->gesture_input_dev); + sensors_remove_symlink(data->light_input_dev); + sensors_remove_symlink(data->prox_input_dev); + sensors_remove_symlink(data->temp_humi_input_dev); + sensors_remove_symlink(data->sig_motion_input_dev); + sensors_remove_symlink(data->step_cnt_input_dev); + sensors_remove_symlink(data->meta_input_dev); +} + +static const struct iio_info accel_info = { + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec accel_channels[] = { + { + .type = IIO_TIMESTAMP, + .channel = -1, + .scan_index = 3, + .scan_type = IIO_ST('s', IIO_BUFFER_6_BYTES*8, + IIO_BUFFER_6_BYTES*8, 0) + } +}; + +static const struct iio_info gyro_info = { + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec gyro_channels[] = { + { + .type = IIO_TIMESTAMP, + .channel = -1, + .scan_index = 3, + .scan_type = IIO_ST('s', IIO_BUFFER_12_BYTES*8, + IIO_BUFFER_12_BYTES*8, 0) + } +}; + +static const struct iio_info uncal_gyro_info = { + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec uncal_gyro_channels[] = { + { + .type = IIO_TIMESTAMP, + .channel = -1, + .scan_index = 3, + .scan_type = IIO_ST('s', IIO_BUFFER_12_BYTES * 8, + IIO_BUFFER_12_BYTES * 8, 0) + } +}; + +static const struct iio_info mag_info = { + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec mag_channels[] = { + { + .type = IIO_TIMESTAMP, + .channel = -1, + .scan_index = 3, + .scan_type = IIO_ST('s', IIO_BUFFER_7_BYTES * 8, + IIO_BUFFER_7_BYTES * 8, 0) + } +}; + +static const struct iio_info uncal_mag_info = { + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec uncal_mag_channels[] = { + { + .type = IIO_TIMESTAMP, + .channel = -1, + .scan_index = 3, + .scan_type = IIO_ST('s', IIO_BUFFER_12_BYTES * 8, + IIO_BUFFER_12_BYTES * 8, 0) + } +}; + +static const struct iio_info game_rot_info = { + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec game_rot_channels[] = { + { + .type = IIO_TIMESTAMP, + .channel = -1, + .scan_index = 3, + .scan_type = IIO_ST('s', IIO_BUFFER_17_BYTES*8, + IIO_BUFFER_17_BYTES*8, 0) + } +}; + +static const struct iio_info rot_info = { + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec rot_channels[] = { + { + .type = IIO_TIMESTAMP, + .channel = -1, + .scan_index = 3, + .scan_type = IIO_ST('s', IIO_BUFFER_17_BYTES*8, + IIO_BUFFER_17_BYTES*8, 0) + } +}; + +static const struct iio_info step_det_info = { + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec step_det_channels[] = { + { + .type = IIO_TIMESTAMP, + .channel = -1, + .scan_index = 3, + .scan_type = IIO_ST('s', IIO_BUFFER_1_BYTES*8, + IIO_BUFFER_1_BYTES*8, 0) + } +}; + +static const struct iio_info pressure_info = { + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec pressure_channels[] = { + { + .type = IIO_TIMESTAMP, + .channel = -1, + .scan_index = 3, + .scan_type = IIO_ST('s', IIO_BUFFER_12_BYTES * 8, + IIO_BUFFER_12_BYTES * 8, 0) + } +}; + +int initialize_input_dev(struct ssp_data *data) +{ + int iRet = 0; + + /* Registering iio device - start */ + data->accel_indio_dev = iio_device_alloc(0); + if (!data->accel_indio_dev) + goto err_alloc_accel; + + data->accel_indio_dev->name = "accelerometer_sensor"; + data->accel_indio_dev->dev.parent = &data->spi->dev; + data->accel_indio_dev->info = &accel_info; + data->accel_indio_dev->channels = accel_channels; + data->accel_indio_dev->num_channels = ARRAY_SIZE(accel_channels); + data->accel_indio_dev->modes = INDIO_DIRECT_MODE; + data->accel_indio_dev->currentmode = INDIO_DIRECT_MODE; + + iRet = ssp_iio_configure_ring(data->accel_indio_dev); + if (iRet) + goto err_config_ring_accel; + + iRet = iio_device_register(data->accel_indio_dev); + if (iRet) + goto err_register_device_accel; + + data->gyro_indio_dev = iio_device_alloc(0); + if (!data->gyro_indio_dev) + goto err_alloc_gyro; + + data->gyro_indio_dev->name = "gyro_sensor"; + data->gyro_indio_dev->dev.parent = &data->spi->dev; + data->gyro_indio_dev->info = &gyro_info; + data->gyro_indio_dev->channels = gyro_channels; + data->gyro_indio_dev->num_channels = ARRAY_SIZE(gyro_channels); + data->gyro_indio_dev->modes = INDIO_DIRECT_MODE; + data->gyro_indio_dev->currentmode = INDIO_DIRECT_MODE; + + iRet = ssp_iio_configure_ring(data->gyro_indio_dev); + if (iRet) + goto err_config_ring_gyro; + + iRet = iio_device_register(data->gyro_indio_dev); + if (iRet) + goto err_register_device_gyro; + + data->uncal_gyro_indio_dev = iio_device_alloc(0); + if (!data->uncal_gyro_indio_dev) + goto err_alloc_uncal_gyro; + + data->uncal_gyro_indio_dev->name = "uncal_gyro_sensor"; + data->uncal_gyro_indio_dev->dev.parent = &data->spi->dev; + data->uncal_gyro_indio_dev->info = &uncal_gyro_info; + data->uncal_gyro_indio_dev->channels = uncal_gyro_channels; + data->uncal_gyro_indio_dev->num_channels = ARRAY_SIZE(uncal_gyro_channels); + data->uncal_gyro_indio_dev->modes = INDIO_DIRECT_MODE; + data->uncal_gyro_indio_dev->currentmode = INDIO_DIRECT_MODE; + + iRet = ssp_iio_configure_ring(data->uncal_gyro_indio_dev); + if (iRet) + goto err_config_ring_uncal_gyro; + + iRet = iio_device_register(data->uncal_gyro_indio_dev); + if (iRet) + goto err_register_device_uncal_gyro; + + data->mag_indio_dev = iio_device_alloc(0); + if (!data->mag_indio_dev) + goto err_alloc_mag; + + data->mag_indio_dev->name = "geomagnetic_sensor"; + data->mag_indio_dev->dev.parent = &data->spi->dev; + data->mag_indio_dev->info = &mag_info; + data->mag_indio_dev->channels = mag_channels; + data->mag_indio_dev->num_channels = ARRAY_SIZE(mag_channels); + data->mag_indio_dev->modes = INDIO_DIRECT_MODE; + data->mag_indio_dev->currentmode = INDIO_DIRECT_MODE; + + iRet = ssp_iio_configure_ring(data->mag_indio_dev); + if (iRet) + goto err_config_ring_mag; + + iRet = iio_device_register(data->mag_indio_dev); + if (iRet) + goto err_register_device_mag; + + data->uncal_mag_indio_dev = iio_device_alloc(0); + if (!data->uncal_mag_indio_dev) + goto err_alloc_uncal_mag; + + data->uncal_mag_indio_dev->name = "uncal_geomagnetic_sensor"; + data->uncal_mag_indio_dev->dev.parent = &data->spi->dev; + data->uncal_mag_indio_dev->info = &uncal_mag_info; + data->uncal_mag_indio_dev->channels = uncal_mag_channels; + data->uncal_mag_indio_dev->num_channels = ARRAY_SIZE(uncal_mag_channels); + data->uncal_mag_indio_dev->modes = INDIO_DIRECT_MODE; + data->uncal_mag_indio_dev->currentmode = INDIO_DIRECT_MODE; + + iRet = ssp_iio_configure_ring(data->uncal_mag_indio_dev); + if (iRet) + goto err_config_ring_uncal_mag; + + iRet = iio_device_register(data->uncal_mag_indio_dev); + if (iRet) + goto err_register_device_uncal_mag; + + data->game_rot_indio_dev = iio_device_alloc(0); + if (!data->game_rot_indio_dev) + goto err_alloc_game_rot; + + data->game_rot_indio_dev->name = "game_rotation_vector"; + data->game_rot_indio_dev->dev.parent = &data->spi->dev; + data->game_rot_indio_dev->info = &game_rot_info; + data->game_rot_indio_dev->channels = game_rot_channels; + data->game_rot_indio_dev->num_channels = ARRAY_SIZE(game_rot_channels); + data->game_rot_indio_dev->modes = INDIO_DIRECT_MODE; + data->game_rot_indio_dev->currentmode = INDIO_DIRECT_MODE; + + iRet = ssp_iio_configure_ring(data->game_rot_indio_dev); + if (iRet) + goto err_config_ring_game_rot; + + iRet = iio_device_register(data->game_rot_indio_dev); + if (iRet) + goto err_register_device_game_rot; + + data->rot_indio_dev = iio_device_alloc(0); + if (!data->rot_indio_dev) + goto err_alloc_rot; + + data->rot_indio_dev->name = "rotation_vector_sensor"; + data->rot_indio_dev->dev.parent = &data->spi->dev; + data->rot_indio_dev->info = &rot_info; + data->rot_indio_dev->channels = rot_channels; + data->rot_indio_dev->num_channels = ARRAY_SIZE(rot_channels); + data->rot_indio_dev->modes = INDIO_DIRECT_MODE; + data->rot_indio_dev->currentmode = INDIO_DIRECT_MODE; + + iRet = ssp_iio_configure_ring(data->rot_indio_dev); + if (iRet) + goto err_config_ring_rot; + + iRet = iio_device_register(data->rot_indio_dev); + if (iRet) + goto err_register_device_rot; + + data->step_det_indio_dev= iio_device_alloc(0); + if (!data->step_det_indio_dev) + goto err_alloc_step_det; + + data->step_det_indio_dev->name = "step_det_sensor"; + data->step_det_indio_dev->dev.parent = &data->spi->dev; + data->step_det_indio_dev->info = &step_det_info; + data->step_det_indio_dev->channels = step_det_channels; + data->step_det_indio_dev->num_channels = ARRAY_SIZE(step_det_channels); + data->step_det_indio_dev->modes = INDIO_DIRECT_MODE; + data->step_det_indio_dev->currentmode = INDIO_DIRECT_MODE; + + iRet = ssp_iio_configure_ring(data->step_det_indio_dev); + if (iRet) + goto err_config_ring_step_det; + + iRet = iio_device_register(data->step_det_indio_dev); + if (iRet) + goto err_register_device_step_det; + + data->pressure_indio_dev = iio_device_alloc(0); + if (!data->pressure_indio_dev) + goto err_alloc_pressure; + data->pressure_indio_dev->name = "pressure_sensor"; + data->pressure_indio_dev->dev.parent = &data->spi->dev; + data->pressure_indio_dev->info = &pressure_info; + data->pressure_indio_dev->channels = pressure_channels; + data->pressure_indio_dev->num_channels = ARRAY_SIZE(pressure_channels); + data->pressure_indio_dev->modes = INDIO_DIRECT_MODE; + data->pressure_indio_dev->currentmode = INDIO_DIRECT_MODE; + + iRet = ssp_iio_configure_ring(data->pressure_indio_dev); + if (iRet) + goto err_config_ring_pressure; + + iRet = iio_device_register(data->pressure_indio_dev); + if (iRet) + goto err_register_device_pressure; + /* Registering iio device - end */ + + data->light_input_dev = input_allocate_device(); + if (data->light_input_dev == NULL) + goto err_initialize_light_input_dev; + + data->light_input_dev->name = "light_sensor"; + input_set_capability(data->light_input_dev, EV_REL, REL_HWHEEL); + input_set_capability(data->light_input_dev, EV_REL, REL_DIAL); + input_set_capability(data->light_input_dev, EV_REL, REL_WHEEL); + input_set_capability(data->light_input_dev, EV_REL, REL_MISC); +#if defined(CONFIG_SENSORS_SSP_TMG399X) + input_set_capability(data->light_input_dev, EV_REL, REL_RY); + input_set_capability(data->light_input_dev, EV_REL, REL_RZ); +#endif + iRet = input_register_device(data->light_input_dev); + if (iRet < 0) { + input_free_device(data->light_input_dev); + goto err_initialize_light_input_dev; + } + input_set_drvdata(data->light_input_dev, data); + + data->prox_input_dev = input_allocate_device(); + if (data->prox_input_dev == NULL) + goto err_initialize_proximity_input_dev; + + data->prox_input_dev->name = "proximity_sensor"; + input_set_capability(data->prox_input_dev, EV_ABS, ABS_DISTANCE); + input_set_abs_params(data->prox_input_dev, ABS_DISTANCE, 0, 1, 0, 0); + iRet = input_register_device(data->prox_input_dev); + if (iRet < 0) { + input_free_device(data->prox_input_dev); + goto err_initialize_proximity_input_dev; + } + input_set_drvdata(data->prox_input_dev, data); + + data->temp_humi_input_dev = input_allocate_device(); + if (data->temp_humi_input_dev == NULL) + goto err_initialize_temp_humi_input_dev; + + data->temp_humi_input_dev->name = "temp_humidity_sensor"; + input_set_capability(data->temp_humi_input_dev, EV_REL, REL_HWHEEL); + input_set_capability(data->temp_humi_input_dev, EV_REL, REL_DIAL); + input_set_capability(data->temp_humi_input_dev, EV_REL, REL_WHEEL); + iRet = input_register_device(data->temp_humi_input_dev); + if (iRet < 0) { + input_free_device(data->temp_humi_input_dev); + goto err_initialize_temp_humi_input_dev; + } + input_set_drvdata(data->temp_humi_input_dev, data); + + data->gesture_input_dev = input_allocate_device(); + if (data->gesture_input_dev == NULL) + goto err_initialize_gesture_input_dev; + + data->gesture_input_dev->name = "gesture_sensor"; + input_set_capability(data->gesture_input_dev, EV_ABS, ABS_X); + input_set_abs_params(data->gesture_input_dev, ABS_X, 0, 1024, 0, 0); + input_set_capability(data->gesture_input_dev, EV_ABS, ABS_Y); + input_set_abs_params(data->gesture_input_dev, ABS_Y, 0, 1024, 0, 0); + input_set_capability(data->gesture_input_dev, EV_ABS, ABS_Z); + input_set_abs_params(data->gesture_input_dev, ABS_Z, 0, 1024, 0, 0); + input_set_capability(data->gesture_input_dev, EV_ABS, ABS_RX); + input_set_abs_params(data->gesture_input_dev, ABS_RX, 0, 1024, 0, 0); + input_set_capability(data->gesture_input_dev, EV_ABS, ABS_RY); + input_set_abs_params(data->gesture_input_dev, ABS_RY, 0, 1024, 0, 0); + input_set_capability(data->gesture_input_dev, EV_ABS, ABS_RZ); + input_set_abs_params(data->gesture_input_dev, ABS_RZ, 0, 1024, 0, 0); + input_set_capability(data->gesture_input_dev, EV_ABS, ABS_THROTTLE); + input_set_abs_params(data->gesture_input_dev, ABS_THROTTLE, 0, 1024, 0, 0); + input_set_capability(data->gesture_input_dev, EV_ABS, ABS_RUDDER); + input_set_abs_params(data->gesture_input_dev, ABS_RUDDER, 0, 1024, 0, 0); + input_set_capability(data->gesture_input_dev, EV_ABS, ABS_WHEEL); + input_set_abs_params(data->gesture_input_dev, ABS_WHEEL, 0, 1024, 0, 0); + input_set_capability(data->gesture_input_dev, EV_ABS, ABS_GAS); + input_set_abs_params(data->gesture_input_dev, ABS_GAS, 0, 1024, 0, 0); + input_set_capability(data->gesture_input_dev, EV_ABS, ABS_BRAKE); + input_set_abs_params(data->gesture_input_dev, ABS_BRAKE, 0, 1024, 0, 0); + input_set_capability(data->gesture_input_dev, EV_ABS, ABS_HAT0X); + input_set_abs_params(data->gesture_input_dev, ABS_HAT0X, 0, 1024, 0, 0); + input_set_capability(data->gesture_input_dev, EV_ABS, ABS_HAT0Y); + input_set_abs_params(data->gesture_input_dev, ABS_HAT0Y, 0, 1024, 0, 0); + input_set_capability(data->gesture_input_dev, EV_ABS, ABS_HAT1X); + input_set_abs_params(data->gesture_input_dev, ABS_HAT1X, 0, 1024, 0, 0); + input_set_capability(data->gesture_input_dev, EV_ABS, ABS_HAT1Y); + input_set_abs_params(data->gesture_input_dev, ABS_HAT1Y, 0, 1024, 0, 0); + input_set_capability(data->gesture_input_dev, EV_ABS, ABS_HAT2X); + input_set_abs_params(data->gesture_input_dev, ABS_HAT2X, 0, 1024, 0, 0); + input_set_capability(data->gesture_input_dev, EV_ABS, ABS_HAT2Y); + input_set_abs_params(data->gesture_input_dev, ABS_HAT2Y, 0, 1024, 0, 0); + input_set_capability(data->gesture_input_dev, EV_ABS, ABS_HAT3X); + input_set_abs_params(data->gesture_input_dev, ABS_HAT3X, 0, 1024, 0, 0); + input_set_capability(data->gesture_input_dev, EV_ABS, ABS_HAT3Y); + input_set_abs_params(data->gesture_input_dev, ABS_HAT3Y, 0, 1024, 0, 0); + input_set_capability(data->gesture_input_dev, EV_ABS, ABS_PRESSURE); + input_set_abs_params(data->gesture_input_dev, ABS_PRESSURE, 0, 1024, 0, 0); + iRet = input_register_device(data->gesture_input_dev); + if (iRet < 0) { + input_free_device(data->gesture_input_dev); + goto err_initialize_gesture_input_dev; + } + input_set_drvdata(data->gesture_input_dev, data); + + data->sig_motion_input_dev = input_allocate_device(); + if (data->sig_motion_input_dev == NULL) + goto err_initialize_sig_motion_input_dev; + + data->sig_motion_input_dev->name = "sig_motion_sensor"; + input_set_capability(data->sig_motion_input_dev, EV_REL, REL_MISC); + iRet = input_register_device(data->sig_motion_input_dev); + if (iRet < 0) { + input_free_device(data->sig_motion_input_dev); + goto err_initialize_sig_motion_input_dev; + } + input_set_drvdata(data->sig_motion_input_dev, data); + + data->step_cnt_input_dev = input_allocate_device(); + if (data->step_cnt_input_dev == NULL) + goto err_initialize_step_cnt_input_dev; + + data->step_cnt_input_dev->name = "step_cnt_sensor"; + input_set_capability(data->step_cnt_input_dev, EV_REL, REL_MISC); + iRet = input_register_device(data->step_cnt_input_dev); + if (iRet < 0) { + input_free_device(data->step_cnt_input_dev); + goto err_initialize_step_cnt_input_dev; + } + input_set_drvdata(data->step_cnt_input_dev, data); + + data->meta_input_dev= input_allocate_device(); + if (data->meta_input_dev == NULL) + goto err_initialize_meta_input_dev; + + data->meta_input_dev->name = "meta_event"; + input_set_capability(data->meta_input_dev, EV_REL, REL_HWHEEL); + input_set_capability(data->meta_input_dev, EV_REL, REL_DIAL); + iRet = input_register_device(data->meta_input_dev); + if (iRet < 0) { + input_free_device(data->meta_input_dev); + goto err_initialize_meta_input_dev; + } + input_set_drvdata(data->meta_input_dev, data); + + return SUCCESS; + +err_initialize_meta_input_dev: + pr_err("[SSP]: %s - could not allocate meta event input device\n", __func__); + input_unregister_device(data->step_cnt_input_dev); +err_initialize_step_cnt_input_dev: + pr_err("[SSP]: %s - could not allocate step cnt input device\n", __func__); + input_unregister_device(data->sig_motion_input_dev); +err_initialize_sig_motion_input_dev: + pr_err("[SSP]: %s - could not allocate sig motion input device\n", __func__); + input_unregister_device(data->gesture_input_dev); +err_initialize_gesture_input_dev: + pr_err("[SSP]: %s - could not allocate gesture input device\n", __func__); + input_unregister_device(data->temp_humi_input_dev); +err_initialize_temp_humi_input_dev: + pr_err("[SSP]: %s - could not allocate temp_humi input device\n", __func__); + input_unregister_device(data->prox_input_dev); +err_initialize_proximity_input_dev: + pr_err("[SSP]: %s - could not allocate proximity input device\n", __func__); + input_unregister_device(data->light_input_dev); +err_initialize_light_input_dev: + pr_err("[SSP]: %s - could not allocate light input device\n", __func__); + iio_device_unregister(data->pressure_indio_dev); +err_register_device_pressure: + pr_err("[SSP]: failed to register pressure_sensor device\n"); + ssp_iio_unconfigure_ring(data->pressure_indio_dev); +err_config_ring_pressure: + pr_err("[SSP]: failed to configure pressure_sensor ring buffer\n"); + iio_device_free(data->pressure_indio_dev); +err_alloc_pressure: + pr_err("[SSP]: failed to allocate memory for iio pressure_sensor device\n"); + iio_device_unregister(data->step_det_indio_dev); +err_register_device_step_det: + pr_err("[SSP]: failed to register step_det device\n"); + ssp_iio_unconfigure_ring(data->step_det_indio_dev); +err_config_ring_step_det: + pr_err("[SSP]: failed to configure step_det ring buffer\n"); + iio_device_free(data->step_det_indio_dev); +err_alloc_step_det: + pr_err("[SSP]: failed to allocate memory for iio step_det device\n"); + iio_device_unregister(data->rot_indio_dev); +err_register_device_rot: + pr_err("[SSP]: failed to register rot device\n"); + ssp_iio_unconfigure_ring(data->rot_indio_dev); +err_config_ring_rot: + pr_err("[SSP]: failed to configure rot ring buffer\n"); + iio_device_free(data->rot_indio_dev); +err_alloc_rot: + pr_err("[SSP]: failed to allocate memory for iio rot device\n"); + iio_device_unregister(data->game_rot_indio_dev); +err_register_device_game_rot: + pr_err("[SSP]: failed to register game_rot device\n"); + ssp_iio_unconfigure_ring(data->game_rot_indio_dev); +err_config_ring_game_rot: + pr_err("[SSP]: failed to configure game_rot ring buffer\n"); + iio_device_free(data->game_rot_indio_dev); +err_alloc_game_rot: + pr_err("[SSP]: failed to allocate memory for iio game_rot device\n"); + iio_device_unregister(data->uncal_mag_indio_dev); +err_register_device_uncal_mag: + pr_err("[SSP]: failed to register uncal mag device\n"); + ssp_iio_unconfigure_ring(data->uncal_mag_indio_dev); +err_config_ring_uncal_mag: + pr_err("[SSP]: failed to configure uncal mag ring buffer\n"); + iio_device_free(data->uncal_mag_indio_dev); +err_alloc_uncal_mag: + pr_err("[SSP]: failed to allocate memory for iio uncal mag device\n"); + iio_device_unregister(data->mag_indio_dev); +err_register_device_mag: + pr_err("[SSP]: failed to register mag device\n"); + ssp_iio_unconfigure_ring(data->mag_indio_dev); +err_config_ring_mag: + pr_err("[SSP]: failed to configure mag ring buffer\n"); + iio_device_free(data->mag_indio_dev); +err_alloc_mag: + pr_err("[SSP]: failed to allocate memory for iio uncal mag device\n"); + iio_device_unregister(data->uncal_gyro_indio_dev); +err_register_device_uncal_gyro: + pr_err("[SSP]: failed to register uncal gyro device\n"); + ssp_iio_unconfigure_ring(data->uncal_gyro_indio_dev); +err_config_ring_uncal_gyro: + pr_err("[SSP]: failed to configure uncal gyro ring buffer\n"); + iio_device_free(data->uncal_gyro_indio_dev); +err_alloc_uncal_gyro: + pr_err("[SSP]: failed to allocate memory for iio uncal gyro device\n"); + iio_device_unregister(data->gyro_indio_dev); +err_register_device_gyro: + pr_err("[SSP]: failed to register gyro device\n"); + ssp_iio_unconfigure_ring(data->gyro_indio_dev); +err_config_ring_gyro: + pr_err("[SSP]: failed to configure gyro ring buffer\n"); + iio_device_free(data->gyro_indio_dev); +err_alloc_gyro: + pr_err("[SSP]: failed to allocate memory for iio gyro device\n"); + iio_device_unregister(data->accel_indio_dev); +err_register_device_accel: + pr_err("[SSP]: failed to register accel device\n"); + ssp_iio_unconfigure_ring(data->accel_indio_dev); +err_config_ring_accel: + pr_err("[SSP]: failed to configure accel ring buffer\n"); + iio_device_free(data->accel_indio_dev); +err_alloc_accel: + pr_err("[SSP]: failed to allocate memory for iio accel device\n"); + return ERROR; +} + +void remove_input_dev(struct ssp_data *data) +{ + input_unregister_device(data->gesture_input_dev); + input_unregister_device(data->light_input_dev); + input_unregister_device(data->prox_input_dev); + input_unregister_device(data->temp_humi_input_dev); + input_unregister_device(data->sig_motion_input_dev); + input_unregister_device(data->step_cnt_input_dev); + input_unregister_device(data->meta_input_dev); +} diff --git a/drivers/sensors/brcm/ssp_misc.c b/drivers/sensors/brcm/ssp_misc.c new file mode 100644 index 0000000..60dadff --- /dev/null +++ b/drivers/sensors/brcm/ssp_misc.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ +#include "ssp.h" + +#define SHAKE_ON 1 +#define SHAKE_OFF 2 + +struct remove_af_noise { + void *af_pdata; + int16_t (*af_func)(void *, bool); +}; + +static struct remove_af_noise af_sensor; + +int remove_af_noise_register(struct remove_af_noise *af_cam) +{ + if (af_cam->af_pdata) + af_sensor.af_pdata = af_cam->af_pdata; + + if (af_cam->af_func) + af_sensor.af_func = af_cam->af_func; + + if (!af_cam->af_pdata || !af_cam->af_func) { + ssp_dbg("[SSP]: %s - no af struct\n", __func__); + return ERROR; + } + + return 0; +} +EXPORT_SYMBOL(remove_af_noise_register); + +void remove_af_noise_unregister(struct remove_af_noise *af_cam) +{ + af_sensor.af_pdata = NULL; + af_sensor.af_func = NULL; +} +EXPORT_SYMBOL(remove_af_noise_unregister); + +void report_shake_cam_data(struct ssp_data *data, + struct sensor_value *shake_cam_data) +{ + data->buf[SHAKE_CAM].x = shake_cam_data->x; + + ssp_dbg("[SSP]: %s - shake = %d\n", __func__, + (char)data->buf[SHAKE_CAM].x); + + if (likely(af_sensor.af_pdata && af_sensor.af_func)) { + if ((char)data->buf[SHAKE_CAM].x == SHAKE_ON) + af_sensor.af_func(af_sensor.af_pdata, true); + else if ((char)data->buf[SHAKE_CAM].x == SHAKE_OFF) + af_sensor.af_func(af_sensor.af_pdata, false); + } else { + ssp_dbg("[SSP]: %s - no af_func\n", __func__); + } +} diff --git a/drivers/sensors/brcm/ssp_sensorhub.c b/drivers/sensors/brcm/ssp_sensorhub.c new file mode 100644 index 0000000..70d033f --- /dev/null +++ b/drivers/sensors/brcm/ssp_sensorhub.c @@ -0,0 +1,714 @@ +/* + * Copyright (C) 2013, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ + +#include "ssp_sensorhub.h" + +static void ssp_sensorhub_log(const char *func_name, + const char *data, int length) +{ + char buf[6]; + char *log_str; + int log_size; + int i; + + if (likely(length <= BIG_DATA_SIZE)) + log_size = length; + else + log_size = PRINT_TRUNCATE * 2 + 1; + + log_size = sizeof(buf) * log_size + 1; + log_str = kzalloc(log_size, GFP_ATOMIC); + if (unlikely(!log_str)) { + sensorhub_err("allocate memory for data log err"); + return; + } + + for (i = 0; i < length; i++) { + if (length < BIG_DATA_SIZE || + i < PRINT_TRUNCATE || i >= length - PRINT_TRUNCATE) { + snprintf(buf, sizeof(buf), "%d", (signed char)data[i]); + strlcat(log_str, buf, log_size); + } + if (length < BIG_DATA_SIZE || + i < PRINT_TRUNCATE || i >= length - PRINT_TRUNCATE) { + if (i < length - 1) + strlcat(log_str, ", ", log_size); + } + if (length > BIG_DATA_SIZE && i == PRINT_TRUNCATE) + strlcat(log_str, "..., ", log_size); + } + + pr_info("[SSP]: %s - %s (%d)", func_name, log_str, length); + kfree(log_str); +} + +static int ssp_sensorhub_send_big_data(struct ssp_sensorhub_data *hub_data, + const char *buf, int count) +{ + int length = count - 3; + int ret = 0; + + /* only support voice service for the moment */ + if (buf[1] != TYPE_WAKE_UP_VOICE_SERVICE) { + sensorhub_err("not voice service(%d)", buf[1]); + return -EINVAL; + } + + /* am or grammer data? */ + if (buf[2] != TYPE_WAKE_UP_VOICE_SOUND_SOURCE_AM && + buf[2] != TYPE_WAKE_UP_VOICE_SOUND_SOURCE_GRAMMER) { + sensorhub_err("voice data type err(%d)", buf[2]); + return -EINVAL; + } + + hub_data->big_send_events.library_data + = kzalloc(length * sizeof(char), GFP_KERNEL); + if (unlikely(!hub_data->big_send_events.library_data)) { + sensorhub_err("allocate memory for big library err"); + return -ENOMEM; + } + + memcpy(hub_data->big_send_events.library_data, buf+3, length); + hub_data->big_send_events.library_length = length; + + /* trigger big data transfer */ + ret = set_big_data_start(hub_data->ssp_data, buf[2]+1, length); + if (ret != SUCCESS) { + sensorhub_err("set_big_data_start err(%d)", ret); + goto exit; + } + + /* wait until write operation is done */ + ret = wait_for_completion_timeout(&hub_data->big_write_done, + COMPLETION_TIMEOUT + 1*HZ); + if (unlikely(!ret)) { + sensorhub_err("wait timed out"); + ret = -EBUSY; + } else if (unlikely(ret < 0)) { + sensorhub_err("wait for completion err(%d)", ret); + ret = -EIO; + } + +exit: + kfree(hub_data->big_send_events.library_data); + return !ret ? ret + 1 : ret; +} + +static int ssp_sensorhub_send_cmd(struct ssp_sensorhub_data *hub_data, + const char *buf, int count) +{ + int ret = 0; + + if (buf[2] < MSG2SSP_AP_STATUS_WAKEUP || + buf[2] >= MSG2SSP_AP_TEMPHUMIDITY_CAL_DONE) { + sensorhub_err("MSG2SSP_INST_LIB_NOTI err(%d)", buf[2]); + return -EINVAL; + } + + ret = ssp_send_cmd(hub_data->ssp_data, buf[2], 0); + + if (buf[2] == MSG2SSP_AP_STATUS_WAKEUP || + buf[2] == MSG2SSP_AP_STATUS_SLEEP) + hub_data->ssp_data->uLastAPState = buf[2]; + + if (buf[2] == MSG2SSP_AP_STATUS_SUSPEND || + buf[2] == MSG2SSP_AP_STATUS_RESUME) + hub_data->ssp_data->uLastResumeState = buf[2]; + + return ret; +} + +static int ssp_sensorhub_send_instruction(struct ssp_sensorhub_data *hub_data, + const char *buf, int count) +{ + unsigned char instruction = buf[0]; + + if (buf[0] == MSG2SSP_INST_LIBRARY_REMOVE) + instruction = REMOVE_LIBRARY; + else if (buf[0] == MSG2SSP_INST_LIBRARY_ADD) + instruction = ADD_LIBRARY; + + return send_instruction(hub_data->ssp_data, instruction, + (unsigned char)buf[1], (unsigned char *)(buf+2), count-2); +} + +static ssize_t ssp_sensorhub_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct ssp_sensorhub_data *hub_data + = container_of(file->private_data, + struct ssp_sensorhub_data, sensorhub_device); + int ret = 0; + + if (unlikely(count < 2)) { + sensorhub_err("library data length err"); + return -EINVAL; + } + + ssp_sensorhub_log(__func__, buf, count); + + if (unlikely(hub_data->ssp_data->bSspShutdown)) { + sensorhub_err("stop sending library data(shutdown)"); + return -EBUSY; + } + + if (buf[0] == MSG2SSP_INST_LIB_DATA && count >= BIG_DATA_SIZE) + ret = ssp_sensorhub_send_big_data(hub_data, buf, count); + else if (buf[0] == MSG2SSP_INST_LIB_NOTI) + ret = ssp_sensorhub_send_cmd(hub_data, buf, count); + else + ret = ssp_sensorhub_send_instruction(hub_data, buf, count); + + if (unlikely(ret <= 0)) { + sensorhub_err("send library data err(%d)", ret); + /* i2c transfer fail */ + if (ret == ERROR) + return -EIO; + /* i2c transfer done but no ack from MCU */ + else if (ret == FAIL) + return -EAGAIN; + else + return ret; + } + + return count; +} + +static ssize_t ssp_sensorhub_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct ssp_sensorhub_data *hub_data + = container_of(file->private_data, + struct ssp_sensorhub_data, sensorhub_device); + struct sensorhub_event *event; + int retries = MAX_DATA_COPY_TRY; + int length = 0; + int ret = 0; + + spin_lock_bh(&hub_data->sensorhub_lock); + if (unlikely(kfifo_is_empty(&hub_data->fifo))) { + sensorhub_info("no library data"); + goto err; + } + + /* first in first out */ + ret = kfifo_out_peek(&hub_data->fifo, &event, sizeof(void *)); + if (unlikely(!ret)) { + sensorhub_err("kfifo out peek err(%d)", ret); + ret = EIO; + goto err; + } + + length = event->library_length; + + while (retries--) { + ret = copy_to_user(buf, + event->library_data, event->library_length); + if (likely(!ret)) + break; + } + if (unlikely(ret)) { + sensorhub_err("read library data err(%d)", ret); + goto err; + } + + ssp_sensorhub_log(__func__, + event->library_data, event->library_length); + + /* remove first event from the list */ + ret = kfifo_out(&hub_data->fifo, &event, sizeof(void *)); + if (unlikely(ret != sizeof(void *))) { + sensorhub_err("kfifo out err(%d)", ret); + ret = EIO; + goto err; + } + + complete(&hub_data->read_done); + spin_unlock_bh(&hub_data->sensorhub_lock); + + return length; + +err: + spin_unlock_bh(&hub_data->sensorhub_lock); + return ret ? -ret : 0; +} + +static long ssp_sensorhub_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct ssp_sensorhub_data *hub_data + = container_of(file->private_data, + struct ssp_sensorhub_data, sensorhub_device); + void __user *argp = (void __user *)arg; + int retries = MAX_DATA_COPY_TRY; + int length = hub_data->big_events.library_length; + int ret = 0; + + switch (cmd) { + case IOCTL_READ_BIG_CONTEXT_DATA: + if (unlikely(!hub_data->big_events.library_data + || !hub_data->big_events.library_length)) { + sensorhub_info("no big library data"); + return 0; + } + + while (retries--) { + ret = copy_to_user(argp, + hub_data->big_events.library_data, + hub_data->big_events.library_length); + if (likely(!ret)) + break; + } + if (unlikely(ret)) { + sensorhub_err("read big library data err(%d)", ret); + return -ret; + } + + ssp_sensorhub_log(__func__, + hub_data->big_events.library_data, + hub_data->big_events.library_length); + + hub_data->is_big_event = false; + kfree(hub_data->big_events.library_data); + hub_data->big_events.library_length = 0; + complete(&hub_data->big_read_done); + break; + + default: + sensorhub_err("ioctl cmd err(%d)", cmd); + return -EINVAL; + } + + return length; +} + +static struct file_operations ssp_sensorhub_fops = { + .owner = THIS_MODULE, + .open = nonseekable_open, + .write = ssp_sensorhub_write, + .read = ssp_sensorhub_read, + .unlocked_ioctl = ssp_sensorhub_ioctl, +}; + +void ssp_sensorhub_report_notice(struct ssp_data *ssp_data, char notice) +{ + struct ssp_sensorhub_data *hub_data = ssp_data->hub_data; + + input_report_rel(hub_data->sensorhub_input_dev, NOTICE, notice); + input_sync(hub_data->sensorhub_input_dev); + + if (notice == MSG2SSP_AP_STATUS_WAKEUP) + sensorhub_info("wake up"); + else if (notice == MSG2SSP_AP_STATUS_SLEEP) + sensorhub_info("sleep"); + else if (notice == MSG2SSP_AP_STATUS_RESET) + sensorhub_info("reset"); + else + sensorhub_err("invalid notice(0x%x)", notice); +} + +static void ssp_sensorhub_report_library(struct ssp_sensorhub_data *hub_data) +{ + input_report_rel(hub_data->sensorhub_input_dev, DATA, DATA); + input_sync(hub_data->sensorhub_input_dev); + wake_lock_timeout(&hub_data->sensorhub_wake_lock, WAKE_LOCK_TIMEOUT); +} + +static void ssp_sensorhub_report_big_library( + struct ssp_sensorhub_data *hub_data) +{ + input_report_rel(hub_data->sensorhub_input_dev, BIG_DATA, BIG_DATA); + input_sync(hub_data->sensorhub_input_dev); + wake_lock_timeout(&hub_data->sensorhub_wake_lock, WAKE_LOCK_TIMEOUT); +} + +static int ssp_sensorhub_list(struct ssp_sensorhub_data *hub_data, + char *dataframe, int length) +{ + struct sensorhub_event *event; + int ret = 0; + + if (unlikely(length <= 0 || length >= PAGE_SIZE)) { + sensorhub_err("library length err(%d)", length); + return -EINVAL; + } + + ssp_sensorhub_log(__func__, dataframe, length); + + /* overwrite new event if list is full */ + if (unlikely(kfifo_is_full(&hub_data->fifo))) { + ret = kfifo_out(&hub_data->fifo, &event, sizeof(void *)); + if (unlikely(ret != sizeof(void *))) { + sensorhub_err("kfifo out err(%d)", ret); + return -EIO; + } + sensorhub_info("overwrite event"); + } + + /* allocate memory for new event */ + kfree(hub_data->events[hub_data->event_number].library_data); + hub_data->events[hub_data->event_number].library_data + = kzalloc(length * sizeof(char), GFP_ATOMIC); + if (unlikely(!hub_data->events[hub_data->event_number].library_data)) { + sensorhub_err("allocate memory for library err"); + return -ENOMEM; + } + + /* copy new event into memory */ + memcpy(hub_data->events[hub_data->event_number].library_data, + dataframe, length); + hub_data->events[hub_data->event_number].library_length = length; + + /* add new event into the end of list */ + event = &hub_data->events[hub_data->event_number]; + ret = kfifo_in(&hub_data->fifo, &event, sizeof(void *)); + if (unlikely(ret != sizeof(void *))) { + sensorhub_err("kfifo in err(%d)", ret); + return -EIO; + } + + /* not to overflow max list capacity */ + if (hub_data->event_number++ >= LIST_SIZE - 1) + hub_data->event_number = 0; + + return kfifo_len(&hub_data->fifo) / sizeof(void *); +} + +int ssp_sensorhub_handle_data(struct ssp_data *ssp_data, char *dataframe, + int start, int end) +{ + struct ssp_sensorhub_data *hub_data = ssp_data->hub_data; + int ret = 0; + + /* add new sensorhub event into list */ + spin_lock_bh(&hub_data->sensorhub_lock); + ret = ssp_sensorhub_list(hub_data, dataframe+start, end-start); + spin_unlock_bh(&hub_data->sensorhub_lock); + + if (ret < 0) + sensorhub_err("sensorhub list err(%d)", ret); + else + wake_up(&hub_data->sensorhub_wq); + + return ret; +} + +static int ssp_sensorhub_thread(void *arg) +{ + struct ssp_sensorhub_data *hub_data = (struct ssp_sensorhub_data *)arg; + int ret = 0; + + while (likely(!kthread_should_stop())) { + /* run thread if list is not empty */ + wait_event_interruptible(hub_data->sensorhub_wq, + kthread_should_stop() || + !kfifo_is_empty(&hub_data->fifo) || + hub_data->is_big_event); + + /* exit thread if kthread should stop */ + if (unlikely(kthread_should_stop())) { + sensorhub_info("kthread_stop()"); + break; + } + + if (likely(!kfifo_is_empty(&hub_data->fifo))) { + /* report sensorhub event to user */ + ssp_sensorhub_report_library(hub_data); + /* wait until transfer finished */ + ret = wait_for_completion_timeout( + &hub_data->read_done, COMPLETION_TIMEOUT); + if (unlikely(!ret)) + sensorhub_err("wait for read timed out"); + else if (unlikely(ret < 0)) + sensorhub_err("read completion err(%d)", ret); + } + + if (unlikely(hub_data->is_big_event)) { + /* report big sensorhub event to user */ + ssp_sensorhub_report_big_library(hub_data); + /* wait until transfer finished */ + ret = wait_for_completion_timeout( + &hub_data->big_read_done, COMPLETION_TIMEOUT); + if (unlikely(!ret)) + sensorhub_err("wait for big read timed out"); + else if (unlikely(ret < 0)) + sensorhub_err("big read completion err(%d)", + ret); + } + } + + return 0; +} + +void ssp_read_big_library_task(struct work_struct *work) +{ + struct ssp_big *big = container_of(work, struct ssp_big, work); + struct ssp_sensorhub_data *hub_data = big->data->hub_data; + struct ssp_msg *msg; + int buf_len, residue = big->length, ret = 0, index = 0, pos = 0; + + hub_data->big_events.library_length = big->length; + hub_data->big_events.library_data = kzalloc(big->length, GFP_KERNEL); + + while (residue > 0) { + buf_len = residue > DATA_PACKET_SIZE + ? DATA_PACKET_SIZE : residue; + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + msg->cmd = MSG2SSP_AP_GET_BIG_DATA; + msg->length = buf_len; + msg->options = AP2HUB_READ | (index++ << SSP_INDEX); + msg->data = big->addr; + msg->buffer = hub_data->big_events.library_data + pos; + msg->free_buffer = 0; + + ret = ssp_spi_sync(big->data, msg, 1000); + if (ret != SUCCESS) { + sensorhub_err("read big data err(%d)", ret); + break; + } + + pos += buf_len; + residue -= buf_len; + + sensorhub_info("read big data (%5d / %5d)", pos, big->length); + } + + hub_data->is_big_event = true; + wake_up(&hub_data->sensorhub_wq); + kfree(big); +} + +void ssp_send_big_library_task(struct work_struct *work) +{ + struct ssp_big *big = container_of(work, struct ssp_big, work); + struct ssp_sensorhub_data *hub_data = big->data->hub_data; + struct ssp_msg *msg; + int buf_len, residue = big->length, ret = 0, index = 0, pos = 0; + + while (residue > 0) { + buf_len = residue > DATA_PACKET_SIZE + ? DATA_PACKET_SIZE : residue; + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + msg->cmd = MSG2SSP_AP_SET_BIG_DATA; + msg->length = buf_len; + msg->options = AP2HUB_WRITE | (index++ << SSP_INDEX); + msg->data = big->addr; + msg->buffer = hub_data->big_send_events.library_data + pos; + msg->free_buffer = 0; + + ret = ssp_spi_sync(big->data, msg, 1000); + if (ret != SUCCESS) { + sensorhub_err("send big data err(%d)", ret); + break; + } + + pos += buf_len; + residue -= buf_len; + + sensorhub_info("send big data (%5d / %5d)", pos, big->length); + } + + complete(&hub_data->big_write_done); + kfree(big); +} + +void ssp_pcm_dump_task(struct work_struct *work) +{ + struct ssp_big *big = container_of(work, struct ssp_big, work); + struct ssp_sensorhub_data *hub_data = big->data->hub_data; + struct ssp_msg *msg; + int buf_len, residue = big->length, ret = 0, index = 0; + + mm_segment_t old_fs; + struct file *voice_filp; + char pcm_path[BIN_PATH_SIZE+1]; + char *buff; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + snprintf(pcm_path, BIN_PATH_SIZE, + "/data/voice%d.pcm", hub_data->pcm_cnt); + voice_filp = filp_open(pcm_path, O_RDWR | O_CREAT | O_APPEND, 0666); + if (IS_ERR(voice_filp)) { + sensorhub_err("open pcm dump file err"); + goto exit; + } + + buf_len = big->length > DATA_PACKET_SIZE + ? DATA_PACKET_SIZE : big->length; + buff = kzalloc(buf_len, GFP_KERNEL); + + while (residue > 0) { + buf_len = residue > DATA_PACKET_SIZE + ? DATA_PACKET_SIZE : residue; + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + msg->cmd = MSG2SSP_AP_GET_BIG_DATA; + msg->length = buf_len; + msg->options = AP2HUB_READ | (index++ << SSP_INDEX); + msg->data = big->addr; + msg->buffer = buff; + msg->free_buffer = 0; + + ret = ssp_spi_sync(big->data, msg, 1000); + if (ret != SUCCESS) { + sensorhub_err("receive pcm dump err(%d)", ret); + break; + } + + ret = voice_filp->f_op->write(voice_filp, (char __user *)buff, + buf_len, &voice_filp->f_pos); + if (ret < 0) { + sensorhub_err("write pcm dump to file err(%d)", ret); + break; + } + + residue -= buf_len; + sensorhub_info("write pcm dump..."); + } + + filp_close(voice_filp, current->files); + kfree(buff); + +exit: + set_fs(old_fs); + kfree(big); +} + +int ssp_sensorhub_pcm_dump(struct ssp_sensorhub_data *hub_data) +{ + hub_data->pcm_cnt++; + return set_big_data_start(hub_data->ssp_data, BIG_TYPE_VOICE_PCM, 0); +} + +int ssp_sensorhub_initialize(struct ssp_data *ssp_data) +{ + struct ssp_sensorhub_data *hub_data; + int ret; + + /* allocate memory for sensorhub data */ + hub_data = kzalloc(sizeof(*hub_data), GFP_KERNEL); + if (!hub_data) { + sensorhub_err("allocate memory for sensorhub data err"); + ret = -ENOMEM; + goto exit; + } + hub_data->ssp_data = ssp_data; + ssp_data->hub_data = hub_data; + + /* init wakelock, list, waitqueue, completion and spinlock */ + wake_lock_init(&hub_data->sensorhub_wake_lock, WAKE_LOCK_SUSPEND, + "ssp_sensorhub_wake_lock"); + init_waitqueue_head(&hub_data->sensorhub_wq); + init_completion(&hub_data->read_done); + init_completion(&hub_data->big_read_done); + init_completion(&hub_data->big_write_done); + spin_lock_init(&hub_data->sensorhub_lock); + + /* allocate sensorhub input device */ + hub_data->sensorhub_input_dev = input_allocate_device(); + if (!hub_data->sensorhub_input_dev) { + sensorhub_err("allocate sensorhub input device err"); + ret = -ENOMEM; + goto err_input_allocate_device_sensorhub; + } + + /* set sensorhub input device */ + input_set_drvdata(hub_data->sensorhub_input_dev, hub_data); + hub_data->sensorhub_input_dev->name = "ssp_context"; + input_set_capability(hub_data->sensorhub_input_dev, EV_REL, DATA); + input_set_capability(hub_data->sensorhub_input_dev, EV_REL, BIG_DATA); + input_set_capability(hub_data->sensorhub_input_dev, EV_REL, NOTICE); + + /* register sensorhub input device */ + ret = input_register_device(hub_data->sensorhub_input_dev); + if (ret < 0) { + sensorhub_err("register sensorhub input device err(%d)", ret); + input_free_device(hub_data->sensorhub_input_dev); + goto err_input_register_device_sensorhub; + } + + /* register sensorhub misc device */ + hub_data->sensorhub_device.minor = MISC_DYNAMIC_MINOR; + hub_data->sensorhub_device.name = "ssp_sensorhub"; + hub_data->sensorhub_device.fops = &ssp_sensorhub_fops; + + ret = misc_register(&hub_data->sensorhub_device); + if (ret < 0) { + sensorhub_err("register sensorhub misc device err(%d)", ret); + goto err_misc_register; + } + + /* allocate fifo */ + ret = kfifo_alloc(&hub_data->fifo, + sizeof(void *) * LIST_SIZE, GFP_KERNEL); + if (ret) { + sensorhub_err("kfifo allocate err(%d)", ret); + goto err_kfifo_alloc; + } + + /* create and run sensorhub thread */ + hub_data->sensorhub_task = kthread_run(ssp_sensorhub_thread, + (void *)hub_data, "ssp_sensorhub_thread"); + if (IS_ERR(hub_data->sensorhub_task)) { + ret = PTR_ERR(hub_data->sensorhub_task); + goto err_kthread_run; + } + + return 0; + +err_kthread_run: + kfifo_free(&hub_data->fifo); +err_kfifo_alloc: + misc_deregister(&hub_data->sensorhub_device); +err_misc_register: + input_unregister_device(hub_data->sensorhub_input_dev); +err_input_register_device_sensorhub: +err_input_allocate_device_sensorhub: + complete_all(&hub_data->big_write_done); + complete_all(&hub_data->big_read_done); + complete_all(&hub_data->read_done); + wake_lock_destroy(&hub_data->sensorhub_wake_lock); + kfree(hub_data); +exit: + return ret; +} + +void ssp_sensorhub_remove(struct ssp_data *ssp_data) +{ + struct ssp_sensorhub_data *hub_data = ssp_data->hub_data; + + ssp_sensorhub_fops.write = NULL; + ssp_sensorhub_fops.read = NULL; + ssp_sensorhub_fops.unlocked_ioctl = NULL; + + kthread_stop(hub_data->sensorhub_task); + kfifo_free(&hub_data->fifo); + misc_deregister(&hub_data->sensorhub_device); + input_unregister_device(hub_data->sensorhub_input_dev); + complete_all(&hub_data->big_write_done); + complete_all(&hub_data->big_read_done); + complete_all(&hub_data->read_done); + wake_lock_destroy(&hub_data->sensorhub_wake_lock); + kfree(hub_data); + sensorhub_err("DONE"); +} + +MODULE_DESCRIPTION("Seamless Sensor Platform(SSP) sensorhub driver"); +MODULE_AUTHOR("Samsung Electronics"); +MODULE_LICENSE("GPL"); diff --git a/drivers/sensors/brcm/ssp_sensorhub.h b/drivers/sensors/brcm/ssp_sensorhub.h new file mode 100644 index 0000000..742806d --- /dev/null +++ b/drivers/sensors/brcm/ssp_sensorhub.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2013, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ + +#ifndef __SSP_SENSORHUB__ +#define __SSP_SENSORHUB__ + +#include +#include +#include +#include +#include +#include "ssp.h" + +/* 'LIST_SIZE' should be be rounded-up to a power of 2 */ +#define LIST_SIZE 4 +#define MAX_DATA_COPY_TRY 2 +#define WAKE_LOCK_TIMEOUT (3*HZ) +#define COMPLETION_TIMEOUT (2*HZ) +#define DATA REL_RX +#define BIG_DATA REL_RY +#define NOTICE REL_RZ +#define BIG_DATA_SIZE 256 +#define PRINT_TRUNCATE 6 +#define BIN_PATH_SIZE 100 + +#define SENSORHUB_IOCTL_MAGIC 'S' +#define IOCTL_READ_BIG_CONTEXT_DATA _IOR(SENSORHUB_IOCTL_MAGIC, 3, char *) + +#define sensorhub_info(str, args...) \ + pr_info("[SSP]: %s - " str, __func__, ##args) +#define sensorhub_debug(str, args...) \ + pr_debug("[SSP]: %s - " str, __func__, ##args) +#define sensorhub_err(str, args...) \ + pr_err("[SSP]: %s - " str, __func__, ##args) + + +struct sensorhub_event { + char *library_data; + int library_length; +}; + +struct ssp_sensorhub_data { + struct ssp_data *ssp_data; + struct input_dev *sensorhub_input_dev; + struct task_struct *sensorhub_task; + struct miscdevice sensorhub_device; + struct wake_lock sensorhub_wake_lock; + struct completion read_done; + struct completion big_read_done; + struct completion big_write_done; + struct sensorhub_event events[LIST_SIZE]; + struct sensorhub_event big_events; + struct sensorhub_event big_send_events; + struct kfifo fifo; + bool is_big_event; + int event_number; + int pcm_cnt; + wait_queue_head_t sensorhub_wq; + spinlock_t sensorhub_lock; +}; + +int ssp_sensorhub_pcm_dump(struct ssp_sensorhub_data *hub_data); +void ssp_sensorhub_report_notice(struct ssp_data *ssp_data, char notice); +int ssp_sensorhub_handle_data(struct ssp_data *ssp_data, char *dataframe, + int start, int end); +int ssp_sensorhub_initialize(struct ssp_data *ssp_data); +void ssp_sensorhub_remove(struct ssp_data *ssp_data); + +#endif diff --git a/drivers/sensors/brcm/ssp_spi.c b/drivers/sensors/brcm/ssp_spi.c new file mode 100644 index 0000000..7381a3d --- /dev/null +++ b/drivers/sensors/brcm/ssp_spi.c @@ -0,0 +1,254 @@ +/* + * driver for Android SensorHub SPI + * + * Copyright (c) 2013, Samsung Electronics. All rights reserved + * + * This program 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 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. + */ + +#include +#include +#include +#include + +#if defined(DEBUG_SSP_SPI) +#define ssp_log(fmt, arg...) \ + do { \ + printk(KERN_ERR "[%s:%d] " fmt , \ + __func__, __LINE__, ##arg); \ + } \ + while (0) +#else +#define ssp_log(fmt, arg...) +#endif + + +/* If AP can't change the endian to BIG */ +/* for s5c73m ISP, this option must is required.*/ +/* This option depends on SPI_DMA_MODE */ +/* in camera driver file*/ +/*#define CHANGE_ENDIAN */ + +int ssp_spi_write_sync(struct spi_device *spi, const u8 *addr, const int len) +{ + int ret; +#if defined(CHANGE_ENDIAN) + u8 buf[8] = {0}; +#endif + + struct spi_message msg; + + struct spi_transfer xfer = { + .len = len, +#if !defined(CHANGE_ENDIAN) + .tx_buf = addr, + /*QCTK ALRAN QUP_CONFIG 0-4 bits BIG ENDIAN*/ + .bits_per_word = 8, +#else + .tx_buf = buf, +#endif + }; + +#if defined(CHANGE_ENDIAN) + buf[0] = addr[3]; + buf[1] = addr[2]; + buf[2] = addr[1]; + buf[3] = addr[0]; + buf[4] = addr[7]; + buf[5] = addr[6]; + buf[6] = addr[5]; + buf[7] = addr[4]; +#endif + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + ret = spi_sync(spi, &msg); + + if (ret < 0) + ssp_log("error %d\n", ret); + + return ret; +} + + +int ssp_spi_read_sync(struct spi_device *spi, u8 *in_buf, size_t len) +{ + int ret; + u8 read_out_buf[2]; + + struct spi_message msg; + struct spi_transfer xfer = { + .tx_buf = read_out_buf, + .rx_buf = in_buf, + .len = len, + .cs_change = 0, + }; + + spi_message_init(&msg); + + spi_message_add_tail(&xfer, &msg); + + ret = spi_sync(spi, &msg); + + if (ret < 0) + ssp_log("%s - error %d\n", + __func__, ret); + + return ret; +} + + +int ssp_spi_sync(struct spi_device *spi, u8 *out_buf, + size_t out_len, u8 *in_buf) +{ + int ret; + + struct spi_message msg; + struct spi_transfer xfer = { + .tx_buf = out_buf, + .rx_buf = in_buf, + .len = out_len, + .cs_change = 0, + }; + + spi_message_init(&msg); + + spi_message_add_tail(&xfer, &msg); + + ret = spi_sync(spi, &msg); + ssp_log("%s - received %d\n", __func__, xfer.len); + + if (ret < 0) + ssp_log("%s - error %d\n", + __func__, ret); + + return ret; +} + +unsigned int g_flag_spirecv; +void ssp_spi_async_complete(void *context) +{ + g_flag_spirecv = 1; +} + +int ssp_spi_async(struct spi_device *spi, u8 *out_buf, + size_t out_len, u8 *in_buf) +{ + int ret; + + struct spi_message msg; + struct spi_transfer xfer = { + .tx_buf = out_buf, + .rx_buf = in_buf, + .len = out_len, + .cs_change = 0, + }; + + + spi_message_init(&msg); + + spi_message_add_tail(&xfer, &msg); + msg.complete = ssp_spi_async_complete; + + ret = spi_async(spi, &msg); + + if (ret < 0) + ssp_log("%s - error %d\n", + __func__, ret); + + return ret; +} + + + + +int ssp_spi_read(struct spi_device *spi, u8 *buf, size_t len, const int rxSize) +{ + int k; + int ret = 0; + u8 temp_buf[4] = {0}; + u32 count = len/rxSize; + u32 extra = len%rxSize; + + for (k = 0; k < count; k++) { + ret = ssp_spi_read_sync(spi, &buf[rxSize*k], rxSize); + if (ret < 0) { + ssp_log("%s - error %d\n", + __func__, ret); + return -EINVAL; + } + } + + if (extra != 0) { + ret = ssp_spi_read_sync(spi, &buf[rxSize*k], extra); + if (ret < 0) { + ssp_log("%s - error %d\n", + __func__, ret); + return -EINVAL; + } + } + + for (k = 0; k < len-3; k += 4) { + memcpy(temp_buf, (char *)&buf[k], sizeof(temp_buf)); + buf[k] = temp_buf[3]; + buf[k+1] = temp_buf[2]; + buf[k+2] = temp_buf[1]; + buf[k+3] = temp_buf[0]; + } + + return 0; +} + +int ssp_spi_write(struct spi_device *spi, const u8 *addr, + const int len, const int txSize) +{ + int i, j = 0; + int ret = 0; + u8 paddingData[8]; + u32 count = len/txSize; + u32 extra = len%txSize; + ssp_log("Entered\n"); + ssp_log("count = %d extra = %d\n", count, extra); + + memset(paddingData, 0, sizeof(paddingData)); + + for (i = 0 ; i < count ; i++) { + ret = ssp_spi_write_sync(spi, &addr[j], txSize); + j += txSize; + if (ret < 0) { + ssp_log("failed to write ssp_spi_write_sync\n"); + goto exit_err; + } + ssp_log("Delay!!!\n"); + msleep(50); + } + + if (extra) { + ret = ssp_spi_write_sync(spi, &addr[j], extra); + if (ret < 0) { + ssp_log("failed to write ssp_spi_write_sync\n"); + goto exit_err; + } + } + + for (i = 0; i < 4; i++) { + memset(paddingData, 0, sizeof(paddingData)); + ret = ssp_spi_write_sync(spi, paddingData, 8); + if (ret < 0) { + ssp_log("failed to write ssp_spi_write_sync\n"); + goto exit_err; + } + } + ssp_log("Finish!!\n"); +exit_err: + return ret; +} + diff --git a/drivers/sensors/brcm/ssp_sysfs.c b/drivers/sensors/brcm/ssp_sysfs.c new file mode 100644 index 0000000..c0cd582 --- /dev/null +++ b/drivers/sensors/brcm/ssp_sysfs.c @@ -0,0 +1,1020 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved. + * + * 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. + * + */ +#include "ssp.h" +#include + +#define BATCH_IOCTL_MAGIC 0xFC + +struct batch_config { + int64_t timeout; + int64_t delay; + int flag; +}; + +/*************************************************************************/ +/* SSP data delay function */ +/*************************************************************************/ + +int get_msdelay(int64_t dDelayRate) +{ + /* + * If App request lower frequency then MaxDelay, + * Sensor have to work with MaxDelay. + */ + + if (dDelayRate > 200000000) + dDelayRate = 200000000; + + return div_s64(dDelayRate, 1000000); +} + +static void enable_sensor(struct ssp_data *data, + int iSensorType, int64_t dNewDelay) +{ + u8 uBuf[9]; + unsigned int uNewEnable = 0; + s32 maxBatchReportLatency = 0; + s8 batchOptions = 0; + int64_t dTempDelay = data->adDelayBuf[iSensorType]; + s32 dMsDelay = get_msdelay(dNewDelay); + int ret = 0; + + data->adDelayBuf[iSensorType] = dNewDelay; + maxBatchReportLatency = data->batchLatencyBuf[iSensorType]; + batchOptions = data->batchOptBuf[iSensorType]; + + switch (data->aiCheckStatus[iSensorType]) { + case ADD_SENSOR_STATE: + ssp_dbg("[SSP]: %s - add %u, New = %lldns\n", + __func__, 1 << iSensorType, dNewDelay); + + if (iSensorType == PROXIMITY_SENSOR) { + get_proximity_threshold(data); + proximity_open_calibration(data); + set_proximity_threshold(data, data->uProxHiThresh, + data->uProxLoThresh); + } + + memcpy(&uBuf[0], &dMsDelay, 4); + memcpy(&uBuf[4], &maxBatchReportLatency, 4); + uBuf[8] = batchOptions; + + if (iSensorType == TEMPERATURE_HUMIDITY_SENSOR && + dMsDelay == 10000) + pr_info("[SSP] Skip Add Temphumidity Sensor\n"); + else + ret = send_instruction(data, ADD_SENSOR, + iSensorType, uBuf, 9); + pr_info("[SSP], delay %d, timeout %d, flag=%d, ret%d\n", + dMsDelay, maxBatchReportLatency, uBuf[8], ret); + if (ret <= 0) { + uNewEnable = + (unsigned int)atomic_read(&data->aSensorEnable) + & (~(unsigned int)(1 << iSensorType)); + atomic_set(&data->aSensorEnable, uNewEnable); + + data->aiCheckStatus[iSensorType] = NO_SENSOR_STATE; + break; + } + + data->aiCheckStatus[iSensorType] = RUNNING_SENSOR_STATE; + + break; + case RUNNING_SENSOR_STATE: + if (get_msdelay(dTempDelay) + == get_msdelay(data->adDelayBuf[iSensorType])) + break; + + ssp_dbg("[SSP]: %s - Change %u, New = %lldns\n", + __func__, 1 << iSensorType, dNewDelay); + + memcpy(&uBuf[0], &dMsDelay, 4); + memcpy(&uBuf[4], &maxBatchReportLatency, 4); + uBuf[8] = batchOptions; + send_instruction(data, CHANGE_DELAY, iSensorType, uBuf, 9); + + break; + default: + data->aiCheckStatus[iSensorType] = ADD_SENSOR_STATE; + } +} + +static void change_sensor_delay(struct ssp_data *data, + int iSensorType, int64_t dNewDelay) +{ + u8 uBuf[9]; + s32 maxBatchReportLatency = 0; + s8 batchOptions = 0; + int64_t dTempDelay = data->adDelayBuf[iSensorType]; + s32 dMsDelay = get_msdelay(dNewDelay); + + data->adDelayBuf[iSensorType] = dNewDelay; + data->batchLatencyBuf[iSensorType] = maxBatchReportLatency; + data->batchOptBuf[iSensorType] = batchOptions; + + switch (data->aiCheckStatus[iSensorType]) { + case RUNNING_SENSOR_STATE: + if (get_msdelay(dTempDelay) + == get_msdelay(data->adDelayBuf[iSensorType])) + break; + + ssp_dbg("[SSP]: %s - Change %u, New = %lldns\n", + __func__, 1 << iSensorType, dNewDelay); + + memcpy(&uBuf[0], &dMsDelay, 4); + memcpy(&uBuf[4], &maxBatchReportLatency, 4); + uBuf[8] = batchOptions; + send_instruction(data, CHANGE_DELAY, iSensorType, uBuf, 9); + + break; + default: + break; + } +} + +/*************************************************************************/ +/* SSP data enable function */ +/*************************************************************************/ + +static int ssp_remove_sensor(struct ssp_data *data, + unsigned int uChangedSensor, unsigned int uNewEnable) +{ + u8 uBuf[4]; + int64_t dSensorDelay = data->adDelayBuf[uChangedSensor]; + + ssp_dbg("[SSP]: %s - remove sensor = %d, current state = %d\n", + __func__, (1 << uChangedSensor), uNewEnable); + + data->adDelayBuf[uChangedSensor] = DEFUALT_POLLING_DELAY; + data->batchLatencyBuf[uChangedSensor] = 0; + data->batchOptBuf[uChangedSensor] = 0; + + if (uChangedSensor == ORIENTATION_SENSOR) { + if (!(atomic_read(&data->aSensorEnable) + & (1 << ACCELEROMETER_SENSOR))) { + uChangedSensor = ACCELEROMETER_SENSOR; + } else { + change_sensor_delay(data, ACCELEROMETER_SENSOR, + data->adDelayBuf[ACCELEROMETER_SENSOR]); + return 0; + } + } else if (uChangedSensor == ACCELEROMETER_SENSOR) { + if (atomic_read(&data->aSensorEnable) + & (1 << ORIENTATION_SENSOR)) { + change_sensor_delay(data, ORIENTATION_SENSOR, + data->adDelayBuf[ORIENTATION_SENSOR]); + return 0; + } + } + + if (!data->bSspShutdown) + if (atomic_read(&data->aSensorEnable) & (1 << uChangedSensor)) { + s32 dMsDelay = get_msdelay(dSensorDelay); + memcpy(&uBuf[0], &dMsDelay, 4); + + send_instruction(data, REMOVE_SENSOR, + uChangedSensor, uBuf, 4); + } + data->aiCheckStatus[uChangedSensor] = NO_SENSOR_STATE; + + return 0; +} + +/*************************************************************************/ +/* ssp Sysfs */ +/*************************************************************************/ + +static ssize_t show_enable_irq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + ssp_dbg("[SSP]: %s - %d\n", __func__, !data->bSspShutdown); + + return sprintf(buf, "%d\n", !data->bSspShutdown); +} + +static ssize_t set_enable_irq(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + u8 dTemp; + struct ssp_data *data = dev_get_drvdata(dev); + + if (kstrtou8(buf, 10, &dTemp) < 0) + return -1; + + pr_info("[SSP] %s - %d start\n", __func__, dTemp); + if (dTemp) { + reset_mcu(data); + enable_debug_timer(data); + } else if (!dTemp) { + disable_debug_timer(data); + ssp_enable(data, 0); + } else + pr_err("[SSP] %s - invalid value\n", __func__); + pr_info("[SSP] %s - %d end\n", __func__, dTemp); + return size; +} + +static ssize_t show_sensors_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + ssp_dbg("[SSP]: %s - cur_enable = %d\n", __func__, + atomic_read(&data->aSensorEnable)); + + return sprintf(buf, "%9u\n", atomic_read(&data->aSensorEnable)); +} + +static ssize_t set_sensors_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int64_t dTemp; + int iRet; + unsigned int uNewEnable = 0, uChangedSensor = 0; + struct ssp_data *data = dev_get_drvdata(dev); + + if (kstrtoll(buf, 10, &dTemp) < 0) + return -EINVAL; + + uNewEnable = (unsigned int)dTemp; + ssp_dbg("[SSP]: %s - new_enable = %u, old_enable = %u\n", __func__, + uNewEnable, atomic_read(&data->aSensorEnable)); + + if ((uNewEnable != atomic_read(&data->aSensorEnable)) && + !(data->uSensorState & (uNewEnable - atomic_read(&data->aSensorEnable)))) { + pr_info("[SSP] %s - %u is not connected(sensortate: 0x%x)\n", + __func__, uNewEnable - atomic_read(&data->aSensorEnable), data->uSensorState); + return -EINVAL; + } + + if (uNewEnable == atomic_read(&data->aSensorEnable)) + return size; + + mutex_lock(&data->enable_mutex); + for (uChangedSensor = 0; uChangedSensor < SENSOR_MAX; uChangedSensor++) { + if ((atomic_read(&data->aSensorEnable) & (1 << uChangedSensor)) + != (uNewEnable & (1 << uChangedSensor))) { + + if (!(uNewEnable & (1 << uChangedSensor))) { + data->reportedData[uChangedSensor] = false; + ssp_remove_sensor(data, uChangedSensor, + uNewEnable); /* disable */ + } else { /* Change to ADD_SENSOR_STATE from KitKat */ + if (data->aiCheckStatus[uChangedSensor] == INITIALIZATION_STATE) { + if (uChangedSensor == ACCELEROMETER_SENSOR) { + accel_open_calibration(data); + iRet = set_accel_cal(data); + if (iRet < 0) + pr_err("[SSP]: %s - set_accel_cal failed %d\n", __func__, iRet); + } + else if (uChangedSensor == GYROSCOPE_SENSOR) { + gyro_open_calibration(data); + iRet = set_gyro_cal(data); + if (iRet < 0) + pr_err("[SSP]: %s - set_gyro_cal failed %d\n", __func__, iRet); + } + else if (uChangedSensor == PRESSURE_SENSOR) + pressure_open_calibration(data); + else if (uChangedSensor == PROXIMITY_SENSOR) { + get_proximity_threshold(data); + proximity_open_calibration(data); + set_proximity_threshold(data, data->uProxHiThresh, data->uProxLoThresh); + } + } + data->aiCheckStatus[uChangedSensor] = ADD_SENSOR_STATE; + enable_sensor(data, uChangedSensor, data->adDelayBuf[uChangedSensor]); + } + break; + } + } + atomic_set(&data->aSensorEnable, uNewEnable); + mutex_unlock(&data->enable_mutex); + + return size; +} + +static ssize_t set_flush(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int64_t dTemp; + u8 sensor_type = 0; + struct ssp_data *data = dev_get_drvdata(dev); + + if (kstrtoll(buf, 10, &dTemp) < 0) + return -EINVAL; + + sensor_type = (u8)dTemp; + if (!(atomic_read(&data->aSensorEnable) & (1 << sensor_type))) + return -EINVAL; + + if (flush(data, sensor_type) < 0) { + pr_err("[SSP] ssp returns error for flush(%x)", sensor_type); + return -EINVAL; + } + return size; +} + +static ssize_t show_shake_cam(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + int enabled = 0; + + if ((atomic_read(&data->aSensorEnable) & (1 << SHAKE_CAM))) + enabled = 1; + + return sprintf(buf, "%d\n", enabled); +} + +static ssize_t set_shake_cam(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct ssp_data *data = dev_get_drvdata(dev); + unsigned long enable = 0; + unsigned char buffer[4]; + unsigned int uNewEnable; + s32 dMsDelay = 20; + + if (kstrtoul(buf, 10, &enable)) + return -EINVAL; + + memcpy(&buffer[0], &dMsDelay, 4); + + if (enable) { + send_instruction(data, ADD_SENSOR, SHAKE_CAM, + buffer, sizeof(buffer)); + uNewEnable = + (unsigned int)atomic_read(&data->aSensorEnable) + | ((unsigned int)(1 << SHAKE_CAM)); + } else { + send_instruction(data, REMOVE_SENSOR, SHAKE_CAM, + buffer, sizeof(buffer)); + uNewEnable = + (unsigned int)atomic_read(&data->aSensorEnable) + & (~(unsigned int)(1 << SHAKE_CAM)); + } + + atomic_set(&data->aSensorEnable, uNewEnable); + return size; +} + +static ssize_t show_acc_delay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%lld\n", data->adDelayBuf[ACCELEROMETER_SENSOR]); +} + +static ssize_t set_acc_delay(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int64_t dNewDelay; + struct ssp_data *data = dev_get_drvdata(dev); + + if (kstrtoll(buf, 10, &dNewDelay) < 0) + return -EINVAL; + + if ((atomic_read(&data->aSensorEnable) & (1 << ORIENTATION_SENSOR)) && + (data->adDelayBuf[ORIENTATION_SENSOR] < dNewDelay)) + data->adDelayBuf[ACCELEROMETER_SENSOR] = dNewDelay; + else + change_sensor_delay(data, ACCELEROMETER_SENSOR, dNewDelay); + + return size; +} + +static ssize_t show_gyro_delay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%lld\n", data->adDelayBuf[GYROSCOPE_SENSOR]); +} + +static ssize_t set_gyro_delay(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int64_t dNewDelay; + struct ssp_data *data = dev_get_drvdata(dev); + + if (kstrtoll(buf, 10, &dNewDelay) < 0) + return -EINVAL; + + change_sensor_delay(data, GYROSCOPE_SENSOR, dNewDelay); + return size; +} + +static ssize_t show_mag_delay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%lld\n", data->adDelayBuf[GEOMAGNETIC_SENSOR]); +} + +static ssize_t set_mag_delay(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int64_t dNewDelay; + struct ssp_data *data = dev_get_drvdata(dev); + + if (kstrtoll(buf, 10, &dNewDelay) < 0) + return -EINVAL; + + change_sensor_delay(data, GEOMAGNETIC_SENSOR, dNewDelay); + + return size; +} + +static ssize_t show_uncal_mag_delay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%lld\n", + data->adDelayBuf[GEOMAGNETIC_UNCALIB_SENSOR]); +} + +static ssize_t set_uncal_mag_delay(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int64_t dNewDelay; + struct ssp_data *data = dev_get_drvdata(dev); + + if (kstrtoll(buf, 10, &dNewDelay) < 0) + return -EINVAL; + + change_sensor_delay(data, GEOMAGNETIC_UNCALIB_SENSOR, dNewDelay); + + return size; +} + +static ssize_t show_rot_delay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%lld\n", data->adDelayBuf[ROTATION_VECTOR]); +} + +static ssize_t set_rot_delay(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int64_t dNewDelay; + struct ssp_data *data = dev_get_drvdata(dev); + + if (kstrtoll(buf, 10, &dNewDelay) < 0) + return -EINVAL; + + change_sensor_delay(data, ROTATION_VECTOR, dNewDelay); + + return size; +} + +static ssize_t show_game_rot_delay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%lld\n", data->adDelayBuf[GAME_ROTATION_VECTOR]); +} + +static ssize_t set_game_rot_delay(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int64_t dNewDelay; + struct ssp_data *data = dev_get_drvdata(dev); + + if (kstrtoll(buf, 10, &dNewDelay) < 0) + return -EINVAL; + + change_sensor_delay(data, GAME_ROTATION_VECTOR, dNewDelay); + + return size; +} + +static ssize_t show_step_det_delay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%lld\n", + data->adDelayBuf[STEP_DETECTOR]); +} + +static ssize_t set_step_det_delay(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int64_t dNewDelay; + struct ssp_data *data = dev_get_drvdata(dev); + + if (kstrtoll(buf, 10, &dNewDelay) < 0) + return -1; + + change_sensor_delay(data, STEP_DETECTOR, dNewDelay); + return size; +} + +static ssize_t show_sig_motion_delay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%lld\n", + data->adDelayBuf[SIG_MOTION_SENSOR]); +} + +static ssize_t set_sig_motion_delay(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int64_t dNewDelay; + struct ssp_data *data = dev_get_drvdata(dev); + + if (kstrtoll(buf, 10, &dNewDelay) < 0) + return -1; + + change_sensor_delay(data, SIG_MOTION_SENSOR, dNewDelay); + return size; +} + +static ssize_t show_step_cnt_delay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%lld\n", + data->adDelayBuf[STEP_COUNTER]); +} + +static ssize_t set_step_cnt_delay(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int64_t dNewDelay; + struct ssp_data *data = dev_get_drvdata(dev); + + if (kstrtoll(buf, 10, &dNewDelay) < 0) + return -1; + + change_sensor_delay(data, STEP_COUNTER, dNewDelay); + return size; +} + +static ssize_t show_uncalib_gyro_delay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%lld\n", data->adDelayBuf[GYRO_UNCALIB_SENSOR]); +} + +static ssize_t set_uncalib_gyro_delay(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int64_t dNewDelay; + struct ssp_data *data = dev_get_drvdata(dev); + + if (kstrtoll(buf, 10, &dNewDelay) < 0) + return -EINVAL; + + change_sensor_delay(data, GYRO_UNCALIB_SENSOR, dNewDelay); + + return size; +} + +static ssize_t show_pressure_delay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%lld\n", data->adDelayBuf[PRESSURE_SENSOR]); +} + +static ssize_t set_pressure_delay(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int64_t dNewDelay; + struct ssp_data *data = dev_get_drvdata(dev); + + if (kstrtoll(buf, 10, &dNewDelay) < 0) + return -EINVAL; + + change_sensor_delay(data, PRESSURE_SENSOR, dNewDelay); + return size; +} + +static ssize_t show_gesture_delay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%lld\n", data->adDelayBuf[GESTURE_SENSOR]); +} + +static ssize_t set_gesture_delay(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int64_t dNewDelay; + struct ssp_data *data = dev_get_drvdata(dev); + + if (kstrtoll(buf, 10, &dNewDelay) < 0) + return -EINVAL; + + change_sensor_delay(data, GESTURE_SENSOR, dNewDelay); + + return size; +} + +static ssize_t show_light_delay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%lld\n", data->adDelayBuf[LIGHT_SENSOR]); +} + +static ssize_t set_light_delay(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int64_t dNewDelay; + struct ssp_data *data = dev_get_drvdata(dev); + + if (kstrtoll(buf, 10, &dNewDelay) < 0) + return -EINVAL; + + change_sensor_delay(data, LIGHT_SENSOR, dNewDelay); + return size; +} + +static ssize_t show_prox_delay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%lld\n", data->adDelayBuf[PROXIMITY_SENSOR]); +} + +static ssize_t set_prox_delay(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int64_t dNewDelay; + struct ssp_data *data = dev_get_drvdata(dev); + + if (kstrtoll(buf, 10, &dNewDelay) < 0) + return -EINVAL; + + change_sensor_delay(data, PROXIMITY_SENSOR, dNewDelay); + return size; +} + +static ssize_t show_temp_humi_delay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%lld\n", + data->adDelayBuf[TEMPERATURE_HUMIDITY_SENSOR]); +} + +static ssize_t set_temp_humi_delay(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int64_t dNewDelay; + struct ssp_data *data = dev_get_drvdata(dev); + + if (kstrtoll(buf, 10, &dNewDelay) < 0) + return -EINVAL; + + change_sensor_delay(data, TEMPERATURE_HUMIDITY_SENSOR, dNewDelay); + return size; +} + +ssize_t ssp_sensorhub_voicel_pcmdump_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ssp_data *data = dev_get_drvdata(dev); + int status = ssp_sensorhub_pcm_dump(data->hub_data); + + return sprintf(buf, "%s\n", (status ? "OK" : "NG")); +} + +static DEVICE_ATTR(voice_pcmdump, S_IRUSR | S_IRGRP, + ssp_sensorhub_voicel_pcmdump_show, NULL); + +static struct device_attribute *voice_attrs[] = { + &dev_attr_voice_pcmdump, + NULL, +}; + +static void initialize_voice_sysfs(struct ssp_data *data) +{ + sensors_register(data->voice_device, data, voice_attrs, "ssp_voice"); +} + +static void remove_voice_sysfs(struct ssp_data *data) +{ + sensors_unregister(data->voice_device, voice_attrs); +} + + +static DEVICE_ATTR(mcu_rev, S_IRUSR | S_IRGRP, mcu_revision_show, NULL); +static DEVICE_ATTR(mcu_name, S_IRUSR | S_IRGRP, mcu_model_name_show, NULL); +static DEVICE_ATTR(mcu_update, S_IRUSR | S_IRGRP, + mcu_update_kernel_bin_show, NULL); +static DEVICE_ATTR(mcu_update2, S_IRUSR | S_IRGRP, + mcu_update_kernel_crashed_bin_show, NULL); +static DEVICE_ATTR(mcu_update_ums, S_IRUSR | S_IRGRP, + mcu_update_ums_bin_show, NULL); +static DEVICE_ATTR(mcu_reset, S_IRUSR | S_IRGRP, mcu_reset_show, NULL); +static DEVICE_ATTR(mcu_dump, S_IRUSR | S_IRGRP, mcu_dump_show, NULL); + +static DEVICE_ATTR(mcu_test, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + mcu_factorytest_show, mcu_factorytest_store); +static DEVICE_ATTR(mcu_sleep_test, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + mcu_sleep_factorytest_show, mcu_sleep_factorytest_store); +static DEVICE_ATTR(enable, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + show_sensors_enable, set_sensors_enable); +static DEVICE_ATTR(enable_irq, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + show_enable_irq, set_enable_irq); +static DEVICE_ATTR(accel_poll_delay, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + show_acc_delay, set_acc_delay); +static DEVICE_ATTR(gyro_poll_delay, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + show_gyro_delay, set_gyro_delay); +static DEVICE_ATTR(uncalib_gyro_poll_delay, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + show_uncalib_gyro_delay, set_uncalib_gyro_delay); +static DEVICE_ATTR(mag_poll_delay, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + show_mag_delay, set_mag_delay); +static DEVICE_ATTR(uncal_mag_poll_delay, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + show_uncal_mag_delay, set_uncal_mag_delay); +static DEVICE_ATTR(rot_poll_delay, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + show_rot_delay, set_rot_delay); +static DEVICE_ATTR(game_rot_poll_delay, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + show_game_rot_delay, set_game_rot_delay); +static DEVICE_ATTR(step_det_poll_delay, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + show_step_det_delay, set_step_det_delay); +static DEVICE_ATTR(pressure_poll_delay, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + show_pressure_delay, set_pressure_delay); +static DEVICE_ATTR(ssp_flush, S_IWUSR | S_IWGRP, + NULL, set_flush); +static DEVICE_ATTR(shake_cam, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + show_shake_cam, set_shake_cam); +static struct device_attribute dev_attr_gesture_poll_delay + = __ATTR(poll_delay, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + show_gesture_delay, set_gesture_delay); +static struct device_attribute dev_attr_light_poll_delay + = __ATTR(poll_delay, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + show_light_delay, set_light_delay); +static struct device_attribute dev_attr_prox_poll_delay + = __ATTR(poll_delay, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + show_prox_delay, set_prox_delay); +static struct device_attribute dev_attr_temp_humi_poll_delay + = __ATTR(poll_delay, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + show_temp_humi_delay, set_temp_humi_delay); +static struct device_attribute dev_attr_sig_motion_poll_delay + = __ATTR(poll_delay, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + show_sig_motion_delay, set_sig_motion_delay); +static struct device_attribute dev_attr_step_cnt_poll_delay + = __ATTR(poll_delay, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + show_step_cnt_delay, set_step_cnt_delay); + +static struct device_attribute *mcu_attrs[] = { + &dev_attr_enable, + &dev_attr_mcu_rev, + &dev_attr_mcu_name, + &dev_attr_mcu_test, + &dev_attr_mcu_reset, + &dev_attr_mcu_dump, + &dev_attr_mcu_update, + &dev_attr_mcu_update2, + &dev_attr_mcu_update_ums, + &dev_attr_mcu_sleep_test, + &dev_attr_enable_irq, + &dev_attr_accel_poll_delay, + &dev_attr_gyro_poll_delay, + &dev_attr_uncalib_gyro_poll_delay, + &dev_attr_mag_poll_delay, + &dev_attr_uncal_mag_poll_delay, + &dev_attr_rot_poll_delay, + &dev_attr_game_rot_poll_delay, + &dev_attr_step_det_poll_delay, + &dev_attr_pressure_poll_delay, + &dev_attr_ssp_flush, + &dev_attr_shake_cam, + NULL, +}; + +static long ssp_batch_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct ssp_data *data + = container_of(file->private_data, + struct ssp_data, batch_io_device); + + struct batch_config batch; + + void __user *argp = (void __user *)arg; + int retries = 2; + int ret = 0; + int sensor_type, ms_delay; + int timeout_ms = 0; + u8 uBuf[9]; + + sensor_type = (cmd & 0xFF); + + if ((cmd >> 8 & 0xFF) != BATCH_IOCTL_MAGIC) { + pr_err("[SSP] Invalid BATCH CMD %x", cmd); + return -EINVAL; + } + + while (retries--) { + ret = copy_from_user(&batch, argp, sizeof(batch)); + if (likely(!ret)) + break; + } + if (unlikely(ret)) { + pr_err("[SSP] batch ioctl err(%d)", ret); + return -EINVAL; + } + ms_delay = get_msdelay(batch.delay); + timeout_ms = div_s64(batch.timeout, 1000000); + memcpy(&uBuf[0], &ms_delay, 4); + memcpy(&uBuf[4], &timeout_ms, 4); + uBuf[8] = batch.flag; + + if (batch.timeout){ /* add or dry */ + + if(!(batch.flag & SENSORS_BATCH_DRY_RUN)) { /* real batch, NOT DRY, change delay */ + ret = 1; + /* if sensor is not running state, enable will be called. + MCU return fail when receive chage delay inst during NO_SENSOR STATE */ + if (data->aiCheckStatus[sensor_type] == RUNNING_SENSOR_STATE) { + ret = send_instruction_sync(data, CHANGE_DELAY, sensor_type, uBuf, 9); + } + if (ret > 0) { /* ret 1 is success */ + data->batchOptBuf[sensor_type] = (u8)batch.flag; + data->batchLatencyBuf[sensor_type] = timeout_ms; + data->adDelayBuf[sensor_type] = batch.delay; + } + } else { /* real batch, DRY RUN */ + ret = send_instruction_sync(data, CHANGE_DELAY, sensor_type, uBuf, 9); + if (ret > 0) { /* ret 1 is success */ + data->batchOptBuf[sensor_type] = (u8)batch.flag; + data->batchLatencyBuf[sensor_type] = timeout_ms; + data->adDelayBuf[sensor_type] = batch.delay; + } + } + } else { /* remove batch or normal change delay, remove or add will be called. */ + + if (!(batch.flag & SENSORS_BATCH_DRY_RUN)) { /* no batch, NOT DRY, change delay */ + data->batchOptBuf[sensor_type] = 0; + data->batchLatencyBuf[sensor_type] = 0; + data->adDelayBuf[sensor_type] = batch.delay; + if (data->aiCheckStatus[sensor_type] == RUNNING_SENSOR_STATE) { + send_instruction(data, CHANGE_DELAY, sensor_type, uBuf, 9); + } + } + } + + pr_info("[SSP] batch %d: delay %lld, timeout %lld, flag %d, ret %d\n", + sensor_type, batch.delay, batch.timeout, batch.flag, ret); + if (!batch.timeout) + return 0; + if (ret <= 0) + return -EINVAL; + else + return 0; +} + + +static struct file_operations ssp_batch_fops = { + .owner = THIS_MODULE, + .open = nonseekable_open, + .unlocked_ioctl = ssp_batch_ioctl, +}; + +static void initialize_mcu_factorytest(struct ssp_data *data) +{ + sensors_register(data->mcu_device, data, mcu_attrs, "ssp_sensor"); +} + +static void remove_mcu_factorytest(struct ssp_data *data) +{ + sensors_unregister(data->mcu_device, mcu_attrs); +} + +int initialize_sysfs(struct ssp_data *data) +{ + if (device_create_file(&data->gesture_input_dev->dev, + &dev_attr_gesture_poll_delay)) + goto err_gesture_input_dev; + + if (device_create_file(&data->light_input_dev->dev, + &dev_attr_light_poll_delay)) + goto err_light_input_dev; + + if (device_create_file(&data->prox_input_dev->dev, + &dev_attr_prox_poll_delay)) + goto err_prox_input_dev; + + if (device_create_file(&data->temp_humi_input_dev->dev, + &dev_attr_temp_humi_poll_delay)) + goto err_temp_humi_input_dev; + + if (device_create_file(&data->sig_motion_input_dev->dev, + &dev_attr_sig_motion_poll_delay)) + goto err_sig_motion_input_dev; + + if (device_create_file(&data->step_cnt_input_dev->dev, + &dev_attr_step_cnt_poll_delay)) + goto err_step_cnt_input_dev; + + data->batch_io_device.minor = MISC_DYNAMIC_MINOR; + data->batch_io_device.name = "batch_io"; + data->batch_io_device.fops = &ssp_batch_fops; + if (misc_register(&data->batch_io_device)) + goto err_batch_io_dev; + + initialize_accel_factorytest(data); + initialize_gyro_factorytest(data); + initialize_prox_factorytest(data); + initialize_light_factorytest(data); + initialize_pressure_factorytest(data); + initialize_magnetic_factorytest(data); + initialize_mcu_factorytest(data); + initialize_gesture_factorytest(data); +#ifdef CONFIG_SENSORS_SSP_MOBEAM + initialize_mobeam(data); +#endif + initialize_voice_sysfs(data); + + return SUCCESS; +err_batch_io_dev: + device_remove_file(&data->step_cnt_input_dev->dev, + &dev_attr_step_cnt_poll_delay); +err_step_cnt_input_dev: + device_remove_file(&data->sig_motion_input_dev->dev, + &dev_attr_sig_motion_poll_delay); +err_sig_motion_input_dev: + device_remove_file(&data->temp_humi_input_dev->dev, + &dev_attr_temp_humi_poll_delay); +err_temp_humi_input_dev: + device_remove_file(&data->prox_input_dev->dev, + &dev_attr_prox_poll_delay); +err_prox_input_dev: + device_remove_file(&data->light_input_dev->dev, + &dev_attr_light_poll_delay); +err_light_input_dev: + device_remove_file(&data->gesture_input_dev->dev, + &dev_attr_gesture_poll_delay); +err_gesture_input_dev: + pr_err("[SSP] error init sysfs"); + return ERROR; +} + +void remove_sysfs(struct ssp_data *data) +{ + device_remove_file(&data->gesture_input_dev->dev, + &dev_attr_gesture_poll_delay); + device_remove_file(&data->light_input_dev->dev, + &dev_attr_light_poll_delay); + device_remove_file(&data->prox_input_dev->dev, + &dev_attr_prox_poll_delay); + device_remove_file(&data->temp_humi_input_dev->dev, + &dev_attr_temp_humi_poll_delay); + device_remove_file(&data->sig_motion_input_dev->dev, + &dev_attr_sig_motion_poll_delay); + device_remove_file(&data->step_cnt_input_dev->dev, + &dev_attr_step_cnt_poll_delay); + ssp_batch_fops.unlocked_ioctl = NULL; + misc_deregister(&data->batch_io_device); + remove_accel_factorytest(data); + remove_gyro_factorytest(data); + remove_prox_factorytest(data); + remove_light_factorytest(data); + remove_pressure_factorytest(data); + remove_magnetic_factorytest(data); + remove_mcu_factorytest(data); + remove_gesture_factorytest(data); +#ifdef CONFIG_SENSORS_SSP_MOBEAM + remove_mobeam(data); +#endif + /*snamy.jeong_0630 voice dump & data*/ + remove_voice_sysfs(data); + + destroy_sensor_class(); +} -- 2.7.4