1613 lines
44 KiB
C
1613 lines
44 KiB
C
/*
|
|
* Copyright (c) 2021 Max Thomas
|
|
* Copyright (c) 2015-2016, Daz Jones
|
|
* This file is part of DSiWifi and is distributed under the MIT license.
|
|
* See dsiwifi_license.txt for terms of use.
|
|
*/
|
|
|
|
#include "wifi_card.h"
|
|
|
|
#include "utils.h"
|
|
#include "wifi_debug.h"
|
|
|
|
#include "wifi_gpio.h"
|
|
#include "wifi_ndma.h"
|
|
//#include "pdn.h" // TODO
|
|
//#include "mcu.h" // TODO
|
|
//#include "irq.h"
|
|
//#include "timer.h" // TODO
|
|
//#include "spi.h" // TODO
|
|
//#include "3ds.h" // TODO
|
|
|
|
//#include "rpc.h" // TODO?
|
|
|
|
#if 0
|
|
#include "atheros_bins/nwm_ar6014_softap.h"
|
|
#include "atheros_bins/ar6014-parta.h"
|
|
#include "atheros_bins/ar6014-partb.h"
|
|
#include "atheros_bins/ar6014-partc.h"
|
|
#include "atheros_bins/ar6014-partd.h"
|
|
#endif
|
|
|
|
#include "ath/wmi.h"
|
|
#include "ath/bmi.h"
|
|
#include "ath/htc.h"
|
|
#include "ath/mbox.h"
|
|
#include "ieee/wpa.h"
|
|
|
|
#include <nds.h>
|
|
#include <nds/interrupts.h>
|
|
#include <nds/arm7/clock.h>
|
|
#include <nds/arm7/serial.h>
|
|
#include <malloc.h>
|
|
|
|
#include "dsiwifi_cmds.h"
|
|
|
|
wifi_card_ctx wlan_ctx = {0};
|
|
int wifi_card_wlan_init();
|
|
|
|
static u8* mbox_out_buffer;
|
|
static u8* mbox_buffer;
|
|
|
|
static int ip_data_out_buf_idx = 0;
|
|
static u8* ip_data_out_buf = NULL;
|
|
static u32 ip_data_out_buf_totlen = 0;
|
|
|
|
#define ID_AR6002 (0x02000271)
|
|
#define ID_AR601x (0x02010271)
|
|
|
|
#define CHIP_ID_AR6002 (0x02000001)
|
|
#define CHIP_ID_AR6013 (0x0D000000)
|
|
#define CHIP_ID_AR6014 (0x0D000001)
|
|
|
|
#define AR6002_HOST_INTEREST_ADDRESS (0x00500400)
|
|
#define AR601x_HOST_INTEREST_ADDRESS (0x00520000)
|
|
|
|
#define SDIO_TICK_INTERVAL_MS (1)
|
|
#define MBOX_TMPBUF_SIZE (0x600)
|
|
#define DATA_BUF_LEN (0x600)
|
|
|
|
// Uncomment to send all data bytewise, helpful for debugging.
|
|
//#define SDIO_NO_BLOCKRW // MelonDS needs this uncommented... hangs on wifi_ndma_wait after write
|
|
|
|
#define WRITE_FOUT_1 0x72
|
|
#define READ_FOUT_1 0x73
|
|
#define WRITE_FOUT_2 0x74
|
|
#define READ_FOUT_2 0x75
|
|
|
|
#define NVRAM_ADDR_WIFICFG (0x1F400)
|
|
|
|
#define IRQ_WIFI_SDIO_CARDIRQ BIT(11)
|
|
|
|
// Collected device info during init
|
|
static wifi_card_ctx* device_curctx;
|
|
static u32 device_chip_id;
|
|
static u32 device_manufacturer;
|
|
static u32 device_host_interest_addr;
|
|
static u32 device_eeprom_addr;
|
|
static u32 device_eeprom_version;
|
|
|
|
static bool wifi_card_bInitted = false;
|
|
|
|
static u32 __attribute((aligned(16))) wifi_card_alignedbuf_small[4];
|
|
|
|
nvram_cfg_wep wifi_card_nvram_wep_configs[3];
|
|
nvram_cfg wifi_card_nvram_configs[3];
|
|
|
|
int wifi_printf_allowed = 0;
|
|
|
|
// CMD52 - IO_RW_DIRECT (read/write single register).
|
|
static const wifi_sdio_command cmd52 = {
|
|
.cmd = 52,
|
|
.response_type = wifi_sdio_resp_48bit,
|
|
};
|
|
|
|
// CMD53 - IO_RW_EXTENDED
|
|
static const wifi_sdio_command cmd53_read = {
|
|
.cmd = 53,
|
|
.command_type = 0,
|
|
.response_type = wifi_sdio_resp_48bit,
|
|
|
|
.data_transfer = true,
|
|
.data_direction = wifi_sdio_data_read,
|
|
.data_length = wifi_sdio_multiple_block,
|
|
.secure = true,
|
|
};
|
|
|
|
static const wifi_sdio_command cmd53_read_single = {
|
|
.cmd = 53,
|
|
.command_type = 0,
|
|
.response_type = wifi_sdio_resp_48bit,
|
|
|
|
.data_transfer = true,
|
|
.data_direction = wifi_sdio_data_read,
|
|
.data_length = wifi_sdio_single_block,
|
|
.secure = true,
|
|
};
|
|
|
|
static const wifi_sdio_command cmd53_write = {
|
|
.cmd = 53,
|
|
.command_type = 0,
|
|
.response_type = wifi_sdio_resp_48bit,
|
|
|
|
|
|
.data_transfer = true,
|
|
.data_direction = wifi_sdio_data_write,
|
|
.data_length = wifi_sdio_multiple_block,
|
|
.secure = true,
|
|
};
|
|
|
|
static const wifi_sdio_command cmd53_write_single = {
|
|
.cmd = 53,
|
|
.command_type = 0,
|
|
.response_type = wifi_sdio_resp_48bit,
|
|
|
|
|
|
.data_transfer = true,
|
|
.data_direction = wifi_sdio_data_write,
|
|
.data_length = wifi_sdio_single_block,
|
|
.secure = true,
|
|
};
|
|
|
|
|
|
// Device info
|
|
|
|
const char* wifi_card_get_chip_str()
|
|
{
|
|
switch (device_chip_id)
|
|
{
|
|
case CHIP_ID_AR6013:
|
|
return "AR6013";
|
|
case CHIP_ID_AR6014:
|
|
return "AR6014";
|
|
case CHIP_ID_AR6002:
|
|
return "AR6002";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
void wifi_card_print_mac(const char* prefix, const u8* mac)
|
|
{
|
|
if (prefix) {
|
|
wifi_printlnf("%s %02x:%02x:%02x:%02x:%02x:%02x", prefix, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|
}
|
|
else {
|
|
wifi_printlnf("%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|
}
|
|
}
|
|
|
|
// SDIO basics
|
|
|
|
int wifi_card_write_func_byte(u8 func, u32 addr, u8 val)
|
|
{
|
|
// Read register 0x02 (function enable) hibyte until it's ready
|
|
wifi_card_send_command(cmd52, BIT(31) /* write flag */ | (func << 28) | addr << 9 | val);
|
|
if(device_curctx->tmio.status & 4) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int wifi_card_read_func1_32bit(u32 addr, void* buf, u32 len)
|
|
{
|
|
if(!device_curctx) return -2;
|
|
|
|
//wifi_sdio_stop(device_curctx->tmio.controller);
|
|
|
|
device_curctx->tmio.buffer = buf;
|
|
device_curctx->tmio.size = len;
|
|
device_curctx->tmio.break_early = true;
|
|
|
|
u32 old_blocksize = device_curctx->tmio.block_size;
|
|
device_curctx->tmio.block_size = sizeof(u32);
|
|
u8 blkcnt = len;
|
|
u8 funcnum = 1;
|
|
wifi_card_send_command_alt(cmd53_read_single, (funcnum << 28) | (1 << 26) | (addr & 0x1FFFF) << 9 | (blkcnt));
|
|
|
|
//device_curctx->tmio.break_early = false;
|
|
|
|
//wifi_sdio_stop(device_curctx->tmio.controller);
|
|
|
|
device_curctx->tmio.block_size = old_blocksize;
|
|
|
|
if(device_curctx->tmio.status & 4)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int wifi_card_read_func1_block(u32 addr, void* buf, u32 len)
|
|
{
|
|
if(!device_curctx) return -2;
|
|
|
|
wifi_ndma_wait();
|
|
wifi_sdio_stop(device_curctx->tmio.controller);
|
|
|
|
device_curctx->tmio.buffer = buf;
|
|
device_curctx->tmio.size = len;
|
|
|
|
u8 blkcnt = len / device_curctx->tmio.block_size;
|
|
u8 funcnum = 1;
|
|
wifi_card_send_command_alt(cmd53_read, (funcnum << 28) | (1 << 27) | (1 << 26) | (addr & 0x1FFFF) << 9 | (blkcnt));
|
|
|
|
wifi_sdio_stop(device_curctx->tmio.controller);
|
|
wifi_ndma_wait();
|
|
|
|
if(device_curctx->tmio.status & 4)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int wifi_card_write_func1_block(u32 addr, void* buf, u32 len)
|
|
{
|
|
if(!device_curctx) return -2;
|
|
|
|
wifi_ndma_wait();
|
|
|
|
wifi_sdio_stop(device_curctx->tmio.controller);
|
|
|
|
device_curctx->tmio.buffer = buf;
|
|
device_curctx->tmio.size = len;
|
|
|
|
u8 blkcnt = len / device_curctx->tmio.block_size;
|
|
u8 funcnum = 1;
|
|
wifi_card_send_command_alt(cmd53_write, BIT(31) /* write flag */ | (funcnum << 28) | (1 << 27) | (1 << 26) | ((addr & 0x1FFFF) << 9) | (blkcnt));
|
|
|
|
wifi_ndma_wait();
|
|
wifi_sdio_stop(device_curctx->tmio.controller);
|
|
|
|
//wifi_printlnf("asdf");
|
|
|
|
if(device_curctx->tmio.status & 4)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
u8 wifi_card_read_func_byte(u8 func, u32 addr)
|
|
{
|
|
// Read register 0x02 (function enable) hibyte until it's ready
|
|
wifi_card_send_command(cmd52, (func << 28) | (addr & 0x1FFFF) << 9);
|
|
if(device_curctx->tmio.status & 4)
|
|
{
|
|
return 0xFF;
|
|
}
|
|
return (device_curctx->tmio.resp[0] & 0xFF);
|
|
}
|
|
|
|
// Func0 boilerplate
|
|
int wifi_card_write_func0_u8(u32 addr, u8 val)
|
|
{
|
|
return wifi_card_write_func_byte(0, addr, val);
|
|
}
|
|
|
|
u8 wifi_card_read_func0_u8(u32 addr)
|
|
{
|
|
return wifi_card_read_func_byte(0, addr);
|
|
}
|
|
|
|
u16 wifi_card_read_func0_u16(u32 addr)
|
|
{
|
|
return wifi_card_read_func0_u8(addr) | (wifi_card_read_func0_u8(addr+1) << 8);
|
|
}
|
|
|
|
u32 wifi_card_read_func0_u32(u32 addr)
|
|
{
|
|
return wifi_card_read_func0_u16(addr) | (wifi_card_read_func0_u16(addr+2) << 16);
|
|
}
|
|
|
|
u16 wifi_card_write_func0_u16(u32 addr, u16 val)
|
|
{
|
|
return wifi_card_write_func0_u8(addr, val & 0xFF) | (wifi_card_write_func0_u8(addr+1, val >> 8) << 8);
|
|
}
|
|
|
|
u32 wifi_card_write_func0_u32(u32 addr, u32 val)
|
|
{
|
|
return wifi_card_write_func0_u16(addr, val & 0xFFFF) | (wifi_card_write_func0_u16(addr+2, val >> 16) << 16);
|
|
}
|
|
|
|
// Func1 boilerplate
|
|
int wifi_card_write_func1_u8(u32 addr, u8 val)
|
|
{
|
|
return wifi_card_write_func_byte(1, addr, val);
|
|
}
|
|
|
|
u8 wifi_card_read_func1_u8(u32 addr)
|
|
{
|
|
return wifi_card_read_func_byte(1, addr);
|
|
}
|
|
|
|
u16 wifi_card_read_func1_u16(u32 addr)
|
|
{
|
|
return wifi_card_read_func1_u8(addr) | (wifi_card_read_func1_u8(addr+1) << 8);
|
|
}
|
|
|
|
u32 wifi_card_read_func1_u32(u32 addr)
|
|
{
|
|
return wifi_card_read_func1_u16(addr) | (wifi_card_read_func1_u16(addr+2) << 16);
|
|
}
|
|
|
|
u32 wifi_card_read_func1_u32_fast(u32 addr)
|
|
{
|
|
//return wifi_card_read_func1_u16(addr) | (wifi_card_read_func1_u16(addr+2) << 16);
|
|
wifi_card_read_func1_32bit(addr, wifi_card_alignedbuf_small, sizeof(u32));
|
|
return wifi_card_alignedbuf_small[0];
|
|
}
|
|
|
|
void wifi_card_write_func1_u16(u32 addr, u16 val)
|
|
{
|
|
// Write from MSB to LSB
|
|
wifi_card_write_func1_u8(addr+1, val >> 8);
|
|
wifi_card_write_func1_u8(addr, val & 0xFF);
|
|
}
|
|
|
|
void wifi_card_write_func1_u32(u32 addr, u32 val)
|
|
{
|
|
// Write from MSB to LSB
|
|
wifi_card_write_func1_u16(addr+2, val >> 16);
|
|
wifi_card_write_func1_u16(addr, val & 0xFFFF);
|
|
}
|
|
|
|
// Internal RAM
|
|
u32 wifi_card_read_intern_word(u32 addr)
|
|
{
|
|
wifi_card_write_func1_u32(0x0047C, addr); // WINDOW_READ_ADDR;
|
|
u32 ret = wifi_card_read_func1_u32(0x00474); // WINDOW_DATA
|
|
return ret;
|
|
}
|
|
|
|
u32 wifi_card_read_intern_word_fast(u32 addr)
|
|
{
|
|
wifi_card_write_func1_u32(0x0047C, addr); // WINDOW_READ_ADDR;
|
|
u32 ret = wifi_card_read_func1_u32_fast(0x00474); // WINDOW_DATA
|
|
return ret;
|
|
}
|
|
|
|
void wifi_card_write_intern_word(u32 addr, u32 data)
|
|
{
|
|
wifi_card_write_func1_u32(0x00474, data); // WINDOW_DATA
|
|
wifi_card_write_func1_u32(0x00478, addr); // WINDOW_WRITE_ADDR;
|
|
}
|
|
|
|
//
|
|
// MBOX
|
|
//
|
|
|
|
void wifi_card_mbox_clear(void)
|
|
{
|
|
wifi_printf("%x %x %x\n", wifi_card_read_func1_u8(F1_HOST_INT_STATUS), wifi_card_read_func1_u8(F1_MBOX_FRAME), wifi_card_read_func1_u8(F1_RX_LOOKAHEAD_VALID));
|
|
/*wifi_card_write_func1_u8(0x468, 1);
|
|
wifi_card_write_func1_u8(0x469, 1);
|
|
|
|
ioDelay(0x400000);
|
|
|
|
wifi_card_write_func1_u8(0x469, 0);*/
|
|
|
|
// Wait for start of data... At least 4 bytes valid
|
|
/*timeout = 100;
|
|
if (!(wifi_card_read_func1_u8(F1_RX_LOOKAHEAD_VALID) & 1))
|
|
{
|
|
wifi_printlnf("b");
|
|
}
|
|
while (!(wifi_card_read_func1_u8(F1_RX_LOOKAHEAD_VALID) & 1) && --timeout)
|
|
{
|
|
|
|
}
|
|
if (!timeout)
|
|
{
|
|
return 0;
|
|
}*/
|
|
|
|
while (wifi_card_read_func1_u8(F1_RX_LOOKAHEAD_VALID) & 0x1)
|
|
{
|
|
wifi_card_read_func1_u8(0xFF);
|
|
//wifi_printf("%x %x %x - %02x\n", wifi_card_read_func1_u8(F1_HOST_INT_STATUS), wifi_card_read_func1_u8(F1_MBOX_FRAME), wifi_card_read_func1_u8(F1_RX_LOOKAHEAD_VALID), val);
|
|
}
|
|
//wifi_printf("%02x\n", wifi_card_read_func1_u8(0xFF));
|
|
//wifi_printf("%02x\n", wifi_card_read_func1_u8(0xFF));
|
|
//wifi_printf("%02x\n", wifi_card_read_func1_u8(0xFF));
|
|
|
|
// Read until End of Message bit is gone (shouldn't be needed)
|
|
/*if (wifi_card_read_func1_u8(F1_MBOX_FRAME) & 0x10)
|
|
{
|
|
wifi_printlnf("c");
|
|
}
|
|
while (wifi_card_read_func1_u8(F1_MBOX_FRAME) & 0x10)
|
|
{
|
|
wifi_card_read_func1_u8(0xFF);
|
|
}*/
|
|
|
|
|
|
|
|
// Wait for start of data... At least 4 bytes valid
|
|
/*if (!(wifi_card_read_func1_u8(F1_RX_LOOKAHEAD_VALID) & 1))
|
|
{
|
|
wifi_printlnf("d");
|
|
}
|
|
while (!(wifi_card_read_func1_u8(F1_RX_LOOKAHEAD_VALID) & 1))
|
|
{
|
|
|
|
}*/
|
|
|
|
wifi_printf("leave\n");
|
|
}
|
|
|
|
// Idk what this even is, no$ wifiboot does it before every BMI cmd,
|
|
// but I suspect it's a workaround for writing too much to FIFOs w/
|
|
// block transfers
|
|
void wifi_card_bmi_wait_count4(void)
|
|
{
|
|
while (wifi_card_read_func1_u8(F1_COUNT4) == 0)
|
|
{
|
|
ioDelay(0x100);
|
|
}
|
|
}
|
|
|
|
void wifi_card_write_mbox0_u32(u32 val)
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
//while (1)
|
|
{
|
|
wifi_card_write_func1_u8(0xFF, val & 0xFF);
|
|
val >>= 8;
|
|
|
|
/*u8 intval = wifi_card_read_func1_u8(F1_ERROR_INT_STATUS);
|
|
if (!(intval & 0x01)) // tx overflow
|
|
{
|
|
break;
|
|
}
|
|
|
|
wifi_card_write_func1_u8(F1_ERROR_INT_STATUS, 0x0);*/
|
|
//break;
|
|
}
|
|
}
|
|
}
|
|
|
|
u32 wifi_card_read_mbox0_u32(void)
|
|
{
|
|
while (!(wifi_card_read_func1_u8(F1_RX_LOOKAHEAD_VALID) & 1));
|
|
|
|
u32 val = 0;
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
// It seems the entire mailbox range is the same 1-byte register?
|
|
// Maybe 0xFF sends an interrupt, but we're reading bytewise
|
|
// so just use 0xFF
|
|
val |= (wifi_card_read_func1_u8(0xFF) << i*8);
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
u32 wifi_card_read_mbox0_u32_timeout()
|
|
{
|
|
u32 timeout = 0;
|
|
while (!(wifi_card_read_func1_u8(F1_RX_LOOKAHEAD_VALID) & 1) && ++timeout < 10);
|
|
|
|
if (!(wifi_card_read_func1_u8(F1_RX_LOOKAHEAD_VALID) & 1))
|
|
return 0xFFFFFFFF;
|
|
|
|
u32 val = 0;
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
// It seems the entire mailbox range is the same 1-byte register?
|
|
// Maybe 0xFF sends an interrupt, but we're reading bytewise
|
|
// so just use 0xFF
|
|
val |= (wifi_card_read_func1_u8(0xFF) << i*8);
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
// TODO: Move this to block transfers.
|
|
// This could get tricky since we'd be unable to failsafe on TX overflows.
|
|
// Maybe it would be better to make a DMA330 driver specifically
|
|
// for this? Or just ignore overflows/find a good way to manage them.
|
|
void wifi_card_mbox0_sendbytes(const u8* data, u32 len)
|
|
{
|
|
u16 send_addr = 0x4000 - len;
|
|
|
|
#ifdef SDIO_NO_BLOCKRW
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
wifi_card_write_func1_u8(send_addr, data[i]);
|
|
send_addr++;
|
|
|
|
if (send_addr >= 0x4000)
|
|
send_addr = 0x3F80;
|
|
}
|
|
#else
|
|
wifi_card_write_func1_block(send_addr, (void*)data, len);
|
|
#endif
|
|
u32 intval = wifi_card_read_func1_u32(F1_HOST_INT_STATUS);
|
|
if (intval & 0x00010000) // tx overflow
|
|
{
|
|
wifi_printlnf("Mbox Full!: %08lx %02x", wifi_card_read_func1_u32(F1_HOST_INT_STATUS), wifi_card_read_func1_u8(F1_RX_LOOKAHEAD_VALID));
|
|
//break;
|
|
}
|
|
|
|
//wifi_card_bmi_wait_count4();
|
|
}
|
|
|
|
void wifi_card_mbox0_send_packet(u8 type, u8 ack_type, const u8* data, u16 len, u16 idk)
|
|
{
|
|
//memset(mbox_out_buffer, 0x0, round_up(len, 0x80));
|
|
|
|
mbox_out_buffer[0] = type;
|
|
mbox_out_buffer[1] = ack_type;
|
|
*(u16*)&mbox_out_buffer[2] = len;
|
|
*(u16*)&mbox_out_buffer[4] = idk;
|
|
|
|
// Truncate to mbox_out_buffer size
|
|
if (len > (MBOX_TMPBUF_SIZE - 0x6))
|
|
len = (MBOX_TMPBUF_SIZE - 0x6);
|
|
|
|
if (data)
|
|
memcpy(&mbox_out_buffer[6], data, len);
|
|
|
|
len = len + 6;
|
|
|
|
// Round to 0x80, block size
|
|
// TODO can this size be reduced?
|
|
len = round_up(len, 0x80);
|
|
|
|
//hexdump(mbox_out_buffer, 8);
|
|
|
|
wifi_card_mbox0_sendbytes(mbox_out_buffer, len); // len
|
|
}
|
|
|
|
void data_handle_pkt(u8* pkt_data, u32 len)
|
|
{
|
|
struct __attribute__((packed)) {
|
|
s16 rssi;
|
|
u8 dst_bssid[6]; // 3DS MAC
|
|
u8 src_bssid[6]; // AP MAC
|
|
u8 data_len_be[2];
|
|
u8 body[];
|
|
|
|
} *data_hdr = (void*)pkt_data;
|
|
|
|
struct __attribute__((packed)) {
|
|
u8 dsap;
|
|
u8 ssap;
|
|
u8 control;
|
|
u8 org[3];
|
|
u8 type_be[2];
|
|
u8 body[];
|
|
|
|
} *data_snap = (void*)data_hdr->body;
|
|
|
|
//u16 data_len = getbe16(data_hdr->data_len_be);
|
|
|
|
if (getbe16(data_snap->type_be) == 0x888E)
|
|
{
|
|
data_handle_auth(data_snap->body, &pkt_data[len] - data_snap->body, data_hdr->dst_bssid, data_hdr->src_bssid);
|
|
}
|
|
else
|
|
{
|
|
// TODO fragment it?
|
|
if (len > DATA_BUF_LEN - 6)
|
|
len = DATA_BUF_LEN - 6;
|
|
|
|
//memcpy(ip_data_out_buf, pkt_data, len);
|
|
|
|
//wifi_printf("%x %x\n", ip_data_out_buf, pkt_data);
|
|
|
|
Wifi_FifoMsg msg;
|
|
msg.cmd = WIFI_IPCINT_PKTDATA;
|
|
msg.pkt_data = pkt_data;//ip_data_out_buf;
|
|
msg.pkt_len = len;
|
|
fifoSendDatamsg(FIFO_DSWIFI, sizeof(msg), (u8*)&msg);
|
|
|
|
//while (*(vu32*)(pkt_data-6) != 0xF00FF00F);
|
|
//wifi_printf(""); // HACK force ARM7 to wait for ARM9 to copy packet
|
|
#if 0
|
|
data_send_to_lwip(pkt_data, len);
|
|
#endif
|
|
#if 0
|
|
wifi_card_print_mac("Dst:", data_hdr->dst_bssid);
|
|
wifi_card_print_mac("Src:", data_hdr->src_bssid);
|
|
|
|
wifi_printlnf("Data Pkt:");
|
|
|
|
u8* dump_data = data_hdr->body;
|
|
for (int i = 0; i < data_len; i += 8)
|
|
{
|
|
wifi_printlnf("%02x: %02x %02x %02x %02x %02x %02x %02x %02x", i, dump_data[i+0], dump_data[i+1], dump_data[i+2], dump_data[i+3], dump_data[i+4], dump_data[i+5], dump_data[i+6], dump_data[i+7]);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void data_send_pkt(u8* pkt_data, u32 len)
|
|
{
|
|
int lock = enterCriticalSection();
|
|
wifi_card_mbox0_send_packet(0x02, MBOXPKT_NOACK, pkt_data, len, 0); // 0x2008 causes broadcast packets?
|
|
leaveCriticalSection(lock);
|
|
}
|
|
|
|
void data_send_pkt_idk(u8* pkt_data, u32 len)
|
|
{
|
|
int lock = enterCriticalSection();
|
|
wifi_card_mbox0_send_packet(0x02, MBOXPKT_NOACK, pkt_data, len, 0x2008); // 0x2008 causes broadcast packets? and might be faster?
|
|
leaveCriticalSection(lock);
|
|
}
|
|
|
|
extern u16 wmi_idk;
|
|
void wmi_send_pkt(u16 wmi_type, u8 ack_type, const void* data, u16 len)
|
|
{
|
|
int lock = enterCriticalSection();
|
|
//memset(mbox_out_buffer, 0, round_up(len, 0x80));
|
|
memset(mbox_out_buffer, 0, 0x8);
|
|
|
|
mbox_out_buffer[0] = MBOXPKT_WMI;
|
|
mbox_out_buffer[1] = ack_type;
|
|
*(u16*)&mbox_out_buffer[2] = len+sizeof(u16);
|
|
*(u16*)&mbox_out_buffer[4] = wmi_idk;
|
|
|
|
*(u16*)&mbox_out_buffer[6+0] = wmi_type;
|
|
|
|
// Truncate to mbox_out_buffer size
|
|
if (len > (MBOX_TMPBUF_SIZE - 0x8))
|
|
len = (MBOX_TMPBUF_SIZE - 0x8);
|
|
|
|
if (data)
|
|
memcpy(&mbox_out_buffer[6+2], data, len);
|
|
|
|
len = len + 8;
|
|
|
|
// Round to 0x80, block size
|
|
// TODO can this size be reduced?
|
|
len = round_up(len, 0x80);
|
|
|
|
//hexdump(mbox_out_buffer, 20);
|
|
|
|
wifi_card_mbox0_sendbytes(mbox_out_buffer, len); // len
|
|
leaveCriticalSection(lock);
|
|
}
|
|
|
|
|
|
static bool mbox_has_lookahead = false;
|
|
static u32 mbox_lookahead = 0;
|
|
|
|
void* data_next_buf()
|
|
{
|
|
#if 0
|
|
//for (int j = 0; j < 1000000; j++)
|
|
while (1)
|
|
{
|
|
for (int i = 0; i < (ip_data_out_buf_totlen / DATA_BUF_LEN); i++)
|
|
{
|
|
void* dst = memUncached(ip_data_out_buf + (DATA_BUF_LEN * i));
|
|
if (*(vu32*)dst == 0xF00FF00F)
|
|
{
|
|
//wifi_printf("ret %u\n", i);
|
|
return dst;
|
|
}
|
|
}
|
|
//wifi_printf("arm9 loop...\n");
|
|
}
|
|
//return ip_data_out_buf;
|
|
return NULL;
|
|
#endif
|
|
|
|
#if 1
|
|
void* ret = ip_data_out_buf + (DATA_BUF_LEN * ip_data_out_buf_idx);
|
|
|
|
//wifi_printf("ret %u\n", ip_data_out_buf_idx);
|
|
|
|
ip_data_out_buf_idx = (ip_data_out_buf_idx + 1) % (ip_data_out_buf_totlen / DATA_BUF_LEN);
|
|
|
|
//memset(ret, 0, DATA_BUF_LEN);
|
|
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
u16 wifi_card_mbox0_readpkt(void)
|
|
{
|
|
//memset(mbox_buffer, 0, MBOX_TMPBUF_SIZE);
|
|
|
|
// Try and wait for mailbox data to arrive
|
|
int timeout = 100;
|
|
while (timeout--)
|
|
{
|
|
u8 sts = wifi_card_read_func1_u8(F1_HOST_INT_STATUS);
|
|
if (sts & 1) break; // RX FIFO is not empty
|
|
}
|
|
|
|
// Timed out
|
|
if (!timeout)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
u32 header = 0;
|
|
|
|
if (mbox_has_lookahead) {
|
|
header = mbox_lookahead;
|
|
mbox_has_lookahead = false;
|
|
}
|
|
else {
|
|
header = wifi_card_read_func1_u32(F1_RX_LOOKAHEAD0); // read lookahead
|
|
}
|
|
|
|
u8* read_buffer = mbox_buffer;
|
|
|
|
u8 pkt_type = header & 0xFF;
|
|
u8 ack_present = (header >> 8) & 0xFF;
|
|
u16 len = header >> 16;
|
|
u16 full_len = round_up(len+6, 0x80);
|
|
|
|
if (ip_data_out_buf && (pkt_type == 2 || pkt_type == 3 || pkt_type == 4 || pkt_type == 5)) {
|
|
//read_buffer = ip_data_out_buf + (ip_data_out_buf_idx * DATA_BUF_LEN);
|
|
//ip_data_out_buf_idx = (ip_data_out_buf_idx + 1) % (ip_data_out_buf_totlen / DATA_BUF_LEN);
|
|
read_buffer = data_next_buf();
|
|
|
|
//while (*(vu32*)ip_data_out_buf != 0xF00FF00F);
|
|
}
|
|
|
|
// On the off chance that a packet gets parsed incorrectly (full_len off-by-one, etc)
|
|
// just discard in chunks of 4 and be loud about it.
|
|
if (len > 0x2000)
|
|
{
|
|
u16 actual_len = 0;
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
if (!(wifi_card_read_func1_u8(F1_HOST_INT_STATUS) & 1)) break;
|
|
wifi_card_read_func1_u8(0xFF);
|
|
actual_len++;
|
|
}
|
|
wifi_printlnf("?? Pkt len %x %lx, %x %lx", len, header, actual_len, wifi_card_read_func1_u32(F1_RX_LOOKAHEAD0));
|
|
return 0;
|
|
}
|
|
|
|
#ifdef SDIO_NO_BLOCKRW
|
|
// Read out all data that we can
|
|
//int end_cnt = 0;
|
|
u16 actual_len = 0;
|
|
while (1) // Read until we see the EOM bit thrice
|
|
{
|
|
while (!(wifi_card_read_func1_u8(F1_HOST_INT_STATUS) & 1)); // We don't have data...
|
|
|
|
u8 val = wifi_card_read_func1_u8(0xFF); // (0x1000 - full_len)+actual_len
|
|
|
|
if (actual_len < MBOX_TMPBUF_SIZE)
|
|
read_buffer[actual_len] = val;
|
|
|
|
actual_len++;
|
|
|
|
if (actual_len >= full_len) break;
|
|
|
|
// We've reached the last few bytes of the packet
|
|
/*if (wifi_card_read_func1_u8(F1_MBOX_FRAME) & 0x10)
|
|
end_cnt++;
|
|
|
|
if (end_cnt > 3)
|
|
break;*/
|
|
}
|
|
#else
|
|
u16 actual_len = full_len;
|
|
u16 send_addr = 0x4000 - full_len;
|
|
wifi_card_read_func1_block(send_addr, read_buffer, full_len);
|
|
#endif
|
|
|
|
if (!actual_len) { return 0; }
|
|
|
|
// Short packet? Shouldn't happen.
|
|
if (actual_len < 6) {
|
|
wifi_printlnf("Packet too short?? %x", actual_len);
|
|
return actual_len;
|
|
}
|
|
|
|
#ifndef SDIO_NO_BLOCKRW
|
|
wifi_ndma_wait();
|
|
#endif
|
|
|
|
u8 ack_len = read_buffer[4];
|
|
if (!ack_present) {
|
|
// ack_len can be set to 0xFF sometimes when an ack is not present, resulting in erroneous data...!
|
|
ack_len = 0;
|
|
}
|
|
u16 len_pkt = len - ack_len;
|
|
u16 pkt_cmd = *(u16*)&read_buffer[6];
|
|
u8* pkt_data = &read_buffer[8];
|
|
u8* ack_data = &read_buffer[6 + len_pkt];
|
|
|
|
if (pkt_type == MBOXPKT_HTC)
|
|
{
|
|
htc_handle_pkt(pkt_cmd, pkt_data, len_pkt - 2, ack_len);
|
|
}
|
|
else if (pkt_type == MBOXPKT_WMI)
|
|
{
|
|
wmi_handle_pkt(pkt_cmd, pkt_data, len_pkt - 2, ack_len);
|
|
}
|
|
else if (pkt_type == 2 || pkt_type == 3 || pkt_type == 4 || pkt_type == 5) // one of my routers sends 0x04 for some reason
|
|
{
|
|
data_handle_pkt(pkt_data - 2, len_pkt);
|
|
}
|
|
else
|
|
{
|
|
wifi_printlnf("wat %02x %02x %02x %02x %02x %02x %02x %02x", read_buffer[0], read_buffer[1], read_buffer[2], read_buffer[3], read_buffer[4], read_buffer[5], read_buffer[6], read_buffer[7]);
|
|
wifi_printlnf("wat %02x %02x %02x %02x %02x %02x %02x %02x", read_buffer[8+0], read_buffer[8+1], read_buffer[8+2], read_buffer[8+3], read_buffer[8+4], read_buffer[8+5], read_buffer[8+6], read_buffer[8+7]);
|
|
}
|
|
|
|
// We can avoid costly CMD52s by using the ack block's lookahead
|
|
mbox_has_lookahead = false;
|
|
u16 ack_idx = 0;
|
|
while (ack_idx < ack_len)
|
|
{
|
|
u8 type = ack_data[ack_idx++];
|
|
u8 len = ack_data[ack_idx++];
|
|
|
|
//if (type == 1 || type == 2)
|
|
// wifi_printlnf("%x %x", type, len);
|
|
|
|
// Lookahead item
|
|
if (type == 2 && len == 6 && !mbox_has_lookahead)
|
|
{
|
|
if (ack_data[ack_idx] == 0xAA && ack_data[ack_idx+5] == 0x55)
|
|
{
|
|
mbox_has_lookahead = true;
|
|
mbox_lookahead = getle32(&ack_data[ack_idx+1]);
|
|
|
|
//hexdump(&ack_data[ack_idx], 6);
|
|
}
|
|
}
|
|
ack_idx += len;
|
|
}
|
|
|
|
if (ack_present != MBOXPKT_RETACK)
|
|
{
|
|
//wifi_printlnf("%02x %02x %04x %04x", pkt_type, ack_present, len, actual_len);
|
|
//wifi_printlnf("%02x %02x %02x %02x %02x %02x %02x %02x", read_buffer[0], read_buffer[1], read_buffer[2], read_buffer[3], read_buffer[4], read_buffer[5], read_buffer[6], read_buffer[7]);
|
|
//wifi_printlnf("%02x %02x %02x %02x %02x %02x %02x %02x", read_buffer[8+0], read_buffer[8+1], read_buffer[8+2], read_buffer[8+3], read_buffer[8+4], read_buffer[8+5], read_buffer[8+6], read_buffer[8+7]);
|
|
}
|
|
|
|
|
|
return len;
|
|
}
|
|
|
|
//
|
|
// BMI
|
|
//
|
|
|
|
void wifi_card_bmi_start_firmware(void)
|
|
{
|
|
//wifi_card_bmi_wait_count4();
|
|
wifi_card_write_mbox0_u32(BMI_DONE);
|
|
}
|
|
|
|
void wifi_card_bmi_cmd_read_memory(u32 addr, u32 len, u8* out)
|
|
{
|
|
// Possibly faster, does the same thing.
|
|
if (len == 4)
|
|
{
|
|
*(u32*)&out[0] = wifi_card_read_intern_word_fast(addr);
|
|
return;
|
|
}
|
|
|
|
wifi_card_write_mbox0_u32(BMI_READ_MEMORY);
|
|
wifi_card_write_mbox0_u32(addr);
|
|
wifi_card_write_mbox0_u32(len + (len % 4));
|
|
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
out[i] = wifi_card_read_func1_u8(0xFF);
|
|
}
|
|
|
|
// Data must be u32 aligned
|
|
for (int i = 0; i < len % 4; i++)
|
|
{
|
|
wifi_card_read_func1_u8(0xFF);
|
|
}
|
|
}
|
|
|
|
void wifi_card_bmi_cmd_write_memory(u32 addr, u8* data, u32 len)
|
|
{
|
|
// Possibly faster, does the same thing.
|
|
if (len == 4)
|
|
{
|
|
wifi_card_write_intern_word(addr, *(u32*)&data[0]);
|
|
return;
|
|
}
|
|
|
|
wifi_card_write_mbox0_u32(BMI_WRITE_MEMORY);
|
|
wifi_card_write_mbox0_u32(addr);
|
|
wifi_card_write_mbox0_u32(len + (len % 4));
|
|
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
wifi_card_write_func1_u8(0xFF, data[i]);
|
|
}
|
|
|
|
for (int i = 0; i < len % 4; i++)
|
|
{
|
|
wifi_card_write_func1_u8(0xFF, 0);
|
|
}
|
|
}
|
|
|
|
u32 wifi_card_bmi_execute(u32 addr, u32 arg)
|
|
{
|
|
wifi_card_write_mbox0_u32(BMI_EXECUTE);
|
|
wifi_card_write_mbox0_u32(addr);
|
|
wifi_card_write_mbox0_u32(arg);
|
|
|
|
return wifi_card_read_mbox0_u32();
|
|
}
|
|
|
|
u32 wifi_card_bmi_read_register(u32 addr)
|
|
{
|
|
wifi_card_write_mbox0_u32(BMI_READ_SOC_REGISTER);
|
|
wifi_card_write_mbox0_u32(addr);
|
|
|
|
return wifi_card_read_mbox0_u32();
|
|
}
|
|
|
|
void wifi_card_bmi_write_register(u32 addr, u32 val)
|
|
{
|
|
wifi_card_write_mbox0_u32(BMI_WRITE_SOC_REGISTER);
|
|
wifi_card_write_mbox0_u32(addr);
|
|
wifi_card_write_mbox0_u32(val);
|
|
}
|
|
|
|
u32 wifi_card_bmi_get_version()
|
|
{
|
|
wifi_card_write_mbox0_u32(BMI_GET_TARGET_ID);
|
|
|
|
u32 ret = wifi_card_read_mbox0_u32();
|
|
if (ret == 0xFFFFFFFF) // Extended
|
|
{
|
|
u32 len = wifi_card_read_mbox0_u32(); // len
|
|
if (len != 0xFFFFFFFF)
|
|
{
|
|
ret = wifi_card_read_mbox0_u32(); // version
|
|
for (int i = 0; i < (len/4)-2; i++)
|
|
{
|
|
wifi_card_read_mbox0_u32();
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void wifi_card_bmi_lz_start(u32 addr)
|
|
{
|
|
wifi_card_write_mbox0_u32(BMI_LZ_STREAM_START);
|
|
wifi_card_write_mbox0_u32(addr);
|
|
}
|
|
|
|
void wifi_card_bmi_lz_data(const u8* data, u32 len)
|
|
{
|
|
wifi_card_write_mbox0_u32(BMI_LZ_DATA);
|
|
wifi_card_write_mbox0_u32(len + len % 4);
|
|
|
|
//u32 len_aligned = len + len % 4;
|
|
u32 len_bulk = len - (len % 0x80);
|
|
|
|
wifi_card_mbox0_sendbytes(data, len_bulk);
|
|
|
|
for (int i = len_bulk; i < len; i++)
|
|
{
|
|
wifi_card_write_func1_u8(0xFF, data[i]);
|
|
}
|
|
|
|
// Data must be u32 aligned
|
|
for (int i = 0; i < len % 4; i++)
|
|
{
|
|
wifi_card_write_func1_u8(0xFF, 0);
|
|
}
|
|
}
|
|
|
|
// BMI helpers
|
|
|
|
void wifi_card_bmi_write_memory(u32 addr, u8* data, u32 len)
|
|
{
|
|
u32 chunk_size = 0x1F0;
|
|
for (int i = 0; i < len; i += chunk_size)
|
|
{
|
|
u32 frag_size = i+chunk_size > len ? len-i : chunk_size;
|
|
wifi_card_bmi_cmd_write_memory(addr + i, &data[i], frag_size);
|
|
}
|
|
}
|
|
|
|
void wifi_card_bmi_lz_upload(u32 addr, const u8* data, u32 len)
|
|
{
|
|
wifi_card_bmi_lz_start(addr);
|
|
|
|
int chunk_size = 0x1F8;
|
|
for (int i = 0; i < len; i += chunk_size)
|
|
{
|
|
u32 frag_size = i+chunk_size > len ? len-i : chunk_size;
|
|
//wifi_printlnf("decomp lz: %08x %08x", i, frag_size);
|
|
wifi_card_bmi_lz_data((u8*)&data[i], frag_size);
|
|
}
|
|
}
|
|
|
|
static void wifi_card_handleMsg(int len, void* user_data)
|
|
{
|
|
Wifi_FifoMsg msg;
|
|
|
|
if (len < 4)
|
|
{
|
|
//wifi_printf("Bad msg len %x\n", len);
|
|
return;
|
|
}
|
|
|
|
fifoGetDatamsg(FIFO_DSWIFI, len, (u8*)&msg);
|
|
|
|
u32 cmd = msg.cmd;
|
|
if (cmd == WIFI_IPCCMD_INIT_IOP)
|
|
{
|
|
wifi_printf_allowed = 1;
|
|
//wifi_printf("iop val %x\n", cmd);
|
|
wifi_card_device_init(wifi_card_dev_wlan);
|
|
}
|
|
else if (cmd == WIFI_IPCCMD_INITBUFS)
|
|
{
|
|
void* data = msg.pkt_data;
|
|
u32 len = msg.pkt_len;
|
|
|
|
ip_data_out_buf = data;
|
|
ip_data_out_buf_totlen = len;
|
|
}
|
|
else if (cmd == WIFI_IPCCMD_SENDPKT)
|
|
{
|
|
void* data = msg.pkt_data;
|
|
u32 len = msg.pkt_len;
|
|
|
|
data_send_pkt_idk(data, len);
|
|
|
|
*(vu32*)data = 0xF00FF00F; // mark packet processed
|
|
|
|
// Craft and send msg
|
|
//msg.cmd = WIFI_IPCINT_PKTSENT;
|
|
//fifoSendDatamsg(FIFO_DSWIFI, sizeof(msg), (u8*)&msg);
|
|
}
|
|
else if (cmd == WIFI_IPCCMD_GET_DEVICE_MAC)
|
|
{
|
|
Wifi_FifoMsg msg;
|
|
|
|
// Get MAC
|
|
u8* mac = wmi_get_mac();
|
|
|
|
// Craft and send msg
|
|
msg.cmd = WIFI_IPCINT_DEVICE_MAC;
|
|
memcpy(msg.mac_addr, mac, 6);
|
|
fifoSendDatamsg(FIFO_DSWIFI, sizeof(msg), (u8*)&msg);
|
|
}
|
|
else if (cmd == WIFI_IPCCMD_GET_AP_MAC)
|
|
{
|
|
Wifi_FifoMsg msg;
|
|
|
|
// Get MAC
|
|
u8* mac = wmi_get_ap_mac();
|
|
|
|
// Craft and send msg
|
|
msg.cmd = WIFI_IPCINT_AP_MAC;
|
|
memcpy(msg.mac_addr, mac, 6);
|
|
fifoSendDatamsg(FIFO_DSWIFI, sizeof(msg), (u8*)&msg);
|
|
}
|
|
else
|
|
{
|
|
wifi_printf("iop val %x\n", cmd);
|
|
}
|
|
}
|
|
|
|
// SDIO main functions
|
|
void wifi_card_init(void)
|
|
{
|
|
mbox_buffer = memalign(16, MBOX_TMPBUF_SIZE);
|
|
mbox_out_buffer = memalign(16, MBOX_TMPBUF_SIZE);
|
|
|
|
// Read NVRAM settings
|
|
u32 end_addr = 0x1FE00;
|
|
|
|
readFirmware(0x20, &end_addr, sizeof(u32));
|
|
end_addr *= 0x8;
|
|
|
|
readFirmware(end_addr - 0x400, (void*)wifi_card_nvram_wep_configs, sizeof(wifi_card_nvram_wep_configs));
|
|
readFirmware(NVRAM_ADDR_WIFICFG, (void*)wifi_card_nvram_configs, sizeof(wifi_card_nvram_configs));
|
|
|
|
wifi_ndma_init();
|
|
wifi_sdio_controller_init(REG_SDIO_BASE);
|
|
|
|
fifoSetDatamsgHandler(FIFO_DSWIFI, wifi_card_handleMsg, 0);
|
|
//wifi_card_device_init(wifi_card_dev_wlan);
|
|
|
|
}
|
|
|
|
void wifi_card_send_command(wifi_sdio_command cmd, u32 args)
|
|
{
|
|
if(!device_curctx) return;
|
|
device_curctx->tmio.buffer = NULL;
|
|
wifi_sdio_send_command(&device_curctx->tmio, cmd, args);
|
|
}
|
|
|
|
void wifi_card_send_command_alt(wifi_sdio_command cmd, u32 args)
|
|
{
|
|
if(!device_curctx) return;
|
|
wifi_sdio_send_command(&device_curctx->tmio, cmd, args);
|
|
}
|
|
|
|
int wifi_card_device_init(wifi_card_device device)
|
|
{
|
|
wifi_card_ctx* ctx = wifi_card_get_context(device);
|
|
if(!ctx) return -1;
|
|
|
|
memset(ctx, 0, sizeof(wifi_card_ctx));
|
|
|
|
ctx->device = device;
|
|
|
|
ctx->tmio.controller = REG_SDIO_BASE;
|
|
ctx->tmio.clk_cnt = 0; // HCLK divider, 7=512 (largest possible) 0=2
|
|
ctx->tmio.bus_width = 1;
|
|
|
|
switch(device)
|
|
{
|
|
case wifi_card_dev_wlan:
|
|
return wifi_card_wlan_init(ctx);
|
|
}
|
|
|
|
return -3;
|
|
}
|
|
|
|
void wifi_card_process_pkts()
|
|
{
|
|
// Ack the interrupts
|
|
u32 int_sts = wifi_card_read_func1_u32(F1_HOST_INT_STATUS);
|
|
wifi_card_write_func1_u32(F1_HOST_INT_STATUS, int_sts);
|
|
|
|
wifi_card_mbox0_readpkt();
|
|
}
|
|
|
|
void wifi_card_irq(void)
|
|
{
|
|
wifi_card_process_pkts();
|
|
|
|
//wmi_tick(); // TODO
|
|
}
|
|
|
|
void wifi_card_send_ready()
|
|
{
|
|
Wifi_FifoMsg msg;
|
|
msg.cmd = WIFI_IPCINT_READY;
|
|
|
|
fifoSendDatamsg(FIFO_DSWIFI, sizeof(msg), (u8*)&msg);
|
|
}
|
|
|
|
void wifi_card_send_connect()
|
|
{
|
|
Wifi_FifoMsg msg;
|
|
msg.cmd = WIFI_IPCINT_CONNECT;
|
|
|
|
fifoSendDatamsg(FIFO_DSWIFI, sizeof(msg), (u8*)&msg);
|
|
}
|
|
|
|
void wifi_card_tick(void)
|
|
{
|
|
if (!wifi_card_bInitted) return;
|
|
|
|
//wifi_card_process_pkts();
|
|
|
|
wmi_tick();
|
|
|
|
//wifi_printlnf("tick end");
|
|
}
|
|
|
|
int wifi_card_wlan_init(wifi_card_ctx* ctx)
|
|
{
|
|
if(!ctx) return -1;
|
|
if(ctx->device != wifi_card_dev_wlan) return -1;
|
|
|
|
if (!mbox_out_buffer || !mbox_buffer)
|
|
{
|
|
wifi_printf("bad mbox alloc %x %x\n", mbox_buffer, mbox_out_buffer);
|
|
while (1);
|
|
}
|
|
|
|
device_curctx = ctx;
|
|
|
|
ctx->tmio.port = 0;
|
|
ctx->tmio.address = ctx->tmio.port;
|
|
|
|
ctx->tmio.debug = false;
|
|
ctx->tmio.break_early = false;
|
|
ctx->tmio.block_size = 128;
|
|
|
|
// TODO figure out if I should just put 3DS in another repo
|
|
#if 0
|
|
// These are pre-TWL-firmlaunch settings
|
|
REG_PDN_WLAN_CNT = 1; // Enable MP registers
|
|
REG_GPIO3_DAT = 0; // select 3DS wifi? (maybe this refers to the wireless LED activity?)
|
|
REG_GPIO5_DAT = 1; // deassert reset on Atheros
|
|
|
|
MCU_SetWirelessLedState(true);
|
|
#endif
|
|
|
|
*(vu16*)0x4004C04 &= ~0x100; // Select Atheros
|
|
//*(vu16*)0x4004C04 |= 0x100; // Select legacy wireless
|
|
|
|
u8 command[2];
|
|
command[0] = WRITE_FOUT_1;
|
|
command[1] = 0x80;
|
|
rtcTransaction(command, 2, 0, 0);
|
|
command[0] = WRITE_FOUT_2;
|
|
command[1] = 0x00;
|
|
rtcTransaction(command, 2, 0, 0);
|
|
i2cWriteRegister(I2C_PM, 0x30, 0x13);
|
|
*(vu16*)0x4004020 = 0x1; // SCFG_WL on?
|
|
|
|
ctx->tmio.bus_width = 4;
|
|
wifi_card_switch_device(ctx);
|
|
ioDelay(0xF000);
|
|
|
|
// Read register 0x00 (Revision) as a test
|
|
wifi_card_read_func0_u8(0x00);
|
|
if(ctx->tmio.status & 4) {
|
|
ctx->tmio.bus_width = 1;
|
|
wifi_card_switch_device(ctx);
|
|
wifi_printlnf("Failed to read revision, assuming 1bit");
|
|
}
|
|
|
|
//if(is_firstboot)
|
|
{
|
|
wifi_printlnf("Resetting SDIO...");
|
|
wifi_sdio_stop(ctx->tmio.controller);
|
|
|
|
// TODO maybe toggle REG_GPIO5_DAT for a hardware reset
|
|
|
|
// Operating Conditions Register.
|
|
u32 ocr = 0;
|
|
|
|
// CMD5 - IO_SEND_OP_COND
|
|
wifi_sdio_command cmd5 = {
|
|
.cmd = 5,
|
|
.response_type = wifi_sdio_resp_48bit_ocr_no_crc7
|
|
};
|
|
|
|
/*
|
|
* Note that this will run at least twice. Once with the OCR parameter
|
|
* as zero, which will read the OCR from the card. In the state following
|
|
* that, the card will not be ready to operate as no operating voltage has
|
|
* been agreed.
|
|
*
|
|
* On the second successful iteration, the OCR parameter is set, which
|
|
* currently just returns the range of voltages that the card gave us (the
|
|
* ones it can support itself) back to the card. Really, we should check to
|
|
* see what operating voltages NWM allows the card to run at.
|
|
*
|
|
* Following that command, the card will be initialized for I/O operations.
|
|
*/
|
|
while(true)
|
|
{
|
|
while(true)
|
|
{
|
|
// CMD5 - IO_SEND_OP_COND
|
|
wifi_card_send_command(cmd5, ocr);
|
|
if(ctx->tmio.status & 4) {
|
|
wifi_printf("Skip opcond...\n");
|
|
goto skip_opcond;
|
|
}
|
|
|
|
if(ctx->tmio.status & 1) break;
|
|
}
|
|
|
|
// TODO: Probably should find out what NWM specifies as the voltage range.
|
|
ocr = ctx->tmio.resp[0] & 0xFFFFFF;
|
|
|
|
// Card ready to operate (initialized).
|
|
if (ctx->tmio.resp[0] & 0x80000000)
|
|
break;
|
|
}
|
|
|
|
// CMD3 - SEND_RELATIVE_ADDR (assign relative address to card).
|
|
wifi_sdio_command cmd3 = {
|
|
.cmd = 3,
|
|
.response_type = wifi_sdio_resp_48bit
|
|
};
|
|
wifi_card_send_command(cmd3, 0);
|
|
if(ctx->tmio.status & 4) return -1;
|
|
ctx->tmio.address = ctx->tmio.resp[0] >> 16;
|
|
|
|
// CMD7 - (DE)SELECT_CARD
|
|
wifi_sdio_command cmd7 = {
|
|
.cmd = 7,
|
|
.response_type = wifi_sdio_resp_48bit_busy
|
|
};
|
|
wifi_card_send_command(cmd7, (u32)(ctx->tmio.address) << 16);
|
|
if(ctx->tmio.status & 4) return -1;
|
|
|
|
skip_opcond:
|
|
|
|
//wifi_card_write_func0_u8(0x6, 0x0);
|
|
wifi_card_write_func0_u8(0x2, 0x0); // adding delay after this one wipes the loaded firmware??
|
|
wifi_card_write_func0_u8(0x2, 0x2);
|
|
ioDelay(0xF000);
|
|
|
|
// Read register 0x07 (Bus Interface Control) of the CCCR.
|
|
wifi_card_send_command(cmd52, 0x07 << 9);
|
|
if(ctx->tmio.status & 4) return -1;
|
|
u8 bus_interface_control = ctx->tmio.resp[0] & 0xFF;
|
|
|
|
// Enable 4-bit mode and card detect
|
|
bus_interface_control |= 0x82;
|
|
|
|
// Write to the Bus Interface Control.
|
|
wifi_card_write_func0_u8(0x07, bus_interface_control);
|
|
if(ctx->tmio.status & 4) return -1;
|
|
ctx->tmio.bus_width = 4;
|
|
|
|
// Apply new bus width
|
|
wifi_card_switch_device(ctx);
|
|
|
|
// Write to the Power Control.
|
|
wifi_card_write_func0_u8(0x12, 0x02);
|
|
if(ctx->tmio.status & 4) return -1;
|
|
|
|
// Set block size for func1 (lsb)
|
|
wifi_card_write_func0_u8(0x110, ctx->tmio.block_size & 0xFF);
|
|
if(ctx->tmio.status & 4) return -1;
|
|
|
|
// Set block size for func1 (msb)
|
|
wifi_card_write_func0_u8(0x111, ctx->tmio.block_size >> 8);
|
|
if(ctx->tmio.status & 4) return -1;
|
|
|
|
// Set block size for func0 (lsb)
|
|
wifi_card_write_func0_u8(0x10, ctx->tmio.block_size & 0xFF);
|
|
if(ctx->tmio.status & 4) return -1;
|
|
|
|
// Set block size for func0 (msb)
|
|
wifi_card_write_func0_u8(0x11, ctx->tmio.block_size >> 8);
|
|
if(ctx->tmio.status & 4) return -1;
|
|
}
|
|
|
|
// Required?
|
|
ioDelay(0xF000);
|
|
|
|
// Read register 0x00 (Revision)
|
|
u8 revision = wifi_card_read_func0_u8(0x00);
|
|
if(ctx->tmio.status & 4) return -1;
|
|
wifi_printf("Rev: %02x\n", revision);
|
|
|
|
// Set function1 enable
|
|
wifi_card_write_func0_u8(0x02, 0x02);
|
|
//if(ctx->tmio.status & 4) return -1;
|
|
|
|
while (true)
|
|
{
|
|
// Read register 0x02 (function enable) hibyte until it's ready
|
|
if (wifi_card_read_func0_u8(0x3) == 0x02)
|
|
break;
|
|
//if(wifi_sdio_critical_fail) return -1;
|
|
}
|
|
|
|
//wifi_printlnf("Int status: %08x %02x", wifi_card_read_func1_u32(F1_HOST_INT_STATUS), wifi_card_read_func1_u8(F1_RX_LOOKAHEAD_VALID));
|
|
|
|
// Get manufacturer
|
|
device_manufacturer = wifi_card_read_func0_u32(0x1007);
|
|
device_chip_id = wifi_card_read_intern_word(0x000040ec);
|
|
if(ctx->tmio.status & 4) {
|
|
wifi_printf("Error status reading manufacturer\n");
|
|
//return -1;
|
|
}
|
|
wifi_printf("Mfg %08lx Cid %08lx (%s)\n", device_manufacturer, device_chip_id, wifi_card_get_chip_str());
|
|
|
|
// TODO detect this?
|
|
device_host_interest_addr = AR601x_HOST_INTEREST_ADDRESS;
|
|
if (device_chip_id == CHIP_ID_AR6002)
|
|
device_host_interest_addr = AR6002_HOST_INTEREST_ADDRESS;
|
|
|
|
u32 is_uploaded = wifi_card_read_intern_word( device_host_interest_addr+0x58);
|
|
|
|
if (!is_uploaded)
|
|
{
|
|
wifi_printf("%s needs firmware upload %lx.\n", wifi_card_get_chip_str(), is_uploaded);
|
|
}
|
|
|
|
// Reset into bootloader
|
|
wifi_card_write_intern_word(0x4000, 0x00000100);
|
|
ioDelay(0x10000);
|
|
|
|
wifi_printf("Reset cause: %08lx\n", wifi_card_read_intern_word(0x40C0)); // RESET_CAUSE, expecting 0x02
|
|
|
|
// FIFOs are weird after reset?
|
|
//wifi_card_bmi_wait_count4();
|
|
|
|
// Begin talking to bootloader
|
|
wifi_printlnf("BMI version: %08lx", wifi_card_bmi_get_version());
|
|
|
|
u32 mem_write32 = 0x2;
|
|
wifi_card_bmi_cmd_write_memory(device_host_interest_addr, (u8*)&mem_write32, sizeof(u32));
|
|
|
|
// TODO is this needed?
|
|
u32 scratch0 = wifi_card_bmi_read_register(0x0180C0);
|
|
wifi_card_bmi_write_register(0x0180C0, scratch0 | 0x8);
|
|
|
|
// TODO is this needed?
|
|
u32 wlan_system_sleep = wifi_card_bmi_read_register(0x0040C4); // WLAN_SYSTEM_SLEEP
|
|
wifi_card_bmi_write_register(0x0040C4, wlan_system_sleep | 1);
|
|
|
|
wifi_card_bmi_write_register(0x004028, 0x5); // WLAN_CLOCK_CONTROL
|
|
wifi_card_bmi_write_register(0x004020, 0x0);
|
|
|
|
#if 0
|
|
// All the part's addresses
|
|
u32 parta_dst = 0x524C00;
|
|
u32 partb_dst = 0x53FE18;
|
|
u32 partc_dst = 0x527000;
|
|
u32 partd_dst = 0x524C00;
|
|
|
|
// TODO: Source AR6014 bins from SAFE_FIRM nwm?
|
|
// Or just leave them because they're only 4KiB
|
|
if (!is_uploaded)
|
|
{
|
|
// Upload and run D and C
|
|
wifi_card_bmi_write_memory(partd_dst, (u8*)ar6014_partd_bin, ar6014_partd_bin_size);
|
|
wifi_card_bmi_write_memory(partc_dst, (u8*)ar6014_partc_bin, ar6014_partc_bin_size);
|
|
|
|
// This executes partC prior to partA (bootrom patches?)
|
|
wifi_card_bmi_execute(partc_dst + 0x400000, partc_dst);
|
|
|
|
#if 0
|
|
// Load PartA (decompressed
|
|
wifi_card_bmi_write_memory(parta_dst, (u8*)ar6014_parta_bin, ar6014_parta_bin_size);
|
|
#endif
|
|
|
|
#if 1
|
|
// Decompress A
|
|
//wifi_card_bmi_lz_upload(parta_dst, ar6014_parta_bin, ar6014_parta_bin_size);
|
|
wifi_card_bmi_lz_upload(parta_dst, nwm_ar6014_softap_bin, nwm_ar6014_softap_bin_size);
|
|
#endif
|
|
|
|
// Upload D and B (is D needed?)
|
|
wifi_card_bmi_write_memory(partd_dst, (u8*)ar6014_partd_bin, ar6014_partd_bin_size);
|
|
wifi_card_bmi_write_memory(partb_dst, (u8*)ar6014_partb_bin, ar6014_partb_bin_size);
|
|
|
|
// Store database addr somewhere
|
|
mem_write32 = partb_dst;
|
|
wifi_card_bmi_cmd_write_memory(device_host_interest_addr+0x18, (u8*)&mem_write32, sizeof(u32));
|
|
}
|
|
#endif
|
|
|
|
/*u16 new_country = 0x0;//0x8348 US, 0x8188 JP, 0x0 debug?
|
|
device_eeprom_addr = wifi_card_read_intern_word(device_host_interest_addr+0x54);
|
|
u32 test_regdom = wifi_card_read_intern_word(device_eeprom_addr+0x08);
|
|
u32 new_regdom = (test_regdom & ~0xFFFF) | new_country;
|
|
wifi_card_write_intern_word(device_eeprom_addr+0x08, new_regdom);
|
|
u32 fix_check = wifi_card_read_intern_word(device_eeprom_addr+0x04);
|
|
fix_check ^= (new_regdom ^ test_regdom);
|
|
wifi_card_write_intern_word(device_eeprom_addr+0x04, fix_check);*/
|
|
|
|
// BMI finish
|
|
wifi_printlnf("BMI finishing...");
|
|
|
|
// TODO: What was this about...? Is it just a warm boot thing?
|
|
// seems to hang here, but no$'s wifiboot doesn't have any loops
|
|
// to check if FIFOs are actually receiving...
|
|
|
|
//wifi_card_bmi_write_register(0x0040C4, wlan_system_sleep & ~1);
|
|
//wifi_printlnf("BMI finishing... aa");
|
|
//wifi_card_bmi_write_register(0x0180C0, scratch0);
|
|
|
|
//wifi_printlnf("BMI finishing... a");
|
|
|
|
mem_write32 = 0x80;
|
|
wifi_card_bmi_cmd_write_memory(device_host_interest_addr+0x6C, (u8*)&mem_write32, sizeof(u32));
|
|
|
|
mem_write32 = 0x63;
|
|
wifi_card_bmi_cmd_write_memory(device_host_interest_addr+0x74, (u8*)&mem_write32, sizeof(u32));
|
|
|
|
// Launch firmware
|
|
wifi_printlnf("Launching!");
|
|
wifi_card_bmi_start_firmware();
|
|
|
|
while (1)
|
|
{
|
|
u32 is_ready = wifi_card_read_intern_word(device_host_interest_addr+0x58);
|
|
|
|
if (is_ready == 1) break;
|
|
ioDelay(0x1000);
|
|
}
|
|
|
|
device_eeprom_addr = wifi_card_read_intern_word(device_host_interest_addr+0x54);
|
|
device_eeprom_version = wifi_card_read_intern_word(device_eeprom_addr+0x10); // version, 609C0202
|
|
|
|
wifi_printlnf("Firmware %08lx ready, handshaking...", device_eeprom_version);
|
|
|
|
wifi_card_bInitted = true;
|
|
|
|
// Scan for APs
|
|
//wmi_scan();
|
|
|
|
// Enable IRQs
|
|
irqSetAUX(IRQ_WIFI_SDIO_CARDIRQ, wifi_card_irq);
|
|
irqEnableAUX(IRQ_WIFI_SDIO_CARDIRQ);
|
|
wifi_sdio_enable_cardirq(REG_SDIO_BASE, true); // MelonDS seems to have trouble with IRQs? Comment out for MelonDS.
|
|
|
|
wifi_card_write_func1_u32(F1_INT_STATUS_ENABLE, 0x010300D1); // INT_STATUS_ENABLE (or 0x1?)
|
|
wifi_card_write_func0_u8(0x4, 0x3); // CCCR irq_enable, master+func1
|
|
|
|
// 100ms timer
|
|
timerStart(3, ClockDivider_1024, TIMER_FREQ_1024(1000 / SDIO_TICK_INTERVAL_MS), wifi_card_tick); //((u64)TIMER_CLOCK * SDIO_TICK_INTERVAL_MS) / 1000
|
|
|
|
wifi_printlnf("wait ready");
|
|
while (!wmi_is_ready())
|
|
{
|
|
//wifi_card_mbox0_readpkt(); // MelonDS seems to have trouble with IRQs? Uncomment for MelonDS.
|
|
}
|
|
|
|
wifi_printlnf("%s fully initialized!", wifi_card_get_chip_str());
|
|
|
|
wmi_scan();
|
|
|
|
return 0;
|
|
}
|
|
|
|
void wifi_card_deinit(void)
|
|
{
|
|
wifi_sdio_enable_cardirq(REG_SDIO_BASE, false);
|
|
|
|
irqDisableAUX(IRQ_WIFI_SDIO_CARDIRQ);
|
|
irqDisable(IRQ_TIMER3);
|
|
|
|
if (wifi_card_bInitted)
|
|
{
|
|
wifi_card_write_func1_u32(F1_INT_STATUS_ENABLE, 0x0); // INT_STATUS_ENABLE
|
|
wifi_card_write_func0_u8(0x4, 0x0); // CCCR irq_enable
|
|
}
|
|
|
|
wifi_card_bInitted = false;
|
|
|
|
wifi_printlnf("%s deinitted", wifi_card_get_chip_str());
|
|
}
|
|
|
|
bool wifi_card_initted()
|
|
{
|
|
return wifi_card_bInitted && wmi_is_ready();
|
|
}
|
|
|
|
u32 wifi_card_host_interest_addr()
|
|
{
|
|
return device_host_interest_addr;
|
|
}
|
|
|
|
wifi_card_ctx* wifi_card_get_context(wifi_card_device device)
|
|
{
|
|
switch(device)
|
|
{
|
|
case wifi_card_dev_wlan:
|
|
return &wlan_ctx;
|
|
|
|
default: return NULL;
|
|
}
|
|
}
|
|
|
|
void wifi_card_switch_device(wifi_card_ctx* ctx)
|
|
{
|
|
if(!ctx) return;
|
|
wifi_sdio_switch_device(&ctx->tmio);
|
|
|
|
device_curctx = ctx;
|
|
}
|
|
|
|
void wifi_card_setclk(u32 data)
|
|
{
|
|
wifi_sdio_setclk(REG_SDIO_BASE, data);
|
|
}
|
|
|
|
void wifi_card_stop(void)
|
|
{
|
|
wifi_sdio_stop(REG_SDIO_BASE);
|
|
}
|