/* * 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 #define SSP_DEBUG_TIMER_SEC (5 * 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; int 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, 0660); 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 OCCURRED(%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; int 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; #ifdef CONFIG_SENSORS_SSP_SHTC1 mutex_lock(&big->data->bulk_temp_read_lock); if (big->data->bulk_buffer == NULL) big->data->bulk_buffer = kzalloc(sizeof(struct shtc1_buffer), GFP_KERNEL); big->data->bulk_buffer->len = big->length / 12; #endif 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; } /* 12 = 1 chunk size for ks79.shin * order is thermistor Bat, thermistor PA, Temp, * Humidity, Baro, Gyro * each data consist of 2bytes */ 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))); #ifdef CONFIG_SENSORS_SSP_SHTC1 big->data->bulk_buffer->batt[buffindex] = *((u16 *) (buffer + i + 0)); big->data->bulk_buffer->chg[buffindex] = *((u16 *) (buffer + i + 2)); big->data->bulk_buffer->temp[buffindex] = *((s16 *) (buffer + i + 4)); big->data->bulk_buffer->humidity[buffindex] = *((u16 *) (buffer + i + 6)); big->data->bulk_buffer->baro[buffindex] = *((s16 *) (buffer + i + 8)); big->data->bulk_buffer->gyro[buffindex] = *((s16 *) (buffer + i + 10)); buffindex++; i += 12; #else buffindex++; i += 12;/* 6 ?? */ #endif } residue -= packet_len; } #ifdef CONFIG_SENSORS_SSP_SHTC1 if (iRet == SUCCESS) report_bulk_comp_data(big->data); mutex_unlock(&big->data->bulk_temp_read_lock); #endif 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 = 0; #if SSP_DBG int cur = *pDataIdx; #endif memcpy(&iLength, pchRcvDataFrame + *pDataIdx, sizeof(u16)); *pDataIdx += sizeof(u16); 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(); ssp_enable(data, false); clean_pending_list(data); bbd_mcu_reset(false); data->uTimeOutCnt = 0; data->uComFailCnt = 0; data->mcuAbnormal = false; } void sync_sensor_state(struct ssp_data *data) { unsigned char uBuf[9] = {0,}; unsigned int uSensorCnt; int iRet = 0; gyro_open_calibration(data); 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__); #ifdef CONFIG_SENSORS_SSP_SX9306 if (atomic64_read(&data->aSensorEnable) & (1 << GRIP_SENSOR)) { open_grip_caldata(data); set_grip_calibration(data, true); } #endif udelay(10); for (uSensorCnt = 0; uSensorCnt < SENSOR_MAX; uSensorCnt++) { mutex_lock(&data->enable_mutex); if (atomic64_read(&data->aSensorEnable) & (1ULL << 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 (atomic64_read(&data->aSensorEnable) & (1ULL << GYROSCOPE_SENSOR)) send_vdis_flag(data, data->IsVDIS_Enabled); 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); set_light_coef(data); 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: 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 GYROSCOPE_SENSOR: #ifdef CONFIG_SENSORS_SSP_INTERRUPT_GYRO_SENSOR case INTERRUPT_GYRO_SENSOR: #endif ssp_dbg("[SSP] %u : %d, %d, %d (%ums, %dms)\n", uSensor, data->buf[uSensor].gyro.x, data->buf[uSensor].gyro.y, data->buf[uSensor].gyro.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_z, 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, data->buf[uSensor].temperature, 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: case UNCAL_LIGHT_SENSOR: 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])); break; case LIGHT_FLICKER_SENSOR: ssp_dbg("[SSP] %u : %u, (%ums)\n", uSensor, data->buf[uSensor].light_flicker, get_msdelay(data->adDelayBuf[uSensor])); break; case LIGHT_CCT_SENSOR: 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])); break; case PROXIMITY_SENSOR: ssp_dbg("[SSP] %u : %d, %d (%ums)\n", uSensor, data->buf[uSensor].prox_detect, data->buf[uSensor].prox_adc, get_msdelay(data->adDelayBuf[uSensor])); break; case PROXIMITY_POCKET: ssp_dbg("[SSP] %u : %d, %d (%ums)\n", uSensor, data->buf[uSensor].proximity_pocket_detect, data->buf[uSensor].proximity_pocket_adc, get_msdelay(data->adDelayBuf[uSensor])); break; case PROXIMITY_ALERT_SENSOR: ssp_dbg("[SSP] %u : %d, %d (%ums)\n", uSensor, data->buf[uSensor].prox_alert_detect, data->buf[uSensor].prox_alert_adc, 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_gyro.x, data->buf[uSensor].uncal_gyro.y, data->buf[uSensor].uncal_gyro.z, data->buf[uSensor].uncal_gyro.offset_x, data->buf[uSensor].uncal_gyro.offset_y, data->buf[uSensor].uncal_gyro.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; case LIGHT_IR_SENSOR: ssp_dbg("[SSP] %u : %u, %u, %u, %u, %u, %u, %u (%ums)\n", uSensor, data->buf[uSensor].irdata, data->buf[uSensor].ir_r, data->buf[uSensor].ir_g, data->buf[uSensor].ir_b, data->buf[uSensor].ir_w, data->buf[uSensor].ir_a_time, data->buf[uSensor].ir_a_gain, get_msdelay(data->adDelayBuf[uSensor])); break; case TILT_DETECTOR: ssp_dbg("[SSP] %u : %u(%ums)\n", uSensor, data->buf[uSensor].tilt_detector, get_msdelay(data->adDelayBuf[uSensor])); break; case PICKUP_GESTURE: ssp_dbg("[SSP] %u : %u(%ums)\n", uSensor, data->buf[uSensor].pickup_gesture, get_msdelay(data->adDelayBuf[uSensor])); break; case THERMISTOR_SENSOR: ssp_dbg("[SSP] %u : %u %u (%ums)\n", uSensor, data->buf[uSensor].thermistor_type, data->buf[uSensor].thermistor_raw, get_msdelay(data->adDelayBuf[uSensor])); break; case ACCEL_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 WAKE_UP_MOTION: ssp_dbg("[SSP] %u : %d (%ums)\n", uSensor, data->buf[uSensor].wakeup_motion, get_msdelay(data->adDelayBuf[uSensor])); case CALL_GESTURE: ssp_dbg("[SSP] %u : %d (%ums)\n", uSensor, data->buf[uSensor].call_gesture, get_msdelay(data->adDelayBuf[uSensor])); break; case MOVE_DETECTOR: ssp_dbg("[SSP] %u : %d (%ums)\n", uSensor, data->buf[uSensor].move_detect, get_msdelay(data->adDelayBuf[uSensor])); break; case LED_COVER_EVENT_SENSOR: ssp_dbg("[SSP] %u : %d (%ums)\n", uSensor, data->buf[uSensor].led_cover_event, get_msdelay(data->adDelayBuf[uSensor])); break; case AUTO_ROTATION_SENSOR: ssp_dbg("[SSP] %u : %d (%ums)\n", uSensor, data->buf[uSensor].auto_rotation_event, get_msdelay(data->adDelayBuf[uSensor])); break; case SAR_BACKOFF_MOTION: ssp_dbg("[SSP] %u : %d (%ums)\n", uSensor, data->buf[uSensor].sar_backoff_motion_event, get_msdelay(data->adDelayBuf[uSensor])); case POCKET_MODE_LITE: ssp_dbg("[SSP] %u : %d %d(%ums)\n", uSensor, data->buf[uSensor].pocket_mode_lite_t.prox, data->buf[uSensor].pocket_mode_lite_t.lux, get_msdelay(data->adDelayBuf[uSensor])); break; case BULK_SENSOR: case GPS_SENSOR: break; default: ssp_dbg("[SSP] Wrong sensorCnt: %u\n", uSensor); break; } } /* *check_sensor_event *- return * true : there is no accel or light sensor event over 5sec when sensor is registered */ bool check_wait_event(struct ssp_data *data) { u64 timestamp = get_current_timestamp(); int check_sensors[2] = {ACCELEROMETER_SENSOR, LIGHT_SENSOR}; int i, sensor; bool res = false; int arrSize = (ANDROID_VERSION < 90000 ? 2 : 1); for (i = 0 ; i < arrSize ; i++) { // because light sensor does not check anymore sensor = check_sensors[i]; //the sensor is registered if ((atomic64_read(&data->aSensorEnable) & (1 << sensor) && !(data->IsGyroselftest)) //non batching mode && data->IsBypassMode[sensor] == 1 //there is no sensor event over 3sec && data->LastSensorTimeforReset[sensor] + 7000000000ULL < timestamp) { pr_info("[SSP] %s - sensor(%d) last = %lld, cur = %lld\n", __func__, sensor, data->LastSensorTimeforReset[sensor], timestamp); res = true; data->uNoRespSensorCnt++; } //pr_info("[SSP]test %s - sensor(%d mode %d) last = %lld, cur = %lld\n", //__func__,sensor,data->IsBypassMode[sensor],data->LastSensorTimeforReset[sensor],timestamp); } return res; } static void debug_work_func(struct work_struct *work) { unsigned int uSensorCnt; struct ssp_data *data = container_of(work, struct ssp_data, work_debug); ssp_dbg("[SSP]: %s(%u) - Sensor state: 0x%llx, RC: %u(%u, %u, %u), CC: %u, TC: %u NSC: %u EC: %u GPS: %s\n", __func__, data->uIrqCnt, data->uSensorState, data->uResetCnt, data->mcuCrashedCnt, data->IsNoRespCnt, data->resetCntGPSisOn, data->uComFailCnt, data->uTimeOutCnt, data->uNoRespSensorCnt, data->errorCount, data->IsGpsWorking ? "true" : "false"); if(!strstr("", data->resetInfoDebug)) pr_info("[SSP]: %s(%lld, %lld)\n", data->resetInfoDebug, data->resetInfoDebugTime, get_current_timestamp()); for (uSensorCnt = 0; uSensorCnt < SENSOR_MAX; uSensorCnt++) { if ((atomic64_read(&data->aSensorEnable) & (1ULL << uSensorCnt)) || data->batchLatencyBuf[uSensorCnt]) { print_sensordata(data, uSensorCnt); if (data->indio_dev[uSensorCnt] != NULL && list_empty(&data->indio_dev[uSensorCnt]->buffer->buffer_list)) { pr_err("[SSP] %u : buffer_list of iio:device%d is empty!\n", uSensorCnt, data->indio_dev[uSensorCnt]->id); } } } if (data->resetting) goto exit; if (((atomic64_read(&data->aSensorEnable) & (1 << ACCELEROMETER_SENSOR)) && (data->batchLatencyBuf[ACCELEROMETER_SENSOR] == 0) && (data->uIrqCnt == 0) && (data->uTimeOutCnt > 0)) || (data->uTimeOutCnt > LIMIT_TIMEOUT_CNT) || (check_wait_event(data)) || (data->mcuAbnormal == true)) { mutex_lock(&data->ssp_enable_mutex); pr_info("[SSP] : %s - uTimeOutCnt(%u), pending(%u)\n", __func__, data->uTimeOutCnt, !list_empty(&data->pending_list)); reset_mcu(data); mutex_unlock(&data->ssp_enable_mutex); } exit: 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; } /* if returns true dump mode on */ unsigned int ssp_check_sec_dump_mode(void) { #if 0 /* def CONFIG_SEC_DEBUG */ if (sec_debug_level.en.kernel_fault == 1) return 1; else return 0; #endif return 0; }