/* * ETF interfaces for XRadio drivers * * Copyright (c) 2013 * Xradio Technology Co., Ltd. * * 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. */ #ifndef XRADIO_ETF_C #define XRADIO_ETF_C #include #include #include #include #include #include #include #include #include "etf.h" #include "xradio.h" #include "sbus.h" #include "debug.h" /********************* interfaces of adapter layer **********************/ static struct xradio_adapter adapter_priv; #if (ETF_QUEUEMODE) static void adapter_rec_handler(struct sk_buff *__skb) { int i = 0; struct adapter_item *item; etf_printk(XRADIO_DBG_TRC, "%s\n", __func__); do { spin_lock_bh(&adapter_priv.recv_lock); if (likely(!list_empty(&adapter_priv.rx_pool))) { item = list_first_entry(&adapter_priv.rx_pool, struct adapter_item, head); item->skb = (void *)skb_get(__skb); list_move_tail(&item->head, &adapter_priv.rx_queue); spin_unlock_bh(&adapter_priv.recv_lock); etf_printk(XRADIO_DBG_NIY, "%s recv msg to rx_queue!\n", __func__); break; } else if (++i > ETF_QUEUE_TIMEOUT) { /* about 2s.*/ spin_unlock_bh(&adapter_priv.recv_lock); etf_printk(XRADIO_DBG_ERROR, "%s driver is process timeout, drop msg!\n", __func__); return; } else { etf_printk(XRADIO_DBG_NIY, "%s rx_pool is empty, wait %d!ms\n", __func__, i); } spin_unlock_bh(&adapter_priv.recv_lock); msleep(i); } while (1); if (atomic_add_return(1, &adapter_priv.rx_cnt) == 1) up(&adapter_priv.proc_sem); return; } #else static void adapter_rec_handler(struct sk_buff *skb) { struct nlmsghdr *nlhdr = (struct nlmsghdr *)skb->data; etf_printk(XRADIO_DBG_TRC, "%s\n", __func__); if (nlhdr->nlmsg_len < sizeof(struct nlmsghdr)) { etf_printk(XRADIO_DBG_ERROR, "Corrupt netlink msg!\n"); } else { int len = nlhdr->nlmsg_len - NLMSG_LENGTH(0); adapter_priv.send_pid = nlhdr->nlmsg_pid; adapter_priv.msg_len = min(ADAPTER_RX_BUF_LEN, len); memcpy(adapter_priv.msg_buf, NLMSG_DATA(nlhdr), adapter_priv.msg_len); if (len != adapter_priv.msg_len) etf_printk(XRADIO_DBG_WARN, "%s recv len(%d), proc len=%d!\n", __func__, len, adapter_priv.msg_len); else etf_printk(XRADIO_DBG_NIY, "%s recv msg len(%d)!\n", __func__, adapter_priv.msg_len); if (adapter_priv.handler) adapter_priv.handler(adapter_priv.msg_buf, adapter_priv.msg_len); } } #endif static int adapter_init_msgbuf(void) { #if (ETF_QUEUEMODE) int i = 0; #endif etf_printk(XRADIO_DBG_NIY, "%s\n", __func__); if (adapter_priv.msg_buf) { etf_printk(XRADIO_DBG_ERROR, "%s msgbuf already init!\n", __func__); return -1; } #if (ETF_QUEUEMODE) atomic_set(&adapter_priv.rx_cnt, 0); INIT_LIST_HEAD(&adapter_priv.rx_queue); INIT_LIST_HEAD(&adapter_priv.rx_pool); for (i = 0; i < ADAPTER_ITEM_MAX; i++) list_add_tail(&adapter_priv.rx_items[i].head, &adapter_priv.rx_pool); #endif adapter_priv.msg_buf = xr_kzalloc(ADAPTER_RX_BUF_LEN, false); if (!adapter_priv.msg_buf) return -ENOMEM; return 0; } static void adapter_deinit_msgbuf(void) { #if (ETF_QUEUEMODE) struct adapter_item *item; #endif etf_printk(XRADIO_DBG_NIY, "%s\n", __func__); if (adapter_priv.msg_buf) { kfree(adapter_priv.msg_buf); adapter_priv.msg_buf = NULL; } #if (ETF_QUEUEMODE) spin_lock_bh(&adapter_priv.recv_lock); list_for_each_entry(item, &adapter_priv.rx_queue, head) { if (item->skb) { dev_kfree_skb((struct sk_buff *)item->skb); item->skb = NULL; } } spin_unlock_bh(&adapter_priv.recv_lock); #endif } #if (ETF_QUEUEMODE) static int adapter_handle_rx_queue(void) { int len = 0; struct nlmsghdr *nlhdr = NULL; struct adapter_item *item; bool bHandler = false; while (1) { spin_lock_bh(&adapter_priv.recv_lock); if (!list_empty(&adapter_priv.rx_queue)) { item = list_first_entry(&adapter_priv.rx_queue, struct adapter_item, head); if (item->skb) { struct sk_buff *skb = (struct sk_buff *)(item->skb); nlhdr = (struct nlmsghdr *)skb->data; if (nlhdr->nlmsg_len < sizeof(struct nlmsghdr)) { etf_printk(XRADIO_DBG_ERROR, "Corrupt netlink msg!\n"); } else { len = nlhdr->nlmsg_len - NLMSG_LENGTH(0); adapter_priv.send_pid = nlhdr->nlmsg_pid; adapter_priv.msg_len = min(ADAPTER_RX_BUF_LEN, len); memcpy(adapter_priv.msg_buf, NLMSG_DATA(nlhdr), len); bHandler = true; if (len != adapter_priv.msg_len) etf_printk(XRADIO_DBG_WARN, "%s recv len(%d), proc len=%d!\n", __func__, len, adapter_priv.msg_len); else etf_printk(XRADIO_DBG_NIY, "%s recv msg len(%d)!\n", __func__, adapter_priv.msg_len); } dev_kfree_skb(skb); item->skb = NULL; list_move_tail(&item->head, &adapter_priv.rx_pool); } else { etf_printk(XRADIO_DBG_ERROR, "%s skb is NULL!\n", __func__); } } else { spin_unlock_bh(&adapter_priv.recv_lock); break; } spin_unlock_bh(&adapter_priv.recv_lock); if (bHandler && adapter_priv.handler) { adapter_priv.handler(adapter_priv.msg_buf, adapter_priv.msg_len); bHandler = false; } } return 0; } static int adapter_proc(void *param) { int ret = 0; int term = 0; int recv = 0; etf_printk(XRADIO_DBG_NIY, "%s\n", __func__); for (;;) { ret = down_interruptible(&adapter_priv.proc_sem); recv = atomic_xchg(&adapter_priv.rx_cnt, 0); term = kthread_should_stop(); etf_printk(XRADIO_DBG_NIY, "%s term=%d!\n", __func__, term); if (term || adapter_priv.exit || ret < 0) { break; } if (recv) { adapter_handle_rx_queue(); } } etf_printk(XRADIO_DBG_NIY, "%s: exit!\n", __func__); return 0; } static int adapter_start_thread(void) { int ret = 0; struct sched_param param = {.sched_priority = 100 }; etf_printk(XRADIO_DBG_NIY, "%s\n", __func__); adapter_priv.thread_tsk = NULL; adapter_priv.exit = 0; adapter_priv.thread_tsk = kthread_create(&adapter_proc, NULL, XRADIO_ADAPTER); if (IS_ERR(adapter_priv.thread_tsk)) { ret = PTR_ERR(adapter_priv.thread_tsk); adapter_priv.thread_tsk = NULL; } else { sched_setscheduler(adapter_priv.thread_tsk, SCHED_NORMAL, ¶m); wake_up_process(adapter_priv.thread_tsk); } return ret; } static void adapter_stop_thread(void) { etf_printk(XRADIO_DBG_NIY, "%s\n", __func__); if (adapter_priv.thread_tsk != NULL) { adapter_priv.exit = 1; up(&adapter_priv.proc_sem); kthread_stop(adapter_priv.thread_tsk); adapter_priv.thread_tsk = NULL; } else { etf_printk(XRADIO_DBG_WARN, "%s: thread_tsk is NULL!\n", __func__); } } #endif /* ETF_QUEUEMODE */ static int xradio_adapter_send(void *data, int len) { int ret = 0; struct sk_buff *skb = NULL; struct nlmsghdr *nlhdr = NULL; u8 *payload = NULL; if (0 == len || NULL == data) return -EINVAL; skb = xr_alloc_skb(NLMSG_SPACE(len + 1)); if (!skb) { etf_printk(XRADIO_DBG_WARN, "%s:xr_alloc_skb failed!\n", __func__); return -ENOMEM; } /* '\0' safe for strcpy, compat for old etf apk, may be removed in furture.*/ nlhdr = nlmsg_put(skb, 0, 0, 0, len + 1, 0); /* (len+1) is payload */ payload = (u8 *)NLMSG_DATA(nlhdr); memcpy(payload, data, len); payload[len] = '\0'; #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) NETLINK_CB(skb).pid = 0; #else NETLINK_CB(skb).portid = 0; #endif NETLINK_CB(skb).dst_group = 0; down(&adapter_priv.send_lock); ret = netlink_unicast(adapter_priv.sock, skb, adapter_priv.send_pid, 0); if (ret < 0) etf_printk(XRADIO_DBG_ERROR, "%s:netlink_unicast failed(%d),pid=%d!\n", __func__, ret, adapter_priv.send_pid); else etf_printk(XRADIO_DBG_NIY, "%s:netlink_unicast len(%d),pid=%d!\n", __func__, ret, adapter_priv.send_pid); up(&adapter_priv.send_lock); return ret; } static int xradio_adapter_send_pkg(void *data1, int len1, void *data2, int len2) { int ret = 0; struct sk_buff *skb = NULL; struct nlmsghdr *nlhdr = NULL; u8 *payload = NULL; int total_len = len1 + len2; if (len1 <= 0 || len2 < 0 || NULL == data1) return -EINVAL; skb = xr_alloc_skb(NLMSG_SPACE(total_len + 1)); if (!skb) { etf_printk(XRADIO_DBG_WARN, "%s:xr_alloc_skb failed!\n", __func__); return -ENOMEM; } /* '\0' safe for strcpy, compat for old etf apk, may be removed in furture.*/ nlhdr = nlmsg_put(skb, 0, 0, 0, total_len + 1, 0); payload = (u8 *)NLMSG_DATA(nlhdr); memcpy(payload, data1, len1); if (data2 && len2) memcpy((payload + len1), data2, len2); payload[total_len] = '\0'; #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) NETLINK_CB(skb).pid = 0; #else NETLINK_CB(skb).portid = 0; #endif NETLINK_CB(skb).dst_group = 0; down(&adapter_priv.send_lock); ret = netlink_unicast(adapter_priv.sock, skb, adapter_priv.send_pid, 0); if (ret < 0) etf_printk(XRADIO_DBG_ERROR, "%s:netlink_unicast failed(%d),pid=%d!\n", __func__, ret, adapter_priv.send_pid); else etf_printk(XRADIO_DBG_NIY, "%s:netlink_unicast len(%d),pid=%d!\n", __func__, ret, adapter_priv.send_pid); up(&adapter_priv.send_lock); return ret; } struct xradio_adapter *xradio_adapter_init(msg_proc func) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) struct netlink_kernel_cfg cfg = { .input = adapter_rec_handler, }; #endif if (func == NULL) { etf_printk(XRADIO_DBG_ERROR, "%s msg_proc is NULL\n", __func__); return NULL; } adapter_priv.handler = func; sema_init(&adapter_priv.send_lock, 1); if (adapter_init_msgbuf()) { etf_printk(XRADIO_DBG_ERROR, "%s adapter_init_msgbuf failed\n", __func__); return NULL; } #if (ETF_QUEUEMODE) sema_init(&adapter_priv.proc_sem, 0); if (adapter_start_thread()) { adapter_deinit_msgbuf(); etf_printk(XRADIO_DBG_ERROR, "%s adapter_start_thread failed\n", __func__); return NULL; } #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) adapter_priv.sock = netlink_kernel_create(&init_net, NL_FOR_XRADIO, 0, adapter_rec_handler, NULL, THIS_MODULE); #elif (LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0)) adapter_priv.sock = netlink_kernel_create(&init_net, NL_FOR_XRADIO, THIS_MODULE, &cfg); #else adapter_priv.sock = netlink_kernel_create(&init_net, NL_FOR_XRADIO, &cfg); #endif if (adapter_priv.sock == NULL) { #if (ETF_QUEUEMODE) adapter_stop_thread(); #endif adapter_deinit_msgbuf(); etf_printk(XRADIO_DBG_ERROR, "%s netlink_kernel_create failed\n", __func__); return NULL; } return &adapter_priv; } void xradio_adapter_deinit(void) { if (adapter_priv.sock != NULL) { netlink_kernel_release(adapter_priv.sock); adapter_priv.sock = NULL; } #if (ETF_QUEUEMODE) adapter_stop_thread(); #endif adapter_deinit_msgbuf(); } /********************* interfaces of etf core **********************/ static struct xradio_etf etf_priv; static struct etf_api_context context_save; int wsm_etf_cmd(struct xradio_common *hw_priv, struct wsm_hdr *arg); int xradio_bh_suspend(struct xradio_common *hw_priv); int xradio_bh_resume(struct xradio_common *hw_priv); bool etf_is_connect(void) { return (bool)(etf_priv.etf_state != ETF_STAT_NULL); } void etf_set_core(void *core_priv) { etf_priv.core_priv = core_priv; } const char *etf_get_fwpath(void) { return (const char *)etf_priv.fw_path; } const char *etf_get_sddpath(void) { return (const char *)etf_priv.sdd_path; } static int etf_get_sdd_param(u8 *sdd, int sdd_len, u8 ies, void **data) { int ie_len = 0; int parsedLength = 0; struct xradio_sdd *pElement = NULL; etf_printk(XRADIO_DBG_TRC, "%s\n", __func__); /*parse SDD config.*/ pElement = (struct xradio_sdd *)sdd; parsedLength += (FIELD_OFFSET(struct xradio_sdd, data) + \ pElement->length); pElement = FIND_NEXT_ELT(pElement); while (parsedLength < sdd_len) { if (pElement->id == ies) { *data = (void *)pElement->data; ie_len = pElement->length; } parsedLength += (FIELD_OFFSET(struct xradio_sdd, data) + \ pElement->length); pElement = FIND_NEXT_ELT(pElement); } xradio_dbg(XRADIO_DBG_MSG, "parse len=%d, ie_len=%d.\n", parsedLength, ie_len); return ie_len; } static int etf_driver_resp(int state, int result) { struct drv_resp drv_ret; drv_ret.len = sizeof(drv_ret); drv_ret.id = ETF_DRIVER_IND_ID; drv_ret.state = state; drv_ret.result = result; return xradio_adapter_send(&drv_ret, drv_ret.len); } static int xradio_etf_connect(void) { int ret = BOOT_SUCCESS; etf_printk(XRADIO_DBG_TRC, "%s\n", __func__); if (etf_priv.etf_state == ETF_STAT_NULL) { etf_priv.etf_state = ETF_STAT_CONNECTING; ret = xradio_core_init(); if (ret) { etf_printk(XRADIO_DBG_ERROR, "%s:xradio_core_init failed(%d).\n", __func__, ret); goto err; } etf_priv.etf_state = ETF_STAT_CONNECTED; } else { etf_printk(XRADIO_DBG_WARN, "%s etf is already connect.\n", __func__); return ETF_ERR_CONNECTED; } etf_printk(XRADIO_DBG_ALWY, "%s:ETF connect success.\n", __func__); return 0; err: etf_priv.etf_state = ETF_STAT_NULL; return ret; } static void xradio_etf_disconnect(void) { etf_printk(XRADIO_DBG_TRC, "%s\n", __func__); if (etf_priv.etf_state != ETF_STAT_NULL) { xradio_core_deinit(); etf_priv.etf_state = ETF_STAT_NULL; } etf_printk(XRADIO_DBG_ALWY, "%s end.\n", __func__); } #if DGB_XRADIO_HWT //HWT extern u8 hwt_testing; extern u8 hwt_tx_en; extern u8 hwt_tx_cfm; /*confirm interval*/ extern u16 hwt_tx_len; extern u16 hwt_tx_num; extern u8 hwt_rx_en; extern u16 hwt_rx_len; extern u16 hwt_rx_num; extern struct timeval hwt_start_time; typedef struct HWT_PARAM_ENC_S { u16 Msglen; u16 MsgID; u16 TestID; u16 Params; u16 Datalen; u16 Data[2]; } HWT_PARAMENC; int wsm_hwt_cmd(struct xradio_common *hw_priv, void *arg, size_t arg_size); int HWT_Tx_handle(HWT_PARAMETERS *cmd) { etf_printk(XRADIO_DBG_TRC, "%s: MsgID=0x%04X, MsgLen=%d, TestID=%d, params=%d, datalen=%d\n", __func__, cmd->MsgID, cmd->Msglen, cmd->TestID, cmd->Params, cmd->Data); if (hwt_testing) { xradio_dbg(XRADIO_DBG_ALWY, "cmd refuse, hwt is testing!\n"); return 0; } if (cmd->TestID == 0x1) { hwt_tx_len = cmd->Data; hwt_tx_num = cmd->Params; hwt_tx_cfm = 1;//16; Be sure about the firmware has been using 16 buf when this value is set to 16 hwt_tx_en = 1; hwt_testing = 1; xradio_dbg(XRADIO_DBG_ALWY, "hwt_tx_en=%d, hwt_tx_len=%d, hwt_tx_num=%d, hwt_tx_cfm=%d\n", hwt_tx_en, hwt_tx_len, hwt_tx_num, hwt_tx_cfm); if (!((struct xradio_common *)etf_priv.core_priv)->bh_error && atomic_add_return(1, &((struct xradio_common *)etf_priv.core_priv)->bh_tx) == 1) wake_up(&((struct xradio_common *)etf_priv.core_priv)->bh_wq); } else if (cmd->TestID == 0x2) { hwt_rx_len = cmd->Data; hwt_rx_num = cmd->Params; hwt_rx_en = 1; hwt_testing = 1; xradio_dbg(XRADIO_DBG_ALWY, "hwt_rx_en=%d, hwt_rx_len=%d, hwt_rx_num=%d\n", hwt_rx_en, hwt_rx_len, hwt_rx_num); wsm_hwt_cmd((struct xradio_common *)etf_priv.core_priv, (void *)&cmd->TestID, sizeof(HWT_PARAMETERS)); do_gettimeofday(&hwt_start_time); } else if (cmd->TestID == 0x3) { xradio_dbg(XRADIO_DBG_ALWY, "pkt_num=%d, pkt_len=%d, enc_data=0x%04X\n", cmd->Params, ((HWT_PARAMENC *)cmd)->Data[0], ((HWT_PARAMENC *)cmd)->Data[1]); hwt_testing = 1; wsm_hwt_cmd((struct xradio_common *)etf_priv.core_priv, (void *)&cmd->TestID, sizeof(HWT_PARAMETERS)+2); } else if (cmd->TestID == 0x4) { hwt_testing = 1; wsm_hwt_cmd((struct xradio_common *)etf_priv.core_priv, (void *)&cmd->TestID, sizeof(HWT_PARAMETERS)); } else { } return 0; } #endif static int xradio_etf_proc(void *data, int len) { int ret = BOOT_SUCCESS; struct wsm_hdr *hdr_rev = (struct wsm_hdr *)data; struct drv_download *download; int datalen; etf_printk(XRADIO_DBG_TRC, "%s\n", __func__); down(&etf_priv.etf_lock); /* Don't process ETF cmd in wlan mode */ if (etf_priv.is_wlan) { etf_printk(XRADIO_DBG_WARN, "%s is in wlan mode.\n", __func__); ret = ETF_ERR_WLAN_MODE; etf_driver_resp(BOOT_STATE_NULL, ret); goto out; } /* Process msg from ETF API */ switch (MSG_ID(hdr_rev->id)) { case ETF_SOFT_RESET_REQ_ID: etf_printk(XRADIO_DBG_ALWY, "SOFT_RESET\n"); xradio_etf_disconnect(); etf_priv.fw_path = XR829_ETF_FIRMWARE; etf_priv.sdd_path = XR829_SDD_FILE; etf_priv.seq_send = 0; etf_priv.seq_recv = 0; memset(&context_save, 0, sizeof(context_save)); etf_driver_resp(BOOT_STATE_NULL, BOOT_SUCCESS); break; case ETF_GET_API_CONTEXT_ID: /* for ETF tools which cannot save context*/ { struct etf_api_context_result *ctxt_ret = NULL; struct xradio_common *hw_priv = etf_priv.core_priv; ctxt_ret = (struct etf_api_context_result *)hdr_rev; if (hw_priv) { ctxt_ret->is_etf_fw_run = hw_priv->wsm_caps.firmwareReady; memcpy(ctxt_ret->fw_label, hw_priv->wsm_caps.fw_label, min(HI_SW_LABEL_MAX, WSM_FW_LABEL)); ctxt_ret->fw_api_ver = hw_priv->wsm_caps.firmwareApiVer; ctxt_ret->mib_baseaddr = context_save.config[1]; } else { ctxt_ret->is_etf_fw_run = 0; memset(ctxt_ret->fw_label, 0, HI_SW_LABEL_MAX); } ctxt_ret->len = sizeof(*ctxt_ret); ctxt_ret->id |= ETF_CNF_BASE; ctxt_ret->result = 0; xradio_adapter_send(ctxt_ret, sizeof(*ctxt_ret)); } break; case ETF_DOWNLOAD_ID: etf_printk(XRADIO_DBG_NIY, "DOWNLOAD_FW, size=%d\n", hdr_rev->len); download = (struct drv_download *)(hdr_rev + 1); datalen = hdr_rev->len - sizeof(*download) - sizeof(*hdr_rev); if (datalen <= 0) { /*it means old version APK, TODO:remove it in future */ if (hdr_rev->len <= sizeof(*hdr_rev)) { etf_driver_resp(BOOT_COMPLETE, BOOT_SUCCESS); ret = xradio_etf_connect(); if (ret) etf_driver_resp(BOOT_STATE_NULL, ret); } break; } if (download->flags & DOWNLOAD_F_PATH_ONLY) { etf_printk(XRADIO_DBG_WARN, "fw path=%s\n", (char *)(download+1)); /*TODO: set etf_priv.fw_path*/ } else if (download->flags & DOWNLOAD_F_START) { etf_printk(XRADIO_DBG_NIY, "fw first block\n"); /*TODO: open a tmp file to save firmware */ } /*TODO: put data into a tmp file */ /*TODO: put data into a tmp file */ if ((download->flags & DOWNLOAD_F_END)) { etf_printk(XRADIO_DBG_NIY, "fw last block\n"); etf_driver_resp(BOOT_COMPLETE, BOOT_SUCCESS); /*TODO: save data tmp file and update etf_priv.fw_path*/ ret = xradio_etf_connect(); if (ret) etf_driver_resp(BOOT_STATE_NULL, ret); } break; case ETF_DOWNLOAD_SDD_ID: etf_printk(XRADIO_DBG_NIY, "DOWNLOAD_SDD, size=%d\n", hdr_rev->len); download = (struct drv_download *)(hdr_rev + 1); datalen = hdr_rev->len - sizeof(*download) - sizeof(*hdr_rev); if (datalen <= 0) { /*it means old version APK, TODO:remove it in future */ if (hdr_rev->len <= sizeof(*hdr_rev)) { etf_driver_resp(BOOT_SDD_COMPLETE, BOOT_SUCCESS); } break; } if (download->flags & DOWNLOAD_F_PATH_ONLY) { etf_printk(XRADIO_DBG_WARN, "sdd path=%s\n", (char *)(download+1)); /*TODO: set etf_priv.fw_path*/ } else if ((download->flags & DOWNLOAD_F_START)) { etf_printk(XRADIO_DBG_NIY, "sdd first block\n"); /*TODO: open a tmp file to save firmware */ } /*TODO: put data into a tmp file */ if ((download->flags & DOWNLOAD_F_END)) { etf_printk(XRADIO_DBG_NIY, "sdd last block\n"); etf_driver_resp(BOOT_SDD_COMPLETE, BOOT_SUCCESS); /*TODO: save data tmp file and update etf_priv.sdd_path*/ } break; case ETF_CONNECT_ID: etf_printk(XRADIO_DBG_ALWY, "ETF_CONNECT_ID!\n"); etf_driver_resp(BOOT_COMPLETE, BOOT_SUCCESS); ret = xradio_etf_connect(); if (ret) etf_driver_resp(BOOT_STATE_NULL, ret); break; case ETF_DISCONNECT_ID: etf_printk(XRADIO_DBG_ALWY, "ETF_DISCONNECT_ID!\n"); xradio_etf_disconnect(); etf_driver_resp(BOOT_STATE_NULL, BOOT_SUCCESS); break; case ETF_RECONNECT_ID: etf_printk(XRADIO_DBG_ALWY, "ETF_RECONNECT_ID!\n"); if (1/*TODO: is update files*/) { xradio_etf_disconnect(); etf_driver_resp(BOOT_COMPLETE, BOOT_SUCCESS); ret = xradio_etf_connect(); if (ret) etf_driver_resp(BOOT_STATE_NULL, ret); } else { ret = BOOT_ERR_BAD_OP; etf_driver_resp(BOOT_STATE_NULL, ret); etf_printk(XRADIO_DBG_ERROR, "No download files before!\n"); } break; case ETF_GET_SDD_POWER_DEFT: /* get default tx power */ { #ifdef USE_VFS_FIRMWARE const struct xr_file *sdd = NULL; #else const struct firmware *sdd = NULL; #endif etf_printk(XRADIO_DBG_NIY, "%s ETF_GET_SDD_POWER_DEFT!\n", __func__); #ifdef USE_VFS_FIRMWARE sdd = xr_request_file(etf_get_sddpath()); #else ret = request_firmware(&sdd, etf_get_sddpath(), NULL); #endif /* get sdd data */ if (likely(sdd) && likely(sdd->data)) { int i; u16 *outpower = (u16 *)(hdr_rev + 1); void *ie_data = NULL; int ie_len = etf_get_sdd_param((u8 *)sdd->data, sdd->size, SDD_MAX_OUTPUT_POWER_2G4_ELT_ID, &ie_data); if (ie_len > 0 && ie_data && ie_len < (ADAPTER_RX_BUF_LEN - sizeof(*hdr_rev))) { memcpy(outpower, ie_data, ie_len); for (i = 0; i < (ie_len>>1); i++) outpower[i] -= 48; /*default pwr = max power - 48*/ } hdr_rev->len = sizeof(*hdr_rev) + ie_len; xradio_adapter_send(hdr_rev, hdr_rev->len); #ifdef USE_VFS_FIRMWARE xr_fileclose(sdd); #else release_firmware(sdd); #endif } else { etf_printk(XRADIO_DBG_ERROR, "%s: can't load sdd file %s.\n", __func__, etf_get_sddpath()); ret = -ENOENT; hdr_rev->len = sizeof(*hdr_rev); xradio_adapter_send(hdr_rev, sizeof(*hdr_rev)); } } break; case ETF_GET_SDD_PARAM_ID: { struct xradio_common *hw_priv = etf_priv.core_priv; struct get_sdd_param_req *sddreq = (struct get_sdd_param_req *)hdr_rev; struct get_sdd_result *sddret = (struct get_sdd_result *)sddreq; void *ie_data = NULL; int ie_len = 0; #ifdef USE_VFS_FIRMWARE const struct xr_file *sdd = NULL; #else const struct firmware *sdd = NULL; #endif etf_printk(XRADIO_DBG_NIY, "%s ETF_GET_SDD_PARAM_ID!\n", __func__); if (hw_priv && hw_priv->sdd) { etf_printk(XRADIO_DBG_NIY, "%s use xradio_common sdd!\n", __func__); sdd = hw_priv->sdd; } else { #ifdef USE_VFS_FIRMWARE sdd = xr_request_file(etf_get_sddpath()); #else ret = request_firmware(&sdd, etf_get_sddpath(), NULL); #endif } hdr_rev->id = hdr_rev->id + ETF_CNF_BASE; if (likely(sdd) && likely(sdd->data)) { if (sddreq->flags & FLAG_GET_SDD_ALL) { sddret->result = 0; sddret->length = sdd->size; xradio_adapter_send_pkg(sddret, sizeof(*sddret), (u8 *)sdd->data, sddret->length); } else { ie_len = etf_get_sdd_param((u8 *)sdd->data, sdd->size, sddreq->ies, &ie_data); if (ie_data && ie_len > 0) { sddret->result = 0; sddret->length = ie_len; hdr_rev->len = sizeof(*sddret) + sddret->length; xradio_adapter_send_pkg(sddret, sizeof(*sddret), ie_data, sddret->length); } else { sddret->result = -ENOMSG; sddret->length = 0; hdr_rev->len = sizeof(*sddret) + sddret->length; xradio_adapter_send(sddret, sizeof(*sddret)); } } if (hw_priv && hw_priv->sdd) { sdd = NULL; } else { #ifdef USE_VFS_FIRMWARE xr_fileclose(sdd); #else release_firmware(sdd); #endif } } else { etf_printk(XRADIO_DBG_ERROR, "%s: can't load sdd file %s.\n", __func__, etf_get_sddpath()); sddret->result = -ENOENT; sddret->length = 0; hdr_rev->len = sizeof(*sddret) + sddret->length; xradio_adapter_send(sddret, sizeof(*sddret)); } } break; default: etf_printk(XRADIO_DBG_NIY, "%s: passed cmd=0x%04x, len=%d", __func__, MSG_ID(hdr_rev->id), hdr_rev->len); if (etf_priv.etf_state != ETF_STAT_CONNECTED) { etf_printk(XRADIO_DBG_WARN, "%s etf Not connect.\n", __func__); ret = ETF_ERR_NOT_CONNECT; etf_driver_resp(BOOT_STATE_NULL, ret); goto out; } if (SEQ_MASK(etf_priv.seq_send) != MSG_SEQ(hdr_rev->id)) { etf_printk(XRADIO_DBG_NIY, "%s:seq err, drv_seq=%d, seq=%d\n", __func__, etf_priv.seq_send, MSG_SEQ(hdr_rev->id)); etf_priv.seq_send = MSG_SEQ(hdr_rev->id); } etf_priv.seq_send++; if (ETF_HWT_REQ == MSG_ID(hdr_rev->id)) { /*TODO: HW test*/ #if DGB_XRADIO_HWT HWT_Tx_handle((HWT_PARAMETERS *)data); #else etf_printk(XRADIO_DBG_ERROR, "%s HW test unsupport\n", __func__); #endif } else if (etf_priv.core_priv) { if (ETF_DOWNLOAD_SDD == MSG_ID(hdr_rev->id) && hdr_rev->len <= sizeof(struct etf_sdd_req)) { struct xradio_common *hw_priv = etf_priv.core_priv; struct etf_sdd_req *sdd_req = (struct etf_sdd_req *)hdr_rev; u32 sdd_cmd = sdd_req->sdd_cmd; etf_printk(XRADIO_DBG_WARN, "%s add sdd data, cmd=0x%08x!\n", __func__, sdd_cmd); if (hw_priv->sdd) { u8 *sdd_data = (u8 *)(hdr_rev + 1); int sdd_len = hw_priv->sdd->size; if (sdd_len + sizeof(*hdr_rev) + 4 < ADAPTER_RX_BUF_LEN) { memcpy(sdd_data, hw_priv->sdd->data, sdd_len); memcpy(sdd_data + sdd_len, &sdd_cmd, 4); hdr_rev->len += sdd_len; } else { etf_printk(XRADIO_DBG_ERROR, "ADAPTER_RX_BUF_LEN=%d, sdd_len=%d\n", ADAPTER_RX_BUF_LEN, sdd_len); } } else etf_printk(XRADIO_DBG_ERROR, "%s core_priv sdd is NULL\n", __func__); } /* send to device*/ ret = wsm_etf_cmd(etf_priv.core_priv, (struct wsm_hdr *)data); if (ret < 0) { etf_driver_resp(BOOT_STATE_NULL, ret); etf_printk(XRADIO_DBG_ERROR, "%s wsm_etf_cmd failed(%d)\n", __func__, ret); } } else { etf_printk(XRADIO_DBG_ERROR, "%s core_priv is NULL\n", __func__); } break; } up(&etf_priv.etf_lock); etf_printk(XRADIO_DBG_NIY, "%s success\n", __func__); return 0; out: up(&etf_priv.etf_lock); if (ret) etf_printk(XRADIO_DBG_ERROR, "%s proc failed(%d)\n", __func__, ret); return ret; } int xradio_etf_from_device(struct sk_buff **skb) { int ret = 0; struct wsm_hdr *wsm = (struct wsm_hdr *)((*skb)->data); etf_printk(XRADIO_DBG_NIY, "%s MsgId=0x%04x, len=%d\n", __func__, wsm->id, wsm->len); ret = xradio_adapter_send((void *)wsm, wsm->len); if (ret < 0) { etf_printk(XRADIO_DBG_ERROR, "%s xradio_adapter_send failed(%d).\n", __func__, ret); } else { etf_priv.seq_recv++; } return 0; } void xradio_etf_save_context(void *buf, int len) { etf_printk(XRADIO_DBG_TRC, "%s\n", __func__); if (len == sizeof(context_save)) { etf_printk(XRADIO_DBG_NIY, "%s, len=%d\n", __func__, len); memcpy(&context_save, buf, sizeof(context_save)); } else if (len > sizeof(context_save)) { etf_printk(XRADIO_DBG_WARN, "%s, len=%d\n", __func__, len); memcpy(&context_save, buf, sizeof(context_save)); } else { etf_printk(XRADIO_DBG_ERROR, "%s,len too short=%d\n", __func__, len); } } void xradio_etf_to_wlan(u32 change) { down(&etf_priv.etf_lock); etf_priv.is_wlan = change; if (change && etf_is_connect()) { etf_printk(XRADIO_DBG_NIY, "%s change to wlan.\n", __func__); xradio_etf_disconnect(); } up(&etf_priv.etf_lock); } EXPORT_SYMBOL_GPL(xradio_etf_to_wlan); int xradio_etf_suspend(void) { etf_printk(XRADIO_DBG_NIY, "%s\n", __func__); if (down_trylock(&etf_priv.etf_lock)) { etf_printk(XRADIO_DBG_WARN, "%s etf_lock is locked\n", __func__); return -EBUSY; /*etf is locked.*/ } if (etf_is_connect()) { etf_printk(XRADIO_DBG_WARN, "%s etf is running, don't suspend!\n", __func__); up(&etf_priv.etf_lock); return -EBUSY; /*etf is running*/ } up(&etf_priv.etf_lock); return 0; } int xradio_etf_resume(void) { etf_printk(XRADIO_DBG_NIY, "%s\n", __func__); return 0; } int xradio_etf_init(void) { int ret = 0; etf_printk(XRADIO_DBG_TRC, "%s\n", __func__); sema_init(&etf_priv.etf_lock, 1); etf_priv.etf_state = ETF_STAT_NULL; etf_priv.fw_path = XR829_ETF_FIRMWARE; etf_priv.sdd_path = XR829_SDD_FILE; etf_priv.adapter = xradio_adapter_init(&xradio_etf_proc); return ret; } void xradio_etf_deinit(void) { etf_printk(XRADIO_DBG_TRC, "%s\n", __func__); /*ensure in disconnect state when etf deinit*/ xradio_etf_to_wlan(1); xradio_adapter_deinit(); memset(&etf_priv, 0, sizeof(etf_priv)); } #endif /*XRADIO_ETF_C*/