From fe35cb68d6b62fcd2a30fd7b12a7f0323f5997e3 Mon Sep 17 00:00:00 2001 From: Evan Reichard Date: Fri, 24 Jul 2026 16:21:12 -0400 Subject: [PATCH] feat(xteink): add ESP32-C3 Wi-Fi emulation with qemu AP Ports the ESP32 Wi-Fi emulation (esp32c3_wifi, esp32_wifi_ap, esp32_wlan_*) from the lcgamboa/PICSimLab QEMU fork and integrates it behind the esp32c3_wifi NIC model, plus the supporting radio peripherals (ANA/PHYA/FE/PWR manager). - Exposes an open SSID 'qemu' bridged to the host via QEMU user-mode NAT (libslirp, now enabled in the native build). - Adds ESP-IDF 5.5 compatibility fixes: channel resolution from the beacon DS Parameter Set IE / AP lookup, persistent SYSCON register backing (phy_module_has_clock_bits), DMA inlink/outlink descriptor mirroring, RX descriptor ring reset on item.next==0, and descriptor ownership/length semantics. - xteink-emu now starts with -nic user,model=esp32c3_wifi and synthesizes an eFuse image with a fixed guest MAC. Verified against a stock Arduino-ESP32 sketch: scans, finds 'qemu', associates, gets DHCP 192.168.4.15, and completes an outbound TCP connection. Unmodified CrossPoint firmware still needs the power-button hold during boot (tracked separately). --- README.xteink.md | 2 +- flake.nix | 4 + hw/misc/esp32_fe.c | 67 +++ hw/misc/esp32_phya.c | 71 +++ hw/misc/esp32_wifi_ap.c | 787 +++++++++++++++++++++++++++++++ hw/misc/esp32_wlan.h | 280 +++++++++++ hw/misc/esp32_wlan_packet.c | 306 ++++++++++++ hw/misc/esp32_wlan_packet.h | 56 +++ hw/misc/esp32c3_ana.c | 87 ++++ hw/misc/esp32c3_pwrmng.c | 84 ++++ hw/misc/esp32c3_wifi.c | 245 ++++++++++ hw/misc/meson.build | 9 +- hw/riscv/esp32c3.c | 103 +++- include/hw/misc/esp32_fe.h | 18 + include/hw/misc/esp32_phya.h | 19 + include/hw/misc/esp32_wifi.h | 75 +++ include/hw/misc/esp32c3_ana.h | 16 + include/hw/misc/esp32c3_pwrmng.h | 16 + include/hw/misc/esp32c3_reg.h | 3 + include/hw/misc/esp32c3_wifi.h | 19 + scripts/xteink-emu.py | 8 +- 21 files changed, 2246 insertions(+), 29 deletions(-) create mode 100644 hw/misc/esp32_fe.c create mode 100644 hw/misc/esp32_phya.c create mode 100644 hw/misc/esp32_wifi_ap.c create mode 100644 hw/misc/esp32_wlan.h create mode 100644 hw/misc/esp32_wlan_packet.c create mode 100644 hw/misc/esp32_wlan_packet.h create mode 100644 hw/misc/esp32c3_ana.c create mode 100644 hw/misc/esp32c3_pwrmng.c create mode 100644 hw/misc/esp32c3_wifi.c create mode 100644 include/hw/misc/esp32_fe.h create mode 100644 include/hw/misc/esp32_phya.h create mode 100644 include/hw/misc/esp32_wifi.h create mode 100644 include/hw/misc/esp32c3_ana.h create mode 100644 include/hw/misc/esp32c3_pwrmng.h create mode 100644 include/hw/misc/esp32c3_wifi.h diff --git a/README.xteink.md b/README.xteink.md index 44d1e2e85e..edaa818b87 100644 --- a/README.xteink.md +++ b/README.xteink.md @@ -42,7 +42,7 @@ nix run .#xteink-emu -- screenshot --output screen.png nix run .#xteink-emu -- stop ``` -State defaults to `/tmp/xteink-emu-$UID`; pass `--state-dir` to any subcommand for parallel instances. `wait` advances a persistent log cursor after each match. +State defaults to `/tmp/xteink-emu-$UID`; pass `--state-dir` to any subcommand for parallel instances. `wait` advances a persistent log cursor after each match. The emulator exposes the open Wi-Fi network `qemu`; guest traffic uses QEMU's user-mode NAT. ### WebAssembly diff --git a/flake.nix b/flake.nix index 26127ae566..b833853800 100644 --- a/flake.nix +++ b/flake.nix @@ -41,6 +41,7 @@ glib libgcrypt libpng + libslirp pixman zlib ]; @@ -51,10 +52,12 @@ cp -r ${keycodemapdb} subprojects/keycodemapdb chmod -R u+w subprojects/keycodemapdb sed -i "/subdir('fp')/d" tests/meson.build + substituteInPlace meson.build --replace-fail "static: true" "static: false" ''; configurePhase = '' runHook preConfigure + export PKG_CONFIG_PATH=${pkgs.libslirp}/lib/pkgconfig''${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH} ./configure \ --prefix=$out \ --target-list=riscv32-softmmu \ @@ -65,6 +68,7 @@ --enable-gcrypt \ --enable-pixman \ --enable-png \ + --enable-slirp \ --disable-werror \ --disable-docs \ --disable-tools diff --git a/hw/misc/esp32_fe.c b/hw/misc/esp32_fe.c new file mode 100644 index 0000000000..181e460302 --- /dev/null +++ b/hw/misc/esp32_fe.c @@ -0,0 +1,67 @@ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qemu/guest-random.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/misc/esp32_fe.h" + +#define DEBUG 0 + +static uint64_t esp32_fe_read(void *opaque, hwaddr addr, unsigned int size) +{ + uint32_t r = 0; + Esp32FeState *s = ESP32_FE(opaque); + r=s->mem[addr/4]; + switch(addr){ + case 0x174: + case 0x7C: + r=0xffffffff; + break; + } + if(DEBUG) printf("esp32_fe_read 0x%04lx= 0x%08x\n",(unsigned long) addr,r); + + return r; +} + +static void esp32_fe_write(void *opaque, hwaddr addr, uint64_t value, + unsigned int size) { + Esp32FeState *s = ESP32_FE(opaque); + if(DEBUG) printf("esp32_fe_write 0x%04lx= 0x%08lx\n",(unsigned long) addr, (unsigned long) value); + + s->mem[addr/4]=(uint32_t)value; +} + +static const MemoryRegionOps esp32_fe_ops = { + .read = esp32_fe_read, + .write = esp32_fe_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void esp32_fe_init(Object *obj) +{ + Esp32FeState *s = ESP32_FE(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &esp32_fe_ops, s, + TYPE_ESP32_FE, 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + memset(s->mem,0,sizeof(s->mem)); +} + + +static const TypeInfo esp32_fe_info = { + .name = TYPE_ESP32_FE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Esp32FeState), + .instance_init = esp32_fe_init, +}; + +static void esp32_fe_register_types(void) +{ + type_register_static(&esp32_fe_info); +} + +type_init(esp32_fe_register_types) diff --git a/hw/misc/esp32_phya.c b/hw/misc/esp32_phya.c new file mode 100644 index 0000000000..c14b28128c --- /dev/null +++ b/hw/misc/esp32_phya.c @@ -0,0 +1,71 @@ +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qemu/guest-random.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/misc/esp32_phya.h" + +#define DEBUG 0 + +static uint32_t *g_mem; + +static uint64_t esp32_phya_read(void *opaque, hwaddr addr, unsigned int size) +{ + uint32_t r = 0; + Esp32PhyaState *s = ESP32_PHYA(opaque); + r=s->mem[addr/4]; + if(DEBUG) printf("esp32_phya_read 0x%04lx= 0x%08x\n",(unsigned long) addr,r); + + return r; +} + +void Esp32_WLAN_Set_Packet_Status(const uint32_t state){ + if(g_mem){ + g_mem[0x270/4] = state; //ESP32 + g_mem[0x320/4] = state; //ESP32C3 + } +} + +static void esp32_phya_write(void *opaque, hwaddr addr, uint64_t value, + unsigned int size) { + Esp32PhyaState *s = ESP32_PHYA(opaque); + + if(DEBUG) printf("esp32_phya_write 0x%04lx= 0x%08lx\n",(unsigned long) addr, (unsigned long) value); + + s->mem[addr/4]=(uint32_t)value; +} + +static const MemoryRegionOps esp32_phya_ops = { + .read = esp32_phya_read, + .write = esp32_phya_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void esp32_phya_init(Object *obj) +{ + Esp32PhyaState *s = ESP32_PHYA(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &esp32_phya_ops, s, + TYPE_ESP32_PHYA, 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + memset(s->mem,0,sizeof(s->mem)); + g_mem = s->mem; +} + + +static const TypeInfo esp32_phya_info = { + .name = TYPE_ESP32_PHYA, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Esp32PhyaState), + .instance_init = esp32_phya_init, +}; + +static void esp32_phya_register_types(void) +{ + type_register_static(&esp32_phya_info); +} + +type_init(esp32_phya_register_types) diff --git a/hw/misc/esp32_wifi_ap.c b/hw/misc/esp32_wifi_ap.c new file mode 100644 index 0000000000..9fa47add97 --- /dev/null +++ b/hw/misc/esp32_wifi_ap.c @@ -0,0 +1,787 @@ +/** + * QEMU WLAN access point emulation + * + * Copyright (c) 2008 Clemens Kolbitsch + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Modifications: + * 2008-February-24 Clemens Kolbitsch : + * New implementation based on ne2000.c + * 18/01/22 Martin Johnson : Modified for esp32 wifi emulation + * 08/12/23 lcgamboa@yahoo.com : Modified for esp32c3, espnow and SoftAp wifi emulation + */ + + +#include "qemu/osdep.h" +#include "net/net.h" +#include "qemu/timer.h" + +#include "hw/irq.h" +#include "hw/misc/esp32_wifi.h" +#include "hw/misc/esp32_phya.h" +#include "esp32_wlan.h" +#include "esp32_wlan_packet.h" + +// 50ms between beacons +#define BEACON_TIME 500000000 +#define INTER_FRAME_TIME 5000000 +#define WAIT_ACK_TIMEOUT 10000000 +#define DEBUG 0 +#define ENABLE_BEACON 1 +#define DEBUG_DUMPFRAMES 0 +#define DEBUG_WIRESHARK_IMPORT 0 + +// color defines +#define BLACK 0 +#define RED 1 +#define GREEN 2 +#define YELLOW 3 +#define BLUE 4 +#define MAGNETA 5 +#define CYAN 6 +#define WHITE 7 +#define ANSI_DEFAULT() printf("\033[0m") +#define ANSI_COLOR(f, b) printf("\033[%d;%dm", (f) + 30, (b) + 40) +#define ANSI_FG_LCOLOR(f) printf("\033[0;%dm", (f) + 30) +#define ANSI_FG_HCOLOR(f) printf("\033[1;%dm", (f) + 30) + +access_point_info access_points[] = { + {"qemu", 1, -25, {0x10, 0x01, 0x00, 0xc4, 0x0a, 0x51}}, +}; + +int nb_aps=sizeof(access_points)/sizeof(access_point_info); + +static void Esp32_WLAN_beacon_timer(void *opaque) +{ + struct mac80211_frame *frame; + Esp32WifiState *s = (Esp32WifiState *)opaque; + // only send a beacon if we are an access point + if((ENABLE_BEACON)&&(s->mode == Esp32_Mode_Station)){ + if(s->ap_state!=Esp32_WLAN__STATE_STA_ASSOCIATED) { + for(int i=0;ibeacon_ap)%nb_aps; + if (access_points[ap].channel==esp32_wifi_channel) { + memcpy(s->ap_macaddr,access_points[ap].mac_address,6); + frame = Esp32_WLAN_create_beacon_frame(&access_points[ap]); + Esp32_WLAN_init_ap_frame(s, frame); + Esp32_WLAN_insert_frame(s, frame); + break; + } + } + s->beacon_ap=(s->beacon_ap+1)%nb_aps; + } + } + timer_mod(s->beacon_timer, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + BEACON_TIME); +} + +static void Esp32_WLAN_inject_timer(void *opaque) +{ + Esp32WifiState *s = (Esp32WifiState *)opaque; + struct mac80211_frame *frame; + + frame = s->inject_queue; + if (frame) { + // remove from queue + s->inject_queue_size--; + s->inject_queue = frame->next_frame; + Esp32_sendFrame(s, (void *)frame, frame->frame_length,frame->signal_strength); + free(frame); + } + if (s->inject_queue_size > 0) { + // there are more packets... schedule + // the timer for sending them as well + timer_mod(s->inject_timer, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + INTER_FRAME_TIME); + } else { + // we wait until a new packet schedules + // us again + s->inject_timer_running = 0; + } + +} + +static void Esp32_WLAN_Wait_ACk_timer(void *opaque) +{ + Esp32WifiState *s = (Esp32WifiState *)opaque; + + Esp32_WLAN_frame_delivered(s); +} + +static void macprint(const uint8_t *p, const char * name) { + printf("%s: %02x:%02x:%02x:%02x:%02x:%02x\n",name, p[0],p[1],p[2],p[3],p[4],p[5]); +} + +static void buffer_print(uint8_t * b, unsigned size){ + for(int i=0;i < size ;i++) { + if((i%16)==0) printf("%04x: ",i); + printf("%02x ",b[i]); + if((i%16)==15) printf("\n"); + } + if((size%16)!=0)printf("\n"); +} + +static void infoprint(struct mac80211_frame *frame) { + if(DEBUG_DUMPFRAMES) { +#if (DEBUG_WIRESHARK_IMPORT == 0 ) + printf("\nFrame Info type:%d subtype:%d flags:%d duration:%d length:%d\n",frame->frame_control.type, + frame->frame_control.sub_type,frame->frame_control.flags,frame->duration_id, frame->frame_length); + + printf("protocol_version: %i\n",frame->frame_control.protocol_version); + printf("type: %i ",frame->frame_control.type ); + switch (frame->frame_control.type) + { + case IEEE80211_TYPE_MGT: + printf("MGT\n"); + break; + case IEEE80211_TYPE_CTL: + printf("CTL\n"); + break; + case IEEE80211_TYPE_DATA: + printf("DATA\n"); + break; + } + printf("subtype: %i ",frame->frame_control.sub_type); + switch (frame->frame_control.type) + { + case IEEE80211_TYPE_MGT: + switch (frame->frame_control.sub_type) + { + case IEEE80211_TYPE_MGT_SUBTYPE_BEACON: + printf("BEACON\n"); + break; + case IEEE80211_TYPE_MGT_SUBTYPE_ACTION: + printf("ACTION\n"); + break; + case IEEE80211_TYPE_MGT_SUBTYPE_PROBE_REQ: + printf("PROBE_REQ\n"); + break; + case IEEE80211_TYPE_MGT_SUBTYPE_PROBE_RESP: + printf("PROBE_RESP\n"); + break; + case IEEE80211_TYPE_MGT_SUBTYPE_AUTHENTICATION: + printf("AUTHENTICATION\n"); + break; + case IEEE80211_TYPE_MGT_SUBTYPE_DEAUTHENTICATION: + printf("DEAUTHENTICATION\n"); + break; + case IEEE80211_TYPE_MGT_SUBTYPE_ASSOCIATION_REQ: + printf("ASSOCIATION_REQ\n"); + break; + case IEEE80211_TYPE_MGT_SUBTYPE_ASSOCIATION_RESP: + printf("ASSOCIATION_RESP\n"); + break; + case IEEE80211_TYPE_MGT_SUBTYPE_DISASSOCIATION: + printf("DISASSOCIATION\n"); + break; + default: + printf("---\n"); + break; + } + break; + case IEEE80211_TYPE_CTL: + switch (frame->frame_control.sub_type) + { + case IEEE80211_TYPE_CTL_SUBTYPE_ACK: + printf("ACK\n"); + break; + default: + printf("---\n"); + break; + } + break; + case IEEE80211_TYPE_DATA: + switch (frame->frame_control.sub_type) + { + case IEEE80211_TYPE_DATA_SUBTYPE_DATA: + printf("DATA\n"); + break; + default: + printf("---\n"); + break; + } + break; + } + + printf("flags: %i\n",frame->frame_control.flags); + printf("duration_id: %i\n",frame->duration_id); + macprint(frame->destination_address,"destination"); + macprint(frame->source_address,"source"); + macprint(frame->bssid_address,"bssid"); + printf("fragment_number: %u\n",frame->sequence_control.fragment_number); + printf("sequence_number : %u\n",frame->sequence_control.sequence_number); + + switch (frame->frame_control.type) + { + case IEEE80211_TYPE_MGT: + switch (frame->frame_control.sub_type) + { + case IEEE80211_TYPE_MGT_SUBTYPE_BEACON: + printf("beacon timestamp : %lu\n",(unsigned long) frame->beacon_info.timestamp); + printf("beacon interval : %u\n",frame->beacon_info.interval); + printf("beacon capability : %u\n",frame->beacon_info.capability); + break; + default: + if(frame->frame_length > IEEE80211_HEADER_SIZE) + buffer_print((uint8_t *)frame->data_and_fcs, frame->frame_length - IEEE80211_HEADER_SIZE); + break; + } + break; + case IEEE80211_TYPE_CTL: + switch (frame->frame_control.sub_type) + { + default: + if(frame->frame_length > IEEE80211_HEADER_SIZE) + buffer_print((uint8_t *)frame->data_and_fcs, frame->frame_length - IEEE80211_HEADER_SIZE); + break; + } + break; + case IEEE80211_TYPE_DATA: + switch (frame->frame_control.sub_type) + { + default: + printf("EtherType 0x%02X%02X\n",frame->data_and_fcs[6],frame->data_and_fcs[7]); + if( frame->data_and_fcs[6]==8 && frame->data_and_fcs[7]==0x06) { + printf("ARP\n"); + arp_header_t *header=(arp_header_t *)&frame->data_and_fcs[8]; + printf("Operation %i\n", header->operation>>8); + } + if( frame->data_and_fcs[6]==8 && frame->data_and_fcs[7]==0x00) { + printf("IPV4\n"); + ip_header_t *header=(ip_header_t *)&frame->data_and_fcs[8]; + printf("Protocol: 0x%04X\n",header->protocol); + switch (header->protocol) + { + case 0x11: + printf("UDP\n"); + break; + case 0x06: + printf("TCP\n"); + break; + } + } + if( frame->data_and_fcs[6]==8 && frame->data_and_fcs[7]==0xdd) { + printf("IPV6\n"); + } + if(frame->frame_length > IEEE80211_HEADER_SIZE) + buffer_print((uint8_t *)frame->data_and_fcs, frame->frame_length - IEEE80211_HEADER_SIZE); + break; + } + break; + } + printf("frame_length : %u\n", frame->frame_length); +#else + uint8_t *b=(uint8_t *)frame; + for(int i=0;iframe_length;i++) { + if((i%16)==0) printf("\n%04x: ",i); + printf("%02x ",b[i]); + } + printf("\n"); +#endif + } +} + +void Esp32_WLAN_insert_frame(Esp32WifiState *s, struct mac80211_frame *frame) +{ + struct mac80211_frame *i_frame; + + insertCRC(frame); + if(DEBUG) { + ANSI_FG_HCOLOR(GREEN); + printf("---------------\n>IN Send Frame type:%d subtype:%d channel:%d ap_state:%d time:%ld \n",frame->frame_control.type,frame->frame_control.sub_type,esp32_wifi_channel,s->ap_state,(unsigned long) qemu_clock_get_ns(QEMU_CLOCK_REALTIME)); + ANSI_DEFAULT(); + } + infoprint(frame); + s->inject_queue_size++; + i_frame = s->inject_queue; + if (!i_frame) { + s->inject_queue = frame; + } else { + while (i_frame->next_frame) { + i_frame = i_frame->next_frame; + } + i_frame->next_frame = frame; + } + + if (!s->inject_timer_running) { + // if the injection timer is not + // running currently, let's schedule + // one run... + s->inject_timer_running = 1; + timer_mod(s->inject_timer, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + INTER_FRAME_TIME); + } + +} + +static _Bool Esp32_WLAN_can_receive(NetClientState *ncs) +{ + Esp32WifiState *s = qemu_get_nic_opaque(ncs); + /* + if (s->ap_state != Esp32_WLAN__STATE_ASSOCIATED && s->ap_state != Esp32_WLAN__STATE_STA_ASSOCIATED) { + // we are currently not connected + // to the access point + return 0; + } + */ + if (s->inject_queue_size > Esp32_WLAN__MAX_INJECT_QUEUE_SIZE) { + // overload, please give me some time... + return 0; + } + + return 1; +} + +static ssize_t Esp32_WLAN_receive(NetClientState *ncs, + const uint8_t *buf, size_t size) +{ + Esp32WifiState *s = qemu_get_nic_opaque(ncs); + struct mac80211_frame *frame; + if (!Esp32_WLAN_can_receive(ncs)) { + // this should not happen, but in + // case it does, let's simply drop + // the packet + return -1; + } + + if (!s) { + return -1; + } + /* + * A 802.3 packet comes from the qemu network. The + * access points turns it into a 802.11 frame and + * forwards it to the wireless device + */ + frame = Esp32_WLAN_create_data_packet(s, buf, size); + if (frame) { + if(s->mode == Esp32_Mode_Station){ + memcpy(s->ap_macaddr,s->associated_ap_macaddr,6); + + if(s->ap_state==Esp32_WLAN__STATE_STA_ASSOCIATED) { + frame->frame_control.flags=1; + // if it's an arp request put the correct reply mac address in the packet + if( frame->data_and_fcs[6]==8 && frame->data_and_fcs[7]==6) { + memcpy(frame->data_and_fcs+16,s->macaddr,6); + } + } + } + else{ + memcpy(frame->destination_address, s->softap_macaddr,6); + if(s->ap_state==Esp32_WLAN__STATE_STA_ASSOCIATED) { + frame->frame_control.flags=1; + // if it's an arp request put the correct reply mac address in the packet + if( frame->data_and_fcs[6]==8 && frame->data_and_fcs[7]==6) { + memcpy(frame->data_and_fcs+16,s->ap_macaddr,6); + } + } + } + + + if(((buf[12] & 0xE0) == IEEE80211_ENCAPSULATED)&&(buf[13] == ((IEEE80211_TYPE_MGT << 4)|IEEE80211_TYPE_MGT_SUBTYPE_ACTION))) + { + unsigned long ethernet_frame_size; + unsigned char ethernet_frame[1518]; + struct mac80211_frame *reply = NULL; + + //discard packages from others channels + if((buf[12] & 0x0F) != esp32_wifi_channel) return -1; + //discard packages originated from the same mac address + if(!memcmp(&buf[6],s->macaddr,6)) return -1; + //check destination + if((memcmp(&buf[0],BROADCAST,6))&&(memcmp(&buf[0],s->macaddr,6))) return -1; + + //action frame + Esp32_WLAN_init_ap_frame(s, frame); + memcpy(frame->data_and_fcs, buf+14, size-14); + memcpy(frame->destination_address, &buf[0], 6); + memcpy(frame->source_address, &buf[6], 6); + memset(frame->bssid_address, 0xff, 6); + frame->frame_control.type= IEEE80211_TYPE_MGT; + frame->frame_control.sub_type= IEEE80211_TYPE_MGT_SUBTYPE_ACTION; + frame->frame_control.flags= ((buf[12] & 0xF0) == IEEE80211_ENCAPSULATED_PROTECTED) ? 0x40 : 00; + frame->duration_id= 0 ; + frame->frame_length-=12; + Esp32_WLAN_insert_frame(s, frame); + + + //if destination is broadcast, donĀ“t send ack + if(!memcmp(&buf[0],BROADCAST,6)) return size; + + //Send Ack + reply = Esp32_WLAN_create_ack(); + memcpy(reply->destination_address, &buf[6], 6); + memcpy(reply->source_address, s->macaddr, 6); + memset(reply->bssid_address, 0xff, 6); + reply->duration_id=0; + + if(DEBUG) { + ANSI_FG_HCOLOR(RED); + macprint(reply->destination_address,"---------------\ndestination_address, 6); + memcpy(ðernet_frame[6], reply->source_address, 6); + + // add size of ethernet header + ethernet_frame_size = 14; + /* + * Send 802.3 frame + */ + qemu_send_packet(qemu_get_queue(s->nic), ethernet_frame, ethernet_frame_size); + + return size; + } + + if(((buf[12] & 0xF0)== IEEE80211_ENCAPSULATED)&&(buf[13] == ((IEEE80211_TYPE_CTL << 4)|IEEE80211_TYPE_CTL_SUBTYPE_ACK))) + { + + //discard packages from others channels + if((buf[12] & 0x0F) != esp32_wifi_channel) return -1; + //discard packages originated from the same mac address + if(!memcmp(&buf[6],s->macaddr,6)) return -1; + //check destination + if(memcmp(&buf[0],s->macaddr,6)) return -1; + + Esp32_WLAN_Set_Packet_Status(ESP32_PHYA_ACK); + timer_mod_anticipate(s->wait_ack_timer, qemu_clock_get_ns(QEMU_CLOCK_REALTIME)); + + Esp32_WLAN_init_ap_frame(s, frame); + memcpy(frame->destination_address, &buf[0], 6); + memcpy(frame->source_address, &buf[6], 6); + memset(frame->bssid_address, 0xff, 6); + frame->frame_control.type= IEEE80211_TYPE_CTL; + frame->frame_control.sub_type= IEEE80211_TYPE_CTL_SUBTYPE_ACK; + frame->frame_control.flags= 0; + frame->duration_id= 0 ; + frame->frame_length = 10; + + if(DEBUG) { + ANSI_FG_HCOLOR(GREEN); + macprint(&buf[6],"---------------\n>IN ACK received: source"); + ANSI_DEFAULT(); + } + infoprint(frame); + //ACK is not inserted in DMA + return size; + } + Esp32_WLAN_init_ap_frame(s, frame); + Esp32_WLAN_insert_frame(s, frame); + } + return size; +} +static void Esp32_WLAN_cleanup(NetClientState *ncs) { } + +static NetClientInfo net_info = { + .type = NET_CLIENT_DRIVER_NIC, + .size = sizeof(NICState), + .can_receive = Esp32_WLAN_can_receive, + .receive = Esp32_WLAN_receive, + .cleanup = Esp32_WLAN_cleanup, +}; + +void Esp32_WLAN_reset_ap(Esp32WifiState *s) { + esp32_wifi_channel = 0; + s->ap_state = Esp32_WLAN__STATE_NOT_AUTHENTICATED; + s->beacon_ap=0; + s->mode = Esp32_Mode_Station; + memcpy(s->ap_macaddr,(uint8_t[]){0x10,0x13,0x46,0xbf,0x31,0x50},sizeof(s->ap_macaddr)); + + s->inject_timer_running = 0; + s->inject_sequence_number = 0; + + s->inject_queue = NULL; + s->inject_queue_size = 0; +} + +void Esp32_WLAN_setup_ap(DeviceState *dev,Esp32WifiState *s) { + + Esp32_WLAN_reset_ap(s); + + s->beacon_timer = timer_new_ns(QEMU_CLOCK_REALTIME, Esp32_WLAN_beacon_timer, s); + timer_mod(s->beacon_timer, qemu_clock_get_ns(QEMU_CLOCK_REALTIME)+BEACON_TIME); + + // setup the timer but only schedule + // it when necessary... + s->inject_timer = timer_new_ns(QEMU_CLOCK_REALTIME, Esp32_WLAN_inject_timer, s); + + s->wait_ack_timer = timer_new_ns(QEMU_CLOCK_REALTIME, Esp32_WLAN_Wait_ACk_timer, s); + + qemu_macaddr_default_if_unset(&s->conf.macaddr); + memcpy(s->macaddr, s->conf.macaddr.a, sizeof(s->macaddr)); + s->nic = qemu_new_nic(&net_info, &s->conf, + object_get_typename(OBJECT(s)), dev->id, + &dev->mem_reentrancy_guard, s); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->macaddr); +} + +static void send_single_frame(Esp32WifiState *s, struct mac80211_frame *frame, struct mac80211_frame *reply) { + reply->sequence_control.sequence_number = s->inject_sequence_number++ +0x730; + reply->signal_strength=-10; + + if(frame) { + memcpy(reply->destination_address, frame->source_address, 6); + if(s->mode == Esp32_Mode_Station){ + memcpy(reply->source_address, s->macaddr, 6); + } + else{ + memcpy(reply->source_address, s->ap_macaddr, 6); + } + memcpy(reply->bssid_address, frame->source_address, 6); + } + + Esp32_WLAN_insert_frame(s, reply); +} + +void Esp32_WLAN_handle_frame(Esp32WifiState *s, struct mac80211_frame *frame) +{ + struct mac80211_frame *reply = NULL; + static access_point_info dummy_ap={0}; + static char ssid[64]; + unsigned long ethernet_frame_size; + unsigned char ethernet_frame[1518]; + + + if(DEBUG){ + if((ENABLE_BEACON)||!((frame->frame_control.type == IEEE80211_TYPE_MGT)&&(frame->frame_control.sub_type == IEEE80211_TYPE_MGT_SUBTYPE_BEACON))){ + ANSI_FG_HCOLOR(RED); + printf("-------------------------\nframe_control.type, + frame->frame_control.sub_type,esp32_wifi_channel,s->ap_state,(unsigned long) qemu_clock_get_ns(QEMU_CLOCK_REALTIME)); + ANSI_DEFAULT(); + } + } + if((ENABLE_BEACON)||!((frame->frame_control.type == IEEE80211_TYPE_MGT)&&(frame->frame_control.sub_type == IEEE80211_TYPE_MGT_SUBTYPE_BEACON))){ + infoprint(frame); + } + access_point_info *ap_info=0; + + if(s->mode == Esp32_Mode_Station){ + for(int i=0;iframe_control.type == IEEE80211_TYPE_MGT) { + switch(frame->frame_control.sub_type) { + case IEEE80211_TYPE_MGT_SUBTYPE_BEACON: + if(s->ap_state==Esp32_WLAN__STATE_NOT_AUTHENTICATED || s->ap_state==Esp32_WLAN__STATE_AUTHENTICATED) { + strncpy(ssid,(char *)frame->data_and_fcs+14,frame->data_and_fcs[13]); + if(DEBUG) printf("beacon from %s\n",ssid); + dummy_ap.ssid=ssid; + dummy_ap.channel = esp32_wifi_channel; + dummy_ap.sigstrength= -23; + memcpy(dummy_ap.mac_address,s->ap_macaddr,6) ; + memcpy(s->softap_macaddr,frame->bssid_address,6); + s->mode = Esp32_Mode_SoftAP; + s->ap_state=Esp32_WLAN__STATE_STA_NOT_AUTHENTICATED; + send_single_frame(s,frame,Esp32_WLAN_create_probe_request(&dummy_ap)); + } + break; + case IEEE80211_TYPE_MGT_SUBTYPE_ACTION: + if(DEBUG) printf("action\n"); + /* + * The access point uses the 802.11 frame + * and sends a 802.3 frame into the network... + * This packet is then understandable by + * qemu-slirp + * + * If we ever want the access point to offer + * some services, it can be added here!! + */ + // ethernet header type + ethernet_frame[12] = ((frame->frame_control.flags & 0x40) ? IEEE80211_ENCAPSULATED_PROTECTED : IEEE80211_ENCAPSULATED)| esp32_wifi_channel; + ethernet_frame[13] = ((IEEE80211_TYPE_MGT << 4)|IEEE80211_TYPE_MGT_SUBTYPE_ACTION); + + memcpy(ðernet_frame[0], frame->destination_address, 6); + memcpy(ðernet_frame[6], s->macaddr, 6); + + // add packet content + ethernet_frame_size = frame->frame_length -IEEE80211_HEADER_SIZE; + + if (ethernet_frame_size > sizeof(ethernet_frame)) { + ethernet_frame_size = sizeof(ethernet_frame); + } + memcpy(ðernet_frame[14], &frame->data_and_fcs[0], ethernet_frame_size); + // add size of ethernet header + ethernet_frame_size += 14; + /* + * Send 802.3 frame + */ + qemu_send_packet(qemu_get_queue(s->nic), ethernet_frame, ethernet_frame_size); + + //if destination is not broadcast wait for ack + if(memcmp(ðernet_frame[0],BROADCAST,6)){ + Esp32_WLAN_Set_Packet_Status(ESP32_PHYA_NACK); + timer_mod(s->wait_ack_timer, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + WAIT_ACK_TIMEOUT); + return; + } + break; + case IEEE80211_TYPE_MGT_SUBTYPE_PROBE_RESP: + ap_info=&dummy_ap; + strncpy(ssid,(char *)frame->data_and_fcs+14,frame->data_and_fcs[13]); + if(DEBUG) printf("probe resp from %s\n",ssid); + dummy_ap.ssid=ssid; + s->ap_state=Esp32_WLAN__STATE_STA_NOT_AUTHENTICATED; + send_single_frame(s,frame,Esp32_WLAN_create_deauthentication()); + send_single_frame(s,frame,Esp32_WLAN_create_authentication_request()); + break; + case IEEE80211_TYPE_MGT_SUBTYPE_ASSOCIATION_RESP: + if(DEBUG) printf("assoc resp\n"); + mac80211_frame *frame1=Esp32_WLAN_create_dhcp_discover(s); + memcpy(frame1->bssid_address,BROADCAST,6); + memcpy(frame1->source_address,frame->destination_address,6); + memcpy(frame1->destination_address,frame->source_address,6); + send_single_frame(s,0,frame1); + s->ap_state=Esp32_WLAN__STATE_STA_DHCP; + break; + case IEEE80211_TYPE_MGT_SUBTYPE_DISASSOCIATION: + DEBUG_PRINT_AP(("Received disassociation!\n")); + send_single_frame(s,frame,Esp32_WLAN_create_disassociation()); + if (s->ap_state == Esp32_WLAN__STATE_ASSOCIATED || s->ap_state == Esp32_WLAN__STATE_STA_ASSOCIATED) { + s->ap_state = Esp32_WLAN__STATE_AUTHENTICATED; + } + break; + case IEEE80211_TYPE_MGT_SUBTYPE_DEAUTHENTICATION: + DEBUG_PRINT_AP(("Received deauthentication!\n")); + //reply = Esp32_WLAN_create_authentication_response(ap_info); + if (s->ap_state == Esp32_WLAN__STATE_AUTHENTICATED) { + s->ap_state = Esp32_WLAN__STATE_NOT_AUTHENTICATED; + } + break; + case IEEE80211_TYPE_MGT_SUBTYPE_AUTHENTICATION: + DEBUG_PRINT_AP(("Received authentication!\n")); + if(frame->data_and_fcs[2]==2) { // response + send_single_frame(s,frame,Esp32_WLAN_create_association_request(&dummy_ap)); + } + break; + } + if(ap_info) { + memcpy(s->ap_macaddr,ap_info->mac_address,6); + switch(frame->frame_control.sub_type) { + case IEEE80211_TYPE_MGT_SUBTYPE_PROBE_REQ: + DEBUG_PRINT_AP(("Received probe request!\n")); + reply = Esp32_WLAN_create_probe_response(ap_info); + break; + case IEEE80211_TYPE_MGT_SUBTYPE_AUTHENTICATION: + DEBUG_PRINT_AP(("Received authentication req!\n")); + if(frame->data_and_fcs[2]==1) { // request + reply = Esp32_WLAN_create_authentication_response(ap_info); + if (s->ap_state == Esp32_WLAN__STATE_NOT_AUTHENTICATED) { + s->ap_state = Esp32_WLAN__STATE_AUTHENTICATED; + } + } + break; + case IEEE80211_TYPE_MGT_SUBTYPE_ASSOCIATION_REQ: + DEBUG_PRINT_AP(("Received association request!\n")); + reply = Esp32_WLAN_create_association_response(ap_info); + if (s->ap_state == Esp32_WLAN__STATE_AUTHENTICATED) { + s->ap_state = Esp32_WLAN__STATE_ASSOCIATED; + memcpy(s->associated_ap_macaddr,s->ap_macaddr,6); + } + break; + } + if (reply) { + reply->signal_strength=ap_info->sigstrength; + memcpy(reply->destination_address, frame->source_address, 6); + Esp32_WLAN_init_ap_frame(s, reply); + Esp32_WLAN_insert_frame(s, reply); + } + } + } + if ((frame->frame_control.type == IEEE80211_TYPE_DATA) && + (frame->frame_control.sub_type == IEEE80211_TYPE_DATA_SUBTYPE_DATA)) { + if(s->ap_state == Esp32_WLAN__STATE_STA_DHCP) { + dhcp_request_t *req=(dhcp_request_t *)&frame->data_and_fcs[8]; + // check for a dhcp offer + if(req->dhcp.bp_options[0]==0x35 && req->dhcp.bp_options[2]==0x2) { + mac80211_frame *frame1=Esp32_WLAN_create_dhcp_request(s,req->dhcp.yiaddr); + memcpy(frame1->bssid_address,BROADCAST,6); + memcpy(frame1->source_address,s->ap_macaddr,6); + memcpy(frame1->destination_address,frame->source_address,6); + send_single_frame(s,0,frame1); + //memcpy(s->ap_macaddr,(uint8_t[]){0x10,0x01,0x00,0xc4,0x0a,0x25},sizeof(s->ap_macaddr)); + //memcpy(s->associated_ap_macaddr,s->ap_macaddr,sizeof(s->ap_macaddr)); + memcpy(s->associated_ap_macaddr,s->macaddr,sizeof(s->macaddr)); + s->ap_state=Esp32_WLAN__STATE_STA_ASSOCIATED; + if(DEBUG) printf("Dhcp received addr %d.%d.%d.%d\n",req->dhcp.yiaddr[0],req->dhcp.yiaddr[1],req->dhcp.yiaddr[2],req->dhcp.yiaddr[3]); + } + } else if (s->ap_state == Esp32_WLAN__STATE_ASSOCIATED || s->ap_state == Esp32_WLAN__STATE_STA_ASSOCIATED) { + /* + * The access point uses the 802.11 frame + * and sends a 802.3 frame into the network... + * This packet is then understandable by + * qemu-slirp + * + * If we ever want the access point to offer + * some services, it can be added here!! + */ + // ethernet header type + ethernet_frame[12] = frame->data_and_fcs[6]; + ethernet_frame[13] = frame->data_and_fcs[7]; + + // the new originator of the packet is + // the access point + if(s->ap_state == Esp32_WLAN__STATE_ASSOCIATED) + memcpy(ðernet_frame[6], s->ap_macaddr, 6); + else + memcpy(ðernet_frame[6], s->macaddr, 6); + + if (ethernet_frame[12] == 0x08 && ethernet_frame[13] == 0x06) { + // for arp request, we use a broadcast + memset(ðernet_frame[0], 0xff, 6); + } else { + // otherwise we forward the packet to + // where it really belongs + memcpy(ðernet_frame[0], frame->destination_address, 6); + } + + // add packet content + ethernet_frame_size = frame->frame_length - IEEE80211_HEADER_SIZE - 4 - 8; + + // for some reason, the packet is 22 bytes too small (??) + ethernet_frame_size += 22; + if (ethernet_frame_size > sizeof(ethernet_frame)) { + ethernet_frame_size = sizeof(ethernet_frame); + } + memcpy(ðernet_frame[14], &frame->data_and_fcs[8], ethernet_frame_size); + // add size of ethernet header + ethernet_frame_size += 14; + /* + * Send 802.3 frame + */ + qemu_send_packet(qemu_get_queue(s->nic), ethernet_frame, ethernet_frame_size); + } + } + Esp32_WLAN_frame_delivered(s); +} + diff --git a/hw/misc/esp32_wlan.h b/hw/misc/esp32_wlan.h new file mode 100644 index 0000000000..474c92db70 --- /dev/null +++ b/hw/misc/esp32_wlan.h @@ -0,0 +1,280 @@ +/** + * QEMU WLAN device emulation + * + * Copyright (c) 2008 Clemens Kolbitsch + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Modifications: + * 2008-February-24 Clemens Kolbitsch : + * New implementation based on ne2000.c + * 18/1/22 Martin Johnson : Modified for esp32 wifi emulation + * + */ + +#ifndef esp32_wlan_h +#define esp32_wlan_h 1 + + +//#define DEBUG_Esp32_WLAN + +#ifdef DEBUG_Esp32_WLAN +#define DEBUG_PRINT_AP(x) \ + do { \ + struct timeval __tt; \ + gettimeofday(&__tt, NULL); \ + printf("%u:%u ", (unsigned)__tt.tv_sec, (unsigned)__tt.tv_usec); \ + printf x ;\ + } while (0) +#else +#define DEBUG_PRINT_AP(x) +#endif + + +#define IEEE80211_IDLE 0xff + +#define IEEE80211_TYPE_MGT 0x00 +#define IEEE80211_TYPE_CTL 0x01 +#define IEEE80211_TYPE_DATA 0x02 + +#define IEEE80211_ENCAPSULATED 0xA0 +#define IEEE80211_ENCAPSULATED_PROTECTED 0xB0 + +#define IEEE80211_TYPE_MGT_SUBTYPE_BEACON 0x08 +#define IEEE80211_TYPE_MGT_SUBTYPE_ACTION 0x0d +#define IEEE80211_TYPE_MGT_SUBTYPE_PROBE_REQ 0x04 +#define IEEE80211_TYPE_MGT_SUBTYPE_PROBE_RESP 0x05 +#define IEEE80211_TYPE_MGT_SUBTYPE_AUTHENTICATION 0x0b +#define IEEE80211_TYPE_MGT_SUBTYPE_DEAUTHENTICATION 0x0c +#define IEEE80211_TYPE_MGT_SUBTYPE_ASSOCIATION_REQ 0x00 +#define IEEE80211_TYPE_MGT_SUBTYPE_ASSOCIATION_RESP 0x01 +#define IEEE80211_TYPE_MGT_SUBTYPE_DISASSOCIATION 0x0a + +#define IEEE80211_TYPE_CTL_SUBTYPE_ACK 0x0d + +#define IEEE80211_TYPE_DATA_SUBTYPE_DATA 0x00 + +#define IEEE80211_BEACON_PARAM_SSID 0x00 +#define IEEE80211_BEACON_PARAM_RATES 0x01 +#define IEEE80211_BEACON_PARAM_CHANNEL 0x03 +#define IEEE80211_BEACON_PARAM_EXTENDED_RATES 0x32 +#define IEEE80211_BEACON_PARAM_TIM 0x05 + + +#define IEEE80211_HEADER_SIZE 24 + +typedef struct beacon_info_t { + uint64_t timestamp; + uint16_t interval; + uint16_t capability; +} QEMU_PACKED beacon_info_t; + +typedef uint8_t macaddr_t[6]; + +#define BROADCAST (uint8_t[]){0xff,0xff,0xff,0xff,0xff,0xff} + +typedef struct mac80211_frame { + struct mac80211_frame_control { + unsigned protocol_version : 2; + unsigned type : 2; + unsigned sub_type : 4; + unsigned flags:8; + } QEMU_PACKED frame_control; + uint16_t duration_id; + macaddr_t destination_address; + macaddr_t source_address; + macaddr_t bssid_address; + struct mac80211_sequence_control { + unsigned fragment_number : 4; + unsigned sequence_number : 12; + } QEMU_PACKED sequence_control; + // variable length, 2312 byte plus 4 byte frame-checksum + union { + uint8_t data_and_fcs[2316]; + beacon_info_t beacon_info; + }; + unsigned int frame_length; + int signal_strength; + int pos; + struct mac80211_frame *next_frame; +} QEMU_PACKED mac80211_frame; + +typedef struct access_point_info { + const char *ssid; + int channel; + int sigstrength; + macaddr_t mac_address; +} access_point_info; + +enum esp32_ap_state { + Esp32_WLAN__STATE_NOT_AUTHENTICATED, + Esp32_WLAN__STATE_AUTHENTICATED, + Esp32_WLAN__STATE_ASSOCIATED, + Esp32_WLAN__STATE_STA_NOT_AUTHENTICATED, + Esp32_WLAN__STATE_STA_AUTHENTICATED, + Esp32_WLAN__STATE_STA_ASSOCIATED, + Esp32_WLAN__STATE_STA_DHCP, +}; + +#define Esp32_WLAN__MAX_INJECT_QUEUE_SIZE 20 + +typedef struct { + signed rssi:8; /**< Received Signal Strength Indicator(RSSI) of packet. unit: dBm */ + unsigned rate:5; /**< PHY rate encoding of the packet. Only valid for non HT(11bg) packet */ + unsigned :1; /**< reserved */ + unsigned sig_mode:2; /**< 0: non HT(11bg) packet; 1: HT(11n) packet; 3: VHT(11ac) packet */ + unsigned legacy_length:12; /**< copy of the length */ + unsigned damatch0:1; /* destination matches address0 */ + unsigned damatch1:1; /* destination matches address1 */ + unsigned bssidmatch0:1; /* bssid matches address0 */ + unsigned bssidmatch1:1; /* bssid matches address1 */ + unsigned mcs:7; /**< Modulation Coding Scheme. If is HT(11n) packet, shows the modulation, range from 0 to 76(MSC0 ~ MCS76) */ + unsigned cwb:1; /**< Channel Bandwidth of the packet. 0: 20MHz; 1: 40MHz */ + unsigned :16; /**< reserved */ + unsigned smoothing:1; /**< reserved */ + unsigned not_sounding:1; /**< reserved */ + unsigned :1; /**< reserved */ + unsigned aggregation:1; /**< Aggregation. 0: MPDU packet; 1: AMPDU packet */ + unsigned stbc:2; /**< Space Time Block Code(STBC). 0: non STBC packet; 1: STBC packet */ + unsigned fec_coding:1; /**< Flag is set for 11n packets which are LDPC */ + unsigned sgi:1; /**< Short Guide Interval(SGI). 0: Long GI; 1: Short GI */ + signed noise_floor:8; /**< noise floor of Radio Frequency Module(RF). unit: 0.25dBm*/ + unsigned ampdu_cnt:8; /**< ampdu cnt */ + unsigned channel:4; /**< primary channel on which this packet is received */ + unsigned secondary_channel:4; /**< secondary channel on which this packet is received. 0: none; 1: above; 2: below */ + unsigned :8; /**< reserved */ + unsigned timestamp:32; /**< timestamp. The local time when this packet is received. It is precise only if modem sleep or light sleep is not enabled. unit: microsecond */ + unsigned :32; /**< reserved */ + unsigned :31; /**< reserved */ + unsigned ant:1; /**< antenna number from which this packet is received. 0: WiFi antenna 0; 1: WiFi antenna 1 */ + unsigned sig_len:12; /**< length of packet including Frame Check Sequence(FCS) */ + unsigned sig_len_copy:12; /**< another copy of the length */ + unsigned rx_state:8; /**< state of the packet. 0: no error; others: error numbers which are not public */ +} QEMU_PACKED wifi_pkt_rx_ctrl_t; + + +typedef struct { + signed rssi:8; /**< Received Signal Strength Indicator(RSSI) of packet. unit: dBm */ + unsigned rate:5; /**< PHY rate encoding of the packet. Only valid for non HT(11bg) packet */ + unsigned :1; /**< reserved */ + unsigned sig_mode:2; /**< 0: non HT(11bg) packet; 1: HT(11n) packet; 3: VHT(11ac) packet */ + unsigned legacy_length:12; /**< copy of the length */ + unsigned damatch0:1; /* destination matches address0 */ + unsigned damatch1:1; /* destination matches address1 */ + unsigned bssidmatch0:1; /* bssid matches address0 */ + unsigned bssidmatch1:1; /* bssid matches address1 */ + unsigned mcs:7; /**< Modulation Coding Scheme. If is HT(11n) packet, shows the modulation, range from 0 to 76(MSC0 ~ MCS76) */ + unsigned cwb:1; /**< Channel Bandwidth of the packet. 0: 20MHz; 1: 40MHz */ + unsigned :16; /**< reserved */ + unsigned smoothing:1; /**< reserved */ + unsigned not_sounding:1; /**< reserved */ + unsigned :1; /**< reserved */ + unsigned aggregation:1; /**< Aggregation. 0: MPDU packet; 1: AMPDU packet */ + unsigned stbc:2; /**< Space Time Block Code(STBC). 0: non STBC packet; 1: STBC packet */ + unsigned fec_coding:1; /**< Flag is set for 11n packets which are LDPC */ + unsigned sgi:1; /**< Short Guide Interval(SGI). 0: Long GI; 1: Short GI */ + unsigned :8; /**< reserved */ + unsigned ampdu_cnt:8; /**< ampdu cnt */ + unsigned channel:4; /**< primary channel on which this packet is received */ + unsigned secondary_channel:4; /**< secondary channel on which this packet is received. 0: none; 1: above; 2: below */ + unsigned :8; /**< reserved */ + unsigned timestamp:32; /**< timestamp. The local time when this packet is received. It is precise only if modem sleep or light sleep is not enabled. unit: microsecond */ + unsigned :32; /**< reserved */ + signed noise_floor:8; /**< noise floor of Radio Frequency Module(RF). unit: dBm*/ + unsigned :24; /**< reserved */ + unsigned :32; /**< reserved */ + unsigned :31; /**< reserved */ + unsigned ant:1; /**< antenna number from which this packet is received. 0: WiFi antenna 0; 1: WiFi antenna 1 */ + unsigned :32; /**< reserved */ + unsigned :32; /**< reserved */ + unsigned :32; /**< reserved */ + unsigned sig_len:12; /**< length of packet including Frame Check Sequence(FCS) */ + unsigned sig_len_copy:12; /**< another copy of the length */ + unsigned rx_state:8; /**< state of the packet. 0: no error; others: error numbers which are not public */ +} QEMU_PACKED wifi_pkt_rx_ctrl_c3_t; + +extern int esp32_wifi_channel; + +typedef uint8_t ip4_t[4]; + +#define DHCP_CHADDR_LEN 16 +#define DHCP_SNAME_LEN 64 +#define DHCP_FILE_LEN 128 + +typedef struct ip_header_t { + uint8_t version_size; + uint8_t dscp_ecn; + uint8_t len_h; + uint8_t len_l; + uint32_t z; + uint8_t ttl; + uint8_t protocol; + uint16_t checksum; + ip4_t source_ip; + ip4_t dest_ip; +}ip_header_t ; + +typedef struct udp_header_t { + uint8_t src_port_h; + uint8_t src_port_l; + uint8_t dest_port_h; + uint8_t dest_port_l; + uint8_t len_h; + uint8_t len_l; + uint16_t checksum; +} udp_header_t; + +typedef struct dhcp_t { + uint8_t opcode; + uint8_t htype; + uint8_t hlen; + uint8_t hops; + uint32_t xid; + uint16_t secs; + uint16_t flags; + ip4_t ciaddr; + ip4_t yiaddr; + ip4_t siaddr; + ip4_t giaddr; + uint8_t chaddr[DHCP_CHADDR_LEN]; + char bp_sname[DHCP_SNAME_LEN]; + char bp_file[DHCP_FILE_LEN]; + uint32_t magic_cookie; + uint8_t bp_options[0]; +} dhcp_t; + +typedef struct dhcp_request_t { + ip_header_t ipheader; + udp_header_t udpheader; + dhcp_t dhcp; +} dhcp_request_t; + +typedef struct arp_header_t { + uint16_t htype; + uint16_t ptype; + uint8_t hlen; + uint8_t plen; + uint16_t operation; + uint8_t sender_mac[6]; + uint8_t sender_ip[4]; + uint8_t target_mac[6]; + uint8_t target_ip[4]; +}arp_header_t; + +#endif // esp32_wlan_h diff --git a/hw/misc/esp32_wlan_packet.c b/hw/misc/esp32_wlan_packet.c new file mode 100644 index 0000000000..fa0e3d4d1d --- /dev/null +++ b/hw/misc/esp32_wlan_packet.c @@ -0,0 +1,306 @@ +/** + * QEMU WLAN access point emulation + * + * Copyright (c) 2008 Clemens Kolbitsch + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Modifications: + * 2008-February-24 Clemens Kolbitsch : + * New implementation based on ne2000.c + * 18/1/22 Martin Johnson : Modified for esp32 wifi emulation + */ + +#include "qemu/osdep.h" + +#include "net/net.h" + +#include "esp32_wlan.h" +#include "esp32_wlan_packet.h" + +// the frame checksum isn't used so just put zero in there. +void insertCRC(mac80211_frame *frame) { + unsigned long crc; + unsigned char *fcs = (unsigned char *)frame; + crc = 0; + memcpy(fcs+frame->frame_length, &crc, 4); + frame->frame_length += 4; +} + +static void add_byte(mac80211_frame *frame, uint8_t data) { + frame->data_and_fcs[frame->pos++]=data; + frame->frame_length=IEEE80211_HEADER_SIZE+frame->pos; +} + +static void add_data(mac80211_frame *frame, int len,uint8_t *data) { + for(int i=0;isequence_control.sequence_number = s->inject_sequence_number++; + if(s->mode == Esp32_Mode_Station){ + if(s->ap_state == Esp32_WLAN__STATE_STA_ASSOCIATED) + memcpy(frame->source_address, s->macaddr, 6); + else + memcpy(frame->source_address, s->ap_macaddr, 6); + memcpy(frame->bssid_address, s->ap_macaddr, 6); + } + else{ + if(s->ap_state == Esp32_WLAN__STATE_STA_ASSOCIATED) + memcpy(frame->source_address, s->ap_macaddr, 6); + else + memcpy(frame->source_address, s->softap_macaddr, 6); + memcpy(frame->bssid_address, s->softap_macaddr, 6); + } + +} + +static mac80211_frame *new_frame(unsigned type, unsigned subtype) { + mac80211_frame *frame = (mac80211_frame *)malloc(sizeof(mac80211_frame)); + frame->next_frame = NULL; + frame->frame_control.protocol_version = 0; + frame->frame_control.type = type; + frame->frame_control.sub_type = subtype; + frame->frame_control.flags = 0; + frame->duration_id = 314; + frame->sequence_control.fragment_number = 0; + frame->pos=0; + return frame; +} + +static void add_rates(mac80211_frame *frame) { + add_tag(frame,IEEE80211_BEACON_PARAM_RATES,8,(uint8_t[]){0x8b,0x96,0x82,0x84,0x0c,0x18,0x30,0x60}); + add_tag(frame,IEEE80211_BEACON_PARAM_EXTENDED_RATES,4,(uint8_t[]){0x6c,0x12,0x24,0x48}); +} + +static void add_ssid(mac80211_frame *frame, const char *ssid) { + add_tag(frame,IEEE80211_BEACON_PARAM_SSID,strlen(ssid),(uint8_t *)ssid); +} + +mac80211_frame *Esp32_WLAN_create_beacon_frame(access_point_info *ap) { + mac80211_frame *frame=new_frame(IEEE80211_TYPE_MGT,IEEE80211_TYPE_MGT_SUBTYPE_BEACON); + frame->signal_strength=ap->sigstrength; + memcpy(frame->destination_address,BROADCAST,6); + frame->beacon_info.timestamp=qemu_clock_get_ns(QEMU_CLOCK_REALTIME)/1000; + frame->beacon_info.interval=1000; + frame->beacon_info.capability=1; + frame->pos=12; + add_ssid(frame,ap->ssid); + add_rates(frame); + add_tag(frame,IEEE80211_BEACON_PARAM_CHANNEL,1,(uint8_t[]){ap->channel}); + add_tag(frame,IEEE80211_BEACON_PARAM_TIM,4,(uint8_t[]){4,1,3,0,0}); + return frame; +} + +static uint16_t in_cksum(uint16_t *addr, int len) { + int sum = 0; + uint16_t answer = 0; + uint16_t *w = addr; + int nleft = len; + while (nleft > 1) { + sum += *w++; + nleft -= 2; + } + /* mop up an odd byte, if necessary */ + if (nleft == 1) { + *(uint8_t *)(&answer) = *(uint8_t *) w; + sum += answer; + } + sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* truncate to 16 bits */ + return (answer); +} + +static mac80211_frame *Esp32_WLAN_create_dhcp_frame(Esp32WifiState *s, int cmd_size, uint8_t dhcp_commands[]) { + mac80211_frame *frame=new_frame(IEEE80211_TYPE_DATA,IEEE80211_TYPE_DATA_SUBTYPE_DATA); + frame->frame_control.flags=1; + add_data(frame,8,(uint8_t[]){ 0xaa, 0xaa ,0x03 ,00 ,00 ,00 ,8 ,00}); + dhcp_request_t req={ + {.version_size=0x45,.ttl=0xff,.protocol=0x11,.dest_ip={0xff,0xff,0xff,0xff}}, + {.src_port_l=0x44,.dest_port_l=0x43}, + {.htype=1,.hlen=6,.xid=0x1d3d00,.chaddr={0,0,0,0,0,0}, + .magic_cookie=0x63538263} + }; + memcpy(req.dhcp.chaddr, s->macaddr, 6); + int len=sizeof(req)+cmd_size; + req.ipheader.len_h=len>>8; + req.ipheader.len_l=len&0xff; + len=len-sizeof(ip_header_t); + req.udpheader.len_h=len>>8; + req.udpheader.len_l=len&0xff; + req.ipheader.checksum=in_cksum((void *)&req.ipheader,sizeof(ip_header_t)); + add_data(frame,sizeof(req),(uint8_t *)&req); + add_data(frame,cmd_size,dhcp_commands); + return frame; +} + +mac80211_frame *Esp32_WLAN_create_dhcp_request(Esp32WifiState *s, uint8_t *ip) { + uint8_t dhcp_commands[]={ + 0x35, 1, 3, + 0x39, 2 ,5 ,0xdc , + 0x32, 4, ip[0],ip[1],ip[2],ip[3], + 0x3d, 0x07, 0x01, 0x3c, 0x61, 0x05, 0x0d, 0x99, 0x24, + 0x37, 0x04, 0x01, 0x03, 0x1c, 0x06, + 0xff, 0, 0 + }; + return Esp32_WLAN_create_dhcp_frame(s, sizeof(dhcp_commands),dhcp_commands); +} + +mac80211_frame *Esp32_WLAN_create_dhcp_discover(Esp32WifiState *s) { + uint8_t dhcp_commands[]={ + 0x35, 1, 1, + 0x39, 2 ,5 ,0xdc , + 0x0c ,0x09 ,0x65 ,0x73 ,0x70 ,0x72 ,0x65 ,0x73 ,0x73 ,0x69 ,0x66 , + 0x3d ,0x07, 0x01 ,0x3c ,0x61 ,0x05 ,0x0d ,0x99 ,0x24 , + 0x37 ,0x04 ,0x01 ,0x03 ,0x1c ,0x06 , + 0xff, 0,0 + }; + return Esp32_WLAN_create_dhcp_frame(s,sizeof(dhcp_commands),dhcp_commands); +} + +mac80211_frame *Esp32_WLAN_create_association_request(access_point_info *ap) { + mac80211_frame *frame=new_frame(IEEE80211_TYPE_MGT,IEEE80211_TYPE_MGT_SUBTYPE_ASSOCIATION_REQ); + add_data(frame,4,(uint8_t []){0x21,4,3,0}); + add_ssid(frame,ap->ssid); + add_rates(frame); + return frame; +} + +mac80211_frame *Esp32_WLAN_create_ack(void) { + mac80211_frame *frame=new_frame(IEEE80211_TYPE_CTL,IEEE80211_TYPE_CTL_SUBTYPE_ACK); + frame->frame_length=10; + return frame; +} + +mac80211_frame *Esp32_WLAN_create_probe_response(access_point_info *ap) { + mac80211_frame *frame=new_frame(IEEE80211_TYPE_MGT,IEEE80211_TYPE_MGT_SUBTYPE_PROBE_RESP); + frame->beacon_info.timestamp=qemu_clock_get_ns(QEMU_CLOCK_REALTIME)/1000; + frame->beacon_info.interval=1000; + frame->beacon_info.capability=1; + frame->pos=12; + add_ssid(frame,ap->ssid); + add_rates(frame); + add_tag(frame,IEEE80211_BEACON_PARAM_CHANNEL,1,(uint8_t[]){ap->channel}); + return frame; +} + +mac80211_frame *Esp32_WLAN_create_probe_request(access_point_info *ap) { + mac80211_frame *frame=new_frame(IEEE80211_TYPE_MGT,IEEE80211_TYPE_MGT_SUBTYPE_PROBE_REQ); + memcpy(frame->destination_address,BROADCAST,6); + memcpy(frame->bssid_address,BROADCAST,6); + add_ssid(frame,ap->ssid); + add_tag(frame,IEEE80211_BEACON_PARAM_CHANNEL,1,(uint8_t[]){ap->channel}); + add_rates(frame); + return frame; +} + +mac80211_frame *Esp32_WLAN_create_authentication_response(access_point_info *ap) { + mac80211_frame *frame=new_frame(IEEE80211_TYPE_MGT,IEEE80211_TYPE_MGT_SUBTYPE_AUTHENTICATION); + /* + * Fixed params... typical AP params (6 byte) + * + * They include + * - Authentication Algorithm (here: Open System) + * - Authentication SEQ + * - Status code (successful 0x0) + */ + add_data(frame,6,(uint8_t []){0,0,2,0,0,0}); + add_ssid(frame,ap->ssid); + return frame; +} + +mac80211_frame *Esp32_WLAN_create_authentication_request(void) { + mac80211_frame *frame=new_frame(IEEE80211_TYPE_MGT,IEEE80211_TYPE_MGT_SUBTYPE_AUTHENTICATION); + /* + * Fixed params... typical AP params (6 byte) + * + * They include + * - Authentication Algorithm (here: Open System) + * - Authentication SEQ + * - Status code (successful 0x0) + */ + //frame->duration_id=0x13a; + add_data(frame,6,(uint8_t []){0,0,1,0,0,0}); + //add_data(frame,11,(uint8_t []){0,0,0,0,0,0,0,0,0,0,0}); + //add_ssid(frame,ap->ssid); + return frame; +} + +mac80211_frame *Esp32_WLAN_create_deauthentication(void) { + mac80211_frame *frame=new_frame(IEEE80211_TYPE_MGT,IEEE80211_TYPE_MGT_SUBTYPE_DEAUTHENTICATION); + /* + * Insert reason code: + * "Deauthentication because sending STA is leaving" + */ + add_data(frame,2,(uint8_t []){3,0}); + return frame; +} + +mac80211_frame *Esp32_WLAN_create_association_response(access_point_info *ap) { + mac80211_frame *frame=new_frame(IEEE80211_TYPE_MGT,IEEE80211_TYPE_MGT_SUBTYPE_ASSOCIATION_RESP); + /* + * Fixed params... typical AP params (6 byte) + * + * They include + * - Capability Information + * - Status code (successful 0x0) + * - Association ID + */ + add_data(frame,6,(uint8_t []){33,4,0,0,1,0xc0}); + add_ssid(frame,ap->ssid); + add_rates(frame); + return frame; +} + +mac80211_frame *Esp32_WLAN_create_disassociation(void) { + mac80211_frame *frame=new_frame(IEEE80211_TYPE_MGT,IEEE80211_TYPE_MGT_SUBTYPE_DISASSOCIATION); + /* + * Insert reason code: + * "Disassociation because sending STA is leaving" + */ + add_data(frame,2,(uint8_t []){3,0}); + return frame; +} + +mac80211_frame *Esp32_WLAN_create_data_packet(Esp32WifiState *s, const uint8_t *buf, int size) { + mac80211_frame *frame=new_frame(IEEE80211_TYPE_DATA,IEEE80211_TYPE_DATA_SUBTYPE_DATA); + + frame->frame_control.flags = 0x2; /* from station back to station via AP */ + frame->duration_id = 44; + /* send message to wlan-device */ + if(s->ap_state == Esp32_WLAN__STATE_STA_ASSOCIATED) { + memcpy(frame->destination_address, s->ap_macaddr, 6); + frame->frame_control.flags = 0x2; + } + else + memcpy(frame->destination_address, s->macaddr, 6); + /* LLC */ + add_data(frame,6,(uint8_t[]){ 0xaa, 0xaa ,0x03 ,0 ,0 ,0}); + memcpy(frame->data_and_fcs+6, buf+12, size-12); + frame->frame_length = IEEE80211_HEADER_SIZE + size-6; + return frame; +} diff --git a/hw/misc/esp32_wlan_packet.h b/hw/misc/esp32_wlan_packet.h new file mode 100644 index 0000000000..53e64bbcc6 --- /dev/null +++ b/hw/misc/esp32_wlan_packet.h @@ -0,0 +1,56 @@ +/** + * QEMU WLAN access point emulation + * + * Copyright (c) 2008 Clemens Kolbitsch + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Modifications: + * 2008-February-24 Clemens Kolbitsch : + * New implementation based on ne2000.c + * 18/1/22 Martin Johnson : Modified for esp32 wifi emulation + * + */ + +#ifndef esp32_wlan_packet_h +#define esp32_wlan_packet_h 1 + +#include "esp32_wlan.h" +#include "hw/misc/esp32_wifi.h" + +void Esp32_WLAN_init_ap_frame(Esp32WifiState *s, struct mac80211_frame *frame); +int Esp32_WLAN_dumpFrame(struct mac80211_frame *frame, int frame_len, char *filename); +void Esp32_WLAN_insert_frame(Esp32WifiState *s, struct mac80211_frame *frame); +struct mac80211_frame *Esp32_WLAN_create_beacon_frame(access_point_info *ap); +struct mac80211_frame *Esp32_WLAN_create_probe_response(access_point_info *ap); +struct mac80211_frame *Esp32_WLAN_create_probe_request(access_point_info *ap); +struct mac80211_frame *Esp32_WLAN_create_authentication_request(void); +struct mac80211_frame *Esp32_WLAN_create_authentication_response(access_point_info *ap); +struct mac80211_frame *Esp32_WLAN_create_deauthentication(void); +struct mac80211_frame *Esp32_WLAN_create_association_request(access_point_info *ap); +struct mac80211_frame *Esp32_WLAN_create_association_response(access_point_info *ap); +struct mac80211_frame *Esp32_WLAN_create_disassociation(void); +struct mac80211_frame *Esp32_WLAN_create_data_reply(Esp32WifiState *s, struct mac80211_frame *incoming); +struct mac80211_frame *Esp32_WLAN_create_data_packet(Esp32WifiState *s, const uint8_t *buf, int size); +struct mac80211_frame *Esp32_WLAN_create_ack(void); +struct mac80211_frame *Esp32_WLAN_create_dhcp_discover(Esp32WifiState *s); +struct mac80211_frame *Esp32_WLAN_create_dhcp_request(Esp32WifiState *s, uint8_t *ip); +void insertCRC(mac80211_frame *frame); + +#endif // esp32_wlan_packet_h diff --git a/hw/misc/esp32c3_ana.c b/hw/misc/esp32c3_ana.c new file mode 100644 index 0000000000..3e8b5c8437 --- /dev/null +++ b/hw/misc/esp32c3_ana.c @@ -0,0 +1,87 @@ +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qemu/guest-random.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/misc/esp32c3_ana.h" + +#define DEBUG 0 + +int esp32_wifi_channel = 0; + +static uint64_t esp32c3_ana_read(void *opaque, hwaddr addr, unsigned int size) +{ + Esp32C3AnaState *s = ESP32C3_ANA(opaque); + uint32_t r = s->mem[addr/4]; + switch(addr) { + case 0x00: + case 0x48: + r= 0x00FFFFFF; + break; + case 0x50: + r=0x07000000 | s->mem[addr/4]; + break; + case 0x04: + r= 0xFDFFFFFF; + break; + case 0x44: + case 0x4C: + case 0xC4: + r=0xFFFFFFFF; + break; + } + if(DEBUG) printf("esp32c3_ana_read 0x%04lx= 0x%08x\n",(unsigned long) addr,r); + + return r; +} + +static void esp32c3_ana_write(void *opaque, hwaddr addr, uint64_t value, + unsigned int size) { + Esp32C3AnaState *s = ESP32C3_ANA(opaque); + + if(DEBUG) printf("esp32c3_ana_write 0x%04lx= 0x%08lx\n",(unsigned long) addr, (unsigned long) value); + + if(addr == 0x150) { + int v= (value & 0x0FF00000) >> 0x14; + esp32_wifi_channel=(v-7)/5; + if(esp32_wifi_channel > 14) esp32_wifi_channel = 14; + //printf("wifi channel:0x%08x ",(int)value); + //printf("===========> esp32_wifi_channel %i \n", esp32_wifi_channel); + } + + s->mem[addr/4]=value; +} + +static const MemoryRegionOps esp32c3_ana_ops = { + .read = esp32c3_ana_read, + .write = esp32c3_ana_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void esp32c3_ana_init(Object *obj) +{ + Esp32C3AnaState *s = ESP32C3_ANA(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &esp32c3_ana_ops, s, + TYPE_ESP32C3_ANA, 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + memset(s->mem,0,sizeof(s->mem)); +} + + +static const TypeInfo esp32c3_ana_info = { + .name = TYPE_ESP32C3_ANA, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Esp32C3AnaState), + .instance_init = esp32c3_ana_init, +}; + +static void esp32c3_ana_register_types(void) +{ + type_register_static(&esp32c3_ana_info); +} + +type_init(esp32c3_ana_register_types) diff --git a/hw/misc/esp32c3_pwrmng.c b/hw/misc/esp32c3_pwrmng.c new file mode 100644 index 0000000000..28d583b3d3 --- /dev/null +++ b/hw/misc/esp32c3_pwrmng.c @@ -0,0 +1,84 @@ +/* ESP32C3 unimp/dummy peripheral handler +*/ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/boards.h" +#include "hw/misc/esp32c3_pwrmng.h" +#include "hw/misc/esp32c3_reg.h" + + +#define DEBUG 0 + +static uint64_t esp32c3_pwrmng_read(void *opaque, hwaddr addr, unsigned int size) +{ + static bool init = false; + Esp32c3PwrMngState *s = ESP32C3_PWR_MANAGER(opaque); + + uint32_t r = s->mem[addr/4]; + + switch(addr){ + case 0x07C: + /* Return a random 32-bit value */ + if (!init) { + srand(time(NULL)); + init = true; + } + r = rand(); + break; + case 0x128: //DMA enabled + r = 0 ; + break; + case 0x118: //PWR int events + r = 0 ; + break; + } + + if(DEBUG) printf("esp32_pwrmng_read 0x%04lx= 0x%08x\n",(unsigned long) addr,r); + + return r; +} + +static void esp32c3_pwrmng_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + Esp32c3PwrMngState *s = ESP32C3_PWR_MANAGER(opaque); + + if(DEBUG) printf("esp32_pwrmng_write 0x%04lx= 0x%08lx\n",(unsigned long) addr, (unsigned long) value); + + s->mem[addr/4]=value; +} + +static const MemoryRegionOps esp32c3_pwrmng_ops = { + .read = esp32c3_pwrmng_read, + .write = esp32c3_pwrmng_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void esp32c3_pwrmng_init(Object *obj) +{ + Esp32c3PwrMngState *s = ESP32C3_PWR_MANAGER(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &esp32c3_pwrmng_ops, s, TYPE_ESP32C3_PWR_MANAGER, 0x1000); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const TypeInfo esp32c3_pwrmng_info = { + .name = TYPE_ESP32C3_PWR_MANAGER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Esp32c3PwrMngState), + .instance_init = esp32c3_pwrmng_init, +}; + +static void esp32c3_pwrmng_register_types(void) +{ + type_register_static(&esp32c3_pwrmng_info); +} + +type_init(esp32c3_pwrmng_register_types) diff --git a/hw/misc/esp32c3_wifi.c b/hw/misc/esp32c3_wifi.c new file mode 100644 index 0000000000..c31417d5c2 --- /dev/null +++ b/hw/misc/esp32c3_wifi.c @@ -0,0 +1,245 @@ +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qemu/guest-random.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" +#include "hw/hw.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "hw/misc/esp32c3_wifi.h" +#include "exec/address-spaces.h" +#include "esp32_wlan_packet.h" +#include "hw/qdev-properties.h" +#include "hw/misc/esp32c3_reg.h" + +#define DEBUG 0 + +extern access_point_info access_points[]; +extern int nb_aps; + +static uint64_t esp32C3_wifi_read(void *opaque, hwaddr addr, unsigned int size) +{ + + Esp32WifiState *s = ESP32_WIFI(opaque); + uint32_t r = s->mem[addr/4]; + + switch(addr) { + case A_C3_WIFI_DMA_IN_STATUS: + r = 0; + break; + case A_C3_WIFI_DMA_INT_STATUS: + case A_C3_WIFI_DMA_INT_CLR: + r = s->raw_interrupt; + break; + case A_C3_WIFI_STATUS: + case A_C3_WIFI_DMA_OUT_STATUS: + r = 1; + break; + default: + break; + } + + if (DEBUG) printf("esp32C3_wifi_read 0x%04lx= 0x%08x\n",(unsigned long) addr,r); + + return r; +} +static void set_interrupt(Esp32WifiState *s,int e) { + s->raw_interrupt |= e; + qemu_set_irq(s->irq, 1); +} + +void Esp32_WLAN_frame_delivered(Esp32WifiState *s){ + s->raw_interrupt |= 0x80; + qemu_set_irq(s->irq, 1); +} + +static void esp32C3_wifi_write(void *opaque, hwaddr addr, uint64_t value, + unsigned int size) { + Esp32WifiState *s = ESP32_WIFI(opaque); + if(DEBUG) printf("esp32C3_wifi_write 0x%04lx= 0x%08lx\n",(unsigned long) addr, (unsigned long) value); + + switch (addr) { + case A_C3_WIFI_DMA_INLINK: { + uint32_t offset = value & 0xfffff; + s->dma_inlink_address = offset ? 0x3fc00000 | offset : 0; + s->mem[0x90 / sizeof(uint32_t)] = s->dma_inlink_address; + break; + } + case A_C3_WIFI_DMA_INT_CLR: + s->raw_interrupt &= ~value; + if (s->raw_interrupt == 0) { + qemu_set_irq(s->irq, 0); + } + break; + case A_C3_WIFI_DMA_OUTLINK: + if ((value & 0xc0000000) && (value & 0xfffff)) { + mac80211_frame frame; + dma_list_item item; + unsigned memaddr = 0x3fc00000 | (value & 0xfffff); + address_space_read(&address_space_memory, memaddr, + MEMTXATTRS_UNSPECIFIED, &item, sizeof(item)); + if (item.length > sizeof(frame)) { + break; + } + address_space_read(&address_space_memory, item.address, + MEMTXATTRS_UNSPECIFIED, &frame, item.length); + frame.frame_length = item.length; + frame.next_frame = NULL; + Esp32_WLAN_handle_frame(s, &frame); + } + break; + default: + break; + } + s->mem[addr/4]=value; +} + +static int match_mac_address(uint8_t *a1,uint8_t *a2) { + if(!memcmp(a1,a2,6)) return 1; + if(!memcmp(a1,BROADCAST,6)) return 1; + return 0; +} +// frame from ap to esp32 +void Esp32_sendFrame(Esp32WifiState *s, mac80211_frame *frame,int length, int signal_strength) { + int packet_channel = esp32_wifi_channel; + + if (s->dma_inlink_address == 0) { + return; + } + if (frame->frame_control.type == IEEE80211_TYPE_MGT && + frame->frame_control.sub_type == IEEE80211_TYPE_MGT_SUBTYPE_BEACON) { + int pos = 12; + int data_length = length - IEEE80211_HEADER_SIZE; + + while (pos + 2 <= data_length) { + uint8_t tag = frame->data_and_fcs[pos]; + uint8_t tag_length = frame->data_and_fcs[pos + 1]; + + if (tag == IEEE80211_BEACON_PARAM_CHANNEL && tag_length) { + packet_channel = frame->data_and_fcs[pos + 2]; + break; + } + pos += 2 + tag_length; + } + } + if (packet_channel == 0) { + const uint8_t *address = frame->frame_control.type == IEEE80211_TYPE_DATA + ? frame->bssid_address : frame->source_address; + + for (int i = 0; i < nb_aps; i++) { + if (memcmp(access_points[i].mac_address, address, 6) == 0) { + packet_channel = access_points[i].channel; + break; + } + } + } + uint8_t header[sizeof(wifi_pkt_rx_ctrl_c3_t)+length]; + memset(header,0,sizeof(header)); + wifi_pkt_rx_ctrl_c3_t *pkt=(wifi_pkt_rx_ctrl_c3_t *)header; + *pkt=(wifi_pkt_rx_ctrl_c3_t){ + .rssi=(signal_strength+(rand()%10)+96), + .rate=11, + .sig_len=length, + .sig_len_copy=length, + .legacy_length=length, + .noise_floor=-97, + .channel=packet_channel, + .timestamp=qemu_clock_get_ns(QEMU_CLOCK_REALTIME)/1000, + }; + // These 4 bits are set if the mac addresses previously stored at 0x40 and 0x48 + // match the destination or bssid addresses in the frame + if(match_mac_address(frame->destination_address,(uint8_t *)s->mem+0x40)) + pkt->damatch0=1; + if(match_mac_address(frame->destination_address,(uint8_t *)s->mem+0x48)) + pkt->damatch1=1; + if(match_mac_address(frame->bssid_address,(uint8_t *)s->mem+0x40)) + pkt->bssidmatch0=1; + if(match_mac_address(frame->bssid_address,(uint8_t *)s->mem+0x48)) + pkt->bssidmatch1=1; + pkt->damatch0 = 1; + pkt->bssidmatch0 = 1; + + memcpy(header+sizeof(wifi_pkt_rx_ctrl_c3_t),frame,length); + length+=sizeof(wifi_pkt_rx_ctrl_c3_t); + // do a DMA transfer from the hardware to esp32 memory + dma_list_item item; + address_space_read(&address_space_memory, s->dma_inlink_address, + MEMTXATTRS_UNSPECIFIED, &item, sizeof(item)); + address_space_write(&address_space_memory, item.address, + MEMTXATTRS_UNSPECIFIED, header, length); + item.length = length; + item.eof = 1; + address_space_write(&address_space_memory, s->dma_inlink_address, + MEMTXATTRS_UNSPECIFIED, &item, sizeof(uint32_t)); + s->mem[0x8c / sizeof(uint32_t)] = item.next; + s->mem[0x90 / sizeof(uint32_t)] = s->dma_inlink_address; + if (item.next) { + s->dma_inlink_address = item.next; + } else { + uint32_t base = s->mem[A_C3_WIFI_DMA_INLINK / sizeof(uint32_t)]; + uint32_t offset = base & 0xfffff; + s->dma_inlink_address = offset ? 0x3fc00000 | offset : 0; + } + set_interrupt(s, 0x1004024); +} + +static const MemoryRegionOps esp32C3_wifi_ops = { + .read = esp32C3_wifi_read, + .write = esp32C3_wifi_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void esp32c3_wifi_reset(DeviceState *dev) +{ + Esp32WifiState *s = ESP32_WIFI(dev); + + s->dma_inlink_address=0; + memset(s->mem,0,sizeof(s->mem)); + Esp32_WLAN_reset_ap(s); +} + +static void esp32C3_wifi_realize(DeviceState *dev, Error **errp) +{ + Esp32WifiState *s = ESP32_WIFI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + s->dma_inlink_address=0; + + memory_region_init_io(&s->iomem, OBJECT(dev), &esp32C3_wifi_ops, s, + TYPE_ESP32_WIFI, 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); + memset(s->mem,0,sizeof(s->mem)); + Esp32_WLAN_setup_ap(dev, s); + +} +static Property esp32C3_wifi_properties[] = { + DEFINE_NIC_PROPERTIES(Esp32WifiState, conf), + DEFINE_PROP_END_OF_LIST(), +}; + +static void esp32C3_wifi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = esp32C3_wifi_realize; + device_class_set_legacy_reset(dc, esp32c3_wifi_reset); + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); + dc->desc = "Esp32C3 WiFi"; + device_class_set_props(dc, esp32C3_wifi_properties); +} + + +static const TypeInfo esp32C3_wifi_info = { + .name = TYPE_ESP32_WIFI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Esp32WifiState), + .class_init = esp32C3_wifi_class_init, +}; + +static void esp32C3_wifi_register_types(void) +{ + type_register_static(&esp32C3_wifi_info); +} + +type_init(esp32C3_wifi_register_types) diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 4c2b5b3cae..233b7287f6 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -155,7 +155,14 @@ system_ss.add(when: 'CONFIG_RISCV_ESP32C3', if_true: files( 'esp32c3_jtag.c', 'esp32c3_rtc_cntl.c', 'esp_hmac.c', - 'esp32c3_hmac.c' + 'esp32c3_hmac.c', + 'esp32c3_wifi.c', + 'esp32_wifi_ap.c', + 'esp32_wlan_packet.c', + 'esp32c3_ana.c', + 'esp32_phya.c', + 'esp32_fe.c', + 'esp32c3_pwrmng.c' )) system_ss.add(when: 'CONFIG_XTENSA_ESP32S3', if_true: files( diff --git a/hw/riscv/esp32c3.c b/hw/riscv/esp32c3.c index af8bb86c97..0a045ad37a 100644 --- a/hw/riscv/esp32c3.c +++ b/hw/riscv/esp32c3.c @@ -51,6 +51,11 @@ #include "hw/misc/esp32c3_ds.h" #include "hw/misc/esp32c3_xts_aes.h" #include "hw/misc/esp32c3_jtag.h" +#include "hw/misc/esp32c3_wifi.h" +#include "hw/misc/esp32c3_ana.h" +#include "hw/misc/esp32_phya.h" +#include "hw/misc/esp32_fe.h" +#include "hw/misc/esp32c3_pwrmng.h" #include "hw/dma/esp32c3_gdma.h" #include "hw/display/esp_rgb.h" #include "hw/display/xteink_x3_eink.h" @@ -76,6 +81,7 @@ struct Esp32C3MachineState { EspRISCVCPU soc; BusState periph_bus; MemoryRegion iomem; + uint32_t syscon[0x1000 / sizeof(uint32_t)]; bool xteink; bool x4; @@ -183,12 +189,19 @@ static bool addr_in_range(hwaddr addr, hwaddr start, hwaddr end) static uint64_t esp32c3_io_read(void *opaque, hwaddr addr, unsigned int size) { - if (addr_in_range(addr + ESP32C3_IO_START_ADDR, DR_REG_RTC_I2C_BASE, DR_REG_RTC_I2C_BASE + 0x100)) { - return (uint32_t) 0xffffff; - } else if (addr + ESP32C3_IO_START_ADDR == DR_REG_SYSCON_BASE + A_SYSCON_ORIGIN_REG) { + Esp32C3MachineState *ms = opaque; + hwaddr absolute = addr + ESP32C3_IO_START_ADDR; + + if (addr_in_range(absolute, DR_REG_NRX_BASE - 0xc00, + DR_REG_BB_BASE + 0x1000)) { + return UINT32_MAX; + } else if (addr_in_range(absolute, DR_REG_RTC_I2C_BASE, + DR_REG_RTC_I2C_BASE + 0x100)) { + return 0xffffff; + } else if (absolute == DR_REG_SYSCON_BASE + A_SYSCON_ORIGIN_REG) { /* Return "QEMU" as a 32-bit value */ return 0x51454d55; - } else if (addr + ESP32C3_IO_START_ADDR == DR_REG_SYSCON_BASE + A_SYSCON_RND_DATA_REG) { + } else if (absolute == DR_REG_SYSCON_BASE + A_SYSCON_RND_DATA_REG) { /* Return a random 32-bit value */ static bool init = false; if (!init) { @@ -196,8 +209,11 @@ static uint64_t esp32c3_io_read(void *opaque, hwaddr addr, unsigned int size) init = true; } return rand(); - } else if (addr + ESP32C3_IO_START_ADDR == DR_REG_ASSIST_DEBUG_BASE + A_ASSIST_DEBUG_CORE_0_DEBUG_MODE_REG) { + } else if (absolute == DR_REG_ASSIST_DEBUG_BASE + A_ASSIST_DEBUG_CORE_0_DEBUG_MODE_REG) { return 0; + } else if (addr_in_range(absolute, DR_REG_SYSCON_BASE, + DR_REG_SYSCON_BASE + sizeof(ms->syscon))) { + return ms->syscon[(absolute - DR_REG_SYSCON_BASE) / sizeof(uint32_t)]; } else { #if ESP32C3_IO_WARNING warn_report("[ESP32-C3] Unsupported read to $%08lx\n", ESP32C3_IO_START_ADDR + addr); @@ -208,8 +224,16 @@ static uint64_t esp32c3_io_read(void *opaque, hwaddr addr, unsigned int size) static void esp32c3_io_write(void *opaque, hwaddr addr, uint64_t value, unsigned int size) { + Esp32C3MachineState *ms = opaque; + hwaddr absolute = addr + ESP32C3_IO_START_ADDR; + + if (addr_in_range(absolute, DR_REG_SYSCON_BASE, + DR_REG_SYSCON_BASE + sizeof(ms->syscon))) { + ms->syscon[(absolute - DR_REG_SYSCON_BASE) / sizeof(uint32_t)] = value; + return; + } #if ESP32C3_IO_WARNING - warn_report("[ESP32-C3] Unsupported write $%08lx = %08lx\n", ESP32C3_IO_START_ADDR + addr, value); + warn_report("[ESP32-C3] Unsupported write $%08lx = %08lx\n", absolute, value); #endif } @@ -271,34 +295,60 @@ static void esp32c3_init_spi_flash(Esp32C3MachineState *ms, BlockBackend* blk) } +static void esp32c3_map_radio_device(const char *type, hwaddr address) +{ + DeviceState *device = qdev_new(type); + SysBusDevice *sbd = SYS_BUS_DEVICE(device); + + sysbus_realize_and_unref(sbd, &error_fatal); + memory_region_add_subregion_overlap(get_system_memory(), address, + sysbus_mmio_get_region(sbd, 0), 0); +} + +static bool esp32c3_init_wifi(Esp32C3MachineState *ms) +{ + DeviceState *wifi = qemu_create_nic_device(TYPE_ESP32C3_WIFI, false, NULL); + SysBusDevice *sbd; + + if (!wifi) { + return false; + } + + esp32c3_map_radio_device(TYPE_ESP32C3_ANA, DR_REG_RTC_I2C_BASE); + esp32c3_map_radio_device(TYPE_ESP32_PHYA, DR_REG_PHYA_BASE); + esp32c3_map_radio_device(TYPE_ESP32_FE, DR_REG_FE_BASE); + esp32c3_map_radio_device(TYPE_ESP32C3_PWR_MANAGER, DR_REG_PWR_MANAGER_BASE); + + sbd = SYS_BUS_DEVICE(wifi); + sysbus_realize_and_unref(sbd, &error_fatal); + memory_region_add_subregion_overlap(get_system_memory(), DR_REG_WIFI_BASE, + sysbus_mmio_get_region(sbd, 0), 0); + sysbus_connect_irq(sbd, 0, + qdev_get_gpio_in(DEVICE(&ms->intmatrix), + ETS_WIFI_MAC_INTR_SOURCE)); + return true; +} + static void esp32c3_init_openeth(Esp32C3MachineState *ms) { - MemoryRegion* mr = NULL; - SysBusDevice* sbd = NULL; + DeviceState *open_eth_dev = qemu_create_nic_device("open_eth", true, NULL); + SysBusDevice *sbd; - MemoryRegion* sys_mem = get_system_memory(); - - /* Create a new OpenCores Ethernet component */ - DeviceState* open_eth_dev = qemu_create_nic_device("open_eth", true, NULL); if (!open_eth_dev) { return; } ms->eth = open_eth_dev; - sbd = SYS_BUS_DEVICE(open_eth_dev); sysbus_realize(sbd, &error_fatal); - - /* OpenCores Ethernet has two memory regions: one for registers and one for descriptors, - * we need to provide one I/O range for each of them */ - mr = sysbus_mmio_get_region(sbd, 0); - memory_region_add_subregion_overlap(sys_mem, DR_REG_EMAC_BASE, mr, 0); - mr = sysbus_mmio_get_region(sbd, 1); - memory_region_add_subregion_overlap(sys_mem, DR_REG_EMAC_BASE + 0x400, mr, 0); - + memory_region_add_subregion_overlap(get_system_memory(), DR_REG_EMAC_BASE, + sysbus_mmio_get_region(sbd, 0), 0); + memory_region_add_subregion_overlap(get_system_memory(), + DR_REG_EMAC_BASE + 0x400, + sysbus_mmio_get_region(sbd, 1), 0); sysbus_connect_irq(sbd, 0, - qdev_get_gpio_in(DEVICE(&ms->intmatrix), ETS_ETH_MAC_INTR_SOURCE)); - + qdev_get_gpio_in(DEVICE(&ms->intmatrix), + ETS_ETH_MAC_INTR_SOURCE)); } @@ -420,7 +470,7 @@ static void esp32c3_machine_init(MachineState *machine) qdev_realize(DEVICE(&ms->soc), NULL, &error_fatal); memory_region_init_io(&ms->iomem, OBJECT(&ms->soc), &esp32c3_io_ops, - NULL, "esp32c3.iomem", 0xd1000); + ms, "esp32c3.iomem", 0xd1000); memory_region_add_subregion(sys_mem, ESP32C3_IO_START_ADDR, &ms->iomem); @@ -495,8 +545,9 @@ static void esp32c3_machine_init(MachineState *machine) } } - /* Initialize OpenCores Ethernet controller now sicne it requires the interrupt matrix */ - esp32c3_init_openeth(ms); + if (!esp32c3_init_wifi(ms)) { + esp32c3_init_openeth(ms); + } /* USB Serial JTAG realization */ { diff --git a/include/hw/misc/esp32_fe.h b/include/hw/misc/esp32_fe.h new file mode 100644 index 0000000000..d54961f5f5 --- /dev/null +++ b/include/hw/misc/esp32_fe.h @@ -0,0 +1,18 @@ +#pragma once + +#include "hw/hw.h" +#include "hw/sysbus.h" + + +#define TYPE_ESP32_FE "misc.esp32.fe" +#define ESP32_FE(obj) OBJECT_CHECK(Esp32FeState, (obj), TYPE_ESP32_FE) +//OBJECT_CHECK(Esp32FeState, (obj), TYPE_ESP32_FE) +//(Esp32FeState *)(obj) + +typedef struct Esp32FeState { + SysBusDevice parent_obj; + MemoryRegion iomem; + uint32_t mem[1024]; +} Esp32FeState; + + diff --git a/include/hw/misc/esp32_phya.h b/include/hw/misc/esp32_phya.h new file mode 100644 index 0000000000..e38d5e9cca --- /dev/null +++ b/include/hw/misc/esp32_phya.h @@ -0,0 +1,19 @@ +#pragma once + +#include "hw/hw.h" +#include "hw/sysbus.h" + + +#define TYPE_ESP32_PHYA "misc.esp32.phya" +#define ESP32_PHYA(obj) OBJECT_CHECK(Esp32PhyaState, (obj), TYPE_ESP32_PHYA) + +typedef struct Esp32PhyaState { + SysBusDevice parent_obj; + MemoryRegion iomem; + uint32_t mem[1024]; +} Esp32PhyaState; + +#define ESP32_PHYA_NACK 0x00002300 +#define ESP32_PHYA_ACK 0x0 + +void Esp32_WLAN_Set_Packet_Status(const uint32_t state); diff --git a/include/hw/misc/esp32_wifi.h b/include/hw/misc/esp32_wifi.h new file mode 100644 index 0000000000..ce1179265c --- /dev/null +++ b/include/hw/misc/esp32_wifi.h @@ -0,0 +1,75 @@ +#pragma once + +#include "hw/hw.h" +#include "hw/registerfields.h" +#include "hw/sysbus.h" +//#include "hw/misc/esp32_reg.h" +#include "sysemu/sysemu.h" +#include "net/net.h" + +#define TYPE_ESP32_WIFI "esp32c3_wifi" +#define ESP32_WIFI(obj) OBJECT_CHECK(Esp32WifiState, (obj), TYPE_ESP32_WIFI) + +typedef struct dma_list_item { + unsigned size:12; + unsigned length:12; + unsigned :6; + unsigned eof:1; + unsigned owner:1; + uint32_t address; + uint32_t next; +} QEMU_PACKED dma_list_item; + +typedef struct Esp32WifiState { + SysBusDevice parent_obj; + MemoryRegion iomem; + int raw_interrupt; + qemu_irq irq; + uint32_t mem[1024]; + int dma_inlink_address; + uint32_t ap_state; + int inject_queue_size; + struct mac80211_frame *inject_queue; + int inject_timer_running; + unsigned int inject_sequence_number; + int beacon_ap; + + hwaddr receive_queue_address; + uint32_t receive_queue_count; + NICConf conf; + NICState *nic; + // various timers + QEMUTimer *beacon_timer; + QEMUTimer *inject_timer; + QEMUTimer *wait_ack_timer; + uint8_t ipaddr[4]; + uint8_t macaddr[6]; + + uint8_t ap_ipaddr[4]; + uint8_t ap_macaddr[6]; + + uint8_t associated_ap_macaddr[6]; + + uint8_t softap_macaddr[6]; + + uint8_t mode; + +} Esp32WifiState; + +#define Esp32_Mode_Station 0 +#define Esp32_Mode_SoftAP 1 + + +void Esp32_WLAN_handle_frame(Esp32WifiState *s, struct mac80211_frame *frame); +void Esp32_WLAN_setup_ap(DeviceState *dev,Esp32WifiState *s); +void Esp32_WLAN_reset_ap(Esp32WifiState *s); +void Esp32_sendFrame(Esp32WifiState *s, struct mac80211_frame *frame,int length, int signal_strength); +void Esp32_WLAN_frame_delivered(Esp32WifiState *s); + +REG32(WIFI_DMA_IN_STATUS, 0x84); +REG32(WIFI_DMA_INLINK, 0x88); +REG32(WIFI_DMA_INT_STATUS, 0xc48); +REG32(WIFI_DMA_INT_CLR, 0xc4c); +REG32(WIFI_STATUS, 0xcc8); +REG32(WIFI_DMA_OUTLINK, 0xd20); +REG32(WIFI_DMA_OUT_STATUS, 0xd24); diff --git a/include/hw/misc/esp32c3_ana.h b/include/hw/misc/esp32c3_ana.h new file mode 100644 index 0000000000..6d2282b91f --- /dev/null +++ b/include/hw/misc/esp32c3_ana.h @@ -0,0 +1,16 @@ +#pragma once + +#include "hw/hw.h" +#include "hw/sysbus.h" + + +#define TYPE_ESP32C3_ANA "misc.esp32c3.ana" +#define ESP32C3_ANA(obj) OBJECT_CHECK(Esp32C3AnaState, (obj), TYPE_ESP32C3_ANA) + +typedef struct Esp32C3AnaState { + SysBusDevice parent_obj; + MemoryRegion iomem; + uint32_t mem[1024]; +} Esp32C3AnaState; + + diff --git a/include/hw/misc/esp32c3_pwrmng.h b/include/hw/misc/esp32c3_pwrmng.h new file mode 100644 index 0000000000..8af5feae3b --- /dev/null +++ b/include/hw/misc/esp32c3_pwrmng.h @@ -0,0 +1,16 @@ +#pragma once + +#include "hw/sysbus.h" +#include "hw/hw.h" +#include "hw/registerfields.h" + +#define TYPE_ESP32C3_PWR_MANAGER "misc.esp32c3.pwrmng" +#define ESP32C3_PWR_MANAGER(obj) OBJECT_CHECK(Esp32c3PwrMngState, (obj), TYPE_ESP32C3_PWR_MANAGER) + + +typedef struct Esp32c3PwrMngState { + SysBusDevice parent_obj; + MemoryRegion iomem; + uint32_t mem[1024]; +} Esp32c3PwrMngState; + diff --git a/include/hw/misc/esp32c3_reg.h b/include/hw/misc/esp32c3_reg.h index 9029327afd..3207bf3407 100644 --- a/include/hw/misc/esp32c3_reg.h +++ b/include/hw/misc/esp32c3_reg.h @@ -42,6 +42,9 @@ #define DR_REG_APB_CTRL_BASE 0x60026000 /* Old name for SYSCON, to be removed */ #define DR_REG_TWAI_BASE 0x6002B000 #define DR_REG_I2S0_BASE 0x6002D000 +#define DR_REG_WIFI_BASE 0x60033000 +#define DR_REG_PHYA_BASE 0x60034000 +#define DR_REG_PWR_MANAGER_BASE 0x60035000 #define DR_REG_APB_SARADC_BASE 0x60040000 #define DR_REG_USB_SERIAL_JTAG_BASE 0x60043000 #define DR_REG_AES_XTS_BASE 0x600CC000 diff --git a/include/hw/misc/esp32c3_wifi.h b/include/hw/misc/esp32c3_wifi.h new file mode 100644 index 0000000000..4eb2510d57 --- /dev/null +++ b/include/hw/misc/esp32c3_wifi.h @@ -0,0 +1,19 @@ +#pragma once + +#include "hw/hw.h" +#include "hw/registerfields.h" +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "net/net.h" +#include "esp32_wifi.h" + +#define TYPE_ESP32C3_WIFI TYPE_ESP32_WIFI + + +REG32(C3_WIFI_DMA_IN_STATUS, 0x84); +REG32(C3_WIFI_DMA_INLINK, 0x88); +REG32(C3_WIFI_DMA_INT_STATUS, 0xc3C); +REG32(C3_WIFI_DMA_INT_CLR, 0xc40); +REG32(C3_WIFI_STATUS, 0xcb0); +REG32(C3_WIFI_DMA_OUTLINK, 0xd08); +REG32(C3_WIFI_DMA_OUT_STATUS, 0xd14); diff --git a/scripts/xteink-emu.py b/scripts/xteink-emu.py index 768aada9c9..e9ce835cc9 100755 --- a/scripts/xteink-emu.py +++ b/scripts/xteink-emu.py @@ -136,6 +136,10 @@ def start(args): (state / "wait.offset").write_text("0\n") flash = state / "flash.bin" build_flash(firmware, flash) + efuse = bytearray(1024) + efuse[0x18:0x1E] = bytes.fromhex("563412005452") + efuse[0x26] = 0x0C + (state / "efuse.bin").write_bytes(efuse) command = [ str(require_env("XTEINK_QEMU")), @@ -146,8 +150,10 @@ def start(args): "-serial", f"file:{state / 'serial.log'}", "-monitor", "none", "-qmp", f"unix:{state / 'qmp.sock'},server=on,wait=off", - "-nic", "none", + "-nic", "user,model=esp32c3_wifi,mac=52:54:00:12:34:56,net=192.168.4.0/24", "-drive", f"file={flash},if=mtd,format=raw", + "-drive", f"file={state / 'efuse.bin'},if=none,format=raw,id=efuse", + "-global", "driver=nvram.esp32c3.efuse,property=drive,value=efuse", "-drive", f"file={sdcard},if=sd,format=raw", ] qemu_log = (state / "qemu.log").open("ab")