Add network device driver model, netdev

This commit is contained in:
Scott Feldman
2023-03-28 12:18:59 -07:00
committed by Ron Evans
parent 00b1b4fbfb
commit 276feecc20
128 changed files with 6389 additions and 15539 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ The way this driver works is by using the SPI interface of your microcontroller
## Using the WiFiNINA Driver
For information on how to use this driver, please take a look at the examples located in the [examples/wifinina](../examples/wifinina) directory.
For information on how to use this driver, please take a look at the examples located in the [examples/net](../examples/net) directory.
## Firmware
-31
View File
@@ -1,31 +0,0 @@
package wifinina
import (
"time"
"tinygo.org/x/drivers/net"
)
func (d *Device) ConnectToAccessPoint(ssid, pass string, timeout time.Duration) error {
if len(ssid) == 0 {
return net.ErrWiFiMissingSSID
}
start := time.Now()
d.SetPassphrase(ssid, pass)
for time.Since(start) < timeout {
st, _ := d.GetConnectionStatus()
if st == StatusConnected {
return nil
}
time.Sleep(100 * time.Millisecond)
}
return net.ErrWiFiConnectTimeout
}
func (d *Device) GetClientIP() (string, error) {
ip, _, _, err := d.GetIP()
return ip.String(), err
}
+15 -3
View File
@@ -1,5 +1,17 @@
//go:build wifidebug
package wifinina
var _debug = true
type debug uint8
const (
debugBasic debug = 1 << iota // show fw version, mac addr, etc
debugNetdev // show netdev entry points
debugCmd // show non-chatty wifinina cmds
debugDetail // show chatty wifinina cmds
debugOff = 0
debugAll = debugBasic | debugNetdev | debugCmd | debugDetail
)
func debugging(want debug) bool {
return (_debug & want) != 0
}
-323
View File
@@ -1,323 +0,0 @@
package wifinina
import (
"bufio"
"bytes"
"fmt"
"io"
"strconv"
"strings"
"time"
"tinygo.org/x/drivers/net/http"
)
func (d *Device) ListenAndServe(addr string, handler http.Handler) error {
if handler == nil {
handler = http.DefaultServeMux
}
server := newServer(d, handler)
if err := server.listen(addr); err != nil {
return err
}
for {
client, err := server.accept()
if err != nil {
return err
}
if err := client.handleHTTP(); err != nil {
return err
}
if err = client.stop(); err != nil {
return err
}
}
return nil
}
// Server stuff
type server struct {
device *Device
handler http.Handler
sock uint8
clients map[uint8]*client // keyed by client sock
}
func newServer(device *Device, handler http.Handler) *server {
return &server{
device: device,
handler: handler,
sock: NoSocketAvail,
clients: make(map[uint8]*client),
}
}
func portFromAddr(addr string) (uint16, error) {
// ignore anything before ':' in address
i := strings.LastIndex(addr, ":")
if i < 0 {
return 0, fmt.Errorf("Missing ':' in address")
}
v, err := strconv.ParseUint(addr[i+1:], 10, 16)
if err != nil {
return 0, fmt.Errorf("Parsing address err: %s", err)
}
return uint16(v), nil
}
func (s *server) listen(addr string) error {
port, err := portFromAddr(addr)
if err != nil {
return fmt.Errorf("Getting port err: %s", err)
}
s.sock, err = s.device.GetSocket()
if err != nil {
return fmt.Errorf("Getting socket err: %s", err)
}
if s.sock == NoSocketAvail {
return fmt.Errorf("No socket available")
}
return s.device.StartServer(port, s.sock, ProtoModeTCP)
}
func (s *server) availServer(sock uint8) (uint8, error) {
d := s.device
d.mu.Lock()
defer d.mu.Unlock()
if err := d.waitForChipSelect(); err != nil {
d.spiChipDeselect()
return NoSocketAvail, fmt.Errorf("Wait for CS: %s", err)
}
l := d.sendCmd(CmdAvailDataTCP, 1)
l += d.sendParam8(sock, true)
d.addPadding(l)
d.spiChipDeselect()
_, err := d.waitRspCmd1(CmdAvailDataTCP)
if err != nil {
return NoSocketAvail, fmt.Errorf("Wait for Rsp: %s", err)
}
newsock, err := d.getUint16(2, err)
if err != nil {
return NoSocketAvail, fmt.Errorf("getUint16: %s", err)
}
return uint8(newsock >> 8), nil
}
func (s *server) accept() (*client, error) {
for {
sock, err := s.availServer(s.sock)
if err != nil {
return nil, fmt.Errorf("accept: %w", err)
}
if sock == NoSocketAvail {
continue
}
if client, ok := s.clients[sock]; ok {
return client, nil
}
client := newClient(s, sock)
s.clients[sock] = client
return client, nil
}
}
// client stuff
type client struct {
server *server
device *Device
sock uint8
// HTTP request
req *http.Request
reqBuf bytes.Buffer
readBuf [256]byte
// HTTP response
res bytes.Buffer
resHdr http.Header
resBuf bytes.Buffer
statusCode int
}
func newClient(server *server, sock uint8) *client {
return &client{
server: server,
device: server.device,
sock: sock,
}
}
// client implements http.ResponseWriter interface
func (c *client) Header() http.Header {
return c.resHdr
}
func (c *client) Write(b []byte) (int, error) {
return c.resBuf.Write(b)
}
func (c *client) WriteHeader(statusCode int) {
c.statusCode = statusCode
}
func (c *client) status() uint8 {
d := c.device
d.mu.Lock()
defer d.mu.Unlock()
if err := d.waitForChipSelect(); err != nil {
d.spiChipDeselect()
return 0
}
l := d.sendCmd(CmdGetClientStateTCP, 1)
l += d.sendParam8(c.sock, true)
d.addPadding(l)
d.spiChipDeselect()
_, err := d.waitRspCmd1(CmdGetClientStateTCP)
if err != nil {
return 0
}
status, err := d.getUint8(1, err)
if err != nil {
return 0
}
return status
}
func (c *client) stop() error {
if err := c.device.StopClient(c.sock); err != nil {
return err
}
// Wait max 5 secs for the connection to close
for i := 0; i < 50 && c.status() != uint8(TCPStateClosed); i++ {
time.Sleep(100 * time.Millisecond)
}
if c.status() != uint8(TCPStateClosed) {
return fmt.Errorf("stop failed, client status %x", c.status())
}
return nil
}
func (c *client) handleHTTP() error {
c.reqBuf.Reset()
end := -1
// read the request
start := time.Now()
for {
// TODO use Server.ReadTimeout
if time.Since(start) > 1*time.Second {
return fmt.Errorf("ReadTimeout")
}
n, err := c.device.GetDataBuf(c.sock, c.readBuf[:])
if err != nil {
return fmt.Errorf("GetDataBuf: %s", err)
}
if n == 0 {
time.Sleep(1 * time.Millisecond)
continue
}
c.reqBuf.Write(c.readBuf[:n])
bytesSoFar := c.reqBuf.Bytes()
if end == -1 {
// search for blank line marking end-of-header
end = bytes.Index(bytesSoFar, []byte("\r\n\r\n"))
if end == -1 {
continue
}
// found end-of-header; parse header
end += len([]byte("\r\n\r\n"))
bufio := bufio.NewReader(bytes.NewReader(bytesSoFar[:end]))
c.req, err = http.ReadRequest(bufio)
if err != nil {
return err
}
}
v := c.req.Header.Get("Content-Length")
if v == "" {
// no body; we're done reading request
break
}
length, _ := strconv.Atoi(v)
if end+length == len(bytesSoFar) {
// got the whole body
body := bytes.NewReader(bytesSoFar[end:])
c.req.Body = io.NopCloser(body)
break
}
// continue reading request...
}
// build the response
c.statusCode = 200
c.resHdr = http.Header{}
c.resHdr.Add(`Content-Type`, `text/html; charset=UTF-8`)
c.resHdr.Add(`Connection`, `close`)
c.resBuf.Reset()
c.server.handler.ServeHTTP(c, c.req)
c.resHdr.Add(`Content-Length`, fmt.Sprintf("%d", c.resBuf.Len()))
c.res.Reset()
fmt.Fprintf(&c.res, "HTTP/1.1 %d %s\r\n", c.statusCode,
http.StatusText(c.statusCode))
if err := c.resHdr.Write(&c.res); err != nil {
return err
}
c.res.WriteByte(byte('\n'))
c.res.Write(c.resBuf.Bytes())
// send the response
written, err := c.device.SendData(c.res.Bytes(), c.sock)
if err != nil {
return err
}
if written == 0 {
return ErrDataNotWritten
}
if sent, _ := c.device.CheckDataSent(c.sock); !sent {
return ErrCheckDataError
}
return nil
}
+7 -5
View File
@@ -32,17 +32,18 @@ var (
ErrPinNoDevice = errors.New("wifinina pin: device not set")
)
var pinDevice *Device
var pinDevice *wifinina
func pinUseDevice(d *Device) {
pinDevice = d
func pinUseDevice(w *wifinina) {
pinDevice = w
}
func (p Pin) Configure(config PinConfig) error {
if pinDevice == nil {
return ErrPinNoDevice
}
return pinDevice.PinMode(uint8(p), uint8(config.Mode))
pinDevice.PinMode(uint8(p), uint8(config.Mode))
return nil
}
func (p Pin) Set(high bool) error {
@@ -53,7 +54,8 @@ func (p Pin) Set(high bool) error {
if high {
value = PinHigh
}
return pinDevice.DigitalWrite(uint8(p), value)
pinDevice.DigitalWrite(uint8(p), value)
return nil
}
func (p Pin) High() error {
-3
View File
@@ -1,3 +0,0 @@
WiFiNINA protocol
=================
-270
View File
@@ -1,270 +0,0 @@
package wifinina
import (
"errors"
"strconv"
"time"
)
const (
ReadBufferSize = 128
)
type readBuffer struct {
data [ReadBufferSize]byte
head int
size int
}
func (d *Device) GetDNS(domain string) (string, error) {
ipAddr, err := d.GetHostByName(domain)
return ipAddr.String(), err
}
func (d *Device) ConnectTCPSocket(addr, portStr string) error {
return d.connectSocket(addr, portStr, ProtoModeTCP)
}
func (d *Device) ConnectSSLSocket(addr, portStr string) error {
return d.connectSocket(addr, portStr, ProtoModeTLS)
}
func (d *Device) connectSocket(addr, portStr string, mode uint8) error {
d.proto, d.ip, d.port = mode, 0, 0
// convert port to uint16
port, err := convertPort(portStr)
if err != nil {
return err
}
hostname := addr
ip := uint32(0)
if mode != ProtoModeTLS {
// look up the hostname if necessary; if an IP address was specified, the
// same will be returned. Otherwise, an IPv4 for the hostname is returned.
ipAddr, err := d.GetHostByName(addr)
if err != nil {
return err
}
hostname = ""
ip = ipAddr.AsUint32()
}
// check to see if socket is already set; if so, stop it
if d.sock != NoSocketAvail {
if err := d.stop(); err != nil {
return err
}
}
// get a socket from the device
if d.sock, err = d.GetSocket(); err != nil {
return err
}
// attempt to start the client
if err := d.StartClient(hostname, ip, port, d.sock, mode); err != nil {
return err
}
// FIXME: this 4 second timeout is simply mimicking the Arduino driver
start := time.Now()
for time.Since(start) < 4*time.Second {
connected, err := d.IsConnected()
if err != nil {
return err
}
if connected {
return nil
}
time.Sleep(1 * time.Millisecond)
}
return ErrConnectionTimeout
}
func convertPort(portStr string) (uint16, error) {
p64, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
return 0, errors.New("could not convert port to uint16: " + err.Error())
}
return uint16(p64), nil
}
func (d *Device) ConnectUDPSocket(addr, portStr, lportStr string) (err error) {
d.proto, d.ip, d.port = ProtoModeUDP, 0, 0
// convert remote port to uint16
if d.port, err = convertPort(portStr); err != nil {
return err
}
// convert local port to uint16
var lport uint16
if lport, err = convertPort(lportStr); err != nil {
return err
}
// look up the hostname if necessary; if an IP address was specified, the
// same will be returned. Otherwise, an IPv4 for the hostname is returned.
ipAddr, err := d.GetHostByName(addr)
if err != nil {
return err
}
d.ip = ipAddr.AsUint32()
// check to see if socket is already set; if so, stop it
// TODO: we can probably have more than one socket at once right?
if d.sock != NoSocketAvail {
if err := d.stop(); err != nil {
return err
}
}
// get a socket from the device
if d.sock, err = d.GetSocket(); err != nil {
return err
}
// start listening for UDP packets on the local port
if err := d.StartServer(lport, d.sock, d.proto); err != nil {
return err
}
return nil
}
func (d *Device) DisconnectSocket() error {
return d.stop()
}
func (d *Device) StartSocketSend(size int) error {
// not needed for WiFiNINA???
return nil
}
func (d *Device) Response(timeout int) ([]byte, error) {
return nil, nil
}
func (d *Device) Write(b []byte) (n int, err error) {
if d.sock == NoSocketAvail {
return 0, ErrNoSocketAvail
}
if len(b) == 0 {
return 0, ErrNoData
}
if d.proto == ProtoModeUDP {
if err := d.StartClient("", d.ip, d.port, d.sock, d.proto); err != nil {
return 0, errors.New("error in startClient: " + err.Error())
}
if _, err := d.InsertDataBuf(b, d.sock); err != nil {
return 0, errors.New("error in insertDataBuf: " + err.Error())
}
if _, err := d.SendUDPData(d.sock); err != nil {
return 0, errors.New("error in sendUDPData: " + err.Error())
}
return len(b), nil
} else {
written, err := d.SendData(b, d.sock)
if err != nil {
return 0, err
}
if written == 0 {
return 0, ErrDataNotWritten
}
if sent, _ := d.CheckDataSent(d.sock); !sent {
return 0, ErrCheckDataError
}
return len(b), nil
}
return len(b), nil
}
func (d *Device) ReadSocket(b []byte) (n int, err error) {
avail, err := d.available()
if err != nil {
println("ReadSocket error: " + err.Error())
return 0, err
}
if avail == 0 {
return 0, nil
}
length := len(b)
if avail < length {
length = avail
}
copy(b, d.readBuf.data[d.readBuf.head:d.readBuf.head+length])
d.readBuf.head += length
d.readBuf.size -= length
return length, nil
}
// IsSocketDataAvailable returns of there is socket data available
func (d *Device) IsSocketDataAvailable() bool {
n, err := d.available()
return err == nil && n > 0
}
func (d *Device) available() (int, error) {
if d.readBuf.size == 0 {
n, err := d.GetDataBuf(d.sock, d.readBuf.data[:])
if n > 0 {
d.readBuf.head = 0
d.readBuf.size = n
}
if err != nil {
return int(n), err
}
}
return d.readBuf.size, nil
}
func (d *Device) IsConnected() (bool, error) {
if d.sock == NoSocketAvail {
return false, nil
}
s, err := d.status()
if err != nil {
return false, err
}
isConnected := !(s == uint8(TCPStateListen) || s == uint8(TCPStateClosed) ||
s == uint8(TCPStateFinWait1) || s == uint8(TCPStateFinWait2) || s == uint8(TCPStateTimeWait) ||
s == uint8(TCPStateSynSent) || s == uint8(TCPStateSynRcvd) || s == uint8(TCPStateCloseWait))
// TODO: investigate if the below is necessary (as per Arduino driver)
//if !isConnected {
// //close socket buffer?
// WiFiSocketBuffer.close(_sock);
// _sock = 255;
//}
return isConnected, nil
}
func (d *Device) status() (uint8, error) {
if d.sock == NoSocketAvail {
return uint8(TCPStateClosed), nil
}
return d.GetClientState(d.sock)
}
func (d *Device) stop() error {
if d.sock == NoSocketAvail {
return nil
}
d.StopClient(d.sock)
start := time.Now()
for time.Since(start) < 5*time.Second {
st, _ := d.status()
if st == uint8(TCPStateClosed) {
break
}
time.Sleep(1 * time.Millisecond)
}
d.sock = NoSocketAvail
return nil
}
+1680 -869
View File
File diff suppressed because it is too large Load Diff