blob: 283ae2743d1834204af847390318647b71c3fd93 [file] [log] [blame]
// Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <fcntl.h>
#include <errno.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/ioctl.h>
#include <net/if_arp.h>
#include <unistd.h>
#include <net/if.h>
#include <assert.h>
#include "error.h"
#include "sdk.h"
#include "device.h"
#include "io.h"
#include "hci.h"
#include "wimax.h"
#include "nds.h"
#include "log.h"
#if defined(TRANSPORT_TEST)
static int dl_transport_handler(int dev_idx, char *buf, int len);
static int start_transport_test(int dev_idx, char *buf, int len);
#endif
int hci_get_tlv(u8 *buf, u8 *T, u16 *L, u8 **V)
{
int next_pos;
*T = buf[0];
if (buf[1] == 0x82) {
*L = U82U16(&buf[2]);
next_pos = 1/*type*/+3/*len*/;
}
else {
*L = buf[1];
next_pos = 1/*type*/+1/*len*/;
}
*V = &buf[next_pos];
next_pos += *L/*length of val*/;
return next_pos;
}
int hci_set_tlv(u8 *buf, u8 T, u16 L, u8 *V)
{
int pos = 0;
buf[pos++] = T;
if (L < 0x80) {
buf[pos++] = L;
}
else {
buf[pos++] = 0x82;
U162U8(&buf[pos], L);
pos+=2;
}
memcpy(&buf[pos], V, L);
pos += L;
return pos;
}
int hci_send(int dev_idx, u16 cmd, u8 *data, int len)
{
u16 buf[HCI_MAX_PACKET / sizeof(u16)];
u8 *data_buf = (u8 *)&buf[0] + HCI_HEADER_SIZE;
int ret;
xfunc_in("cmd=0x%04x, len=%d", cmd, len);
buf[0] = H2B(cmd);
buf[1] = H2B(len);
memcpy(data_buf, data, len);
ret = io_send(dev_idx, buf, len+HCI_HEADER_SIZE);
if (ret == len+HCI_HEADER_SIZE)
ret = len;
else if (ret >= 0) {
xprintf(SDK_ERR, "Wrong sent size=%d\n", ret);
ret = -1;
}
xfunc_out("ret=%d", ret);
return ret;
}
#define DESTROYED_HCI -2
int hci_send_wait(int dev_idx, u16 s_cmd, u8 *data, int plen,
u16 w_cmd, void *buf, int buf_len, int chk_len, u32 flag, int timeout)
{
#define timeval2timespec(tv,ts) \
((ts)->tv_sec = (tv)->tv_sec, (ts)->tv_nsec = (tv)->tv_usec * 1000)
device_t *dev;
hci_req_t hcir;
struct list_head *head;
struct timeval tv;
struct timespec ts;
int ret;
xfunc_in("cmd=0x%04x", s_cmd);
if (!(dev = dm_get_dev(dev_idx)))
return -1;
pthread_mutex_lock(&dev->hci_wait_signal);
if ((ret = hci_send(dev_idx, s_cmd, data, plen)) < 0)
goto out;
INIT_LIST_HEAD(&hcir.list);
hcir.cmd_evt = w_cmd;
hcir.buf = buf;
hcir.len = buf_len;
hcir.chk_len = chk_len;
hcir.flag = flag;
pthread_cond_init(&hcir.cond, NULL);
if (timeout) {
gettimeofday(&tv, NULL);
timeval2timespec(&tv, &ts);
ts.tv_sec += timeout;
}
head = &dev->hci_wait_list;
list_add_tail(&hcir.list, head);
xprintf(SDK_DBG, "Wait cmd(0x%04x)\n", hcir.cmd_evt);
ret = pthread_cond_timedwait(&hcir.cond, &dev->hci_wait_signal, &ts);
if (ret == ETIMEDOUT) {
list_del(&hcir.list);
if (w_cmd != WIMAX_ARM_CAPABILITY_RESULT)
xprintf(SDK_ERR, "hci_send_wait(0x%04X TIMEOUT(%d)\n", w_cmd, timeout);
ret = - ETIMEDOUT;
}
else {
ret = hcir.len;
if (ret == DESTROYED_HCI)
xprintf(SDK_NOTICE, "HCI(%04x) has been destroyed\n", w_cmd);
}
out:
pthread_mutex_unlock(&dev->hci_wait_signal);
xfunc_out("ret=%d", ret);
dm_put_dev(dev_idx);
return ret;
}
int hci_wait(int dev_idx, u16 cmd_evt,
void *buf, int len, int chk_len, u32 flag, int timeout)
{
#define timeval2timespec(tv,ts) \
((ts)->tv_sec = (tv)->tv_sec, (ts)->tv_nsec = (tv)->tv_usec * 1000)
device_t *dev;
hci_req_t hcir;
struct list_head *head;
struct timeval tv;
struct timespec ts;
int ret;
xfunc_in("cmd=0x%04x", cmd_evt);
if (!(dev = dm_get_dev(dev_idx)))
return -1;
INIT_LIST_HEAD(&hcir.list);
hcir.cmd_evt = cmd_evt;
hcir.buf = buf;
hcir.len = len;
hcir.chk_len = chk_len;
hcir.flag = flag;
pthread_cond_init(&hcir.cond, NULL);
if (timeout) {
gettimeofday(&tv, NULL);
timeval2timespec(&tv, &ts);
ts.tv_sec += timeout;
}
pthread_mutex_lock(&dev->hci_wait_signal);
head = &dev->hci_wait_list;
list_add_tail(&hcir.list, head);
ret = pthread_cond_timedwait(&hcir.cond, &dev->hci_wait_signal, &ts);
if (ret == ETIMEDOUT) {
list_del(&hcir.list);
xprintf(SDK_ERR, "hci_wait(0x%04X TIMEOUT(%d)\n", cmd_evt, timeout);
ret = - 1;
}
else {
ret = hcir.len;
if (ret == DESTROYED_HCI)
xprintf(SDK_NOTICE, "HCI(%04x) has been destroyed\n", cmd_evt);
}
pthread_mutex_unlock(&dev->hci_wait_signal);
xfunc_out("ret=%d", ret);
dm_put_dev(dev_idx);
return ret;
}
int hci_destroy_resp(int dev_idx)
{
device_t *dev;
struct list_head *head;
hci_req_t *hcir, *tmp;
int ret = 0;
xfunc_in("dev=%d", dev_idx);
if (!(dev = dm_get_dev(dev_idx)))
return -1;
head = &dev->hci_wait_list;
pthread_mutex_lock(&dev->hci_wait_signal);
list_for_each_entry_safe(hcir, tmp, head, list) {
hcir->len = DESTROYED_HCI;
pthread_cond_signal(&hcir->cond);
ret++;
}
pthread_mutex_unlock(&dev->hci_wait_signal);
xfunc_out("ret=%d", ret);
dm_put_dev(dev_idx);
return ret;
}
static int hci_unsolicited_getinfo_result(int dev_idx, u8 *buf, int len)
{
device_t *dev;
u8 T, *V;
u16 L;
int pos = 0, ret = 0;
xfunc_in();
if (!(dev = dm_get_dev(dev_idx)))
return -1;
while (pos < len) {
pos += hci_get_tlv(&buf[pos], &T, &L, &V);
switch (T) {
case TLV_T(T_REALM):
memcpy(dev->wimax->dev_info.eapp.visited_realm, V, L);
dev->wimax->dev_info.eapp.visited_realm[L] = 0;
xprintf(SDK_NOTICE, "Network Realm=%s\n",
dev->wimax->dev_info.eapp.visited_realm);
break;
case TLV_T(T_BSID):
xprintf(SDK_INFO, "BSID=%06X:%06X\n", U82U24(&V[0]), U82U24(&V[3]));
break;
case TLV_T(T_RSSI):
xprintf(SDK_INFO, "T_RSSI(%d)\n", wm_convert_rssi(*V));
break;
default:
xprintf(SDK_DBG, "Unknown Type=0x%02x\n", T);
ret = -1;
goto out;
}
}
out:
dm_put_dev(dev_idx);
xfunc_out("ret=%d", ret);
return ret;
}
static bool hci_receive_resp(int dev_idx, u16 cmd_evt, u8 *buf, int len)
{
device_t *dev;
struct list_head *head;
hci_req_t *hcir, *tmp;
int handled_cnt = 0;
xfunc_in("cmd=0x%04x", cmd_evt);
if (!(dev = dm_get_dev(dev_idx)))
return -1;
head = &dev->hci_wait_list;
pthread_mutex_lock(&dev->hci_wait_signal);
list_for_each_entry_safe(hcir, tmp, head, list) {
if (cmd_evt == hcir->cmd_evt) {
if (hcir->chk_len) {
if (memcmp(hcir->buf, buf, hcir->chk_len)) {
xprintf_hex(SDK_DBG, "data to check is not matched: 1",
buf, hcir->chk_len);
xprintf_hex(SDK_DBG, "data to check is not matched: 2",
hcir->buf, hcir->chk_len);
continue;
}
}
if (hcir->buf && hcir->len) {
assert(hcir->len >= len);
memcpy(hcir->buf, buf, len);
hcir->len = len;
}
else
hcir->len = 0;
list_del(&hcir->list);
xprintf(SDK_DBG, "Send signal(0x%04x(%d): %02x, %02x)\n",
hcir->cmd_evt, hcir->len, buf[0], buf[1]);
pthread_cond_signal(&hcir->cond);
if (hcir->flag & HCI_INDICATION)
handled_cnt = -1;
if (handled_cnt >=0)
handled_cnt++;
}
}
pthread_mutex_unlock(&dev->hci_wait_signal);
xfunc_out("handled_cnt=%d", handled_cnt);
dm_put_dev(dev_idx);
return handled_cnt <= 0 ? FALSE : TRUE;
}
static int hci_receive_ind(int dev_idx, void *buf, int len)
{
device_t *dev;
struct hci *hci = (struct hci *) buf;
u16 cmd_evt, cmd_len, category;
u8 *data_buf = (u8 *) hci->data;
bool should_bypass = FALSE;
int ret = -1;
if (!(dev = dm_get_dev(dev_idx)))
return -1;
cmd_evt = B2H(hci->cmd_evt);
cmd_len = B2H(hci->length);
category = (cmd_evt >> 8) & 0xf;
xfunc_in("0x%04x, %d, %02x, %02x, %02x, %02x", cmd_evt, cmd_len,
hci->data[0], hci->data[1], hci->data[2], hci->data[3]);
if (len > HCI_MAX_PACKET || (len-HCI_HEADER_SIZE) < cmd_len) {
xprintf(SDK_ERR, "[%d] Invalid packet length(%d) (%d,%d)\n",
dev_idx, len, (len-HCI_HEADER_SIZE), cmd_len);
goto out;
}
if (hci_receive_resp(dev_idx, cmd_evt, hci->data, cmd_len))
goto out;
if (category == 3)
should_bypass = TRUE;
if (!dev->wimax) {
xprintf(SDK_NOTICE, "Device has been closed!\n");
goto out;
}
/*The following is a indication.*/
switch (cmd_evt) {
case WIMAX_FSM_UPDATE:
ret = sdk_ind_status_update(dev_idx, data_buf, cmd_len);
break;
case WIMAX_IF_UPDOWN:
ret = sdk_ind_if_updown(dev_idx, data_buf, cmd_len);
break;
case WIMAX_RADIO_STATE_IND:
ret = sdk_ind_rf_state(dev_idx, data_buf, cmd_len);
break;
case WIMAX_SCAN_RESULT:
ret = nds_update_scan_result(dev_idx, data_buf, cmd_len);
break;
case WIMAX_SCAN_COMPLETE:
ret = sdk_ind_event(dev_idx, NM_ScanComplete);
break;
case WIMAX_CONNECT_START:
ret = sdk_ind_event(dev_idx, NC_ConnectStart);
break;
case WIMAX_CONNECT_COMPLETE:
ret = nds_connect_complete(dev_idx, data_buf, cmd_len);
break;
case WIMAX_ASSOC_START:
ret = nds_associate_start(dev_idx, data_buf, cmd_len);
break;
case WIMAX_ASSOC_COMPLETE:
ret = nds_associate_complete(dev_idx, data_buf, cmd_len);
break;
case WIMAX_DISCONN_IND:
ret = nds_disconnect_ind(dev_idx, data_buf, cmd_len);
ret |= sdk_ind_recv_hci_pkt(dev_idx, buf, len);
break;
case WIMAX_ENTRY_IND:
ret = nds_connection_stage(dev_idx, data_buf, cmd_len);
break;
case WIMAX_HO_START:
ret = nds_ho_start(dev_idx, data_buf, cmd_len);
break;
case WIMAX_HO_COMPLETE:
ret = nds_ho_complete(dev_idx, data_buf, cmd_len);
break;
case WIMAX_IP_RENEW_IND:
should_bypass = TRUE;
break;
case WIMAX_RX_EAP:
xprintf(SDK_ERR, "WIMAX_RX_EAP is NOT supported.\n", cmd_evt);
break;
case WIMAX_NOTIFICATION:
ret = wm_notification(dev_idx, (char *) data_buf, cmd_len);
should_bypass = FALSE;
break;
#if defined(TRANSPORT_TEST)
case WIMAX_START_TRANSPORT_TEST:
start_transport_test(dev_idx, (char *) data_buf, cmd_len);
should_bypass = FALSE;
break;
case WIMAX_DL_TRANSPORT_TEST:
dl_transport_handler(dev_idx, (char *) data_buf, cmd_len);
should_bypass = FALSE;
break;
#endif
case WIMAX_ARM_CAPABILITY_RESULT:
wm_report_capability(dev_idx, data_buf, cmd_len);
should_bypass = FALSE;
break;
case WIMAX_MODE_CHANGE:
ret = sdk_ind_power_mode_change(dev_idx, data_buf, cmd_len);
break;
case WIMAX_SF_IND:
ret = wm_ind_sf_mode(dev_idx, data_buf, cmd_len);
break;
case WIMAX_GET_INFO_RESULT:
ret = hci_unsolicited_getinfo_result(dev_idx, data_buf, cmd_len);
if (!ret)
break;
case WIMAX_FULL_REENTRY:
ret = wm_report_full_reentry(dev_idx, data_buf, cmd_len);
break;
#if defined(CONFIG_ENABLE_SERVICE_FLOW)
case WIMAX_DSX_COMPLETE:
// Using original data
ret = sdk_sf_recv_dsx_complete(dev_idx, buf, len);
break;
#endif // CONFIG_ENABLE_SERVICE_FLOW
default:
if (!should_bypass)
xprintf(SDK_INFO, "Unhandled HCI(0x%04x)\n", cmd_evt);
break;
}
if (should_bypass) {
xprintf(SDK_DBG, "Bypass HCI(0x%04x)\n", cmd_evt);
ret = sdk_ind_recv_hci_pkt(dev_idx, buf, len);
}
out:
xfunc_out("ret=%d", ret);
dm_put_dev(dev_idx);
return ret;
}
bool hci_parse_lv(u8 *buf, u16 event, u8 T, u8 *L, u8 **V)
{
u8 type;
u16 cmd_evt;
cmd_evt = B2H(*(u16 *)&buf[0]);
if (cmd_evt == event) {
type = buf[HCI_HEADER_SIZE];
if (T == type) {
*L = buf[HCI_HEADER_SIZE+1];
*V = &buf[HCI_HEADER_SIZE+2];
return TRUE;
}
else
xprintf(SDK_DBG, "receive another type(0x%02X!=0x%02X)\n", type, T);
}
else
xprintf(SDK_DBG, "receive another event(0x%04X!=0x%04X)\n", cmd_evt, event);
return FALSE;
}
int hci_req_getinfo(int dev_idx, u8 *buf, tlv_t *tlv, int cnt)
{
u8 param[HCI_MAX_TLV];
int pos = 0, i;
int chk_len;
u8 T;
int ret = -1;
xfunc_in("dev=%d, cnt=%d", dev_idx, cnt);
assert(HCI_MAX_TLV >= cnt);
for (i = 0; i < cnt; i++)
param[i] = tlv[i].T;
buf[0] = tlv[0].T;
chk_len = 1;
ret = hci_send_wait(dev_idx, WIMAX_GET_INFO, param, cnt,
WIMAX_GET_INFO_RESULT, buf, HCI_MAX_PACKET, chk_len, 0, IO_TIMEOUT_SEC);
if (ret > cnt/*min. size*/) {
for (i = 0; i < cnt; i++) {
pos += hci_get_tlv(&buf[pos], &T, &tlv[i].L, &tlv[i].V);
if (T != tlv[i].T) {
xprintf_hex(SDK_ERR, "GET_INFO", buf, ret);
xprintf(SDK_ERR, "hci_get_tlv mismatch 0x%02X!=0x%02X\n", T, tlv[i].T);
ret = -1;
goto out;
}
}
ret = 0;
}
else if (ret >= 0){
xprintf(SDK_ERR, "hci_send_wait mismatch: %d<=%d\n", ret, cnt);
ret = -1;
}
out:
xfunc_out("ret=%d", ret);
return ret;
}
int hci_req_getinfo32(int dev_idx, u8 type, u32 *val)
{
u8 buf[HCI_MAX_PACKET];
tlv_t tlv;
int ret;
xfunc_in();
tlv.T = type;
ret = hci_req_getinfo(dev_idx, buf, &tlv, 1);
if (!ret) {
if (tlv.L == sizeof(u32)) {
memcpy(val, tlv.V, sizeof(u32));
*val = DB2H(*val);
}
else {
xprintf(SDK_ERR, "length mismatch(%d!=%d)\n", tlv.L, sizeof(u32));
ret = -1;
}
}
xfunc_out("ret=%d, *val=0x%08X", ret, *val);
return ret;
}
int hci_req_setinfo32(int dev_idx, u8 type, u32 val)
{
u8 buf[HCI_MAX_PARAM];
u8 *pos = buf;
u8 T, *V;
u16 L;
int len, ret;
xfunc_in("val=%d", val);
val = DH2B(val);
T = type;
L = (u16) sizeof(u32);
V = (u8 *) &val;
pos += hci_set_tlv(pos, T, L, V);
len = pos - buf;
ret = hci_send(dev_idx, WIMAX_SET_INFO, buf, len);
if (len == ret)
ret = 0;
else {
xprintf(SDK_STD_ERR, "[%d] hci_send(%d!=%d)\n", dev_idx, len, ret);
ret = sdk_set_errno(ERR_STD);
}
xfunc_out("ret=%d", ret);
return ret;
}
int hci_send_msg(int dev_idx, hci_msg_t *msg)
{
msg_thr_t *hci_recvr = &sdk_mng.hci_recvr;
xfunc_in("%02x%02x", (u8)msg->buf[0], (u8)msg->buf[1]);
msg_send(&hci_recvr->msg_cb, dev_idx, msg);
xfunc_out();
return 0;
}
static void *hci_receiver_thread(void *data)
{
msg_thr_t *hci_recvr = (msg_thr_t *) data;
hci_msg_t *msg;
int dev_idx;
char *buf;
int len;
xfunc_in();
while (1) {
xprintf(SDK_DBG, "[%d] In hci_receiver\n", dev_idx);
if (msg_recv(&hci_recvr->msg_cb, &dev_idx, (void **)&msg) < 0) {
xprintf(SDK_ERR, "hci msg_recv error\n");
break;
}
if (msg == THREAD_EXIT_MSG) {
xprintf(SDK_INFO, "%s thread exit...\n", __FUNCTION__);
break;
}
buf = msg->buf;
len = msg->len;
hci_receive_ind(dev_idx, buf, len);
sdk_free(msg);
}
msg_deinit(&hci_recvr->msg_cb);
xfunc_out();
return NULL;
}
int hci_receiver_create(msg_thr_t *hci_recvr)
{
xfunc_in();
msg_init(&hci_recvr->msg_cb);
pthread_create(&hci_recvr->thread, NULL, hci_receiver_thread, (void *)hci_recvr);
xfunc_out();
return 0;
}
int hci_receiver_delete(msg_thr_t *hci_recvr)
{
pthread_t thread;
xfunc_in("thread=0x%08X", (int) hci_recvr->thread);
if ((thread = hci_recvr->thread)) {
hci_recvr->thread = (pthread_t) NULL;
msg_send(&hci_recvr->msg_cb, 0, THREAD_EXIT_MSG);
pthread_join(thread, NULL);
}
xfunc_out();
return 0;
}
#if defined(TRANSPORT_TEST)
#define TYPE_NONE 0
#define TYPE_DL 1
#define TYPE_UL 2
#define TYPE_LOOPBACK 3
#define TYPE_BI_DIR 4
#define TYPE_MAX 4
#define PATTERN_ZERO_FILLED 0
#define PATTERN_INCREMENTAL 1
#define PATTERN_RANDOM 2
#define PATTERN_MAX 2
#define PACKETSIZE_MAX 1400
typedef struct trans_test_s {
unsigned char type;
unsigned char pattern;
unsigned short outer_loop;
unsigned short inner_loop;
unsigned short packet_size;
} __attribute__((packed)) trans_test_t;
typedef struct trans_mng_s {
trans_test_t tt;
int mismatches;
} trans_mng_t;
typedef struct trans_packet_s {
unsigned short seq_nr;
unsigned char packet[0];
} __attribute__((packed)) trans_packet_t;
typedef struct trans_phase_s {
int outer_loop;
int inner_loop;
} trans_phase_t;
static trans_mng_t trans_mng;
static trans_phase_t ul_phase;
static trans_phase_t dl_phase;
static char send_hci[HCI_MAX_PACKET] __attribute__((aligned(1024)));
static unsigned char packet_data[HCI_MAX_PACKET];
static void fill_incremental_buf(void *buf, int len, unsigned char start)
{
unsigned char *p = buf;
int i;
for (i = 0; i < len; i++)
*p++ = start++;
}
static void fill_random_buf(void *buf, int len)
{
unsigned short *p = (unsigned short *) buf;
unsigned char *bp = (unsigned char *) buf;
int slen = len / 2;
int i;
for (i = 0; i < slen; i++)
*p++ = rand();
if (len & 1)
bp[len-1] = rand();
}
static void init_send_hci(trans_mng_t *ptm)
{
struct hci *hci;
trans_packet_t *data;
int len = ptm->tt.packet_size;
unsigned short length = sizeof(data->seq_nr) + len;
if (ptm->tt.type == TYPE_UL || ptm->tt.type == TYPE_LOOPBACK) {
hci = (struct hci *) send_hci;
data = (trans_packet_t *) hci->data;
hci->cmd_evt = H2B(WIMAX_UL_TRANSPORT_TEST);
hci->length = H2B(length);
if (ptm->tt.pattern == PATTERN_ZERO_FILLED)
memset(data->packet, 0, len);
else if (ptm->tt.pattern == PATTERN_INCREMENTAL)
fill_incremental_buf(packet_data, sizeof(packet_data), 0);
else if (ptm->tt.pattern == PATTERN_RANDOM)
fill_random_buf(packet_data, sizeof(packet_data));
}
}
static struct hci *get_hci_buf(trans_mng_t *ptm, unsigned char seq_nr)
{
struct hci *hci = (struct hci *) send_hci;
trans_packet_t *data = (trans_packet_t *) hci->data;
if (ptm->tt.pattern == PATTERN_INCREMENTAL || ptm->tt.pattern == PATTERN_RANDOM)
memcpy(data->packet, &packet_data[seq_nr], ptm->tt.packet_size);
return hci;
}
static unsigned int elapsed_time_us(struct timeval *start)
{
struct timeval tv, now;
unsigned int us;
gettimeofday(&now, NULL);
timersub(&now, start, &tv);
us = tv.tv_sec * 1000000 + tv.tv_usec;
return us;
}
static unsigned int get_throughput(trans_mng_t *ptm, unsigned int us)
{
int loop = ptm->tt.inner_loop;
int packet_size = ptm->tt.packet_size;
unsigned int sent;
unsigned int bps;
double sec;
if (ptm->tt.type == TYPE_DL)
loop--;
sent = (HCI_HEADER_SIZE + sizeof(trans_packet_t) + packet_size)
* 8/*bits*/ *loop;
sec = (double)us / 1000000;
bps = sent / sec;
return bps;
}
static void print_throughput(const char *title, trans_mng_t *ptm, double bps)
{
char str[1024], *p = str;
#define Kbits (1024)
#define Mbits (1024*Kbits)
if (bps > Mbits)
p += sprintf(p, "%s: %.2f Mbits", title, bps/Mbits);
else if (bps > Kbits)
p += sprintf(p, "%s: %.2f Kbits", title, bps/Kbits);
else
p += sprintf(p, "%s: %.2f bits", title, bps);
if (ptm->mismatches)
p += sprintf(p, ", mismatches=%d", ptm->mismatches);
xprintf(SDK_NOTICE, "%s\n", str);
}
static void report_throughput(trans_mng_t *ptm, char *name,
struct timeval *start, int index)
{
char title[128];
unsigned int elapsed_us;
double bps;
elapsed_us = elapsed_time_us(start);
sprintf(title, "%d. %s(bps)", index, name);
bps = get_throughput(ptm, elapsed_us);
print_throughput(title, ptm, bps);
}
static void ul_transport_send(int dev_idx, trans_mng_t *ptm, int seq_nr)
{
struct hci *hci;
trans_packet_t *data;
int len;
hci = get_hci_buf(ptm, seq_nr);
data = (trans_packet_t *) hci->data;
data->seq_nr = H2B(seq_nr);
len = B2H(hci->length);
io_send(dev_idx, hci, len+HCI_HEADER_SIZE);
}
static void ul_transport_test(int dev_idx, trans_mng_t *ptm)
{
struct timeval start;
int i, j;
assert(ptm->tt.type == TYPE_UL);
for (i = 0; i < ptm->tt.outer_loop; i++) {
gettimeofday(&start, NULL);
for (j = 0; j < ptm->tt.inner_loop; j++)
ul_transport_send(dev_idx, ptm, j);
report_throughput(ptm, "UL", &start, i);
sleep(1);
}
}
static void loopback_test(int dev_idx, trans_mng_t *ptm)
{
assert(ptm->tt.type == TYPE_LOOPBACK);
ul_transport_send(dev_idx, ptm, ul_phase.inner_loop);
if (++ul_phase.inner_loop == ptm->tt.inner_loop) {
ul_phase.inner_loop = 0;
ul_phase.outer_loop++;
}
}
static int dl_transport_handler(int dev_idx, char *buf, int len)
{
trans_mng_t *ptm = &trans_mng;
trans_packet_t *data = (trans_packet_t *) buf;
static struct timeval start;
unsigned short seq_nr = B2H(data->seq_nr);
if (!dl_phase.inner_loop)
gettimeofday(&start, NULL);
if (ptm->tt.type == TYPE_LOOPBACK)
loopback_test(dev_idx, ptm);
if (seq_nr != dl_phase.inner_loop)
ptm->mismatches++;
if (++dl_phase.inner_loop == ptm->tt.inner_loop) {
if (ptm->tt.type == TYPE_LOOPBACK)
report_throughput(ptm, "Loopback", &start, dl_phase.outer_loop++);
else
report_throughput(ptm, "DL", &start, dl_phase.outer_loop++);
dl_phase.inner_loop = 0;
}
return 0;
}
static int chk_trans_test_param(trans_mng_t *ptm)
{
if (ptm->tt.type > TYPE_MAX)
return 0;
if (ptm->tt.pattern > PATTERN_MAX)
return 0;
if (!ptm->tt.inner_loop)
return 0;
if (ptm->tt.packet_size > PACKETSIZE_MAX)
return 0;
return 1;
}
static int start_transport_test(int dev_idx, char *buf, int len)
{
trans_test_t *data = (trans_test_t *) buf;
trans_mng_t *ptm = &trans_mng;
memset(ptm, 0, sizeof(trans_mng_t));
ptm->tt.type = data->type;
ptm->tt.pattern = data->pattern;
ptm->tt.outer_loop = B2H(data->outer_loop);
ptm->tt.inner_loop = B2H(data->inner_loop);
ptm->tt.packet_size = B2H(data->packet_size);
xprintf(SDK_NOTICE, "transt %d %d %d %d %d\n",
ptm->tt.type, ptm->tt.pattern, ptm->tt.outer_loop, ptm->tt.inner_loop, ptm->tt.packet_size);
if (!chk_trans_test_param(ptm)) {
xprintf(SDK_ERR, "transport test param is invalid\n");
return -1;
}
memset(&dl_phase, 0, sizeof(dl_phase));
memset(&ul_phase, 0, sizeof(ul_phase));
init_send_hci(ptm);
switch (ptm->tt.type) {
case TYPE_UL:
ul_transport_test(dev_idx, ptm);
break;
case TYPE_LOOPBACK:
loopback_test(dev_idx, ptm);
break;
}
return 0;
}
#endif