Skip to content

Commit

Permalink
network: Add SCSI ethernet support, emulating a Dayna SCSI/Link
Browse files Browse the repository at this point in the history
Add SCSI commands to start a WiFi scan, check status, and get SSID
list

For some reason trying to use command IDs in the vendor range
(0xc0-0xf0) result in errors on the bus
  • Loading branch information
jcs committed Aug 6, 2023
1 parent 216d434 commit 3b9f2c9
Show file tree
Hide file tree
Showing 11 changed files with 881 additions and 29 deletions.
12 changes: 12 additions & 0 deletions lib/BlueSCSI_platform_RP2040/BlueSCSI_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,18 @@ bool platform_write_romdrive(const uint8_t *data, uint32_t start, uint32_t count
extern const uint16_t g_scsi_parity_lookup[256];
extern const uint16_t g_scsi_parity_check_lookup[512];

// Network functions
bool platform_network_supported();
void platform_network_poll();
int platform_network_init(char *mac, char *ssid, char *password);
int platform_network_wifi_start_scan();
int platform_network_wifi_scan_finished();
void platform_network_wifi_dump_scan_list();
int platform_network_wifi_rssi();
char * platform_network_wifi_ssid();
char * platform_network_wifi_bssid();
int platform_network_wifi_channel();

// Below are GPIO access definitions that are used from scsiPhy.cpp.

// Write a single SCSI pin.
Expand Down
283 changes: 283 additions & 0 deletions lib/BlueSCSI_platform_RP2040/BlueSCSI_platform_network.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
/*
* Copyright (c) 2023 joshua stein <jcs@jcs.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "BlueSCSI_platform.h"
#include "BlueSCSI_log.h"
#include "BlueSCSI_config.h"
#include <scsi.h>
#include <network.h>

extern "C" {

#include <cyw43.h>
#include <pico/cyw43_arch.h>

#ifndef CYW43_IOCTL_GET_RSSI
#define CYW43_IOCTL_GET_RSSI (0xfe)
#endif

// A default DaynaPort-compatible MAC
static const char defaultMAC[] = { 0x00, 0x80, 0x19, 0xc0, 0xff, 0xee };

static bool network_in_use = false;

bool platform_network_supported()
{
/* from cores/rp2040/RP2040Support.h */
#if !defined(ARDUINO_RASPBERRY_PI_PICO_W)
return false;
#else
extern bool __isPicoW;
return __isPicoW;
#endif
}

int platform_network_init(char *mac, char *ssid, char *password)
{
uint8_t tmac[6];
int ret;

if (!platform_network_supported())
return -1;

memset(wifi_network_list, 0, sizeof(wifi_network_list));

cyw43_deinit(&cyw43_state);

if (mac == NULL || (mac[0] == 0 && mac[1] == 0 && mac[2] == 0 && mac[3] == 0 && mac[4] == 0 && mac[5] == 0)) {
mac = (char *)&defaultMAC;
memcpy(scsiDev.boardCfg.wifiMACAddress, mac, sizeof(scsiDev.boardCfg.wifiMACAddress));
}

// XXX: this requires libpico to be compiled with CYW43_USE_OTP_MAC=0
memcpy(cyw43_state.mac, mac, sizeof(cyw43_state.mac));

cyw43_arch_enable_sta_mode();
cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_STA, (uint8_t *)&tmac);

log(" ");
log("=== Network Initialization");

log_f("WiFi MAC is %02X:%02X:%02X:%02X:%02X:%02X", tmac[0], tmac[1], tmac[2], tmac[3], tmac[4], tmac[5]);
if (memcmp(mac, tmac, sizeof(tmac)) != 0)
log("WARNING: WiFi MAC is not what was requested, is libpico not compiled with CYW43_USE_OTP_MAC=0?");

#if 0
platform_network_wifi_start_scan();
while (!platform_network_wifi_scan_finished())
{
platform_network_poll();
}
platform_network_wifi_dump_scan_list();
#endif

if (password == NULL || password[0] == 0)
{
log("Connecting to WiFi SSID \"", ssid, "\" with no authentication");
ret = cyw43_arch_wifi_connect_async(ssid, NULL, CYW43_AUTH_OPEN);
}
else
{
log("Connecting to WiFi SSID \"", ssid, "\" with WPA/WPA2 PSK");
ret = cyw43_arch_wifi_connect_async(ssid, password, CYW43_AUTH_WPA2_MIXED_PSK);
}
if (ret != 0)
log_f("WiFi connection failed: %d", ret);

network_in_use = true;

return 0;
}

void platform_network_poll()
{
if (network_in_use)
cyw43_arch_poll();
}

int platform_network_send(uint8_t *buf, size_t len)
{
int ret = cyw43_send_ethernet(&cyw43_state, 0, len, buf, 0);
if (ret != 0)
log_f("cyw43_send_ethernet failed: %d", ret);

return ret;
}

