Compare commits

...

14 Commits

Author SHA1 Message Date
deadprogram 31f09d93b8 all: update for relase v0.27
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-02-26 19:55:23 +01:00
deadprogram 0ea969f25a docs: update LICENSE year
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-02-26 19:55:23 +01:00
Niccolò Maggioni 8665d44e0f Use int16 for ADXL345 readings (#656)
ADXL345: fix: use int16 for ADXL345 readings
2024-02-24 19:16:06 +01:00
deadprogram c344e5d879 ssd1306: improvements needed for Thumby SPI display
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-02-23 20:07:40 -05:00
BCG 308763f500 Adding driver for rotary encoder support 2024-02-15 16:55:54 +01:00
deadprogram c41f2e472d rtl8720dn, wifinina: use drivers package version as the driver version
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-01-27 16:43:48 +01:00
Scott Feldman aa7bd47c68 examples: add classic network snake test example
Adding this example to test multiple concurrent socket connections.
2024-01-24 08:25:20 +01:00
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
deadprogram 3c5e17423a netlink, examples: use 'ninafw' tag instead of individual board tags to simplify maintenence. Also see PR #4085 in the main TinyGo repo
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-01-17 08:47:00 +01:00
deadprogram 0262122ccd rtl8720dn: allow connecting to open wifi access points
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-01-12 10:45:53 +01:00
Scott Feldman 7d24c3c285 wioterminal: fix check for bad Wifi connect
rpc_wifi_connect() will return 0 on successful connection, so check for
that.  Also, check that if passphrase is given, that it's the minimum 8
chars per WPA Wifi.
2024-01-11 12:43:50 +01:00
Scott Feldman 5c0f0480bc fix wifinina UDP send
Fix an error I introduced in porting wifinina to netdev.  The driver was
starting a client on the socket once, during Connect.  The first UDP
send on the socket would succeed, any subsequent sends would fail.  The
fix is to start the client on the socket for each UDP send.

I think I see the logic in this design, so the fix makes sense.  If the
device was sending to many UDP clients, it could use a single socket,
but change the dst addr for each send.  The pkt data would be queued to
hw just once, and then sent from hw to each client dst addr.  This would
be a real efficient way to multicast to many clients.
2024-01-10 17:40:25 +01:00
Scott Feldman 8642886f73 correct netdever Accept() prototype
According to man page accept(2), accept returns new client sockfd and
remote peer ip:port.  This patch corrects the Accept() prototype in the
netdever interface to not take in an ip:port arg, but rather return an
ip:port for remote peer.

Tested with examples/net/tcpecho on wioterminal and nano-rp2040.  Here's
a run with wioterminal:

SERVER
============
sfeldma@nuc:~/work/drivers$ tinygo flash -monitor -target wioterminal -size short -stack-size=8kb ./examples/net/tcpecho
code    data     bss |   flash     ram
110876    2552   11212 |  113428   13764
Connected to /dev/ttyACM2. Press Ctrl-C to exit.

Realtek rtl8720dn Wifi network device driver (rtl8720dn)

Driver version           : 0.0.1
RTL8720 firmware version : 2.1.2
MAC address              : 2c:f7:f1:1c:9b:2f

Connecting to Wifi SSID 'test'...CONNECTED

DHCP-assigned IP         : 10.0.0.140
DHCP-assigned subnet     : 255.255.255.0
DHCP-assigned gateway    : 10.0.0.1

Starting TCP server listening on :8080
Client 10.0.0.190:50000 connected
Client 10.0.0.190:50000 closed

CLIENT
=============
nc -p 50000 10.0.0.140 8080
2023-12-19 09:33:54 +01:00
Simon Dismo 1a96fc4547 Add more line speeds to mcp2515.go (#626)
mcp2515: adding the possibility to configure lower line speeds on the CAN bus.
2023-12-11 20:40:36 +01:00
37 changed files with 816 additions and 145 deletions
+83
View File
@@ -1,3 +1,86 @@
0.27.0
---
- **core**
- prepare for CGo changes in TinyGo
- **new devices**
- **adafruit4650**
- support for Adafruit 4650 feather OLED
- **net**
- new networking support based on tinygo net package
- **pixel**
- add package for efficiently working with raw pixel buffers
- **rotary**
- Adding driver for rotary encoder support
- **seesaw**
- Adding support for Adafruit Seesaw platform
- **sgp30**
- add SGP30 air quality sensor
- **sk6812**
- added support for SK6812 to WS2812 device (#610)
- **enhancements**
- **epd2in13**
- add Sleep method like other displays
- unify rotation configuration with other displays
- use better black/white approximation
- **ili9341**
- add DrawBitmap method
- **lora/lorawan**
- LoRa WAN US915 Support
- LoRa WAN add setter functions
- refactor shared functionality for channels/regions
- **mcp2515**
- Add more line speeds to mcp2515.go (#626)
- **rtl8720dn**
- use drivers package version as the driver version
- **ssd1306**
- improvements needed for Thumby SPI display
- **st7735**
- make the display generic over RGB565 and RGB444
- **st7789**
- add DrawBitmap method
- make the display generic over RGB565 and RGB444
- **wifinina**
- add ResetIsHigh cfg switch for MKR 1010 (copied from #561)
- maintenence. Also see PR #4085 in the main TinyGo repo
- use drivers package version as the driver version
- **bugfixes**
- **adxl345**
- Use int16 for ADXL345 readings (#656)
- **at24cx**
- fixed the description of the device struct
- **rtl8720dn**
- allow connecting to open wifi access points
- fix check for bad Wifi connect
- **sh1106**
- fix I2C interface and add smoketest
- fixed the description of the device struct
- **wifinina**
- add 'unknown failure' reason code for AP connect
- fix concurrency issues with multiple sockets
- fix wifinina UDP send
- **examples**
- **ds3231**
- fix the description in the example
- **lorawan**
- add missing functions for simulated interface
- modify atcmd and basic demo to support choosing any one of the supported regions at compile time by using ldflags
- **net**
- all networking examples now using netdev and netlink.
- **build**
- **all**
- fix broken testrunner
- migrated legacy I2C
- add natiu package for tests
- **smoketest**
- add stack-size param for net tests.
- allow stack-size flag as it is needed for net examples
0.26.0 0.26.0
--- ---
- **core** - **core**
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2018-2023 The TinyGo Authors. All rights reserved. Copyright (c) 2018-2024 The TinyGo Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are modification, are permitted provided that the following conditions are
+1 -1
View File
@@ -3,7 +3,7 @@
[![PkgGoDev](https://pkg.go.dev/badge/tinygo.org/x/drivers)](https://pkg.go.dev/tinygo.org/x/drivers) [![Build](https://github.com/tinygo-org/drivers/actions/workflows/build.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/drivers/actions/workflows/build.yml) [![PkgGoDev](https://pkg.go.dev/badge/tinygo.org/x/drivers)](https://pkg.go.dev/tinygo.org/x/drivers) [![Build](https://github.com/tinygo-org/drivers/actions/workflows/build.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/drivers/actions/workflows/build.yml)
This package provides a collection of 101 different hardware drivers for devices such as sensors and displays that can be used together with [TinyGo](https://tinygo.org). This package provides a collection of 102 different hardware drivers for devices such as sensors and displays that can be used together with [TinyGo](https://tinygo.org).
For the complete list, please see: For the complete list, please see:
https://tinygo.org/docs/reference/devices/ https://tinygo.org/docs/reference/devices/
+7 -7
View File
@@ -95,16 +95,16 @@ func (d *Device) Restart() {
func (d *Device) ReadAcceleration() (x int32, y int32, z int32, err error) { func (d *Device) ReadAcceleration() (x int32, y int32, z int32, err error) {
rx, ry, rz := d.ReadRawAcceleration() rx, ry, rz := d.ReadRawAcceleration()
x = d.dataFormat.convertToIS(rx) x = int32(d.dataFormat.convertToIS(rx))
y = d.dataFormat.convertToIS(ry) y = int32(d.dataFormat.convertToIS(ry))
z = d.dataFormat.convertToIS(rz) z = int32(d.dataFormat.convertToIS(rz))
return return
} }
// ReadRawAcceleration reads the sensor values and returns the raw x, y and z axis // ReadRawAcceleration reads the sensor values and returns the raw x, y and z axis
// from the adxl345. // from the adxl345.
func (d *Device) ReadRawAcceleration() (x int32, y int32, z int32) { func (d *Device) ReadRawAcceleration() (x int16, y int16, z int16) {
data := []byte{0, 0, 0, 0, 0, 0} data := []byte{0, 0, 0, 0, 0, 0}
legacy.ReadRegister(d.bus, uint8(d.Address), REG_DATAX0, data) legacy.ReadRegister(d.bus, uint8(d.Address), REG_DATAX0, data)
@@ -140,7 +140,7 @@ func (d *Device) SetRange(sensorRange Range) bool {
} }
// convertToIS adjusts the raw values from the adxl345 with the range configuration // convertToIS adjusts the raw values from the adxl345 with the range configuration
func (d *dataFormat) convertToIS(rawValue int32) int32 { func (d *dataFormat) convertToIS(rawValue int16) int16 {
switch d.sensorRange { switch d.sensorRange {
case RANGE_2G: case RANGE_2G:
return rawValue * 4 // rawValue * 2 * 1000 / 512 return rawValue * 4 // rawValue * 2 * 1000 / 512
@@ -190,6 +190,6 @@ func (b *bwRate) toByte() (bits uint8) {
} }
// readInt converts two bytes to int16 // readInt converts two bytes to int16
func readIntLE(msb byte, lsb byte) int32 { func readIntLE(msb byte, lsb byte) int16 {
return int32(uint16(msb) | uint16(lsb)<<8) return int16(uint16(msb) | uint16(lsb)<<8)
} }
+34
View File
@@ -0,0 +1,34 @@
package encoders
type QuadratureDevice struct {
cfg QuadratureConfig
impl quadratureImpl
}
type QuadratureConfig struct {
Precision int
}
type quadratureImpl interface {
configure(cfg QuadratureConfig) error
readValue() int
writeValue(int)
}
func (enc *QuadratureDevice) Configure(cfg QuadratureConfig) error {
if cfg.Precision < 1 {
cfg.Precision = 4
}
enc.cfg = cfg
return enc.impl.configure(cfg)
}
// Position returns the stored int value for the encoder
func (enc *QuadratureDevice) Position() int {
return enc.impl.readValue() / enc.cfg.Precision
}
// SetPosition overwrites the currently stored value with the specified int value
func (enc *QuadratureDevice) SetPosition(v int) {
enc.impl.writeValue(v * enc.cfg.Precision)
}
+69
View File
@@ -0,0 +1,69 @@
//go:build tinygo && (rp2040 || stm32 || k210 || esp32c3 || nrf || (avr && (atmega328p || atmega328pb)))
// Implementation based on:
// https://gist.github.com/aykevl/3fc1683ed77bb0a9c07559dfe857304a
// Note: build constraints in this file list targets that define machine.PinToggle.
// If this is supported for additional targets in the future, they can be added above.
package encoders
import (
"machine"
"runtime/volatile"
)
var (
states = []int8{0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0}
)
// NewQuadratureViaInterrupt returns a rotary encoder device that uses GPIO
// interrupts and a lookup table to keep track of quadrature state changes.
//
// This constructur is only available for TinyGo targets for which machine.PinToggle
// is defined as a valid interrupt type.
func NewQuadratureViaInterrupt(pinA, pinB machine.Pin) *QuadratureDevice {
return &QuadratureDevice{impl: &quadInterruptImpl{pinA: pinA, pinB: pinB, oldAB: 0b00000011}}
}
type quadInterruptImpl struct {
pinA machine.Pin
pinB machine.Pin
// precision int
oldAB int
value volatile.Register32
}
func (enc *quadInterruptImpl) configure(cfg QuadratureConfig) error {
enc.pinA.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
enc.pinA.SetInterrupt(machine.PinToggle, enc.interrupt)
enc.pinB.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
enc.pinB.SetInterrupt(machine.PinToggle, enc.interrupt)
return nil
}
func (enc *quadInterruptImpl) interrupt(pin machine.Pin) {
aHigh, bHigh := enc.pinA.Get(), enc.pinB.Get()
enc.oldAB <<= 2
if aHigh {
enc.oldAB |= 1 << 1
}
if bHigh {
enc.oldAB |= 1
}
enc.writeValue(enc.readValue() + int(states[enc.oldAB&0x0f]))
}
// readValue gets the value using volatile operations and returns it as an int
func (enc *quadInterruptImpl) readValue() int {
return int(enc.value.Get())
}
// writeValue set the value to the specified int using volatile operations
func (enc *quadInterruptImpl) writeValue(v int) {
enc.value.Set(uint32(v))
}
+2 -2
View File
@@ -218,8 +218,8 @@ func (d *Device) Listen(sockfd int, backlog int) error {
return nil return nil
} }
func (d *Device) Accept(sockfd int, ip netip.AddrPort) (int, error) { func (d *Device) Accept(sockfd int) (int, netip.AddrPort, error) {
return -1, netdev.ErrNotSupported return -1, netip.AddrPort{}, netdev.ErrNotSupported
} }
func (d *Device) sendChunk(sockfd int, buf []byte, deadline time.Time) (int, error) { func (d *Device) sendChunk(sockfd int, buf []byte, deadline time.Time) (int, error) {
@@ -0,0 +1,28 @@
//go:build macropad_rp2040
package main
import (
"machine"
"tinygo.org/x/drivers/encoders"
)
var (
enc = encoders.NewQuadratureViaInterrupt(machine.ROT_A, machine.ROT_B)
)
func main() {
enc.Configure(encoders.QuadratureConfig{
Precision: 4,
})
for oldValue := 0; ; {
if newValue := enc.Position(); newValue != oldValue {
println("value: ", newValue)
oldValue = newValue
}
}
}
+1 -1
View File
@@ -9,7 +9,7 @@
// examples/net/webclient (for HTTP) // examples/net/webclient (for HTTP)
// examples/net/tlsclient (for HTTPS) // examples/net/tlsclient (for HTTPS)
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal //go:build ninafw || wioterminal
package main package main
+1 -1
View File
@@ -9,7 +9,7 @@
// examples/net/webclient (for HTTP) // examples/net/webclient (for HTTP)
// examples/net/tlsclient (for HTTPS) // examples/net/tlsclient (for HTTPS)
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal //go:build ninafw || wioterminal
package main package main
+1 -1
View File
@@ -9,7 +9,7 @@
// examples/net/webclient (for HTTP) // examples/net/webclient (for HTTP)
// examples/net/tlsclient (for HTTPS) // examples/net/tlsclient (for HTTPS)
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal //go:build ninafw || wioterminal
package main package main
+1 -1
View File
@@ -9,7 +9,7 @@
// examples/net/webclient (for HTTP) // examples/net/webclient (for HTTP)
// examples/net/tlsclient (for HTTPS) // examples/net/tlsclient (for HTTPS)
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal //go:build ninafw || wioterminal
package main package main
+1 -1
View File
@@ -4,7 +4,7 @@
// Note: It may be necessary to increase the stack size when using // Note: It may be necessary to increase the stack size when using
// paho.mqtt.golang. Use the -stack-size=4KB command line option. // paho.mqtt.golang. Use the -stack-size=4KB command line option.
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040 //go:build ninafw || wioterminal || challenger_rp2040
package main package main
+1 -1
View File
@@ -4,7 +4,7 @@
// Note: It may be necessary to increase the stack size when using // Note: It may be necessary to increase the stack size when using
// paho.mqtt.golang. Use the -stack-size=4KB command line option. // paho.mqtt.golang. Use the -stack-size=4KB command line option.
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040 //go:build ninafw || wioterminal || challenger_rp2040
package main package main
+1 -1
View File
@@ -3,7 +3,7 @@
// It creates a UDP connection to request the current time and parse the // It creates a UDP connection to request the current time and parse the
// response from a NTP server. The system time is set to NTP time. // response from a NTP server. The system time is set to NTP time.
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040 //go:build ninafw || wioterminal || challenger_rp2040
package main package main
+30
View File
@@ -0,0 +1,30 @@
//go:build ninafw || wioterminal
package main
import (
"log"
"time"
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
)
var (
ssid string
pass string
)
func init() {
time.Sleep(2 * time.Second)
link, _ := probe.Probe()
err := link.NetConnect(&netlink.ConnectParams{
Ssid: ssid,
Passphrase: pass,
})
if err != nil {
log.Fatal(err)
}
}
+120
View File
@@ -0,0 +1,120 @@
// This example is the classic snake network test. The snake is feed a steady
// diet of pkts and the pkts work themselves thru the snake segments and exit
// the tail. Each snake segment is a TCP socket connection to a server. The
// server echos pkts received back to the snake, and serves each segment on a
// different port. (See server/main.go for server).
//
// snake | server
// |
// head ----->---|-->--+
// seg a | |
// +---<-|--<--+
// | |
// +-->--|-->--+
// seg b | |
// +---<-|--<--+
// | |
// +-->--|-->--+
// seg c | |
// +---<-|--<--+
// | |
// +-->--|-->--+
// ... | |
// +---<-|--<--+
// | |
// +-->--|-->--+
// seg n | |
// tail -------<-|--<--+
// |
// The snake segments are linked by channels and each segment is run as a go
// func. This forces segments to connect and run concurrently, which is a good
// test of the underlying driver's ability to handle concurrent connections.
//go:build ninafw || wioterminal
package main
import (
_ "embed"
"fmt"
"log"
"net"
"strings"
"time"
)
//go:embed main.go
var code string
var (
server string = "10.0.0.100:8080"
)
func segment(in chan []byte, out chan []byte) {
var buf [512]byte
for {
c, err := net.Dial("tcp", server)
for ; err != nil; c, err = net.Dial("tcp", server) {
println(err.Error())
time.Sleep(5 * time.Second)
}
for {
select {
case msg := <-in:
_, err := c.Write(msg)
if err != nil {
log.Fatal(err.Error())
}
time.Sleep(100 * time.Millisecond)
n, err := c.Read(buf[:])
if err != nil {
log.Fatal(err.Error())
}
out <- buf[:n]
}
}
}
}
func feedit(head chan []byte) {
for i := 0; i < 100; i++ {
head <- []byte(fmt.Sprintf("\n---%d---\n", i))
for _, line := range strings.Split(code, "\n") {
if len(line) == 0 {
line = " "
}
head <- []byte(line)
}
}
}
var head = make(chan []byte)
var a = make(chan []byte)
var b = make(chan []byte)
var c = make(chan []byte)
var d = make(chan []byte)
var e = make(chan []byte)
var f = make(chan []byte)
var tail = make(chan []byte)
func main() {
// The snake
go segment(head, a)
go segment(a, b)
go segment(b, c)
go segment(c, d)
go segment(d, e)
go segment(e, f)
go segment(f, tail)
go feedit(head)
for {
select {
case msg := <-tail:
println(string(msg))
}
}
}
+34
View File
@@ -0,0 +1,34 @@
package main
import (
"io"
"log"
"net"
)
func main() {
// Listen for connections
l, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatal(err.Error())
}
defer l.Close()
println("Listening on port", ":8080")
for {
// Wait for a connection
conn, err := l.Accept()
if err != nil {
log.Fatal(err)
}
println("Accepted connection from", conn.RemoteAddr().String())
// Service the new connection in a goroutine.
// The loop then returns to accepting, so that
// multiple connections may be served concurrently
go func(c net.Conn) {
// Echo all incoming data
io.Copy(c, c)
// Shut down the connection
c.Close()
}(conn)
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
// //
// nc -lk 8080 // nc -lk 8080
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040 //go:build ninafw || wioterminal || challenger_rp2040
package main package main
+1 -1
View File
@@ -5,7 +5,7 @@
// //
// nc -lk 8080 // nc -lk 8080
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040 || pico //go:build ninafw || wioterminal || challenger_rp2040 || pico
package main package main
+5 -2
View File
@@ -7,7 +7,7 @@
// //
// $ nc 10.0.0.2 8080 <file >copy ; cmp file copy // $ nc 10.0.0.2 8080 <file >copy ; cmp file copy
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal //go:build ninafw || wioterminal
package main package main
@@ -30,16 +30,18 @@ var (
var buf [1024]byte var buf [1024]byte
func echo(conn net.Conn) { func echo(conn net.Conn) {
println("Client", conn.RemoteAddr(), "connected")
defer conn.Close() defer conn.Close()
_, err := io.CopyBuffer(conn, conn, buf[:]) _, err := io.CopyBuffer(conn, conn, buf[:])
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
log.Fatal(err.Error()) log.Fatal(err.Error())
} }
println("Client", conn.RemoteAddr(), "closed")
} }
func main() { func main() {
time.Sleep(time.Second) time.Sleep(2 * time.Second)
link, _ := probe.Probe() link, _ := probe.Probe()
@@ -51,6 +53,7 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
println("Starting TCP server listening on", port)
l, err := net.Listen("tcp", port) l, err := net.Listen("tcp", port)
if err != nil { if err != nil {
log.Fatal(err.Error()) log.Fatal(err.Error())
+1 -1
View File
@@ -5,7 +5,7 @@
// //
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal //go:build ninafw || wioterminal
package main package main
+1 -1
View File
@@ -17,7 +17,7 @@
// } // }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal //go:build ninafw || wioterminal
package main package main
+1 -1
View File
@@ -6,7 +6,7 @@
// Note: It may be necessary to increase the stack size when using "net/http". // Note: It may be necessary to increase the stack size when using "net/http".
// Use the -stack-size=4KB command line option. // Use the -stack-size=4KB command line option.
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal //go:build ninafw || wioterminal
package main package main
+1 -1
View File
@@ -6,7 +6,7 @@
// Note: It may be necessary to increase the stack size when using // Note: It may be necessary to increase the stack size when using
// "golang.org/x/net/websocket". Use the -stack-size=4KB command line option. // "golang.org/x/net/websocket". Use the -stack-size=4KB command line option.
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal //go:build ninafw || wioterminal
package main package main
+1 -1
View File
@@ -6,7 +6,7 @@
// Note: It may be necessary to increase the stack size when using // Note: It may be necessary to increase the stack size when using
// "golang.org/x/net/websocket". Use the -stack-size=4KB command line option. // "golang.org/x/net/websocket". Use the -stack-size=4KB command line option.
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal //go:build ninafw || wioterminal
package main package main
+1 -1
View File
@@ -3,7 +3,7 @@
// Note: It may be necessary to increase the stack size when using "net/http". // Note: It may be necessary to increase the stack size when using "net/http".
// Use the -stack-size=4KB command line option. // Use the -stack-size=4KB command line option.
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal //go:build ninafw || wioterminal
package main package main
+50
View File
@@ -0,0 +1,50 @@
// This example using the SSD1306 OLED display over SPI on the Thumby board
// A very tiny 72x40 display.
package main
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers/ssd1306"
)
func main() {
machine.SPI0.Configure(machine.SPIConfig{})
display := ssd1306.NewSPI(machine.SPI0, machine.THUMBY_DC_PIN, machine.THUMBY_RESET_PIN, machine.THUMBY_CS_PIN)
display.Configure(ssd1306.Config{
Width: 72,
Height: 40,
ResetCol: ssd1306.ResetValue{28, 99},
ResetPage: ssd1306.ResetValue{0, 5},
})
display.ClearDisplay()
x := int16(36)
y := int16(20)
deltaX := int16(1)
deltaY := int16(1)
for {
pixel := display.GetPixel(x, y)
c := color.RGBA{255, 255, 255, 255}
if pixel {
c = color.RGBA{0, 0, 0, 255}
}
display.SetPixel(x, y, c)
display.Display()
x += deltaX
y += deltaY
if x == 0 || x == 71 {
deltaX = -deltaX
}
if y == 0 || y == 39 {
deltaY = -deltaY
}
time.Sleep(1 * time.Millisecond)
}
}
+112 -1
View File
@@ -236,16 +236,83 @@ func (d *Device) getMode() (byte, error) {
} }
func (d *Device) configRate(speed, clock byte) error { func (d *Device) configRate(speed, clock byte) error {
// TODO: add another baudrate
var cfg1, cfg2, cfg3 byte var cfg1, cfg2, cfg3 byte
set := true set := true
switch clock { switch clock {
case Clock16MHz: case Clock16MHz:
switch speed { switch speed {
case CAN5kBps:
cfg1 = mcp16mHz5kBpsCfg1
cfg2 = mcp16mHz5kBpsCfg2
cfg3 = mcp16mHz5kBpsCfg3
case CAN10kBps:
cfg1 = mcp16mHz10kBpsCfg1
cfg2 = mcp16mHz10kBpsCfg2
cfg3 = mcp16mHz10kBpsCfg3
case CAN20kBps:
cfg1 = mcp16mHz20kBpsCfg1
cfg2 = mcp16mHz20kBpsCfg2
cfg3 = mcp16mHz20kBpsCfg3
case CAN25kBps:
cfg1 = mcp16mHz25kBpsCfg1
cfg2 = mcp16mHz25kBpsCfg2
cfg3 = mcp16mHz25kBpsCfg3
case CAN31k25Bps:
cfg1 = mcp16mHz31k25BpsCfg1
cfg2 = mcp16mHz31k25BpsCfg2
cfg3 = mcp16mHz31k25BpsCfg3
case CAN33kBps:
cfg1 = mcp16mHz33kBpsCfg1
cfg2 = mcp16mHz33kBpsCfg2
cfg3 = mcp16mHz33kBpsCfg3
case CAN40kBps:
cfg1 = mcp16mHz40kBpsCfg1
cfg2 = mcp16mHz40kBpsCfg2
cfg3 = mcp16mHz40kBpsCfg3
case CAN47kBps:
cfg1 = mcp16mHz47kBpsCfg1
cfg2 = mcp16mHz47kBpsCfg2
cfg3 = mcp16mHz47kBpsCfg3
case CAN50kBps:
cfg1 = mcp16mHz50kBpsCfg1
cfg2 = mcp16mHz50kBpsCfg2
cfg3 = mcp16mHz50kBpsCfg3
case CAN80kBps:
cfg1 = mcp16mHz80kBpsCfg1
cfg2 = mcp16mHz80kBpsCfg2
cfg3 = mcp16mHz80kBpsCfg3
case CAN83k3Bps:
cfg1 = mcp16mHz83k3BpsCfg1
cfg2 = mcp16mHz83k3BpsCfg2
cfg3 = mcp16mHz83k3BpsCfg3
case CAN95kBps:
cfg1 = mcp16mHz95kBpsCfg1
cfg2 = mcp16mHz95kBpsCfg2
cfg3 = mcp16mHz95kBpsCfg3
case CAN100kBps:
cfg1 = mcp16mHz100kBpsCfg1
cfg2 = mcp16mHz100kBpsCfg2
cfg3 = mcp16mHz100kBpsCfg3
case CAN125kBps:
cfg1 = mcp16mHz125kBpsCfg1
cfg2 = mcp16mHz125kBpsCfg2
cfg3 = mcp16mHz125kBpsCfg3
case CAN200kBps:
cfg1 = mcp16mHz200kBpsCfg1
cfg2 = mcp16mHz200kBpsCfg2
cfg3 = mcp16mHz200kBpsCfg3
case CAN250kBps:
cfg1 = mcp16mHz250kBpsCfg1
cfg2 = mcp16mHz250kBpsCfg2
cfg3 = mcp16mHz250kBpsCfg3
case CAN500kBps: case CAN500kBps:
cfg1 = mcp16mHz500kBpsCfg1 cfg1 = mcp16mHz500kBpsCfg1
cfg2 = mcp16mHz500kBpsCfg2 cfg2 = mcp16mHz500kBpsCfg2
cfg3 = mcp16mHz500kBpsCfg3 cfg3 = mcp16mHz500kBpsCfg3
case CAN666kBps:
cfg1 = mcp16mHz666kBpsCfg1
cfg2 = mcp16mHz666kBpsCfg2
cfg3 = mcp16mHz666kBpsCfg3
case CAN1000kBps: case CAN1000kBps:
cfg1 = mcp16mHz1000kBpsCfg1 cfg1 = mcp16mHz1000kBpsCfg1
cfg2 = mcp16mHz1000kBpsCfg2 cfg2 = mcp16mHz1000kBpsCfg2
@@ -255,6 +322,50 @@ func (d *Device) configRate(speed, clock byte) error {
} }
case Clock8MHz: case Clock8MHz:
switch speed { switch speed {
case CAN5kBps:
cfg1 = mcp8mHz5kBpsCfg1
cfg2 = mcp8mHz5kBpsCfg2
cfg3 = mcp8mHz5kBpsCfg3
case CAN10kBps:
cfg1 = mcp8mHz10kBpsCfg1
cfg2 = mcp8mHz10kBpsCfg2
cfg3 = mcp8mHz10kBpsCfg3
case CAN20kBps:
cfg1 = mcp8mHz20kBpsCfg1
cfg2 = mcp8mHz20kBpsCfg2
cfg3 = mcp8mHz20kBpsCfg3
case CAN31k25Bps:
cfg1 = mcp8mHz31k25BpsCfg1
cfg2 = mcp8mHz31k25BpsCfg2
cfg3 = mcp8mHz31k25BpsCfg3
case CAN40kBps:
cfg1 = mcp8mHz40kBpsCfg1
cfg2 = mcp8mHz40kBpsCfg2
cfg3 = mcp8mHz40kBpsCfg3
case CAN50kBps:
cfg1 = mcp8mHz50kBpsCfg1
cfg2 = mcp8mHz50kBpsCfg2
cfg3 = mcp8mHz50kBpsCfg3
case CAN80kBps:
cfg1 = mcp8mHz80kBpsCfg1
cfg2 = mcp8mHz80kBpsCfg2
cfg3 = mcp8mHz80kBpsCfg3
case CAN100kBps:
cfg1 = mcp8mHz100kBpsCfg1
cfg2 = mcp8mHz100kBpsCfg2
cfg3 = mcp8mHz100kBpsCfg3
case CAN125kBps:
cfg1 = mcp8mHz125kBpsCfg1
cfg2 = mcp8mHz125kBpsCfg2
cfg3 = mcp8mHz125kBpsCfg3
case CAN200kBps:
cfg1 = mcp8mHz200kBpsCfg1
cfg2 = mcp8mHz200kBpsCfg2
cfg3 = mcp8mHz200kBpsCfg3
case CAN250kBps:
cfg1 = mcp8mHz250kBpsCfg1
cfg2 = mcp8mHz250kBpsCfg2
cfg3 = mcp8mHz250kBpsCfg3
case CAN500kBps: case CAN500kBps:
cfg1 = mcp8mHz500kBpsCfg1 cfg1 = mcp8mHz500kBpsCfg1
cfg2 = mcp8mHz500kBpsCfg2 cfg2 = mcp8mHz500kBpsCfg2
+2 -1
View File
@@ -39,6 +39,7 @@ var (
ErrNoMoreSockets = errors.New("No more sockets") ErrNoMoreSockets = errors.New("No more sockets")
ErrClosingSocket = errors.New("Error closing socket") ErrClosingSocket = errors.New("Error closing socket")
ErrNotSupported = errors.New("Not supported") ErrNotSupported = errors.New("Not supported")
ErrInvalidSocketFd = errors.New("Invalid socket fd")
) )
// Duplicate of non-exported net.errTimeout // Duplicate of non-exported net.errTimeout
@@ -82,7 +83,7 @@ type Netdever interface {
Bind(sockfd int, ip netip.AddrPort) error Bind(sockfd int, ip netip.AddrPort) error
Connect(sockfd int, host string, ip netip.AddrPort) error Connect(sockfd int, host string, ip netip.AddrPort) error
Listen(sockfd int, backlog int) error Listen(sockfd int, backlog int) error
Accept(sockfd int, ip netip.AddrPort) (int, error) Accept(sockfd int) (int, netip.AddrPort, error)
Send(sockfd int, buf []byte, flags int, deadline time.Time) (int, error) Send(sockfd int, buf []byte, flags int, deadline time.Time) (int, error)
Recv(sockfd int, buf []byte, flags int, deadline time.Time) (int, error) Recv(sockfd int, buf []byte, flags int, deadline time.Time) (int, error)
Close(sockfd int) error Close(sockfd int) error
+1
View File
@@ -13,6 +13,7 @@ var (
ErrConnectFailed = errors.New("Connect failed") ErrConnectFailed = errors.New("Connect failed")
ErrConnectTimeout = errors.New("Connect timed out") ErrConnectTimeout = errors.New("Connect timed out")
ErrMissingSSID = errors.New("Missing WiFi SSID") ErrMissingSSID = errors.New("Missing WiFi SSID")
ErrShortPassphrase = errors.New("Invalid Wifi Passphrase < 8 chars")
ErrAuthFailure = errors.New("Wifi authentication failure") ErrAuthFailure = errors.New("Wifi authentication failure")
ErrAuthTypeNoGood = errors.New("Wifi authorization type not supported") ErrAuthTypeNoGood = errors.New("Wifi authorization type not supported")
ErrConnectModeNoGood = errors.New("Connect mode not supported") ErrConnectModeNoGood = errors.New("Connect mode not supported")
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || matrixportal_m4 //go:build ninafw && !arduino_mkrwifi1010
package probe package probe
+32 -10
View File
@@ -17,6 +17,7 @@ import (
"sync" "sync"
"time" "time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/netdev"
"tinygo.org/x/drivers/netlink" "tinygo.org/x/drivers/netlink"
) )
@@ -27,7 +28,6 @@ var _debug debug = debugBasic
//var _debug debug = debugBasic | debugNetdev | debugRpc //var _debug debug = debugBasic | debugNetdev | debugRpc
var ( var (
version = "0.0.1"
driverName = "Realtek rtl8720dn Wifi network device driver (rtl8720dn)" driverName = "Realtek rtl8720dn Wifi network device driver (rtl8720dn)"
) )
@@ -102,14 +102,22 @@ func (r *rtl8720dn) connectToAP() error {
return netlink.ErrMissingSSID return netlink.ErrMissingSSID
} }
if len(r.params.Passphrase) != 0 && len(r.params.Passphrase) < 8 {
return netlink.ErrShortPassphrase
}
if debugging(debugBasic) { if debugging(debugBasic) {
fmt.Printf("Connecting to Wifi SSID '%s'...", r.params.Ssid) fmt.Printf("Connecting to Wifi SSID '%s'...", r.params.Ssid)
} }
// Start the connection process // Start the connection process
securityType := uint32(0x00400004) securityType := uint32(0) // RTW_SECURITY_OPEN
if len(r.params.Passphrase) != 0 {
securityType = 0x00400004 // RTW_SECURITY_WPA2_AES_PSK
}
result := r.rpc_wifi_connect(r.params.Ssid, r.params.Passphrase, securityType, -1, 0) result := r.rpc_wifi_connect(r.params.Ssid, r.params.Passphrase, securityType, -1, 0)
if result == -1 { if result != 0 {
if debugging(debugBasic) { if debugging(debugBasic) {
fmt.Printf("FAILED\r\n") fmt.Printf("FAILED\r\n")
} }
@@ -134,7 +142,7 @@ func (r *rtl8720dn) showDriver() {
if debugging(debugBasic) { if debugging(debugBasic) {
fmt.Printf("\r\n") fmt.Printf("\r\n")
fmt.Printf("%s\r\n\r\n", driverName) fmt.Printf("%s\r\n\r\n", driverName)
fmt.Printf("Driver version : %s\r\n", version) fmt.Printf("Driver version : %s\r\n", drivers.Version)
} }
r.driverShown = true r.driverShown = true
} }
@@ -437,6 +445,12 @@ func ipToName(ip netip.AddrPort) []byte {
return name return name
} }
func nameToIp(name []byte) netip.AddrPort {
port := uint16(name[2])<<8 | uint16(name[3])
addr, _ := netip.AddrFromSlice(name[4:8])
return netip.AddrPortFrom(addr, port)
}
func (r *rtl8720dn) Bind(sockfd int, ip netip.AddrPort) error { func (r *rtl8720dn) Bind(sockfd int, ip netip.AddrPort) error {
if debugging(debugNetdev) { if debugging(debugNetdev) {
@@ -534,10 +548,10 @@ func (r *rtl8720dn) Listen(sockfd int, backlog int) error {
return nil return nil
} }
func (r *rtl8720dn) Accept(sockfd int, ip netip.AddrPort) (int, error) { func (r *rtl8720dn) Accept(sockfd int) (int, netip.AddrPort, error) {
if debugging(debugNetdev) { if debugging(debugNetdev) {
fmt.Printf("[Accept] sockfd: %d, peer: %s\r\n", sockfd, ip) fmt.Printf("[Accept] sockfd: %d\r\n", sockfd)
} }
r.mu.Lock() r.mu.Lock()
@@ -546,12 +560,12 @@ func (r *rtl8720dn) Accept(sockfd int, ip netip.AddrPort) (int, error) {
var newSock int32 var newSock int32
var lsock = sock(sockfd) var lsock = sock(sockfd)
var socket = r.sockets[lsock] var socket = r.sockets[lsock]
var name = ipToName(ip) var name = ipToName(netip.AddrPort{})
switch socket.protocol { switch socket.protocol {
case netdev.IPPROTO_TCP: case netdev.IPPROTO_TCP:
default: default:
return -1, netdev.ErrProtocolNotSupported return -1, netip.AddrPort{}, netdev.ErrProtocolNotSupported
} }
for { for {
@@ -570,6 +584,14 @@ func (r *rtl8720dn) Accept(sockfd int, ip netip.AddrPort) (int, error) {
continue continue
} }
// Get remote peer ip:port
namelen = uint32(len(name))
result := r.rpc_lwip_getpeername(int32(newSock), name, &namelen)
if result == -1 {
return -1, netip.AddrPort{}, fmt.Errorf("Getpeername failed")
}
raddr := nameToIp(name)
// If we've already seen this socket, we can re-use // If we've already seen this socket, we can re-use
// the socket and return it. But, only if the socket // the socket and return it. But, only if the socket
// is closed. If it's not closed, we'll just come back // is closed. If it's not closed, we'll just come back
@@ -582,12 +604,12 @@ func (r *rtl8720dn) Accept(sockfd int, ip netip.AddrPort) (int, error) {
continue continue
} }
// Reuse client socket // Reuse client socket
return int(newSock), nil return int(newSock), raddr, nil
} }
// Create new socket for client and return fd // Create new socket for client and return fd
r.sockets[sock(newSock)] = newSocket(socket.protocol) r.sockets[sock(newSock)] = newSocket(socket.protocol)
return int(newSock), nil return int(newSock), raddr, nil
} }
} }
+1
View File
@@ -129,6 +129,7 @@ tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ndir/ma
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/ndir/main_ndir.go tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/ndir/main_ndir.go
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mpu9150/main.go tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mpu9150/main.go
tinygo build -size short -o ./build/test.hex -target=macropad-rp2040 ./examples/sh1106/macropad_spi tinygo build -size short -o ./build/test.hex -target=macropad-rp2040 ./examples/sh1106/macropad_spi
tinygo build -size short -o ./build/test.hex -target=macropad-rp2040 ./examples/encoders/quadrature-interrupt
# network examples (espat) # network examples (espat)
tinygo build -size short -o ./build/test.hex -target=challenger-rp2040 ./examples/net/ntpclient/ tinygo build -size short -o ./build/test.hex -target=challenger-rp2040 ./examples/net/ntpclient/
# network examples (wifinina) # network examples (wifinina)
+26 -4
View File
@@ -13,6 +13,8 @@ import (
"tinygo.org/x/drivers/internal/legacy" "tinygo.org/x/drivers/internal/legacy"
) )
type ResetValue [2]byte
// Device wraps I2C or SPI connection. // Device wraps I2C or SPI connection.
type Device struct { type Device struct {
bus Buser bus Buser
@@ -22,6 +24,8 @@ type Device struct {
bufferSize int16 bufferSize int16
vccState VccMode vccState VccMode
canReset bool canReset bool
resetCol ResetValue
resetPage ResetValue
} }
// Config is the configuration for the display // Config is the configuration for the display
@@ -30,6 +34,13 @@ type Config struct {
Height int16 Height int16
VccState VccMode VccState VccMode
Address uint16 Address uint16
// ResetCol and ResetPage are used to reset the screen to 0x0
// This is useful for some screens that have a different size than 128x64
// For example, the Thumby's screen is 72x40
// The default values are normally set automatically based on the size.
// If you're using a different size, you might need to set these values manually.
ResetCol ResetValue
ResetPage ResetValue
} }
type I2CBus struct { type I2CBus struct {
@@ -79,6 +90,7 @@ func NewSPI(bus drivers.SPI, dcPin, resetPin, csPin machine.Pin) Device {
// Configure initializes the display with default configuration // Configure initializes the display with default configuration
func (d *Device) Configure(cfg Config) { func (d *Device) Configure(cfg Config) {
var zeroReset ResetValue
if cfg.Width != 0 { if cfg.Width != 0 {
d.width = cfg.Width d.width = cfg.Width
} else { } else {
@@ -97,6 +109,16 @@ func (d *Device) Configure(cfg Config) {
} else { } else {
d.vccState = SWITCHCAPVCC d.vccState = SWITCHCAPVCC
} }
if cfg.ResetCol != zeroReset {
d.resetCol = cfg.ResetCol
} else {
d.resetCol = ResetValue{0, uint8(d.width - 1)}
}
if cfg.ResetPage != zeroReset {
d.resetPage = cfg.ResetPage
} else {
d.resetPage = ResetValue{0, uint8(d.height/8) - 1}
}
d.bufferSize = d.width * d.height / 8 d.bufferSize = d.width * d.height / 8
d.buffer = make([]byte, d.bufferSize) d.buffer = make([]byte, d.bufferSize)
d.canReset = cfg.Address != 0 || d.width != 128 || d.height != 64 // I2C or not 128x64 d.canReset = cfg.Address != 0 || d.width != 128 || d.height != 64 // I2C or not 128x64
@@ -186,11 +208,11 @@ func (d *Device) Display() error {
// Since we're printing the whole buffer, avoid resetting it in this case // Since we're printing the whole buffer, avoid resetting it in this case
if d.canReset { if d.canReset {
d.Command(COLUMNADDR) d.Command(COLUMNADDR)
d.Command(0) d.Command(d.resetCol[0])
d.Command(uint8(d.width - 1)) d.Command(d.resetCol[1])
d.Command(PAGEADDR) d.Command(PAGEADDR)
d.Command(0) d.Command(d.resetPage[0])
d.Command(uint8(d.height/8) - 1) d.Command(d.resetPage[1])
} }
return d.Tx(d.buffer, false) return d.Tx(d.buffer, false)
+1 -1
View File
@@ -2,4 +2,4 @@ package drivers
// Version returns a user-readable string showing the version of the drivers package for support purposes. // Version returns a user-readable string showing the version of the drivers package for support purposes.
// Update this value before release of new version of software. // Update this value before release of new version of software.
const Version = "0.26.0" const Version = "0.27.0"
+161 -99
View File
@@ -33,7 +33,6 @@ var _debug debug = debugBasic
//var _debug debug = debugBasic | debugNetdev | debugCmd | debugDetail //var _debug debug = debugBasic | debugNetdev | debugCmd | debugDetail
var ( var (
version = "0.0.1"
driverName = "Tinygo ESP32 Wifi network device driver (WiFiNINA)" driverName = "Tinygo ESP32 Wifi network device driver (WiFiNINA)"
) )
@@ -163,10 +162,12 @@ type encryptionType uint8
type sock uint8 type sock uint8
type hwerr uint8 type hwerr uint8
type socket struct { type Socket struct {
protocol int protocol int
ip netip.AddrPort clientConnected bool
inuse bool laddr netip.AddrPort // Set in Bind()
raddr netip.AddrPort // Set in Connect()
sock // Device socket, as returned from w.getSocket()
} }
type Config struct { type Config struct {
@@ -213,17 +214,13 @@ type wifinina struct {
killWatchdog chan bool killWatchdog chan bool
fault error fault error
sockets map[sock]*socket // keyed by sock as returned by getSocket() sockets map[int]*Socket // keyed by sockfd
}
func newSocket(protocol int) *socket {
return &socket{protocol: protocol, inuse: true}
} }
func New(cfg *Config) *wifinina { func New(cfg *Config) *wifinina {
w := wifinina{ w := wifinina{
cfg: cfg, cfg: cfg,
sockets: make(map[sock]*socket), sockets: make(map[int]*Socket),
killWatchdog: make(chan bool), killWatchdog: make(chan bool),
cs: cfg.Cs, cs: cfg.Cs,
ack: cfg.Ack, ack: cfg.Ack,
@@ -313,7 +310,7 @@ func (w *wifinina) showDriver() {
if debugging(debugBasic) { if debugging(debugBasic) {
fmt.Printf("\r\n") fmt.Printf("\r\n")
fmt.Printf("%s\r\n\r\n", driverName) fmt.Printf("%s\r\n\r\n", driverName)
fmt.Printf("Driver version : %s\r\n", version) fmt.Printf("Driver version : %s\r\n", drivers.Version)
} }
w.driverShown = true w.driverShown = true
} }
@@ -504,6 +501,12 @@ func (w *wifinina) GetHostByName(name string) (netip.Addr, error) {
fmt.Printf("[GetHostByName] name: %s\r\n", name) 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() w.mu.Lock()
defer w.mu.Unlock() defer w.mu.Unlock()
@@ -546,6 +549,20 @@ func (w *wifinina) Addr() (netip.Addr, error) {
return ip, nil 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. // See man socket(2) for standard Berkely sockets for Socket, Bind, etc.
// The driver strives to meet the function and semantics of socket(2). // The driver strives to meet the function and semantics of socket(2).
@@ -573,37 +590,49 @@ func (w *wifinina) Socket(domain int, stype int, protocol int) (int, error) {
w.mu.Lock() w.mu.Lock()
defer w.mu.Unlock() defer w.mu.Unlock()
sock := w.getSocket() sockfd := w.newSockfd()
if sock == noSocketAvail { if sockfd == -1 {
return -1, netdev.ErrNoMoreSockets return -1, netdev.ErrNoMoreSockets
} }
socket := newSocket(protocol) w.sockets[sockfd] = &Socket{
w.sockets[sock] = socket protocol: protocol,
sock: noSocketAvail,
}
return int(sock), nil if debugging(debugNetdev) {
fmt.Printf("[Socket] <-- sockfd %d\r\n", sockfd)
}
return sockfd, nil
} }
func (w *wifinina) Bind(sockfd int, ip netip.AddrPort) error { func (w *wifinina) Bind(sockfd int, ip netip.AddrPort) error {
if debugging(debugNetdev) { if debugging(debugNetdev) {
fmt.Printf("[Bind] sockfd: %d, addr: %s\r\n", sockfd, ip) fmt.Printf("[Bind] sockfd: %d, addr: %s:%d\r\n", sockfd, ip.Addr(), ip.Port())
} }
w.mu.Lock() w.mu.Lock()
defer w.mu.Unlock() defer w.mu.Unlock()
var sock = sock(sockfd) socket, ok := w.sockets[sockfd]
var socket = w.sockets[sock] if !ok {
return netdev.ErrInvalidSocketFd
}
switch socket.protocol { switch socket.protocol {
case netdev.IPPROTO_TCP: case netdev.IPPROTO_TCP:
case netdev.IPPROTO_TLS: case netdev.IPPROTO_TLS:
case netdev.IPPROTO_UDP: case netdev.IPPROTO_UDP:
w.startServer(sock, ip.Port(), protoModeUDP) socket.sock = w.getSocket()
if socket.sock == noSocketAvail {
return netdev.ErrNoMoreSockets
}
w.startServer(socket.sock, ip.Port(), protoModeUDP)
} }
socket.ip = ip socket.laddr = ip
return nil return nil
} }
@@ -628,21 +657,40 @@ func (w *wifinina) Connect(sockfd int, host string, ip netip.AddrPort) error {
w.mu.Lock() w.mu.Lock()
defer w.mu.Unlock() defer w.mu.Unlock()
var sock = sock(sockfd) socket, ok := w.sockets[sockfd]
var socket = w.sockets[sock] if !ok {
return netdev.ErrInvalidSocketFd
}
// Start the connection // Start the connection
switch socket.protocol { switch socket.protocol {
case netdev.IPPROTO_TCP: case netdev.IPPROTO_TCP:
w.startClient(sock, "", toUint32(ip.Addr().As4()), ip.Port(), protoModeTCP) 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: case netdev.IPPROTO_TLS:
w.startClient(sock, host, 0, ip.Port(), protoModeTLS) socket.sock = w.getSocket()
if socket.sock == noSocketAvail {
return netdev.ErrNoMoreSockets
}
w.startClient(socket.sock, host, 0, ip.Port(), protoModeTLS)
case netdev.IPPROTO_UDP: case netdev.IPPROTO_UDP:
w.startClient(sock, "", toUint32(ip.Addr().As4()), ip.Port(), protoModeUDP) if socket.sock == noSocketAvail {
return fmt.Errorf("Must Bind before Connecting")
}
// See start in sendUDP()
socket.raddr = ip
socket.clientConnected = true
return nil return nil
} }
if w.getClientState(sock) == tcpStateEstablished { if w.getClientState(socket.sock) == tcpStateEstablished {
socket.clientConnected = true
return nil return nil
} }
@@ -662,12 +710,18 @@ func (w *wifinina) Listen(sockfd int, backlog int) error {
w.mu.Lock() w.mu.Lock()
defer w.mu.Unlock() defer w.mu.Unlock()
var sock = sock(sockfd) socket, ok := w.sockets[sockfd]
var socket = w.sockets[sock] if !ok {
return netdev.ErrInvalidSocketFd
}
switch socket.protocol { switch socket.protocol {
case netdev.IPPROTO_TCP: case netdev.IPPROTO_TCP:
w.startServer(sock, socket.ip.Port(), protoModeTCP) socket.sock = w.getSocket()
if socket.sock == noSocketAvail {
return netdev.ErrNoMoreSockets
}
w.startServer(socket.sock, socket.laddr.Port(), protoModeTCP)
case netdev.IPPROTO_UDP: case netdev.IPPROTO_UDP:
default: default:
return netdev.ErrProtocolNotSupported return netdev.ErrProtocolNotSupported
@@ -676,27 +730,29 @@ func (w *wifinina) Listen(sockfd int, backlog int) error {
return nil return nil
} }
func (w *wifinina) Accept(sockfd int, ip netip.AddrPort) (int, error) { func (w *wifinina) Accept(sockfd int) (int, netip.AddrPort, error) {
if debugging(debugNetdev) { if debugging(debugNetdev) {
fmt.Printf("[Accept] sockfd: %d, peer: %s\r\n", sockfd, ip) fmt.Printf("[Accept] sockfd: %d\r\n", sockfd)
} }
w.mu.Lock() w.mu.Lock()
defer w.mu.Unlock() defer w.mu.Unlock()
var client sock socket, ok := w.sockets[sockfd]
var sock = sock(sockfd) if !ok {
var socket = w.sockets[sock] return -1, netip.AddrPort{}, netdev.ErrInvalidSocketFd
}
switch socket.protocol { switch socket.protocol {
case netdev.IPPROTO_TCP: case netdev.IPPROTO_TCP:
default: default:
return -1, netdev.ErrProtocolNotSupported return -1, netip.AddrPort{}, netdev.ErrProtocolNotSupported
} }
skip:
for { for {
// Accept() will be sleeping most of the time, checking for a // Accept() will be sleeping most of the time, checking for
// new clients every 1/10 sec. // new clients every 1/10 sec.
w.mu.Unlock() w.mu.Unlock()
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
@@ -704,49 +760,46 @@ func (w *wifinina) Accept(sockfd int, ip netip.AddrPort) (int, error) {
// Check if we've faulted // Check if we've faulted
if w.fault != nil { if w.fault != nil {
return -1, w.fault return -1, netip.AddrPort{}, w.fault
} }
// TODO: BUG: Currently, a sock that is 100% busy will always be
// TODO: returned by w.accept(sock), starving other socks
// TODO: from begin serviced. Need to figure out how to
// TODO: service socks fairly (round-robin?) so no one sock
// TODO: can dominate.
// Check if a client has data // Check if a client has data
client = w.accept(sock) var client sock = w.accept(socket.sock)
if client == noSocketAvail { if client == noSocketAvail {
// None ready // None ready
continue continue
} }
// If we've already seen this socket, we can reuse // If we already have a socket for the client, skip
// the socket and return it. But, only if the socket for _, s := range w.sockets {
// is closed. If it's not closed, we'll just come back if s.sock == client {
// later to reuse it. continue skip
clientSocket, ok := w.sockets[client]
if ok {
// Wait for client to Close
if clientSocket.inuse {
continue
} }
// Reuse client socket
return int(client), nil
} }
// Create new socket for client and return fd // Otherwise, create a new socket
w.sockets[client] = newSocket(socket.protocol) clientfd := w.newSockfd()
return int(client), nil 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(sock sock) bool { func (w *wifinina) sockDown(socket *Socket) bool {
var socket = w.sockets[sock]
if socket.protocol == netdev.IPPROTO_UDP { if socket.protocol == netdev.IPPROTO_UDP {
return false return false
} }
return w.getClientState(sock) != tcpStateEstablished return w.getClientState(socket.sock) != tcpStateEstablished
} }
func (w *wifinina) sendTCP(sock sock, buf []byte, deadline time.Time) (int, error) { func (w *wifinina) sendTCP(sock sock, buf []byte, deadline time.Time) (int, error) {
@@ -774,7 +827,7 @@ func (w *wifinina) sendTCP(sock sock, buf []byte, deadline time.Time) (int, erro
} }
// Check if socket went down // Check if socket went down
if w.sockDown(sock) { if w.getClientState(sock) != tcpStateEstablished {
return -1, io.EOF return -1, io.EOF
} }
@@ -792,7 +845,10 @@ func (w *wifinina) sendTCP(sock sock, buf []byte, deadline time.Time) (int, erro
return -1, netdev.ErrTimeout return -1, netdev.ErrTimeout
} }
func (w *wifinina) sendUDP(sock sock, buf []byte, deadline time.Time) (int, error) { 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 // Queue it
ok := w.insertDataBuf(sock, buf) ok := w.insertDataBuf(sock, buf)
@@ -810,8 +866,10 @@ func (w *wifinina) sendUDP(sock sock, buf []byte, deadline time.Time) (int, erro
} }
func (w *wifinina) sendChunk(sockfd int, buf []byte, deadline time.Time) (int, error) { func (w *wifinina) sendChunk(sockfd int, buf []byte, deadline time.Time) (int, error) {
var sock = sock(sockfd) socket, ok := w.sockets[sockfd]
var socket = w.sockets[sock] if !ok {
return -1, netdev.ErrInvalidSocketFd
}
// Check if we've timed out // Check if we've timed out
if !deadline.IsZero() { if !deadline.IsZero() {
@@ -822,9 +880,9 @@ func (w *wifinina) sendChunk(sockfd int, buf []byte, deadline time.Time) (int, e
switch socket.protocol { switch socket.protocol {
case netdev.IPPROTO_TCP, netdev.IPPROTO_TLS: case netdev.IPPROTO_TCP, netdev.IPPROTO_TLS:
return w.sendTCP(sock, buf, deadline) return w.sendTCP(socket.sock, buf, deadline)
case netdev.IPPROTO_UDP: case netdev.IPPROTO_UDP:
return w.sendUDP(sock, buf, deadline) return w.sendUDP(socket.sock, socket.raddr, buf, deadline)
} }
return -1, netdev.ErrProtocolNotSupported return -1, netdev.ErrProtocolNotSupported
@@ -869,7 +927,10 @@ func (w *wifinina) Recv(sockfd int, buf []byte, flags int,
w.mu.Lock() w.mu.Lock()
defer w.mu.Unlock() defer w.mu.Unlock()
var sock = sock(sockfd) socket, ok := w.sockets[sockfd]
if !ok {
return -1, netdev.ErrInvalidSocketFd
}
// Limit max read size to chunk large read requests // Limit max read size to chunk large read requests
var max = len(buf) var max = len(buf)
@@ -890,22 +951,22 @@ func (w *wifinina) Recv(sockfd int, buf []byte, flags int,
// doesn't return unless there is data, even a single byte, or // doesn't return unless there is data, even a single byte, or
// on error such as timeout or EOF. // on error such as timeout or EOF.
n := int(w.getDataBuf(sock, buf[:max])) n := int(w.getDataBuf(socket.sock, buf[:max]))
if n > 0 { if n > 0 {
if debugging(debugNetdev) { if debugging(debugNetdev) {
fmt.Printf("[<--Recv] sockfd: %d, n: %d\r\n", fmt.Printf("[<--Recv] sockfd: %d, n: %d\r\n",
sock, n) sockfd, n)
} }
return n, nil return n, nil
} }
// Check if socket went down // Check if socket went down
if w.sockDown(sock) { if w.sockDown(socket) {
// Get any last bytes // Get any last bytes
n = int(w.getDataBuf(sock, buf[:max])) n = int(w.getDataBuf(socket.sock, buf[:max]))
if debugging(debugNetdev) { if debugging(debugNetdev) {
fmt.Printf("[<--Recv] sockfd: %d, n: %d, EOF\r\n", fmt.Printf("[<--Recv] sockfd: %d, n: %d, EOF\r\n",
sock, n) sockfd, n)
} }
if n > 0 { if n > 0 {
return n, io.EOF return n, io.EOF
@@ -934,34 +995,18 @@ func (w *wifinina) Close(sockfd int) error {
w.mu.Lock() w.mu.Lock()
defer w.mu.Unlock() defer w.mu.Unlock()
var sock = sock(sockfd) socket, ok := w.sockets[sockfd]
var socket = w.sockets[sock] if !ok {
return netdev.ErrInvalidSocketFd
if !socket.inuse {
return nil
} }
w.stopClient(sock) if socket.clientConnected {
w.stopClient(socket.sock)
if socket.protocol == netdev.IPPROTO_UDP {
socket.inuse = false
return nil
} }
start := time.Now() delete(w.sockets, sockfd)
for time.Since(start) < 5*time.Second {
if w.getClientState(sock) == tcpStateClosed { return nil
socket.inuse = false
return nil
}
w.mu.Unlock()
time.Sleep(100 * time.Millisecond)
w.mu.Lock()
}
return netdev.ErrClosingSocket
} }
func (w *wifinina) SetSockOpt(sockfd int, level int, opt int, value interface{}) error { func (w *wifinina) SetSockOpt(sockfd int, level int, opt int, value interface{}) error {
@@ -1123,6 +1168,23 @@ func (w *wifinina) accept(s sock) sock {
return 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 // insertDataBuf adds data to the buffer used for sending UDP data
func (w *wifinina) insertDataBuf(sock sock, buf []byte) bool { func (w *wifinina) insertDataBuf(sock sock, buf []byte) bool {