blob: e150e27f4c8b1b6eba4e88acef2b912de5edee8d [file] [log] [blame]
/*
* Copyright (c) 2009 Mindspeed Technologies, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
*/
#include "module_wifi.h"
#include "fpp_globals.h"
#include "events.h"
#include "module_Rx.h"
#include "module_tx.h"
#include "gemac.h"
#include "xdma.h"
#include "fpool.h"
#include "fpp.h"
#include "module_ethernet.h"
#include "module_hidrv.h"
#include "module_timer.h"
#include "module_expt.h"
#include "module_pppoe.h"
#include "module_stat.h"
#include "fe.h"
#include "channels.h"
//#include "scc.h"
#include "module_ipv4.h"
#include "module_bridge.h"
#include "control_bridge.h"
#ifdef CFG_WIFI_OFFLOAD
struct tWifiIfDesc wifiDesc[MAX_WIFI_VAPS];
struct tRX_wifi_context gWifiRxCtx;
#if defined(COMCERTO_2000_CONTROL) || defined(COMCERTO_1000)
static int wifi_vap_entry( U16 *ptr, U16 len )
{
struct wifiCmd cmd;
struct tRX_wifi_context *rxc;
int id;
int portid;
struct physical_port *port;
rxc = &gWifiRxCtx;
printk("%s:%d\n", __func__, __LINE__);
if (len != sizeof(struct wifiCmd))
return ERR_WRONG_COMMAND_SIZE;
SFL_memcpy( &cmd, ptr, sizeof(struct wifiCmd));
if( cmd.VAPID >= MAX_WIFI_VAPS )
return ERR_UNKNOWN_ACTION;
portid = PORT_WIFI_IDX + cmd.VAPID;
port = phy_port_get(portid);
switch (cmd.action)
{
case WIFI_REMOVE_VAP:
printk("%s:%d Remove entry\n", __func__, __LINE__);
if( wifiDesc[cmd.VAPID].VAPID == 0XFFFF )
return ERR_WLAN_DUPLICATE_OPERATION;
printk("%s: PHYID:%d vapid:%d\n", __func__, portid, cmd.VAPID);
wifiDesc[cmd.VAPID].VAPID = 0xFFFF;
bridge_interface_deregister(portid);
remove_onif_by_index(port->itf.index);
if ( rxc->users )
rxc->users--;
break;
case WIFI_ADD_VAP:
if ( rxc->users >= MAX_WIFI_VAPS )
return CMD_ERR;
printk("%s:%d ADD entry\n", __func__, __LINE__);
if( wifiDesc[cmd.VAPID].VAPID != 0XFFFF )
return ERR_WLAN_DUPLICATE_OPERATION;
if(!add_onif(cmd.ifname, &port->itf, NULL, IF_TYPE_WLAN | IF_TYPE_PHYSICAL))
{
return CMD_ERR;
}
wifiDesc[cmd.VAPID].VAPID = cmd.VAPID;
bridge_interface_register(cmd.ifname, portid);
SFL_memcpy(port->mac_addr, cmd.mac_addr, 6);
#if defined(COMCERTO_2000_CONTROL)
printk("%s: PHYID:%d vapid:%d mac_addr: %02x:%02x:%02x:%02x:%02x:%02x\n", __func__, portid, cmd.VAPID,
port->mac_addr[0],
port->mac_addr[1],
port->mac_addr[2],
port->mac_addr[3],
port->mac_addr[4],
port->mac_addr[5]);
if( portid < MAX_PHY_PORTS_FAST) {
for (id = CLASS0_ID; id <= CLASS_MAX_ID; id++) {
pe_dmem_memcpy_to32(id, virt_to_class_dmem(&port->mac_addr),
port->mac_addr, 6);
pe_dmem_writeb(id, port->itf.index, virt_to_class_dmem(&port->itf.index));
}
} else {
class_pe_lmem_memcpy_to32(virt_to_class_pe_lmem(&port->mac_addr),
&port->mac_addr[0], 6);
class_bus_writeb(port->itf.index, virt_to_class_pe_lmem(&port->itf.index));
}
#endif
if ( rxc->users < MAX_WIFI_VAPS )
rxc->users++;
break;
case WIFI_UPDATE_VAP:
printk("%s:%d Update Entry\n", __func__, __LINE__);
if( wifiDesc[cmd.VAPID].VAPID == 0XFFFF )
return CMD_ERR;
SFL_memcpy(port->mac_addr, cmd.mac_addr, 6);
#if defined(COMCERTO_2000_CONTROL)
printk("%s: PHYID:%d vapid:%d mac_addr: %02x:%02x:%02x:%02x:%02x:%02x\n", __func__, portid, cmd.VAPID,
port->mac_addr[0],
port->mac_addr[1],
port->mac_addr[2],
port->mac_addr[3],
port->mac_addr[4],
port->mac_addr[5]);
if( portid < MAX_PHY_PORTS_FAST) {
for (id = CLASS0_ID; id <= CLASS_MAX_ID; id++)
pe_dmem_memcpy_to32(id, virt_to_class_dmem(&port->mac_addr),
port->mac_addr, 6);
} else {
class_pe_lmem_memcpy_to32(virt_to_class_pe_lmem(&port->mac_addr),
&port->mac_addr[0], 6);
}
#endif
break;
default:
return ERR_UNKNOWN_ACTION;
}
return NO_ERR;
}
static U16 M_wifi_rx_cmdproc(U16 cmd_code, U16 cmd_len, U16 *pcmd)
{
U16 acklen;
U16 ackstatus;
U16 i;
struct tRX_wifi_context *rxc;
struct physical_port *port;
rxc = &gWifiRxCtx;
acklen = 2;
ackstatus = CMD_OK;
printk("%s:%d\n", __func__, __LINE__);
switch (cmd_code)
{
case CMD_WIFI_VAP_ENTRY:
ackstatus = wifi_vap_entry(pcmd, cmd_len);
break;
#if defined(COMCERTO_1000)
case CMD_CFG_WIFI_OFFLOAD:
if( !rxc->enabled )
{
M_expt_rx_enable(PORT_WIFI_IDX);
M_wifi_rx_enable();
}
else
ackstatus = CMD_ERR;
break;
case CMD_WIFI_DISABLE:
if( rxc->enabled )
{
M_expt_rx_disable(PORT_WIFI_IDX);
M_wifi_rx_flush();
M_wifi_rx_disable();
}
else
ackstatus = CMD_ERR;
break;
#endif
case CMD_WIFI_VAP_QUERY: {
wifi_vap_query_response_t *vaps;
vaps = (wifi_vap_query_response_t *)pcmd;
printk("%s:%d\n", __func__, __LINE__);
for (i = 0; i < MAX_WIFI_VAPS; i++)
{
vaps[i].vap_id = wifiDesc[i].VAPID;
if( vaps[i].vap_id != 0xFFFF )
vaps[i].phy_port_id = PORT_WIFI_IDX + i;
port = phy_port_get(PORT_WIFI_IDX + i);
SFL_memcpy(vaps[i].ifname, get_onif_name(port->itf.index), 12);
}
acklen += ( MAX_WIFI_VAPS * sizeof(wifi_vap_query_response_t));
break;
}
case CMD_WIFI_VAP_RESET:
printk("%s:%d\n", __func__, __LINE__);
for (i = 0; i < MAX_WIFI_VAPS; i++)
{
if( wifiDesc[i].VAPID != 0XFFFF )
{
wifiDesc[i].VAPID = 0xFFFF;
port = phy_port_get(PORT_WIFI_IDX + i);
remove_onif_by_index(port->itf.index);
if ( rxc->users )
rxc->users--;
}
}
break;
default:
ackstatus = CMD_ERR;
break;
}
*pcmd = ackstatus;
return acklen;
}
int onif_is_wifi( POnifDesc pOnif )
{
int i;
PWifiIfDesc pWifi;
for( i = 0; i < MAX_WIFI_VAPS; i++ )
{
pWifi = (PWifiIfDesc)&wifiDesc[i];
if( ( pWifi->VAPID != 0xFFFF ) && ((void *)pWifi == pOnif->itf) )
{
return 1;
}
}
return 0;
}
#endif
#if defined(COMCERTO_1000)
// M_wifi_rx_enable
// Enables WiFi fast path queue from ACP->FPP
static void M_wifi_rx_enable(void)
{
struct tRX_wifi_context *rxc;
DISABLE_INTERRUPTS();
rxc = &gWifiRxCtx;
rxc->rxToCleanIndex = 0;
HAL_arm1_fiq_enable_1(rxc->PKTTX_irqm);
// program expt path ring buffer base address
rxc->rxRing_baseaddr = *(U32*)(rxc->SMI_baseaddr + FPP_SMI_WIFI_RXBASE);
rxc->enabled = 1;
//Initialize Rx Offset value, this used by VWD
*(U32 *)(rxc->SMI_baseaddr + FPP_SMI_WIFI_RX_OFFSET) = BaseOffset;
ENABLE_INTERRUPTS();
return ;
}
// M_wifi_rx_disable
// Disables WiFi fast path queue from ACP->FPP
static void M_wifi_rx_disable(void)
{
struct tRX_wifi_context *rxc;
DISABLE_INTERRUPTS();
rxc = &gWifiRxCtx;
HAL_arm1_fiq_disable_1(rxc->PKTTX_irqm);
rxc->enabled = 0;
ENABLE_INTERRUPTS();
return ;
}
static void M_wifi_rx_flush(void)
{
struct tRX_wifi_context *rxc;
struct tWiFiRXdesc ThisRXdesc;
U8* bptr;
U16 boffset;
U16 rtc;
rxc = &gWifiRxCtx;
rtc = rxc->rxToCleanIndex;
if ((*(V32*)(rxc->SMI_baseaddr + FPP_SMI_CTRL ) & WIFI_RX_EN) == 0)
return;
DISABLE_INTERRUPTS();
L1_dc_linefill_disable();
// something from wifi
*((U64*) &ThisRXdesc) = Read64((U64*)(rxc->rxRing_baseaddr + sizeof(struct tWiFiRXdesc)*rtc));
L1_dc_linefill_enable();
ENABLE_INTERRUPTS();
while ((ThisRXdesc.rx_status0 & WIFIRX_USED_MASK) == 0) {
bptr = (U8*)ThisRXdesc.rx_data;
boffset = (ThisRXdesc.rx_status0 & WIFIRX_OFFSET_MASK) >> WIFIRX_OFFSET_SHIFT;
buffer_put(bptr - boffset, POOLB, 0);
COUNTER_INCREMENT(packets_dropped_poolB);
if (ThisRXdesc.rx_status0 & WIFIRX_WRAP)
rtc = 0;
else
rtc++;
DISABLE_INTERRUPTS();
L1_dc_linefill_disable();
*((U64*) &ThisRXdesc) = Read64((U64*)(rxc->rxRing_baseaddr + sizeof(struct tWiFiRXdesc)*rtc));
L1_dc_linefill_enable();
ENABLE_INTERRUPTS();
}
// re-enable interrupt
DISABLE_INTERRUPTS();
HAL_arm1_fiq_enable_1(rxc->PKTTX_irqm);
ENABLE_INTERRUPTS();
rxc->rxToCleanIndex = rtc;
}
#endif
void M_wifi_init_rx(void)
{
int i;
struct physical_port *port;
#if defined(COMCERTO_1000)
struct tRX_wifi_context *rxc;
rxc = &gWifiRxCtx;
rxc->SMI_baseaddr = SMI_WIFI_BASE; //Shared memory base address
rxc->PKTTX_irqm = IRQM_CSPVWDTX;
rxc->PKTRX_irqm = IRQM_CSPVWDRX;
set_event_handler(EVENT_PKT_WIFIRX, M_wifi_rx_entry);
#endif
set_cmd_handler(EVENT_PKT_WIFIRX, M_wifi_rx_cmdproc);
for ( i = 0; i < MAX_WIFI_VAPS; i++ )
{
wifiDesc[i].VAPID = 0xFFFF;
#if defined(COMCERTO_1000)
tx_channel[PORT_WIFI_IDX + i] = EVENT_EXPT;
#endif
port = phy_port_get(PORT_WIFI_IDX + i);
port->id = PORT_WIFI_IDX + i;
}
}
int wifi_init(void)
{
M_wifi_init_rx();
return 0;
}
void wifi_exit(void)
{
}
#endif /* CFG_WIFI_OFFLOAD */