blob: dff086c379fa6b53ff2822918ee14f453c4a2869 [file] [log] [blame]
/*
* Copyright (c) 2013 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <adf_os_stdtypes.h>
#include <adf_os_types.h>
#include <adf_os_util.h>
#include <adf_os_module.h>
#include <adf_os_lock.h>
#include <adf_nbuf.h>
#include <adf_net.h>
#include <adf_net_types.h>
#include <athdefs.h>
#include <a_types.h>
#include <hif.h>
#include <linux/proc_fs.h>
typedef struct __loop_sc{
adf_net_handle_t net_handle;
a_uint8_t tx_pipe;
a_uint8_t rx_pipe;
a_uint32_t next_idx;/*next softc index*/
adf_os_spinlock_t lock_irq;
adf_net_dev_info_t dev_info;
}__loop_sc_t;
#define TEST_SEND_PKT 0
#define TEST_PIPE_NUM 0
#define TEST_DEFAULT_PIPE 0
#define TEST_MAX_DEVS 1
#define HIF_PCI_PIPE_RX0 0
#define HIF_PCI_PIPE_RX1 1
#define HIF_PCI_PIPE_TX0 0
#define HIF_PCI_PIPE_TX1 1
__loop_sc_t loop_sc[TEST_MAX_DEVS] = {
{ /* Pipe 0*/
// .next_idx = 1,
.next_idx = 0,
.dev_info = { .if_name = "ld" , .dev_addr = "\0SNUL0"},
.tx_pipe = HIF_PCI_PIPE_TX0,
.rx_pipe = HIF_PCI_PIPE_RX0,
}
// Pipe 1
/* {
.next_idx = 0,
.dev_info = { .if_name = "ld" , .dev_addr = "\0SNUL1"},
.tx_pipe = HIF_PCI_PIPE_TX0,
.rx_pipe = HIF_PCI_PIPE_RX0,
}
*/
};
extern a_status_t hif_boot_start(HIF_HANDLE hdl);
a_uint8_t loop_pipe = TEST_PIPE_NUM;
HIF_HANDLE hif_handle = NULL;
unsigned long total_rx_pkt;
unsigned long total_tx_pkt;
unsigned long total_valid_pkt;
static struct proc_dir_entry *pci_proc;
/* Test packet formation */
/**
* @brief Function to display the counter values via cat /proc/pci_loop
*
*/
static int ath_pci_proc_display(char *buf, char **start, off_t offset,
int count, int *eof, void *data)
{
return sprintf(buf,
"Total rx packet = %li\n"
"Total valid rx pkt = %li\n"
"Total tx packet = %li\n",
total_rx_pkt, total_valid_pkt, total_tx_pkt);
}
/******************************* DUMMY CALLS **********************/
a_status_t
loop_cmd(adf_drv_handle_t hdl, adf_net_cmd_t cmd,
adf_net_cmd_data_t *data)
{
return A_STATUS_OK;
}
a_status_t
loop_tx_timeout(adf_drv_handle_t hdl)
{
return A_STATUS_OK;
}
/**
* @brief Test Receive Function
*
* @param context
* @param buf
* @param pipe
*
* @return A_STATUS
*/
hif_status_t
loop_recv(void *context, adf_nbuf_t buf, a_uint8_t pipe)
{
a_uint32_t len = 0;
__loop_sc_t *sc = NULL;
/**
* Assert if the pipe != 0 or 1
*/
total_rx_pkt++;
adf_os_assert(pipe == 0 || pipe == 1);
len = adf_nbuf_len(buf);
if (len == 0) {
printk("Invalid packet of len 0\n");
return HIF_ERROR;
}
sc = &loop_sc[0];
adf_net_indicate_packet(sc->net_handle, buf, len);
return HIF_OK;
}
/**
* @brief Function to transmit the packet
*
* @param adf handle
* @param buf
*
* @return A_STATUS
*/
a_status_t
loop_tx(adf_drv_handle_t hdl, adf_nbuf_t buf)
{
__loop_sc_t *sc = (__loop_sc_t *)hdl;
unsigned long flags = 0;
hif_status_t status;
total_tx_pkt++;
adf_os_spin_lock_irq(&sc->lock_irq, flags);
status = HIFSend(hif_handle, sc->tx_pipe, NULL, buf);
if (status != HIF_OK) {
adf_net_stop_queue(sc->net_handle);
adf_os_spin_unlock_irq(&sc->lock_irq, flags);
return A_STATUS_ENOMEM;
}
adf_os_spin_unlock_irq(&sc->lock_irq, flags);
return A_STATUS_OK;
}
/**
* @brief Function called via transmit done interrupt
*
* @param context information
* @param buf
*
* @return A_STATUS
*/
hif_status_t
loop_xmit_done(void *context, adf_nbuf_t buf)
{
__loop_sc_t *sc;
sc = context;
adf_nbuf_free(buf);
if (adf_net_queue_stopped(sc->net_handle)) {
adf_net_wake_queue(sc->net_handle);
}
return HIF_OK;
}
/******************************* Open & Close *************************/
a_status_t
loop_open(adf_drv_handle_t hdl)
{
__loop_sc_t *sc = (__loop_sc_t *)hdl;
adf_net_start_queue(sc->net_handle);
// HIFStart(hif_handle);
return A_STATUS_OK;
}
void
loop_close(adf_drv_handle_t hdl)
{
__loop_sc_t *sc = (__loop_sc_t *)hdl;
adf_net_stop_queue(sc->net_handle);
}
/*************************** Device Inserted & Removed handler ********/
A_STATUS
loop_create_dev(a_uint8_t dev_num)
{
__loop_sc_t *sc = &loop_sc[dev_num];
adf_dev_sw_t sw = {0};
sw.drv_open = loop_open;
sw.drv_close = loop_close;
sw.drv_cmd = loop_cmd;
sw.drv_tx = loop_tx;
sw.drv_tx_timeout = loop_tx_timeout;
sc->net_handle = adf_net_dev_create(sc, &sw, &sc->dev_info);
adf_net_ifname(sc->net_handle);
if ( !sc->net_handle ) {
adf_os_print("Failed to create netdev\n");
return A_NO_MEMORY;
}
adf_os_spinlock_init(&sc->lock_irq);
return A_OK;
}
hif_status_t
loop_insert_dev(HIF_HANDLE hif, adf_os_handle_t os_hdl)
{
HTC_CALLBACKS pkt_cb = {0};
A_STATUS ret = A_OK;
a_uint8_t i = 0;
for (i = 0; i < TEST_MAX_DEVS; i++) {
if((ret = loop_create_dev(i)) != A_OK)
return ret;
}
hif_handle = hif;
pkt_cb.Context = &loop_sc;
pkt_cb.rxCompletionHandler = loop_recv;
pkt_cb.txCompletionHandler = loop_xmit_done;
HIFPostInit(hif_handle, NULL, &pkt_cb);
hif_boot_start(hif);
// HIFStart(hif_handle);
// adf_os_print("HIF start now.... \n");
return HIF_OK;
}
hif_status_t
loop_remove_dev(void *ctx)
{
a_uint8_t i = 0;
HIFShutDown(hif_handle);
for( i = 0; i < TEST_MAX_DEVS; i++)
adf_net_dev_delete(loop_sc[i].net_handle);
return HIF_OK;
}
/************************** Module Init & Exit ********************/
int
loop_init(void)
{
HTC_DRVREG_CALLBACKS cb;
cb.deviceInsertedHandler = loop_insert_dev;
cb.deviceRemovedHandler = loop_remove_dev;
create_proc_read_entry("pci_loop", 0, pci_proc,
ath_pci_proc_display, NULL);
HIF_register(&cb);
return 0;
}
void
loop_exit(void)
{
loop_remove_dev(NULL);
}
adf_os_export_symbol(loop_open);
adf_os_export_symbol(loop_close);
adf_os_export_symbol(loop_tx);
adf_os_export_symbol(loop_cmd);
adf_os_export_symbol(loop_tx_timeout);
adf_os_virt_module_init(loop_init);
adf_os_virt_module_exit(loop_exit);