/* * Copyright (C) 2013 SAMSUNG S.LSI * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * */ #include #include #include #include #include #include #include #include #include #include #include #include "device.h" #include "hal.h" #include "osi.h" #include "sec_nfc.h" #include "util.h" int pw_driver, tr_driver; pthread_mutex_t tr_lock; int tr_closer; bool isSleep; int wakeup_delay; bool log_ptr; eNFC_DEV_MODE dev_state; tOSI_TASK_HANDLER read_task; // [Start] Workaround - i2c write fail(self wakeup) bool first_wakeup; // [End] Workaround - i2c write fail(self wakeup) void read_thread(void); void data_trace(const char* head, int len, uint8_t* p_data); int device_init(int data_trace) { dev_state = NFC_DEV_MODE_OFF; log_ptr = data_trace; read_task = OSI_task_allocate("read_task", read_thread); if (!read_task) { OSI_loge("Failed to allocate task for read thread!!"); return -1; } pthread_mutex_init(&tr_lock, NULL); return 0; } void device_deinit() { device_close(); pthread_mutex_destroy(&tr_lock); OSI_task_free(read_task); } int device_open() { int ret; char pw_driver_name[64]; char tr_driver_name[64]; ret = get_config_string(cfg_name_table[CFG_POWER_DRIVER], pw_driver_name, sizeof(pw_driver_name)); if (ret == 0) return -EPERM; ret = get_config_string(cfg_name_table[CFG_TRANS_DRIVER], tr_driver_name, sizeof(tr_driver_name)); if (ret == 0) return -EPERM; pw_driver = open(pw_driver_name, O_RDWR | O_NOCTTY); if (pw_driver < 0) { OSI_loge("Failed to open device driver: %s, pw_driver : 0x%x, errno = %d", pw_driver_name, pw_driver, errno); return pw_driver; } tr_driver = pw_driver; OSI_loge("pw_driver: %d, tr_driver: %d", pw_driver, tr_driver); device_set_mode(NFC_DEV_MODE_BOOTLOADER); if (OSI_OK != OSI_task_run(read_task)) { OSI_loge("Failed to run read task!!"); OSI_task_stop(read_task); close(tr_driver); close(pw_driver); return -1; } if (!get_config_int(cfg_name_table[CFG_WAKEUP_DELAY], &wakeup_delay)) wakeup_delay = 10; return 0; } void device_close(void) { close(tr_driver); close(pw_driver); pthread_mutex_lock(&tr_lock); tr_driver = -1; pw_driver = -1; pthread_mutex_unlock(&tr_lock); if (tr_closer != 0) write(tr_closer, "x", 1); OSI_task_stop(read_task); } int device_set_mode(eNFC_DEV_MODE mode) { int ret; OSI_logt("device mode chage: %d -> %d", dev_state, mode); ret = ioctl(pw_driver, SEC_NFC_SET_MODE, (int)mode); if (!ret) { if (mode == NFC_DEV_MODE_ON) isSleep = true; dev_state = mode; } return ret; } int device_sleep(void) { if (isSleep) return 0; isSleep = true; OSI_logt("NFC can be going to sleep"); return ioctl(pw_driver, SEC_NFC_SLEEP, 0); } int device_wakeup(void) { int ret = 0; if (!isSleep) return 0; isSleep = false; // [Start] Workaround - i2c write fail(self wakeup) first_wakeup = true; // [End] Workaround - i2c write fail(self wakeup) ret = ioctl(pw_driver, SEC_NFC_WAKEUP, 0); /* START [H16031401] */ if (nfc_hal_info.state == HAL_STATE_SERVICE && nfc_hal_info.msg_event == HAL_EVT_READ) return ret; /* END [H16031401] */ OSI_logt("Wakeup! in %d ms", wakeup_delay); /* wakeup delay */ OSI_delay(wakeup_delay); OSI_logt("exit"); return ret; } int device_write(uint8_t* data, size_t len) { OSI_logt("enter"); int ret = 0; int total = 0; int retry = 1; while (len != 0) { OSI_logt("before system call"); ret = write(tr_driver, data + total, len); OSI_logt("after system call"); if (ret < 0) { OSI_loge("write error ret = %d, errno = %d, retry = %d", ret, errno, retry); if (retry++ < 3 && (nfc_hal_info.flag & HAL_FLAG_RETRY_TRNS)) { // [Start] Workaround - i2c write fail(self wakeup) if ((retry == 2) && (first_wakeup == true)) { ret = ioctl(pw_driver, SEC_NFC_SLEEP, 0); OSI_delay(1); ret = ioctl(pw_driver, SEC_NFC_WAKEUP, 0); OSI_delay(wakeup_delay); first_wakeup = false; continue; } // [End] Workaround - i2c write fail(self wakeup) else { OSI_delay(5); continue; } } break; } total += ret; len -= ret; } if (len == 0) data_trace("Send", total, data); OSI_logt("exit"); return total; } int device_read(uint8_t* buffer, size_t len) { int ret = 0; int total = 0; int retry = 1; while (len != 0) { ret = read(tr_driver, buffer + total, len); if (ret <= 0) { OSI_loge("Read error ret = %d, errno = %d", ret, errno); if (retry++ < 3 && (nfc_hal_info.flag & HAL_FLAG_RETRY_TRNS)) continue; break; } total += ret; len -= ret; } return total; } void read_thread(void) { tOSI_QUEUE_HANDLER msg_que = NULL; tNFC_HAL_MSG* msg = NULL; fd_set rfds; uint8_t header[NCI_HDR_SIZE]; int close_pipe[2]; int max_fd; struct timeval tv; struct timeval* ptv = NULL; int ret; OSI_logt("enter"); /* get msg que */ msg_que = OSI_queue_get_handler("msg_q"); if (!msg_que) { OSI_loge("Not find %s queue!! exit read thread", "msg_q"); return; } /* closer */ if (pipe(close_pipe) < 0) { OSI_loge("pipe open error for closing read thread"); close_pipe[0] = 0; close_pipe[1] = 0; ptv = &tv; } tr_closer = close_pipe[1]; max_fd = (close_pipe[0] > tr_driver) ? close_pipe[0] : tr_driver; while (OSI_task_isRun(read_task) == OSI_RUN) { pthread_mutex_lock(&tr_lock); if (tr_driver < 0) { pthread_mutex_unlock(&tr_lock); break; } FD_ZERO(&rfds); FD_SET(tr_driver, &rfds); pthread_mutex_unlock(&tr_lock); if (close_pipe[0] > 0) { FD_SET(close_pipe[0], &rfds); } else { tv.tv_sec = 0; tv.tv_usec = 2000; } ret = select(max_fd + 1, &rfds, NULL, NULL, ptv); pthread_mutex_lock(&tr_lock); if (tr_driver < 0) { pthread_mutex_unlock(&tr_lock); break; } pthread_mutex_unlock(&tr_lock); if (ret == 0) /* timeout */ continue; else if (ret < 0 && errno == EINTR) /* signal received */ continue; else if (ret < 0) { OSI_loge("Polling error"); nfc_stack_cback(HAL_NFC_ERROR_EVT, HAL_NFC_STATUS_OK); break; } /* read 3 bytes (header)*/ ret = device_read(header, NCI_HDR_SIZE); if (ret == 0) continue; else if (ret != NCI_HDR_SIZE) { OSI_loge("Reading NCI header failed"); continue; } msg = (tNFC_HAL_MSG*)OSI_mem_get(NCI_CTRL_SIZE); if (!msg) { OSI_loge("Failed to allocate memory!1"); nfc_stack_cback(HAL_NFC_ERROR_EVT, HAL_NFC_STATUS_OK); break; } /* payload will read upper layer */ msg->event = HAL_EVT_READ; memcpy((void*)msg->param, (void*)header, NCI_HDR_SIZE); ret = OSI_queue_put(msg_que, (void*)msg); OSI_logd("Sent message to HAL message task, remind que: %d", ret); } close(close_pipe[0]); close(close_pipe[1]); tr_closer = 0; osi_unlock(); // TODO: why? OSI_logt("end;"); } #define TRACE_BUFFER_SIZE (NCI_CTRL_SIZE * 3 + 1) void data_trace(const char* head, int len, uint8_t* p_data) { int i = 0, header; char trace_buffer[TRACE_BUFFER_SIZE + 2]; header = (dev_state == NFC_DEV_MODE_BOOTLOADER) ? 4 : 3; while (len-- > 0 && i < NCI_CTRL_SIZE) { if (i < header) sprintf(trace_buffer + (i * 3), "%02x ", p_data[i]); else sprintf(trace_buffer + (i * 3 + 2), "%02x ", p_data[i]); i++; } if (log_ptr) OSI_logd(" %s(%3d) %s", head, i, trace_buffer); }