static int platform_network_wifi_scan_result(void *env, const cyw43_ev_scan_result_t *result)
{
struct wifi_network_entry *entry = NULL;

if (!result || !result->ssid_len || !result->ssid[0])
return 0;

for (int i = 0; i < WIFI_NETWORK_LIST_ENTRY_COUNT; i++)
{
// take first available
if (wifi_network_list[i].ssid[0] == '\0')
{
entry = &wifi_network_list[i];
break;
}
// or if we've seen this network before, use this slot
else if (strcmp((char *)result->ssid, wifi_network_list[i].ssid) == 0)
{
entry = &wifi_network_list[i];
break;
}
}

if (!entry) {
// no available slots, insert according to our RSSI
for (int i = 0; i < WIFI_NETWORK_LIST_ENTRY_COUNT; i++)
{
if (result->rssi > wifi_network_list[i].rssi)
{
// shift everything else down
for (int j = WIFI_NETWORK_LIST_ENTRY_COUNT - 1; j > i; j--)
wifi_network_list[j] = wifi_network_list[j - 1];

entry = &wifi_network_list[i];
memset(entry, 0, sizeof(struct wifi_network_entry));
break;
}
}
}

if (entry == NULL)
return 0;

if (entry->rssi == 0 || result->rssi > entry->rssi)
{
entry->channel = result->channel;
entry->rssi = result->rssi;
}
if (result->auth_mode & 7)
entry->flags = WIFI_NETWORK_FLAGS_AUTH;
strncpy(entry->ssid, (const char *)result->ssid, sizeof(entry->ssid));
entry->ssid[sizeof(entry->ssid) - 1] = '\0';
memcpy(entry->bssid, result->bssid, sizeof(entry->bssid));

return 0;
}

int platform_network_wifi_start_scan()
{
if (cyw43_wifi_scan_active(&cyw43_state))
return -1;

cyw43_wifi_scan_options_t scan_options = { 0 };
memset(wifi_network_list, 0, sizeof(wifi_network_list));
return cyw43_wifi_scan(&cyw43_state, &scan_options, NULL, platform_network_wifi_scan_result);
}

int platform_network_wifi_scan_finished()
{
return !cyw43_wifi_scan_active(&cyw43_state);
}

void platform_network_wifi_dump_scan_list()
{
struct wifi_network_entry *entry = NULL;

for (int i = 0; i < WIFI_NETWORK_LIST_ENTRY_COUNT; i++)
{
entry = &wifi_network_list[i];

if (entry->ssid[0] == '\0')
break;

log_f("wifi[%d] = %s, channel %d, rssi %d, bssid %02x:%02x:%02x:%02x:%02x:%02x, flags %d",
i, entry->ssid, entry->channel, entry->rssi,
entry->bssid[0], entry->bssid[1], entry->bssid[2],
entry->bssid[3], entry->bssid[4], entry->bssid[5],
entry->flags);
}
}

int platform_network_wifi_rssi()
{
int32_t rssi = 0;

cyw43_ioctl(&cyw43_state, CYW43_IOCTL_GET_RSSI, sizeof(rssi), (uint8_t *)&rssi, CYW43_ITF_STA);
return rssi;
}

char * platform_network_wifi_ssid()
{
struct ssid_t {
uint32_t ssid_len;
uint8_t ssid[32 + 1];
} ssid;
static char cur_ssid[32 + 1];

memset(cur_ssid, 0, sizeof(cur_ssid));

int ret = cyw43_ioctl(&cyw43_state, CYW43_IOCTL_GET_SSID, sizeof(ssid), (uint8_t *)&ssid, CYW43_ITF_STA);
if (ret)
{
log_f("Failed getting WiFi SSID: %d", ret);
return NULL;
}

ssid.ssid[sizeof(ssid.ssid) - 1] = '\0';
if (ssid.ssid_len < sizeof(ssid.ssid))
ssid.ssid[ssid.ssid_len] = '\0';

strlcpy(cur_ssid, (char *)ssid.ssid, sizeof(cur_ssid));
return cur_ssid;
}

char * platform_network_wifi_bssid()
{
static char bssid[6];

memset(bssid, 0, sizeof(bssid));

/* TODO */

return bssid;
}

int platform_network_wifi_channel()
{
int32_t channel = 0;

cyw43_ioctl(&cyw43_state, CYW43_IOCTL_GET_CHANNEL, sizeof(channel), (uint8_t *)&channel, CYW43_ITF_STA);
return channel;
}

// these override weakly-defined functions in pico-sdk

void cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf)
{
scsiNetworkEnqueue(buf, len);
}

void cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf)
{
log_f("Disassociated from WiFi SSID %s", self->ap_ssid);
}

void cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf)
{
char *ssid = platform_network_wifi_ssid();

if (ssid)
log_f("Successfully connected to WiFi SSID \"%s\"", ssid);
}

}
10 changes: 7 additions & 3 deletions lib/SCSI2SD/include/scsi2sd.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ typedef enum
S2S_CFG_OPTICAL,
S2S_CFG_FLOPPY_14MB,
S2S_CFG_MO,
S2S_CFG_SEQUENTIAL

S2S_CFG_SEQUENTIAL,
S2S_CFG_NETWORK,
} S2S_CFG_TYPE;

typedef enum
Expand Down Expand Up @@ -139,7 +139,11 @@ typedef struct __attribute__((packed))

uint8_t scsiSpeed;

uint8_t reserved[119]; // Pad out to 128 bytes
char wifiMACAddress[6];
char wifiSSID[32];
char wifiPassword[63];

uint8_t reserved[18]; // Pad out to 128 bytes
} S2S_BoardCfg;

typedef enum
Expand Down
11 changes: 10 additions & 1 deletion lib/SCSI2SD/src/firmware/inquiry.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,11 @@ void s2s_scsiInquiry()
case S2S_CFG_REMOVEABLE:
scsiDev.data[1] |= 0x80; // Removable bit.
break;

case S2S_CFG_NETWORK:
scsiDev.data[2] = 0x01; // Page code.
break;

default:
// Accept defaults for a fixed disk.
break;
Expand Down Expand Up @@ -264,9 +269,13 @@ uint8_t getDeviceTypeQualifier()
return 0;
break;

case S2S_CFG_NETWORK:
// processor device
return 0x03;
break;

default:
// Accept defaults for a fixed disk.
return 0;
}
}

Loading

0 comments on commit 3b9f2c9

Please sign in to comment.