0
0
Fork 0
hgicf/utils/fwctrl.c

1159 lines
35 KiB
C

#ifdef __RTOS__
#include <linux/module.h>
#include <linux/types.h>
#include <asm/unaligned.h>
#include <linux/skbuff.h>
#include <linux/completion.h>
#else
#include <linux/version.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/net.h>
#include <linux/slab.h>
#include <linux/firmware.h>
#include <linux/etherdevice.h>
#endif
#include "../hgic_def.h"
#include "fwdl.h"
#include "fwctrl.h"
#include "utils.h"
#define ARG_DUP(arg,len) if(ctrl->param.arg != (void *)arg){ \
if(ctrl->param.arg) kfree(ctrl->param.arg); \
ctrl->param.arg = hgic_strdup(arg,len);\
}
#define ARG_FREE(arg) do{\
if(ctrl->param.arg) kfree(ctrl->param.arg);\
ctrl->param.arg = NULL;\
} while (0)
#define STR_LEN(s) ((s)?strlen(s):0)
static void *hgic_strdup(void *arg, u32 len)
{
int s = 0;
u8 *ptr = NULL;
if (arg) {
s = (len > 0) ? (len) : (strlen((char *)arg) + 1);
ptr = kzalloc(s, GFP_KERNEL);
if (ptr) {
if (len > 0) { memcpy(ptr, arg, len); }
else { strcpy(ptr, (char *)arg); }
}
}
return ptr;
}
u16 hgic_ctrl_cookie(struct hgic_fwctrl *ctrl)
{
unsigned long flags;
uint16_t cookie = 0;
spin_lock_irqsave(&ctrl->lock, flags);
cookie = ctrl->cookie++;
ctrl->cookie &= HGIC_TX_COOKIE_MASK;
spin_unlock_irqrestore(&ctrl->lock, flags);
return cookie;
}
static struct sk_buff *hgic_alloc_ctrl_skb(u32 size)
{
struct sk_buff *skb = dev_alloc_skb(sizeof(struct hgic_ctrl_hdr) + size + 4);
if (!skb) {
return skb;
}
memset(skb->data, 0, sizeof(struct hgic_ctrl_hdr) + size);
skb_reserve(skb, sizeof(struct hgic_ctrl_hdr));
skb_put(skb, size);
return skb;
}
static struct sk_buff *hgic_fwctrl_send_cmd(struct hgic_fwctrl *ctrl, u32 cmd_id, struct sk_buff *skb, bool sync)
{
struct hgic_ctrl_hdr *hdr = NULL;
struct hgic_cmd_response resp;
u16 id = cmd_id - HGIC_CMD_START;
hdr = (struct hgic_ctrl_hdr *)skb_push(skb, sizeof(struct hgic_ctrl_hdr));
hdr->hdr.magic = HGIC_HDR_TX_MAGIC;
hdr->hdr.length = skb->len;
hdr->hdr.ifidx = 1;
hdr->hdr.cookie = hgic_ctrl_cookie(ctrl);
if (id > 255) {
hdr->hdr.type = HGIC_HDR_TYPE_CMD2;
hdr->cmd2.cmd_id = id;
hdr->cmd2.status = 0;
} else {
hdr->hdr.type = HGIC_HDR_TYPE_CMD;
hdr->cmd.cmd_id = id;
hdr->cmd.status = 0;
}
memset(&resp, 0, sizeof(resp));
resp.cookie = hdr->hdr.cookie;
return hgic_fwctrl_send_data(ctrl, skb, &resp, HGIC_CMD_TIMEOUT);
}
static int hgic_fwctrl_set_byte(struct hgic_fwctrl *ctrl, u32 cmd_id, u8 val)
{
int ret = -1;
struct sk_buff *skb = NULL;
struct sk_buff *resp = NULL;
struct hgic_ctrl_hdr *hdr = NULL;
skb = hgic_alloc_ctrl_skb(1);
if (skb) {
skb->data[0] = val;
resp = hgic_fwctrl_send_cmd(ctrl, cmd_id, skb, true);
if (resp) {
hdr = (struct hgic_ctrl_hdr *)resp->data;
ret = hdr->cmd.status;
dev_kfree_skb_any(resp);
}
}
if (ret) {
hgic_dbg("cmd:%d, ret:%d (%s)\r\n", cmd_id, ret, resp ? "Responsed" : "No Response");
}
return ret;
}
static int hgic_fwctrl_set_int_val(struct hgic_fwctrl *ctrl, u32 cmd_id, u32 val)
{
int ret = -1;
struct sk_buff *skb = NULL;
struct sk_buff *resp = NULL;
struct hgic_ctrl_hdr *hdr = NULL;
skb = hgic_alloc_ctrl_skb(4);
if (skb) {
put_unaligned_le32(val, skb->data);
resp = hgic_fwctrl_send_cmd(ctrl, cmd_id, skb, true);
if (resp) {
hdr = (struct hgic_ctrl_hdr *)resp->data;
ret = hdr->cmd.status;
dev_kfree_skb_any(resp);
}
}
if (ret) {
hgic_dbg("cmd:%d, ret:%d (%s)\r\n", cmd_id, ret, resp ? "Responsed" : "No Response");
}
return ret;
}
static short hgic_fwctrl_get_short_val(struct hgic_fwctrl *ctrl, u32 cmd_id)
{
short ret = -1;
struct sk_buff *skb = NULL;
struct sk_buff *resp = NULL;
struct hgic_ctrl_hdr *hdr = NULL;
skb = hgic_alloc_ctrl_skb(0);
if (skb) {
resp = hgic_fwctrl_send_cmd(ctrl, cmd_id, skb, true);
if (resp) {
hdr = (struct hgic_ctrl_hdr *)resp->data;
ret = hdr->cmd.status;
dev_kfree_skb_any(resp);
}
}
return ret;
}
static int hgic_fwctrl_set_bytes(struct hgic_fwctrl *ctrl, u32 cmd_id, u8 *data, u32 len)
{
int ret = -1;
struct sk_buff *skb = NULL;
struct sk_buff *resp = NULL;
struct hgic_ctrl_hdr *hdr = NULL;
skb = hgic_alloc_ctrl_skb(len);
if (skb) {
if (data && len > 0) {
memcpy(skb->data, data, len);
skb->data[len] = 0;
}
resp = hgic_fwctrl_send_cmd(ctrl, cmd_id, skb, true);
if (resp) {
hdr = (struct hgic_ctrl_hdr *)resp->data;
ret = hdr->cmd.status;
dev_kfree_skb_any(resp);
}
if (ret) {
hgic_dbg("cmd:%d, ret:%d (%s)\r\n", cmd_id, ret, resp ? "Responsed" : "No Response");
}
}
return ret;
}
static int hgic_fwctrl_get_bytes(struct hgic_fwctrl *ctrl, u32 cmd_id, u8 *buff, u32 len)
{
int ret = -1;
struct sk_buff *skb = NULL;
struct sk_buff *resp = NULL;
struct hgic_ctrl_hdr *hdr = NULL;
skb = hgic_alloc_ctrl_skb(0);
if (skb) {
resp = hgic_fwctrl_send_cmd(ctrl, cmd_id, skb, true);
if (resp) {
hdr = (struct hgic_ctrl_hdr *)resp->data;
ret = hdr->cmd.status;
if (ret > 0 && len > 0) {
ret = ret > len ? len : ret;
memcpy(buff, (char *)(hdr + 1), ret);
}
dev_kfree_skb_any(resp);
}
}
if (ret < 0) {
hgic_dbg("get string: cmd:%d, ret:%d (%s)\r\n", cmd_id, ret, resp ? "Responsed" : "No Response");
}
return ret;
}
static int hgic_fwctrl_do_cmd(struct hgic_fwctrl *ctrl, u32 cmd_id, u8 *data, u32 len, u8 *buff, u32 size)
{
int ret = -1;
struct sk_buff *skb = NULL;
struct sk_buff *resp = NULL;
struct hgic_ctrl_hdr *hdr = NULL;
skb = hgic_alloc_ctrl_skb(len);
if (skb) {
if (data && len > 0) {
memcpy(skb->data, data, len);
skb->data[len] = 0;
}
resp = hgic_fwctrl_send_cmd(ctrl, cmd_id, skb, true);
if (resp) {
hdr = (struct hgic_ctrl_hdr *)resp->data;
ret = hdr->cmd.status;
if (ret > 0 && buff) {
memcpy(buff, (char *)(hdr + 1), ret > size ? size : ret);
}
dev_kfree_skb_any(resp);
}
if (ret < 0) {
hgic_dbg("cmd:%d, ret:%d (%s)\r\n", cmd_id, ret, resp ? "Responsed" : "No Response");
}
}
return ret;
}
static int hgic_fwctrl_rx_response(struct hgic_fwctrl *ctrl, struct sk_buff *skb)
{
int find = 0;
unsigned long flags;
struct hgic_cmd_response *resp = NULL;
struct hgic_ctrl_hdr *cmd = (struct hgic_ctrl_hdr *)skb->data;;
spin_lock_irqsave(&ctrl->lock, flags);
list_for_each_entry(resp, &ctrl->pd_list, list) {
if (resp->cookie == cmd->hdr.cookie) {
resp->skb = skb;
complete(&resp->cmpl);
find = 1;
break;
}
}
spin_unlock_irqrestore(&ctrl->lock, flags);
if (!find) {
dev_kfree_skb_any(skb);
}
return 0;
}
static void hgic_fwctrl_clear_pdlist(struct hgic_fwctrl *ctrl)
{
unsigned long flags;
struct hgic_cmd_response *sync = NULL;
spin_lock_irqsave(&ctrl->lock, flags);
list_for_each_entry(sync, &ctrl->pd_list, list) {
complete(&sync->cmpl);
}
spin_unlock_irqrestore(&ctrl->lock, flags);
}
static void hgic_fwctrl_work(struct work_struct *work)
{
struct hgic_fwctrl *ctrl = NULL;
struct sk_buff *skb = NULL;
struct hgic_ctrl_hdr *hdr = NULL;
ctrl = container_of(work, struct hgic_fwctrl, work);
while (!skb_queue_empty(&ctrl->rxq)) {
skb = skb_dequeue(&ctrl->rxq);
hdr = (struct hgic_ctrl_hdr *)skb->data;
switch (hdr->hdr.type) {
case HGIC_HDR_TYPE_CMD:
case HGIC_HDR_TYPE_CMD2:
case HGIC_HDR_TYPE_BOOTDL:
case HGIC_HDR_TYPE_OTA:
hgic_fwctrl_rx_response(ctrl, skb);
break;
case HGIC_HDR_TYPE_EVENT:
case HGIC_HDR_TYPE_EVENT2:
hgic_rx_fw_event(ctrl, skb);
break;
default:
dev_kfree_skb(skb);
break;
}
}
}
static void hgic_fwctrl_flush_work(struct work_struct *work)
{
struct hgic_fwctrl *ctrl = container_of(work, struct hgic_fwctrl, flush_work);
hgic_fwctrl_flush_param(ctrl);
}
struct sk_buff *hgic_fwctrl_send_data(struct hgic_fwctrl *ctrl, struct sk_buff *skb, struct hgic_cmd_response *resp, u32 timeout)
{
unsigned long flags;
init_completion(&resp->cmpl);
spin_lock_irqsave(&ctrl->lock, flags);
list_add(&resp->list, &ctrl->pd_list);
spin_unlock_irqrestore(&ctrl->lock, flags);
skb_queue_tail(&ctrl->txq, skb);
hgic_schedule(ctrl);
if (timeout) {
wait_for_completion_timeout(&resp->cmpl, msecs_to_jiffies(timeout));
}
spin_lock_irqsave(&ctrl->lock, flags);
list_del(&resp->list);
spin_unlock_irqrestore(&ctrl->lock, flags);
#ifdef __RTOS__
deinit_completion(&resp->cmpl);
#endif
return resp->skb;
}
void hgic_fwctrl_rx(struct hgic_fwctrl *ctrl, struct sk_buff *skb)
{
skb_queue_tail(&ctrl->rxq, skb);
if (ctrl->wq) {
queue_work(ctrl->wq, &ctrl->work);
} else {
hgic_err("fwctrl workqueue is NULL\r\n");
}
}
void hgic_fwctrl_init(struct hgic_fwctrl *ctrl, void *dev)
{
memset(ctrl, 0, sizeof(struct hgic_fwctrl));
ctrl->dev = dev;
ctrl->param.acs = 0x3;
ctrl->param.tx_mcs = 0xff;
ctrl->param.ps_mode = 0xff;
ctrl->param.wkio_mode = 0xff;
spin_lock_init(&ctrl->lock);
INIT_LIST_HEAD(&ctrl->pd_list);
skb_queue_head_init(&ctrl->rxq);
skb_queue_head_init(&ctrl->txq);
INIT_WORK(&ctrl->flush_work, hgic_fwctrl_flush_work);
INIT_WORK(&ctrl->work, hgic_fwctrl_work);
ctrl->wq = ALLOC_ORDERED_WORKQUEUE("fwctrl", 0);
}
void hgic_fwctrl_release(struct hgic_fwctrl *ctrl)
{
cancel_work_sync(&ctrl->work);
cancel_work_sync(&ctrl->flush_work);
hgic_fwctrl_clear_pdlist(ctrl);
hgic_clear_queue(&ctrl->txq);
hgic_clear_queue(&ctrl->rxq);
#ifdef __RTOS__
skb_queue_head_deinit(&ctrl->rxq);
skb_queue_head_deinit(&ctrl->txq);
spin_lock_deinit(&ctrl->lock);
#endif
if (ctrl->wq) {
flush_workqueue(ctrl->wq);
destroy_workqueue(ctrl->wq);
}
ARG_FREE(country_code);
ARG_FREE(ssid);
ARG_FREE(bssid);
ARG_FREE(bssid_filter);
ARG_FREE(key_mgmt);
ARG_FREE(wpa_psk);
ARG_FREE(mode);
ARG_FREE(mac_addr);
ARG_FREE(chan_list);
ARG_FREE(paired_stas);
ARG_FREE(mcast_key);
}
void hgic_fwctrl_flush_param(struct hgic_fwctrl *ctrl)
{
//int retry = 0;
if (ctrl->param.bss_bw) {
hgic_fwctrl_set_bss_bw(ctrl, ctrl->param.bss_bw);
}
if (ctrl->param.chan_list) {
hgic_fwctrl_set_chan_list(ctrl, (u16 *)ctrl->param.chan_list, ctrl->param.chan_cnt);
}
if (ctrl->param.freq_start) {
hgic_fwctrl_set_freq_range(ctrl, ctrl->param.freq_start, ctrl->param.freq_end, ctrl->param.chan_bw);
}
if (ctrl->param.country_code) {
hgic_fwctrl_set_countryregion(ctrl, ctrl->param.country_code);
}
if (ctrl->param.ssid) {
hgic_fwctrl_set_ssid(ctrl, ctrl->param.ssid);
}
if (ctrl->param.bssid) {
hgic_fwctrl_set_bssid(ctrl, ctrl->param.bssid);
}
if (ctrl->param.bssid_filter) {
hgic_fwctrl_set_bssid_filter(ctrl, ctrl->param.bssid_filter);
}
if (ctrl->param.key_mgmt) {
hgic_fwctrl_set_key_mgmt(ctrl, ctrl->param.key_mgmt);
}
if (ctrl->param.wpa_psk) {
hgic_fwctrl_set_wpa_psk(ctrl, ctrl->param.wpa_psk);
}
if (ctrl->param.mcast_key) {
hgic_fwctrl_set_mcast_key(ctrl, ctrl->param.mcast_key);
}
if (ctrl->param.mac_addr) {
hgic_fwctrl_set_mac(ctrl, ctrl->param.mac_addr);
}
if (ctrl->param.channel) {
hgic_fwctrl_set_channel(ctrl, ctrl->param.channel);
}
if (ctrl->param.rts_threshold) {
hgic_fwctrl_set_rts_threshold(ctrl, ctrl->param.rts_threshold);
}
if (ctrl->param.frag_threshold) {
hgic_fwctrl_set_frag_threshold(ctrl, ctrl->param.frag_threshold);
}
if (ctrl->param.listen_interval) {
hgic_fwctrl_set_listen_interval(ctrl, ctrl->param.listen_interval);
}
if (ctrl->param.center_freq) {
hgic_fwctrl_set_center_freq(ctrl, ctrl->param.center_freq);
}
if (ctrl->param.beacon_int) {
hgic_fwctrl_set_beacon_int(ctrl, ctrl->param.beacon_int);
}
if (ctrl->param.ethertype) {
hgic_fwctrl_set_ethertype(ctrl, ctrl->param.ethertype);
}
if (ctrl->param.txpower) {
hgic_fwctrl_set_txpower(ctrl, ctrl->param.txpower);
}
if (ctrl->param.primary_chan) {
hgic_fwctrl_set_primary_chan(ctrl, ctrl->param.primary_chan);
}
if (ctrl->param.mode) {
hgic_fwctrl_set_mode(ctrl, ctrl->param.mode);
}
if (ctrl->param.acs != 0x3) {
hgic_fwctrl_set_acs(ctrl, ctrl->param.acs, ctrl->param.acs_tmo);
}
if (ctrl->param.bss_max_idle) {
hgic_fwctrl_set_bss_max_idle(ctrl, ctrl->param.bss_max_idle);
}
if (ctrl->param.dtim_period) {
hgic_fwctrl_set_dtim_period(ctrl, ctrl->param.dtim_period);
}
if (ctrl->param.ps_connect) {
hgic_fwctrl_set_ps_connect(ctrl, ctrl->param.ps_connect, ctrl->param.ps_connect_roundup);
}
if (ctrl->param.agg_cnt) {
hgic_fwctrl_set_agg_cnt(ctrl, ctrl->param.agg_cnt);
}
if (ctrl->param.wkio_mode != 0xff) {
hgic_fwctrl_set_wkio_mode(ctrl, ctrl->param.wkio_mode);
}
if (ctrl->param.ps_mode != 0xff) {
hgic_fwctrl_set_ps_mode(ctrl, ctrl->param.ps_mode);
}
if (ctrl->param.aplost_time) {
hgic_fwctrl_set_aplost_time(ctrl, ctrl->param.aplost_time);
}
if (ctrl->param.tx_mcs != 0xff) {
hgic_fwctrl_set_tx_mcs(ctrl, ctrl->param.tx_mcs);
}
}
///////////////////////////////////////////////////////////////////////////////////////////
int hgic_fwctrl_testmode_cmd(struct hgic_fwctrl *ctrl, u8 *cmd, u32 size)
{
int ret = 0;
ret = hgic_fwctrl_do_cmd(ctrl, HGIC_CMD_TESTMODE_CMD, cmd, STR_LEN(cmd), cmd, size);
if (ret < 0) {
strcpy(cmd, "failed");
} else {
cmd[ret] = 0;
}
return ret;
}
int hgic_fwctrl_get_status(struct hgic_fwctrl *ctrl, u8 *buff, u32 len)
{
return hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_STATUS, buff, len);
}
int hgic_fwctrl_get_conn_state(struct hgic_fwctrl *ctrl)
{
return hgic_fwctrl_do_cmd(ctrl, HGIC_CMD_GET_CONN_STATE, NULL, 0, NULL, 0);
}
int hgic_fwctrl_get_fwinfo(struct hgic_fwctrl *ctrl, struct hgic_fw_info *info)
{
return hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_FW_INFO, (u8 *)info, sizeof(struct hgic_fw_info));
}
int hgic_fwctrl_set_countryregion(struct hgic_fwctrl *ctrl, u8 *country_code)
{
ARG_DUP(country_code, 0);
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_COUNTERY, country_code, STR_LEN(country_code));
}
int hgic_fwctrl_set_ssid(struct hgic_fwctrl *ctrl, u8 *ssid)
{
ARG_DUP(ssid, 0);
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_SSID, ssid, STR_LEN(ssid));
}
int hgic_fwctrl_set_bssid(struct hgic_fwctrl *ctrl, u8 *bssid)
{
ARG_DUP(bssid, 6);
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_BSSID, bssid, 6);
}
int hgic_fwctrl_set_channel(struct hgic_fwctrl *ctrl, u32 channel)
{
ctrl->param.acs = 0;
ctrl->param.channel = channel;
return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_CHANNEL, channel);
}
int hgic_fwctrl_set_bssid_filter(struct hgic_fwctrl *ctrl, u8 *bssid_filter)
{
ARG_DUP(bssid_filter, 0);
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_BSSID_FILTER, bssid_filter, STR_LEN(bssid_filter));
}
int hgic_fwctrl_set_rts_threshold(struct hgic_fwctrl *ctrl, u32 rts_threshold)
{
ctrl->param.rts_threshold = rts_threshold;
return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_RTS_THRESHOLD, rts_threshold);
}
int hgic_fwctrl_set_frag_threshold(struct hgic_fwctrl *ctrl, u32 frag_threshold)
{
ctrl->param.frag_threshold = frag_threshold;
return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_FRG_THRESHOLD, frag_threshold);
}
int hgic_fwctrl_set_key_mgmt(struct hgic_fwctrl *ctrl, u8 *key_mgmt)
{
ARG_DUP(key_mgmt, 0);
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_KEY_MGMT, key_mgmt, STR_LEN(key_mgmt));
}
int hgic_fwctrl_set_wpa_psk(struct hgic_fwctrl *ctrl, u8 *wpa_psk)
{
ARG_DUP(wpa_psk, 0);
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_WPA_PSK, wpa_psk, STR_LEN(wpa_psk));
}
int hgic_fwctrl_set_wbnat(struct hgic_fwctrl *ctrl, u32 enable)
{
return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_WBNAT, enable);
}
int hgic_fwctrl_set_freq_range(struct hgic_fwctrl *ctrl, u32 freq_start, u32 freq_end, u32 bss_bw)
{
u8 data[12];
put_unaligned_le32(freq_start, data);
put_unaligned_le32(freq_end, data + 4);
put_unaligned_le32(bss_bw, data + 8);
ctrl->param.freq_start = freq_start;
ctrl->param.freq_end = freq_end;
ctrl->param.chan_bw = bss_bw;
ARG_FREE(chan_list);
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_FREQ_RANGE, data, 12);
}
int hgic_fwctrl_set_bss_bw(struct hgic_fwctrl *ctrl, u8 bss_bw)
{
ctrl->param.bss_bw = bss_bw;
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_BSS_BW, bss_bw);
}
int hgic_fwctrl_set_tx_bw(struct hgic_fwctrl *ctrl, u8 tx_bw)
{
ctrl->param.tx_bw = tx_bw;
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_TX_BW, tx_bw);
}
int hgic_fwctrl_set_tx_mcs(struct hgic_fwctrl *ctrl, u8 tx_mcs)
{
ctrl->param.tx_mcs = tx_mcs;
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_TX_MCS, tx_mcs);
}
int hgic_fwctrl_set_acs(struct hgic_fwctrl *ctrl, u8 acs, u8 acs_tmo)
{
u8 data[2] = {acs, acs_tmo};
ctrl->param.acs = acs;
ctrl->param.acs_tmo = acs_tmo;
if (acs) {
ctrl->param.channel = 0;
ctrl->param.center_freq = 0;
}
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_ACS_ENABLE, data, 2);
}
int hgic_fwctrl_set_bgrssi(struct hgic_fwctrl *ctrl, u8 bgrssi)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_BG_RSSI, bgrssi);
}
int hgic_fwctrl_set_chan_list(struct hgic_fwctrl *ctrl, u16 *chan_list, u32 cnt)
{
int ret = -1;
int i = 0;
u8 *buf = kzalloc((1 + cnt) * sizeof(u16), GFP_KERNEL);
ARG_DUP(chan_list, cnt * sizeof(u16));
ctrl->param.chan_cnt = cnt;
ctrl->param.freq_start = 0;
if (buf) {
put_unaligned_le16(cnt, buf);
for (i = 0; i < cnt; i++) {
put_unaligned_le16(chan_list[i], buf + (i + 1) * sizeof(u16));
}
ret = hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_CHAN_LIST, buf, (1 + cnt) * sizeof(u16));
kfree(buf);
}
return ret;
}
int hgic_fwctrl_set_mode(struct hgic_fwctrl *ctrl, u8 *mode)
{
ARG_DUP(mode, 0);
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_WORK_MODE, mode, STR_LEN(mode));
}
int hgic_fwctrl_set_paired_stas(struct hgic_fwctrl *ctrl, u8 *paired_stas, u32 len)
{
ARG_DUP(paired_stas, len);
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_PAIRED_STATIONS, paired_stas, len);
}
int hgic_fwctrl_set_pairing(struct hgic_fwctrl *ctrl, u8 start)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_PAIRING, start);
}
int hgic_fwctrl_open_dev(struct hgic_fwctrl *ctrl)
{
return hgic_fwctrl_do_cmd(ctrl, HGIC_CMD_DEV_OPEN, 0, 0, 0, 0);
}
int hgic_fwctrl_close_dev(struct hgic_fwctrl *ctrl)
{
return hgic_fwctrl_do_cmd(ctrl, HGIC_CMD_DEV_CLOSE, 0, 0, 0, 0);
}
int hgic_fwctrl_set_txpower(struct hgic_fwctrl *ctrl, u32 tx_power)
{
ctrl->param.txpower = tx_power;
return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_TX_POWER, tx_power);
}
int hgic_fwctrl_get_txpower(struct hgic_fwctrl *ctrl)
{
return hgic_fwctrl_get_short_val(ctrl, HGIC_CMD_GET_TX_POWER);
}
int hgic_fwctrl_set_listen_interval(struct hgic_fwctrl *ctrl, u32 listen_interval)
{
ctrl->param.listen_interval = listen_interval;
return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_LISTEN_INTERVAL, listen_interval);
}
int hgic_fwctrl_set_center_freq(struct hgic_fwctrl *ctrl, u32 center_freq)
{
ctrl->param.acs = 0;
ctrl->param.center_freq = center_freq;
return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_CENTER_FREQ, center_freq);
}
int hgic_fwctrl_set_tx_count(struct hgic_fwctrl *ctrl, u32 short_frm_tx_count, u32 long_frm_tx_count)
{
u8 data[8];
put_unaligned_le32(short_frm_tx_count, data);
put_unaligned_le32(long_frm_tx_count, data + 4);
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_TX_LCOUNT, data, 8);
}
int hgic_fwctrl_set_key(struct hgic_fwctrl *ctrl, u8 cmd, u16 aid, u8 *key, u8 len)
{
int ret = -ENOMEM;
u8 *buff = kmalloc(len + 4, GFP_KERNEL);
if (buff) {
buff[0] = cmd;
put_unaligned_le16(aid, buff + 1);
buff[3] = len;
memcpy(buff + 4, key, len);
ret = hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_KEY, buff, len + 4);
kfree(buff);
}
return ret;
}
int hgic_fwctrl_add_sta(struct hgic_fwctrl *ctrl, u16 aid, u8 *addr)
{
uint8_t sta_info[8];
put_unaligned_le16(aid, sta_info);
memcpy(sta_info + 2, addr, 6);
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_ADD_STA, sta_info, 8);
}
int hgic_fwctrl_del_sta(struct hgic_fwctrl *ctrl, u32 aid)
{
return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_REMOVE_STA, aid);
}
int hgic_fwctrl_set_primary_chan(struct hgic_fwctrl *ctrl, u8 primary_chan)
{
ctrl->param.primary_chan = primary_chan;
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_PRIMARY_CHAN, primary_chan);
}
int hgic_fwctrl_set_aid(struct hgic_fwctrl *ctrl, u32 aid)
{
return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_AID, aid);
}
int hgic_fwctrl_set_mac(struct hgic_fwctrl *ctrl, u8 *mac_addr)
{
ARG_DUP(mac_addr, 6);
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_MAC, mac_addr, 6);
}
int hgic_fwctrl_get_scan_list(struct hgic_fwctrl *ctrl, u8 *buff, u32 size)
{
return hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_SCAN_LIST, buff, size);
}
int hgic_fwctrl_scan(struct hgic_fwctrl *ctrl)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SCAN, 1);
}
int hgic_fwctrl_set_txq_param(struct hgic_fwctrl *ctrl, u8 *txq, u32 size)
{
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_TXQ_PARAM, txq, size);
}
int hgic_fwctrl_get_temperature(struct hgic_fwctrl *ctrl)
{
return hgic_fwctrl_get_short_val(ctrl, HGIC_CMD_GET_TEMPERATURE);
}
int hgic_fwctrl_enter_sleep(struct hgic_fwctrl *ctrl, u8 sleep)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_ENTER_SLEEP, sleep);
}
int hgic_fwctrl_get_sta_list(struct hgic_fwctrl *ctrl, struct hgic_sta_info *sta_list, u32 size)
{
int ret = hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_STA_LIST, (u8 *)sta_list, size * sizeof(struct hgic_sta_info));
return ret > 0 ? (ret / sizeof(struct hgic_sta_info)) : ret;
}
int hgic_fwctrl_set_beacon_int(struct hgic_fwctrl *ctrl, u32 beacon_int)
{
ctrl->param.beacon_int = beacon_int;
return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_BEACON_INT, beacon_int);
}
int hgic_fwctrl_get_ssid(struct hgic_fwctrl *ctrl, u8 *ssid, u32 size)
{
return hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_SSID, ssid, size);
}
int hgic_fwctrl_get_bssid(struct hgic_fwctrl *ctrl, u8 *bssid)
{
return hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_BSSID, bssid, 6);
}
int hgic_fwctrl_get_wpapsk(struct hgic_fwctrl *ctrl, u8 *psk, u32 size)
{
return hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_WPA_PSK, psk, size);
}
int hgic_fwctrl_save_cfg(struct hgic_fwctrl *ctrl)
{
return hgic_fwctrl_do_cmd(ctrl, HGIC_CMD_SAVE_CFG, 0, 0, 0, 0);
}
int hgic_fwctrl_join_group(struct hgic_fwctrl *ctrl, u8 *addr, u8 aid)
{
u8 val[7];
memcpy(val, addr, 6);
val[6] = aid;
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_JOIN_GROUP, val, 7);
}
int hgic_fwctrl_set_ethertype(struct hgic_fwctrl *ctrl, u16 ethertype)
{
u8 vals[2];
ctrl->param.ethertype = ethertype;
put_unaligned_le16(ethertype, vals);
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_ETHER_TYPE, vals, 2);
}
int hgic_fwctrl_get_sta_count(struct hgic_fwctrl *ctrl)
{
return hgic_fwctrl_do_cmd(ctrl, HGIC_CMD_GET_STA_COUNT, 0, 0, 0, 0);
}
int hgic_fwctrl_get_agg_cnt(struct hgic_fwctrl *ctrl)
{
return hgic_fwctrl_get_short_val(ctrl, HGIC_CMD_GET_AGG_CNT);
}
int hgic_fwctrl_set_agg_cnt(struct hgic_fwctrl *ctrl, u8 agg_cnt)
{
ctrl->param.agg_cnt = agg_cnt;
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_AGG_CNT, agg_cnt);
}
int hgic_fwctrl_get_bss_bw(struct hgic_fwctrl *ctrl)
{
return hgic_fwctrl_get_short_val(ctrl, HGIC_CMD_GET_BSS_BW);
}
int hgic_fwctrl_get_freq_range(struct hgic_fwctrl *ctrl, u32 *freq_start, u32 *freq_end, u32 *bss_bw)
{
int ret = 0;
u32 vals[3];
ret = hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_FREQ_RANGE, (u8 *)&vals, sizeof(vals));
if (ret == 12) {
*freq_start = vals[0];
*freq_end = vals[1];
*bss_bw = vals[2];
}
return (ret == 12);
}
int hgic_fwctrl_get_chan_list(struct hgic_fwctrl *ctrl, u16 *chan_list, u16 count)
{
int ret = hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_CHAN_LIST, (u8 *)chan_list, count * sizeof(u16));
return ret > 0 ? ret / sizeof(u16) : 0;
}
int hgic_fwctrl_wakeup_sta(struct hgic_fwctrl *ctrl, u8 *addr)
{
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_WAKEUP_STA, addr, 6);
}
int hgic_fwctrl_set_ps_heartbeat(struct hgic_fwctrl *ctrl, u32 ipaddr, u32 dport, u32 period, u32 hb_tmo)
{
u8 val[16];
put_unaligned_le32(ipaddr, val);
put_unaligned_le32(dport, val + 4);
put_unaligned_le32(period, val + 8);
put_unaligned_le32(hb_tmo, val + 12);
hgic_dbg("ip:%x, port:%d, period:%d, hb_tmo:%d\r\n", ipaddr, dport, period, hb_tmo);
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_PS_HEARTBEAT, val, 16);
}
int hgic_fwctrl_set_ps_heartbeat_resp(struct hgic_fwctrl *ctrl, u8 *data, u32 size)
{
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_PS_HEARTBEAT_RESP, data, size);
}
int hgic_fwctrl_set_ps_wakeup_data(struct hgic_fwctrl *ctrl, u8 *data, u32 size)
{
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_PS_WAKEUP_DATA, data, size);
}
int hgic_fwctrl_set_ps_connect(struct hgic_fwctrl *ctrl, u8 period, u8 roundup)
{
u8 val[2] = {period, roundup};
ctrl->param.ps_connect = period;
ctrl->param.ps_connect_roundup = roundup;
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_PS_CONNECT, val, 2);
}
int hgic_fwctrl_radio_onoff(struct hgic_fwctrl *ctrl, u8 onoff)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_RADIO_ONOFF, onoff);
}
int hgic_fwctrl_set_bss_max_idle(struct hgic_fwctrl *ctrl, u32 max_idle)
{
ctrl->param.bss_max_idle = max_idle;
return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_BSS_MAX_IDLE, max_idle);
}
int hgic_fwctrl_set_wkio_mode(struct hgic_fwctrl *ctrl, u8 mode)
{
ctrl->param.wkio_mode = mode;
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_WKIO_MODE, mode);
}
int hgic_fwctrl_set_dtim_period(struct hgic_fwctrl *ctrl, u32 period)
{
ctrl->param.dtim_period = period;
return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_DTIM_PERIOD, period);
}
int hgic_fwctrl_set_ps_mode(struct hgic_fwctrl *ctrl, u8 mode)
{
ctrl->param.ps_mode = mode;
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_PS_MODE, mode);
}
int hgic_fwctrl_set_load_def(struct hgic_fwctrl *ctrl, u8 rst)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_LOAD_DEF, rst);
}
int hgic_fwctrl_disassoc_sta(struct hgic_fwctrl *ctrl, u8 *addr)
{
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_DISASSOC_STA, addr, 6);
}
int hgic_fwctrl_set_aplost_time(struct hgic_fwctrl *ctrl, u32 aplost_time)
{
return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_APLOST_TIME, aplost_time);
}
int hgic_fwctrl_get_wkreason(struct hgic_fwctrl *ctrl)
{
return hgic_fwctrl_do_cmd(ctrl, HGIC_CMD_GET_WAKEUP_REASON, 0, 0, 0, 0);
}
int hgic_fwctrl_unpair(struct hgic_fwctrl *ctrl, u8 *addr)
{
ARG_FREE(ssid);
ARG_FREE(bssid);
ARG_FREE(bssid_filter);
ARG_FREE(key_mgmt);
ARG_FREE(wpa_psk);
ARG_FREE(paired_stas);
ARG_FREE(mcast_key);
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_UNPAIR, addr, 6);
}
int hgic_fwctrl_set_auto_chswitch(struct hgic_fwctrl *ctrl, u8 enable)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_AUTO_CHAN_SWITCH, enable);
}
int hgic_fwctrl_set_mcast_key(struct hgic_fwctrl *ctrl, u8 *mcast_key)
{
ARG_DUP(mcast_key, 0);
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_MCAST_KEY, mcast_key, STR_LEN(mcast_key));
}
int hgic_fwctrl_set_reassoc_wkhost(struct hgic_fwctrl *ctrl, u8 enable)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_REASSOC_WKHOST, enable);
}
int hgic_fwctrl_set_wakeup_io(struct hgic_fwctrl *ctrl, u8 io, u8 edge)
{
u8 val[2] = {io, edge};
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_WAKEUP_IO, val, 2);
}
int hgic_fwctrl_set_dbginfo_output(struct hgic_fwctrl *ctrl, u8 enable)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_DBGINFO_OUTPUT, enable);
}
int hgic_fwctrl_set_sysdbg(struct hgic_fwctrl *ctrl, u8 *cmd)
{
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_SYSDBG, cmd, STR_LEN(cmd));
}
int hgic_fwctrl_set_autosleep_time(struct hgic_fwctrl *ctrl, u8 time)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_AUTO_SLEEP_TIME, time);
}
int hgic_fwctrl_get_key_mgmt(struct hgic_fwctrl *ctrl, u8 *ssid, u32 size)
{
return hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_KEY_MGMT, ssid, size);
}
int hgic_fwctrl_set_supper_pwr(struct hgic_fwctrl *ctrl, u8 enable)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_SUPPER_PWR, enable);
}
int hgic_fwctrl_set_repeater_ssid(struct hgic_fwctrl *ctrl, u8 *ssid)
{
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_REPEATER_SSID, ssid, STR_LEN(ssid));
}
int hgic_fwctrl_set_repeater_psk(struct hgic_fwctrl *ctrl, u8 *wpa_psk)
{
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_REPEATER_PSK, wpa_psk, STR_LEN(wpa_psk));
}
int hgic_fwctrl_set_auto_save(struct hgic_fwctrl *ctrl, u8 enable)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_CFG_AUTO_SAVE, enable);
}
int hgic_fwctrl_set_pair_autostop(struct hgic_fwctrl *ctrl, u8 enable)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_PAIR_AUTOSTOP, enable);
}
int hgic_fwctrl_send_cust_mgmt(struct hgic_fwctrl *ctrl, u8 *data, u32 len)
{
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SEND_CUST_MGMT, data, len);
}
int hgic_fwctrl_get_battery_level(struct hgic_fwctrl *ctrl)
{
return hgic_fwctrl_get_short_val(ctrl, HGIC_CMD_GET_BATTERY_LEVEL);
}
int hgic_fwctrl_set_dcdc13v(struct hgic_fwctrl *ctrl, u8 enable)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_DCDC13, enable);
}
int hgic_fwctrl_set_acktmo(struct hgic_fwctrl *ctrl, u32 tmo)
{
return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_ACKTMO, tmo);
}
int hgic_fwctrl_get_module_type(struct hgic_fwctrl *ctrl)
{
return hgic_fwctrl_get_short_val(ctrl, HGIC_CMD_GET_MODULETYPE);
}
int hgic_fwctrl_set_pa_pwrctl_dis(struct hgic_fwctrl *ctrl, u8 dis)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_PA_PWRCTRL_DIS, dis);
}
int hgic_fwctrl_set_dhcpc(struct hgic_fwctrl *ctrl, u8 en)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_DHCPC, en);
}
int hgic_fwctrl_get_dhcpc_result(struct hgic_fwctrl *ctrl, u8 *buff, int len)
{
return hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_DHCPC_RESULT, buff, len);
}
int hgic_fwctrl_set_wkdata_mask(struct hgic_fwctrl *ctrl, u16 offset, u8 *mask, u8 mask_len)
{
u8 data[128];
if(mask_len > 16) mask_len = 16;
memset(data, 0, sizeof(data));
put_unaligned_le16(offset, data);
memcpy(data + 2, mask, mask_len);
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_WKUPDATA_MASK, data, mask_len + 2);
}
int hgic_fwctrl_get_wkdata_buff(struct hgic_fwctrl *ctrl, u8 *buff, int len)
{
return hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_WKDATA_BUFF, buff, len);
}
int hgic_fwctrl_get_disassoc_reason(struct hgic_fwctrl *ctrl)
{
return hgic_fwctrl_get_short_val(ctrl, HGIC_CMD_GET_DISASSOC_REASON);
}
int hgic_fwctrl_set_wkdata_save(struct hgic_fwctrl *ctrl, u8 save)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_WKUPDATA_SAVEEN, save);
}
int hgic_fwctrl_set_cust_driver_data(struct hgic_fwctrl *ctrl, u8 *data, u32 len)
{
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_CUST_DRIVER_DATA, data, len);
}
int hgic_fwctrl_set_mcast_txparam(struct hgic_fwctrl *ctrl, struct hgic_mcast_txparam *param)
{
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_MCAST_TXPARAM, (u8 *)param, sizeof(struct hgic_mcast_txparam));
}
int hgic_fwctrl_set_freqinfo(struct hgic_fwctrl *ctrl, u8 *data, u32 len)
{
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_STA_FREQINFO, data, len);
}
int hgic_fwctrl_reset_sta(struct hgic_fwctrl *ctrl, u8 *addr)
{
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_RESET_STA, addr, 6);
}
int hgic_fwctrl_set_ant_auto(struct hgic_fwctrl *ctrl, u8 en)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_ANT_AUTO, en);
}
int hgic_fwctrl_select_ant(struct hgic_fwctrl *ctrl, u8 ant)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_ANT_SEL, ant);
}
int hgic_fwctrl_get_ant_sel(struct hgic_fwctrl *ctrl)
{
return hgic_fwctrl_get_short_val(ctrl, HGIC_CMD_GET_ANT_SEL);
}
int hgic_fwctrl_set_wkhost_reasons(struct hgic_fwctrl *ctrl, u8 *reasons, u8 count)
{
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_WKUP_HOST_REASON, reasons, count);
}
int hgic_fwctrl_set_mac_filter(struct hgic_fwctrl *ctrl, u8 en)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_MAC_FILTER_EN, en);
}
int hgic_fwctrl_set_atcmd(struct hgic_fwctrl *ctrl, char *atcmd)
{
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_ATCMD, atcmd, strlen(atcmd));
}
int hgic_fwctrl_set_roaming(struct hgic_fwctrl *ctrl, u8 en, u8 same_freq)
{
u8 vals[2] = {en, same_freq};
return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_ROAMING, vals, 2);
}
int hgic_fwctrl_set_ap_hide(struct hgic_fwctrl *ctrl, u8 hide)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_AP_HIDE, hide);
}
int hgic_fwctrl_set_frm_tx_maxcnt(struct hgic_fwctrl *ctrl, u8 txcnt)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_MAX_TCNT, txcnt);
}
int hgic_fwctrl_set_assert_holdup(struct hgic_fwctrl *ctrl, u8 holdup)
{
return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_ASSERT_HOLDUP, holdup);
}