538 lines
14 KiB
C
538 lines
14 KiB
C
/**
|
|
******************************************************************************
|
|
*
|
|
* rwnx_cmds.c
|
|
*
|
|
* Handles queueing (push to IPC, ack/cfm from IPC) of commands issued to
|
|
* LMAC FW
|
|
*
|
|
* Copyright (C) RivieraWaves 2014-2019
|
|
*
|
|
******************************************************************************
|
|
*/
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include "rwnx_cmds.h"
|
|
#include "rwnx_defs.h"
|
|
#include "rwnx_strs.h"
|
|
//#define CREATE_TRACE_POINTS
|
|
#include "rwnx_events.h"
|
|
#include "aicwf_txrxif.h"
|
|
#ifdef AICWF_SDIO_SUPPORT
|
|
#include "aicwf_sdio.h"
|
|
#else
|
|
#include "aicwf_usb.h"
|
|
#endif
|
|
/**
|
|
*
|
|
*/
|
|
extern int aicwf_sdio_writeb(struct aic_sdio_dev *sdiodev, uint regaddr, u8 val);
|
|
|
|
void rwnx_cmd_free(struct rwnx_cmd *cmd);
|
|
|
|
static void cmd_dump(const struct rwnx_cmd *cmd)
|
|
{
|
|
printk(KERN_CRIT "tkn[%d] flags:%04x result:%3d cmd:%4d-%-24s - reqcfm(%4d-%-s)\n",
|
|
cmd->tkn, cmd->flags, cmd->result, cmd->id, RWNX_ID2STR(cmd->id),
|
|
cmd->reqid, cmd->reqid != (lmac_msg_id_t)-1 ? RWNX_ID2STR(cmd->reqid) : "none");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static void cmd_complete(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd)
|
|
{
|
|
//RWNX_DBG(RWNX_FN_ENTRY_STR);
|
|
lockdep_assert_held(&cmd_mgr->lock);
|
|
|
|
list_del(&cmd->list);
|
|
cmd_mgr->queue_sz--;
|
|
|
|
cmd->flags |= RWNX_CMD_FLAG_DONE;
|
|
if (cmd->flags & RWNX_CMD_FLAG_NONBLOCK) {
|
|
rwnx_cmd_free(cmd);//kfree(cmd);
|
|
} else {
|
|
if (RWNX_CMD_WAIT_COMPLETE(cmd->flags)) {
|
|
cmd->result = 0;
|
|
complete(&cmd->complete);
|
|
}
|
|
}
|
|
}
|
|
|
|
int cmd_mgr_queue_force_defer(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd)
|
|
{
|
|
bool defer_push = false;
|
|
|
|
RWNX_DBG(RWNX_FN_ENTRY_STR);
|
|
#ifdef CREATE_TRACE_POINTS
|
|
trace_msg_send(cmd->id);
|
|
#endif
|
|
spin_lock_bh(&cmd_mgr->lock);
|
|
|
|
if (cmd_mgr->state == RWNX_CMD_MGR_STATE_CRASHED) {
|
|
printk(KERN_CRIT"cmd queue crashed\n");
|
|
cmd->result = -EPIPE;
|
|
spin_unlock_bh(&cmd_mgr->lock);
|
|
return -EPIPE;
|
|
}
|
|
|
|
#ifndef CONFIG_RWNX_FHOST
|
|
if (!list_empty(&cmd_mgr->cmds)) {
|
|
if (cmd_mgr->queue_sz == cmd_mgr->max_queue_sz) {
|
|
printk(KERN_CRIT"Too many cmds (%d) already queued\n",
|
|
cmd_mgr->max_queue_sz);
|
|
cmd->result = -ENOMEM;
|
|
spin_unlock_bh(&cmd_mgr->lock);
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
cmd->flags |= RWNX_CMD_FLAG_WAIT_PUSH;
|
|
defer_push = true;
|
|
|
|
if (cmd->flags & RWNX_CMD_FLAG_REQ_CFM)
|
|
cmd->flags |= RWNX_CMD_FLAG_WAIT_CFM;
|
|
|
|
cmd->tkn = cmd_mgr->next_tkn++;
|
|
cmd->result = -EINTR;
|
|
|
|
if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK))
|
|
init_completion(&cmd->complete);
|
|
|
|
list_add_tail(&cmd->list, &cmd_mgr->cmds);
|
|
cmd_mgr->queue_sz++;
|
|
spin_unlock_bh(&cmd_mgr->lock);
|
|
|
|
WAKE_CMD_WORK(cmd_mgr);
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_mgr_queue(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd)
|
|
{
|
|
#ifdef AICWF_SDIO_SUPPORT
|
|
int ret;
|
|
struct aic_sdio_dev *sdiodev = container_of(cmd_mgr, struct aic_sdio_dev, cmd_mgr);
|
|
#endif
|
|
#ifdef AICWF_USB_SUPPORT
|
|
struct aic_usb_dev *usbdev = container_of(cmd_mgr, struct aic_usb_dev, cmd_mgr);
|
|
#endif
|
|
bool defer_push = false;
|
|
|
|
//RWNX_DBG(RWNX_FN_ENTRY_STR);
|
|
#ifdef CREATE_TRACE_POINTS
|
|
trace_msg_send(cmd->id);
|
|
#endif
|
|
spin_lock_bh(&cmd_mgr->lock);
|
|
|
|
if (cmd_mgr->state == RWNX_CMD_MGR_STATE_CRASHED) {
|
|
printk(KERN_CRIT"cmd queue crashed\n");
|
|
cmd->result = -EPIPE;
|
|
spin_unlock_bh(&cmd_mgr->lock);
|
|
return -EPIPE;
|
|
}
|
|
|
|
#ifndef CONFIG_RWNX_FHOST
|
|
if (!list_empty(&cmd_mgr->cmds)) {
|
|
struct rwnx_cmd *last;
|
|
|
|
if (cmd_mgr->queue_sz == cmd_mgr->max_queue_sz) {
|
|
printk(KERN_CRIT"Too many cmds (%d) already queued\n",
|
|
cmd_mgr->max_queue_sz);
|
|
cmd->result = -ENOMEM;
|
|
spin_unlock_bh(&cmd_mgr->lock);
|
|
return -ENOMEM;
|
|
}
|
|
last = list_entry(cmd_mgr->cmds.prev, struct rwnx_cmd, list);
|
|
if (last->flags & (RWNX_CMD_FLAG_WAIT_ACK | RWNX_CMD_FLAG_WAIT_PUSH | RWNX_CMD_FLAG_WAIT_CFM)) {
|
|
#if 0 // queue even NONBLOCK command.
|
|
if (cmd->flags & RWNX_CMD_FLAG_NONBLOCK) {
|
|
printk(KERN_CRIT"cmd queue busy\n");
|
|
cmd->result = -EBUSY;
|
|
spin_unlock_bh(&cmd_mgr->lock);
|
|
return -EBUSY;
|
|
}
|
|
#endif
|
|
cmd->flags |= RWNX_CMD_FLAG_WAIT_PUSH;
|
|
defer_push = true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
cmd->flags |= RWNX_CMD_FLAG_WAIT_ACK;
|
|
#endif
|
|
if (cmd->flags & RWNX_CMD_FLAG_REQ_CFM)
|
|
cmd->flags |= RWNX_CMD_FLAG_WAIT_CFM;
|
|
|
|
cmd->tkn = cmd_mgr->next_tkn++;
|
|
cmd->result = -EINTR;
|
|
|
|
if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK))
|
|
init_completion(&cmd->complete);
|
|
|
|
list_add_tail(&cmd->list, &cmd_mgr->cmds);
|
|
cmd_mgr->queue_sz++;
|
|
|
|
if (cmd->a2e_msg->id == ME_TRAFFIC_IND_REQ
|
|
#ifdef AICWF_ARP_OFFLOAD
|
|
|| cmd->a2e_msg->id == MM_SET_ARPOFFLOAD_REQ
|
|
#endif
|
|
) {
|
|
defer_push = true;
|
|
cmd->flags |= RWNX_CMD_FLAG_WAIT_PUSH;
|
|
//printk("defer push: tkn=%d\r\n", cmd->tkn);
|
|
}
|
|
|
|
spin_unlock_bh(&cmd_mgr->lock);
|
|
if (!defer_push) {
|
|
//printk("queue:id=%x, param_len=%u\n",cmd->a2e_msg->id, cmd->a2e_msg->param_len);
|
|
#ifdef AICWF_SDIO_SUPPORT
|
|
aicwf_set_cmd_tx((void *)(sdiodev), cmd->a2e_msg, sizeof(struct lmac_msg) + cmd->a2e_msg->param_len);
|
|
#else
|
|
aicwf_set_cmd_tx((void *)(usbdev), cmd->a2e_msg, sizeof(struct lmac_msg) + cmd->a2e_msg->param_len);
|
|
#endif
|
|
//rwnx_ipc_msg_push(rwnx_hw, cmd, RWNX_CMD_A2EMSG_LEN(cmd->a2e_msg));
|
|
|
|
kfree(cmd->a2e_msg);
|
|
} else {
|
|
WAKE_CMD_WORK(cmd_mgr);
|
|
return 0;
|
|
}
|
|
|
|
if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK)) {
|
|
#ifdef CONFIG_RWNX_FHOST
|
|
if (wait_for_completion_killable(&cmd->complete)) {
|
|
cmd->result = -EINTR;
|
|
spin_lock_bh(&cmd_mgr->lock);
|
|
cmd_complete(cmd_mgr, cmd);
|
|
spin_unlock_bh(&cmd_mgr->lock);
|
|
/* TODO: kill the cmd at fw level */
|
|
}
|
|
#else
|
|
unsigned long tout = msecs_to_jiffies(RWNX_80211_CMD_TIMEOUT_MS * cmd_mgr->queue_sz);
|
|
if (!wait_for_completion_timeout(&cmd->complete, tout)) {
|
|
printk(KERN_CRIT"cmd timed-out\n");
|
|
#ifdef AICWF_SDIO_SUPPORT
|
|
ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.wakeup_reg, 2);
|
|
if (ret < 0) {
|
|
sdio_err("reg:%d write failed!\n", sdiodev->sdio_reg.wakeup_reg);
|
|
}
|
|
#endif
|
|
|
|
cmd_dump(cmd);
|
|
spin_lock_bh(&cmd_mgr->lock);
|
|
cmd_mgr->state = RWNX_CMD_MGR_STATE_CRASHED;
|
|
if (!(cmd->flags & RWNX_CMD_FLAG_DONE)) {
|
|
cmd->result = -ETIMEDOUT;
|
|
cmd_complete(cmd_mgr, cmd);
|
|
}
|
|
spin_unlock_bh(&cmd_mgr->lock);
|
|
} else {
|
|
rwnx_cmd_free(cmd);//kfree(cmd);
|
|
if (!list_empty(&cmd_mgr->cmds))
|
|
WAKE_CMD_WORK(cmd_mgr);
|
|
}
|
|
#endif
|
|
} else {
|
|
cmd->result = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static int cmd_mgr_llind(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd)
|
|
{
|
|
struct rwnx_cmd *cur, *acked = NULL, *next = NULL;
|
|
|
|
RWNX_DBG(RWNX_FN_ENTRY_STR);
|
|
|
|
spin_lock_bh(&cmd_mgr->lock);
|
|
list_for_each_entry(cur, &cmd_mgr->cmds, list) {
|
|
if (!acked) {
|
|
if (cur->tkn == cmd->tkn) {
|
|
if (WARN_ON_ONCE(cur != cmd)) {
|
|
cmd_dump(cmd);
|
|
}
|
|
acked = cur;
|
|
continue;
|
|
}
|
|
}
|
|
if (cur->flags & RWNX_CMD_FLAG_WAIT_PUSH) {
|
|
next = cur;
|
|
break;
|
|
}
|
|
}
|
|
if (!acked) {
|
|
printk(KERN_CRIT "Error: acked cmd not found\n");
|
|
} else {
|
|
cmd->flags &= ~RWNX_CMD_FLAG_WAIT_ACK;
|
|
if (RWNX_CMD_WAIT_COMPLETE(cmd->flags))
|
|
cmd_complete(cmd_mgr, cmd);
|
|
}
|
|
|
|
if (next) {
|
|
#if 0 //there is no ack
|
|
struct rwnx_hw *rwnx_hw = container_of(cmd_mgr, struct rwnx_hw, cmd_mgr);
|
|
next->flags &= ~RWNX_CMD_FLAG_WAIT_PUSH;
|
|
rwnx_ipc_msg_push(rwnx_hw, next, RWNX_CMD_A2EMSG_LEN(next->a2e_msg));
|
|
kfree(next->a2e_msg);
|
|
#endif
|
|
}
|
|
spin_unlock(&cmd_mgr->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void cmd_mgr_task_process(struct work_struct *work)
|
|
{
|
|
struct rwnx_cmd_mgr *cmd_mgr = container_of(work, struct rwnx_cmd_mgr, cmdWork);
|
|
struct rwnx_cmd *cur, *next = NULL;
|
|
unsigned long tout;
|
|
|
|
RWNX_DBG(RWNX_FN_ENTRY_STR);
|
|
|
|
while (1) {
|
|
next = NULL;
|
|
spin_lock_bh(&cmd_mgr->lock);
|
|
|
|
list_for_each_entry(cur, &cmd_mgr->cmds, list) {
|
|
if (cur->flags & RWNX_CMD_FLAG_WAIT_PUSH) { //just judge the first
|
|
next = cur;
|
|
}
|
|
break;
|
|
}
|
|
spin_unlock_bh(&cmd_mgr->lock);
|
|
|
|
if (next == NULL)
|
|
break;
|
|
|
|
if (next) {
|
|
#ifdef AICWF_SDIO_SUPPORT
|
|
struct aic_sdio_dev *sdiodev = container_of(cmd_mgr, struct aic_sdio_dev, cmd_mgr);
|
|
#endif
|
|
#ifdef AICWF_USB_SUPPORT
|
|
struct aic_usb_dev *usbdev = container_of(cmd_mgr, struct aic_usb_dev, cmd_mgr);
|
|
#endif
|
|
next->flags &= ~RWNX_CMD_FLAG_WAIT_PUSH;
|
|
|
|
//printk("cmd_process, cmd->id=%d, tkn=%d\r\n",next->reqid, next->tkn);
|
|
//rwnx_ipc_msg_push(rwnx_hw, next, RWNX_CMD_A2EMSG_LEN(next->a2e_msg));
|
|
#ifdef AICWF_SDIO_SUPPORT
|
|
aicwf_set_cmd_tx((void *)(sdiodev), next->a2e_msg, sizeof(struct lmac_msg) + next->a2e_msg->param_len);
|
|
#else
|
|
aicwf_set_cmd_tx((void *)(usbdev), next->a2e_msg, sizeof(struct lmac_msg) + next->a2e_msg->param_len);
|
|
#endif
|
|
kfree(next->a2e_msg);
|
|
|
|
tout = msecs_to_jiffies(RWNX_80211_CMD_TIMEOUT_MS * cmd_mgr->queue_sz);
|
|
if (!wait_for_completion_timeout(&next->complete, tout)) {
|
|
printk(KERN_CRIT"cmd timed-out\n");
|
|
#ifdef AICWF_SDIO_SUPPORT
|
|
if (aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.wakeup_reg, 2) < 0) {
|
|
sdio_err("reg:%d write failed!\n", sdiodev->sdio_reg.wakeup_reg);
|
|
}
|
|
#endif
|
|
cmd_dump(next);
|
|
spin_lock_bh(&cmd_mgr->lock);
|
|
cmd_mgr->state = RWNX_CMD_MGR_STATE_CRASHED;
|
|
if (!(next->flags & RWNX_CMD_FLAG_DONE)) {
|
|
next->result = -ETIMEDOUT;
|
|
cmd_complete(cmd_mgr, next);
|
|
}
|
|
spin_unlock_bh(&cmd_mgr->lock);
|
|
} else
|
|
rwnx_cmd_free(next);//kfree(next);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
static int cmd_mgr_run_callback(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd,
|
|
struct rwnx_cmd_e2amsg *msg, msg_cb_fct cb)
|
|
{
|
|
int res;
|
|
|
|
if (!cb) {
|
|
return 0;
|
|
}
|
|
//RWNX_DBG(RWNX_FN_ENTRY_STR);
|
|
//spin_lock_bh(&rwnx_hw->cb_lock);
|
|
res = cb(rwnx_hw, cmd, msg);
|
|
//spin_unlock_bh(&rwnx_hw->cb_lock);
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
*
|
|
|
|
*/
|
|
static int cmd_mgr_msgind(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd_e2amsg *msg,
|
|
msg_cb_fct cb)
|
|
{
|
|
#ifdef AICWF_SDIO_SUPPORT
|
|
struct aic_sdio_dev *sdiodev = container_of(cmd_mgr, struct aic_sdio_dev, cmd_mgr);
|
|
struct rwnx_hw *rwnx_hw = sdiodev->rwnx_hw;
|
|
#endif
|
|
#ifdef AICWF_USB_SUPPORT
|
|
struct aic_usb_dev *usbdev = container_of(cmd_mgr, struct aic_usb_dev, cmd_mgr);
|
|
struct rwnx_hw *rwnx_hw = usbdev->rwnx_hw;
|
|
#endif
|
|
struct rwnx_cmd *cmd, *pos;
|
|
bool found = false;
|
|
|
|
// RWNX_DBG(RWNX_FN_ENTRY_STR);
|
|
#ifdef CREATE_TRACE_POINTS
|
|
trace_msg_recv(msg->id);
|
|
#endif
|
|
//printk("cmd->id=%x\n", msg->id);
|
|
spin_lock_bh(&cmd_mgr->lock);
|
|
list_for_each_entry_safe(cmd, pos, &cmd_mgr->cmds, list) {
|
|
if (cmd->reqid == msg->id &&
|
|
(cmd->flags & RWNX_CMD_FLAG_WAIT_CFM)) {
|
|
|
|
if (!cmd_mgr_run_callback(rwnx_hw, cmd, msg, cb)) {
|
|
found = true;
|
|
cmd->flags &= ~RWNX_CMD_FLAG_WAIT_CFM;
|
|
|
|
if (WARN((msg->param_len > RWNX_CMD_E2AMSG_LEN_MAX),
|
|
"Unexpect E2A msg len %d > %d\n", msg->param_len,
|
|
RWNX_CMD_E2AMSG_LEN_MAX)) {
|
|
msg->param_len = RWNX_CMD_E2AMSG_LEN_MAX;
|
|
}
|
|
|
|
if (cmd->e2a_msg && msg->param_len)
|
|
memcpy(cmd->e2a_msg, &msg->param, msg->param_len);
|
|
|
|
if (RWNX_CMD_WAIT_COMPLETE(cmd->flags))
|
|
cmd_complete(cmd_mgr, cmd);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
spin_unlock_bh(&cmd_mgr->lock);
|
|
|
|
if (!found)
|
|
cmd_mgr_run_callback(rwnx_hw, NULL, msg, cb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static void cmd_mgr_print(struct rwnx_cmd_mgr *cmd_mgr)
|
|
{
|
|
struct rwnx_cmd *cur;
|
|
|
|
spin_lock_bh(&cmd_mgr->lock);
|
|
RWNX_DBG("q_sz/max: %2d / %2d - next tkn: %d\n",
|
|
cmd_mgr->queue_sz, cmd_mgr->max_queue_sz,
|
|
cmd_mgr->next_tkn);
|
|
list_for_each_entry(cur, &cmd_mgr->cmds, list) {
|
|
cmd_dump(cur);
|
|
}
|
|
spin_unlock_bh(&cmd_mgr->lock);
|
|
}
|
|
|
|
static void cmd_mgr_drain(struct rwnx_cmd_mgr *cmd_mgr)
|
|
{
|
|
struct rwnx_cmd *cur, *nxt;
|
|
|
|
RWNX_DBG(RWNX_FN_ENTRY_STR);
|
|
|
|
spin_lock_bh(&cmd_mgr->lock);
|
|
list_for_each_entry_safe(cur, nxt, &cmd_mgr->cmds, list) {
|
|
list_del(&cur->list);
|
|
cmd_mgr->queue_sz--;
|
|
if (!(cur->flags & RWNX_CMD_FLAG_NONBLOCK))
|
|
complete(&cur->complete);
|
|
}
|
|
spin_unlock_bh(&cmd_mgr->lock);
|
|
}
|
|
|
|
void rwnx_cmd_mgr_init(struct rwnx_cmd_mgr *cmd_mgr)
|
|
{
|
|
RWNX_DBG(RWNX_FN_ENTRY_STR);
|
|
|
|
INIT_LIST_HEAD(&cmd_mgr->cmds);
|
|
cmd_mgr->state = RWNX_CMD_MGR_STATE_INITED;
|
|
spin_lock_init(&cmd_mgr->lock);
|
|
cmd_mgr->max_queue_sz = RWNX_CMD_MAX_QUEUED;
|
|
cmd_mgr->queue = &cmd_mgr_queue;
|
|
cmd_mgr->print = &cmd_mgr_print;
|
|
cmd_mgr->drain = &cmd_mgr_drain;
|
|
cmd_mgr->llind = &cmd_mgr_llind;
|
|
cmd_mgr->msgind = &cmd_mgr_msgind;
|
|
|
|
INIT_WORK(&cmd_mgr->cmdWork, cmd_mgr_task_process);
|
|
cmd_mgr->cmd_wq = create_singlethread_workqueue("cmd_wq");
|
|
if (!cmd_mgr->cmd_wq) {
|
|
txrx_err("insufficient memory to create cmd workqueue.\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
void rwnx_cmd_mgr_deinit(struct rwnx_cmd_mgr *cmd_mgr)
|
|
{
|
|
cmd_mgr->print(cmd_mgr);
|
|
cmd_mgr->drain(cmd_mgr);
|
|
cmd_mgr->print(cmd_mgr);
|
|
flush_workqueue(cmd_mgr->cmd_wq);
|
|
destroy_workqueue(cmd_mgr->cmd_wq);
|
|
memset(cmd_mgr, 0, sizeof(*cmd_mgr));
|
|
}
|
|
|
|
void aicwf_set_cmd_tx(void *dev, struct lmac_msg *msg, uint len)
|
|
{
|
|
u8 *buffer = NULL;
|
|
u16 index = 0;
|
|
#ifdef AICWF_SDIO_SUPPORT
|
|
struct aic_sdio_dev *sdiodev = (struct aic_sdio_dev *)dev;
|
|
struct aicwf_bus *bus = sdiodev->bus_if;
|
|
#else
|
|
struct aic_usb_dev *usbdev = (struct aic_usb_dev *)dev;
|
|
struct aicwf_bus *bus = NULL;
|
|
if (!usbdev->state) {
|
|
printk("down msg \n");
|
|
return;
|
|
}
|
|
bus = usbdev->bus_if;
|
|
#endif
|
|
buffer = bus->cmd_buf;
|
|
|
|
memset(buffer, 0, CMD_BUF_MAX);
|
|
buffer[0] = (len+4) & 0x00ff;
|
|
buffer[1] = ((len+4) >> 8) &0x0f;
|
|
buffer[2] = 0x11;
|
|
if (sdiodev->chipid == PRODUCT_ID_AIC8801 || sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
|
|
sdiodev->chipid == PRODUCT_ID_AIC8800DW)
|
|
buffer[3] = 0x0;
|
|
else if (sdiodev->chipid == PRODUCT_ID_AIC8800D80)
|
|
buffer[3] = crc8_ponl_107(&buffer[0], 3); // crc8
|
|
index += 4;
|
|
//there is a dummy word
|
|
index += 4;
|
|
|
|
//make sure little endian
|
|
put_u16(&buffer[index], msg->id);
|
|
index += 2;
|
|
put_u16(&buffer[index], msg->dest_id);
|
|
index += 2;
|
|
put_u16(&buffer[index], msg->src_id);
|
|
index += 2;
|
|
put_u16(&buffer[index], msg->param_len);
|
|
index += 2;
|
|
memcpy(&buffer[index], (u8 *)msg->param, msg->param_len);
|
|
|
|
aicwf_bus_txmsg(bus, buffer, len + 8);
|
|
}
|
|
|