Files
drivers/wifinina/wifinina.go
T
Scott Feldman e7931c6a22 wifinina: fix concurrency issues with multiple sockets
wifinina driver was not handling concurrent socket connections
correctly.  When trying to make multiple socket connections, the sockfd
returned by the first Socket() call would be the same sockfd returned by
subsequent Socket() calls.  The problem is the sockfd returned by
Socket() isn't used until Connect() (or Appect()).  This would result in
Connect() trying to use the same sockfd for multiple connections,
causing wifinina fw to lock up.

The solution in this PR is to create a new sockfd space managed by the
driver that gives the app a unique, safe sockfd for each connection.
The real underlying sock fd returned by fw is set on Connect() (or
Accept()), and mapped back to the apps sockfd using a map:

    sockets map[int]*Socket // keyed by sockfd

Where Socket has a reference to the fw sock:

    type Socket struct {
            protocol        int
            clientConnected bool
            laddr           netip.AddrPort // Set in Bind()
            raddr           netip.AddrPort // Set in Connect()
            sock                           // Device socket, as returned from w.getSocket()
    }
2024-01-22 13:32:57 -08:00

1964 lines
42 KiB
Go

// Package wifinina implements TCP wireless communication over SPI with an
// attached separate ESP32 SoC using the Arduino WiFiNINA protocol.
//
// In order to use this driver, the ESP32 must be flashed with specific
// firmware from Arduino. For more information:
// https://github.com/arduino/nina-fw
//
// 12/2022 sfeldma@gmail.com Heavily modified to use netdev interface
package wifinina // import "tinygo.org/x/drivers/wifinina"
import (
"encoding/binary"
"encoding/hex"
"fmt"
"io"
"machine"
"math/bits"
"net"
"net/netip"
"sync"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/netdev"
"tinygo.org/x/drivers/netlink"
)
var _debug debug = debugBasic
//var _debug debug = debugBasic | debugNetdev
//var _debug debug = debugBasic | debugNetdev | debugCmd
//var _debug debug = debugBasic | debugNetdev | debugCmd | debugDetail
var (
version = "0.0.1"
driverName = "Tinygo ESP32 Wifi network device driver (WiFiNINA)"
)
const (
maxNetworks = 10
statusNoShield connectionStatus = 255
statusIdle connectionStatus = 0
statusNoSSIDAvail connectionStatus = 1
statusScanCompleted connectionStatus = 2
statusConnected connectionStatus = 3
statusConnectFailed connectionStatus = 4
statusConnectionLost connectionStatus = 5
statusDisconnected connectionStatus = 6
encTypeTKIP encryptionType = 2
encTypeCCMP encryptionType = 4
encTypeWEP encryptionType = 5
encTypeNone encryptionType = 7
encTypeAuto encryptionType = 8
tcpStateClosed = 0
tcpStateListen = 1
tcpStateSynSent = 2
tcpStateSynRcvd = 3
tcpStateEstablished = 4
tcpStateFinWait1 = 5
tcpStateFinWait2 = 6
tcpStateCloseWait = 7
tcpStateClosing = 8
tcpStateLastACK = 9
tcpStateTimeWait = 10
flagCmd = 0
flagReply = 1 << 7
flagData = 0x40
cmdStart = 0xE0
cmdEnd = 0xEE
cmdErr = 0xEF
dummyData = 0xFF
cmdSetNet = 0x10
cmdSetPassphrase = 0x11
cmdSetKey = 0x12
cmdSetIPConfig = 0x14
cmdSetDNSConfig = 0x15
cmdSetHostname = 0x16
cmdSetPowerMode = 0x17
cmdSetAPNet = 0x18
cmdSetAPPassphrase = 0x19
cmdSetDebug = 0x1A
cmdGetTemperature = 0x1B
cmdGetReasonCode = 0x1F
cmdGetConnStatus = 0x20
cmdGetIPAddr = 0x21
cmdGetMACAddr = 0x22
cmdGetCurrSSID = 0x23
cmdGetCurrBSSID = 0x24
cmdGetCurrRSSI = 0x25
cmdGetCurrEncrType = 0x26
cmdScanNetworks = 0x27
cmdStartServerTCP = 0x28
cmdGetStateTCP = 0x29
cmdDataSentTCP = 0x2A
cmdAvailDataTCP = 0x2B
cmdGetDataTCP = 0x2C
cmdStartClientTCP = 0x2D
cmdStopClientTCP = 0x2E
cmdGetClientStateTCP = 0x2F
cmdDisconnect = 0x30
cmdGetIdxRSSI = 0x32
cmdGetIdxEncrType = 0x33
cmdReqHostByName = 0x34
cmdGetHostByName = 0x35
cmdStartScanNetworks = 0x36
cmdGetFwVersion = 0x37
cmdSendDataUDP = 0x39
cmdGetRemoteData = 0x3A
cmdGetTime = 0x3B
cmdGetIdxBSSID = 0x3C
cmdGetIdxChannel = 0x3D
cmdPing = 0x3E
cmdGetSocket = 0x3F
// All commands with DATA_FLAG 0x4x send a 16bit Len
cmdSendDataTCP = 0x44
cmdGetDatabufTCP = 0x45
cmdInsertDataBuf = 0x46
// Regular format commands
cmdSetPinMode = 0x50
cmdSetDigitalWrite = 0x51
cmdSetAnalogWrite = 0x52
errTimeoutChipReady hwerr = 0x01
errTimeoutChipSelect hwerr = 0x02
errCheckStartCmd hwerr = 0x03
errWaitRsp hwerr = 0x04
errUnexpectedLength hwerr = 0xE0
errNoParamsReturned hwerr = 0xE1
errIncorrectSentinel hwerr = 0xE2
errCmdErrorReceived hwerr = 0xEF
errNotImplemented hwerr = 0xF0
errUnknownHost hwerr = 0xF1
errSocketAlreadySet hwerr = 0xF2
errConnectionTimeout hwerr = 0xF3
errNoData hwerr = 0xF4
errDataNotWritten hwerr = 0xF5
errCheckDataError hwerr = 0xF6
errBufferTooSmall hwerr = 0xF7
errNoSocketAvail hwerr = 0xFF
noSocketAvail sock = 0xFF
)
const (
protoModeTCP = iota
protoModeUDP
protoModeTLS
protoModeMul
)
type connectionStatus uint8
type encryptionType uint8
type sock uint8
type hwerr uint8
type Socket struct {
protocol int
clientConnected bool
laddr netip.AddrPort // Set in Bind()
raddr netip.AddrPort // Set in Connect()
sock // Device socket, as returned from w.getSocket()
}
type Config struct {
// SPI config
Spi drivers.SPI
Freq uint32
Sdo machine.Pin
Sdi machine.Pin
Sck machine.Pin
// Device config
Cs machine.Pin
Ack machine.Pin
Gpio0 machine.Pin
Resetn machine.Pin
// ResetIsHigh controls if the RESET signal to the processor should be
// High or Low (the default). Set this to true for boards such as the
// Arduino MKR 1010, where the reset signal needs to go high instead of
// low.
ResetIsHigh bool
}
type wifinina struct {
cfg *Config
notifyCb func(netlink.Event)
mu sync.Mutex
spi drivers.SPI
cs machine.Pin
ack machine.Pin
gpio0 machine.Pin
resetn machine.Pin
buf [64]byte
ssids [maxNetworks]string
params *netlink.ConnectParams
netConnected bool
driverShown bool
deviceShown bool
spiSetup bool
killWatchdog chan bool
fault error
sockets map[int]*Socket // keyed by sockfd
}
func New(cfg *Config) *wifinina {
w := wifinina{
cfg: cfg,
sockets: make(map[int]*Socket),
killWatchdog: make(chan bool),
cs: cfg.Cs,
ack: cfg.Ack,
gpio0: cfg.Gpio0,
resetn: cfg.Resetn,
}
return &w
}
func (err hwerr) Error() string {
return "[wifinina] error: 0x" + hex.EncodeToString([]byte{uint8(err)})
}
func (w *wifinina) reason() string {
reason := w.getReasonCode()
switch reason {
case 0:
return "unknown failure"
case 201:
return "no AP found"
case 202:
return "auth failed"
}
return fmt.Sprintf("%d", reason)
}
func (w *wifinina) connectToAP() error {
timeout := w.params.ConnectTimeout
if timeout == 0 {
timeout = netlink.DefaultConnectTimeout
}
if len(w.params.Ssid) == 0 {
return netlink.ErrMissingSSID
}
if debugging(debugBasic) {
fmt.Printf("Connecting to Wifi SSID '%s'...", w.params.Ssid)
}
start := time.Now()
// Start the connection process
w.setPassphrase(w.params.Ssid, w.params.Passphrase)
// Check if we connected
for {
status := w.getConnectionStatus()
switch status {
case statusConnected:
if debugging(debugBasic) {
fmt.Printf("CONNECTED\r\n")
}
if w.notifyCb != nil {
w.notifyCb(netlink.EventNetUp)
}
return nil
case statusConnectFailed:
if debugging(debugBasic) {
fmt.Printf("FAILED (%s)\r\n", w.reason())
}
return netlink.ErrConnectFailed
}
if time.Since(start) > timeout {
break
}
time.Sleep(1 * time.Second)
}
if debugging(debugBasic) {
fmt.Printf("FAILED (timed out)\r\n")
}
return netlink.ErrConnectTimeout
}
func (w *wifinina) netDisconnect() {
w.disconnect()
}
func (w *wifinina) showDriver() {
if w.driverShown {
return
}
if debugging(debugBasic) {
fmt.Printf("\r\n")
fmt.Printf("%s\r\n\r\n", driverName)
fmt.Printf("Driver version : %s\r\n", version)
}
w.driverShown = true
}
func (w *wifinina) setupSPI() {
if w.spiSetup {
return
}
spi := machine.NINA_SPI
spi.Configure(machine.SPIConfig{
Frequency: w.cfg.Freq,
SDO: w.cfg.Sdo,
SDI: w.cfg.Sdi,
SCK: w.cfg.Sck,
})
w.spi = spi
w.spiSetup = true
}
func (w *wifinina) start() {
pinUseDevice(w)
w.cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
w.ack.Configure(machine.PinConfig{Mode: machine.PinInput})
w.resetn.Configure(machine.PinConfig{Mode: machine.PinOutput})
w.gpio0.Configure(machine.PinConfig{Mode: machine.PinOutput})
w.gpio0.High()
w.cs.High()
w.resetn.Set(w.cfg.ResetIsHigh)
time.Sleep(10 * time.Millisecond)
w.resetn.Set(!w.cfg.ResetIsHigh)
time.Sleep(750 * time.Millisecond)
w.gpio0.Low()
w.gpio0.Configure(machine.PinConfig{Mode: machine.PinInput})
}
func (w *wifinina) stop() {
w.resetn.Low()
w.cs.Configure(machine.PinConfig{Mode: machine.PinInput})
}
func (w *wifinina) showDevice() {
if w.deviceShown {
return
}
if debugging(debugBasic) {
fmt.Printf("ESP32 firmware version : %s\r\n", w.getFwVersion())
mac := w.getMACAddr()
fmt.Printf("MAC address : %s\r\n", mac.String())
fmt.Printf("\r\n")
}
w.deviceShown = true
}
func (w *wifinina) showIP() {
if debugging(debugBasic) {
ip, subnet, gateway := w.getIP()
fmt.Printf("\r\n")
fmt.Printf("DHCP-assigned IP : %s\r\n", ip)
fmt.Printf("DHCP-assigned subnet : %s\r\n", subnet)
fmt.Printf("DHCP-assigned gateway : %s\r\n", gateway)
fmt.Printf("\r\n")
}
}
func (w *wifinina) networkDown() bool {
return w.getConnectionStatus() != statusConnected
}
func (w *wifinina) watchdog() {
ticker := time.NewTicker(w.params.WatchdogTimeout)
for {
select {
case <-w.killWatchdog:
return
case <-ticker.C:
w.mu.Lock()
if w.fault != nil {
if debugging(debugBasic) {
fmt.Printf("Watchdog: FAULT: %s\r\n", w.fault)
}
w.netDisconnect()
w.netConnect(true)
w.fault = nil
} else if w.networkDown() {
if debugging(debugBasic) {
fmt.Printf("Watchdog: Wifi NOT CONNECTED, trying again...\r\n")
}
if w.notifyCb != nil {
w.notifyCb(netlink.EventNetDown)
}
w.netConnect(false)
}
w.mu.Unlock()
}
}
}
func (w *wifinina) netConnect(reset bool) error {
if reset {
w.start()
}
w.showDevice()
for i := 0; w.params.Retries == 0 || i < w.params.Retries; i++ {
if err := w.connectToAP(); err != nil {
switch err {
case netlink.ErrConnectTimeout, netlink.ErrConnectFailed:
continue
}
return err
}
break
}
if w.networkDown() {
return netlink.ErrConnectFailed
}
w.showIP()
return nil
}
func (w *wifinina) NetConnect(params *netlink.ConnectParams) error {
w.mu.Lock()
defer w.mu.Unlock()
if w.netConnected {
return netlink.ErrConnected
}
w.params = params
w.showDriver()
w.setupSPI()
if err := w.netConnect(true); err != nil {
return err
}
w.netConnected = true
if w.params.WatchdogTimeout != 0 {
go w.watchdog()
}
return nil
}
func (w *wifinina) NetDisconnect() {
w.mu.Lock()
defer w.mu.Unlock()
if !w.netConnected {
return
}
if w.params.WatchdogTimeout != 0 {
w.killWatchdog <- true
}
w.netDisconnect()
w.stop()
w.netConnected = false
if debugging(debugBasic) {
fmt.Printf("\r\nDisconnected from Wifi SSID '%s'\r\n\r\n", w.params.Ssid)
}
if w.notifyCb != nil {
w.notifyCb(netlink.EventNetDown)
}
}
func (w *wifinina) NetNotify(cb func(netlink.Event)) {
w.notifyCb = cb
}
func (w *wifinina) GetHostByName(name string) (netip.Addr, error) {
if debugging(debugNetdev) {
fmt.Printf("[GetHostByName] name: %s\r\n", name)
}
// If it's already in dotted-decimal notation, return a copy
// per gethostbyname(3).
if ip, err := netip.ParseAddr(name); err == nil {
return ip, nil
}
w.mu.Lock()
defer w.mu.Unlock()
ip := w.getHostByName(name)
if ip == "" {
return netip.Addr{}, netdev.ErrHostUnknown
}
addr, ok := netip.AddrFromSlice([]byte(ip))
if !ok {
return netip.Addr{}, netdev.ErrMalAddr
}
return addr, nil
}
func (w *wifinina) GetHardwareAddr() (net.HardwareAddr, error) {
if debugging(debugNetdev) {
fmt.Printf("[GetHardwareAddr]\r\n")
}
w.mu.Lock()
defer w.mu.Unlock()
return w.getMACAddr(), nil
}
func (w *wifinina) Addr() (netip.Addr, error) {
if debugging(debugNetdev) {
fmt.Printf("[GetIPAddr]\r\n")
}
w.mu.Lock()
defer w.mu.Unlock()
ip, _, _ := w.getIP()
return ip, nil
}
// newSockfd returns the next available sockfd, or -1 if none available
func (w *wifinina) newSockfd() int {
if len(w.sockets) >= maxNetworks {
return -1
}
// Search for the next available sockfd starting at 0
for sockfd := 0; ; sockfd++ {
if _, ok := w.sockets[sockfd]; !ok {
return sockfd
}
}
return -1
}
// See man socket(2) for standard Berkely sockets for Socket, Bind, etc.
// The driver strives to meet the function and semantics of socket(2).
func (w *wifinina) Socket(domain int, stype int, protocol int) (int, error) {
if debugging(debugNetdev) {
fmt.Printf("[Socket] domain: %d, type: %d, protocol: %d\r\n",
domain, stype, protocol)
}
switch domain {
case netdev.AF_INET:
default:
return -1, netdev.ErrFamilyNotSupported
}
switch {
case protocol == netdev.IPPROTO_TCP && stype == netdev.SOCK_STREAM:
case protocol == netdev.IPPROTO_TLS && stype == netdev.SOCK_STREAM:
case protocol == netdev.IPPROTO_UDP && stype == netdev.SOCK_DGRAM:
default:
return -1, netdev.ErrProtocolNotSupported
}
w.mu.Lock()
defer w.mu.Unlock()
sockfd := w.newSockfd()
if sockfd == -1 {
return -1, netdev.ErrNoMoreSockets
}
w.sockets[sockfd] = &Socket{
protocol: protocol,
sock: noSocketAvail,
}
if debugging(debugNetdev) {
fmt.Printf("[Socket] <-- sockfd %d\r\n", sockfd)
}
return sockfd, nil
}
func (w *wifinina) Bind(sockfd int, ip netip.AddrPort) error {
if debugging(debugNetdev) {
fmt.Printf("[Bind] sockfd: %d, addr: %s:%d\r\n", sockfd, ip.Addr(), ip.Port())
}
w.mu.Lock()
defer w.mu.Unlock()
socket, ok := w.sockets[sockfd]
if !ok {
return netdev.ErrInvalidSocketFd
}
switch socket.protocol {
case netdev.IPPROTO_TCP:
case netdev.IPPROTO_TLS:
case netdev.IPPROTO_UDP:
socket.sock = w.getSocket()
if socket.sock == noSocketAvail {
return netdev.ErrNoMoreSockets
}
w.startServer(socket.sock, ip.Port(), protoModeUDP)
}
socket.laddr = ip
return nil
}
func toUint32(ip [4]byte) uint32 {
return uint32(ip[0])<<24 |
uint32(ip[1])<<16 |
uint32(ip[2])<<8 |
uint32(ip[3])
}
func (w *wifinina) Connect(sockfd int, host string, ip netip.AddrPort) error {
if debugging(debugNetdev) {
if host == "" {
fmt.Printf("[Connect] sockfd: %d, addr: %s\r\n", sockfd, ip)
} else {
fmt.Printf("[Connect] sockfd: %d, host: %s:%d\r\n", sockfd, host, ip.Port())
}
}
w.mu.Lock()
defer w.mu.Unlock()
socket, ok := w.sockets[sockfd]
if !ok {
return netdev.ErrInvalidSocketFd
}
// Start the connection
switch socket.protocol {
case netdev.IPPROTO_TCP:
socket.sock = w.getSocket()
if socket.sock == noSocketAvail {
return netdev.ErrNoMoreSockets
}
w.startClient(socket.sock, "", toUint32(ip.Addr().As4()), ip.Port(), protoModeTCP)
case netdev.IPPROTO_TLS:
socket.sock = w.getSocket()
if socket.sock == noSocketAvail {
return netdev.ErrNoMoreSockets
}
w.startClient(socket.sock, host, 0, ip.Port(), protoModeTLS)
case netdev.IPPROTO_UDP:
if socket.sock == noSocketAvail {
return fmt.Errorf("Must Bind before Connecting")
}
// See start in sendUDP()
socket.raddr = ip
socket.clientConnected = true
return nil
}
if w.getClientState(socket.sock) == tcpStateEstablished {
socket.clientConnected = true
return nil
}
if host == "" {
return fmt.Errorf("Connect to %s failed", ip)
} else {
return fmt.Errorf("Connect to %s:%d failed", host, ip.Port())
}
}
func (w *wifinina) Listen(sockfd int, backlog int) error {
if debugging(debugNetdev) {
fmt.Printf("[Listen] sockfd: %d\r\n", sockfd)
}
w.mu.Lock()
defer w.mu.Unlock()
socket, ok := w.sockets[sockfd]
if !ok {
return netdev.ErrInvalidSocketFd
}
switch socket.protocol {
case netdev.IPPROTO_TCP:
socket.sock = w.getSocket()
if socket.sock == noSocketAvail {
return netdev.ErrNoMoreSockets
}
w.startServer(socket.sock, socket.laddr.Port(), protoModeTCP)
case netdev.IPPROTO_UDP:
default:
return netdev.ErrProtocolNotSupported
}
return nil
}
func (w *wifinina) Accept(sockfd int) (int, netip.AddrPort, error) {
if debugging(debugNetdev) {
fmt.Printf("[Accept] sockfd: %d\r\n", sockfd)
}
w.mu.Lock()
defer w.mu.Unlock()
socket, ok := w.sockets[sockfd]
if !ok {
return -1, netip.AddrPort{}, netdev.ErrInvalidSocketFd
}
switch socket.protocol {
case netdev.IPPROTO_TCP:
default:
return -1, netip.AddrPort{}, netdev.ErrProtocolNotSupported
}
skip:
for {
// Accept() will be sleeping most of the time, checking for
// new clients every 1/10 sec.
w.mu.Unlock()
time.Sleep(100 * time.Millisecond)
w.mu.Lock()
// Check if we've faulted
if w.fault != nil {
return -1, netip.AddrPort{}, w.fault
}
// Check if a client has data
var client sock = w.accept(socket.sock)
if client == noSocketAvail {
// None ready
continue
}
// If we already have a socket for the client, skip
for _, s := range w.sockets {
if s.sock == client {
continue skip
}
}
// Otherwise, create a new socket
clientfd := w.newSockfd()
if clientfd == -1 {
return -1, netip.AddrPort{}, netdev.ErrNoMoreSockets
}
w.sockets[clientfd] = &Socket{
protocol: netdev.IPPROTO_TCP,
sock: client,
clientConnected: true,
}
raddr := w.getRemoteData(client)
return clientfd, raddr, nil
}
}
func (w *wifinina) sockDown(socket *Socket) bool {
if socket.protocol == netdev.IPPROTO_UDP {
return false
}
return w.getClientState(socket.sock) != tcpStateEstablished
}
func (w *wifinina) sendTCP(sock sock, buf []byte, deadline time.Time) (int, error) {
var timeoutDataSent = 25
// Send it
n := int(w.sendData(sock, buf))
if n == 0 {
return -1, io.EOF
}
// Check if data was sent
for i := 0; i < timeoutDataSent; i++ {
sent := w.checkDataSent(sock)
if sent {
return n, nil
}
// Check if we've timed out
if !deadline.IsZero() {
if time.Now().After(deadline) {
return -1, netdev.ErrTimeout
}
}
// Check if socket went down
if w.getClientState(sock) != tcpStateEstablished {
return -1, io.EOF
}
// Check if we've faulted
if w.fault != nil {
return -1, w.fault
}
// Unlock while we sleep, so others can make progress
w.mu.Unlock()
time.Sleep(100 * time.Millisecond)
w.mu.Lock()
}
return -1, netdev.ErrTimeout
}
func (w *wifinina) sendUDP(sock sock, raddr netip.AddrPort, buf []byte, deadline time.Time) (int, error) {
// Start a client for each send
w.startClient(sock, "", toUint32(raddr.Addr().As4()), raddr.Port(), protoModeUDP)
// Queue it
ok := w.insertDataBuf(sock, buf)
if !ok {
return -1, fmt.Errorf("Insert UDP data failed, len(buf)=%d", len(buf))
}
// Send it
ok = w.sendUDPData(sock)
if !ok {
return -1, fmt.Errorf("Send UDP data failed, len(buf)=%d", len(buf))
}
return len(buf), nil
}
func (w *wifinina) sendChunk(sockfd int, buf []byte, deadline time.Time) (int, error) {
socket, ok := w.sockets[sockfd]
if !ok {
return -1, netdev.ErrInvalidSocketFd
}
// Check if we've timed out
if !deadline.IsZero() {
if time.Now().After(deadline) {
return -1, netdev.ErrTimeout
}
}
switch socket.protocol {
case netdev.IPPROTO_TCP, netdev.IPPROTO_TLS:
return w.sendTCP(socket.sock, buf, deadline)
case netdev.IPPROTO_UDP:
return w.sendUDP(socket.sock, socket.raddr, buf, deadline)
}
return -1, netdev.ErrProtocolNotSupported
}
func (w *wifinina) Send(sockfd int, buf []byte, flags int,
deadline time.Time) (int, error) {
if debugging(debugNetdev) {
fmt.Printf("[Send] sockfd: %d, len(buf): %d, flags: %d\r\n",
sockfd, len(buf), flags)
}
w.mu.Lock()
defer w.mu.Unlock()
// Break large bufs into chunks so we don't overrun the hw queue
chunkSize := 1436
for i := 0; i < len(buf); i += chunkSize {
end := i + chunkSize
if end > len(buf) {
end = len(buf)
}
_, err := w.sendChunk(sockfd, buf[i:end], deadline)
if err != nil {
return -1, err
}
}
return len(buf), nil
}
func (w *wifinina) Recv(sockfd int, buf []byte, flags int,
deadline time.Time) (int, error) {
if debugging(debugNetdev) {
fmt.Printf("[Recv] sockfd: %d, len(buf): %d, flags: %d\r\n",
sockfd, len(buf), flags)
}
w.mu.Lock()
defer w.mu.Unlock()
socket, ok := w.sockets[sockfd]
if !ok {
return -1, netdev.ErrInvalidSocketFd
}
// Limit max read size to chunk large read requests
var max = len(buf)
if max > 1436 {
max = 1436
}
for {
// Check if we've timed out
if !deadline.IsZero() {
if time.Now().After(deadline) {
return -1, netdev.ErrTimeout
}
}
// Receive into buf, if any data available. It's ok if no data
// is available, we'll just sleep a bit and recheck. Recv()
// doesn't return unless there is data, even a single byte, or
// on error such as timeout or EOF.
n := int(w.getDataBuf(socket.sock, buf[:max]))
if n > 0 {
if debugging(debugNetdev) {
fmt.Printf("[<--Recv] sockfd: %d, n: %d\r\n",
sockfd, n)
}
return n, nil
}
// Check if socket went down
if w.sockDown(socket) {
// Get any last bytes
n = int(w.getDataBuf(socket.sock, buf[:max]))
if debugging(debugNetdev) {
fmt.Printf("[<--Recv] sockfd: %d, n: %d, EOF\r\n",
sockfd, n)
}
if n > 0 {
return n, io.EOF
}
return -1, io.EOF
}
// Check if we've faulted
if w.fault != nil {
return -1, w.fault
}
// Unlock while we sleep, so others can make progress
w.mu.Unlock()
time.Sleep(100 * time.Millisecond)
w.mu.Lock()
}
}
func (w *wifinina) Close(sockfd int) error {
if debugging(debugNetdev) {
fmt.Printf("[Close] sockfd: %d\r\n", sockfd)
}
w.mu.Lock()
defer w.mu.Unlock()
socket, ok := w.sockets[sockfd]
if !ok {
return netdev.ErrInvalidSocketFd
}
if socket.clientConnected {
w.stopClient(socket.sock)
}
delete(w.sockets, sockfd)
return nil
}
func (w *wifinina) SetSockOpt(sockfd int, level int, opt int, value interface{}) error {
if debugging(debugNetdev) {
fmt.Printf("[SetSockOpt] sockfd: %d\r\n", sockfd)
}
return netdev.ErrNotSupported
}
func (w *wifinina) startClient(sock sock, hostname string, addr uint32, port uint16, mode uint8) {
if debugging(debugCmd) {
fmt.Printf(" [cmdStartClientTCP] sock: %d, hostname: \"%s\", addr: % 02X, port: %d, mode: %d\r\n",
sock, hostname, addr, port, mode)
}
w.waitForChipReady()
w.spiChipSelect()
if len(hostname) > 0 {
w.sendCmd(cmdStartClientTCP, 5)
w.sendParamStr(hostname, false)
} else {
w.sendCmd(cmdStartClientTCP, 4)
}
w.sendParam32(addr, false)
w.sendParam16(port, false)
w.sendParam8(uint8(sock), false)
w.sendParam8(mode, true)
if len(hostname) > 0 {
w.padTo4(17 + len(hostname))
}
w.spiChipDeselect()
w.waitRspCmd1(cmdStartClientTCP)
}
func (w *wifinina) getSocket() sock {
if debugging(debugCmd) {
fmt.Printf(" [cmdGetSocket]\r\n")
}
return sock(w.getUint8(w.req0(cmdGetSocket)))
}
func (w *wifinina) getClientState(sock sock) uint8 {
if debugging(debugCmd) {
fmt.Printf(" [cmdGetClientStateTCP] sock: %d\r\n", sock)
}
return w.getUint8(w.reqUint8(cmdGetClientStateTCP, uint8(sock)))
}
func (w *wifinina) sendData(sock sock, buf []byte) uint16 {
if debugging(debugCmd) {
fmt.Printf(" [cmdSendDataTCP] sock: %d, len(buf): %d\r\n",
sock, len(buf))
}
w.waitForChipReady()
w.spiChipSelect()
l := w.sendCmd(cmdSendDataTCP, 2)
l += w.sendParamBuf([]byte{uint8(sock)}, false)
l += w.sendParamBuf(buf, true)
w.addPadding(l)
w.spiChipDeselect()
sent := w.getUint16(w.waitRspCmd1(cmdSendDataTCP))
return bits.RotateLeft16(sent, 8)
}
func (w *wifinina) checkDataSent(sock sock) bool {
if debugging(debugCmd) {
fmt.Printf(" [cmdDataSentTCP] sock: %d\r\n", sock)
}
sent := w.getUint8(w.reqUint8(cmdDataSentTCP, uint8(sock)))
return sent > 0
}
func (w *wifinina) getDataBuf(sock sock, buf []byte) uint16 {
if debugging(debugCmd) {
fmt.Printf(" [cmdGetDatabufTCP] sock: %d, len(buf): %d\r\n",
sock, len(buf))
}
w.waitForChipReady()
w.spiChipSelect()
p := uint16(len(buf))
l := w.sendCmd(cmdGetDatabufTCP, 2)
l += w.sendParamBuf([]byte{uint8(sock)}, false)
l += w.sendParamBuf([]byte{uint8(p & 0x00FF), uint8((p) >> 8)}, true)
w.addPadding(l)
w.spiChipDeselect()
w.waitForChipReady()
w.spiChipSelect()
n := w.waitRspBuf16(cmdGetDatabufTCP, buf)
w.spiChipDeselect()
if n > 0 {
if debugging(debugCmd) {
fmt.Printf(" [<--cmdGetDatabufTCP] sock: %d, got n: %d\r\n",
sock, n)
}
}
return n
}
func (w *wifinina) stopClient(sock sock) {
if debugging(debugCmd) {
fmt.Printf(" [cmdStopClientTCP] sock: %d\r\n", sock)
}
w.getUint8(w.reqUint8(cmdStopClientTCP, uint8(sock)))
}
func (w *wifinina) startServer(sock sock, port uint16, mode uint8) {
if debugging(debugCmd) {
fmt.Printf(" [cmdStartServerTCP] sock: %d, port: %d, mode: %d\r\n",
sock, port, mode)
}
w.waitForChipReady()
w.spiChipSelect()
l := w.sendCmd(cmdStartServerTCP, 3)
l += w.sendParam16(port, false)
l += w.sendParam8(uint8(sock), false)
l += w.sendParam8(mode, true)
w.addPadding(l)
w.spiChipDeselect()
w.waitRspCmd1(cmdStartServerTCP)
}
func (w *wifinina) accept(s sock) sock {
if debugging(debugCmd) {
fmt.Printf(" [cmdAvailDataTCP] sock: %d\r\n", s)
}
w.waitForChipReady()
w.spiChipSelect()
l := w.sendCmd(cmdAvailDataTCP, 1)
l += w.sendParam8(uint8(s), true)
w.addPadding(l)
w.spiChipDeselect()
newsock16 := w.getUint16(w.waitRspCmd1(cmdAvailDataTCP))
newsock := sock(uint8(bits.RotateLeft16(newsock16, 8)))
if newsock != noSocketAvail {
if debugging(debugCmd) {
fmt.Printf(" [cmdAvailDataTCP-->] sock: %d, got sock: %d\r\n",
s, newsock)
}
}
return newsock
}
func (w *wifinina) getRemoteData(s sock) netip.AddrPort {
if debugging(debugCmd) {
fmt.Printf(" [cmdGetRemoteData] sock: %d\r\n", s)
}
sl := make([]string, 2)
l := w.reqRspStr1(cmdGetRemoteData, uint8(s), sl)
if l != 2 {
w.faultf("getRemoteData wanted l=2, got l=%d", l)
return netip.AddrPort{}
}
ip, _ := netip.AddrFromSlice([]byte(sl[0])[:4])
port := binary.BigEndian.Uint16([]byte(sl[1]))
return netip.AddrPortFrom(ip, port)
}
// insertDataBuf adds data to the buffer used for sending UDP data
func (w *wifinina) insertDataBuf(sock sock, buf []byte) bool {
if debugging(debugCmd) {
fmt.Printf(" [cmdInsertDataBuf] sock: %d, len(buf): %d\r\n",
sock, len(buf))
}
w.waitForChipReady()
w.spiChipSelect()
l := w.sendCmd(cmdInsertDataBuf, 2)
l += w.sendParamBuf([]byte{uint8(sock)}, false)
l += w.sendParamBuf(buf, true)
w.addPadding(l)
w.spiChipDeselect()
n := w.getUint8(w.waitRspCmd1(cmdInsertDataBuf))
return n == 1
}
// sendUDPData sends the data previously added to the UDP buffer
func (w *wifinina) sendUDPData(sock sock) bool {
if debugging(debugCmd) {
fmt.Printf(" [cmdSendDataUDP] sock: %d\r\n", sock)
}
w.waitForChipReady()
w.spiChipSelect()
l := w.sendCmd(cmdSendDataUDP, 1)
l += w.sendParam8(uint8(sock), true)
w.addPadding(l)
w.spiChipDeselect()
n := w.getUint8(w.waitRspCmd1(cmdSendDataUDP))
return n == 1
}
func (w *wifinina) disconnect() {
if debugging(debugCmd) {
fmt.Printf(" [cmdDisconnect]\r\n")
}
w.req1(cmdDisconnect)
}
func (w *wifinina) getFwVersion() string {
if debugging(debugCmd) {
fmt.Printf(" [cmdGetFwVersion]\r\n")
}
return w.getString(w.req0(cmdGetFwVersion))
}
func (w *wifinina) getConnectionStatus() connectionStatus {
if debugging(debugCmd) {
fmt.Printf(" [cmdGetConnStatus]\r\n")
}
status := w.getUint8(w.req0(cmdGetConnStatus))
return connectionStatus(status)
}
func (w *wifinina) getCurrentencryptionType() encryptionType {
enctype := w.getUint8(w.req1(cmdGetCurrEncrType))
return encryptionType(enctype)
}
func (w *wifinina) getCurrentBSSID() net.HardwareAddr {
return w.getMACAddress(w.req1(cmdGetCurrBSSID))
}
func (w *wifinina) getCurrentRSSI() int32 {
return w.getInt32(w.req1(cmdGetCurrRSSI))
}
func (w *wifinina) getCurrentSSID() string {
return w.getString(w.req1(cmdGetCurrSSID))
}
func (w *wifinina) getMACAddr() net.HardwareAddr {
if debugging(debugCmd) {
fmt.Printf(" [cmdGetMACAddr]\r\n")
}
return w.getMACAddress(w.req1(cmdGetMACAddr))
}
func (w *wifinina) faultf(f string, args ...any) {
// Only record the first fault
if w.fault == nil {
w.fault = fmt.Errorf(f, args...)
}
}
func (w *wifinina) getIP() (ip, subnet, gateway netip.Addr) {
if debugging(debugCmd) {
fmt.Printf(" [cmdGetIPAddr]\r\n")
}
sl := make([]string, 3)
l := w.reqRspStr1(cmdGetIPAddr, dummyData, sl)
if l != 3 {
w.faultf("getIP wanted l=3, got l=%d", l)
return
}
ip, _ = netip.AddrFromSlice([]byte(sl[0])[:4])
subnet, _ = netip.AddrFromSlice([]byte(sl[1])[:4])
gateway, _ = netip.AddrFromSlice([]byte(sl[2])[:4])
return
}
func (w *wifinina) getHostByName(name string) string {
if debugging(debugCmd) {
fmt.Printf(" [cmdGetHostByName]\r\n")
}
ok := w.getUint8(w.reqStr(cmdReqHostByName, name))
if ok != 1 {
return ""
}
return w.getString(w.req0(cmdGetHostByName))
}
func (w *wifinina) getNetworkBSSID(idx int) net.HardwareAddr {
if idx < 0 || idx >= maxNetworks {
return net.HardwareAddr{}
}
return w.getMACAddress(w.reqUint8(cmdGetIdxBSSID, uint8(idx)))
}
func (w *wifinina) getNetworkChannel(idx int) uint8 {
if idx < 0 || idx >= maxNetworks {
return 0
}
return w.getUint8(w.reqUint8(cmdGetIdxChannel, uint8(idx)))
}
func (w *wifinina) getNetworkEncrType(idx int) encryptionType {
if idx < 0 || idx >= maxNetworks {
return 0
}
enctype := w.getUint8(w.reqUint8(cmdGetIdxEncrType, uint8(idx)))
return encryptionType(enctype)
}
func (w *wifinina) getNetworkRSSI(idx int) int32 {
if idx < 0 || idx >= maxNetworks {
return 0
}
return w.getInt32(w.reqUint8(cmdGetIdxRSSI, uint8(idx)))
}
func (w *wifinina) getNetworkSSID(idx int) string {
if idx < 0 || idx >= maxNetworks {
return ""
}
return w.ssids[idx]
}
func (w *wifinina) getReasonCode() uint8 {
return w.getUint8(w.req0(cmdGetReasonCode))
}
// getTime is the time as a Unix timestamp
func (w *wifinina) getTime() uint32 {
return w.getUint32(w.req0(cmdGetTime))
}
func (w *wifinina) getTemperature() float32 {
return w.getFloat32(w.req0(cmdGetTemperature))
}
func (w *wifinina) setDebug(on bool) {
var v uint8
if on {
v = 1
}
w.reqUint8(cmdSetDebug, v)
}
func (w *wifinina) setNetwork(ssid string) {
w.reqStr(cmdSetNet, ssid)
}
func (w *wifinina) setPassphrase(ssid string, passphrase string) {
if debugging(debugCmd) {
fmt.Printf(" [cmdSetPassphrase] ssid: %s, passphrase: ******\r\n",
ssid)
}
// Dont' show passphrase in debug output
saveDebug := _debug
_debug = _debug & ^debugDetail
w.reqStr2(cmdSetPassphrase, ssid, passphrase)
_debug = saveDebug
}
func (w *wifinina) setKey(ssid string, index uint8, key string) {
w.waitForChipReady()
w.spiChipSelect()
w.sendCmd(cmdSetKey, 3)
w.sendParamStr(ssid, false)
w.sendParam8(index, false)
w.sendParamStr(key, true)
w.padTo4(8 + len(ssid) + len(key))
w.spiChipDeselect()
w.waitRspCmd1(cmdSetKey)
}
func (w *wifinina) setNetworkForAP(ssid string) {
w.reqStr(cmdSetAPNet, ssid)
}
func (w *wifinina) setPassphraseForAP(ssid string, passphrase string) {
w.reqStr2(cmdSetAPPassphrase, ssid, passphrase)
}
func (w *wifinina) setDNS(which uint8, dns1 uint32, dns2 uint32) {
w.waitForChipReady()
w.spiChipSelect()
w.sendCmd(cmdSetDNSConfig, 3)
w.sendParam8(which, false)
w.sendParam32(dns1, false)
w.sendParam32(dns2, true)
//pad??
w.spiChipDeselect()
w.waitRspCmd1(cmdSetDNSConfig)
}
func (w *wifinina) setHostname(hostname string) {
w.waitForChipReady()
w.spiChipSelect()
w.sendCmd(cmdSetHostname, 3)
w.sendParamStr(hostname, true)
w.padTo4(5 + len(hostname))
w.spiChipDeselect()
w.waitRspCmd1(cmdSetHostname)
}
func (w *wifinina) setPowerMode(mode uint8) {
w.reqUint8(cmdSetPowerMode, mode)
}
func (w *wifinina) scanNetworks() uint8 {
return w.reqRspStr0(cmdScanNetworks, w.ssids[:])
}
func (w *wifinina) startScanNetworks() uint8 {
return w.getUint8(w.req0(cmdStartScanNetworks))
}
func (w *wifinina) PinMode(pin uint8, mode uint8) {
if debugging(debugCmd) {
fmt.Printf(" [cmdSetPinMode] pin: %d, mode: %d\r\n", pin, mode)
}
w.req2Uint8(cmdSetPinMode, pin, mode)
}
func (w *wifinina) DigitalWrite(pin uint8, value uint8) {
if debugging(debugCmd) {
fmt.Printf(" [cmdSetDigitialWrite] pin: %d, value: %d\r\n", pin, value)
}
w.req2Uint8(cmdSetDigitalWrite, pin, value)
}
func (w *wifinina) AnalogWrite(pin uint8, value uint8) {
w.req2Uint8(cmdSetAnalogWrite, pin, value)
}
func (w *wifinina) getString(l uint8) string {
return string(w.buf[0:l])
}
func (w *wifinina) getUint8(l uint8) uint8 {
if l == 1 {
return w.buf[0]
}
w.faultf("expected length 1, was actually %d", l)
return 0
}
func (w *wifinina) getUint16(l uint8) uint16 {
if l == 2 {
return binary.BigEndian.Uint16(w.buf[0:2])
}
w.faultf("expected length 2, was actually %d", l)
return 0
}
func (w *wifinina) getUint32(l uint8) uint32 {
if l == 4 {
return binary.BigEndian.Uint32(w.buf[0:4])
}
w.faultf("expected length 4, was actually %d", l)
return 0
}
func (w *wifinina) getInt32(l uint8) int32 {
return int32(w.getUint32(l))
}
func (w *wifinina) getFloat32(l uint8) float32 {
return float32(w.getUint32(l))
}
func (w *wifinina) getMACAddress(l uint8) net.HardwareAddr {
if l == 6 {
mac := w.buf[0:6]
// Reverse the bytes
for i, j := 0, len(mac)-1; i < j; i, j = i+1, j-1 {
mac[i], mac[j] = mac[j], mac[i]
}
return mac
}
w.faultf("expected length 6, was actually %d", l)
return net.HardwareAddr{}
}
func (w *wifinina) transfer(b byte) byte {
v, err := w.spi.Transfer(b)
if err != nil {
w.faultf("SPI.Transfer")
return 0
}
return v
}
// Cmd Struct Message */
// ._______________________________________________________________________.
// | START CMD | C/R | CMD | N.PARAM | PARAM LEN | PARAM | .. | END CMD |
// |___________|______|______|_________|___________|________|____|_________|
// | 8 bit | 1bit | 7bit | 8bit | 8bit | nbytes | .. | 8bit |
// |___________|______|______|_________|___________|________|____|_________|
// req0 sends a command to the device with no request parameters
func (w *wifinina) req0(cmd uint8) uint8 {
w.sendCmd0(cmd)
return w.waitRspCmd1(cmd)
}
// req1 sends a command to the device with a single dummy parameters of 0xFF
func (w *wifinina) req1(cmd uint8) uint8 {
return w.reqUint8(cmd, dummyData)
}
// reqUint8 sends a command to the device with a single uint8 parameter
func (w *wifinina) reqUint8(cmd uint8, data uint8) uint8 {
w.sendCmdPadded1(cmd, data)
return w.waitRspCmd1(cmd)
}
// req2Uint8 sends a command to the device with two uint8 parameters
func (w *wifinina) req2Uint8(cmd, p1, p2 uint8) uint8 {
w.sendCmdPadded2(cmd, p1, p2)
return w.waitRspCmd1(cmd)
}
// reqStr sends a command to the device with a single string parameter
func (w *wifinina) reqStr(cmd uint8, p1 string) uint8 {
w.sendCmdStr(cmd, p1)
return w.waitRspCmd1(cmd)
}
// reqStr2 sends a command to the device with 2 string parameters
func (w *wifinina) reqStr2(cmd uint8, p1 string, p2 string) {
w.sendCmdStr2(cmd, p1, p2)
w.waitRspCmd1(cmd)
}
// reqStrRsp0 sends a command passing a string slice for the response
func (w *wifinina) reqRspStr0(cmd uint8, sl []string) (l uint8) {
w.sendCmd0(cmd)
w.waitForChipReady()
w.spiChipSelect()
l = w.waitRspStr(cmd, sl)
w.spiChipDeselect()
return
}
// reqStrRsp1 sends a command with a uint8 param and a string slice for the response
func (w *wifinina) reqRspStr1(cmd uint8, data uint8, sl []string) uint8 {
w.sendCmdPadded1(cmd, data)
w.waitForChipReady()
w.spiChipSelect()
l := w.waitRspStr(cmd, sl)
w.spiChipDeselect()
return l
}
func (w *wifinina) sendCmd0(cmd uint8) {
w.waitForChipReady()
w.spiChipSelect()
w.sendCmd(cmd, 0)
w.spiChipDeselect()
}
func (w *wifinina) sendCmdPadded1(cmd uint8, data uint8) {
w.waitForChipReady()
w.spiChipSelect()
w.sendCmd(cmd, 1)
w.sendParam8(data, true)
w.transfer(dummyData)
w.transfer(dummyData)
w.spiChipDeselect()
return
}
func (w *wifinina) sendCmdPadded2(cmd, data1, data2 uint8) {
w.waitForChipReady()
w.spiChipSelect()
w.sendCmd(cmd, 1)
w.sendParam8(data1, false)
w.sendParam8(data2, true)
w.transfer(dummyData)
w.spiChipDeselect()
}
func (w *wifinina) sendCmdStr(cmd uint8, p1 string) {
w.waitForChipReady()
w.spiChipSelect()
w.sendCmd(cmd, 1)
w.sendParamStr(p1, true)
w.padTo4(5 + len(p1))
w.spiChipDeselect()
}
func (w *wifinina) sendCmdStr2(cmd uint8, p1 string, p2 string) {
w.waitForChipReady()
w.spiChipSelect()
w.sendCmd(cmd, 2)
w.sendParamStr(p1, false)
w.sendParamStr(p2, true)
w.padTo4(6 + len(p1) + len(p2))
w.spiChipDeselect()
}
func (w *wifinina) waitRspCmd1(cmd uint8) uint8 {
w.waitForChipReady()
w.spiChipSelect()
l := w.waitRspCmd(cmd, 1)
w.spiChipDeselect()
return l
}
func (w *wifinina) sendCmd(cmd uint8, numParam uint8) (l int) {
if debugging(debugDetail) {
fmt.Printf(" sendCmd: %02X %02X %02X",
cmdStart, cmd & ^(uint8(flagReply)), numParam)
}
l = 3
w.transfer(cmdStart)
w.transfer(cmd & ^(uint8(flagReply)))
w.transfer(numParam)
if numParam == 0 {
w.transfer(cmdEnd)
l += 1
if debugging(debugDetail) {
fmt.Printf(" %02X", cmdEnd)
}
}
if debugging(debugDetail) {
fmt.Printf(" (%d)\r\n", l)
}
return
}
func (w *wifinina) sendParamLen16(p uint16) (l int) {
w.transfer(uint8(p >> 8))
w.transfer(uint8(p & 0xFF))
if debugging(debugDetail) {
fmt.Printf(" %02X %02X", uint8(p>>8), uint8(p&0xFF))
}
return 2
}
func (w *wifinina) sendParamBuf(p []byte, isLastParam bool) (l int) {
if debugging(debugDetail) {
fmt.Printf(" sendParamBuf:")
}
l += w.sendParamLen16(uint16(len(p)))
for _, b := range p {
if debugging(debugDetail) {
fmt.Printf(" %02X", b)
}
w.transfer(b)
l += 1
}
if isLastParam {
if debugging(debugDetail) {
fmt.Printf(" %02X", cmdEnd)
}
w.transfer(cmdEnd)
l += 1
}
if debugging(debugDetail) {
fmt.Printf(" (%d) \r\n", l)
}
return
}
func (w *wifinina) sendParamStr(p string, isLastParam bool) (l int) {
if debugging(debugDetail) {
fmt.Printf(" sendParamStr: p: %s, lastParam: %t\r\n", p, isLastParam)
}
l = len(p)
w.transfer(uint8(l))
if l > 0 {
w.spi.Tx([]byte(p), nil)
}
if isLastParam {
w.transfer(cmdEnd)
l += 1
}
return
}
func (w *wifinina) sendParam8(p uint8, isLastParam bool) (l int) {
if debugging(debugDetail) {
fmt.Printf(" sendParam8: p: %d, lastParam: %t\r\n", p, isLastParam)
}
l = 2
w.transfer(1)
w.transfer(p)
if isLastParam {
w.transfer(cmdEnd)
l += 1
}
return
}
func (w *wifinina) sendParam16(p uint16, isLastParam bool) (l int) {
if debugging(debugDetail) {
fmt.Printf(" sendParam16: p: %d, lastParam: %t\r\n", p, isLastParam)
}
l = 3
w.transfer(2)
w.transfer(uint8(p >> 8))
w.transfer(uint8(p & 0xFF))
if isLastParam {
w.transfer(cmdEnd)
l += 1
}
return
}
func (w *wifinina) sendParam32(p uint32, isLastParam bool) (l int) {
if debugging(debugDetail) {
fmt.Printf(" sendParam32: p: %d, lastParam: %t\r\n", p, isLastParam)
}
l = 5
w.transfer(4)
w.transfer(uint8(p >> 24))
w.transfer(uint8(p >> 16))
w.transfer(uint8(p >> 8))
w.transfer(uint8(p & 0xFF))
if isLastParam {
w.transfer(cmdEnd)
l += 1
}
return
}
func (w *wifinina) waitForChipReady() {
if debugging(debugDetail) {
fmt.Printf(" waitForChipReady\r\n")
}
for i := 0; w.ack.Get(); i++ {
time.Sleep(1 * time.Millisecond)
if i == 10000 {
w.faultf("hung in waitForChipReady")
return
}
}
}
func (w *wifinina) spiChipSelect() {
if debugging(debugDetail) {
fmt.Printf(" spiChipSelect\r\n")
}
w.cs.Low()
start := time.Now()
for time.Since(start) < 10*time.Millisecond {
if w.ack.Get() {
return
}
time.Sleep(100 * time.Microsecond)
}
w.faultf("hung in spiChipSelect")
}
func (w *wifinina) spiChipDeselect() {
if debugging(debugDetail) {
fmt.Printf(" spiChipDeselect\r\n")
}
w.cs.High()
}
func (w *wifinina) waitSpiChar(desired byte) {
if debugging(debugDetail) {
fmt.Printf(" waitSpiChar: desired: %02X\r\n", desired)
}
var read byte
for i := 0; i < 10; i++ {
w.readParam(&read)
switch read {
case cmdErr:
w.faultf("cmdErr received, waiting for %d", desired)
return
case desired:
return
}
time.Sleep(10 * time.Millisecond)
}
w.faultf("timeout waiting for SPI char %02X\r\n", desired)
}
func (w *wifinina) waitRspCmd(cmd uint8, np uint8) (l uint8) {
if debugging(debugDetail) {
fmt.Printf(" waitRspCmd: cmd: %02X, np: %d\r\n", cmd, np)
}
var data byte
w.waitSpiChar(cmdStart)
if !w.readAndCheckByte(cmd|flagReply, &data) {
w.faultf("expected cmd %02X, read %02X", cmd, data)
return
}
if w.readAndCheckByte(np, &data) {
w.readParam(&l)
for i := uint8(0); i < l; i++ {
w.readParam(&w.buf[i])
}
if !w.readAndCheckByte(cmdEnd, &data) {
w.faultf("expected cmdEnd, read %02X", data)
}
}
return
}
func (w *wifinina) waitRspBuf16(cmd uint8, buf []byte) (l uint16) {
if debugging(debugDetail) {
fmt.Printf(" waitRspBuf16: cmd: %02X, len(buf): %d\r\n", cmd, len(buf))
}
var data byte
w.waitSpiChar(cmdStart)
if !w.readAndCheckByte(cmd|flagReply, &data) {
w.faultf("expected cmd %02X, read %02X", cmd, data)
return
}
if w.readAndCheckByte(1, &data) {
l = w.readParamLen16()
for i := uint16(0); i < l; i++ {
w.readParam(&buf[i])
}
if !w.readAndCheckByte(cmdEnd, &data) {
w.faultf("expected cmdEnd, read %02X", data)
}
}
return
}
func (w *wifinina) waitRspStr(cmd uint8, sl []string) (numRead uint8) {
if debugging(debugDetail) {
fmt.Printf(" waitRspStr: cmd: %02X, len(sl): %d\r\n", cmd, len(sl))
}
var data byte
w.waitSpiChar(cmdStart)
if !w.readAndCheckByte(cmd|flagReply, &data) {
w.faultf("expected cmd %02X, read %02X", cmd, data)
return
}
numRead = w.transfer(dummyData)
if numRead == 0 {
w.faultf("waitRspStr numRead == 0")
return
}
maxNumRead := uint8(len(sl))
for j, l := uint8(0), uint8(0); j < numRead; j++ {
w.readParam(&l)
for i := uint8(0); i < l; i++ {
w.readParam(&w.buf[i])
}
if j < maxNumRead {
sl[j] = string(w.buf[0:l])
if debugging(debugDetail) {
fmt.Printf(" str: %d (%d) - %08X\r\n", j, l, []byte(sl[j]))
}
}
}
for j := numRead; j < maxNumRead; j++ {
if debugging(debugDetail) {
fmt.Printf(" str: ", j, "\"\"\r")
}
sl[j] = ""
}
if !w.readAndCheckByte(cmdEnd, &data) {
w.faultf("expected cmdEnd, read %02X", data)
return
}
if numRead > maxNumRead {
numRead = maxNumRead
}
return
}
func (w *wifinina) readAndCheckByte(check byte, read *byte) bool {
w.readParam(read)
return *read == check
}
// readParamLen16 reads 2 bytes from the SPI bus (MSB first), returning uint16
func (w *wifinina) readParamLen16() (v uint16) {
b := w.transfer(0xFF)
v = uint16(b) << 8
b = w.transfer(0xFF)
v |= uint16(b)
return
}
func (w *wifinina) readParam(b *byte) {
*b = w.transfer(0xFF)
}
func (w *wifinina) addPadding(l int) {
if debugging(debugDetail) {
fmt.Printf(" addPadding: l: %d\r\n", l)
}
for i := (4 - (l % 4)) & 3; i > 0; i-- {
if debugging(debugDetail) {
fmt.Printf(" padding\r\n")
}
w.transfer(dummyData)
}
}
func (w *wifinina) padTo4(l int) {
if debugging(debugDetail) {
fmt.Printf(" padTo4: l: %d\r\n", l)
}
for l%4 != 0 {
if debugging(debugDetail) {
fmt.Printf(" padding\r\n")
}
w.transfer(dummyData)
l++
}
}