From a20f2eceeb426114eec285462a13673a5b96763b Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Fri, 10 Feb 2023 00:17:31 -0800 Subject: [PATCH 01/47] fresh netdev checkin preparing for PR --- espat/adapter.go | 20 - espat/espat.go | 278 +- espat/wifi.go | 16 +- examples/espat/espconsole/main.go | 145 - examples/espat/esphub/main.go | 131 - examples/espat/espstation/main.go | 110 - examples/espat/mqttclient/main.go | 144 - examples/espat/mqttsub/main.go | 165 - examples/espat/tcpclient/main.go | 113 - examples/net/espat/main.go | 101 + examples/net/http-get/main.go | 77 + examples/net/http-get/rtl8720dn.go | 39 + examples/net/http-get/wifinina.go | 43 + examples/net/http-head/main.go | 57 + examples/net/http-head/rtl8720dn.go | 39 + examples/net/http-head/wifinina.go | 43 + examples/net/http-post/main.go | 61 + examples/net/http-post/rtl8720dn.go | 39 + examples/net/http-post/wifinina.go | 43 + examples/net/http-postform/main.go | 62 + examples/net/http-postform/rtl8720dn.go | 39 + examples/net/http-postform/wifinina.go | 43 + examples/net/mqttclient/main.go | 77 + examples/net/mqttclient/rtl8720dn.go | 39 + examples/net/mqttclient/wifinina.go | 43 + examples/net/ntpclient/main.go | 104 + examples/net/ntpclient/rtl8720dn.go | 39 + examples/net/ntpclient/wifinina.go | 43 + examples/{wifi => net}/tcpclient/main.go | 66 +- examples/net/tcpclient/rtl8720dn.go | 39 + examples/net/tcpclient/wifinina.go | 43 + examples/net/tcpecho/main.go | 56 + examples/net/tcpecho/rtl8720dn.go | 39 + examples/net/tcpecho/wifinina.go | 43 + examples/net/tlsclient/main.go | 95 + examples/net/tlsclient/rtl8720dn.go | 39 + examples/net/tlsclient/wifinina.go | 43 + examples/net/webclient-tinyterm/main.go | 142 + examples/net/webclient/main.go | 106 + examples/net/webclient/rtl8720dn.go | 39 + examples/net/webclient/wifinina.go | 43 + examples/{wifinina => net}/webserver/main.go | 81 +- examples/net/webserver/rtl8720dn.go | 39 + examples/net/webserver/wifinina.go | 43 + examples/net/websocket/dial/main.go | 84 + examples/net/websocket/dial/rtl8720dn.go | 39 + examples/net/websocket/dial/wifinina.go | 43 + examples/net/websocket/handler/main.go | 52 + examples/net/websocket/handler/rtl8720dn.go | 39 + examples/net/websocket/handler/wifinina.go | 43 + examples/net/webstatic/images/tinygo-logo.png | Bin 0 -> 30898 bytes examples/net/webstatic/index.html | 28 + examples/net/webstatic/main.go | 38 + examples/net/webstatic/rtl8720dn.go | 39 + examples/net/webstatic/wifinina.go | 43 + examples/rtl8720dn/mqttclient/main.go | 131 - examples/rtl8720dn/mqttsub/main.go | 154 - examples/rtl8720dn/ntpclient/main.go | 143 - examples/rtl8720dn/tcpclient/main.go | 121 - examples/rtl8720dn/tlsclient/main.go | 137 - examples/rtl8720dn/udpstation/main.go | 91 - examples/rtl8720dn/version/main.go | 40 - examples/rtl8720dn/webclient-tinyterm/main.go | 162 - examples/rtl8720dn/webclient/main.go | 106 - examples/rtl8720dn/webserver/main.go | 193 - examples/wifi/tcpclient/espat.go | 27 - examples/wifi/tcpclient/rtl8720dn.go | 76 - examples/wifi/tcpclient/wifinina.go | 35 - examples/wifinina/connect/main.go | 144 - examples/wifinina/http-get/main.go | 158 - examples/wifinina/mqttclient/main.go | 145 - examples/wifinina/mqttsub/main.go | 158 - examples/wifinina/ntpclient/main.go | 177 - examples/wifinina/pins/main.go | 90 - examples/wifinina/tcpclient/main.go | 139 - examples/wifinina/tlsclient/main.go | 149 - examples/wifinina/udpstation/main.go | 101 - examples/wifinina/webclient/main.go | 150 - net/adapter.go | 44 - net/http/client.go | 253 - net/http/cookie.go | 435 -- net/http/cookiejar/jar.go | 504 -- net/http/cookiejar/punycode.go | 159 - net/http/driver.go | 15 - net/http/header.go | 259 - net/http/http.go | 162 - net/http/jar.go | 27 - net/http/request.go | 769 --- net/http/response.go | 120 - net/http/server.go | 581 -- net/http/status.go | 152 - net/http/tinygo.go | 308 - net/http/transefer.go | 34 - net/ipsocki.go | 26 - net/mqtt/mqtt.go | 396 -- net/mqtt/paho.go | 299 - net/mqtt/router.go | 178 - net/mqtt/token.go | 19 - net/net.go | 423 -- net/net_test.go | 14 - net/tls/tls.go | 42 - netdev/README.md | 184 + netdev/netdev.go | 133 + netdev/netdev_models.jpg | Bin 0 -> 65672 bytes netdev/socket.go | 141 + rtl8720dn/README.md | 7 +- rtl8720dn/adapter.go | 57 - rtl8720dn/debug.go | 16 + rtl8720dn/http.go | 236 - rtl8720dn/netdriver.go | 377 -- rtl8720dn/rpc.go | 4963 +++-------------- rtl8720dn/rpc_util.go | 16 +- rtl8720dn/rtl8720dn.go | 788 ++- rtl8720dn/util.go | 51 - rtl8720dn/wifi.go | 72 - wifinina/README.md | 2 +- wifinina/adapter.go | 31 - wifinina/debug.go | 17 + wifinina/http.go | 323 -- wifinina/pins.go | 12 +- wifinina/protocol/readme.md | 3 - wifinina/tcp.go | 270 - wifinina/wifinina.go | 2326 +++++--- 123 files changed, 6272 insertions(+), 15356 deletions(-) delete mode 100644 espat/adapter.go delete mode 100644 examples/espat/espconsole/main.go delete mode 100644 examples/espat/esphub/main.go delete mode 100644 examples/espat/espstation/main.go delete mode 100644 examples/espat/mqttclient/main.go delete mode 100644 examples/espat/mqttsub/main.go delete mode 100644 examples/espat/tcpclient/main.go create mode 100644 examples/net/espat/main.go create mode 100644 examples/net/http-get/main.go create mode 100644 examples/net/http-get/rtl8720dn.go create mode 100644 examples/net/http-get/wifinina.go create mode 100644 examples/net/http-head/main.go create mode 100644 examples/net/http-head/rtl8720dn.go create mode 100644 examples/net/http-head/wifinina.go create mode 100644 examples/net/http-post/main.go create mode 100644 examples/net/http-post/rtl8720dn.go create mode 100644 examples/net/http-post/wifinina.go create mode 100644 examples/net/http-postform/main.go create mode 100644 examples/net/http-postform/rtl8720dn.go create mode 100644 examples/net/http-postform/wifinina.go create mode 100644 examples/net/mqttclient/main.go create mode 100644 examples/net/mqttclient/rtl8720dn.go create mode 100644 examples/net/mqttclient/wifinina.go create mode 100644 examples/net/ntpclient/main.go create mode 100644 examples/net/ntpclient/rtl8720dn.go create mode 100644 examples/net/ntpclient/wifinina.go rename examples/{wifi => net}/tcpclient/main.go (51%) create mode 100644 examples/net/tcpclient/rtl8720dn.go create mode 100644 examples/net/tcpclient/wifinina.go create mode 100644 examples/net/tcpecho/main.go create mode 100644 examples/net/tcpecho/rtl8720dn.go create mode 100644 examples/net/tcpecho/wifinina.go create mode 100644 examples/net/tlsclient/main.go create mode 100644 examples/net/tlsclient/rtl8720dn.go create mode 100644 examples/net/tlsclient/wifinina.go create mode 100644 examples/net/webclient-tinyterm/main.go create mode 100644 examples/net/webclient/main.go create mode 100644 examples/net/webclient/rtl8720dn.go create mode 100644 examples/net/webclient/wifinina.go rename examples/{wifinina => net}/webserver/main.go (77%) create mode 100644 examples/net/webserver/rtl8720dn.go create mode 100644 examples/net/webserver/wifinina.go create mode 100644 examples/net/websocket/dial/main.go create mode 100644 examples/net/websocket/dial/rtl8720dn.go create mode 100644 examples/net/websocket/dial/wifinina.go create mode 100644 examples/net/websocket/handler/main.go create mode 100644 examples/net/websocket/handler/rtl8720dn.go create mode 100644 examples/net/websocket/handler/wifinina.go create mode 100644 examples/net/webstatic/images/tinygo-logo.png create mode 100644 examples/net/webstatic/index.html create mode 100644 examples/net/webstatic/main.go create mode 100644 examples/net/webstatic/rtl8720dn.go create mode 100644 examples/net/webstatic/wifinina.go delete mode 100644 examples/rtl8720dn/mqttclient/main.go delete mode 100644 examples/rtl8720dn/mqttsub/main.go delete mode 100644 examples/rtl8720dn/ntpclient/main.go delete mode 100644 examples/rtl8720dn/tcpclient/main.go delete mode 100644 examples/rtl8720dn/tlsclient/main.go delete mode 100644 examples/rtl8720dn/udpstation/main.go delete mode 100644 examples/rtl8720dn/version/main.go delete mode 100644 examples/rtl8720dn/webclient-tinyterm/main.go delete mode 100644 examples/rtl8720dn/webclient/main.go delete mode 100644 examples/rtl8720dn/webserver/main.go delete mode 100644 examples/wifi/tcpclient/espat.go delete mode 100644 examples/wifi/tcpclient/rtl8720dn.go delete mode 100644 examples/wifi/tcpclient/wifinina.go delete mode 100644 examples/wifinina/connect/main.go delete mode 100644 examples/wifinina/http-get/main.go delete mode 100644 examples/wifinina/mqttclient/main.go delete mode 100644 examples/wifinina/mqttsub/main.go delete mode 100644 examples/wifinina/ntpclient/main.go delete mode 100644 examples/wifinina/pins/main.go delete mode 100644 examples/wifinina/tcpclient/main.go delete mode 100644 examples/wifinina/tlsclient/main.go delete mode 100644 examples/wifinina/udpstation/main.go delete mode 100644 examples/wifinina/webclient/main.go delete mode 100644 net/adapter.go delete mode 100644 net/http/client.go delete mode 100644 net/http/cookie.go delete mode 100644 net/http/cookiejar/jar.go delete mode 100644 net/http/cookiejar/punycode.go delete mode 100644 net/http/driver.go delete mode 100644 net/http/header.go delete mode 100644 net/http/http.go delete mode 100644 net/http/jar.go delete mode 100644 net/http/request.go delete mode 100644 net/http/response.go delete mode 100644 net/http/server.go delete mode 100644 net/http/status.go delete mode 100644 net/http/tinygo.go delete mode 100644 net/http/transefer.go delete mode 100644 net/ipsocki.go delete mode 100644 net/mqtt/mqtt.go delete mode 100644 net/mqtt/paho.go delete mode 100644 net/mqtt/router.go delete mode 100644 net/mqtt/token.go delete mode 100644 net/net.go delete mode 100644 net/net_test.go delete mode 100644 net/tls/tls.go create mode 100644 netdev/README.md create mode 100644 netdev/netdev.go create mode 100644 netdev/netdev_models.jpg create mode 100644 netdev/socket.go delete mode 100644 rtl8720dn/adapter.go create mode 100644 rtl8720dn/debug.go delete mode 100644 rtl8720dn/http.go delete mode 100644 rtl8720dn/netdriver.go delete mode 100644 rtl8720dn/util.go delete mode 100644 rtl8720dn/wifi.go delete mode 100644 wifinina/adapter.go create mode 100644 wifinina/debug.go delete mode 100644 wifinina/http.go delete mode 100644 wifinina/protocol/readme.md delete mode 100644 wifinina/tcp.go diff --git a/espat/adapter.go b/espat/adapter.go deleted file mode 100644 index 3df9180e9..000000000 --- a/espat/adapter.go +++ /dev/null @@ -1,20 +0,0 @@ -package espat - -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 - } - - d.SetWifiMode(WifiModeClient) - return d.ConnectToAP(ssid, pass, int(timeout.Seconds())) -} - -func (d *Device) Disconnect() error { - return d.DisconnectFromAP() -} diff --git a/espat/espat.go b/espat/espat.go index cdb97cc80..c24731f76 100644 --- a/espat/espat.go +++ b/espat/espat.go @@ -15,41 +15,279 @@ // // AT command set: // https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf +// +// 02/2023 sfeldma@gmail.com Heavily modified to use netdev interface + package espat // import "tinygo.org/x/drivers/espat" import ( "errors" + "fmt" "strconv" + "machine" "strings" "time" - "tinygo.org/x/drivers" - "tinygo.org/x/drivers/net" + "tinygo.org/x/drivers/netdev" ) -// Device wraps UART connection to the ESP8266/ESP32. -type Device struct { - bus drivers.UART +var ( + version = "0.0.1" + driverName = "Espressif ESP8266/ESP32 AT Wifi network device driver (espat)" +) + +type Config struct { + // AP creditials + Ssid string + Passphrase string + + // UART config + Uart *machine.UART + Tx machine.Pin + Rx machine.Pin +} +type Device struct { + cfg *Config + uart *machine.UART // command responses that come back from the ESP8266/ESP32 response []byte - // data received from a TCP/UDP connection forwarded by the ESP8266/ESP32 socketdata []byte + socketInUse bool + socketProtocol netdev.Protocol + socketLaddr netdev.SockAddr +} + +func New(cfg *Config) *Device { + d := Device{ + cfg: cfg, + response: make([]byte, 512), + socketdata: make([]byte, 0, 1024), + } + return &d +} + +func (d *Device) NetConnect() error { + + fmt.Printf("\r\n") + fmt.Printf("%s\r\n\r\n", driverName) + fmt.Printf("Driver version : %s\r\n\r\n", version) + + if len(d.cfg.Ssid) == 0 { + return netdev.ErrMissingSSID + } + + d.uart = d.cfg.Uart + d.uart.Configure(machine.UARTConfig{TX: d.cfg.Tx, RX: d.cfg.Rx}) + + // Connect to ESP8266/ESP32 + fmt.Printf("Connecting to device...") + + for i := 0; i < 5; i++ { + if d.Connected() { + break + } + time.Sleep(1 * time.Second) + } + + if !d.Connected() { + fmt.Printf("FAILED\r\n") + return netdev.ErrConnectFailed + } + + fmt.Printf("CONNECTED\r\n") + + fmt.Printf("\r\n") + fmt.Printf("ESP8266/ESP32 firmware version : %s\r\n", string(d.Version())) + fmt.Printf("MAC address : %s\r\n", d.GetMacAddress()) + fmt.Printf("\r\n") + + // Connect to Wifi AP + fmt.Printf("Connecting to Wifi SSID '%s'...", d.cfg.Ssid) + + d.SetWifiMode(WifiModeClient) + err := d.ConnectToAP(d.cfg.Ssid, d.cfg.Passphrase, 10 /* secs */) + if err != nil { + fmt.Printf("FAILED\r\n") + return netdev.ErrConnectFailed + } + + fmt.Printf("CONNECTED\r\n") + + ip, err := d.GetClientIP() + if err != nil { + return netdev.ErrConnectFailed + } + + fmt.Printf("\r\n") + fmt.Printf("DHCP-assigned IP : %s\r\n", ip) + fmt.Printf("\r\n") + + return nil } -// ActiveDevice is the currently configured Device in use. There can only be one. -var ActiveDevice *Device +func (d *Device) NetDisconnect() { + d.DisconnectFromAP() + fmt.Printf("\r\nDisconnected from Wifi SSID '%s'\r\n\r\n", d.cfg.Ssid) +} + +func (d *Device) NetNotify(cb func(netdev.Event)) { + // Not supported +} + +func (d *Device) GetHostByName(name string) (netdev.IP, error) { + ip, err := d.GetDNS(name) + return netdev.ParseIP(ip), err +} + +func (d *Device) GetHardwareAddr() (netdev.HardwareAddr, error) { + return netdev.ParseHardwareAddr(d.GetMacAddress()), nil +} + +func (d *Device) GetIPAddr() (netdev.IP, error) { + ip, err := d.GetClientIP() + return netdev.ParseIP(ip), err +} + +func (d *Device) Socket(family netdev.AddressFamily, sockType netdev.SockType, + protocol netdev.Protocol) (netdev.Sockfd, error) { + + switch family { + case netdev.AF_INET: + default: + return -1, netdev.ErrFamilyNotSupported + } + + switch { + case protocol == netdev.IPPROTO_TCP && sockType == netdev.SOCK_STREAM: + case protocol == netdev.IPPROTO_TLS && sockType == netdev.SOCK_STREAM: + case protocol == netdev.IPPROTO_UDP && sockType == netdev.SOCK_DGRAM: + default: + return -1, netdev.ErrProtocolNotSupported + } + + // Only supporting single connection mode, so only one socket at a time + if d.socketInUse { + return -1, netdev.ErrNoMoreSockets + } + d.socketInUse = true + d.socketProtocol = protocol + + return netdev.Sockfd(0), nil +} + +func (d *Device) Bind(sockfd netdev.Sockfd, addr netdev.SockAddr) error { + d.socketLaddr = addr + return nil +} + +func (d *Device) Connect(sockfd netdev.Sockfd, servaddr netdev.SockAddr) error { + var err error + var addr = servaddr.Ip().String() + var port = fmt.Sprintf("%d", servaddr.Port()) + var lport = fmt.Sprintf("%d", d.socketLaddr.Port()) + + switch d.socketProtocol { + case netdev.IPPROTO_TCP: + err = d.ConnectTCPSocket(addr, port) + case netdev.IPPROTO_UDP: + err = d.ConnectUDPSocket(addr, port, lport) + case netdev.IPPROTO_TLS: + err = d.ConnectSSLSocket(addr, port) + } + + return err +} + +func (d *Device) Listen(sockfd netdev.Sockfd, backlog int) error { + switch d.socketProtocol { + case netdev.IPPROTO_UDP: + default: + return netdev.ErrProtocolNotSupported + } + return nil +} + +func (d *Device) Accept(sockfd netdev.Sockfd, peer netdev.SockAddr) (netdev.Sockfd, error) { + return -1, netdev.ErrNotSupported +} + +func (d *Device) sendChunk(sockfd netdev.Sockfd, buf []byte, timeout time.Duration) (int, error) { + err := d.StartSocketSend(len(buf)) + if err != nil { + return -1, err + } + n, err := d.Write(buf) + if err != nil { + return -1, err + } + _, err = d.Response(1000) + if err != nil { + return -1, err + } + return n, err +} + +func (d *Device) Send(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, + timeout time.Duration) (int, error) { + + // 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 := d.sendChunk(sockfd, buf[i:end], timeout) + if err != nil { + return -1, err + } + } + + return len(buf), nil +} + +func (d *Device) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, + timeout time.Duration) (int, error) { + + var length = len(buf) + var expire = time.Now().Add(timeout) + + // Limit length read size to chunk large read requests + if length > 1436 { + length = 1436 + } + + for { + // Check if we've timed out + if timeout > 0 { + if time.Now().Before(expire) { + return -1, netdev.ErrRecvTimeout + } + } + + n, err := d.ReadSocket(buf[:length]) + if err != nil { + return -1, err + } + if n == 0 { + time.Sleep(100 * time.Millisecond) + continue + } + + return n, nil + } +} -// New returns a new espat driver. Pass in a fully configured UART bus. -func New(b drivers.UART) *Device { - return &Device{bus: b, response: make([]byte, 512), socketdata: make([]byte, 0, 1024)} +func (d *Device) Close(sockfd netdev.Sockfd) error { + return d.DisconnectSocket() } -// Configure sets up the device for communication. -func (d Device) Configure() { - ActiveDevice = &d - net.ActiveDevice = ActiveDevice +func (d *Device) SetSockOpt(sockfd netdev.Sockfd, level netdev.SockOptLevel, + opt netdev.SockOpt, value any) error { + return netdev.ErrNotSupported } // Connected checks if there is communication with the ESP8266/ESP32. @@ -66,12 +304,12 @@ func (d *Device) Connected() bool { // Write raw bytes to the UART. func (d *Device) Write(b []byte) (n int, err error) { - return d.bus.Write(b) + return d.uart.Write(b) } // Read raw bytes from the UART. func (d *Device) Read(b []byte) (n int, err error) { - return d.bus.Read(b) + return d.uart.Read(b) } // how long in milliseconds to pause after sending AT commands @@ -157,11 +395,11 @@ func (d *Device) Response(timeout int) ([]byte, error) { retries := timeout / pause for { - size = d.bus.Buffered() + size = d.uart.Buffered() if size > 0 { end += size - d.bus.Read(d.response[start:end]) + d.uart.Read(d.response[start:end]) // if "+IPD" then read socket data if strings.Contains(string(d.response[:end]), "+IPD") { @@ -217,5 +455,5 @@ func (d *Device) parseIPD(end int) error { // IsSocketDataAvailable returns of there is socket data available func (d *Device) IsSocketDataAvailable() bool { - return len(d.socketdata) > 0 || d.bus.Buffered() > 0 + return len(d.socketdata) > 0 || d.uart.Buffered() > 0 } diff --git a/espat/wifi.go b/espat/wifi.go index ea37a8e8f..ff752234f 100644 --- a/espat/wifi.go +++ b/espat/wifi.go @@ -44,10 +44,7 @@ func (d *Device) ConnectToAP(ssid, pwd string, ws int) error { d.Set(ConnectAP, val) _, err := d.Response(ws * 1000) - if err != nil { - return err - } - return nil + return err } // DisconnectFromAP disconnects the ESP8266/ESP32 from the current access point. @@ -57,6 +54,17 @@ func (d *Device) DisconnectFromAP() error { return err } +// GetClientIP returns the ESP8266/ESP32 current station MAC addess when +// connected to an Access Point. +func (d *Device) GetMacAddress() string { + d.Query(SetStationMACAddress) + r, err := d.Response(1000) + if err != nil { + return "unknown" + } + return string(r) +} + // GetClientIP returns the ESP8266/ESP32 current client IP addess when connected to an Access Point. func (d *Device) GetClientIP() (string, error) { d.Query(SetStationIP) diff --git a/examples/espat/espconsole/main.go b/examples/espat/espconsole/main.go deleted file mode 100644 index ad550062c..000000000 --- a/examples/espat/espconsole/main.go +++ /dev/null @@ -1,145 +0,0 @@ -// This is a console to a ESP8266/ESP32 running on the device UART1. -// Allows you to type AT commands from your computer via the microcontroller. -// -// In other words: -// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> INTERNET -// -// More information on the Espressif AT command set at: -// https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf -package main - -import ( - "machine" - "time" - - "tinygo.org/x/drivers/espat" -) - -// change actAsAP to true to act as an access point instead of connecting to one. -const actAsAP = false - -var ( - // access point info - ssid string - pass string -) - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - uart = machine.UART1 - tx = machine.PA22 - rx = machine.PA23 - - console = machine.Serial - - adaptor *espat.Device -) - -func main() { - uart.Configure(machine.UARTConfig{TX: tx, RX: rx}) - - // Init esp8266 - adaptor = espat.New(uart) - adaptor.Configure() - - // first check if connected - if connectToESP() { - println("Connected to wifi adaptor.") - adaptor.Echo(false) - - connectToAP() - } else { - println("") - failMessage("Unable to connect to wifi adaptor.") - return - } - - println("Type an AT command then press enter:") - prompt() - - input := make([]byte, 64) - i := 0 - for { - if console.Buffered() > 0 { - data, _ := console.ReadByte() - - switch data { - case 13: - // return key - console.Write([]byte("\r\n")) - - // send command to ESP8266 - input[i] = byte('\r') - input[i+1] = byte('\n') - adaptor.Write(input[:i+2]) - - // display response - r, _ := adaptor.Response(500) - console.Write(r) - - // prompt - prompt() - - i = 0 - continue - default: - // just echo the character - console.WriteByte(data) - input[i] = data - i++ - } - } - time.Sleep(10 * time.Millisecond) - } -} - -func prompt() { - print("ESPAT>") -} - -// connect to ESP8266/ESP32 -func connectToESP() bool { - for i := 0; i < 5; i++ { - println("Connecting to wifi adaptor...") - if adaptor.Connected() { - return true - } - time.Sleep(1 * time.Second) - } - return false -} - -// connect to access point -func connectToAP() { - println("Connecting to wifi network '" + ssid + "'") - - if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil { - failMessage(err.Error()) - } - - println("Connected.") - ip, err := adaptor.GetClientIP() - if err != nil { - failMessage(err.Error()) - } - - println(ip) -} - -// provide access point -func provideAP() { - println("Starting wifi network as access point '" + ssid + "'...") - adaptor.SetWifiMode(espat.WifiModeAP) - adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK) - println("Ready.") - ip, _ := adaptor.GetAPIP() - println(ip) -} - -func failMessage(msg string) { - for { - println(msg) - time.Sleep(1 * time.Second) - } -} diff --git a/examples/espat/esphub/main.go b/examples/espat/esphub/main.go deleted file mode 100644 index e253955ea..000000000 --- a/examples/espat/esphub/main.go +++ /dev/null @@ -1,131 +0,0 @@ -// This is a sensor hub that uses a ESP8266/ESP32 running on the device UART1. -// It creates a UDP "server" you can use to get info to/from your computer via the microcontroller. -// -// In other words: -// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> INTERNET -package main - -import ( - "machine" - "time" - - "tinygo.org/x/drivers/espat" - "tinygo.org/x/drivers/net" -) - -// change actAsAP to true to act as an access point instead of connecting to one. -const actAsAP = false - -var ( - // access point info - ssid string - pass string -) - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - uart = machine.UART1 - tx = machine.PA22 - rx = machine.PA23 - - adaptor *espat.Device -) - -func main() { - uart.Configure(machine.UARTConfig{TX: tx, RX: rx}) - - // Init esp8266 - adaptor = espat.New(uart) - adaptor.Configure() - - readyled := machine.LED - readyled.Configure(machine.PinConfig{Mode: machine.PinOutput}) - readyled.High() - - // first check if connected - if connectToESP() { - println("Connected to wifi adaptor.") - adaptor.Echo(false) - - connectToAP() - } else { - println("") - failMessage("Unable to connect to wifi adaptor.") - return - } - - // now make UDP connection - laddr := &net.UDPAddr{Port: 2222} - println("Loading UDP listener...") - conn, _ := net.ListenUDP("UDP", laddr) - - println("Waiting for data...") - data := make([]byte, 50) - blink := true - for { - n, _ := conn.Read(data) - if n > 0 { - println(string(data[:n])) - conn.Write([]byte("hello back\r\n")) - } - blink = !blink - if blink { - readyled.High() - } else { - readyled.Low() - } - time.Sleep(500 * time.Millisecond) - } - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting UDP...") - conn.Close() - println("Done.") -} - -// connect to ESP8266/ESP32 -func connectToESP() bool { - for i := 0; i < 5; i++ { - println("Connecting to wifi adaptor...") - if adaptor.Connected() { - return true - } - time.Sleep(1 * time.Second) - } - return false -} - -// connect to access point -func connectToAP() { - println("Connecting to wifi network '" + ssid + "'") - - if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil { - failMessage(err.Error()) - } - - println("Connected.") - ip, err := adaptor.GetClientIP() - if err != nil { - failMessage(err.Error()) - } - - println(ip) -} - -// provide access point -func provideAP() { - println("Starting wifi network as access point '" + ssid + "'...") - adaptor.SetWifiMode(espat.WifiModeAP) - adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK) - println("Ready.") - ip, _ := adaptor.GetAPIP() - println(ip) -} - -func failMessage(msg string) { - for { - println(msg) - time.Sleep(1 * time.Second) - } -} diff --git a/examples/espat/espstation/main.go b/examples/espat/espstation/main.go deleted file mode 100644 index 5ae2e3d69..000000000 --- a/examples/espat/espstation/main.go +++ /dev/null @@ -1,110 +0,0 @@ -// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1. -// It creates a UDP connection you can use to get info to/from your computer via the microcontroller. -// -// In other words: -// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 -package main - -import ( - "machine" - "time" - - "tinygo.org/x/drivers/espat" - "tinygo.org/x/drivers/net" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the listener aka "hub". Replace with your own info. -const hubIP = "0.0.0.0" - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - uart = machine.UART1 - tx = machine.PA22 - rx = machine.PA23 - - adaptor *espat.Device -) - -func main() { - uart.Configure(machine.UARTConfig{TX: tx, RX: rx}) - - // Init esp8266/esp32 - adaptor = espat.New(uart) - adaptor.Configure() - - // first check if connected - if connectToESP() { - println("Connected to wifi adaptor.") - adaptor.Echo(false) - - connectToAP() - } else { - println("") - failMessage("Unable to connect to wifi adaptor.") - return - } - - // now make UDP connection - ip := net.ParseIP(hubIP) - raddr := &net.UDPAddr{IP: ip, Port: 2222} - laddr := &net.UDPAddr{Port: 2222} - - println("Dialing UDP connection...") - conn, _ := net.DialUDP("udp", laddr, raddr) - - for { - // send data - println("Sending data...") - conn.Write([]byte("hello\r\n")) - - time.Sleep(1000 * time.Millisecond) - } - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting UDP...") - conn.Close() - println("Done.") -} - -// connect to ESP8266/ESP32 -func connectToESP() bool { - for i := 0; i < 5; i++ { - println("Connecting to wifi adaptor...") - if adaptor.Connected() { - return true - } - time.Sleep(1 * time.Second) - } - return false -} - -// connect to access point -func connectToAP() { - println("Connecting to wifi network '" + ssid + "'") - - if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil { - failMessage(err.Error()) - } - - println("Connected.") - ip, err := adaptor.GetClientIP() - if err != nil { - failMessage(err.Error()) - } - - println(ip) -} - -func failMessage(msg string) { - for { - println(msg) - time.Sleep(1 * time.Second) - } -} diff --git a/examples/espat/mqttclient/main.go b/examples/espat/mqttclient/main.go deleted file mode 100644 index aaa88554e..000000000 --- a/examples/espat/mqttclient/main.go +++ /dev/null @@ -1,144 +0,0 @@ -// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1. -// It creates an MQTT connection that publishes a message every second -// to an MQTT broker. -// -// In other words: -// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> Internet <--> MQTT broker. -// -// You must install the Paho MQTT package to build this program: -// -// go get -u github.com/eclipse/paho.mqtt.golang -package main - -import ( - "machine" - "math/rand" - "time" - - "tinygo.org/x/drivers/espat" - "tinygo.org/x/drivers/net/mqtt" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the MQTT broker to use. Replace with your own info. -const server = "tcp://test.mosquitto.org:1883" - -//const server = "ssl://test.mosquitto.org:8883" - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - uart = machine.UART2 - tx = machine.PA22 - rx = machine.PA23 - - console = machine.Serial - - adaptor *espat.Device - topic = "tinygo" -) - -func main() { - time.Sleep(3000 * time.Millisecond) - - uart.Configure(machine.UARTConfig{TX: tx, RX: rx}) - rand.Seed(time.Now().UnixNano()) - - // Init esp8266/esp32 - adaptor = espat.New(uart) - adaptor.Configure() - - // first check if connected - if connectToESP() { - println("Connected to wifi adaptor.") - adaptor.Echo(false) - - connectToAP() - } else { - println("") - failMessage("Unable to connect to wifi adaptor.") - return - } - - opts := mqtt.NewClientOptions() - opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10)) - - println("Connecting to MQTT broker at", server) - cl := mqtt.NewClient(opts) - if token := cl.Connect(); token.Wait() && token.Error() != nil { - failMessage(token.Error().Error()) - } - - for { - println("Publishing MQTT message...") - data := []byte("{\"e\":[{ \"n\":\"hello\", \"v\":101 }]}") - token := cl.Publish(topic, 0, false, data) - token.Wait() - if token.Error() != nil { - println(token.Error().Error()) - } - - time.Sleep(1000 * time.Millisecond) - } - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting MQTT...") - cl.Disconnect(100) - - println("Done.") -} - -// connect to ESP8266/ESP32 -func connectToESP() bool { - for i := 0; i < 5; i++ { - println("Connecting to wifi adaptor...") - if adaptor.Connected() { - return true - } - time.Sleep(1 * time.Second) - } - return false -} - -// connect to access point -func connectToAP() { - println("Connecting to wifi network '" + ssid + "'") - - if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil { - failMessage(err.Error()) - } - - println("Connected.") - ip, err := adaptor.GetClientIP() - if err != nil { - failMessage(err.Error()) - } - - println(ip) -} - -// Returns an int >= min, < max -func randomInt(min, max int) int { - return min + rand.Intn(max-min) -} - -// Generate a random string of A-Z chars with len = l -func randomString(len int) string { - bytes := make([]byte, len) - for i := 0; i < len; i++ { - bytes[i] = byte(randomInt(65, 90)) - } - return string(bytes) -} - -func failMessage(msg string) { - for { - println(msg) - time.Sleep(1 * time.Second) - } -} diff --git a/examples/espat/mqttsub/main.go b/examples/espat/mqttsub/main.go deleted file mode 100644 index 65753852e..000000000 --- a/examples/espat/mqttsub/main.go +++ /dev/null @@ -1,165 +0,0 @@ -// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1. -// It creates an MQTT connection that publishes a message every second -// to an MQTT broker. -// -// In other words: -// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> Internet <--> MQTT broker. -// -// You must also install the Paho MQTT package to build this program: -// -// go get -u github.com/eclipse/paho.mqtt.golang -package main - -import ( - "fmt" - "machine" - "math/rand" - "time" - - "tinygo.org/x/drivers/espat" - "tinygo.org/x/drivers/net/mqtt" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the MQTT broker to use. Replace with your own info. -//const server = "tcp://test.mosquitto.org:1883" - -const server = "ssl://test.mosquitto.org:8883" - -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - // these are defaults for the Arduino Nano33 IoT. - uart = machine.UART1 - tx = machine.PA22 - rx = machine.PA23 - - console = machine.Serial - - adaptor *espat.Device - cl mqtt.Client - topicTx = "tinygo/tx" - topicRx = "tinygo/rx" -) - -func subHandler(client mqtt.Client, msg mqtt.Message) { - fmt.Printf("[%s] ", msg.Topic()) - fmt.Printf("%s\r\n", msg.Payload()) -} - -func main() { - time.Sleep(3000 * time.Millisecond) - - uart.Configure(machine.UARTConfig{TX: tx, RX: rx}) - rand.Seed(time.Now().UnixNano()) - - // Init esp8266/esp32 - adaptor = espat.New(uart) - adaptor.Configure() - - // first check if connected - if connectToESP() { - println("Connected to wifi adaptor.") - adaptor.Echo(false) - - connectToAP() - } else { - println("") - failMessage("Unable to connect to wifi adaptor.") - return - } - - opts := mqtt.NewClientOptions() - opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10)) - - println("Connecting to MQTT broker at", server) - cl = mqtt.NewClient(opts) - if token := cl.Connect(); token.Wait() && token.Error() != nil { - failMessage(token.Error().Error()) - } - - // subscribe - token := cl.Subscribe(topicRx, 0, subHandler) - token.Wait() - if token.Error() != nil { - failMessage(token.Error().Error()) - } - - go publishing() - - select {} - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting MQTT...") - cl.Disconnect(100) - - println("Done.") -} - -func publishing() { - for { - println("Publishing MQTT message...") - data := []byte("{\"e\":[{ \"n\":\"hello\", \"v\":101 }]}") - token := cl.Publish(topicTx, 0, false, data) - token.Wait() - if token.Error() != nil { - println(token.Error().Error()) - } - - time.Sleep(1000 * time.Millisecond) - } -} - -// connect to ESP8266/ESP32 -func connectToESP() bool { - for i := 0; i < 5; i++ { - println("Connecting to wifi adaptor...") - if adaptor.Connected() { - return true - } - time.Sleep(1 * time.Second) - } - return false -} - -// connect to access point -func connectToAP() { - println("Connecting to wifi network '" + ssid + "'") - - if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil { - failMessage(err.Error()) - } - - println("Connected.") - ip, err := adaptor.GetClientIP() - if err != nil { - failMessage(err.Error()) - } - - println(ip) -} - -// Returns an int >= min, < max -func randomInt(min, max int) int { - return min + rand.Intn(max-min) -} - -// Generate a random string of A-Z chars with len = l -func randomString(len int) string { - bytes := make([]byte, len) - for i := 0; i < len; i++ { - bytes[i] = byte(randomInt(65, 90)) - } - return string(bytes) -} - -func failMessage(msg string) { - for { - println(msg) - time.Sleep(1 * time.Second) - } -} diff --git a/examples/espat/tcpclient/main.go b/examples/espat/tcpclient/main.go deleted file mode 100644 index db1bcb8cc..000000000 --- a/examples/espat/tcpclient/main.go +++ /dev/null @@ -1,113 +0,0 @@ -// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1. -// It creates a UDP connection you can use to get info to/from your computer via the microcontroller. -// -// In other words: -// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 -package main - -import ( - "machine" - "time" - - "tinygo.org/x/drivers/espat" - "tinygo.org/x/drivers/net" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the server aka "hub". Replace with your own info. -const serverIP = "0.0.0.0" - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - uart = machine.UART1 - tx = machine.PA22 - rx = machine.PA23 - - adaptor *espat.Device -) - -func main() { - uart.Configure(machine.UARTConfig{TX: tx, RX: rx}) - - // Init esp8266/esp32 - adaptor = espat.New(uart) - adaptor.Configure() - - // first check if connected - if connectToESP() { - println("Connected to wifi adaptor.") - adaptor.Echo(false) - - connectToAP() - } else { - println("") - failMessage("Unable to connect to wifi adaptor.") - return - } - - // now make TCP connection - ip := net.ParseIP(serverIP) - raddr := &net.TCPAddr{IP: ip, Port: 8080} - laddr := &net.TCPAddr{Port: 8080} - - println("Dialing TCP connection...") - conn, err := net.DialTCP("tcp", laddr, raddr) - if err != nil { - failMessage(err.Error()) - } - - for { - // send data - println("Sending data...") - conn.Write([]byte("hello\r\n")) - - time.Sleep(1000 * time.Millisecond) - } - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting TCP...") - conn.Close() - println("Done.") -} - -// connect to ESP8266/ESP32 -func connectToESP() bool { - for i := 0; i < 5; i++ { - println("Connecting to wifi adaptor...") - if adaptor.Connected() { - return true - } - time.Sleep(1 * time.Second) - } - return false -} - -// connect to access point -func connectToAP() { - println("Connecting to wifi network '" + ssid + "'") - - if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil { - failMessage(err.Error()) - } - - println("Connected.") - ip, err := adaptor.GetClientIP() - if err != nil { - failMessage(err.Error()) - } - - println(ip) -} - -func failMessage(msg string) { - for { - println(msg) - time.Sleep(1 * time.Second) - } -} diff --git a/examples/net/espat/main.go b/examples/net/espat/main.go new file mode 100644 index 000000000..a1891fe0e --- /dev/null +++ b/examples/net/espat/main.go @@ -0,0 +1,101 @@ +package main + +import ( + "bytes" + "fmt" + "log" + "machine" + "net" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/espat" +) + +var ( + ssid string + pass string + addr string = "10.0.0.100:8080" +) + +var ( + netcfg = espat.Config{ + Ssid: ssid, + Passphrase: pass, + Uart: machine.UART2, + Tx: machine.TX1, + Rx: machine.RX0, + } + + dev = espat.New(&netcfg) +) + +var buf = &bytes.Buffer{} + +func main() { + + waitSerial() + + netdev.Use(dev) + if err := dev.NetConnect(); err != nil { + log.Fatal(err) + } + + for { + sendBatch() + time.Sleep(500 * time.Millisecond) + } +} + +func sendBatch() { + + // make TCP connection + message("---------------\r\nDialing TCP connection") + conn, err := net.Dial("tcp", addr) + for ; err != nil; conn, err = net.Dial("tcp", addr) { + message(err.Error()) + time.Sleep(5 * time.Second) + } + + n := 0 + w := 0 + start := time.Now() + + // send data + message("Sending data") + + for i := 0; i < 1000; i++ { + buf.Reset() + fmt.Fprint(buf, + "\r---------------------------- i == ", i, " ----------------------------"+ + "\r---------------------------- i == ", i, " ----------------------------") + if w, err = conn.Write(buf.Bytes()); err != nil { + println("error:", err.Error(), "\r") + break + } + n += w + } + + buf.Reset() + ms := time.Now().Sub(start).Milliseconds() + fmt.Fprint(buf, "\nWrote ", n, " bytes in ", ms, " ms\r\n") + message(buf.String()) + + if _, err := conn.Write(buf.Bytes()); err != nil { + println("error:", err.Error(), "\r") + } + + println("Disconnecting TCP...") + conn.Close() +} + +func message(msg string) { + println(msg, "\r") +} + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} diff --git a/examples/net/http-get/main.go b/examples/net/http-get/main.go new file mode 100644 index 000000000..9028da578 --- /dev/null +++ b/examples/net/http-get/main.go @@ -0,0 +1,77 @@ +// This example gets an URL using http.Get(). URL scheme can be http or https. +// +// Note: It may be necessary to increase the stack size when using "net/http". +// Use the -stack-size=4KB command line option. +// +// Some targets (Arduino Nano33 IoT) don't have enough SRAM to run http.Get(). +// Use the following for those targets: +// +// examples/net/webclient (for HTTP) +// examples/net/tlsclient (for HTTPS) + +package main + +import ( + "fmt" + "io" + "log" + "machine" + "net/http" + "net/url" + "strings" + "time" +) + +var ( + ssid string + pass string +) + +func main() { + + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + name := "John Doe" + occupation := "gardener" + + params := "name=" + url.QueryEscape(name) + "&" + + "occupation=" + url.QueryEscape(occupation) + + path := fmt.Sprintf("https://httpbin.org/get?%s", params) + + cnt := 0 + for { + fmt.Printf("Getting %s\r\n\r\n", path) + resp, err := http.Get(path) + if err != nil { + fmt.Printf("%s\r\n", err.Error()) + time.Sleep(10 * time.Second) + continue + } + + fmt.Printf("%s %s\r\n", resp.Proto, resp.Status) + for k, v := range resp.Header { + fmt.Printf("%s: %s\r\n", k, strings.Join(v, " ")) + } + fmt.Printf("\r\n") + + body, err := io.ReadAll(resp.Body) + println(string(body)) + resp.Body.Close() + + cnt++ + fmt.Printf("-------- %d --------\r\n", cnt) + time.Sleep(10 * time.Second) + } +} + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} diff --git a/examples/net/http-get/rtl8720dn.go b/examples/net/http-get/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/http-get/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/http-get/wifinina.go b/examples/net/http-get/wifinina.go new file mode 100644 index 000000000..658492e71 --- /dev/null +++ b/examples/net/http-get/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/http-head/main.go b/examples/net/http-head/main.go new file mode 100644 index 000000000..641f7c227 --- /dev/null +++ b/examples/net/http-head/main.go @@ -0,0 +1,57 @@ +// This example gets an URL using http.Head(). URL scheme can be http or https. +// +// Note: It may be necessary to increase the stack size when using "net/http". +// Use the -stack-size=4KB command line option. +// +// Some targets (Arduino Nano33 IoT) don't have enough SRAM to run http.Head(). +// Use the following for those targets: +// +// examples/net/webclient (for HTTP) +// examples/net/tlsclient (for HTTPS) + +package main + +import ( + "bytes" + "fmt" + "log" + "machine" + "net/http" + "time" +) + +var ( + ssid string + pass string + url string = "https://httpbin.org" +) + +func main() { + + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + resp, err := http.Head(url) + if err != nil { + log.Fatal(err) + } + defer resp.Body.Close() + + var buf bytes.Buffer + if err := resp.Write(&buf); err != nil { + log.Fatal(err) + } + fmt.Println(string(buf.Bytes())) + + NetDisconnect() +} + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} diff --git a/examples/net/http-head/rtl8720dn.go b/examples/net/http-head/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/http-head/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/http-head/wifinina.go b/examples/net/http-head/wifinina.go new file mode 100644 index 000000000..658492e71 --- /dev/null +++ b/examples/net/http-head/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/http-post/main.go b/examples/net/http-post/main.go new file mode 100644 index 000000000..79d2134d7 --- /dev/null +++ b/examples/net/http-post/main.go @@ -0,0 +1,61 @@ +// This example posts an URL using http.Post(). URL scheme can be http or https. +// +// Note: It may be necessary to increase the stack size when using "net/http". +// Use the -stack-size=4KB command line option. +// +// Some targets (Arduino Nano33 IoT) don't have enough SRAM to run http.Post(). +// Use the following for those targets: +// +// examples/net/webclient (for HTTP) +// examples/net/tlsclient (for HTTPS) + +package main + +import ( + "bytes" + "fmt" + "io" + "log" + "machine" + "net/http" + "time" +) + +var ( + ssid string + pass string +) + +func main() { + + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + path := "https://httpbin.org/post" + data := []byte("{\"name\":\"John Doe\",\"occupation\":\"gardener\"}") + + resp, err := http.Post(path, "application/json", bytes.NewBuffer(data)) + if err != nil { + log.Fatal(err) + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Fatal(err) + } + + fmt.Println(string(body)) + + NetDisconnect() +} + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} diff --git a/examples/net/http-post/rtl8720dn.go b/examples/net/http-post/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/http-post/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/http-post/wifinina.go b/examples/net/http-post/wifinina.go new file mode 100644 index 000000000..658492e71 --- /dev/null +++ b/examples/net/http-post/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/http-postform/main.go b/examples/net/http-postform/main.go new file mode 100644 index 000000000..a88d560dd --- /dev/null +++ b/examples/net/http-postform/main.go @@ -0,0 +1,62 @@ +// This example posts an URL using http.PostForm(). URL scheme can be http or https. +// +// Note: It may be necessary to increase the stack size when using "net/http". +// Use the -stack-size=4KB command line option. +// +// Some targets (Arduino Nano33 IoT) don't have enough SRAM to run +// http.PostForm(). Use the following for those targets: +// +// examples/net/webclient (for HTTP) +// examples/net/tlsclient (for HTTPS) + +package main + +import ( + "fmt" + "io" + "log" + "machine" + "net/http" + "net/url" + "time" +) + +var ( + ssid string + pass string +) + +func main() { + + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + path := "https://httpbin.org/post" + data := url.Values{ + "name": {"John Doe"}, + "occupation": {"gardener"}, + } + + resp, err := http.PostForm(path, data) + if err != nil { + log.Fatal(err) + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Fatal(err) + } + + fmt.Println(string(body)) +} + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} diff --git a/examples/net/http-postform/rtl8720dn.go b/examples/net/http-postform/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/http-postform/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/http-postform/wifinina.go b/examples/net/http-postform/wifinina.go new file mode 100644 index 000000000..658492e71 --- /dev/null +++ b/examples/net/http-postform/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/mqttclient/main.go b/examples/net/mqttclient/main.go new file mode 100644 index 000000000..cd7a0059f --- /dev/null +++ b/examples/net/mqttclient/main.go @@ -0,0 +1,77 @@ +// This example is a MQTT client. It sends machine.ReadTemparature() readings +// to the broker every second for 10 seconds. +// +// Note: It may be necessary to increase the stack size when using +// paho.mqtt.golang. Use the -stack-size=4KB command line option. + +package main + +import ( + "fmt" + "log" + "machine" + "time" + + mqtt "github.com/eclipse/paho.mqtt.golang" +) + +var ( + ssid string + pass string + broker string = "tcp://test.mosquitto.org:1883" +) + +var messagePubHandler mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) { + fmt.Printf("Message %s received on topic %s\n", msg.Payload(), msg.Topic()) +} + +var connectHandler mqtt.OnConnectHandler = func(client mqtt.Client) { + fmt.Println("Connected") +} + +var connectionLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, err error) { + fmt.Printf("Connection Lost: %s\n", err.Error()) +} + +func main() { + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + options := mqtt.NewClientOptions() + options.AddBroker(broker) + options.SetClientID("tinygo_mqtt_example") + options.SetDefaultPublishHandler(messagePubHandler) + options.OnConnect = connectHandler + options.OnConnectionLost = connectionLostHandler + + client := mqtt.NewClient(options) + token := client.Connect() + if token.Wait() && token.Error() != nil { + panic(token.Error()) + } + + topic := "cpu/freq" + token = client.Subscribe(topic, 1, nil) + token.Wait() + fmt.Printf("Subscribed to topic %s\n", topic) + + for i := 0; i < 10; i++ { + freq := float32(machine.CPUFrequency()) / 1000000 + payload := fmt.Sprintf("%.02fMhz", freq) + token = client.Publish(topic, 0, false, payload) + token.Wait() + time.Sleep(time.Second) + } + + client.Disconnect(100) +} + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} diff --git a/examples/net/mqttclient/rtl8720dn.go b/examples/net/mqttclient/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/mqttclient/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/mqttclient/wifinina.go b/examples/net/mqttclient/wifinina.go new file mode 100644 index 000000000..658492e71 --- /dev/null +++ b/examples/net/mqttclient/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/ntpclient/main.go b/examples/net/ntpclient/main.go new file mode 100644 index 000000000..1d74feb00 --- /dev/null +++ b/examples/net/ntpclient/main.go @@ -0,0 +1,104 @@ +// This is an example of an NTP client. +// +// 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. + +package main + +import ( + "fmt" + "io" + "log" + "machine" + "net" + "runtime" + "time" +) + +var ( + ssid string + pass string + // IP address of the server aka "hub". Replace with your own info. + ntpHost string = "0.pool.ntp.org:123" +) + +const NTP_PACKET_SIZE = 48 + +var response = make([]byte, NTP_PACKET_SIZE) + +func main() { + + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + conn, err := net.Dial("udp", ntpHost) + if err != nil { + log.Fatal(err) + } + + println("Requesting NTP time...") + + t, err := getCurrentTime(conn) + if err != nil { + log.Fatal(fmt.Sprintf("Error getting current time: %v", err)) + } else { + message("NTP time: %v", t) + } + + conn.Close() + NetDisconnect() + + runtime.AdjustTimeOffset(-1 * int64(time.Since(t))) + + for { + message("Current time: %v", time.Now()) + time.Sleep(time.Minute) + } +} + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} + +func getCurrentTime(conn net.Conn) (time.Time, error) { + if err := sendNTPpacket(conn); err != nil { + return time.Time{}, err + } + + n, err := conn.Read(response) + if err != nil && err != io.EOF { + return time.Time{}, err + } + if n != NTP_PACKET_SIZE { + return time.Time{}, fmt.Errorf("expected NTP packet size of %d: %d", NTP_PACKET_SIZE, n) + } + + return parseNTPpacket(response), nil +} + +func sendNTPpacket(conn net.Conn) error { + var request = [48]byte{ + 0xe3, + } + + _, err := conn.Write(request[:]) + return err +} + +func parseNTPpacket(r []byte) time.Time { + // the timestamp starts at byte 40 of the received packet and is four bytes, + // this is NTP time (seconds since Jan 1 1900): + t := uint32(r[40])<<24 | uint32(r[41])<<16 | uint32(r[42])<<8 | uint32(r[43]) + const seventyYears = 2208988800 + return time.Unix(int64(t-seventyYears), 0) +} + +func message(format string, args ...interface{}) { + println(fmt.Sprintf(format, args...), "\r") +} diff --git a/examples/net/ntpclient/rtl8720dn.go b/examples/net/ntpclient/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/ntpclient/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/ntpclient/wifinina.go b/examples/net/ntpclient/wifinina.go new file mode 100644 index 000000000..07859b1a0 --- /dev/null +++ b/examples/net/ntpclient/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal arduino_nano33 nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/wifi/tcpclient/main.go b/examples/net/tcpclient/main.go similarity index 51% rename from examples/wifi/tcpclient/main.go rename to examples/net/tcpclient/main.go index 74eb1916f..2e603d879 100644 --- a/examples/wifi/tcpclient/main.go +++ b/examples/net/tcpclient/main.go @@ -1,52 +1,49 @@ -// This example opens a TCP connection and sends some data, -// for the purpose of testing speed and connectivity. +// This example opens a TCP connection and sends some data, for the purpose of +// testing speed and connectivity. // // You can open a server to accept connections from this program using: // -// nc -w 5 -lk 8080 +// nc -lk 8080 + package main import ( "bytes" "fmt" + "log" + "machine" + "net" "time" - - "tinygo.org/x/drivers/net" ) var ( - // access point info ssid string pass string + addr string = "10.0.0.100:8080" ) -// IP address of the server aka "hub". Replace with your own info. -const serverIP = "" - var buf = &bytes.Buffer{} func main() { - initAdaptor() - connectToAP() + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } for { sendBatch() time.Sleep(500 * time.Millisecond) } - println("Done.") } func sendBatch() { // make TCP connection - ip := net.ParseIP(serverIP) - raddr := &net.TCPAddr{IP: ip, Port: 8080} - laddr := &net.TCPAddr{Port: 8080} - message("---------------\r\nDialing TCP connection") - conn, err := net.DialTCP("tcp", laddr, raddr) - for ; err != nil; conn, err = net.DialTCP("tcp", laddr, raddr) { + conn, err := net.Dial("tcp", addr) + for ; err != nil; conn, err = net.Dial("tcp", addr) { message(err.Error()) time.Sleep(5 * time.Second) } @@ -65,7 +62,7 @@ func sendBatch() { "\r---------------------------- i == ", i, " ----------------------------") if w, err = conn.Write(buf.Bytes()); err != nil { println("error:", err.Error(), "\r") - continue + break } n += w } @@ -79,34 +76,17 @@ func sendBatch() { println("error:", err.Error(), "\r") } - // Right now this code is never reached. Need a way to trigger it... println("Disconnecting TCP...") conn.Close() } -// connect to access point -func connectToAP() { - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { // error connecting to AP - for { - println(err) - time.Sleep(1 * time.Second) - } - } - - println("Connected.") - - time.Sleep(2 * time.Second) - ip, err := adaptor.GetClientIP() - for ; err != nil; ip, err = adaptor.GetClientIP() { - message(err.Error()) - time.Sleep(1 * time.Second) - } - message(ip) -} - func message(msg string) { println(msg, "\r") } + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} diff --git a/examples/net/tcpclient/rtl8720dn.go b/examples/net/tcpclient/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/tcpclient/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/tcpclient/wifinina.go b/examples/net/tcpclient/wifinina.go new file mode 100644 index 000000000..07859b1a0 --- /dev/null +++ b/examples/net/tcpclient/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal arduino_nano33 nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/tcpecho/main.go b/examples/net/tcpecho/main.go new file mode 100644 index 000000000..6ffe81d57 --- /dev/null +++ b/examples/net/tcpecho/main.go @@ -0,0 +1,56 @@ +// This example listens on port :8080 for client connections. Bytes +// received from the client are echo'ed back to the client. Multiple +// clients can connect as the same time, each consuming a client socket, +// and being serviced by it's own go func. +// +// Example test using nc as client to copy file: +// +// $ nc 10.0.0.2 8080 copy ; cmp file copy + +package main + +import ( + "io" + "log" + "net" + "time" +) + +var ( + ssid string + pass string + port string = ":8080" +) + +var buf [1024]byte + +func echo(conn net.Conn) { + defer conn.Close() + _, err := io.CopyBuffer(conn, conn, buf[:]) + if err != nil && err != io.EOF { + log.Fatal(err.Error()) + } +} + +func main() { + + time.Sleep(time.Second) + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + l, err := net.Listen("tcp", port) + if err != nil { + log.Fatal(err.Error()) + } + defer l.Close() + + for { + conn, err := l.Accept() + if err != nil { + log.Fatal(err.Error()) + } + go echo(conn) + } +} diff --git a/examples/net/tcpecho/rtl8720dn.go b/examples/net/tcpecho/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/tcpecho/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/tcpecho/wifinina.go b/examples/net/tcpecho/wifinina.go new file mode 100644 index 000000000..07859b1a0 --- /dev/null +++ b/examples/net/tcpecho/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal arduino_nano33 nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/tlsclient/main.go b/examples/net/tlsclient/main.go new file mode 100644 index 000000000..ec8ae0630 --- /dev/null +++ b/examples/net/tlsclient/main.go @@ -0,0 +1,95 @@ +// This example uses TLS to send an HTTPS request to retrieve a webpage +// +// You shall see "strict-transport-security" header in the response, +// this confirms communication is indeed over HTTPS +// +// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security + +package main + +import ( + "bufio" + "crypto/tls" + "fmt" + "io" + "log" + "machine" + "net" + "strings" + "time" +) + +var ( + ssid string + pass string + // HTTPS server address to hit with a GET / request + address string = "httpbin.org:443" +) + +var conn net.Conn + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} + +func check(err error) { + if err != nil { + println("Hit an error:", err.Error()) + panic("BYE") + } +} + +func readResponse() { + r := bufio.NewReader(conn) + resp, err := io.ReadAll(r) + check(err) + println(string(resp)) +} + +func closeConnection() { + conn.Close() +} + +func dialConnection() { + var err error + + println("\r\n---------------\r\nDialing TLS connection") + conn, err = tls.Dial("tcp", address, nil) + for ; err != nil; conn, err = tls.Dial("tcp", address, nil) { + println("Connection failed:", err.Error()) + time.Sleep(5 * time.Second) + } + println("Connected!\r") +} + +func makeRequest() { + print("Sending HTTPS request...") + w := bufio.NewWriter(conn) + fmt.Fprintln(w, "GET /get HTTP/1.1") + fmt.Fprintln(w, "Host:", strings.Split(address, ":")[0]) + fmt.Fprintln(w, "User-Agent: TinyGo") + fmt.Fprintln(w, "Connection: close") + fmt.Fprintln(w) + check(w.Flush()) + println("Sent!\r\n\r") +} + +func main() { + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + for i := 0; ; i++ { + dialConnection() + makeRequest() + readResponse() + closeConnection() + println("--------", i, "--------\r\n") + time.Sleep(10 * time.Second) + } +} diff --git a/examples/net/tlsclient/rtl8720dn.go b/examples/net/tlsclient/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/tlsclient/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/tlsclient/wifinina.go b/examples/net/tlsclient/wifinina.go new file mode 100644 index 000000000..07859b1a0 --- /dev/null +++ b/examples/net/tlsclient/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal arduino_nano33 nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/webclient-tinyterm/main.go b/examples/net/webclient-tinyterm/main.go new file mode 100644 index 000000000..697101282 --- /dev/null +++ b/examples/net/webclient-tinyterm/main.go @@ -0,0 +1,142 @@ +// This example runs on wioterminal. It gets an URL using http.Get() and +// displays the output on the wioterminal LCD screen. +// +// Note: It may be necessary to increase the stack size when using "net/http". +// Use the -stack-size=4KB command line option. + +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "fmt" + "image/color" + "io" + "log" + "machine" + "net/http" + "net/url" + "strings" + "time" + + "tinygo.org/x/drivers/ili9341" + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" + "tinygo.org/x/tinyfont/proggy" + "tinygo.org/x/tinyterm" +) + +var ( + ssid string + pass string +) + +var ( + netcfg = rtl8720dn.Config{ + Ssid: ssid, + Passphrase: pass, + En: machine.RTL8720D_CHIP_PU, + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + } + + dev = rtl8720dn.New(&netcfg) + + display = ili9341.NewSPI( + machine.SPI3, + machine.LCD_DC, + machine.LCD_SS_PIN, + machine.LCD_RESET, + ) + + backlight = machine.LCD_BACKLIGHT + + terminal = tinyterm.NewTerminal(display) + + black = color.RGBA{0, 0, 0, 255} + white = color.RGBA{255, 255, 255, 255} + red = color.RGBA{255, 0, 0, 255} + blue = color.RGBA{0, 0, 255, 255} + green = color.RGBA{0, 255, 0, 255} + + font = &proggy.TinySZ8pt7b +) + +func notify(e netdev.Event) { + switch e { + case netdev.EventNetUp: + fmt.Println("Wifi connection UP") + fmt.Fprintf(terminal, "Wifi connection UP") + case netdev.EventNetDown: + fmt.Println("Wifi connection DOWN") + fmt.Fprintf(terminal, "Wifi connection DOWN") + } +} + +func main() { + + machine.SPI3.Configure(machine.SPIConfig{ + SCK: machine.LCD_SCK_PIN, + SDO: machine.LCD_SDO_PIN, + SDI: machine.LCD_SDI_PIN, + Frequency: 40000000, + }) + + display.Configure(ili9341.Config{}) + display.FillScreen(black) + + backlight.Configure(machine.PinConfig{machine.PinOutput}) + backlight.High() + + terminal.Configure(&tinyterm.Config{ + Font: font, + FontHeight: 10, + FontOffset: 6, + }) + + fmt.Fprintf(terminal, "Connecting to %s...\r\n", ssid) + + dev.NetNotify(notify) + netdev.Use(dev) + + if err := dev.NetConnect(); err != nil { + log.Fatal(err) + } + + name := "John Doe" + occupation := "gardener" + + params := "name=" + url.QueryEscape(name) + "&" + + "occupation=" + url.QueryEscape(occupation) + + path := fmt.Sprintf("https://httpbin.org/get?%s", params) + + cnt := 0 + for { + fmt.Fprintf(terminal, "Getting %s\r\n\r\n", path) + resp, err := http.Get(path) + if err != nil { + fmt.Fprintf(terminal, "%s\r\n", err.Error()) + time.Sleep(10 * time.Second) + continue + } + + fmt.Fprintf(terminal, "%s %s\r\n", resp.Proto, resp.Status) + for k, v := range resp.Header { + fmt.Fprintf(terminal, "%s: %s\r\n", k, strings.Join(v, " ")) + } + fmt.Fprintf(terminal, "\r\n") + + body, err := io.ReadAll(resp.Body) + fmt.Fprintf(terminal, string(body)) + resp.Body.Close() + + cnt++ + fmt.Fprintf(terminal, "-------- %d --------\r\n", cnt) + time.Sleep(10 * time.Second) + } +} diff --git a/examples/net/webclient/main.go b/examples/net/webclient/main.go new file mode 100644 index 000000000..05b6a63dc --- /dev/null +++ b/examples/net/webclient/main.go @@ -0,0 +1,106 @@ +// This example uses TCP to send an HTTP request to retrieve a webpage. The +// HTTP request is hand-rolled to avoid the overhead of using http.Get() from +// the "net/http" package. See example/net/http-get for the full http.Get() +// functionality. +// +// Example HTTP server: +// --------------------------------------------------------------------------- +// package main +// +// import "net/http" +// +// func main() { +// http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { +// w.Write([]byte("hello")) +// }) +// http.ListenAndServe(":8080", nil) +// } +// --------------------------------------------------------------------------- + +package main + +import ( + "bufio" + "fmt" + "io" + "log" + "machine" + "net" + "strings" + "time" +) + +var ( + ssid string + pass string + // HTTP server address to hit with a GET / request + address string = "10.0.0.100:8080" +) + +var conn net.Conn + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} + +func dialConnection() { + var err error + + println("\r\n---------------\r\nDialing TCP connection") + conn, err = net.Dial("tcp", address) + for ; err != nil; conn, err = net.Dial("tcp", address) { + println("Connection failed:", err.Error()) + time.Sleep(5 * time.Second) + } + println("Connected!\r") +} + +func check(err error) { + if err != nil { + println("Hit an error:", err.Error()) + panic("BYE") + } +} + +func makeRequest() { + println("Sending HTTP request...") + w := bufio.NewWriter(conn) + fmt.Fprintln(w, "GET / HTTP/1.1") + fmt.Fprintln(w, "Host:", strings.Split(address, ":")[0]) + fmt.Fprintln(w, "User-Agent: TinyGo") + fmt.Fprintln(w, "Connection: close") + fmt.Fprintln(w) + check(w.Flush()) + println("Sent!\r\n\r") +} + +func readResponse() { + r := bufio.NewReader(conn) + resp, err := io.ReadAll(r) + check(err) + println(string(resp)) +} + +func closeConnection() { + conn.Close() +} + +func main() { + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + for i := 0; ; i++ { + dialConnection() + makeRequest() + readResponse() + closeConnection() + println("--------", i, "--------\r\n") + time.Sleep(10 * time.Second) + } +} diff --git a/examples/net/webclient/rtl8720dn.go b/examples/net/webclient/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/webclient/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/webclient/wifinina.go b/examples/net/webclient/wifinina.go new file mode 100644 index 000000000..07859b1a0 --- /dev/null +++ b/examples/net/webclient/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal arduino_nano33 nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/wifinina/webserver/main.go b/examples/net/webserver/main.go similarity index 77% rename from examples/wifinina/webserver/main.go rename to examples/net/webserver/main.go index f8bf65eba..9a32cf444 100644 --- a/examples/wifinina/webserver/main.go +++ b/examples/net/webserver/main.go @@ -1,82 +1,41 @@ +// This example listens on port :80 serving a web page. Multiple clients +// may connect and be serviced at the same time. IPv4 only. HTTP only. +// +// $ curl http://10.0.0.2 +// +// Note: It may be necessary to increase the stack size when using "net/http". +// Use the -stack-size=4KB command line option. + package main import ( "fmt" + "log" "machine" + "net/http" "strconv" "time" - - "tinygo.org/x/drivers/net/http" - "tinygo.org/x/drivers/wifinina" ) -// You can override the settings with the init() in another source code: -// -// func init() { -// ssid = "your-ssid" -// pass = "your-password" -// } -// -// Or use -ldflags option on tinygo command to set at compile-time: -// -// tinygo flash ... -ldflags '-X "main.ssid=xxx" -X "main.pass=xxx"' ... -// - var ( ssid string pass string + port string = ":80" ) var led = machine.LED func main() { - led.Configure(machine.PinConfig{Mode: machine.PinOutput}) - - err := run() - for err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} -func run() error { - - spi := machine.NINA_SPI - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - adaptor := wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() - - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { - return err - } + // wait a bit for serial + time.Sleep(time.Second) - println("Connected.") + led.Configure(machine.PinConfig{Mode: machine.PinOutput}) - time.Sleep(2 * time.Second) - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - return err + if err := NetConnect(); err != nil { + log.Fatal(err) } - fmt.Printf("IP Address : %s\r\n", ip) - fmt.Printf("Mask : %s\r\n", subnet) - fmt.Printf("Gateway : %s\r\n", gateway) - - http.UseDriver(adaptor) - http.HandleFunc("/", root) http.HandleFunc("/hello", hello) http.HandleFunc("/cnt", cnt) @@ -84,7 +43,11 @@ func run() error { http.HandleFunc("/off", LED_OFF) http.HandleFunc("/on", LED_ON) - return http.ListenAndServe(":80", nil) + err := http.ListenAndServe(port, nil) + for err != nil { + fmt.Printf("error: %s\r\n", err.Error()) + time.Sleep(5 * time.Second) + } } func root(w http.ResponseWriter, r *http.Request) { @@ -159,7 +122,7 @@ func root(w http.ResponseWriter, r *http.Request) {

- `, access) +`, access) } func sixlines(w http.ResponseWriter, r *http.Request) { diff --git a/examples/net/webserver/rtl8720dn.go b/examples/net/webserver/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/webserver/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/webserver/wifinina.go b/examples/net/webserver/wifinina.go new file mode 100644 index 000000000..658492e71 --- /dev/null +++ b/examples/net/webserver/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/websocket/dial/main.go b/examples/net/websocket/dial/main.go new file mode 100644 index 000000000..72be95749 --- /dev/null +++ b/examples/net/websocket/dial/main.go @@ -0,0 +1,84 @@ +// This example is a websocket client. It connects to a websocket server +// which echos messages back to the client. For server, see +// +// https://pkg.go.dev/golang.org/x/net/websocket#example-Handler +// +// 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. + +package main + +import ( + // "crypto/rand" + "fmt" + "log" + "machine" + "time" + + "golang.org/x/net/websocket" +) + +var ( + ssid string + pass string + url string = "ws://10.0.0.100:8080/echo" +) + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} + +/* +func init() { + rand.Reader = &reader{} +} + +type reader struct {} + +func (r *reader) Read(b []byte) (n int, err error) { + if len(b) == 0 { + return + } + + var randomByte uint32 + for i := range b { + if i%4 == 0 { + randomByte, err = machine.GetRNG() + if err != nil { + return n, err + } + } else { + randomByte >>= 8 + } + b[i] = byte(randomByte) + } + + return len(b), nil +} +*/ + +func main() { + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + origin := "http://localhost/" + ws, err := websocket.Dial(url, "", origin) + if err != nil { + log.Fatal(err) + } + if _, err := ws.Write([]byte("hello, world!\n")); err != nil { + log.Fatal(err) + } + var msg = make([]byte, 512) + var n int + if n, err = ws.Read(msg); err != nil { + log.Fatal(err) + } + fmt.Printf("Received: %s", msg[:n]) +} diff --git a/examples/net/websocket/dial/rtl8720dn.go b/examples/net/websocket/dial/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/websocket/dial/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/websocket/dial/wifinina.go b/examples/net/websocket/dial/wifinina.go new file mode 100644 index 000000000..658492e71 --- /dev/null +++ b/examples/net/websocket/dial/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/websocket/handler/main.go b/examples/net/websocket/handler/main.go new file mode 100644 index 000000000..5a50e9b6d --- /dev/null +++ b/examples/net/websocket/handler/main.go @@ -0,0 +1,52 @@ +// This example is a websocket server. It listens for websocket clients +// to connect and echos messages back to the client. For client, see +// +// https://pkg.go.dev/golang.org/x/net/websocket#example-Dial +// +// 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. + +package main + +import ( + "io" + "log" + "machine" + "net/http" + "time" + + "golang.org/x/net/websocket" +) + +var ( + ssid string + pass string + port string = ":8080" +) + +// Echo the data received on the WebSocket. +func EchoServer(ws *websocket.Conn) { + io.Copy(ws, ws) +} + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} + +// This example demonstrates a trivial echo server. +func main() { + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + http.Handle("/echo", websocket.Handler(EchoServer)) + err := http.ListenAndServe(port, nil) + if err != nil { + panic("ListenAndServe: " + err.Error()) + } +} diff --git a/examples/net/websocket/handler/rtl8720dn.go b/examples/net/websocket/handler/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/websocket/handler/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/websocket/handler/wifinina.go b/examples/net/websocket/handler/wifinina.go new file mode 100644 index 000000000..658492e71 --- /dev/null +++ b/examples/net/websocket/handler/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/webstatic/images/tinygo-logo.png b/examples/net/webstatic/images/tinygo-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d362856fd97eb03e150096cce163d1ef2a116c04 GIT binary patch literal 30898 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 + + TinyGo - A Go Compiler For Small Places + + + +

TinyGo - A Go Compiler For Small Places

+ + + diff --git a/examples/net/webstatic/main.go b/examples/net/webstatic/main.go new file mode 100644 index 000000000..d2c522bc6 --- /dev/null +++ b/examples/net/webstatic/main.go @@ -0,0 +1,38 @@ +// This example is an HTTP server serving up a static file system +// +// Note: It may be necessary to increase the stack size when using "net/http". +// Use the -stack-size=4KB command line option. + +package main + +import ( + "embed" + "log" + "net/http" + "time" +) + +var ( + ssid string + pass string + port string = ":80" +) + +//go:embed index.html main.go images +var fs embed.FS + +func main() { + // wait a bit for console + time.Sleep(time.Second) + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + hfs := http.FileServer(http.FS(fs)) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + hfs.ServeHTTP(w, r) + }) + + log.Fatal(http.ListenAndServe(port, nil)) +} diff --git a/examples/net/webstatic/rtl8720dn.go b/examples/net/webstatic/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/webstatic/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/webstatic/wifinina.go b/examples/net/webstatic/wifinina.go new file mode 100644 index 000000000..658492e71 --- /dev/null +++ b/examples/net/webstatic/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/rtl8720dn/mqttclient/main.go b/examples/rtl8720dn/mqttclient/main.go deleted file mode 100644 index f4e8d8484..000000000 --- a/examples/rtl8720dn/mqttclient/main.go +++ /dev/null @@ -1,131 +0,0 @@ -// This is a sensor station that uses a RTL8720DN running on the device UART2. -// It creates an MQTT connection that publishes a message every second -// to an MQTT broker. -// -// In other words: -// Your computer <--> USB-CDC <--> MCU <--> UART2 <--> RTL8720DN <--> Internet <--> MQTT broker. -// -// You must install the Paho MQTT package to build this program: -// -// go get -u github.com/eclipse/paho.mqtt.golang -// -// You can check that mqttpub is running successfully with the following command. -// -// mosquitto_sub -h test.mosquitto.org -t tinygo -package main - -import ( - "machine" - - "fmt" - "math/rand" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/net/mqtt" - "tinygo.org/x/drivers/rtl8720dn" -) - -// You can override the setting with the init() in another source code. -// func init() { -// ssid = "your-ssid" -// pass = "your-password" -// debug = true -// server = "tinygo.org" -// } - -var ( - ssid string - pass string - server string = "tcp://test.mosquitto.org:1883" - debug = false -) - -var buf [0x400]byte - -var lastRequestTime time.Time -var conn net.Conn -var adaptor *rtl8720dn.Driver - -func main() { - err := run() - for err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} - -var ( - topic = "tinygo" -) - -func run() error { - // change the UART and pins as needed for platforms other than the WioTerminal. - adaptor = rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) - adaptor.Debug(debug) - adaptor.Configure() - - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { - return err - } - - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - return err - } - fmt.Printf("IP Address : %s\r\n", ip) - fmt.Printf("Mask : %s\r\n", subnet) - fmt.Printf("Gateway : %s\r\n", gateway) - - rand.Seed(time.Now().UnixNano()) - - opts := mqtt.NewClientOptions() - opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10)) - - println("Connectng to MQTT...") - cl := mqtt.NewClient(opts) - if token := cl.Connect(); token.Wait() && token.Error() != nil { - failMessage(token.Error().Error()) - } - - for i := 0; ; i++ { - println("Publishing MQTT message...") - data := []byte(fmt.Sprintf(`{"e":[{"n":"hello %d","v":101}]}`, i)) - token := cl.Publish(topic, 0, false, data) - token.Wait() - if err := token.Error(); err != nil { - return err - } - time.Sleep(100 * time.Millisecond) - } - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting MQTT...") - cl.Disconnect(100) - - println("Done.") - - return nil -} - -// Returns an int >= min, < max -func randomInt(min, max int) int { - return min + rand.Intn(max-min) -} - -// Generate a random string of A-Z chars with len = l -func randomString(len int) string { - bytes := make([]byte, len) - for i := 0; i < len; i++ { - bytes[i] = byte(randomInt(65, 90)) - } - return string(bytes) -} - -func failMessage(msg string) { - for { - println(msg) - time.Sleep(1 * time.Second) - } -} diff --git a/examples/rtl8720dn/mqttsub/main.go b/examples/rtl8720dn/mqttsub/main.go deleted file mode 100644 index 6c53313df..000000000 --- a/examples/rtl8720dn/mqttsub/main.go +++ /dev/null @@ -1,154 +0,0 @@ -// This is a sensor station that uses a RTL8720DN running on the device UART2. -// It creates an MQTT connection that publishes a message every second -// to an MQTT broker. -// -// In other words: -// Your computer <--> USB-CDC <--> MCU <--> UART2 <--> RTL8720DN <--> Internet <--> MQTT broker. -// -// You must also install the Paho MQTT package to build this program: -// -// go get -u github.com/eclipse/paho.mqtt.golang -// -// You can check that mqttpub/mqttsub is running successfully with the following command. -// -// mosquitto_sub -h test.mosquitto.org -t tinygo/tx -// mosquitto_pub -h test.mosquitto.org -t tinygo/rx -m "hello world" -package main - -import ( - "machine" - - "fmt" - "math/rand" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/net/mqtt" - "tinygo.org/x/drivers/rtl8720dn" -) - -// You can override the setting with the init() in another source code. -// func init() { -// ssid = "your-ssid" -// pass = "your-password" -// debug = true -// server = "tinygo.org" -// } - -var ( - ssid string - pass string - server string = "tcp://test.mosquitto.org:1883" - debug = false -) - -var buf [0x400]byte - -var lastRequestTime time.Time -var conn net.Conn -var adaptor *rtl8720dn.Driver - -func main() { - err := run() - for err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} - -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - cl mqtt.Client - topicTx = "tinygo/tx" - topicRx = "tinygo/rx" -) - -func subHandler(client mqtt.Client, msg mqtt.Message) { - fmt.Printf("[%s] ", msg.Topic()) - fmt.Printf("%s\r\n", msg.Payload()) -} - -func run() error { - // change the UART and pins as needed for platforms other than the WioTerminal. - adaptor = rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) - adaptor.Debug(debug) - adaptor.Configure() - - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { - return err - } - - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - return err - } - fmt.Printf("IP Address : %s\r\n", ip) - fmt.Printf("Mask : %s\r\n", subnet) - fmt.Printf("Gateway : %s\r\n", gateway) - - rand.Seed(time.Now().UnixNano()) - - opts := mqtt.NewClientOptions() - opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10)) - - println("Connecting to MQTT broker at", server) - cl = mqtt.NewClient(opts) - if token := cl.Connect(); token.Wait() && token.Error() != nil { - failMessage(token.Error().Error()) - } - - // subscribe - token := cl.Subscribe(topicRx, 0, subHandler) - token.Wait() - if token.Error() != nil { - failMessage(token.Error().Error()) - } - - go publishing() - - select {} - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting MQTT...") - cl.Disconnect(100) - - println("Done.") - - return nil -} - -func publishing() { - for i := 0; ; i++ { - println("Publishing MQTT message...") - data := []byte(fmt.Sprintf(`{"e":[{"n":"hello %d","v":101}]}`, i)) - token := cl.Publish(topicTx, 0, false, data) - token.Wait() - if token.Error() != nil { - println(token.Error().Error()) - } - - time.Sleep(20 * 100 * time.Millisecond) - } -} - -// Returns an int >= min, < max -func randomInt(min, max int) int { - return min + rand.Intn(max-min) -} - -// Generate a random string of A-Z chars with len = l -func randomString(len int) string { - bytes := make([]byte, len) - for i := 0; i < len; i++ { - bytes[i] = byte(randomInt(65, 90)) - } - return string(bytes) -} - -func failMessage(msg string) { - for { - println(msg) - time.Sleep(1 * time.Second) - } -} diff --git a/examples/rtl8720dn/ntpclient/main.go b/examples/rtl8720dn/ntpclient/main.go deleted file mode 100644 index 7149afcb9..000000000 --- a/examples/rtl8720dn/ntpclient/main.go +++ /dev/null @@ -1,143 +0,0 @@ -// This is an example of using the rtl8720dn driver to implement a NTP client. -// It creates a UDP connection to request the current time and parse the -// response from a NTP server. -package main - -import ( - "machine" - - "errors" - "fmt" - "runtime" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/rtl8720dn" -) - -// IP address of the server aka "hub". Replace with your own info. -// You can override the setting with the init() in another source code. -// func init() { -// ssid = "your-ssid" -// pass = "your-password" -// ntpHost = "129.6.15.29" -// debug = true -// } - -var ( - ssid string - pass string - ntpHost = "129.6.15.29" - debug = false -) - -const NTP_PACKET_SIZE = 48 - -var b = make([]byte, NTP_PACKET_SIZE) - -func main() { - err := run() - for err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} - -func run() error { - adaptor := rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) - adaptor.Debug(debug) - adaptor.Configure() - - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { - return err - } - - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - return err - } - fmt.Printf("IP Address : %s\r\n", ip) - fmt.Printf("Mask : %s\r\n", subnet) - fmt.Printf("Gateway : %s\r\n", gateway) - - // now make UDP connection - hip := net.ParseIP(ntpHost) - raddr := &net.UDPAddr{IP: hip, Port: 123} - laddr := &net.UDPAddr{Port: 2390} - conn, err := net.DialUDP("udp", laddr, raddr) - if err != nil { - return err - } - - for { - // send data - println("Requesting NTP time...") - t, err := getCurrentTime(conn) - if err != nil { - message("Error getting current time: %v", err) - } else { - message("NTP time: %v", t) - } - runtime.AdjustTimeOffset(-1 * int64(time.Since(t))) - for i := 0; i < 10; i++ { - message("Current time: %v", time.Now()) - time.Sleep(1 * time.Second) - } - } - -} - -func getCurrentTime(conn *net.UDPSerialConn) (time.Time, error) { - if err := sendNTPpacket(conn); err != nil { - return time.Time{}, err - } - clearBuffer() - for now := time.Now(); time.Since(now) < time.Second; { - time.Sleep(5 * time.Millisecond) - if n, err := conn.Read(b); err != nil { - return time.Time{}, fmt.Errorf("error reading UDP packet: %w", err) - } else if n == 0 { - continue // no packet received yet - } else if n != NTP_PACKET_SIZE { - return time.Time{}, fmt.Errorf("expected NTP packet size of %d: %d", NTP_PACKET_SIZE, n) - } - return parseNTPpacket(), nil - } - return time.Time{}, errors.New("no packet received after 1 second") -} - -func sendNTPpacket(conn *net.UDPSerialConn) error { - clearBuffer() - b[0] = 0b11100011 // LI, Version, Mode - b[1] = 0 // Stratum, or type of clock - b[2] = 6 // Polling Interval - b[3] = 0xEC // Peer Clock Precision - // 8 bytes of zero for Root Delay & Root Dispersion - b[12] = 49 - b[13] = 0x4E - b[14] = 49 - b[15] = 52 - if _, err := conn.Write(b); err != nil { - return err - } - return nil -} - -func parseNTPpacket() time.Time { - // the timestamp starts at byte 40 of the received packet and is four bytes, - // this is NTP time (seconds since Jan 1 1900): - t := uint32(b[40])<<24 | uint32(b[41])<<16 | uint32(b[42])<<8 | uint32(b[43]) - const seventyYears = 2208988800 - return time.Unix(int64(t-seventyYears), 0) -} - -func clearBuffer() { - for i := range b { - b[i] = 0 - } -} - -func message(format string, args ...interface{}) { - println(fmt.Sprintf(format, args...), "\r") -} diff --git a/examples/rtl8720dn/tcpclient/main.go b/examples/rtl8720dn/tcpclient/main.go deleted file mode 100644 index 650f41531..000000000 --- a/examples/rtl8720dn/tcpclient/main.go +++ /dev/null @@ -1,121 +0,0 @@ -// This example opens a TCP connection using a device with RTL8720DN firmware -// and sends some data, for the purpose of testing speed and connectivity. -// -// You can open a server to accept connections from this program using: -// -// nc -w 5 -lk 8080 -package main - -import ( - "machine" - - "bytes" - "fmt" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/rtl8720dn" -) - -// You can override the setting with the init() in another source code. -// func init() { -// ssid = "your-ssid" -// pass = "your-password" -// serverIP = "192.168.1.119" -// debug = true -// } - -var ( - ssid string - pass string - serverIP = "" - debug = false -) - -var buf = &bytes.Buffer{} - -func main() { - err := run() - for err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} - -func run() error { - adaptor := rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) - adaptor.Debug(debug) - adaptor.Configure() - - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { - return err - } - - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - return err - } - fmt.Printf("IP Address : %s\r\n", ip) - fmt.Printf("Mask : %s\r\n", subnet) - fmt.Printf("Gateway : %s\r\n", gateway) - - for { - sendBatch() - time.Sleep(500 * time.Millisecond) - } - println("Done.") - - return nil -} - -func sendBatch() { - - // make TCP connection - ip := net.ParseIP(serverIP) - raddr := &net.TCPAddr{IP: ip, Port: 8080} - laddr := &net.TCPAddr{Port: 8080} - - message("---------------\r\nDialing TCP connection") - conn, err := net.DialTCP("tcp", laddr, raddr) - for ; err != nil; conn, err = net.DialTCP("tcp", laddr, raddr) { - message(err.Error()) - time.Sleep(5 * time.Second) - } - - n := 0 - w := 0 - start := time.Now() - - // send data - message("Sending data") - - for i := 0; i < 1000; i++ { - buf.Reset() - fmt.Fprint(buf, - "\r---------------------------- i == ", i, " ----------------------------"+ - "\r---------------------------- i == ", i, " ----------------------------") - if w, err = conn.Write(buf.Bytes()); err != nil { - println("error:", err.Error(), "\r") - continue - } - n += w - } - - buf.Reset() - ms := time.Now().Sub(start).Milliseconds() - fmt.Fprint(buf, "\nWrote ", n, " bytes in ", ms, " ms\r\n") - message(buf.String()) - - if _, err := conn.Write(buf.Bytes()); err != nil { - println("error:", err.Error(), "\r") - } - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting TCP...") - conn.Close() -} - -func message(msg string) { - println(msg, "\r") -} diff --git a/examples/rtl8720dn/tlsclient/main.go b/examples/rtl8720dn/tlsclient/main.go deleted file mode 100644 index e86dd93c7..000000000 --- a/examples/rtl8720dn/tlsclient/main.go +++ /dev/null @@ -1,137 +0,0 @@ -package main - -import ( - "machine" - - "bufio" - "fmt" - "strings" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/net/http" - "tinygo.org/x/drivers/rtl8720dn" -) - -// You can override the setting with the init() in another source code. -// func init() { -// ssid = "your-ssid" -// pass = "your-password" -// debug = true -// url = "https://www.example.com" -// test_root_ca = "..." -// } - -var ( - ssid string - pass string - url string = "https://www.example.com" - debug = false -) - -// Set the test_root_ca created by the following command -// $ openssl s_client -showcerts -verify 5 -connect www.example.com:443 < /dev/null -var test_root_ca = `-----BEGIN CERTIFICATE----- -MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD -QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT -MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j -b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB -CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 -nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt -43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P -T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 -gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO -BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR -TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw -DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr -hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg -06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF -PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls -YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk -CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= ------END CERTIFICATE----- -` - -var buf [0x1000]byte - -var lastRequestTime time.Time -var conn net.Conn - -func main() { - err := run() - for err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} - -func run() error { - adaptor := rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) - adaptor.Debug(debug) - adaptor.Configure() - - adaptor.SetRootCA(&test_root_ca) - http.SetBuf(buf[:]) - - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { - return err - } - - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - return err - } - fmt.Printf("IP Address : %s\r\n", ip) - fmt.Printf("Mask : %s\r\n", subnet) - fmt.Printf("Gateway : %s\r\n", gateway) - - // You can send and receive cookies in the following way - // import "tinygo.org/x/drivers/net/http/cookiejar" - // jar, err := cookiejar.New(nil) - // if err != nil { - // return err - // } - // client := &http.Client{Jar: jar} - // http.DefaultClient = client - - cnt := 0 - for { - // Various examples are as follows - // - // -- Get - // resp, err := http.Get(url) - // - // -- Post - // body := `cnt=12` - // resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(body)) - // - // -- Post with JSON - // body := `{"msg": "hello"}` - // resp, err := http.Post(url, "application/json", strings.NewReader(body)) - - resp, err := http.Get(url) - if err != nil { - return err - } - - fmt.Printf("%s %s\r\n", resp.Proto, resp.Status) - for k, v := range resp.Header { - fmt.Printf("%s: %s\r\n", k, strings.Join(v, " ")) - } - fmt.Printf("\r\n") - - scanner := bufio.NewScanner(resp.Body) - for scanner.Scan() { - fmt.Printf("%s\r\n", scanner.Text()) - } - resp.Body.Close() - - cnt++ - fmt.Printf("-------- %d --------\r\n", cnt) - time.Sleep(10 * time.Second) - } -} diff --git a/examples/rtl8720dn/udpstation/main.go b/examples/rtl8720dn/udpstation/main.go deleted file mode 100644 index fead6c548..000000000 --- a/examples/rtl8720dn/udpstation/main.go +++ /dev/null @@ -1,91 +0,0 @@ -package main - -import ( - "machine" - - "fmt" - "strconv" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/net/http" - "tinygo.org/x/drivers/rtl8720dn" -) - -// IP address of the server aka "hub". Replace with your own info. -// You can override the setting with the init() in another source code. -// func init() { -// ssid = "your-ssid" -// pass = "your-password" -// hubIP = "192.168.1.118" -// debug = true -// } - -var ( - ssid string - pass string - hubIP = "" - debug = false -) - -var buf [0x400]byte - -func main() { - err := run() - for err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} - -func run() error { - adaptor := rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) - adaptor.Debug(debug) - adaptor.Configure() - - http.SetBuf(buf[:]) - - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { - return err - } - - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - return err - } - fmt.Printf("IP Address : %s\r\n", ip) - fmt.Printf("Mask : %s\r\n", subnet) - fmt.Printf("Gateway : %s\r\n", gateway) - - // now make UDP connection - hip := net.ParseIP(hubIP) - raddr := &net.UDPAddr{IP: hip, Port: 2222} - laddr := &net.UDPAddr{Port: 2222} - - println("Dialing UDP connection...") - conn, err := net.DialUDP("udp", laddr, raddr) - if err != nil { - return err - } - - for { - // send data - println("Sending data...") - for i := 0; i < 25; i++ { - conn.Write([]byte("hello " + strconv.Itoa(i) + "\r\n")) - } - time.Sleep(1000 * time.Millisecond) - } - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting UDP...") - conn.Close() - println("Done.") - - return nil -} - -func message(msg string) { - println(msg, "\r") -} diff --git a/examples/rtl8720dn/version/main.go b/examples/rtl8720dn/version/main.go deleted file mode 100644 index 7a02e2f20..000000000 --- a/examples/rtl8720dn/version/main.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "machine" - - "fmt" - "time" - - "tinygo.org/x/drivers/rtl8720dn" -) - -var ( - debug = false -) - -func main() { - err := run() - for err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} - -func run() error { - adaptor := rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) - adaptor.Debug(debug) - adaptor.Configure() - - ver, err := adaptor.Version() - if err != nil { - return nil - } - - for { - fmt.Printf("RTL8270DN Firmware Version: %s\r\n", ver) - time.Sleep(10 * time.Second) - } - - return nil -} diff --git a/examples/rtl8720dn/webclient-tinyterm/main.go b/examples/rtl8720dn/webclient-tinyterm/main.go deleted file mode 100644 index 060aaf7f4..000000000 --- a/examples/rtl8720dn/webclient-tinyterm/main.go +++ /dev/null @@ -1,162 +0,0 @@ -package main - -import ( - "machine" - - "bufio" - "fmt" - "image/color" - "strings" - "time" - - "tinygo.org/x/drivers/ili9341" - "tinygo.org/x/drivers/net/http" - "tinygo.org/x/drivers/rtl8720dn" - - "tinygo.org/x/tinyfont/proggy" - "tinygo.org/x/tinyterm" -) - -// You can override the setting with the init() in another source code. -// If debug is enabled, a serial connection is required. -// func init() { -// ssid = "your-ssid" -// pass = "your-password" -// debug = false // true -// server = "tinygo.org" -// } - -var ( - ssid string - pass string - url = "http://tinygo.org/" - debug = false -) - -var ( - display = ili9341.NewSPI( - machine.SPI3, - machine.LCD_DC, - machine.LCD_SS_PIN, - machine.LCD_RESET, - ) - - backlight = machine.LCD_BACKLIGHT - - terminal = tinyterm.NewTerminal(display) - - black = color.RGBA{0, 0, 0, 255} - white = color.RGBA{255, 255, 255, 255} - red = color.RGBA{255, 0, 0, 255} - blue = color.RGBA{0, 0, 255, 255} - green = color.RGBA{0, 255, 0, 255} - - font = &proggy.TinySZ8pt7b -) - -var buf [0x400]byte - -func main() { - display.FillScreen(black) - backlight.High() - - terminal.Configure(&tinyterm.Config{ - Font: font, - FontHeight: 10, - FontOffset: 6, - }) - - err := run() - for err != nil { - fmt.Fprintf(terminal, "error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} - -func run() error { - fmt.Fprintf(terminal, "setupRTL8720DN()\r\n") - if debug { - fmt.Fprintf(terminal, "Running in debug mode.\r\n") - fmt.Fprintf(terminal, "A serial connection is required to continue execution.\r\n") - } - - adaptor := rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) - adaptor.Debug(debug) - adaptor.Configure() - - http.UseDriver(adaptor) - http.SetBuf(buf[:]) - - fmt.Fprintf(terminal, "ConnectToAP()\r\n") - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { - return err - } - fmt.Fprintf(terminal, "connected\r\n\r\n") - - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - return err - } - fmt.Fprintf(terminal, "IP Address : %s\r\n", ip) - fmt.Fprintf(terminal, "Mask : %s\r\n", subnet) - fmt.Fprintf(terminal, "Gateway : %s\r\n", gateway) - - // You can send and receive cookies in the following way - // import "tinygo.org/x/drivers/net/http/cookiejar" - // jar, err := cookiejar.New(nil) - // if err != nil { - // return err - // } - // client := &http.Client{Jar: jar} - // http.DefaultClient = client - - cnt := 0 - for { - // Various examples are as follows - // - // -- Get - // resp, err := http.Get(url) - // - // -- Post - // body := `cnt=12` - // resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(body)) - // - // -- Post with JSON - // body := `{"msg": "hello"}` - // resp, err := http.Post(url, "application/json", strings.NewReader(body)) - - resp, err := http.Get(url) - if err != nil { - return err - } - - fmt.Fprintf(terminal, "%s %s\r\n", resp.Proto, resp.Status) - for k, v := range resp.Header { - fmt.Fprintf(terminal, "%s: %s\r\n", k, strings.Join(v, " ")) - } - fmt.Printf("\r\n") - - scanner := bufio.NewScanner(resp.Body) - for scanner.Scan() { - fmt.Fprintf(terminal, "%s\r\n", scanner.Text()) - } - resp.Body.Close() - - cnt++ - fmt.Fprintf(terminal, "-------- %d --------\r\n", cnt) - time.Sleep(10 * time.Second) - } -} - -func init() { - machine.SPI3.Configure(machine.SPIConfig{ - SCK: machine.LCD_SCK_PIN, - SDO: machine.LCD_SDO_PIN, - SDI: machine.LCD_SDI_PIN, - Frequency: 40000000, - }) - display.Configure(ili9341.Config{}) - - backlight.Configure(machine.PinConfig{machine.PinOutput}) -} diff --git a/examples/rtl8720dn/webclient/main.go b/examples/rtl8720dn/webclient/main.go deleted file mode 100644 index 5c7dabea1..000000000 --- a/examples/rtl8720dn/webclient/main.go +++ /dev/null @@ -1,106 +0,0 @@ -package main - -import ( - "machine" - - "bufio" - "fmt" - "strings" - "time" - - "tinygo.org/x/drivers/net/http" - "tinygo.org/x/drivers/rtl8720dn" -) - -// You can override the setting with the init() in another source code. -// func init() { -// ssid = "your-ssid" -// pass = "your-password" -// url = "http://tinygo.org/" -// debug = true -// } - -var ( - ssid string - pass string - url = "http://tinygo.org/" - debug = false -) - -var buf [0x400]byte - -func main() { - err := run() - for err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} - -func run() error { - adaptor := rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) - adaptor.Debug(debug) - adaptor.Configure() - - http.UseDriver(adaptor) - http.SetBuf(buf[:]) - - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { - return err - } - - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - return err - } - fmt.Printf("IP Address : %s\r\n", ip) - fmt.Printf("Mask : %s\r\n", subnet) - fmt.Printf("Gateway : %s\r\n", gateway) - - // You can send and receive cookies in the following way - // import "tinygo.org/x/drivers/net/http/cookiejar" - // jar, err := cookiejar.New(nil) - // if err != nil { - // return err - // } - // client := &http.Client{Jar: jar} - // http.DefaultClient = client - - cnt := 0 - for { - // Various examples are as follows - // - // -- Get - // resp, err := http.Get(url) - // - // -- Post - // body := `cnt=12` - // resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(body)) - // - // -- Post with JSON - // body := `{"msg": "hello"}` - // resp, err := http.Post(url, "application/json", strings.NewReader(body)) - - resp, err := http.Get(url) - if err != nil { - return err - } - - fmt.Printf("%s %s\r\n", resp.Proto, resp.Status) - for k, v := range resp.Header { - fmt.Printf("%s: %s\r\n", k, strings.Join(v, " ")) - } - fmt.Printf("\r\n") - - scanner := bufio.NewScanner(resp.Body) - for scanner.Scan() { - fmt.Printf("%s\r\n", scanner.Text()) - } - resp.Body.Close() - - cnt++ - fmt.Printf("-------- %d --------\r\n", cnt) - time.Sleep(10 * time.Second) - } -} diff --git a/examples/rtl8720dn/webserver/main.go b/examples/rtl8720dn/webserver/main.go deleted file mode 100644 index 6ae17c712..000000000 --- a/examples/rtl8720dn/webserver/main.go +++ /dev/null @@ -1,193 +0,0 @@ -package main - -import ( - "fmt" - "machine" - "strconv" - "time" - - "tinygo.org/x/drivers/net/http" - "tinygo.org/x/drivers/rtl8720dn" -) - -// You can override the setting with the init() in another source code. -// func init() { -// ssid = "your-ssid" -// pass = "your-password" -// debug = true -// } - -var ( - ssid string - pass string - debug = false -) - -var led = machine.LED -var backlight = machine.LCD_BACKLIGHT - -func main() { - led.Configure(machine.PinConfig{Mode: machine.PinOutput}) - backlight.Configure(machine.PinConfig{Mode: machine.PinOutput}) - - err := run() - for err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} - -func run() error { - adaptor := rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) - adaptor.Debug(debug) - adaptor.Configure() - - http.UseDriver(adaptor) - - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { - return err - } - - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - return err - } - fmt.Printf("IP Address : %s\r\n", ip) - fmt.Printf("Mask : %s\r\n", subnet) - fmt.Printf("Gateway : %s\r\n", gateway) - - http.HandleFunc("/", root) - http.HandleFunc("/hello", hello) - http.HandleFunc("/cnt", cnt) - http.HandleFunc("/6", sixlines) - http.HandleFunc("/off", LED_OFF) - http.HandleFunc("/on", LED_ON) - if err := http.ListenAndServe(":80", nil); err != nil { - message(err.Error()) - } - return nil -} - -func root(w http.ResponseWriter, r *http.Request) { - access := 1 - - cookie, err := r.Cookie("access") - if err != nil { - if err == http.ErrNoCookie { - cookie = &http.Cookie{ - Name: "access", - Value: "1", - } - } else { - http.Error(w, fmt.Sprintf("%s", err.Error()), http.StatusBadRequest) - return - } - } else { - v, err := strconv.ParseInt(cookie.Value, 10, 0) - if err != nil { - http.Error(w, fmt.Sprintf("invalid cookie.Value : %s", cookie.Value), http.StatusBadRequest) - return - } - cookie.Value = fmt.Sprintf("%d", v+1) - access = int(v) + 1 - } - http.SetCookie(w, cookie) - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - - - TinyGo HTTP Server - - - -
TinyGo HTTP Server
- -

- access: %d -

- -
/hello
- /6
- -

- LED
- /on
- /off
-

- - -

- /cnt
- cnt:
- incrCnt()
-

- - -
-

- - - `, access) -} - -func sixlines(w http.ResponseWriter, r *http.Request) { - // https://fukuno.jig.jp/3267 - fmt.Fprint(w, ``) -} - -func LED_ON(w http.ResponseWriter, r *http.Request) { - led.High() - backlight.High() - w.Header().Set(`Content-Type`, `text/plain; charset=UTF-8`) - fmt.Fprintf(w, "led.High()") -} - -func LED_OFF(w http.ResponseWriter, r *http.Request) { - led.Low() - backlight.Low() - w.Header().Set(`Content-Type`, `text/plain; charset=UTF-8`) - fmt.Fprintf(w, "led.Low()") -} - -func hello(w http.ResponseWriter, r *http.Request) { - w.Header().Set(`Content-Type`, `text/plain; charset=UTF-8`) - fmt.Fprintf(w, "hello") -} - -var counter int - -func cnt(w http.ResponseWriter, r *http.Request) { - r.ParseForm() - if r.Method == "POST" { - c := r.Form.Get("cnt") - if c != "" { - i64, _ := strconv.ParseInt(c, 0, 0) - counter = int(i64) - } - } - - w.Header().Set(`Content-Type`, `application/json`) - fmt.Fprintf(w, `{"cnt": %d}`, counter) -} - -func message(msg string) { - println(msg, "\r") -} diff --git a/examples/wifi/tcpclient/espat.go b/examples/wifi/tcpclient/espat.go deleted file mode 100644 index 1ae8c1a30..000000000 --- a/examples/wifi/tcpclient/espat.go +++ /dev/null @@ -1,27 +0,0 @@ -//go:build espat - -package main - -import ( - "machine" - "tinygo.org/x/drivers/espat" -) - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - uart = machine.UART1 - tx = machine.PA22 - rx = machine.PA23 - - adaptor *espat.Device -) - -func initAdaptor() *espat.Device { - uart.Configure(machine.UARTConfig{TX: tx, RX: rx}) - - adaptor = espat.New(uart) - adaptor.Configure() - - return adaptor -} diff --git a/examples/wifi/tcpclient/rtl8720dn.go b/examples/wifi/tcpclient/rtl8720dn.go deleted file mode 100644 index 7a3078995..000000000 --- a/examples/wifi/tcpclient/rtl8720dn.go +++ /dev/null @@ -1,76 +0,0 @@ -//go:build rtl8720dn - -package main - -import ( - "device/sam" - "machine" - "runtime/interrupt" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/rtl8720dn" -) - -var ( - adaptor *rtl8720dn.RTL8720DN - - uart UARTx -) - -func initAdaptor() *rtl8720dn.RTL8720DN { - adaptor, err := setupRTL8720DN() - if err != nil { - return nil - } - net.UseDriver(adaptor) - - return adaptor -} - -func handleInterrupt(interrupt.Interrupt) { - // should reset IRQ - uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF))) - uart.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INT_INTFLAG_RXC) -} - -func setupRTL8720DN() (*rtl8720dn.RTL8720DN, error) { - machine.RTL8720D_CHIP_PU.Configure(machine.PinConfig{Mode: machine.PinOutput}) - machine.RTL8720D_CHIP_PU.Low() - time.Sleep(100 * time.Millisecond) - machine.RTL8720D_CHIP_PU.High() - time.Sleep(1000 * time.Millisecond) - - uart = UARTx{ - UART: &machine.UART{ - Buffer: machine.NewRingBuffer(), - Bus: sam.SERCOM0_USART_INT, - SERCOM: 0, - }, - } - - uart.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, handleInterrupt) - uart.Configure(machine.UARTConfig{TX: machine.PB24, RX: machine.PC24, BaudRate: 614400}) - - rtl := rtl8720dn.New(uart) - //rtl.Debug(debug) - - _, err := rtl.Rpc_tcpip_adapter_init() - if err != nil { - return nil, err - } - - return rtl, nil -} - -type UARTx struct { - *machine.UART -} - -func (u UARTx) Read(p []byte) (n int, err error) { - if u.Buffered() == 0 { - time.Sleep(1 * time.Millisecond) - return 0, nil - } - return u.UART.Read(p) -} diff --git a/examples/wifi/tcpclient/wifinina.go b/examples/wifi/tcpclient/wifinina.go deleted file mode 100644 index a6308765a..000000000 --- a/examples/wifi/tcpclient/wifinina.go +++ /dev/null @@ -1,35 +0,0 @@ -//go:build wifinina - -package main - -import ( - "machine" - "tinygo.org/x/drivers/wifinina" -) - -var ( - // default interface for the Arduino Nano33 IoT. - spi = machine.NINA_SPI - - // ESP32/ESP8266 chip that has the WIFININA firmware flashed on it - adaptor *wifinina.Device -) - -func initAdaptor() *wifinina.Device { - // Configure SPI for 8Mhz, Mode 0, MSB First - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - adaptor = wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() - - return adaptor -} diff --git a/examples/wifinina/connect/main.go b/examples/wifinina/connect/main.go deleted file mode 100644 index e758ce4f6..000000000 --- a/examples/wifinina/connect/main.go +++ /dev/null @@ -1,144 +0,0 @@ -// This example connects to Access Point and prints some info -package main - -import ( - "machine" - "strconv" - "time" - - "tinygo.org/x/drivers/wifinina" -) - -var ( - // access point info - ssid string - pass string -) - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - - // these are the default pins for the Arduino Nano33 IoT. - spi = machine.NINA_SPI - - // this is the ESP chip that has the WIFININA firmware flashed on it - adaptor *wifinina.Device -) - -func setup() { - - // Configure SPI for 8Mhz, Mode 0, MSB First - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - adaptor = wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() -} - -func main() { - - setup() - - waitSerial() - - connectToAP() - - for { - println("----------------------------------------") - printSSID() - printRSSI() - printMac() - printIPs() - printTime() - time.Sleep(10 * time.Second) - } - -} - -func printSSID() { - print("SSID: ") - ssid, err := adaptor.GetCurrentSSID() - if err != nil { - println("Unknown (error: ", err.Error(), ")") - return - } - println(ssid) -} - -func printRSSI() { - print("RSSI: ") - rssi, err := adaptor.GetCurrentRSSI() - if err != nil { - println("Unknown (error: ", err.Error(), ")") - return - } - println(strconv.Itoa(int(rssi))) -} - -func printIPs() { - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - println("IP: Unknown (error: ", err.Error(), ")") - return - } - println("IP: ", ip.String()) - println("Subnet: ", subnet.String()) - println("Gateway: ", gateway.String()) -} - -func printTime() { - print("Time: ") - t, err := adaptor.GetTime() - for { - if err != nil { - println("Unknown (error: ", err.Error(), ")") - return - } - if t != 0 { - break - } - time.Sleep(time.Second) - t, err = adaptor.GetTime() - } - println(time.Unix(int64(t), 0).String()) -} - -func printMac() { - print("MAC: ") - mac, err := adaptor.GetMACAddress() - if err != nil { - println("Unknown (", err.Error(), ")") - } - println(mac.String()) -} - -// Wait for user to open serial console -func waitSerial() { - for !machine.Serial.DTR() { - time.Sleep(100 * time.Millisecond) - } -} - -// connect to access point -func connectToAP() { - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { // error connecting to AP - for { - println(err) - time.Sleep(1 * time.Second) - } - } - - println("Connected.") -} diff --git a/examples/wifinina/http-get/main.go b/examples/wifinina/http-get/main.go deleted file mode 100644 index cc44142ab..000000000 --- a/examples/wifinina/http-get/main.go +++ /dev/null @@ -1,158 +0,0 @@ -// This example opens a TCP connection using a device with WiFiNINA firmware -// and sends a HTTP request to retrieve a webpage, based on the following -// Arduino example: -// -// https://github.com/arduino-libraries/WiFiNINA/blob/master/examples/WiFiWebClientRepeating/ -// -// This example will not work with samd21 or other systems with less than 32KB -// of RAM. Use the following if you want to run wifinina on samd21, etc. -// -// examples/wifinina/webclient -// examples/wifinina/tlsclient -package main - -import ( - "bufio" - "fmt" - "machine" - "strings" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/net/http" - "tinygo.org/x/drivers/wifinina" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the server aka "hub". Replace with your own info. -// Can specify a URL starting with http or https -const url = "http://tinygo.org/" - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - - // these are the default pins for the Arduino Nano33 IoT. - spi = machine.NINA_SPI - - // this is the ESP chip that has the WIFININA firmware flashed on it - adaptor *wifinina.Device -) - -var buf [0x400]byte - -var lastRequestTime time.Time -var conn net.Conn - -func setup() { - // Configure SPI for 8Mhz, Mode 0, MSB First - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - adaptor = wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() -} - -func main() { - - setup() - http.SetBuf(buf[:]) - - waitSerial() - - connectToAP() - - // You can send and receive cookies in the following way - // import "tinygo.org/x/drivers/net/http/cookiejar" - // jar, err := cookiejar.New(nil) - // if err != nil { - // return err - // } - // client := &http.Client{Jar: jar} - // http.DefaultClient = client - - cnt := 0 - for { - // Various examples are as follows - // - // -- Get - // resp, err := http.Get(url) - // - // -- Post - // body := `cnt=12` - // resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(body)) - // - // -- Post with JSON - // body := `{"msg": "hello"}` - // resp, err := http.Post(url, "application/json", strings.NewReader(body)) - - resp, err := http.Get(url) - if err != nil { - fmt.Printf("%s\r\n", err.Error()) - continue - } - - fmt.Printf("%s %s\r\n", resp.Proto, resp.Status) - for k, v := range resp.Header { - fmt.Printf("%s: %s\r\n", k, strings.Join(v, " ")) - } - fmt.Printf("\r\n") - - scanner := bufio.NewScanner(resp.Body) - for scanner.Scan() { - fmt.Printf("%s\r\n", scanner.Text()) - } - resp.Body.Close() - - cnt++ - fmt.Printf("-------- %d --------\r\n", cnt) - time.Sleep(10 * time.Second) - } - -} - -// Wait for user to open serial console -func waitSerial() { - for !machine.Serial.DTR() { - time.Sleep(100 * time.Millisecond) - } -} - -// connect to access point -func connectToAP() { - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { // error connecting to AP - for { - println(err) - time.Sleep(1 * time.Second) - } - } - - println("Connected.") - - ip, _, _, err := adaptor.GetIP() - for ; err != nil; ip, _, _, err = adaptor.GetIP() { - message(err.Error()) - time.Sleep(1 * time.Second) - } - message(ip.String()) -} - -func message(msg string) { - println(msg, "\r") -} diff --git a/examples/wifinina/mqttclient/main.go b/examples/wifinina/mqttclient/main.go deleted file mode 100644 index c03ed92cb..000000000 --- a/examples/wifinina/mqttclient/main.go +++ /dev/null @@ -1,145 +0,0 @@ -// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1. -// It creates an MQTT connection that publishes a message every second -// to an MQTT broker. -// -// In other words: -// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> Internet <--> MQTT broker. -// -// You must install the Paho MQTT package to build this program: -// -// go get -u github.com/eclipse/paho.mqtt.golang -package main - -import ( - "fmt" - "machine" - "math/rand" - "time" - - "tinygo.org/x/drivers/net/mqtt" - "tinygo.org/x/drivers/wifinina" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the MQTT broker to use. Replace with your own info. -const server = "tcp://test.mosquitto.org:1883" - -//const server = "ssl://test.mosquitto.org:8883" - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - - // these are the default pins for the Arduino Nano33 IoT. - spi = machine.NINA_SPI - - // this is the ESP chip that has the WIFININA firmware flashed on it - adaptor *wifinina.Device - topic = "tinygo" -) - -func main() { - time.Sleep(3000 * time.Millisecond) - - rand.Seed(time.Now().UnixNano()) - - // Configure SPI for 8Mhz, Mode 0, MSB First - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - // Init esp8266/esp32 - adaptor = wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() - - connectToAP() - - opts := mqtt.NewClientOptions() - opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10)) - - println("Connectng to MQTT...") - cl := mqtt.NewClient(opts) - if token := cl.Connect(); token.Wait() && token.Error() != nil { - failMessage(token.Error().Error()) - } - - for i := 0; ; i++ { - println("Publishing MQTT message...") - data := []byte(fmt.Sprintf(`{"e":[{"n":"hello %d","v":101}]}`, i)) - token := cl.Publish(topic, 0, false, data) - token.Wait() - if err := token.Error(); err != nil { - switch t := err.(type) { - case wifinina.Error: - println(t.Error(), "attempting to reconnect") - if token := cl.Connect(); token.Wait() && token.Error() != nil { - failMessage(token.Error().Error()) - } - default: - println(err.Error()) - } - } - time.Sleep(100 * time.Millisecond) - } - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting MQTT...") - cl.Disconnect(100) - - println("Done.") -} - -// connect to access point -func connectToAP() { - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { // error connecting to AP - for { - println(err) - time.Sleep(1 * time.Second) - } - } - - println("Connected.") - - ip, _, _, err := adaptor.GetIP() - for ; err != nil; ip, _, _, err = adaptor.GetIP() { - println(err.Error()) - time.Sleep(1 * time.Second) - } - println(ip.String()) -} - -// Returns an int >= min, < max -func randomInt(min, max int) int { - return min + rand.Intn(max-min) -} - -// Generate a random string of A-Z chars with len = l -func randomString(len int) string { - bytes := make([]byte, len) - for i := 0; i < len; i++ { - bytes[i] = byte(randomInt(65, 90)) - } - return string(bytes) -} - -func failMessage(msg string) { - for { - println(msg) - time.Sleep(1 * time.Second) - } -} diff --git a/examples/wifinina/mqttsub/main.go b/examples/wifinina/mqttsub/main.go deleted file mode 100644 index b19ed1ecf..000000000 --- a/examples/wifinina/mqttsub/main.go +++ /dev/null @@ -1,158 +0,0 @@ -// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1. -// It creates an MQTT connection that publishes a message every second -// to an MQTT broker. -// -// In other words: -// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> Internet <--> MQTT broker. -// -// You must also install the Paho MQTT package to build this program: -// -// go get -u github.com/eclipse/paho.mqtt.golang -package main - -import ( - "fmt" - "machine" - "math/rand" - "time" - - "tinygo.org/x/drivers/net/mqtt" - "tinygo.org/x/drivers/wifinina" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the MQTT broker to use. Replace with your own info. -const server = "tcp://test.mosquitto.org:1883" - -//const server = "ssl://test.mosquitto.org:8883" - -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - // these are the default pins for the Arduino Nano33 IoT. - spi = machine.NINA_SPI - - // this is the ESP chip that has the WIFININA firmware flashed on it - adaptor *wifinina.Device - - cl mqtt.Client - topicTx = "tinygo/tx" - topicRx = "tinygo/rx" -) - -func subHandler(client mqtt.Client, msg mqtt.Message) { - fmt.Printf("[%s] ", msg.Topic()) - fmt.Printf("%s\r\n", msg.Payload()) -} - -func main() { - time.Sleep(3000 * time.Millisecond) - - rand.Seed(time.Now().UnixNano()) - - // Configure SPI for 8Mhz, Mode 0, MSB First - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - // Init esp8266/esp32 - adaptor = wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() - - connectToAP() - - opts := mqtt.NewClientOptions() - opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10)) - - println("Connecting to MQTT broker at", server) - cl = mqtt.NewClient(opts) - if token := cl.Connect(); token.Wait() && token.Error() != nil { - failMessage(token.Error().Error()) - } - - // subscribe - token := cl.Subscribe(topicRx, 0, subHandler) - token.Wait() - if token.Error() != nil { - failMessage(token.Error().Error()) - } - - go publishing() - - select {} - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting MQTT...") - cl.Disconnect(100) - - println("Done.") -} - -func publishing() { - for i := 0; ; i++ { - println("Publishing MQTT message...") - data := []byte(fmt.Sprintf(`{"e":[{"n":"hello %d","v":101}]}`, i)) - token := cl.Publish(topicRx, 0, false, data) - token.Wait() - if token.Error() != nil { - println(token.Error().Error()) - } - - time.Sleep(100 * time.Millisecond) - } -} - -// connect to access point -func connectToAP() { - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { // error connecting to AP - for { - println(err) - time.Sleep(1 * time.Second) - } - } - - println("Connected.") - - time.Sleep(2 * time.Second) - ip, _, _, err := adaptor.GetIP() - for ; err != nil; ip, _, _, err = adaptor.GetIP() { - println(err.Error()) - time.Sleep(1 * time.Second) - } - println(ip.String()) -} - -// Returns an int >= min, < max -func randomInt(min, max int) int { - return min + rand.Intn(max-min) -} - -// Generate a random string of A-Z chars with len = l -func randomString(len int) string { - bytes := make([]byte, len) - for i := 0; i < len; i++ { - bytes[i] = byte(randomInt(65, 90)) - } - return string(bytes) -} - -func failMessage(msg string) { - for { - println(msg) - time.Sleep(1 * time.Second) - } -} diff --git a/examples/wifinina/ntpclient/main.go b/examples/wifinina/ntpclient/main.go deleted file mode 100644 index 606e54bbf..000000000 --- a/examples/wifinina/ntpclient/main.go +++ /dev/null @@ -1,177 +0,0 @@ -// This is an example of using the wifinina driver to implement a NTP client. -// It creates a UDP connection to request the current time and parse the -// response from a NTP server. -package main - -import ( - "errors" - "fmt" - "machine" - "runtime" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/wifinina" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the server aka "hub". Replace with your own info. -const ntpHost = "129.6.15.29" - -const NTP_PACKET_SIZE = 48 - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - - // these are the default pins for the Arduino Nano33 IoT. - spi = machine.NINA_SPI - - // this is the ESP chip that has the WIFININA firmware flashed on it - adaptor *wifinina.Device - b = make([]byte, NTP_PACKET_SIZE) -) - -func setup() { - // Configure SPI for 8Mhz, Mode 0, MSB First - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - adaptor = wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() -} - -func main() { - - setup() - - waitSerial() - - connectToAP() - - // now make UDP connection - ip := net.ParseIP(ntpHost) - raddr := &net.UDPAddr{IP: ip, Port: 123} - laddr := &net.UDPAddr{Port: 2390} - conn, err := net.DialUDP("udp", laddr, raddr) - if err != nil { - for { - time.Sleep(time.Second) - println(err) - } - } - - for { - // send data - println("Requesting NTP time...") - t, err := getCurrentTime(conn) - if err != nil { - message("Error getting current time: %v", err) - } else { - message("NTP time: %v", t) - } - runtime.AdjustTimeOffset(-1 * int64(time.Since(t))) - for i := 0; i < 10; i++ { - message("Current time: %v", time.Now()) - time.Sleep(1 * time.Second) - } - } - -} - -// Wait for user to open serial console -func waitSerial() { - for !machine.Serial.DTR() { - time.Sleep(100 * time.Millisecond) - } -} - -func getCurrentTime(conn *net.UDPSerialConn) (time.Time, error) { - if err := sendNTPpacket(conn); err != nil { - return time.Time{}, err - } - clearBuffer() - for now := time.Now(); time.Since(now) < time.Second; { - time.Sleep(5 * time.Millisecond) - if n, err := conn.Read(b); err != nil { - return time.Time{}, fmt.Errorf("error reading UDP packet: %w", err) - } else if n == 0 { - continue // no packet received yet - } else if n != NTP_PACKET_SIZE { - return time.Time{}, fmt.Errorf("expected NTP packet size of %d: %d", NTP_PACKET_SIZE, n) - } - return parseNTPpacket(), nil - } - return time.Time{}, errors.New("no packet received after 1 second") -} - -func sendNTPpacket(conn *net.UDPSerialConn) error { - clearBuffer() - b[0] = 0b11100011 // LI, Version, Mode - b[1] = 0 // Stratum, or type of clock - b[2] = 6 // Polling Interval - b[3] = 0xEC // Peer Clock Precision - // 8 bytes of zero for Root Delay & Root Dispersion - b[12] = 49 - b[13] = 0x4E - b[14] = 49 - b[15] = 52 - if _, err := conn.Write(b); err != nil { - return err - } - return nil -} - -func parseNTPpacket() time.Time { - // the timestamp starts at byte 40 of the received packet and is four bytes, - // this is NTP time (seconds since Jan 1 1900): - t := uint32(b[40])<<24 | uint32(b[41])<<16 | uint32(b[42])<<8 | uint32(b[43]) - const seventyYears = 2208988800 - return time.Unix(int64(t-seventyYears), 0) -} - -func clearBuffer() { - for i := range b { - b[i] = 0 - } -} - -// connect to access point -func connectToAP() { - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { // error connecting to AP - for { - println(err) - time.Sleep(1 * time.Second) - } - } - - println("Connected.") - - time.Sleep(2 * time.Second) - ip, _, _, err := adaptor.GetIP() - for ; err != nil; ip, _, _, err = adaptor.GetIP() { - message(err.Error()) - time.Sleep(1 * time.Second) - } - message(ip.String()) -} - -func message(format string, args ...interface{}) { - println(fmt.Sprintf(format, args...), "\r") -} diff --git a/examples/wifinina/pins/main.go b/examples/wifinina/pins/main.go deleted file mode 100644 index 0285e6f05..000000000 --- a/examples/wifinina/pins/main.go +++ /dev/null @@ -1,90 +0,0 @@ -//go:build nano_rp2040 - -// This examples shows how to control RGB LED connected to -// NINA-W102 chip on Arduino Nano RP2040 Connect board -// Built-in LED code added for API comparison - -package main - -import ( - "machine" - "time" - - "tinygo.org/x/drivers/wifinina" -) - -const ( - LED = machine.LED - - // Arduino Nano RP2040 Connect board RGB LED pins - // See https://docs.arduino.cc/static/3525d638b5c76a2d19588d6b41cd02a0/ABX00053-full-pinout.pdf - LED_R wifinina.Pin = 27 - LED_G wifinina.Pin = 25 - LED_B wifinina.Pin = 26 -) - -var ( - - // these are the default pins for the Arduino Nano-RP2040 Connect - spi = machine.NINA_SPI - - // this is the ESP chip that has the WIFININA firmware flashed on it - device *wifinina.Device -) - -func setup() { - - // Configure SPI for 8Mhz, Mode 0, MSB First - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - device = wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - device.Configure() - - time.Sleep(time.Second) - - LED.Configure(machine.PinConfig{Mode: machine.PinOutput}) - LED_R.Configure(wifinina.PinConfig{Mode: wifinina.PinOutput}) - LED_G.Configure(wifinina.PinConfig{Mode: wifinina.PinOutput}) - LED_B.Configure(wifinina.PinConfig{Mode: wifinina.PinOutput}) -} - -func main() { - - setup() - - LED.Low() // OFF - LED_R.High() // OFF - LED_G.High() // OFF - LED_B.High() // OFF - - go func() { - for { - LED.Low() - time.Sleep(time.Second) - LED.High() - time.Sleep(time.Second) - } - }() - - for { - LED_R.Low() // ON - time.Sleep(time.Second) - LED_R.High() // OFF - LED_G.Low() // ON - time.Sleep(time.Second) - LED_G.High() // OFF - LED_B.Low() // ON - time.Sleep(time.Second) - LED_B.High() // OFF - } - -} diff --git a/examples/wifinina/tcpclient/main.go b/examples/wifinina/tcpclient/main.go deleted file mode 100644 index a51a91006..000000000 --- a/examples/wifinina/tcpclient/main.go +++ /dev/null @@ -1,139 +0,0 @@ -// This example opens a TCP connection using a device with WiFiNINA firmware -// and sends some data, for the purpose of testing speed and connectivity. -// -// You can open a server to accept connections from this program using: -// -// nc -w 5 -lk 8080 -package main - -import ( - "bytes" - "fmt" - "machine" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/wifinina" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the server aka "hub". Replace with your own info. -const serverIP = "" - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - - // these are the default pins for the Arduino Nano33 IoT. - spi = machine.NINA_SPI - - // this is the ESP chip that has the WIFININA firmware flashed on it - adaptor *wifinina.Device -) - -var buf = &bytes.Buffer{} - -func main() { - - // Configure SPI for 8Mhz, Mode 0, MSB First - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - adaptor = wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() - - connectToAP() - - for { - sendBatch() - time.Sleep(500 * time.Millisecond) - } - println("Done.") -} - -func sendBatch() { - - // make TCP connection - ip := net.ParseIP(serverIP) - raddr := &net.TCPAddr{IP: ip, Port: 8080} - laddr := &net.TCPAddr{Port: 8080} - - message("---------------\r\nDialing TCP connection") - conn, err := net.DialTCP("tcp", laddr, raddr) - for ; err != nil; conn, err = net.DialTCP("tcp", laddr, raddr) { - message(err.Error()) - time.Sleep(5 * time.Second) - } - - n := 0 - w := 0 - start := time.Now() - - // send data - message("Sending data") - - for i := 0; i < 1000; i++ { - buf.Reset() - fmt.Fprint(buf, - "\r---------------------------- i == ", i, " ----------------------------"+ - "\r---------------------------- i == ", i, " ----------------------------") - if w, err = conn.Write(buf.Bytes()); err != nil { - println("error:", err.Error(), "\r") - continue - } - n += w - } - - buf.Reset() - ms := time.Now().Sub(start).Milliseconds() - fmt.Fprint(buf, "\nWrote ", n, " bytes in ", ms, " ms\r\n") - message(buf.String()) - - if _, err := conn.Write(buf.Bytes()); err != nil { - println("error:", err.Error(), "\r") - } - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting TCP...") - conn.Close() -} - -// connect to access point -func connectToAP() { - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { // error connecting to AP - for { - println(err) - time.Sleep(1 * time.Second) - } - } - - println("Connected.") - - time.Sleep(2 * time.Second) - ip, _, _, err := adaptor.GetIP() - for ; err != nil; ip, _, _, err = adaptor.GetIP() { - message(err.Error()) - time.Sleep(1 * time.Second) - } - message(ip.String()) -} - -func message(msg string) { - println(msg, "\r") -} diff --git a/examples/wifinina/tlsclient/main.go b/examples/wifinina/tlsclient/main.go deleted file mode 100644 index 9e50e8970..000000000 --- a/examples/wifinina/tlsclient/main.go +++ /dev/null @@ -1,149 +0,0 @@ -// This example opens a TCP connection using a device with WiFiNINA firmware -// and sends a HTTPS request to retrieve a webpage -// -// You shall see "strict-transport-security" header in the response, -// this confirms communication is indeed over HTTPS -// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security -package main - -import ( - "fmt" - "machine" - "strings" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/net/tls" - "tinygo.org/x/drivers/wifinina" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the server aka "hub". Replace with your own info. -const server = "tinygo.org" - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - - // these are the default pins for the Arduino Nano33 IoT. - spi = machine.NINA_SPI - - // this is the ESP chip that has the WIFININA firmware flashed on it - adaptor *wifinina.Device -) - -var buf [256]byte - -var lastRequestTime time.Time -var conn net.Conn - -func setup() { - // Configure SPI for 8Mhz, Mode 0, MSB First - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - adaptor = wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() -} - -func main() { - - setup() - - waitSerial() - - connectToAP() - - for { - readConnection() - if time.Now().Sub(lastRequestTime).Milliseconds() >= 10000 { - makeHTTPSRequest() - } - } - -} - -// Wait for user to open serial console -func waitSerial() { - for !machine.Serial.DTR() { - time.Sleep(100 * time.Millisecond) - } -} - -func readConnection() { - if conn != nil { - for n, err := conn.Read(buf[:]); n > 0; n, err = conn.Read(buf[:]) { - if err != nil { - println("Read error: " + err.Error()) - } else { - print(string(buf[0:n])) - } - } - } -} - -func makeHTTPSRequest() { - - var err error - if conn != nil { - conn.Close() - } - - message("\r\n---------------\r\nDialing TCP connection") - conn, err = tls.Dial("tcp", server, nil) - for ; err != nil; conn, err = tls.Dial("tcp", server, nil) { - message("Connection failed: " + err.Error()) - time.Sleep(5 * time.Second) - } - println("Connected!\r") - - print("Sending HTTPS request...") - fmt.Fprintln(conn, "GET / HTTP/1.1") - fmt.Fprintln(conn, "Host:", strings.Split(server, ":")[0]) - fmt.Fprintln(conn, "User-Agent: TinyGo") - fmt.Fprintln(conn, "Connection: close") - fmt.Fprintln(conn) - println("Sent!\r\n\r") - - lastRequestTime = time.Now() -} - -// connect to access point -func connectToAP() { - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { // error connecting to AP - for { - println(err) - time.Sleep(1 * time.Second) - } - } - - println("Connected.") - - time.Sleep(2 * time.Second) - ip, _, _, err := adaptor.GetIP() - for ; err != nil; ip, _, _, err = adaptor.GetIP() { - message(err.Error()) - time.Sleep(1 * time.Second) - } - message(ip.String()) -} - -func message(msg string) { - println(msg, "\r") -} diff --git a/examples/wifinina/udpstation/main.go b/examples/wifinina/udpstation/main.go deleted file mode 100644 index 7c921068e..000000000 --- a/examples/wifinina/udpstation/main.go +++ /dev/null @@ -1,101 +0,0 @@ -// This is a sensor station that uses a ESP32 running nina-fw over SPI. -// It creates a UDP connection you can use to get info to/from your computer via the microcontroller. -// -// In other words: -// Your computer <--> UART0 <--> MCU <--> SPI <--> ESP32 -package main - -import ( - "machine" - "strconv" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/wifinina" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the server aka "hub". Replace with your own info. -const hubIP = "" - -var ( - // this is the ESP chip that has the WIFININA firmware flashed on it - adaptor *wifinina.Device -) - -func main() { - - // Init esp8266/esp32 - // Configure SPI for 8Mhz, Mode 0, MSB First - machine.NINA_SPI.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - // these are the default pins for the Arduino Nano33 IoT. - // change these to connect to a different UART or pins for the ESP8266/ESP32 - adaptor = wifinina.New(machine.NINA_SPI, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() - - // connect to access point - connectToAP() - - // now make UDP connection - ip := net.ParseIP(hubIP) - raddr := &net.UDPAddr{IP: ip, Port: 2222} - laddr := &net.UDPAddr{Port: 2222} - - println("Dialing UDP connection...") - conn, _ := net.DialUDP("udp", laddr, raddr) - - for { - // send data - println("Sending data...") - for i := 0; i < 25; i++ { - conn.Write([]byte("hello " + strconv.Itoa(i) + "\r\n")) - } - time.Sleep(1000 * time.Millisecond) - } - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting UDP...") - conn.Close() - println("Done.") -} - -// connect to access point -func connectToAP() { - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { // error connecting to AP - for { - println(err) - time.Sleep(1 * time.Second) - } - } - - println("Connected.") - - ip, _, _, err := adaptor.GetIP() - for ; err != nil; ip, _, _, err = adaptor.GetIP() { - message(err.Error()) - time.Sleep(1 * time.Second) - } - message(ip.String()) -} - -func message(msg string) { - println(msg, "\r") -} diff --git a/examples/wifinina/webclient/main.go b/examples/wifinina/webclient/main.go deleted file mode 100644 index e8968fefd..000000000 --- a/examples/wifinina/webclient/main.go +++ /dev/null @@ -1,150 +0,0 @@ -// This example opens a TCP connection using a device with WiFiNINA firmware -// and sends a HTTP request to retrieve a webpage, based on the following -// Arduino example: -// -// https://github.com/arduino-libraries/WiFiNINA/blob/master/examples/WiFiWebClientRepeating/ -package main - -import ( - "fmt" - "machine" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/wifinina" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the server aka "hub". Replace with your own info. -const server = "tinygo.org" - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - - // these are the default pins for the Arduino Nano33 IoT. - spi = machine.NINA_SPI - - // this is the ESP chip that has the WIFININA firmware flashed on it - adaptor *wifinina.Device -) - -var buf [256]byte - -var lastRequestTime time.Time -var conn net.Conn - -func setup() { - // Configure SPI for 8Mhz, Mode 0, MSB First - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - adaptor = wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() -} - -func main() { - - setup() - - waitSerial() - - connectToAP() - - for { - readConnection() - if time.Now().Sub(lastRequestTime).Milliseconds() >= 10000 { - makeHTTPRequest() - } - } - -} - -// Wait for user to open serial console -func waitSerial() { - for !machine.Serial.DTR() { - time.Sleep(100 * time.Millisecond) - } -} - -func readConnection() { - if conn != nil { - for n, err := conn.Read(buf[:]); n > 0; n, err = conn.Read(buf[:]) { - if err != nil { - println("Read error: " + err.Error()) - } else { - print(string(buf[0:n])) - } - } - } -} - -func makeHTTPRequest() { - - var err error - if conn != nil { - conn.Close() - } - - // make TCP connection - ip := net.ParseIP(server) - raddr := &net.TCPAddr{IP: ip, Port: 80} - laddr := &net.TCPAddr{Port: 8080} - - message("\r\n---------------\r\nDialing TCP connection") - conn, err = net.DialTCP("tcp", laddr, raddr) - for ; err != nil; conn, err = net.DialTCP("tcp", laddr, raddr) { - message("Connection failed: " + err.Error()) - time.Sleep(5 * time.Second) - } - println("Connected!\r") - - print("Sending HTTP request...") - fmt.Fprintln(conn, "GET / HTTP/1.1") - fmt.Fprintln(conn, "Host:", server) - fmt.Fprintln(conn, "User-Agent: TinyGo") - fmt.Fprintln(conn, "Connection: close") - fmt.Fprintln(conn) - println("Sent!\r\n\r") - - lastRequestTime = time.Now() -} - -// connect to access point -func connectToAP() { - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { // error connecting to AP - for { - println(err) - time.Sleep(1 * time.Second) - } - } - - println("Connected.") - - ip, _, _, err := adaptor.GetIP() - for ; err != nil; ip, _, _, err = adaptor.GetIP() { - message(err.Error()) - time.Sleep(1 * time.Second) - } - message(ip.String()) -} - -func message(msg string) { - println(msg, "\r") -} diff --git a/net/adapter.go b/net/adapter.go deleted file mode 100644 index 501def85d..000000000 --- a/net/adapter.go +++ /dev/null @@ -1,44 +0,0 @@ -package net - -import ( - "errors" - "time" -) - -var ( - ErrWiFiMissingSSID = errors.New("missing SSID") - ErrWiFiConnectTimeout = errors.New("WiFi connect timeout") -) - -// Adapter interface is used to communicate with the network adapter. -type Adapter interface { - // functions used to connect/disconnect to/from an access point - ConnectToAccessPoint(ssid, pass string, timeout time.Duration) error - Disconnect() error - GetClientIP() (string, error) - - // these functions are used once the adapter is connected to the network - GetDNS(domain string) (string, error) - ConnectTCPSocket(addr, port string) error - ConnectSSLSocket(addr, port string) error - ConnectUDPSocket(addr, sendport, listenport string) error - DisconnectSocket() error - StartSocketSend(size int) error - Write(b []byte) (n int, err error) - ReadSocket(b []byte) (n int, err error) - IsSocketDataAvailable() bool - - // FIXME: this is really specific to espat, and maybe shouldn't be part - // of the driver interface - Response(timeout int) ([]byte, error) -} - -var ActiveDevice Adapter - -func UseDriver(a Adapter) { - // TODO: rethink and refactor this - if ActiveDevice != nil { - panic("net.ActiveDevice is already set") - } - ActiveDevice = a -} diff --git a/net/http/client.go b/net/http/client.go deleted file mode 100644 index ab1d81dfe..000000000 --- a/net/http/client.go +++ /dev/null @@ -1,253 +0,0 @@ -package http - -import ( - "io" - "net/url" - "strings" - "time" -) - -// A Client is an HTTP client. Its zero value (DefaultClient) is a -// usable client that uses DefaultTransport. -// -// The Client's Transport typically has internal state (cached TCP -// connections), so Clients should be reused instead of created as -// needed. Clients are safe for concurrent use by multiple goroutines. -// -// A Client is higher-level than a RoundTripper (such as Transport) -// and additionally handles HTTP details such as cookies and -// redirects. -// -// When following redirects, the Client will forward all headers set on the -// initial Request except: -// -// • when forwarding sensitive headers like "Authorization", -// "WWW-Authenticate", and "Cookie" to untrusted targets. -// These headers will be ignored when following a redirect to a domain -// that is not a subdomain match or exact match of the initial domain. -// For example, a redirect from "foo.com" to either "foo.com" or "sub.foo.com" -// will forward the sensitive headers, but a redirect to "bar.com" will not. -// -// • when forwarding the "Cookie" header with a non-nil cookie Jar. -// Since each redirect may mutate the state of the cookie jar, -// a redirect may possibly alter a cookie set in the initial request. -// When forwarding the "Cookie" header, any mutated cookies will be omitted, -// with the expectation that the Jar will insert those mutated cookies -// with the updated values (assuming the origin matches). -// If Jar is nil, the initial cookies are forwarded without change. -type Client struct { - // Transport specifies the mechanism by which individual - // HTTP requests are made. - // If nil, DefaultTransport is used. - Transport RoundTripper - - // CheckRedirect specifies the policy for handling redirects. - // If CheckRedirect is not nil, the client calls it before - // following an HTTP redirect. The arguments req and via are - // the upcoming request and the requests made already, oldest - // first. If CheckRedirect returns an error, the Client's Get - // method returns both the previous Response (with its Body - // closed) and CheckRedirect's error (wrapped in a url.Error) - // instead of issuing the Request req. - // As a special case, if CheckRedirect returns ErrUseLastResponse, - // then the most recent response is returned with its body - // unclosed, along with a nil error. - // - // If CheckRedirect is nil, the Client uses its default policy, - // which is to stop after 10 consecutive requests. - CheckRedirect func(req *Request, via []*Request) error - - // Jar specifies the cookie jar. - // - // The Jar is used to insert relevant cookies into every - // outbound Request and is updated with the cookie values - // of every inbound Response. The Jar is consulted for every - // redirect that the Client follows. - // - // If Jar is nil, cookies are only sent if they are explicitly - // set on the Request. - Jar CookieJar - - // Timeout specifies a time limit for requests made by this - // Client. The timeout includes connection time, any - // redirects, and reading the response body. The timer remains - // running after Get, Head, Post, or Do return and will - // interrupt reading of the Response.Body. - // - // A Timeout of zero means no timeout. - // - // The Client cancels requests to the underlying Transport - // as if the Request's Context ended. - // - // For compatibility, the Client will also use the deprecated - // CancelRequest method on Transport if found. New - // RoundTripper implementations should use the Request's Context - // for cancellation instead of implementing CancelRequest. - Timeout time.Duration -} - -// DefaultClient is the default Client and is used by Get, Head, and Post. -var DefaultClient = &Client{} - -// RoundTripper is an interface representing the ability to execute a -// single HTTP transaction, obtaining the Response for a given Request. -// -// A RoundTripper must be safe for concurrent use by multiple -// goroutines. -type RoundTripper interface { - // RoundTrip executes a single HTTP transaction, returning - // a Response for the provided Request. - // - // RoundTrip should not attempt to interpret the response. In - // particular, RoundTrip must return err == nil if it obtained - // a response, regardless of the response's HTTP status code. - // A non-nil err should be reserved for failure to obtain a - // response. Similarly, RoundTrip should not attempt to - // handle higher-level protocol details such as redirects, - // authentication, or cookies. - // - // RoundTrip should not modify the request, except for - // consuming and closing the Request's Body. RoundTrip may - // read fields of the request in a separate goroutine. Callers - // should not mutate or reuse the request until the Response's - // Body has been closed. - // - // RoundTrip must always close the body, including on errors, - // but depending on the implementation may do so in a separate - // goroutine even after RoundTrip returns. This means that - // callers wanting to reuse the body for subsequent requests - // must arrange to wait for the Close call before doing so. - // - // The Request's URL and Header fields must be initialized. - RoundTrip(*Request) (*Response, error) -} - -// Get issues a GET to the specified URL. If the response is one of -// the following redirect codes, Get follows the redirect, up to a -// maximum of 10 redirects: -// -// 301 (Moved Permanently) -// 302 (Found) -// 303 (See Other) -// 307 (Temporary Redirect) -// 308 (Permanent Redirect) -// -// An error is returned if there were too many redirects or if there -// was an HTTP protocol error. A non-2xx response doesn't cause an -// error. Any returned error will be of type *url.Error. The url.Error -// value's Timeout method will report true if request timed out or was -// canceled. -// -// When err is nil, resp always contains a non-nil resp.Body. -// Caller should close resp.Body when done reading from it. -// -// Get is a wrapper around DefaultClient.Get. -// -// To make a request with custom headers, use NewRequest and -// DefaultClient.Do. -func Get(url string) (resp *Response, err error) { - return DefaultClient.Get(url) -} - -// Get issues a GET to the specified URL. If the response is one of the -// following redirect codes, Get follows the redirect after calling the -// Client's CheckRedirect function: -// -// 301 (Moved Permanently) -// 302 (Found) -// 303 (See Other) -// 307 (Temporary Redirect) -// 308 (Permanent Redirect) -// -// An error is returned if the Client's CheckRedirect function fails -// or if there was an HTTP protocol error. A non-2xx response doesn't -// cause an error. Any returned error will be of type *url.Error. The -// url.Error value's Timeout method will report true if the request -// timed out. -// -// When err is nil, resp always contains a non-nil resp.Body. -// Caller should close resp.Body when done reading from it. -// -// To make a request with custom headers, use NewRequest and Client.Do. -func (c *Client) Get(url string) (resp *Response, err error) { - req, err := NewRequest("GET", url, nil) - if err != nil { - return nil, err - } - return c.Do(req) -} - -// Post issues a POST to the specified URL. -// -// Caller should close resp.Body when done reading from it. -// -// If the provided body is an io.Closer, it is closed after the -// request. -// -// Post is a wrapper around DefaultClient.Post. -// -// To set custom headers, use NewRequest and DefaultClient.Do. -// -// See the Client.Do method documentation for details on how redirects -// are handled. -func Post(url, contentType string, body io.Reader) (resp *Response, err error) { - return DefaultClient.Post(url, contentType, body) -} - -// Post issues a POST to the specified URL. -// -// Caller should close resp.Body when done reading from it. -// -// If the provided body is an io.Closer, it is closed after the -// request. -// -// To set custom headers, use NewRequest and Client.Do. -// -// See the Client.Do method documentation for details on how redirects -// are handled. -func (c *Client) Post(url, contentType string, body io.Reader) (resp *Response, err error) { - req, err := NewRequest("POST", url, body) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", contentType) - return c.Do(req) -} - -// PostForm issues a POST to the specified URL, with data's keys and -// values URL-encoded as the request body. -// -// The Content-Type header is set to application/x-www-form-urlencoded. -// To set other headers, use NewRequest and DefaultClient.Do. -// -// When err is nil, resp always contains a non-nil resp.Body. -// Caller should close resp.Body when done reading from it. -// -// PostForm is a wrapper around DefaultClient.PostForm. -// -// See the Client.Do method documentation for details on how redirects -// are handled. -// -// To make a request with a specified context.Context, use NewRequestWithContext -// and DefaultClient.Do. -func PostForm(url string, data url.Values) (resp *Response, err error) { - return DefaultClient.PostForm(url, data) -} - -// PostForm issues a POST to the specified URL, -// with data's keys and values URL-encoded as the request body. -// -// The Content-Type header is set to application/x-www-form-urlencoded. -// To set other headers, use NewRequest and Client.Do. -// -// When err is nil, resp always contains a non-nil resp.Body. -// Caller should close resp.Body when done reading from it. -// -// See the Client.Do method documentation for details on how redirects -// are handled. -// -// To make a request with a specified context.Context, use NewRequestWithContext -// and Client.Do. -func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error) { - return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) -} diff --git a/net/http/cookie.go b/net/http/cookie.go deleted file mode 100644 index 2155289b7..000000000 --- a/net/http/cookie.go +++ /dev/null @@ -1,435 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package http - -import ( - "log" - "net" - "net/textproto" - "strconv" - "strings" - "time" -) - -// A Cookie represents an HTTP cookie as sent in the Set-Cookie header of an -// HTTP response or the Cookie header of an HTTP request. -// -// See https://tools.ietf.org/html/rfc6265 for details. -type Cookie struct { - Name string - Value string - - Path string // optional - Domain string // optional - Expires time.Time // optional - RawExpires string // for reading cookies only - - // MaxAge=0 means no 'Max-Age' attribute specified. - // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0' - // MaxAge>0 means Max-Age attribute present and given in seconds - MaxAge int - Secure bool - HttpOnly bool - SameSite SameSite - Raw string - Unparsed []string // Raw text of unparsed attribute-value pairs -} - -// SameSite allows a server to define a cookie attribute making it impossible for -// the browser to send this cookie along with cross-site requests. The main -// goal is to mitigate the risk of cross-origin information leakage, and provide -// some protection against cross-site request forgery attacks. -// -// See https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00 for details. -type SameSite int - -const ( - SameSiteDefaultMode SameSite = iota + 1 - SameSiteLaxMode - SameSiteStrictMode - SameSiteNoneMode -) - -// readSetCookies parses all "Set-Cookie" values from -// the header h and returns the successfully parsed Cookies. -func readSetCookies(h Header) []*Cookie { - cookieCount := len(h["Set-Cookie"]) - if cookieCount == 0 { - return []*Cookie{} - } - cookies := make([]*Cookie, 0, cookieCount) - for _, line := range h["Set-Cookie"] { - parts := strings.Split(textproto.TrimString(line), ";") - if len(parts) == 1 && parts[0] == "" { - continue - } - parts[0] = textproto.TrimString(parts[0]) - j := strings.Index(parts[0], "=") - if j < 0 { - continue - } - name, value := parts[0][:j], parts[0][j+1:] - if !isCookieNameValid(name) { - continue - } - value, ok := parseCookieValue(value, true) - if !ok { - continue - } - c := &Cookie{ - Name: name, - Value: value, - Raw: line, - } - for i := 1; i < len(parts); i++ { - parts[i] = textproto.TrimString(parts[i]) - if len(parts[i]) == 0 { - continue - } - - attr, val := parts[i], "" - if j := strings.Index(attr, "="); j >= 0 { - attr, val = attr[:j], attr[j+1:] - } - lowerAttr := strings.ToLower(attr) - val, ok = parseCookieValue(val, false) - if !ok { - c.Unparsed = append(c.Unparsed, parts[i]) - continue - } - switch lowerAttr { - case "samesite": - lowerVal := strings.ToLower(val) - switch lowerVal { - case "lax": - c.SameSite = SameSiteLaxMode - case "strict": - c.SameSite = SameSiteStrictMode - case "none": - c.SameSite = SameSiteNoneMode - default: - c.SameSite = SameSiteDefaultMode - } - continue - case "secure": - c.Secure = true - continue - case "httponly": - c.HttpOnly = true - continue - case "domain": - c.Domain = val - continue - case "max-age": - secs, err := strconv.Atoi(val) - if err != nil || secs != 0 && val[0] == '0' { - break - } - if secs <= 0 { - secs = -1 - } - c.MaxAge = secs - continue - case "expires": - c.RawExpires = val - exptime, err := time.Parse(time.RFC1123, val) - if err != nil { - exptime, err = time.Parse("Mon, 02-Jan-2006 15:04:05 MST", val) - if err != nil { - c.Expires = time.Time{} - break - } - } - c.Expires = exptime.UTC() - continue - case "path": - c.Path = val - continue - } - c.Unparsed = append(c.Unparsed, parts[i]) - } - cookies = append(cookies, c) - } - return cookies -} - -// SetCookie adds a Set-Cookie header to the provided ResponseWriter's headers. -// The provided cookie must have a valid Name. Invalid cookies may be -// silently dropped. -func SetCookie(w ResponseWriter, cookie *Cookie) { - if v := cookie.String(); v != "" { - w.Header().Add("Set-Cookie", v) - } -} - -// String returns the serialization of the cookie for use in a Cookie -// header (if only Name and Value are set) or a Set-Cookie response -// header (if other fields are set). -// If c is nil or c.Name is invalid, the empty string is returned. -func (c *Cookie) String() string { - if c == nil || !isCookieNameValid(c.Name) { - return "" - } - // extraCookieLength derived from typical length of cookie attributes - // see RFC 6265 Sec 4.1. - const extraCookieLength = 110 - var b strings.Builder - b.Grow(len(c.Name) + len(c.Value) + len(c.Domain) + len(c.Path) + extraCookieLength) - b.WriteString(c.Name) - b.WriteRune('=') - b.WriteString(sanitizeCookieValue(c.Value)) - - if len(c.Path) > 0 { - b.WriteString("; Path=") - b.WriteString(sanitizeCookiePath(c.Path)) - } - if len(c.Domain) > 0 { - if validCookieDomain(c.Domain) { - // A c.Domain containing illegal characters is not - // sanitized but simply dropped which turns the cookie - // into a host-only cookie. A leading dot is okay - // but won't be sent. - d := c.Domain - if d[0] == '.' { - d = d[1:] - } - b.WriteString("; Domain=") - b.WriteString(d) - } else { - log.Printf("net/http: invalid Cookie.Domain %q; dropping domain attribute", c.Domain) - } - } - var buf [len(TimeFormat)]byte - if validCookieExpires(c.Expires) { - b.WriteString("; Expires=") - b.Write(c.Expires.UTC().AppendFormat(buf[:0], TimeFormat)) - } - if c.MaxAge > 0 { - b.WriteString("; Max-Age=") - b.Write(strconv.AppendInt(buf[:0], int64(c.MaxAge), 10)) - } else if c.MaxAge < 0 { - b.WriteString("; Max-Age=0") - } - if c.HttpOnly { - b.WriteString("; HttpOnly") - } - if c.Secure { - b.WriteString("; Secure") - } - switch c.SameSite { - case SameSiteDefaultMode: - // Skip, default mode is obtained by not emitting the attribute. - case SameSiteNoneMode: - b.WriteString("; SameSite=None") - case SameSiteLaxMode: - b.WriteString("; SameSite=Lax") - case SameSiteStrictMode: - b.WriteString("; SameSite=Strict") - } - return b.String() -} - -// readCookies parses all "Cookie" values from the header h and -// returns the successfully parsed Cookies. -// -// if filter isn't empty, only cookies of that name are returned -func readCookies(h Header, filter string) []*Cookie { - lines := h["Cookie"] - if len(lines) == 0 { - return []*Cookie{} - } - - cookies := make([]*Cookie, 0, len(lines)+strings.Count(lines[0], ";")) - for _, line := range lines { - line = textproto.TrimString(line) - - var part string - for len(line) > 0 { // continue since we have rest - if splitIndex := strings.Index(line, ";"); splitIndex > 0 { - part, line = line[:splitIndex], line[splitIndex+1:] - } else { - part, line = line, "" - } - part = textproto.TrimString(part) - if len(part) == 0 { - continue - } - name, val := part, "" - if j := strings.Index(part, "="); j >= 0 { - name, val = name[:j], name[j+1:] - } - if !isCookieNameValid(name) { - continue - } - if filter != "" && filter != name { - continue - } - val, ok := parseCookieValue(val, true) - if !ok { - continue - } - cookies = append(cookies, &Cookie{Name: name, Value: val}) - } - } - return cookies -} - -// validCookieDomain reports whether v is a valid cookie domain-value. -func validCookieDomain(v string) bool { - if isCookieDomainName(v) { - return true - } - if net.ParseIP(v) != nil && !strings.Contains(v, ":") { - return true - } - return false -} - -// validCookieExpires reports whether v is a valid cookie expires-value. -func validCookieExpires(t time.Time) bool { - // IETF RFC 6265 Section 5.1.1.5, the year must not be less than 1601 - return t.Year() >= 1601 -} - -// isCookieDomainName reports whether s is a valid domain name or a valid -// domain name with a leading dot '.'. It is almost a direct copy of -// package net's isDomainName. -func isCookieDomainName(s string) bool { - if len(s) == 0 { - return false - } - if len(s) > 255 { - return false - } - - if s[0] == '.' { - // A cookie a domain attribute may start with a leading dot. - s = s[1:] - } - last := byte('.') - ok := false // Ok once we've seen a letter. - partlen := 0 - for i := 0; i < len(s); i++ { - c := s[i] - switch { - default: - return false - case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z': - // No '_' allowed here (in contrast to package net). - ok = true - partlen++ - case '0' <= c && c <= '9': - // fine - partlen++ - case c == '-': - // Byte before dash cannot be dot. - if last == '.' { - return false - } - partlen++ - case c == '.': - // Byte before dot cannot be dot, dash. - if last == '.' || last == '-' { - return false - } - if partlen > 63 || partlen == 0 { - return false - } - partlen = 0 - } - last = c - } - if last == '-' || partlen > 63 { - return false - } - - return ok -} - -var cookieNameSanitizer = strings.NewReplacer("\n", "-", "\r", "-") - -func sanitizeCookieName(n string) string { - return cookieNameSanitizer.Replace(n) -} - -// sanitizeCookieValue produces a suitable cookie-value from v. -// https://tools.ietf.org/html/rfc6265#section-4.1.1 -// cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE ) -// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E -// -// ; US-ASCII characters excluding CTLs, -// ; whitespace DQUOTE, comma, semicolon, -// ; and backslash -// -// We loosen this as spaces and commas are common in cookie values -// but we produce a quoted cookie-value if and only if v contains -// commas or spaces. -// See https://golang.org/issue/7243 for the discussion. -func sanitizeCookieValue(v string) string { - v = sanitizeOrWarn("Cookie.Value", validCookieValueByte, v) - if len(v) == 0 { - return v - } - if strings.IndexByte(v, ' ') >= 0 || strings.IndexByte(v, ',') >= 0 { - return `"` + v + `"` - } - return v -} - -func validCookieValueByte(b byte) bool { - return 0x20 <= b && b < 0x7f && b != '"' && b != ';' && b != '\\' -} - -// path-av = "Path=" path-value -// path-value = -func sanitizeCookiePath(v string) string { - return sanitizeOrWarn("Cookie.Path", validCookiePathByte, v) -} - -func validCookiePathByte(b byte) bool { - return 0x20 <= b && b < 0x7f && b != ';' -} - -func sanitizeOrWarn(fieldName string, valid func(byte) bool, v string) string { - ok := true - for i := 0; i < len(v); i++ { - if valid(v[i]) { - continue - } - log.Printf("net/http: invalid byte %q in %s; dropping invalid bytes", v[i], fieldName) - ok = false - break - } - if ok { - return v - } - buf := make([]byte, 0, len(v)) - for i := 0; i < len(v); i++ { - if b := v[i]; valid(b) { - buf = append(buf, b) - } - } - return string(buf) -} - -func parseCookieValue(raw string, allowDoubleQuote bool) (string, bool) { - // Strip the quotes, if present. - if allowDoubleQuote && len(raw) > 1 && raw[0] == '"' && raw[len(raw)-1] == '"' { - raw = raw[1 : len(raw)-1] - } - for i := 0; i < len(raw); i++ { - if !validCookieValueByte(raw[i]) { - return "", false - } - } - return raw, true -} - -func isCookieNameValid(raw string) bool { - if raw == "" { - return false - } - return strings.IndexFunc(raw, isNotToken) < 0 -} diff --git a/net/http/cookiejar/jar.go b/net/http/cookiejar/jar.go deleted file mode 100644 index 9a773f8a7..000000000 --- a/net/http/cookiejar/jar.go +++ /dev/null @@ -1,504 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package cookiejar implements an in-memory RFC 6265-compliant http.CookieJar. -package cookiejar - -import ( - "errors" - "fmt" - "net/url" - "sort" - "strings" - "sync" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/net/http" -) - -// PublicSuffixList provides the public suffix of a domain. For example: -// - the public suffix of "example.com" is "com", -// - the public suffix of "foo1.foo2.foo3.co.uk" is "co.uk", and -// - the public suffix of "bar.pvt.k12.ma.us" is "pvt.k12.ma.us". -// -// Implementations of PublicSuffixList must be safe for concurrent use by -// multiple goroutines. -// -// An implementation that always returns "" is valid and may be useful for -// testing but it is not secure: it means that the HTTP server for foo.com can -// set a cookie for bar.com. -// -// A public suffix list implementation is in the package -// golang.org/x/net/publicsuffix. -type PublicSuffixList interface { - // PublicSuffix returns the public suffix of domain. - // - // TODO: specify which of the caller and callee is responsible for IP - // addresses, for leading and trailing dots, for case sensitivity, and - // for IDN/Punycode. - PublicSuffix(domain string) string - - // String returns a description of the source of this public suffix - // list. The description will typically contain something like a time - // stamp or version number. - String() string -} - -// Options are the options for creating a new Jar. -type Options struct { - // PublicSuffixList is the public suffix list that determines whether - // an HTTP server can set a cookie for a domain. - // - // A nil value is valid and may be useful for testing but it is not - // secure: it means that the HTTP server for foo.co.uk can set a cookie - // for bar.co.uk. - PublicSuffixList PublicSuffixList -} - -// Jar implements the http.CookieJar interface from the net/http package. -type Jar struct { - psList PublicSuffixList - - // mu locks the remaining fields. - mu sync.Mutex - - // entries is a set of entries, keyed by their eTLD+1 and subkeyed by - // their name/domain/path. - entries map[string]map[string]entry - - // nextSeqNum is the next sequence number assigned to a new cookie - // created SetCookies. - nextSeqNum uint64 -} - -// New returns a new cookie jar. A nil *Options is equivalent to a zero -// Options. -func New(o *Options) (*Jar, error) { - jar := &Jar{ - entries: make(map[string]map[string]entry), - } - if o != nil { - jar.psList = o.PublicSuffixList - } - return jar, nil -} - -// entry is the internal representation of a cookie. -// -// This struct type is not used outside of this package per se, but the exported -// fields are those of RFC 6265. -type entry struct { - Name string - Value string - Domain string - Path string - SameSite string - Secure bool - HttpOnly bool - Persistent bool - HostOnly bool - Expires time.Time - Creation time.Time - LastAccess time.Time - - // seqNum is a sequence number so that Cookies returns cookies in a - // deterministic order, even for cookies that have equal Path length and - // equal Creation time. This simplifies testing. - seqNum uint64 -} - -// id returns the domain;path;name triple of e as an id. -func (e *entry) id() string { - return fmt.Sprintf("%s;%s;%s", e.Domain, e.Path, e.Name) -} - -// shouldSend determines whether e's cookie qualifies to be included in a -// request to host/path. It is the caller's responsibility to check if the -// cookie is expired. -func (e *entry) shouldSend(https bool, host, path string) bool { - return e.domainMatch(host) && e.pathMatch(path) && (https || !e.Secure) -} - -// domainMatch implements "domain-match" of RFC 6265 section 5.1.3. -func (e *entry) domainMatch(host string) bool { - if e.Domain == host { - return true - } - return !e.HostOnly && hasDotSuffix(host, e.Domain) -} - -// pathMatch implements "path-match" according to RFC 6265 section 5.1.4. -func (e *entry) pathMatch(requestPath string) bool { - if requestPath == e.Path { - return true - } - if strings.HasPrefix(requestPath, e.Path) { - if e.Path[len(e.Path)-1] == '/' { - return true // The "/any/" matches "/any/path" case. - } else if requestPath[len(e.Path)] == '/' { - return true // The "/any" matches "/any/path" case. - } - } - return false -} - -// hasDotSuffix reports whether s ends in "."+suffix. -func hasDotSuffix(s, suffix string) bool { - return len(s) > len(suffix) && s[len(s)-len(suffix)-1] == '.' && s[len(s)-len(suffix):] == suffix -} - -// Cookies implements the Cookies method of the http.CookieJar interface. -// -// It returns an empty slice if the URL's scheme is not HTTP or HTTPS. -func (j *Jar) Cookies(u *url.URL) (cookies []*http.Cookie) { - return j.cookies(u, time.Now()) -} - -// cookies is like Cookies but takes the current time as a parameter. -func (j *Jar) cookies(u *url.URL, now time.Time) (cookies []*http.Cookie) { - if u.Scheme != "http" && u.Scheme != "https" { - return cookies - } - host, err := canonicalHost(u.Host) - if err != nil { - return cookies - } - key := jarKey(host, j.psList) - - j.mu.Lock() - defer j.mu.Unlock() - - submap := j.entries[key] - if submap == nil { - return cookies - } - - https := u.Scheme == "https" - path := u.Path - if path == "" { - path = "/" - } - - modified := false - var selected []entry - for id, e := range submap { - if e.Persistent && !e.Expires.After(now) { - delete(submap, id) - modified = true - continue - } - if !e.shouldSend(https, host, path) { - continue - } - e.LastAccess = now - submap[id] = e - selected = append(selected, e) - modified = true - } - if modified { - if len(submap) == 0 { - delete(j.entries, key) - } else { - j.entries[key] = submap - } - } - - // sort according to RFC 6265 section 5.4 point 2: by longest - // path and then by earliest creation time. - sort.Slice(selected, func(i, j int) bool { - s := selected - if len(s[i].Path) != len(s[j].Path) { - return len(s[i].Path) > len(s[j].Path) - } - if !s[i].Creation.Equal(s[j].Creation) { - return s[i].Creation.Before(s[j].Creation) - } - return s[i].seqNum < s[j].seqNum - }) - for _, e := range selected { - cookies = append(cookies, &http.Cookie{Name: e.Name, Value: e.Value}) - } - - return cookies -} - -// SetCookies implements the SetCookies method of the http.CookieJar interface. -// -// It does nothing if the URL's scheme is not HTTP or HTTPS. -func (j *Jar) SetCookies(u *url.URL, cookies []*http.Cookie) { - j.setCookies(u, cookies, time.Now()) -} - -// setCookies is like SetCookies but takes the current time as parameter. -func (j *Jar) setCookies(u *url.URL, cookies []*http.Cookie, now time.Time) { - if len(cookies) == 0 { - return - } - if u.Scheme != "http" && u.Scheme != "https" { - return - } - host, err := canonicalHost(u.Host) - if err != nil { - return - } - key := jarKey(host, j.psList) - defPath := defaultPath(u.Path) - - j.mu.Lock() - defer j.mu.Unlock() - - submap := j.entries[key] - - modified := false - for _, cookie := range cookies { - e, remove, err := j.newEntry(cookie, now, defPath, host) - if err != nil { - continue - } - id := e.id() - if remove { - if submap != nil { - if _, ok := submap[id]; ok { - delete(submap, id) - modified = true - } - } - continue - } - if submap == nil { - submap = make(map[string]entry) - } - - if old, ok := submap[id]; ok { - e.Creation = old.Creation - e.seqNum = old.seqNum - } else { - e.Creation = now - e.seqNum = j.nextSeqNum - j.nextSeqNum++ - } - e.LastAccess = now - submap[id] = e - modified = true - } - - if modified { - if len(submap) == 0 { - delete(j.entries, key) - } else { - j.entries[key] = submap - } - } -} - -// canonicalHost strips port from host if present and returns the canonicalized -// host name. -func canonicalHost(host string) (string, error) { - var err error - host = strings.ToLower(host) - if hasPort(host) { - host, _, err = net.SplitHostPort(host) - if err != nil { - return "", err - } - } - if strings.HasSuffix(host, ".") { - // Strip trailing dot from fully qualified domain names. - host = host[:len(host)-1] - } - return toASCII(host) -} - -// hasPort reports whether host contains a port number. host may be a host -// name, an IPv4 or an IPv6 address. -func hasPort(host string) bool { - colons := strings.Count(host, ":") - if colons == 0 { - return false - } - if colons == 1 { - return true - } - return host[0] == '[' && strings.Contains(host, "]:") -} - -// jarKey returns the key to use for a jar. -func jarKey(host string, psl PublicSuffixList) string { - if isIP(host) { - return host - } - - var i int - if psl == nil { - i = strings.LastIndex(host, ".") - if i <= 0 { - return host - } - } else { - suffix := psl.PublicSuffix(host) - if suffix == host { - return host - } - i = len(host) - len(suffix) - if i <= 0 || host[i-1] != '.' { - // The provided public suffix list psl is broken. - // Storing cookies under host is a safe stopgap. - return host - } - // Only len(suffix) is used to determine the jar key from - // here on, so it is okay if psl.PublicSuffix("www.buggy.psl") - // returns "com" as the jar key is generated from host. - } - prevDot := strings.LastIndex(host[:i-1], ".") - return host[prevDot+1:] -} - -// isIP reports whether host is an IP address. -func isIP(host string) bool { - return net.ParseIP(host) != nil -} - -// defaultPath returns the directory part of an URL's path according to -// RFC 6265 section 5.1.4. -func defaultPath(path string) string { - if len(path) == 0 || path[0] != '/' { - return "/" // Path is empty or malformed. - } - - i := strings.LastIndex(path, "/") // Path starts with "/", so i != -1. - if i == 0 { - return "/" // Path has the form "/abc". - } - return path[:i] // Path is either of form "/abc/xyz" or "/abc/xyz/". -} - -// newEntry creates an entry from a http.Cookie c. now is the current time and -// is compared to c.Expires to determine deletion of c. defPath and host are the -// default-path and the canonical host name of the URL c was received from. -// -// remove records whether the jar should delete this cookie, as it has already -// expired with respect to now. In this case, e may be incomplete, but it will -// be valid to call e.id (which depends on e's Name, Domain and Path). -// -// A malformed c.Domain will result in an error. -func (j *Jar) newEntry(c *http.Cookie, now time.Time, defPath, host string) (e entry, remove bool, err error) { - e.Name = c.Name - - if c.Path == "" || c.Path[0] != '/' { - e.Path = defPath - } else { - e.Path = c.Path - } - - e.Domain, e.HostOnly, err = j.domainAndType(host, c.Domain) - if err != nil { - return e, false, err - } - - // MaxAge takes precedence over Expires. - if c.MaxAge < 0 { - return e, true, nil - } else if c.MaxAge > 0 { - e.Expires = now.Add(time.Duration(c.MaxAge) * time.Second) - e.Persistent = true - } else { - if c.Expires.IsZero() { - e.Expires = endOfTime - e.Persistent = false - } else { - if !c.Expires.After(now) { - return e, true, nil - } - e.Expires = c.Expires - e.Persistent = true - } - } - - e.Value = c.Value - e.Secure = c.Secure - e.HttpOnly = c.HttpOnly - - switch c.SameSite { - case http.SameSiteDefaultMode: - e.SameSite = "SameSite" - case http.SameSiteStrictMode: - e.SameSite = "SameSite=Strict" - case http.SameSiteLaxMode: - e.SameSite = "SameSite=Lax" - } - - return e, false, nil -} - -var ( - errIllegalDomain = errors.New("cookiejar: illegal cookie domain attribute") - errMalformedDomain = errors.New("cookiejar: malformed cookie domain attribute") - errNoHostname = errors.New("cookiejar: no host name available (IP only)") -) - -// endOfTime is the time when session (non-persistent) cookies expire. -// This instant is representable in most date/time formats (not just -// Go's time.Time) and should be far enough in the future. -var endOfTime = time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC) - -// domainAndType determines the cookie's domain and hostOnly attribute. -func (j *Jar) domainAndType(host, domain string) (string, bool, error) { - if domain == "" { - // No domain attribute in the SetCookie header indicates a - // host cookie. - return host, true, nil - } - - if isIP(host) { - // According to RFC 6265 domain-matching includes not being - // an IP address. - // TODO: This might be relaxed as in common browsers. - return "", false, errNoHostname - } - - // From here on: If the cookie is valid, it is a domain cookie (with - // the one exception of a public suffix below). - // See RFC 6265 section 5.2.3. - if domain[0] == '.' { - domain = domain[1:] - } - - if len(domain) == 0 || domain[0] == '.' { - // Received either "Domain=." or "Domain=..some.thing", - // both are illegal. - return "", false, errMalformedDomain - } - domain = strings.ToLower(domain) - - if domain[len(domain)-1] == '.' { - // We received stuff like "Domain=www.example.com.". - // Browsers do handle such stuff (actually differently) but - // RFC 6265 seems to be clear here (e.g. section 4.1.2.3) in - // requiring a reject. 4.1.2.3 is not normative, but - // "Domain Matching" (5.1.3) and "Canonicalized Host Names" - // (5.1.2) are. - return "", false, errMalformedDomain - } - - // See RFC 6265 section 5.3 #5. - if j.psList != nil { - if ps := j.psList.PublicSuffix(domain); ps != "" && !hasDotSuffix(domain, ps) { - if host == domain { - // This is the one exception in which a cookie - // with a domain attribute is a host cookie. - return host, true, nil - } - return "", false, errIllegalDomain - } - } - - // The domain must domain-match host: www.mycompany.com cannot - // set cookies for .ourcompetitors.com. - if host != domain && !hasDotSuffix(host, domain) { - return "", false, errIllegalDomain - } - - return domain, false, nil -} diff --git a/net/http/cookiejar/punycode.go b/net/http/cookiejar/punycode.go deleted file mode 100644 index a9cc666e8..000000000 --- a/net/http/cookiejar/punycode.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cookiejar - -// This file implements the Punycode algorithm from RFC 3492. - -import ( - "fmt" - "strings" - "unicode/utf8" -) - -// These parameter values are specified in section 5. -// -// All computation is done with int32s, so that overflow behavior is identical -// regardless of whether int is 32-bit or 64-bit. -const ( - base int32 = 36 - damp int32 = 700 - initialBias int32 = 72 - initialN int32 = 128 - skew int32 = 38 - tmax int32 = 26 - tmin int32 = 1 -) - -// encode encodes a string as specified in section 6.3 and prepends prefix to -// the result. -// -// The "while h < length(input)" line in the specification becomes "for -// remaining != 0" in the Go code, because len(s) in Go is in bytes, not runes. -func encode(prefix, s string) (string, error) { - output := make([]byte, len(prefix), len(prefix)+1+2*len(s)) - copy(output, prefix) - delta, n, bias := int32(0), initialN, initialBias - b, remaining := int32(0), int32(0) - for _, r := range s { - if r < utf8.RuneSelf { - b++ - output = append(output, byte(r)) - } else { - remaining++ - } - } - h := b - if b > 0 { - output = append(output, '-') - } - for remaining != 0 { - m := int32(0x7fffffff) - for _, r := range s { - if m > r && r >= n { - m = r - } - } - delta += (m - n) * (h + 1) - if delta < 0 { - return "", fmt.Errorf("cookiejar: invalid label %q", s) - } - n = m - for _, r := range s { - if r < n { - delta++ - if delta < 0 { - return "", fmt.Errorf("cookiejar: invalid label %q", s) - } - continue - } - if r > n { - continue - } - q := delta - for k := base; ; k += base { - t := k - bias - if t < tmin { - t = tmin - } else if t > tmax { - t = tmax - } - if q < t { - break - } - output = append(output, encodeDigit(t+(q-t)%(base-t))) - q = (q - t) / (base - t) - } - output = append(output, encodeDigit(q)) - bias = adapt(delta, h+1, h == b) - delta = 0 - h++ - remaining-- - } - delta++ - n++ - } - return string(output), nil -} - -func encodeDigit(digit int32) byte { - switch { - case 0 <= digit && digit < 26: - return byte(digit + 'a') - case 26 <= digit && digit < 36: - return byte(digit + ('0' - 26)) - } - panic("cookiejar: internal error in punycode encoding") -} - -// adapt is the bias adaptation function specified in section 6.1. -func adapt(delta, numPoints int32, firstTime bool) int32 { - if firstTime { - delta /= damp - } else { - delta /= 2 - } - delta += delta / numPoints - k := int32(0) - for delta > ((base-tmin)*tmax)/2 { - delta /= base - tmin - k += base - } - return k + (base-tmin+1)*delta/(delta+skew) -} - -// Strictly speaking, the remaining code below deals with IDNA (RFC 5890 and -// friends) and not Punycode (RFC 3492) per se. - -// acePrefix is the ASCII Compatible Encoding prefix. -const acePrefix = "xn--" - -// toASCII converts a domain or domain label to its ASCII form. For example, -// toASCII("bücher.example.com") is "xn--bcher-kva.example.com", and -// toASCII("golang") is "golang". -func toASCII(s string) (string, error) { - if ascii(s) { - return s, nil - } - labels := strings.Split(s, ".") - for i, label := range labels { - if !ascii(label) { - a, err := encode(acePrefix, label) - if err != nil { - return "", err - } - labels[i] = a - } - } - return strings.Join(labels, "."), nil -} - -func ascii(s string) bool { - for i := 0; i < len(s); i++ { - if s[i] >= utf8.RuneSelf { - return false - } - } - return true -} diff --git a/net/http/driver.go b/net/http/driver.go deleted file mode 100644 index d12593985..000000000 --- a/net/http/driver.go +++ /dev/null @@ -1,15 +0,0 @@ -package http - -type DeviceDriver interface { - ListenAndServe(addr string, handler Handler) error -} - -var ActiveDevice DeviceDriver - -func UseDriver(driver DeviceDriver) { - // TODO: rethink and refactor this - if ActiveDevice != nil { - panic("net.ActiveDevice is already set") - } - ActiveDevice = driver -} diff --git a/net/http/header.go b/net/http/header.go deleted file mode 100644 index 0cc0e556c..000000000 --- a/net/http/header.go +++ /dev/null @@ -1,259 +0,0 @@ -package http - -import ( - "io" - "net/http/httptrace" - "net/textproto" - "sort" - "strings" - "sync" - "time" -) - -// A Header represents the key-value pairs in an HTTP header. -// -// The keys should be in canonical form, as returned by -// CanonicalHeaderKey. -type Header map[string][]string - -// Add adds the key, value pair to the header. -// It appends to any existing values associated with key. -// The key is case insensitive; it is canonicalized by -// CanonicalHeaderKey. -func (h Header) Add(key, value string) { - textproto.MIMEHeader(h).Add(key, value) -} - -// Set sets the header entries associated with key to the -// single element value. It replaces any existing values -// associated with key. The key is case insensitive; it is -// canonicalized by textproto.CanonicalMIMEHeaderKey. -// To use non-canonical keys, assign to the map directly. -func (h Header) Set(key, value string) { - textproto.MIMEHeader(h).Set(key, value) -} - -// Get gets the first value associated with the given key. If -// there are no values associated with the key, Get returns "". -// It is case insensitive; textproto.CanonicalMIMEHeaderKey is -// used to canonicalize the provided key. To use non-canonical keys, -// access the map directly. -func (h Header) Get(key string) string { - return textproto.MIMEHeader(h).Get(key) -} - -// Values returns all values associated with the given key. -// It is case insensitive; textproto.CanonicalMIMEHeaderKey is -// used to canonicalize the provided key. To use non-canonical -// keys, access the map directly. -// The returned slice is not a copy. -func (h Header) Values(key string) []string { - return textproto.MIMEHeader(h).Values(key) -} - -// get is like Get, but key must already be in CanonicalHeaderKey form. -func (h Header) get(key string) string { - if v := h[key]; len(v) > 0 { - return v[0] - } - return "" -} - -// has reports whether h has the provided key defined, even if it's -// set to 0-length slice. -func (h Header) has(key string) bool { - _, ok := h[key] - return ok -} - -// Del deletes the values associated with key. -// The key is case insensitive; it is canonicalized by -// CanonicalHeaderKey. -func (h Header) Del(key string) { - textproto.MIMEHeader(h).Del(key) -} - -// Write writes a header in wire format. -func (h Header) Write(w io.Writer) error { - return h.write(w, nil) -} - -func (h Header) write(w io.Writer, trace *httptrace.ClientTrace) error { - return h.writeSubset(w, nil, trace) -} - -// Clone returns a copy of h or nil if h is nil. -func (h Header) Clone() Header { - if h == nil { - return nil - } - - // Find total number of values. - nv := 0 - for _, vv := range h { - nv += len(vv) - } - sv := make([]string, nv) // shared backing array for headers' values - h2 := make(Header, len(h)) - for k, vv := range h { - n := copy(sv, vv) - h2[k] = sv[:n:n] - sv = sv[n:] - } - return h2 -} - -var timeFormats = []string{ - TimeFormat, - time.RFC850, - time.ANSIC, -} - -// ParseTime parses a time header (such as the Date: header), -// trying each of the three formats allowed by HTTP/1.1: -// TimeFormat, time.RFC850, and time.ANSIC. -func ParseTime(text string) (t time.Time, err error) { - for _, layout := range timeFormats { - t, err = time.Parse(layout, text) - if err == nil { - return - } - } - return -} - -var headerNewlineToSpace = strings.NewReplacer("\n", " ", "\r", " ") - -// stringWriter implements WriteString on a Writer. -type stringWriter struct { - w io.Writer -} - -func (w stringWriter) WriteString(s string) (n int, err error) { - return w.w.Write([]byte(s)) -} - -type keyValues struct { - key string - values []string -} - -// A headerSorter implements sort.Interface by sorting a []keyValues -// by key. It's used as a pointer, so it can fit in a sort.Interface -// interface value without allocation. -type headerSorter struct { - kvs []keyValues -} - -func (s *headerSorter) Len() int { return len(s.kvs) } -func (s *headerSorter) Swap(i, j int) { s.kvs[i], s.kvs[j] = s.kvs[j], s.kvs[i] } -func (s *headerSorter) Less(i, j int) bool { return s.kvs[i].key < s.kvs[j].key } - -var headerSorterPool = sync.Pool{ - New: func() interface{} { return new(headerSorter) }, -} - -// sortedKeyValues returns h's keys sorted in the returned kvs -// slice. The headerSorter used to sort is also returned, for possible -// return to headerSorterCache. -func (h Header) sortedKeyValues(exclude map[string]bool) (kvs []keyValues, hs *headerSorter) { - hs = headerSorterPool.Get().(*headerSorter) - if cap(hs.kvs) < len(h) { - hs.kvs = make([]keyValues, 0, len(h)) - } - kvs = hs.kvs[:0] - for k, vv := range h { - if !exclude[k] { - kvs = append(kvs, keyValues{k, vv}) - } - } - hs.kvs = kvs - sort.Sort(hs) - return kvs, hs -} - -// WriteSubset writes a header in wire format. -// If exclude is not nil, keys where exclude[key] == true are not written. -// Keys are not canonicalized before checking the exclude map. -func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error { - return h.writeSubset(w, exclude, nil) -} - -func (h Header) writeSubset(w io.Writer, exclude map[string]bool, trace *httptrace.ClientTrace) error { - ws, ok := w.(io.StringWriter) - if !ok { - ws = stringWriter{w} - } - kvs, sorter := h.sortedKeyValues(exclude) - var formattedVals []string - for _, kv := range kvs { - for _, v := range kv.values { - v = headerNewlineToSpace.Replace(v) - v = textproto.TrimString(v) - for _, s := range []string{kv.key, ": ", v, "\r\n"} { - if _, err := ws.WriteString(s); err != nil { - headerSorterPool.Put(sorter) - return err - } - } - if trace != nil && trace.WroteHeaderField != nil { - formattedVals = append(formattedVals, v) - } - } - if trace != nil && trace.WroteHeaderField != nil { - trace.WroteHeaderField(kv.key, formattedVals) - formattedVals = nil - } - } - headerSorterPool.Put(sorter) - return nil -} - -// CanonicalHeaderKey returns the canonical format of the -// header key s. The canonicalization converts the first -// letter and any letter following a hyphen to upper case; -// the rest are converted to lowercase. For example, the -// canonical key for "accept-encoding" is "Accept-Encoding". -// If s contains a space or invalid header field bytes, it is -// returned without modifications. -func CanonicalHeaderKey(s string) string { return textproto.CanonicalMIMEHeaderKey(s) } - -// hasToken reports whether token appears with v, ASCII -// case-insensitive, with space or comma boundaries. -// token must be all lowercase. -// v may contain mixed cased. -func hasToken(v, token string) bool { - if len(token) > len(v) || token == "" { - return false - } - if v == token { - return true - } - for sp := 0; sp <= len(v)-len(token); sp++ { - // Check that first character is good. - // The token is ASCII, so checking only a single byte - // is sufficient. We skip this potential starting - // position if both the first byte and its potential - // ASCII uppercase equivalent (b|0x20) don't match. - // False positives ('^' => '~') are caught by EqualFold. - if b := v[sp]; b != token[0] && b|0x20 != token[0] { - continue - } - // Check that start pos is on a valid token boundary. - if sp > 0 && !isTokenBoundary(v[sp-1]) { - continue - } - // Check that end pos is on a valid token boundary. - if endPos := sp + len(token); endPos != len(v) && !isTokenBoundary(v[endPos]) { - continue - } - if strings.EqualFold(v[sp:sp+len(token)], token) { - return true - } - } - return false -} - -func isTokenBoundary(b byte) bool { - return b == ' ' || b == ',' || b == '\t' -} diff --git a/net/http/http.go b/net/http/http.go deleted file mode 100644 index 926869870..000000000 --- a/net/http/http.go +++ /dev/null @@ -1,162 +0,0 @@ -package http - -import ( - "io" - "strconv" - "strings" - "time" - "unicode/utf8" - - "golang.org/x/net/http/httpguts" -) - -// incomparable is a zero-width, non-comparable type. Adding it to a struct -// makes that struct also non-comparable, and generally doesn't add -// any size (as long as it's first). -type incomparable [0]func() - -// maxInt64 is the effective "infinite" value for the Server and -// Transport's byte-limiting readers. -const maxInt64 = 1<<63 - 1 - -// aLongTimeAgo is a non-zero time, far in the past, used for -// immediate cancellation of network operations. -var aLongTimeAgo = time.Unix(1, 0) - -// omitBundledHTTP2 is set by omithttp2.go when the nethttpomithttp2 -// build tag is set. That means h2_bundle.go isn't compiled in and we -// shouldn't try to use it. -var omitBundledHTTP2 bool - -// TODO(bradfitz): move common stuff here. The other files have accumulated -// generic http stuff in random places. - -// contextKey is a value for use with context.WithValue. It's used as -// a pointer so it fits in an interface{} without allocation. -type contextKey struct { - name string -} - -func (k *contextKey) String() string { return "net/http context value " + k.name } - -// Given a string of the form "host", "host:port", or "[ipv6::address]:port", -// return true if the string includes a port. -func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") } - -// removeEmptyPort strips the empty port in ":port" to "" -// as mandated by RFC 3986 Section 6.2.3. -func removeEmptyPort(host string) string { - if hasPort(host) { - return strings.TrimSuffix(host, ":") - } - return host -} - -func isNotToken(r rune) bool { - return !httpguts.IsTokenRune(r) -} - -func isASCII(s string) bool { - for i := 0; i < len(s); i++ { - if s[i] >= utf8.RuneSelf { - return false - } - } - return true -} - -// stringContainsCTLByte reports whether s contains any ASCII control character. -func stringContainsCTLByte(s string) bool { - for i := 0; i < len(s); i++ { - b := s[i] - if b < ' ' || b == 0x7f { - return true - } - } - return false -} - -func hexEscapeNonASCII(s string) string { - newLen := 0 - for i := 0; i < len(s); i++ { - if s[i] >= utf8.RuneSelf { - newLen += 3 - } else { - newLen++ - } - } - if newLen == len(s) { - return s - } - b := make([]byte, 0, newLen) - for i := 0; i < len(s); i++ { - if s[i] >= utf8.RuneSelf { - b = append(b, '%') - b = strconv.AppendInt(b, int64(s[i]), 16) - } else { - b = append(b, s[i]) - } - } - return string(b) -} - -// NoBody is an io.ReadCloser with no bytes. Read always returns EOF -// and Close always returns nil. It can be used in an outgoing client -// request to explicitly signal that a request has zero bytes. -// An alternative, however, is to simply set Request.Body to nil. -var NoBody = noBody{} - -type noBody struct{} - -func (noBody) Read([]byte) (int, error) { return 0, io.EOF } -func (noBody) Close() error { return nil } -func (noBody) WriteTo(io.Writer) (int64, error) { return 0, nil } - -var ( - // verify that an io.Copy from NoBody won't require a buffer: - _ io.WriterTo = NoBody - _ io.ReadCloser = NoBody -) - -// PushOptions describes options for Pusher.Push. -type PushOptions struct { - // Method specifies the HTTP method for the promised request. - // If set, it must be "GET" or "HEAD". Empty means "GET". - Method string - - // Header specifies additional promised request headers. This cannot - // include HTTP/2 pseudo header fields like ":path" and ":scheme", - // which will be added automatically. - Header Header -} - -// Pusher is the interface implemented by ResponseWriters that support -// HTTP/2 server push. For more background, see -// https://tools.ietf.org/html/rfc7540#section-8.2. -type Pusher interface { - // Push initiates an HTTP/2 server push. This constructs a synthetic - // request using the given target and options, serializes that request - // into a PUSH_PROMISE frame, then dispatches that request using the - // server's request handler. If opts is nil, default options are used. - // - // The target must either be an absolute path (like "/path") or an absolute - // URL that contains a valid host and the same scheme as the parent request. - // If the target is a path, it will inherit the scheme and host of the - // parent request. - // - // The HTTP/2 spec disallows recursive pushes and cross-authority pushes. - // Push may or may not detect these invalid pushes; however, invalid - // pushes will be detected and canceled by conforming clients. - // - // Handlers that wish to push URL X should call Push before sending any - // data that may trigger a request for URL X. This avoids a race where the - // client issues requests for X before receiving the PUSH_PROMISE for X. - // - // Push will run in a separate goroutine making the order of arrival - // non-deterministic. Any required synchronization needs to be implemented - // by the caller. - // - // Push returns ErrNotSupported if the client has disabled push or if push - // is not supported on the underlying connection. - Push(target string, opts *PushOptions) error -} diff --git a/net/http/jar.go b/net/http/jar.go deleted file mode 100644 index 5c3de0dad..000000000 --- a/net/http/jar.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package http - -import ( - "net/url" -) - -// A CookieJar manages storage and use of cookies in HTTP requests. -// -// Implementations of CookieJar must be safe for concurrent use by multiple -// goroutines. -// -// The net/http/cookiejar package provides a CookieJar implementation. -type CookieJar interface { - // SetCookies handles the receipt of the cookies in a reply for the - // given URL. It may or may not choose to save the cookies, depending - // on the jar's policy and implementation. - SetCookies(u *url.URL, cookies []*Cookie) - - // Cookies returns the cookies to send in a request for the given URL. - // It is up to the implementation to honor the standard cookie use - // restrictions such as in RFC 6265. - Cookies(u *url.URL) []*Cookie -} diff --git a/net/http/request.go b/net/http/request.go deleted file mode 100644 index ac67be2b3..000000000 --- a/net/http/request.go +++ /dev/null @@ -1,769 +0,0 @@ -package http - -import ( - "bufio" - "bytes" - "context" - "crypto/tls" - "errors" - "fmt" - "io" - "mime" - "mime/multipart" - "net/textproto" - "net/url" - urlpkg "net/url" - "strconv" - "strings" - "sync" -) - -func badStringError(what, val string) error { return fmt.Errorf("%s %q", what, val) } - -type Request struct { - // Method specifies the HTTP method (GET, POST, PUT, etc.). - // For client requests, an empty string means GET. - // - // Go's HTTP client does not support sending a request with - // the CONNECT method. See the documentation on Transport for - // details. - Method string - - // URL specifies either the URI being requested (for server - // requests) or the URL to access (for client requests). - // - // For server requests, the URL is parsed from the URI - // supplied on the Request-Line as stored in RequestURI. For - // most requests, fields other than Path and RawQuery will be - // empty. (See RFC 7230, Section 5.3) - // - // For client requests, the URL's Host specifies the server to - // connect to, while the Request's Host field optionally - // specifies the Host header value to send in the HTTP - // request. - URL *url.URL - - // The protocol version for incoming server requests. - // - // For client requests, these fields are ignored. The HTTP - // client code always uses either HTTP/1.1 or HTTP/2. - // See the docs on Transport for details. - Proto string // "HTTP/1.0" - ProtoMajor int // 1 - ProtoMinor int // 0 - - // Header contains the request header fields either received - // by the server or to be sent by the client. - // - // If a server received a request with header lines, - // - // Host: example.com - // accept-encoding: gzip, deflate - // Accept-Language: en-us - // fOO: Bar - // foo: two - // - // then - // - // Header = map[string][]string{ - // "Accept-Encoding": {"gzip, deflate"}, - // "Accept-Language": {"en-us"}, - // "Foo": {"Bar", "two"}, - // } - // - // For incoming requests, the Host header is promoted to the - // Request.Host field and removed from the Header map. - // - // HTTP defines that header names are case-insensitive. The - // request parser implements this by using CanonicalHeaderKey, - // making the first character and any characters following a - // hyphen uppercase and the rest lowercase. - // - // For client requests, certain headers such as Content-Length - // and Connection are automatically written when needed and - // values in Header may be ignored. See the documentation - // for the Request.Write method. - Header Header - - // Body is the request's body. - // - // For client requests, a nil body means the request has no - // body, such as a GET request. The HTTP Client's Transport - // is responsible for calling the Close method. - // - // For server requests, the Request Body is always non-nil - // but will return EOF immediately when no body is present. - // The Server will close the request body. The ServeHTTP - // Handler does not need to. - // - // Body must allow Read to be called concurrently with Close. - // In particular, calling Close should unblock a Read waiting - // for input. - Body io.ReadCloser - - // GetBody defines an optional func to return a new copy of - // Body. It is used for client requests when a redirect requires - // reading the body more than once. Use of GetBody still - // requires setting Body. - // - // For server requests, it is unused. - GetBody func() (io.ReadCloser, error) - - // ContentLength records the length of the associated content. - // The value -1 indicates that the length is unknown. - // Values >= 0 indicate that the given number of bytes may - // be read from Body. - // - // For client requests, a value of 0 with a non-nil Body is - // also treated as unknown. - ContentLength int64 - - // TransferEncoding lists the transfer encodings from outermost to - // innermost. An empty list denotes the "identity" encoding. - // TransferEncoding can usually be ignored; chunked encoding is - // automatically added and removed as necessary when sending and - // receiving requests. - TransferEncoding []string - - // Close indicates whether to close the connection after - // replying to this request (for servers) or after sending this - // request and reading its response (for clients). - // - // For server requests, the HTTP server handles this automatically - // and this field is not needed by Handlers. - // - // For client requests, setting this field prevents re-use of - // TCP connections between requests to the same hosts, as if - // Transport.DisableKeepAlives were set. - Close bool - - // For server requests, Host specifies the host on which the - // URL is sought. For HTTP/1 (per RFC 7230, section 5.4), this - // is either the value of the "Host" header or the host name - // given in the URL itself. For HTTP/2, it is the value of the - // ":authority" pseudo-header field. - // It may be of the form "host:port". For international domain - // names, Host may be in Punycode or Unicode form. Use - // golang.org/x/net/idna to convert it to either format if - // needed. - // To prevent DNS rebinding attacks, server Handlers should - // validate that the Host header has a value for which the - // Handler considers itself authoritative. The included - // ServeMux supports patterns registered to particular host - // names and thus protects its registered Handlers. - // - // For client requests, Host optionally overrides the Host - // header to send. If empty, the Request.Write method uses - // the value of URL.Host. Host may contain an international - // domain name. - Host string - - // Form contains the parsed form data, including both the URL - // field's query parameters and the PATCH, POST, or PUT form data. - // This field is only available after ParseForm is called. - // The HTTP client ignores Form and uses Body instead. - Form url.Values - - // PostForm contains the parsed form data from PATCH, POST - // or PUT body parameters. - // - // This field is only available after ParseForm is called. - // The HTTP client ignores PostForm and uses Body instead. - PostForm url.Values - - // MultipartForm is the parsed multipart form, including file uploads. - // This field is only available after ParseMultipartForm is called. - // The HTTP client ignores MultipartForm and uses Body instead. - MultipartForm *multipart.Form - - // Trailer specifies additional headers that are sent after the request - // body. - // - // For server requests, the Trailer map initially contains only the - // trailer keys, with nil values. (The client declares which trailers it - // will later send.) While the handler is reading from Body, it must - // not reference Trailer. After reading from Body returns EOF, Trailer - // can be read again and will contain non-nil values, if they were sent - // by the client. - // - // For client requests, Trailer must be initialized to a map containing - // the trailer keys to later send. The values may be nil or their final - // values. The ContentLength must be 0 or -1, to send a chunked request. - // After the HTTP request is sent the map values can be updated while - // the request body is read. Once the body returns EOF, the caller must - // not mutate Trailer. - // - // Few HTTP clients, servers, or proxies support HTTP trailers. - Trailer Header - - // RemoteAddr allows HTTP servers and other software to record - // the network address that sent the request, usually for - // logging. This field is not filled in by ReadRequest and - // has no defined format. The HTTP server in this package - // sets RemoteAddr to an "IP:port" address before invoking a - // handler. - // This field is ignored by the HTTP client. - RemoteAddr string - - // RequestURI is the unmodified request-target of the - // Request-Line (RFC 7230, Section 3.1.1) as sent by the client - // to a server. Usually the URL field should be used instead. - // It is an error to set this field in an HTTP client request. - RequestURI string - - // TLS allows HTTP servers and other software to record - // information about the TLS connection on which the request - // was received. This field is not filled in by ReadRequest. - // The HTTP server in this package sets the field for - // TLS-enabled connections before invoking a handler; - // otherwise it leaves the field nil. - // This field is ignored by the HTTP client. - TLS *tls.ConnectionState - - // Cancel is an optional channel whose closure indicates that the client - // request should be regarded as canceled. Not all implementations of - // RoundTripper may support Cancel. - // - // For server requests, this field is not applicable. - // - // Deprecated: Set the Request's context with NewRequestWithContext - // instead. If a Request's Cancel field and context are both - // set, it is undefined whether Cancel is respected. - Cancel <-chan struct{} - - // Response is the redirect response which caused this request - // to be created. This field is only populated during client - // redirects. - Response *Response - - // ctx is either the client or server context. It should only - // be modified via copying the whole Request using WithContext. - // It is unexported to prevent people from using Context wrong - // and mutating the contexts held by callers of the same request. - ctx context.Context -} - -// ProtoAtLeast reports whether the HTTP protocol used -// in the request is at least major.minor. -func (r *Request) ProtoAtLeast(major, minor int) bool { - return r.ProtoMajor > major || - r.ProtoMajor == major && r.ProtoMinor >= minor -} - -// UserAgent returns the client's User-Agent, if sent in the request. -func (r *Request) UserAgent() string { - return r.Header.Get("User-Agent") -} - -// Cookies parses and returns the HTTP cookies sent with the request. -func (r *Request) Cookies() []*Cookie { - return readCookies(r.Header, "") -} - -// ErrNoCookie is returned by Request's Cookie method when a cookie is not found. -var ErrNoCookie = errors.New("http: named cookie not present") - -// Cookie returns the named cookie provided in the request or -// ErrNoCookie if not found. -// If multiple cookies match the given name, only one cookie will -// be returned. -func (r *Request) Cookie(name string) (*Cookie, error) { - for _, c := range readCookies(r.Header, name) { - return c, nil - } - return nil, ErrNoCookie -} - -// AddCookie adds a cookie to the request. Per RFC 6265 section 5.4, -// AddCookie does not attach more than one Cookie header field. That -// means all cookies, if any, are written into the same line, -// separated by semicolon. -// AddCookie only sanitizes c's name and value, and does not sanitize -// a Cookie header already present in the request. -func (r *Request) AddCookie(c *Cookie) { - s := fmt.Sprintf("%s=%s", sanitizeCookieName(c.Name), sanitizeCookieValue(c.Value)) - if c := r.Header.Get("Cookie"); c != "" { - r.Header.Set("Cookie", c+"; "+s) - } else { - r.Header.Set("Cookie", s) - } -} - -// Referer returns the referring URL, if sent in the request. -// -// Referer is misspelled as in the request itself, a mistake from the -// earliest days of HTTP. This value can also be fetched from the -// Header map as Header["Referer"]; the benefit of making it available -// as a method is that the compiler can diagnose programs that use the -// alternate (correct English) spelling req.Referrer() but cannot -// diagnose programs that use Header["Referrer"]. -func (r *Request) Referer() string { - return r.Header.Get("Referer") -} - -// isH2Upgrade reports whether r represents the http2 "client preface" -// magic string. -func (r *Request) isH2Upgrade() bool { - return r.Method == "PRI" && len(r.Header) == 0 && r.URL.Path == "*" && r.Proto == "HTTP/2.0" -} - -// ParseHTTPVersion parses an HTTP version string. -// "HTTP/1.0" returns (1, 0, true). -func ParseHTTPVersion(vers string) (major, minor int, ok bool) { - const Big = 1000000 // arbitrary upper bound - switch vers { - case "HTTP/1.1": - return 1, 1, true - case "HTTP/1.0": - return 1, 0, true - } - if !strings.HasPrefix(vers, "HTTP/") { - return 0, 0, false - } - dot := strings.Index(vers, ".") - if dot < 0 { - return 0, 0, false - } - major, err := strconv.Atoi(vers[5:dot]) - if err != nil || major < 0 || major > Big { - return 0, 0, false - } - minor, err = strconv.Atoi(vers[dot+1:]) - if err != nil || minor < 0 || minor > Big { - return 0, 0, false - } - return major, minor, true -} - -func validMethod(method string) bool { - /* - Method = "OPTIONS" ; Section 9.2 - | "GET" ; Section 9.3 - | "HEAD" ; Section 9.4 - | "POST" ; Section 9.5 - | "PUT" ; Section 9.6 - | "DELETE" ; Section 9.7 - | "TRACE" ; Section 9.8 - | "CONNECT" ; Section 9.9 - | extension-method - extension-method = token - token = 1* - */ - return len(method) > 0 && strings.IndexFunc(method, isNotToken) == -1 -} - -// NewRequest wraps NewRequestWithContext using the background context. -func NewRequest(method, url string, body io.Reader) (*Request, error) { - return NewRequestWithContext(context.Background(), method, url, body) -} - -// NewRequestWithContext returns a new Request given a method, URL, and -// optional body. -// -// If the provided body is also an io.Closer, the returned -// Request.Body is set to body and will be closed by the Client -// methods Do, Post, and PostForm, and Transport.RoundTrip. -// -// NewRequestWithContext returns a Request suitable for use with -// Client.Do or Transport.RoundTrip. To create a request for use with -// testing a Server Handler, either use the NewRequest function in the -// net/http/httptest package, use ReadRequest, or manually update the -// Request fields. For an outgoing client request, the context -// controls the entire lifetime of a request and its response: -// obtaining a connection, sending the request, and reading the -// response headers and body. See the Request type's documentation for -// the difference between inbound and outbound request fields. -// -// If body is of type *bytes.Buffer, *bytes.Reader, or -// *strings.Reader, the returned request's ContentLength is set to its -// exact value (instead of -1), GetBody is populated (so 307 and 308 -// redirects can replay the body), and Body is set to NoBody if the -// ContentLength is 0. -func NewRequestWithContext(ctx context.Context, method, url string, body io.Reader) (*Request, error) { - if method == "" { - // We document that "" means "GET" for Request.Method, and people have - // relied on that from NewRequest, so keep that working. - // We still enforce validMethod for non-empty methods. - method = "GET" - } - if !validMethod(method) { - return nil, fmt.Errorf("net/http: invalid method %q", method) - } - if ctx == nil { - return nil, errors.New("net/http: nil Context") - } - u, err := urlpkg.Parse(url) - if err != nil { - return nil, err - } - rc, ok := body.(io.ReadCloser) - if !ok && body != nil { - rc = io.NopCloser(body) - } - // The host's colon:port should be normalized. See Issue 14836. - u.Host = removeEmptyPort(u.Host) - req := &Request{ - ctx: ctx, - Method: method, - URL: u, - Proto: "HTTP/1.1", - ProtoMajor: 1, - ProtoMinor: 1, - Header: make(Header), - Body: rc, - Host: u.Host, - } - if body != nil { - switch v := body.(type) { - case *bytes.Buffer: - req.ContentLength = int64(v.Len()) - buf := v.Bytes() - req.GetBody = func() (io.ReadCloser, error) { - r := bytes.NewReader(buf) - return io.NopCloser(r), nil - } - case *bytes.Reader: - req.ContentLength = int64(v.Len()) - snapshot := *v - req.GetBody = func() (io.ReadCloser, error) { - r := snapshot - return io.NopCloser(&r), nil - } - case *strings.Reader: - req.ContentLength = int64(v.Len()) - snapshot := *v - req.GetBody = func() (io.ReadCloser, error) { - r := snapshot - return io.NopCloser(&r), nil - } - default: - // This is where we'd set it to -1 (at least - // if body != NoBody) to mean unknown, but - // that broke people during the Go 1.8 testing - // period. People depend on it being 0 I - // guess. Maybe retry later. See Issue 18117. - } - // For client requests, Request.ContentLength of 0 - // means either actually 0, or unknown. The only way - // to explicitly say that the ContentLength is zero is - // to set the Body to nil. But turns out too much code - // depends on NewRequest returning a non-nil Body, - // so we use a well-known ReadCloser variable instead - // and have the http package also treat that sentinel - // variable to mean explicitly zero. - if req.GetBody != nil && req.ContentLength == 0 { - req.Body = NoBody - req.GetBody = func() (io.ReadCloser, error) { return NoBody, nil } - } - } - - return req, nil -} - -// parseRequestLine parses "GET /foo HTTP/1.1" into its three parts. -func parseRequestLine(line string) (method, requestURI, proto string, ok bool) { - s1 := strings.Index(line, " ") - s2 := strings.Index(line[s1+1:], " ") - if s1 < 0 || s2 < 0 { - return - } - s2 += s1 + 1 - return line[:s1], line[s1+1 : s2], line[s2+1:], true -} - -var textprotoReaderPool sync.Pool - -func newTextprotoReader(br *bufio.Reader) *textproto.Reader { - if v := textprotoReaderPool.Get(); v != nil { - tr := v.(*textproto.Reader) - tr.R = br - return tr - } - return textproto.NewReader(br) -} - -func putTextprotoReader(r *textproto.Reader) { - r.R = nil - textprotoReaderPool.Put(r) -} - -// ReadRequest reads and parses an incoming request from b. -// -// ReadRequest is a low-level function and should only be used for -// specialized applications; most code should use the Server to read -// requests and handle them via the Handler interface. ReadRequest -// only supports HTTP/1.x requests. For HTTP/2, use golang.org/x/net/http2. -func ReadRequest(b *bufio.Reader) (*Request, error) { - return readRequest(b, deleteHostHeader) -} - -// Constants for readRequest's deleteHostHeader parameter. -const ( - deleteHostHeader = true - keepHostHeader = false -) - -func readRequest(b *bufio.Reader, deleteHostHeader bool) (req *Request, err error) { - tp := newTextprotoReader(b) - req = new(Request) - - // First line: GET /index.html HTTP/1.0 - var s string - if s, err = tp.ReadLine(); err != nil { - return nil, err - } - defer func() { - putTextprotoReader(tp) - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - }() - - var ok bool - req.Method, req.RequestURI, req.Proto, ok = parseRequestLine(s) - if !ok { - return nil, badStringError("malformed HTTP request", s) - } - if !validMethod(req.Method) { - return nil, badStringError("invalid method", req.Method) - } - rawurl := req.RequestURI - if req.ProtoMajor, req.ProtoMinor, ok = ParseHTTPVersion(req.Proto); !ok { - return nil, badStringError("malformed HTTP version", req.Proto) - } - - // CONNECT requests are used two different ways, and neither uses a full URL: - // The standard use is to tunnel HTTPS through an HTTP proxy. - // It looks like "CONNECT www.google.com:443 HTTP/1.1", and the parameter is - // just the authority section of a URL. This information should go in req.URL.Host. - // - // The net/rpc package also uses CONNECT, but there the parameter is a path - // that starts with a slash. It can be parsed with the regular URL parser, - // and the path will end up in req.URL.Path, where it needs to be in order for - // RPC to work. - justAuthority := req.Method == "CONNECT" && !strings.HasPrefix(rawurl, "/") - if justAuthority { - rawurl = "http://" + rawurl - } - - if req.URL, err = url.ParseRequestURI(rawurl); err != nil { - return nil, err - } - - if justAuthority { - // Strip the bogus "http://" back off. - req.URL.Scheme = "" - } - - // Subsequent lines: Key: value. - mimeHeader, err := tp.ReadMIMEHeader() - if err != nil { - return nil, err - } - req.Header = Header(mimeHeader) - - // RFC 7230, section 5.3: Must treat - // GET /index.html HTTP/1.1 - // Host: www.google.com - // and - // GET http://www.google.com/index.html HTTP/1.1 - // Host: doesntmatter - // the same. In the second case, any Host line is ignored. - req.Host = req.URL.Host - if req.Host == "" { - req.Host = req.Header.get("Host") - } - if deleteHostHeader { - delete(req.Header, "Host") - } - - fixPragmaCacheControl(req.Header) - - req.Close = shouldClose(req.ProtoMajor, req.ProtoMinor, req.Header, false) - - err = readTransfer(req, b) - if err != nil { - return nil, err - } - - if req.isH2Upgrade() { - // Because it's neither chunked, nor declared: - req.ContentLength = -1 - - // We want to give handlers a chance to hijack the - // connection, but we need to prevent the Server from - // dealing with the connection further if it's not - // hijacked. Set Close to ensure that: - req.Close = true - } - return req, nil -} - -// MaxBytesReader is similar to io.LimitReader but is intended for -// limiting the size of incoming request bodies. In contrast to -// io.LimitReader, MaxBytesReader's result is a ReadCloser, returns a -// non-EOF error for a Read beyond the limit, and closes the -// underlying reader when its Close method is called. -// -// MaxBytesReader prevents clients from accidentally or maliciously -// sending a large request and wasting server resources. -func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser { - return &maxBytesReader{w: w, r: r, n: n} -} - -type maxBytesReader struct { - w ResponseWriter - r io.ReadCloser // underlying reader - n int64 // max bytes remaining - err error // sticky error -} - -func (l *maxBytesReader) Read(p []byte) (n int, err error) { - if l.err != nil { - return 0, l.err - } - if len(p) == 0 { - return 0, nil - } - // If they asked for a 32KB byte read but only 5 bytes are - // remaining, no need to read 32KB. 6 bytes will answer the - // question of the whether we hit the limit or go past it. - if int64(len(p)) > l.n+1 { - p = p[:l.n+1] - } - n, err = l.r.Read(p) - - if int64(n) <= l.n { - l.n -= int64(n) - l.err = err - return n, err - } - - n = int(l.n) - l.n = 0 - - // The server code and client code both use - // maxBytesReader. This "requestTooLarge" check is - // only used by the server code. To prevent binaries - // which only using the HTTP Client code (such as - // cmd/go) from also linking in the HTTP server, don't - // use a static type assertion to the server - // "*response" type. Check this interface instead: - type requestTooLarger interface { - requestTooLarge() - } - if res, ok := l.w.(requestTooLarger); ok { - res.requestTooLarge() - } - l.err = errors.New("http: request body too large") - return n, l.err -} - -func (l *maxBytesReader) Close() error { - return l.r.Close() -} - -func copyValues(dst, src url.Values) { - for k, vs := range src { - dst[k] = append(dst[k], vs...) - } -} - -func parsePostForm(r *Request) (vs url.Values, err error) { - if r.Body == nil { - err = errors.New("missing form body") - return - } - ct := r.Header.Get("Content-Type") - // RFC 7231, section 3.1.1.5 - empty type - // MAY be treated as application/octet-stream - if ct == "" { - ct = "application/octet-stream" - } - ct, _, err = mime.ParseMediaType(ct) - switch { - case ct == "application/x-www-form-urlencoded": - var reader io.Reader = r.Body - maxFormSize := int64(1<<63 - 1) - if _, ok := r.Body.(*maxBytesReader); !ok { - maxFormSize = int64(10 << 20) // 10 MB is a lot of text. - reader = io.LimitReader(r.Body, maxFormSize+1) - } - b, e := io.ReadAll(reader) - if e != nil { - if err == nil { - err = e - } - break - } - if int64(len(b)) > maxFormSize { - err = errors.New("http: POST too large") - return - } - vs, e = url.ParseQuery(string(b)) - if err == nil { - err = e - } - case ct == "multipart/form-data": - // handled by ParseMultipartForm (which is calling us, or should be) - // TODO(bradfitz): there are too many possible - // orders to call too many functions here. - // Clean this up and write more tests. - // request_test.go contains the start of this, - // in TestParseMultipartFormOrder and others. - } - return -} - -// ParseForm populates r.Form and r.PostForm. -// -// For all requests, ParseForm parses the raw query from the URL and updates -// r.Form. -// -// For POST, PUT, and PATCH requests, it also reads the request body, parses it -// as a form and puts the results into both r.PostForm and r.Form. Request body -// parameters take precedence over URL query string values in r.Form. -// -// If the request Body's size has not already been limited by MaxBytesReader, -// the size is capped at 10MB. -// -// For other HTTP methods, or when the Content-Type is not -// application/x-www-form-urlencoded, the request Body is not read, and -// r.PostForm is initialized to a non-nil, empty value. -// -// ParseMultipartForm calls ParseForm automatically. -// ParseForm is idempotent. -func (r *Request) ParseForm() error { - var err error - if r.PostForm == nil { - if r.Method == "POST" || r.Method == "PUT" || r.Method == "PATCH" { - r.PostForm, err = parsePostForm(r) - } - if r.PostForm == nil { - r.PostForm = make(url.Values) - } - } - if r.Form == nil { - if len(r.PostForm) > 0 { - r.Form = make(url.Values) - copyValues(r.Form, r.PostForm) - } - var newValues url.Values - if r.URL != nil { - var e error - newValues, e = url.ParseQuery(r.URL.RawQuery) - if err == nil { - err = e - } - } - if newValues == nil { - newValues = make(url.Values) - } - if r.Form == nil { - r.Form = newValues - } else { - copyValues(r.Form, newValues) - } - } - return err -} diff --git a/net/http/response.go b/net/http/response.go deleted file mode 100644 index 3015e5270..000000000 --- a/net/http/response.go +++ /dev/null @@ -1,120 +0,0 @@ -package http - -import ( - "crypto/tls" - "io" -) - -// Response represents the response from an HTTP request. -// -// The Client and Transport return Responses from servers once -// the response headers have been received. The response body -// is streamed on demand as the Body field is read. -type Response struct { - Status string // e.g. "200 OK" - StatusCode int // e.g. 200 - Proto string // e.g. "HTTP/1.0" - ProtoMajor int // e.g. 1 - ProtoMinor int // e.g. 0 - - // Header maps header keys to values. If the response had multiple - // headers with the same key, they may be concatenated, with comma - // delimiters. (RFC 7230, section 3.2.2 requires that multiple headers - // be semantically equivalent to a comma-delimited sequence.) When - // Header values are duplicated by other fields in this struct (e.g., - // ContentLength, TransferEncoding, Trailer), the field values are - // authoritative. - // - // Keys in the map are canonicalized (see CanonicalHeaderKey). - Header Header - - // Body represents the response body. - // - // The response body is streamed on demand as the Body field - // is read. If the network connection fails or the server - // terminates the response, Body.Read calls return an error. - // - // The http Client and Transport guarantee that Body is always - // non-nil, even on responses without a body or responses with - // a zero-length body. It is the caller's responsibility to - // close Body. The default HTTP client's Transport may not - // reuse HTTP/1.x "keep-alive" TCP connections if the Body is - // not read to completion and closed. - // - // The Body is automatically dechunked if the server replied - // with a "chunked" Transfer-Encoding. - // - // As of Go 1.12, the Body will also implement io.Writer - // on a successful "101 Switching Protocols" response, - // as used by WebSockets and HTTP/2's "h2c" mode. - Body io.ReadCloser - - // ContentLength records the length of the associated content. The - // value -1 indicates that the length is unknown. Unless Request.Method - // is "HEAD", values >= 0 indicate that the given number of bytes may - // be read from Body. - ContentLength int64 - - // Contains transfer encodings from outer-most to inner-most. Value is - // nil, means that "identity" encoding is used. - TransferEncoding []string - - // Close records whether the header directed that the connection be - // closed after reading Body. The value is advice for clients: neither - // ReadResponse nor Response.Write ever closes a connection. - Close bool - - // Uncompressed reports whether the response was sent compressed but - // was decompressed by the http package. When true, reading from - // Body yields the uncompressed content instead of the compressed - // content actually set from the server, ContentLength is set to -1, - // and the "Content-Length" and "Content-Encoding" fields are deleted - // from the responseHeader. To get the original response from - // the server, set Transport.DisableCompression to true. - Uncompressed bool - - // Trailer maps trailer keys to values in the same - // format as Header. - // - // The Trailer initially contains only nil values, one for - // each key specified in the server's "Trailer" header - // value. Those values are not added to Header. - // - // Trailer must not be accessed concurrently with Read calls - // on the Body. - // - // After Body.Read has returned io.EOF, Trailer will contain - // any trailer values sent by the server. - Trailer Header - - // Request is the request that was sent to obtain this Response. - // Request's Body is nil (having already been consumed). - // This is only populated for Client requests. - Request *Request - - // TLS contains information about the TLS connection on which the - // response was received. It is nil for unencrypted responses. - // The pointer is shared between responses and should not be - // modified. - TLS *tls.ConnectionState -} - -// Cookies parses and returns the cookies set in the Set-Cookie headers. -func (r *Response) Cookies() []*Cookie { - return readSetCookies(r.Header) -} - -// RFC 7234, section 5.4: Should treat -// -// Pragma: no-cache -// -// like -// -// Cache-Control: no-cache -func fixPragmaCacheControl(header Header) { - if hp, ok := header["Pragma"]; ok && len(hp) > 0 && hp[0] == "no-cache" { - if _, presentcc := header["Cache-Control"]; !presentcc { - header["Cache-Control"] = []string{"no-cache"} - } - } -} diff --git a/net/http/server.go b/net/http/server.go deleted file mode 100644 index 383d4f2ac..000000000 --- a/net/http/server.go +++ /dev/null @@ -1,581 +0,0 @@ -package http - -import ( - "fmt" - "net" - "net/url" - urlpkg "net/url" - "path" - "sort" - "strings" - "sync" -) - -// A Handler responds to an HTTP request. -// -// ServeHTTP should write reply headers and data to the ResponseWriter -// and then return. Returning signals that the request is finished; it -// is not valid to use the ResponseWriter or read from the -// Request.Body after or concurrently with the completion of the -// ServeHTTP call. -// -// Depending on the HTTP client software, HTTP protocol version, and -// any intermediaries between the client and the Go server, it may not -// be possible to read from the Request.Body after writing to the -// ResponseWriter. Cautious handlers should read the Request.Body -// first, and then reply. -// -// Except for reading the body, handlers should not modify the -// provided Request. -// -// If ServeHTTP panics, the server (the caller of ServeHTTP) assumes -// that the effect of the panic was isolated to the active request. -// It recovers the panic, logs a stack trace to the server error log, -// and either closes the network connection or sends an HTTP/2 -// RST_STREAM, depending on the HTTP protocol. To abort a handler so -// the client sees an interrupted response but the server doesn't log -// an error, panic with the value ErrAbortHandler. -type Handler interface { - ServeHTTP(ResponseWriter, *Request) -} - -// A ResponseWriter interface is used by an HTTP handler to -// construct an HTTP response. -// -// A ResponseWriter may not be used after the Handler.ServeHTTP method -// has returned. -type ResponseWriter interface { - // Header returns the header map that will be sent by - // WriteHeader. The Header map also is the mechanism with which - // Handlers can set HTTP trailers. - // - // Changing the header map after a call to WriteHeader (or - // Write) has no effect unless the modified headers are - // trailers. - // - // There are two ways to set Trailers. The preferred way is to - // predeclare in the headers which trailers you will later - // send by setting the "Trailer" header to the names of the - // trailer keys which will come later. In this case, those - // keys of the Header map are treated as if they were - // trailers. See the example. The second way, for trailer - // keys not known to the Handler until after the first Write, - // is to prefix the Header map keys with the TrailerPrefix - // constant value. See TrailerPrefix. - // - // To suppress automatic response headers (such as "Date"), set - // their value to nil. - Header() Header - - // Write writes the data to the connection as part of an HTTP reply. - // - // If WriteHeader has not yet been called, Write calls - // WriteHeader(http.StatusOK) before writing the data. If the Header - // does not contain a Content-Type line, Write adds a Content-Type set - // to the result of passing the initial 512 bytes of written data to - // DetectContentType. Additionally, if the total size of all written - // data is under a few KB and there are no Flush calls, the - // Content-Length header is added automatically. - // - // Depending on the HTTP protocol version and the client, calling - // Write or WriteHeader may prevent future reads on the - // Request.Body. For HTTP/1.x requests, handlers should read any - // needed request body data before writing the response. Once the - // headers have been flushed (due to either an explicit Flusher.Flush - // call or writing enough data to trigger a flush), the request body - // may be unavailable. For HTTP/2 requests, the Go HTTP server permits - // handlers to continue to read the request body while concurrently - // writing the response. However, such behavior may not be supported - // by all HTTP/2 clients. Handlers should read before writing if - // possible to maximize compatibility. - Write([]byte) (int, error) - - // WriteHeader sends an HTTP response header with the provided - // status code. - // - // If WriteHeader is not called explicitly, the first call to Write - // will trigger an implicit WriteHeader(http.StatusOK). - // Thus explicit calls to WriteHeader are mainly used to - // send error codes. - // - // The provided code must be a valid HTTP 1xx-5xx status code. - // Only one header may be written. Go does not currently - // support sending user-defined 1xx informational headers, - // with the exception of 100-continue response header that the - // Server sends automatically when the Request.Body is read. - WriteHeader(statusCode int) -} - -// TimeFormat is the time format to use when generating times in HTTP -// headers. It is like time.RFC1123 but hard-codes GMT as the time -// zone. The time being formatted must be in UTC for Format to -// generate the correct format. -// -// For parsing this time format, see ParseTime. -const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT" - -// The HandlerFunc type is an adapter to allow the use of -// ordinary functions as HTTP handlers. If f is a function -// with the appropriate signature, HandlerFunc(f) is a -// Handler that calls f. -type HandlerFunc func(ResponseWriter, *Request) - -// ServeHTTP calls f(w, r). -func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { - f(w, r) -} - -// Helper handlers - -// Error replies to the request with the specified error message and HTTP code. -// It does not otherwise end the request; the caller should ensure no further -// writes are done to w. -// The error message should be plain text. -func Error(w ResponseWriter, error string, code int) { - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - w.Header().Set("X-Content-Type-Options", "nosniff") - w.WriteHeader(code) - fmt.Fprintln(w, error) -} - -// NotFound replies to the request with an HTTP 404 not found error. -func NotFound(w ResponseWriter, r *Request) { Error(w, "404 page not found", StatusNotFound) } - -// NotFoundHandler returns a simple request handler -// that replies to each request with a “404 page not found” reply. -func NotFoundHandler() Handler { return HandlerFunc(NotFound) } - -// StripPrefix returns a handler that serves HTTP requests by removing the -// given prefix from the request URL's Path (and RawPath if set) and invoking -// the handler h. StripPrefix handles a request for a path that doesn't begin -// with prefix by replying with an HTTP 404 not found error. The prefix must -// match exactly: if the prefix in the request contains escaped characters -// the reply is also an HTTP 404 not found error. -func StripPrefix(prefix string, h Handler) Handler { - if prefix == "" { - return h - } - return HandlerFunc(func(w ResponseWriter, r *Request) { - p := strings.TrimPrefix(r.URL.Path, prefix) - rp := strings.TrimPrefix(r.URL.RawPath, prefix) - if len(p) < len(r.URL.Path) && (r.URL.RawPath == "" || len(rp) < len(r.URL.RawPath)) { - r2 := new(Request) - *r2 = *r - r2.URL = new(url.URL) - *r2.URL = *r.URL - r2.URL.Path = p - r2.URL.RawPath = rp - h.ServeHTTP(w, r2) - } else { - NotFound(w, r) - } - }) -} - -// Redirect replies to the request with a redirect to url, -// which may be a path relative to the request path. -// -// The provided code should be in the 3xx range and is usually -// StatusMovedPermanently, StatusFound or StatusSeeOther. -// -// If the Content-Type header has not been set, Redirect sets it -// to "text/html; charset=utf-8" and writes a small HTML body. -// Setting the Content-Type header to any value, including nil, -// disables that behavior. -func Redirect(w ResponseWriter, r *Request, url string, code int) { - if u, err := urlpkg.Parse(url); err == nil { - // If url was relative, make its path absolute by - // combining with request path. - // The client would probably do this for us, - // but doing it ourselves is more reliable. - // See RFC 7231, section 7.1.2 - if u.Scheme == "" && u.Host == "" { - oldpath := r.URL.Path - if oldpath == "" { // should not happen, but avoid a crash if it does - oldpath = "/" - } - - // no leading http://server - if url == "" || url[0] != '/' { - // make relative path absolute - olddir, _ := path.Split(oldpath) - url = olddir + url - } - - var query string - if i := strings.Index(url, "?"); i != -1 { - url, query = url[:i], url[i:] - } - - // clean up but preserve trailing slash - trailing := strings.HasSuffix(url, "/") - url = path.Clean(url) - if trailing && !strings.HasSuffix(url, "/") { - url += "/" - } - url += query - } - } - - h := w.Header() - - // RFC 7231 notes that a short HTML body is usually included in - // the response because older user agents may not understand 301/307. - // Do it only if the request didn't already have a Content-Type header. - _, hadCT := h["Content-Type"] - - h.Set("Location", hexEscapeNonASCII(url)) - if !hadCT && (r.Method == "GET" || r.Method == "HEAD") { - h.Set("Content-Type", "text/html; charset=utf-8") - } - w.WriteHeader(code) - - // Shouldn't send the body for POST or HEAD; that leaves GET. - if !hadCT && r.Method == "GET" { - body := "" + statusText[code] + ".\n" - fmt.Fprintln(w, body) - } -} - -var htmlReplacer = strings.NewReplacer( - "&", "&", - "<", "<", - ">", ">", - // """ is shorter than """. - `"`, """, - // "'" is shorter than "'" and apos was not in HTML until HTML5. - "'", "'", -) - -func htmlEscape(s string) string { - return htmlReplacer.Replace(s) -} - -// Redirect to a fixed URL -type redirectHandler struct { - url string - code int -} - -func (rh *redirectHandler) ServeHTTP(w ResponseWriter, r *Request) { - Redirect(w, r, rh.url, rh.code) -} - -// RedirectHandler returns a request handler that redirects -// each request it receives to the given url using the given -// status code. -// -// The provided code should be in the 3xx range and is usually -// StatusMovedPermanently, StatusFound or StatusSeeOther. -func RedirectHandler(url string, code int) Handler { - return &redirectHandler{url, code} -} - -// ServeMux is an HTTP request multiplexer. -// It matches the URL of each incoming request against a list of registered -// patterns and calls the handler for the pattern that -// most closely matches the URL. -// -// Patterns name fixed, rooted paths, like "/favicon.ico", -// or rooted subtrees, like "/images/" (note the trailing slash). -// Longer patterns take precedence over shorter ones, so that -// if there are handlers registered for both "/images/" -// and "/images/thumbnails/", the latter handler will be -// called for paths beginning "/images/thumbnails/" and the -// former will receive requests for any other paths in the -// "/images/" subtree. -// -// Note that since a pattern ending in a slash names a rooted subtree, -// the pattern "/" matches all paths not matched by other registered -// patterns, not just the URL with Path == "/". -// -// If a subtree has been registered and a request is received naming the -// subtree root without its trailing slash, ServeMux redirects that -// request to the subtree root (adding the trailing slash). This behavior can -// be overridden with a separate registration for the path without -// the trailing slash. For example, registering "/images/" causes ServeMux -// to redirect a request for "/images" to "/images/", unless "/images" has -// been registered separately. -// -// Patterns may optionally begin with a host name, restricting matches to -// URLs on that host only. Host-specific patterns take precedence over -// general patterns, so that a handler might register for the two patterns -// "/codesearch" and "codesearch.google.com/" without also taking over -// requests for "http://www.google.com/". -// -// ServeMux also takes care of sanitizing the URL request path and the Host -// header, stripping the port number and redirecting any request containing . or -// .. elements or repeated slashes to an equivalent, cleaner URL. -type ServeMux struct { - mu sync.RWMutex - m map[string]muxEntry - es []muxEntry // slice of entries sorted from longest to shortest. - hosts bool // whether any patterns contain hostnames -} - -type muxEntry struct { - h Handler - pattern string -} - -// NewServeMux allocates and returns a new ServeMux. -func NewServeMux() *ServeMux { return new(ServeMux) } - -// DefaultServeMux is the default ServeMux used by Serve. -var DefaultServeMux = &defaultServeMux - -var defaultServeMux ServeMux - -// cleanPath returns the canonical path for p, eliminating . and .. elements. -func cleanPath(p string) string { - if p == "" { - return "/" - } - if p[0] != '/' { - p = "/" + p - } - np := path.Clean(p) - // path.Clean removes trailing slash except for root; - // put the trailing slash back if necessary. - if p[len(p)-1] == '/' && np != "/" { - // Fast path for common case of p being the string we want: - if len(p) == len(np)+1 && strings.HasPrefix(p, np) { - np = p - } else { - np += "/" - } - } - return np -} - -// stripHostPort returns h without any trailing ":". -func stripHostPort(h string) string { - // If no port on host, return unchanged - if strings.IndexByte(h, ':') == -1 { - return h - } - host, _, err := net.SplitHostPort(h) - if err != nil { - return h // on error, return unchanged - } - return host -} - -// Find a handler on a handler map given a path string. -// Most-specific (longest) pattern wins. -func (mux *ServeMux) match(path string) (h Handler, pattern string) { - // Check for exact match first. - v, ok := mux.m[path] - if ok { - return v.h, v.pattern - } - - // Check for longest valid match. mux.es contains all patterns - // that end in / sorted from longest to shortest. - for _, e := range mux.es { - if strings.HasPrefix(path, e.pattern) { - return e.h, e.pattern - } - } - return nil, "" -} - -// redirectToPathSlash determines if the given path needs appending "/" to it. -// This occurs when a handler for path + "/" was already registered, but -// not for path itself. If the path needs appending to, it creates a new -// URL, setting the path to u.Path + "/" and returning true to indicate so. -func (mux *ServeMux) redirectToPathSlash(host, path string, u *url.URL) (*url.URL, bool) { - mux.mu.RLock() - shouldRedirect := mux.shouldRedirectRLocked(host, path) - mux.mu.RUnlock() - if !shouldRedirect { - return u, false - } - path = path + "/" - u = &url.URL{Path: path, RawQuery: u.RawQuery} - return u, true -} - -// shouldRedirectRLocked reports whether the given path and host should be redirected to -// path+"/". This should happen if a handler is registered for path+"/" but -// not path -- see comments at ServeMux. -func (mux *ServeMux) shouldRedirectRLocked(host, path string) bool { - p := []string{path, host + path} - - for _, c := range p { - if _, exist := mux.m[c]; exist { - return false - } - } - - n := len(path) - if n == 0 { - return false - } - for _, c := range p { - if _, exist := mux.m[c+"/"]; exist { - return path[n-1] != '/' - } - } - - return false -} - -// Handler returns the handler to use for the given request, -// consulting r.Method, r.Host, and r.URL.Path. It always returns -// a non-nil handler. If the path is not in its canonical form, the -// handler will be an internally-generated handler that redirects -// to the canonical path. If the host contains a port, it is ignored -// when matching handlers. -// -// The path and host are used unchanged for CONNECT requests. -// -// Handler also returns the registered pattern that matches the -// request or, in the case of internally-generated redirects, -// the pattern that will match after following the redirect. -// -// If there is no registered handler that applies to the request, -// Handler returns a “page not found” handler and an empty pattern. -func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) { - - // CONNECT requests are not canonicalized. - if r.Method == "CONNECT" { - // If r.URL.Path is /tree and its handler is not registered, - // the /tree -> /tree/ redirect applies to CONNECT requests - // but the path canonicalization does not. - if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok { - return RedirectHandler(u.String(), StatusMovedPermanently), u.Path - } - - return mux.handler(r.Host, r.URL.Path) - } - - // All other requests have any port stripped and path cleaned - // before passing to mux.handler. - host := stripHostPort(r.Host) - path := cleanPath(r.URL.Path) - - // If the given path is /tree and its handler is not registered, - // redirect for /tree/. - if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok { - return RedirectHandler(u.String(), StatusMovedPermanently), u.Path - } - - if path != r.URL.Path { - _, pattern = mux.handler(host, path) - url := *r.URL - url.Path = path - return RedirectHandler(url.String(), StatusMovedPermanently), pattern - } - - return mux.handler(host, r.URL.Path) -} - -// handler is the main implementation of Handler. -// The path is known to be in canonical form, except for CONNECT methods. -func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) { - mux.mu.RLock() - defer mux.mu.RUnlock() - - // Host-specific pattern takes precedence over generic ones - if mux.hosts { - h, pattern = mux.match(host + path) - } - if h == nil { - h, pattern = mux.match(path) - } - if h == nil { - h, pattern = NotFoundHandler(), "" - } - return -} - -// ServeHTTP dispatches the request to the handler whose -// pattern most closely matches the request URL. -func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { - if r.RequestURI == "*" { - if r.ProtoAtLeast(1, 1) { - w.Header().Set("Connection", "close") - } - w.WriteHeader(StatusBadRequest) - return - } - h, _ := mux.Handler(r) - h.ServeHTTP(w, r) -} - -// Handle registers the handler for the given pattern. -// If a handler already exists for pattern, Handle panics. -func (mux *ServeMux) Handle(pattern string, handler Handler) { - mux.mu.Lock() - defer mux.mu.Unlock() - - if pattern == "" { - panic("http: invalid pattern") - } - if handler == nil { - panic("http: nil handler") - } - if _, exist := mux.m[pattern]; exist { - panic("http: multiple registrations for " + pattern) - } - - if mux.m == nil { - mux.m = make(map[string]muxEntry) - } - e := muxEntry{h: handler, pattern: pattern} - mux.m[pattern] = e - if pattern[len(pattern)-1] == '/' { - mux.es = appendSorted(mux.es, e) - } - - if pattern[0] != '/' { - mux.hosts = true - } -} - -func appendSorted(es []muxEntry, e muxEntry) []muxEntry { - n := len(es) - i := sort.Search(n, func(i int) bool { - return len(es[i].pattern) < len(e.pattern) - }) - if i == n { - return append(es, e) - } - // we now know that i points at where we want to insert - es = append(es, muxEntry{}) // try to grow the slice in place, any entry works. - copy(es[i+1:], es[i:]) // Move shorter entries down - es[i] = e - return es -} - -// HandleFunc registers the handler function for the given pattern. -func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { - if handler == nil { - panic("http: nil handler") - } - mux.Handle(pattern, HandlerFunc(handler)) -} - -// Handle registers the handler for the given pattern -// in the DefaultServeMux. -// The documentation for ServeMux explains how patterns are matched. -func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) } - -// HandleFunc registers the handler function for the given pattern -// in the DefaultServeMux. -// The documentation for ServeMux explains how patterns are matched. -func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { - DefaultServeMux.HandleFunc(pattern, handler) -} - -// ListenAndServe listens on the TCP network address addr and then calls -// Serve with handler to handle requests on incoming connections. -// Accepted connections are configured to enable TCP keep-alives. -// -// The handler is typically nil, in which case the DefaultServeMux is used. -// -// ListenAndServe always returns a non-nil error. -func ListenAndServe(addr string, handler Handler) error { - return ActiveDevice.ListenAndServe(addr, handler) -} diff --git a/net/http/status.go b/net/http/status.go deleted file mode 100644 index 286315f63..000000000 --- a/net/http/status.go +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package http - -// HTTP status codes as registered with IANA. -// See: https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml -const ( - StatusContinue = 100 // RFC 7231, 6.2.1 - StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2 - StatusProcessing = 102 // RFC 2518, 10.1 - StatusEarlyHints = 103 // RFC 8297 - - StatusOK = 200 // RFC 7231, 6.3.1 - StatusCreated = 201 // RFC 7231, 6.3.2 - StatusAccepted = 202 // RFC 7231, 6.3.3 - StatusNonAuthoritativeInfo = 203 // RFC 7231, 6.3.4 - StatusNoContent = 204 // RFC 7231, 6.3.5 - StatusResetContent = 205 // RFC 7231, 6.3.6 - StatusPartialContent = 206 // RFC 7233, 4.1 - StatusMultiStatus = 207 // RFC 4918, 11.1 - StatusAlreadyReported = 208 // RFC 5842, 7.1 - StatusIMUsed = 226 // RFC 3229, 10.4.1 - - StatusMultipleChoices = 300 // RFC 7231, 6.4.1 - StatusMovedPermanently = 301 // RFC 7231, 6.4.2 - StatusFound = 302 // RFC 7231, 6.4.3 - StatusSeeOther = 303 // RFC 7231, 6.4.4 - StatusNotModified = 304 // RFC 7232, 4.1 - StatusUseProxy = 305 // RFC 7231, 6.4.5 - _ = 306 // RFC 7231, 6.4.6 (Unused) - StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7 - StatusPermanentRedirect = 308 // RFC 7538, 3 - - StatusBadRequest = 400 // RFC 7231, 6.5.1 - StatusUnauthorized = 401 // RFC 7235, 3.1 - StatusPaymentRequired = 402 // RFC 7231, 6.5.2 - StatusForbidden = 403 // RFC 7231, 6.5.3 - StatusNotFound = 404 // RFC 7231, 6.5.4 - StatusMethodNotAllowed = 405 // RFC 7231, 6.5.5 - StatusNotAcceptable = 406 // RFC 7231, 6.5.6 - StatusProxyAuthRequired = 407 // RFC 7235, 3.2 - StatusRequestTimeout = 408 // RFC 7231, 6.5.7 - StatusConflict = 409 // RFC 7231, 6.5.8 - StatusGone = 410 // RFC 7231, 6.5.9 - StatusLengthRequired = 411 // RFC 7231, 6.5.10 - StatusPreconditionFailed = 412 // RFC 7232, 4.2 - StatusRequestEntityTooLarge = 413 // RFC 7231, 6.5.11 - StatusRequestURITooLong = 414 // RFC 7231, 6.5.12 - StatusUnsupportedMediaType = 415 // RFC 7231, 6.5.13 - StatusRequestedRangeNotSatisfiable = 416 // RFC 7233, 4.4 - StatusExpectationFailed = 417 // RFC 7231, 6.5.14 - StatusTeapot = 418 // RFC 7168, 2.3.3 - StatusMisdirectedRequest = 421 // RFC 7540, 9.1.2 - StatusUnprocessableEntity = 422 // RFC 4918, 11.2 - StatusLocked = 423 // RFC 4918, 11.3 - StatusFailedDependency = 424 // RFC 4918, 11.4 - StatusTooEarly = 425 // RFC 8470, 5.2. - StatusUpgradeRequired = 426 // RFC 7231, 6.5.15 - StatusPreconditionRequired = 428 // RFC 6585, 3 - StatusTooManyRequests = 429 // RFC 6585, 4 - StatusRequestHeaderFieldsTooLarge = 431 // RFC 6585, 5 - StatusUnavailableForLegalReasons = 451 // RFC 7725, 3 - - StatusInternalServerError = 500 // RFC 7231, 6.6.1 - StatusNotImplemented = 501 // RFC 7231, 6.6.2 - StatusBadGateway = 502 // RFC 7231, 6.6.3 - StatusServiceUnavailable = 503 // RFC 7231, 6.6.4 - StatusGatewayTimeout = 504 // RFC 7231, 6.6.5 - StatusHTTPVersionNotSupported = 505 // RFC 7231, 6.6.6 - StatusVariantAlsoNegotiates = 506 // RFC 2295, 8.1 - StatusInsufficientStorage = 507 // RFC 4918, 11.5 - StatusLoopDetected = 508 // RFC 5842, 7.2 - StatusNotExtended = 510 // RFC 2774, 7 - StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6 -) - -var statusText = map[int]string{ - StatusContinue: "Continue", - StatusSwitchingProtocols: "Switching Protocols", - StatusProcessing: "Processing", - StatusEarlyHints: "Early Hints", - - StatusOK: "OK", - StatusCreated: "Created", - StatusAccepted: "Accepted", - StatusNonAuthoritativeInfo: "Non-Authoritative Information", - StatusNoContent: "No Content", - StatusResetContent: "Reset Content", - StatusPartialContent: "Partial Content", - StatusMultiStatus: "Multi-Status", - StatusAlreadyReported: "Already Reported", - StatusIMUsed: "IM Used", - - StatusMultipleChoices: "Multiple Choices", - StatusMovedPermanently: "Moved Permanently", - StatusFound: "Found", - StatusSeeOther: "See Other", - StatusNotModified: "Not Modified", - StatusUseProxy: "Use Proxy", - StatusTemporaryRedirect: "Temporary Redirect", - StatusPermanentRedirect: "Permanent Redirect", - - StatusBadRequest: "Bad Request", - StatusUnauthorized: "Unauthorized", - StatusPaymentRequired: "Payment Required", - StatusForbidden: "Forbidden", - StatusNotFound: "Not Found", - StatusMethodNotAllowed: "Method Not Allowed", - StatusNotAcceptable: "Not Acceptable", - StatusProxyAuthRequired: "Proxy Authentication Required", - StatusRequestTimeout: "Request Timeout", - StatusConflict: "Conflict", - StatusGone: "Gone", - StatusLengthRequired: "Length Required", - StatusPreconditionFailed: "Precondition Failed", - StatusRequestEntityTooLarge: "Request Entity Too Large", - StatusRequestURITooLong: "Request URI Too Long", - StatusUnsupportedMediaType: "Unsupported Media Type", - StatusRequestedRangeNotSatisfiable: "Requested Range Not Satisfiable", - StatusExpectationFailed: "Expectation Failed", - StatusTeapot: "I'm a teapot", - StatusMisdirectedRequest: "Misdirected Request", - StatusUnprocessableEntity: "Unprocessable Entity", - StatusLocked: "Locked", - StatusFailedDependency: "Failed Dependency", - StatusTooEarly: "Too Early", - StatusUpgradeRequired: "Upgrade Required", - StatusPreconditionRequired: "Precondition Required", - StatusTooManyRequests: "Too Many Requests", - StatusRequestHeaderFieldsTooLarge: "Request Header Fields Too Large", - StatusUnavailableForLegalReasons: "Unavailable For Legal Reasons", - - StatusInternalServerError: "Internal Server Error", - StatusNotImplemented: "Not Implemented", - StatusBadGateway: "Bad Gateway", - StatusServiceUnavailable: "Service Unavailable", - StatusGatewayTimeout: "Gateway Timeout", - StatusHTTPVersionNotSupported: "HTTP Version Not Supported", - StatusVariantAlsoNegotiates: "Variant Also Negotiates", - StatusInsufficientStorage: "Insufficient Storage", - StatusLoopDetected: "Loop Detected", - StatusNotExtended: "Not Extended", - StatusNetworkAuthenticationRequired: "Network Authentication Required", -} - -// StatusText returns a text for the HTTP status code. It returns the empty -// string if the code is unknown. -func StatusText(code int) string { - return statusText[code] -} diff --git a/net/http/tinygo.go b/net/http/tinygo.go deleted file mode 100644 index 6ac34495a..000000000 --- a/net/http/tinygo.go +++ /dev/null @@ -1,308 +0,0 @@ -package http - -import ( - "bufio" - "bytes" - "fmt" - "io" - "strconv" - "strings" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/net/tls" -) - -var buf []byte - -func SetBuf(b []byte) { - buf = b -} - -func (c *Client) Do(req *Request) (*Response, error) { - if c.Jar != nil { - for _, cookie := range c.Jar.Cookies(req.URL) { - req.AddCookie(cookie) - } - } - switch req.URL.Scheme { - case "http": - return c.doHTTP(req) - case "https": - return c.doHTTPS(req) - default: - return nil, fmt.Errorf("invalid schemer : %s", req.URL.Scheme) - } -} - -func (c *Client) doHTTP(req *Request) (*Response, error) { - // make TCP connection - ip := net.ParseIP(req.URL.Hostname()) - port := 80 - if req.URL.Port() != "" { - p, err := strconv.ParseUint(req.URL.Port(), 0, 64) - if err != nil { - return nil, err - } - port = int(p) - } - raddr := &net.TCPAddr{IP: ip, Port: port} - laddr := &net.TCPAddr{Port: 8080} - - conn, err := net.DialTCP("tcp", laddr, raddr) - retry := 0 - for ; err != nil; conn, err = net.DialTCP("tcp", laddr, raddr) { - retry++ - if retry > 10 { - return nil, fmt.Errorf("Connection failed: %s", err.Error()) - } - time.Sleep(1 * time.Second) - } - - p := req.URL.Path - if p == "" { - p = "/" - } - if req.URL.RawQuery != "" { - p += "?" + req.URL.RawQuery - } - fmt.Fprintln(conn, req.Method+" "+p+" HTTP/1.1") - fmt.Fprintln(conn, "Host:", req.URL.Host) - - if req.Header.get(`User-Agent`) == "" { - fmt.Fprintln(conn, "User-Agent: TinyGo") - } - - for k, v := range req.Header { - if v == nil || len(v) == 0 { - return nil, fmt.Errorf("req.Header error: %s", k) - } - fmt.Fprintln(conn, k+": "+v[0]) - } - - if req.Header.get(`Connection`) == "" { - fmt.Fprintln(conn, "Connection: close") - } - - if req.ContentLength > 0 { - fmt.Fprintf(conn, "Content-Length: %d\n", req.ContentLength) - } - - fmt.Fprintln(conn) - - if req.ContentLength > 0 { - b, err := req.GetBody() - if err != nil { - return nil, err - } - - n, err := b.Read(buf) - if err != nil { - return nil, err - } - conn.Write(buf[:n]) - - b.Close() - - } - - return c.doResp(conn, req) -} - -func (c *Client) doHTTPS(req *Request) (*Response, error) { - conn, err := tls.Dial("tcp", req.URL.Host, nil) - retry := 0 - for ; err != nil; conn, err = tls.Dial("tcp", req.URL.Host, nil) { - retry++ - if retry > 10 { - return nil, fmt.Errorf("Connection failed: %s", err.Error()) - } - time.Sleep(1 * time.Second) - } - - p := req.URL.Path - if p == "" { - p = "/" - } - if req.URL.RawQuery != "" { - p += "?" + req.URL.RawQuery - } - fmt.Fprintln(conn, req.Method+" "+p+" HTTP/1.1") - fmt.Fprintln(conn, "Host:", req.URL.Host) - - if req.Header.get(`User-Agent`) == "" { - fmt.Fprintln(conn, "User-Agent: TinyGo") - } - - for k, v := range req.Header { - if v == nil || len(v) == 0 { - return nil, fmt.Errorf("req.Header error: %s", k) - } - fmt.Fprintln(conn, k+": "+v[0]) - } - - if req.Header.get(`Connection`) == "" { - fmt.Fprintln(conn, "Connection: close") - } - - if req.ContentLength > 0 { - fmt.Fprintf(conn, "Content-Length: %d\n", req.ContentLength) - } - - fmt.Fprintln(conn) - - if req.ContentLength > 0 { - b, err := req.GetBody() - if err != nil { - return nil, err - } - - n, err := b.Read(buf) - if err != nil { - return nil, err - } - conn.Write(buf[:n]) - - b.Close() - - } - - return c.doResp(conn, req) -} - -func (c *Client) doResp(conn net.Conn, req *Request) (*Response, error) { - resp := &Response{ - Header: map[string][]string{}, - } - - // Header - var scanner *bufio.Scanner - cont := true - ofs := 0 - remain := int64(0) - for cont { - for n, err := conn.Read(buf[ofs:]); n > 0; n, err = conn.Read(buf[ofs:]) { - if err != nil { - println("Read error: " + err.Error()) - } else { - // Take care of the case where "\r\n\r\n" is on the boundary of a buffer - start := ofs - if start > 3 { - start -= 3 - } - idx := bytes.Index(buf[start:ofs+n], []byte("\r\n\r\n")) - if idx == -1 { - ofs += n - continue - } - idx += start + 4 - - scanner = bufio.NewScanner(bytes.NewReader(buf[0 : ofs+n])) - if resp.Status == "" && scanner.Scan() { - status := strings.SplitN(scanner.Text(), " ", 2) - if len(status) != 2 { - conn.Close() - return nil, fmt.Errorf("invalid status : %q", scanner.Text()) - } - resp.Proto = status[0] - fmt.Sscanf(status[0], "HTTP/%d.%d", &resp.ProtoMajor, &resp.ProtoMinor) - - resp.Status = status[1] - fmt.Sscanf(status[1], "%d", &resp.StatusCode) - } - - for scanner.Scan() { - text := scanner.Text() - if text == "" { - // end of header - if idx < n+ofs { - ofs = ofs + n - idx - for i := 0; i < ofs; i++ { - buf[i] = buf[i+idx] - } - } else { - ofs = 0 - } - break - } else { - header := strings.SplitN(text, ": ", 2) - if len(header) != 2 { - conn.Close() - return nil, fmt.Errorf("invalid header : %q", text) - } - if resp.Header.Get(header[0]) == "" { - resp.Header.Set(header[0], header[1]) - } else { - resp.Header.Add(header[0], header[1]) - } - - if strings.ToLower(header[0]) == "content-length" { - resp.ContentLength, err = strconv.ParseInt(header[1], 10, 64) - if err != nil { - conn.Close() - return nil, err - } - remain = resp.ContentLength - } - } - } - cont = false - break - } - } - } - - // Body - remain -= int64(ofs) - if remain <= 0 { - resp.Body = io.NopCloser(bytes.NewReader(buf[:ofs])) - if c.Jar != nil { - if rc := resp.Cookies(); len(rc) > 0 { - c.Jar.SetCookies(req.URL, rc) - } - } - return resp, conn.Close() - } - - cont = true - lastRequestTime := time.Now() - for cont { - for { - end := ofs + 0x400 - if len(buf) < end { - return nil, fmt.Errorf("slice out of range : use http.SetBuf() to change the allocation to %d bytes or more", end) - } - n, err := conn.Read(buf[ofs : ofs+0x400]) - if err != nil { - return nil, err - } - if n == 0 { - continue - } - if err != nil { - conn.Close() - return nil, err - } else { - ofs += n - remain -= int64(n) - if remain <= 0 { - resp.Body = io.NopCloser(bytes.NewReader(buf[:ofs])) - cont = false - break - } - if time.Now().Sub(lastRequestTime).Milliseconds() >= 1000 { - conn.Close() - return nil, fmt.Errorf("time out") - } - } - } - } - - if c.Jar != nil { - if rc := resp.Cookies(); len(rc) > 0 { - c.Jar.SetCookies(req.URL, rc) - } - } - - return resp, conn.Close() -} diff --git a/net/http/transefer.go b/net/http/transefer.go deleted file mode 100644 index d9a440621..000000000 --- a/net/http/transefer.go +++ /dev/null @@ -1,34 +0,0 @@ -package http - -import ( - "bufio" - - "golang.org/x/net/http/httpguts" -) - -// msg is *Request or *Response. -func readTransfer(msg *Request, r *bufio.Reader) (err error) { - // TODO: - return nil -} - -// Determine whether to hang up after sending a request and body, or -// receiving a response and body -// 'header' is the request headers -func shouldClose(major, minor int, header Header, removeCloseHeader bool) bool { - if major < 1 { - return true - } - - conv := header["Connection"] - hasClose := httpguts.HeaderValuesContainsToken(conv, "close") - if major == 1 && minor == 0 { - return hasClose || !httpguts.HeaderValuesContainsToken(conv, "keep-alive") - } - - if hasClose && removeCloseHeader { - header.Del("Connection") - } - - return hasClose -} diff --git a/net/ipsocki.go b/net/ipsocki.go deleted file mode 100644 index dfc30c9b2..000000000 --- a/net/ipsocki.go +++ /dev/null @@ -1,26 +0,0 @@ -package net - -import "strings" - -// SplitHostPort splits a network address of the form "host:port", -// "host%zone:port", "[host]:port" or "[host%zone]:port" into host or -// host%zone and port. -// -// A literal IPv6 address in hostport must be enclosed in square -// brackets, as in "[::1]:80", "[::1%lo0]:80". -// -// See func Dial for a description of the hostport parameter, and host -// and port results. -func SplitHostPort(hostport string) (host, port string, err error) { - - if strings.Contains(hostport, ":") { - spl := strings.Split(hostport, ":") - host = spl[0] - port = spl[1] - } else { - host = hostport - port = "80" - } - - return host, port, nil -} diff --git a/net/mqtt/mqtt.go b/net/mqtt/mqtt.go deleted file mode 100644 index c18641ba9..000000000 --- a/net/mqtt/mqtt.go +++ /dev/null @@ -1,396 +0,0 @@ -// Package mqtt is intended to provide compatible interfaces with the -// Paho mqtt library. -package mqtt - -import ( - "errors" - "strings" - "sync" - "time" - - "github.com/eclipse/paho.mqtt.golang/packets" - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/net/tls" -) - -// NewClient will create an MQTT v3.1.1 client with all of the options specified -// in the provided ClientOptions. The client must have the Connect method called -// on it before it may be used. This is to make sure resources (such as a net -// connection) are created before the application is actually ready. -func NewClient(o *ClientOptions) Client { - c := &mqttclient{opts: o, adaptor: o.Adaptor} - c.msgRouter, c.stopRouter = newRouter() - - c.inboundPacketChan = make(chan packets.ControlPacket, 10) - c.stopInbound = make(chan struct{}) - c.incomingPubChan = make(chan *packets.PublishPacket, 10) - // this launches a goroutine, so only call once per client: - c.msgRouter.matchAndDispatch(c.incomingPubChan, c.opts.Order, c) - return c -} - -type mqttclient struct { - adaptor net.Adapter - conn net.Conn - connected bool - opts *ClientOptions - mid uint16 - inboundPacketChan chan packets.ControlPacket - stopInbound chan struct{} - msgRouter *router - stopRouter chan bool - incomingPubChan chan *packets.PublishPacket - // stats for keepalive - lastReceive time.Time - lastSend time.Time - // keep track of routines and signal a shutdown - workers sync.WaitGroup - shutdown bool -} - -// AddRoute allows you to add a handler for messages on a specific topic -// without making a subscription. For example having a different handler -// for parts of a wildcard subscription -func (c *mqttclient) AddRoute(topic string, callback MessageHandler) { - return -} - -// IsConnected returns a bool signifying whether -// the client is connected or not. -func (c *mqttclient) IsConnected() bool { - return c.connected -} - -// IsConnectionOpen return a bool signifying whether the client has an active -// connection to mqtt broker, i.e not in disconnected or reconnect mode -func (c *mqttclient) IsConnectionOpen() bool { - return c.connected -} - -// Connect will create a connection to the message broker. -func (c *mqttclient) Connect() Token { - if c.IsConnected() { - return &mqtttoken{} - } - var err error - - // make connection - if strings.Contains(c.opts.Servers, "ssl://") { - url := strings.TrimPrefix(c.opts.Servers, "ssl://") - c.conn, err = tls.Dial("tcp", url, nil) - if err != nil { - return &mqtttoken{err: err} - } - } else if strings.Contains(c.opts.Servers, "tcp://") { - url := strings.TrimPrefix(c.opts.Servers, "tcp://") - c.conn, err = net.Dial("tcp", url) - if err != nil { - return &mqtttoken{err: err} - } - } else { - // invalid protocol - return &mqtttoken{err: errors.New("invalid protocol")} - } - - c.mid = 1 - - // send the MQTT connect message - connectPkt := packets.NewControlPacket(packets.Connect).(*packets.ConnectPacket) - connectPkt.Qos = 0 - if c.opts.Username != "" { - connectPkt.Username = c.opts.Username - connectPkt.UsernameFlag = true - } - - if c.opts.Password != "" { - connectPkt.Password = []byte(c.opts.Password) - connectPkt.PasswordFlag = true - } - - connectPkt.ClientIdentifier = c.opts.ClientID - connectPkt.ProtocolVersion = byte(c.opts.ProtocolVersion) - connectPkt.ProtocolName = "MQTT" - connectPkt.Keepalive = uint16(c.opts.KeepAlive) - - connectPkt.WillFlag = c.opts.WillEnabled - connectPkt.WillTopic = c.opts.WillTopic - connectPkt.WillMessage = c.opts.WillPayload - connectPkt.WillQos = c.opts.WillQos - connectPkt.WillRetain = c.opts.WillRetained - - err = connectPkt.Write(c.conn) - if err != nil { - return &mqtttoken{err: err} - } - c.lastSend = time.Now() - - // TODO: handle timeout as ReadPacket blocks until it gets a packet. - // CONNECT response. - packet, err := packets.ReadPacket(c.conn) - if err != nil { - return &mqtttoken{err: err} - } - if packet != nil { - ack, ok := packet.(*packets.ConnackPacket) - if ok { - if ack.ReturnCode != 0 { - return &mqtttoken{err: errors.New(packet.String())} - } - c.connected = true - } - } - - go processInbound(c) - go readMessages(c) - go keepAlive(c) - - return &mqtttoken{} -} - -// Disconnect will end the connection with the server, but not before waiting -// the specified number of milliseconds to wait for existing work to be -// completed. Blocks until disconnected. -func (c *mqttclient) Disconnect(quiesce uint) { - c.shutdownRoutines() - // block until all done - for c.connected { - time.Sleep(time.Millisecond * 10) - } - return -} - -// shutdownRoutines will disconnect and shut down all processes. If you want to trigger a -// disconnect internally, make sure you call this instead of Disconnect() to avoid deadlocks -func (c *mqttclient) shutdownRoutines() { - if c.shutdown { - return - } - c.shutdown = true - c.conn.Close() - c.stopInbound <- struct{}{} -} - -// Publish will publish a message with the specified QoS and content -// to the specified topic. -// Returns a token to track delivery of the message to the broker -func (c *mqttclient) Publish(topic string, qos byte, retained bool, payload interface{}) Token { - if !c.IsConnected() { - return &mqtttoken{err: errors.New("MQTT client not connected")} - } - - pub := packets.NewControlPacket(packets.Publish).(*packets.PublishPacket) - pub.Qos = qos - pub.TopicName = topic - pub.Retain = retained - - switch payload.(type) { - case string: - pub.Payload = []byte(payload.(string)) - case []byte: - pub.Payload = payload.([]byte) - default: - return &mqtttoken{err: errors.New("Unknown payload type")} - } - pub.MessageID = c.mid - c.mid++ - - err := pub.Write(c.conn) - if err != nil { - return &mqtttoken{err: err} - } - // update this for every control message that is sent successfully, for keepalive - c.lastSend = time.Now() - - return &mqtttoken{} -} - -// Subscribe starts a new subscription. Provide a MessageHandler to be executed when -// a message is published on the topic provided. -func (c *mqttclient) Subscribe(topic string, qos byte, callback MessageHandler) Token { - if !c.IsConnected() { - return &mqtttoken{err: errors.New("MQTT client not connected")} - } - - sub := packets.NewControlPacket(packets.Subscribe).(*packets.SubscribePacket) - sub.Topics = append(sub.Topics, topic) - sub.Qoss = append(sub.Qoss, qos) - - if callback != nil { - c.msgRouter.addRoute(topic, callback) - } - - sub.MessageID = c.mid - c.mid++ - - // drop in the channel to send - err := sub.Write(c.conn) - if err != nil { - return &mqtttoken{err: err} - } - c.lastSend = time.Now() - - return &mqtttoken{} -} - -// SubscribeMultiple starts a new subscription for multiple topics. Provide a MessageHandler to -// be executed when a message is published on one of the topics provided. -func (c *mqttclient) SubscribeMultiple(filters map[string]byte, callback MessageHandler) Token { - return &mqtttoken{} -} - -// Unsubscribe will end the subscription from each of the topics provided. -// Messages published to those topics from other clients will no longer be -// received. -func (c *mqttclient) Unsubscribe(topics ...string) Token { - return &mqtttoken{} -} - -// OptionsReader returns a ClientOptionsReader which is a copy of the clientoptions -// in use by the client. -func (c *mqttclient) OptionsReader() ClientOptionsReader { - r := ClientOptionsReader{} - return r -} - -func processInbound(c *mqttclient) { -PROCESS: - for { - select { - case msg := <-c.inboundPacketChan: - switch m := msg.(type) { - case *packets.PingrespPacket: - // println("pong") - case *packets.SubackPacket: - // TODO: handle this - case *packets.UnsubackPacket: - // TODO: handle this - case *packets.PublishPacket: - // TODO: handle Qos - c.incomingPubChan <- m - case *packets.PubackPacket: - // TODO: handle this - case *packets.PubrecPacket: - // TODO: handle this - case *packets.PubrelPacket: - // TODO: handle this - case *packets.PubcompPacket: - // TODO: handle this - } - case <-c.stopInbound: - break PROCESS - } - } - - // as this routine could be the last to finish (if a lot of messages are queued in the - // channel), it is the last to turn out the lights - - c.workers.Wait() - c.connected = false - c.shutdown = false -} - -// readMessages reads incoming messages off the wire. -// incoming messages are then send into inbound buffered channel. -func readMessages(c *mqttclient) { - c.workers.Add(1) - defer c.workers.Done() - - var err error - var cp packets.ControlPacket - - for !c.shutdown { - if cp, err = c.ReadPacket(); err != nil { - c.shutdownRoutines() - return - } - if cp != nil { - c.inboundPacketChan <- cp - // notify keepalive logic that we recently received a packet - c.lastReceive = time.Now() - } - - time.Sleep(100 * time.Millisecond) - } -} - -// keepAlive is a goroutine to handle sending ping requests according to the MQTT spec. If the keepalive time has -// been reached with no messages being sent, we will send a ping request and check back to see if we've -// had any activity by the timeout. If not, disconnect. -func keepAlive(c *mqttclient) { - c.workers.Add(1) - defer c.workers.Done() - - var err error - var ping *packets.PingreqPacket - var timeout, pingsent time.Time - - for !c.shutdown { - // As long as we haven't reached the keepalive value... - if time.Since(c.lastSend) < time.Duration(c.opts.KeepAlive)*time.Second { - // ...sleep and check shutdown status again - time.Sleep(time.Millisecond * 100) - continue - } - - // value has been reached, so send a ping request - ping = packets.NewControlPacket(packets.Pingreq).(*packets.PingreqPacket) - if err = ping.Write(c.conn); err != nil { - // if connection is lost, report disconnect - c.shutdownRoutines() - return - } - // println("ping") - - c.lastSend = time.Now() - pingsent = time.Now() - timeout = pingsent.Add(c.opts.PingTimeout) - - // as long as we are still connected and haven't received anything after the ping... - for !c.shutdown && c.lastReceive.Before(pingsent) { - // if the timeout has passed, disconnect - if time.Now().After(timeout) { - c.shutdownRoutines() - return - } - time.Sleep(time.Millisecond * 100) - } - } -} - -func (c *mqttclient) ackFunc(packet *packets.PublishPacket) func() { - return func() { - switch packet.Qos { - case 2: - // pr := packets.NewControlPacket(packets.Pubrec).(*packets.PubrecPacket) - // pr.MessageID = packet.MessageID - // DEBUG.Println(NET, "putting pubrec msg on obound") - // select { - // case c.oboundP <- &PacketAndToken{p: pr, t: nil}: - // case <-c.stop: - // } - // DEBUG.Println(NET, "done putting pubrec msg on obound") - case 1: - // pa := packets.NewControlPacket(packets.Puback).(*packets.PubackPacket) - // pa.MessageID = packet.MessageID - // DEBUG.Println(NET, "putting puback msg on obound") - // persistOutbound(c.persist, pa) - // select { - // case c.oboundP <- &PacketAndToken{p: pa, t: nil}: - // case <-c.stop: - // } - // DEBUG.Println(NET, "done putting puback msg on obound") - case 0: - // do nothing, since there is no need to send an ack packet back - } - } -} - -// ReadPacket tries to read the next incoming packet from the MQTT broker. -// If there is no data yet but also is no error, it returns nil for both values. -func (c *mqttclient) ReadPacket() (packets.ControlPacket, error) { - // check for data first... - if net.ActiveDevice.IsSocketDataAvailable() { - return packets.ReadPacket(c.conn) - } - return nil, nil -} diff --git a/net/mqtt/paho.go b/net/mqtt/paho.go deleted file mode 100644 index ff9c7af52..000000000 --- a/net/mqtt/paho.go +++ /dev/null @@ -1,299 +0,0 @@ -// The following code is a slightly modified version of code taken from the Paho MQTT library. -// It is here until TinyGo can compile the "net" package from the standard library, at which time -// it can be removed. - -/* - * Copyright (c) 2013 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Seth Hoenig - * Allan Stockdill-Mander - * Mike Robertson - */ - -// Portions copyright © 2018 TIBCO Software Inc. - -package mqtt - -import ( - "strings" - "time" - - "github.com/eclipse/paho.mqtt.golang/packets" - "tinygo.org/x/drivers/net" -) - -const ( - disconnected uint32 = iota - connecting - reconnecting - connected -) - -// Client is the interface definition for a Client as used by this -// library, the interface is primarily to allow mocking tests. -// -// It is an MQTT v3.1.1 client for communicating -// with an MQTT server using non-blocking methods that allow work -// to be done in the background. -// An application may connect to an MQTT server using: -// -// A plain TCP socket -// A secure SSL/TLS socket -// A websocket -// -// To enable ensured message delivery at Quality of Service (QoS) levels -// described in the MQTT spec, a message persistence mechanism must be -// used. This is done by providing a type which implements the Store -// interface. For convenience, FileStore and MemoryStore are provided -// implementations that should be sufficient for most use cases. More -// information can be found in their respective documentation. -// Numerous connection options may be specified by configuring a -// and then supplying a ClientOptions type. -type Client interface { - // IsConnected returns a bool signifying whether - // the client is connected or not. - IsConnected() bool - // IsConnectionOpen return a bool signifying wether the client has an active - // connection to mqtt broker, i.e not in disconnected or reconnect mode - IsConnectionOpen() bool - // Connect will create a connection to the message broker, by default - // it will attempt to connect at v3.1.1 and auto retry at v3.1 if that - // fails - Connect() Token - // Disconnect will end the connection with the server, but not before waiting - // the specified number of milliseconds to wait for existing work to be - // completed. - Disconnect(quiesce uint) - // Publish will publish a message with the specified QoS and content - // to the specified topic. - // Returns a token to track delivery of the message to the broker - Publish(topic string, qos byte, retained bool, payload interface{}) Token - // Subscribe starts a new subscription. Provide a MessageHandler to be executed when - // a message is published on the topic provided, or nil for the default handler - Subscribe(topic string, qos byte, callback MessageHandler) Token - // SubscribeMultiple starts a new subscription for multiple topics. Provide a MessageHandler to - // be executed when a message is published on one of the topics provided, or nil for the - // default handler - SubscribeMultiple(filters map[string]byte, callback MessageHandler) Token - // Unsubscribe will end the subscription from each of the topics provided. - // Messages published to those topics from other clients will no longer be - // received. - Unsubscribe(topics ...string) Token - // AddRoute allows you to add a handler for messages on a specific topic - // without making a subscription. For example having a different handler - // for parts of a wildcard subscription - AddRoute(topic string, callback MessageHandler) - // OptionsReader returns a ClientOptionsReader which is a copy of the clientoptions - // in use by the client. - OptionsReader() ClientOptionsReader -} - -// Token defines the interface for the tokens used to indicate when -// actions have completed. -type Token interface { - Wait() bool - WaitTimeout(time.Duration) bool - Error() error -} - -// MessageHandler is a callback type which can be set to be -// executed upon the arrival of messages published to topics -// to which the client is subscribed. -type MessageHandler func(Client, Message) - -// Message defines the externals that a message implementation must support -// these are received messages that are passed to the callbacks, not internal -// messages -type Message interface { - Duplicate() bool - Qos() byte - Retained() bool - Topic() string - MessageID() uint16 - Payload() []byte - Ack() -} - -type message struct { - duplicate bool - qos byte - retained bool - topic string - messageID uint16 - payload []byte - ack func() -} - -func (m *message) Duplicate() bool { - return m.duplicate -} - -func (m *message) Qos() byte { - return m.qos -} - -func (m *message) Retained() bool { - return m.retained -} - -func (m *message) Topic() string { - return m.topic -} - -func (m *message) MessageID() uint16 { - return m.messageID -} - -func (m *message) Payload() []byte { - return m.payload -} - -func (m *message) Ack() { - return -} - -func messageFromPublish(p *packets.PublishPacket, ack func()) Message { - return &message{ - duplicate: p.Dup, - qos: p.Qos, - retained: p.Retain, - topic: p.TopicName, - messageID: p.MessageID, - payload: p.Payload, - ack: ack, - } -} - -// ClientOptionsReader provides an interface for reading ClientOptions after the client has been initialized. -type ClientOptionsReader struct { - options *ClientOptions -} - -// ClientOptions contains configurable options for an MQTT Client. -type ClientOptions struct { - Adaptor net.Adapter - - //Servers []*url.URL - Servers string - ClientID string - Username string - Password string - //CredentialsProvider CredentialsProvider - CleanSession bool - Order bool - WillEnabled bool - WillTopic string - WillPayload []byte - WillQos byte - WillRetained bool - ProtocolVersion uint - protocolVersionExplicit bool - //TLSConfig *tls.Config - KeepAlive int64 - PingTimeout time.Duration - ConnectTimeout time.Duration - MaxReconnectInterval time.Duration - AutoReconnect bool - //Store Store - //DefaultPublishHandler MessageHandler - //OnConnect OnConnectHandler - //OnConnectionLost ConnectionLostHandler - WriteTimeout time.Duration - MessageChannelDepth uint - ResumeSubs bool - //HTTPHeaders http.Header -} - -// NewClientOptions returns a new ClientOptions struct. -func NewClientOptions() *ClientOptions { - return &ClientOptions{Adaptor: net.ActiveDevice, ProtocolVersion: 4, KeepAlive: 60, PingTimeout: time.Second * 10} -} - -// AddBroker adds a broker URI to the list of brokers to be used. The format should be -// scheme://host:port -// Where "scheme" is one of "tcp", "ssl", or "ws", "host" is the ip-address (or hostname) -// and "port" is the port on which the broker is accepting connections. -// -// Default values for hostname is "127.0.0.1", for schema is "tcp://". -// -// An example broker URI would look like: tcp://foobar.com:1883 -func (o *ClientOptions) AddBroker(server string) *ClientOptions { - if len(server) > 0 && server[0] == ':' { - server = "127.0.0.1" + server - } - if !strings.Contains(server, "://") { - server = "tcp://" + server - } - - o.Servers = server - return o -} - -// SetClientID will set the client id to be used by this client when -// connecting to the MQTT broker. According to the MQTT v3.1 specification, -// a client id mus be no longer than 23 characters. -func (o *ClientOptions) SetClientID(id string) *ClientOptions { - o.ClientID = id - return o -} - -// SetUsername will set the username to be used by this client when connecting -// to the MQTT broker. Note: without the use of SSL/TLS, this information will -// be sent in plaintext accross the wire. -func (o *ClientOptions) SetUsername(u string) *ClientOptions { - o.Username = u - return o -} - -// SetPassword will set the password to be used by this client when connecting -// to the MQTT broker. Note: without the use of SSL/TLS, this information will -// be sent in plaintext accross the wire. -func (o *ClientOptions) SetPassword(p string) *ClientOptions { - o.Password = p - return o -} - -// SetKeepAlive will set the amount of time (in seconds) that the client -// should wait before sending a PING request to the broker. This will -// allow the client to know that a connection has not been lost with the -// server. -func (o *ClientOptions) SetKeepAlive(k time.Duration) *ClientOptions { - o.KeepAlive = int64(k / time.Second) - return o -} - -// SetPingTimeout will set the amount of time (in seconds) that the client -// will wait after sending a PING request to the broker, before deciding -// that the connection has been lost. Default is 10 seconds. -func (o *ClientOptions) SetPingTimeout(k time.Duration) *ClientOptions { - o.PingTimeout = k - return o -} - -// SetWill accepts a string will message to be set. When the client connects, -// it will give this will message to the broker, which will then publish the -// provided payload (the will) to any clients that are subscribed to the provided -// topic. -func (o *ClientOptions) SetWill(topic string, payload string, qos byte, retained bool) *ClientOptions { - o.SetBinaryWill(topic, []byte(payload), qos, retained) - return o -} - -// SetBinaryWill accepts a []byte will message to be set. When the client connects, -// it will give this will message to the broker, which will then publish the -// provided payload (the will) to any clients that are subscribed to the provided -// topic. -func (o *ClientOptions) SetBinaryWill(topic string, payload []byte, qos byte, retained bool) *ClientOptions { - o.WillEnabled = true - o.WillTopic = topic - o.WillPayload = payload - o.WillQos = qos - o.WillRetained = retained - return o -} diff --git a/net/mqtt/router.go b/net/mqtt/router.go deleted file mode 100644 index 7d9b930f1..000000000 --- a/net/mqtt/router.go +++ /dev/null @@ -1,178 +0,0 @@ -// The following code is a slightly modified version of code taken from the Paho MQTT library. -// It is here until TinyGo can compile the "net" package from the standard library, at which time -// it can be removed. - -/* - * Copyright (c) 2013 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Seth Hoenig - * Allan Stockdill-Mander - * Mike Robertson - */ - -package mqtt - -import ( - "container/list" - "strings" - - "github.com/eclipse/paho.mqtt.golang/packets" -) - -// route is a type which associates MQTT Topic strings with a -// callback to be executed upon the arrival of a message associated -// with a subscription to that topic. -type route struct { - topic string - callback MessageHandler -} - -// match takes a slice of strings which represent the route being tested having been split on '/' -// separators, and a slice of strings representing the topic string in the published message, similarly -// split. -// The function determines if the topic string matches the route according to the MQTT topic rules -// and returns a boolean of the outcome -func match(route []string, topic []string) bool { - if len(route) == 0 { - if len(topic) == 0 { - return true - } - return false - } - - if len(topic) == 0 { - if route[0] == "#" { - return true - } - return false - } - - if route[0] == "#" { - return true - } - - if (route[0] == "+") || (route[0] == topic[0]) { - return match(route[1:], topic[1:]) - } - return false -} - -func routeIncludesTopic(route, topic string) bool { - return match(routeSplit(route), strings.Split(topic, "/")) -} - -// removes $share and sharename when splitting the route to allow -// shared subscription routes to correctly match the topic -func routeSplit(route string) []string { - var result []string - if strings.HasPrefix(route, "$share") { - result = strings.Split(route, "/")[2:] - } else { - result = strings.Split(route, "/") - } - return result -} - -// match takes the topic string of the published message and does a basic compare to the -// string of the current Route, if they match it returns true -func (r *route) match(topic string) bool { - return r.topic == topic || routeIncludesTopic(r.topic, topic) -} - -type router struct { - //sync.RWMutex - routes *list.List - defaultHandler MessageHandler - messages chan *packets.PublishPacket - stop chan bool -} - -// newRouter returns a new instance of a Router and channel which can be used to tell the Router -// to stop -func newRouter() (*router, chan bool) { - router := &router{routes: list.New(), messages: make(chan *packets.PublishPacket), stop: make(chan bool)} - stop := router.stop - return router, stop -} - -// addRoute takes a topic string and MessageHandler callback. It looks in the current list of -// routes to see if there is already a matching Route. If there is it replaces the current -// callback with the new one. If not it add a new entry to the list of Routes. -func (r *router) addRoute(topic string, callback MessageHandler) { - for e := r.routes.Front(); e != nil; e = e.Next() { - if e.Value.(*route).match(topic) { - r := e.Value.(*route) - r.callback = callback - return - } - } - r.routes.PushBack(&route{topic: topic, callback: callback}) -} - -// deleteRoute takes a route string, looks for a matching Route in the list of Routes. If -// found it removes the Route from the list. -func (r *router) deleteRoute(topic string) { - for e := r.routes.Front(); e != nil; e = e.Next() { - if e.Value.(*route).match(topic) { - r.routes.Remove(e) - return - } - } -} - -// setDefaultHandler assigns a default callback that will be called if no matching Route -// is found for an incoming Publish. -func (r *router) setDefaultHandler(handler MessageHandler) { - r.defaultHandler = handler -} - -// matchAndDispatch takes a channel of Message pointers as input and starts a go routine that -// takes messages off the channel, matches them against the internal route list and calls the -// associated callback (or the defaultHandler, if one exists and no other route matched). If -// anything is sent down the stop channel the function will end. -func (r *router) matchAndDispatch(messages <-chan *packets.PublishPacket, order bool, client *mqttclient) { - go func() { - for { - select { - case message := <-messages: - sent := false - m := messageFromPublish(message, client.ackFunc(message)) - handlers := []MessageHandler{} - for e := r.routes.Front(); e != nil; e = e.Next() { - if e.Value.(*route).match(message.TopicName) { - if order { - handlers = append(handlers, e.Value.(*route).callback) - } else { - hd := e.Value.(*route).callback - hd(client, m) - //TODO: m.Ack() - } - sent = true - } - } - if !sent && r.defaultHandler != nil { - if order { - handlers = append(handlers, r.defaultHandler) - } else { - r.defaultHandler(client, m) - //TODO: m.Ack() - } - } - for _, handler := range handlers { - func() { - handler(client, m) - //TODO: m.Ack() - }() - } - case <-r.stop: - return - } - } - }() -} diff --git a/net/mqtt/token.go b/net/mqtt/token.go deleted file mode 100644 index f4225ffce..000000000 --- a/net/mqtt/token.go +++ /dev/null @@ -1,19 +0,0 @@ -package mqtt - -import "time" - -type mqtttoken struct { - err error -} - -func (t *mqtttoken) Wait() bool { - return true -} - -func (t *mqtttoken) WaitTimeout(time.Duration) bool { - return true -} - -func (t *mqtttoken) Error() error { - return t.err -} diff --git a/net/net.go b/net/net.go deleted file mode 100644 index f28da84a7..000000000 --- a/net/net.go +++ /dev/null @@ -1,423 +0,0 @@ -// package net is intended to provide compatible interfaces with the -// Go standard library's net package. -package net - -import ( - "errors" - "strconv" - "strings" - "time" -) - -// DialUDP makes a UDP network connection. raadr is the port that the messages will -// be sent to, and laddr is the port that will be listened to in order to -// receive incoming messages. -func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPSerialConn, error) { - addr := raddr.IP.String() - sendport := strconv.Itoa(raddr.Port) - listenport := strconv.Itoa(laddr.Port) - - // disconnect any old socket - ActiveDevice.DisconnectSocket() - - // connect new socket - err := ActiveDevice.ConnectUDPSocket(addr, sendport, listenport) - if err != nil { - return nil, err - } - - return &UDPSerialConn{SerialConn: SerialConn{Adaptor: ActiveDevice}, laddr: laddr, raddr: raddr}, nil -} - -// ListenUDP listens for UDP connections on the port listed in laddr. -func ListenUDP(network string, laddr *UDPAddr) (*UDPSerialConn, error) { - addr := "0" - sendport := "0" - listenport := strconv.Itoa(laddr.Port) - - // disconnect any old socket - ActiveDevice.DisconnectSocket() - - // connect new socket - err := ActiveDevice.ConnectUDPSocket(addr, sendport, listenport) - if err != nil { - return nil, err - } - - return &UDPSerialConn{SerialConn: SerialConn{Adaptor: ActiveDevice}, laddr: laddr}, nil -} - -// DialTCP makes a TCP network connection. raadr is the port that the messages will -// be sent to, and laddr is the port that will be listened to in order to -// receive incoming messages. -func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPSerialConn, error) { - addr := raddr.IP.String() - sendport := strconv.Itoa(raddr.Port) - - // disconnect any old socket? - //ActiveDevice.DisconnectSocket() - - // connect new socket - err := ActiveDevice.ConnectTCPSocket(addr, sendport) - if err != nil { - return nil, err - } - - return &TCPSerialConn{SerialConn: SerialConn{Adaptor: ActiveDevice}, laddr: laddr, raddr: raddr}, nil -} - -// Dial connects to the address on the named network. -// It tries to provide a mostly compatible interface -// to net.Dial(). -func Dial(network, address string) (Conn, error) { - switch network { - case "tcp": - raddr, err := ResolveTCPAddr(network, address) - if err != nil { - return nil, err - } - - c, e := DialTCP(network, &TCPAddr{}, raddr) - return c.opConn(), e - case "udp": - raddr, err := ResolveUDPAddr(network, address) - if err != nil { - return nil, err - } - - c, e := DialUDP(network, &UDPAddr{}, raddr) - return c.opConn(), e - default: - return nil, errors.New("invalid network for dial") - } -} - -// SerialConn is a loosely net.Conn compatible implementation -type SerialConn struct { - Adaptor Adapter -} - -// UDPSerialConn is a loosely net.Conn compatible intended to support -// UDP over serial. -type UDPSerialConn struct { - SerialConn - laddr *UDPAddr - raddr *UDPAddr -} - -// NewUDPSerialConn returns a new UDPSerialConn/ -func NewUDPSerialConn(c SerialConn, laddr, raddr *UDPAddr) *UDPSerialConn { - return &UDPSerialConn{SerialConn: c, raddr: raddr} -} - -// TCPSerialConn is a loosely net.Conn compatible intended to support -// TCP over serial. -type TCPSerialConn struct { - SerialConn - laddr *TCPAddr - raddr *TCPAddr -} - -// NewTCPSerialConn returns a new TCPSerialConn/ -func NewTCPSerialConn(c SerialConn, laddr, raddr *TCPAddr) *TCPSerialConn { - return &TCPSerialConn{SerialConn: c, raddr: raddr} -} - -// Read reads data from the connection. -// TODO: implement the full method functionality: -// Read can be made to time out and return an Error with Timeout() == true -// after a fixed time limit; see SetDeadline and SetReadDeadline. -func (c *SerialConn) Read(b []byte) (n int, err error) { - // read only the data that has been received via "+IPD" socket - return c.Adaptor.ReadSocket(b) -} - -// Write writes data to the connection. -// TODO: implement the full method functionality for timeouts. -// Write can be made to time out and return an Error with Timeout() == true -// after a fixed time limit; see SetDeadline and SetWriteDeadline. -func (c *SerialConn) Write(b []byte) (n int, err error) { - // specify that is a data transfer to the - // currently open socket, not commands to the ESP8266/ESP32. - err = c.Adaptor.StartSocketSend(len(b)) - if err != nil { - return - } - n, err = c.Adaptor.Write(b) - if err != nil { - return n, err - } - /* TODO(bcg): this is kind of specific to espat, should maybe refactor */ - _, err = c.Adaptor.Response(1000) - if err != nil { - return n, err - } - return n, err -} - -// Close closes the connection. -// Currently only supports a single Read or Write operations without blocking. -func (c *SerialConn) Close() error { - c.Adaptor.DisconnectSocket() - return nil -} - -// LocalAddr returns the local network address. -func (c *UDPSerialConn) LocalAddr() Addr { - return c.laddr.opAddr() -} - -// RemoteAddr returns the remote network address. -func (c *UDPSerialConn) RemoteAddr() Addr { - return c.raddr.opAddr() -} - -func (c *UDPSerialConn) opConn() Conn { - if c == nil { - return nil - } - return c -} - -// LocalAddr returns the local network address. -func (c *TCPSerialConn) LocalAddr() Addr { - return c.laddr.opAddr() -} - -// RemoteAddr returns the remote network address. -func (c *TCPSerialConn) RemoteAddr() Addr { - return c.raddr.opAddr() -} - -func (c *TCPSerialConn) opConn() Conn { - if c == nil { - return nil - } - return c -} - -// SetDeadline sets the read and write deadlines associated -// with the connection. It is equivalent to calling both -// SetReadDeadline and SetWriteDeadline. -// -// A deadline is an absolute time after which I/O operations -// fail with a timeout (see type Error) instead of -// blocking. The deadline applies to all future and pending -// I/O, not just the immediately following call to Read or -// Write. After a deadline has been exceeded, the connection -// can be refreshed by setting a deadline in the future. -// -// An idle timeout can be implemented by repeatedly extending -// the deadline after successful Read or Write calls. -// -// A zero value for t means I/O operations will not time out. -func (c *SerialConn) SetDeadline(t time.Time) error { - return nil -} - -// SetReadDeadline sets the deadline for future Read calls -// and any currently-blocked Read call. -// A zero value for t means Read will not time out. -func (c *SerialConn) SetReadDeadline(t time.Time) error { - return nil -} - -// SetWriteDeadline sets the deadline for future Write calls -// and any currently-blocked Write call. -// Even if write times out, it may return n > 0, indicating that -// some of the data was successfully written. -// A zero value for t means Write will not time out. -func (c *SerialConn) SetWriteDeadline(t time.Time) error { - return nil -} - -// ResolveTCPAddr returns an address of TCP end point. -// -// The network must be a TCP network name. -func ResolveTCPAddr(network, address string) (*TCPAddr, error) { - // TODO: make sure network is 'tcp' - // separate domain from port, if any - r := strings.Split(address, ":") - addr, err := ActiveDevice.GetDNS(r[0]) - if err != nil { - return nil, err - } - ip := IP(addr) - if len(r) > 1 { - port, e := strconv.Atoi(r[1]) - if e != nil { - return nil, e - } - return &TCPAddr{IP: ip, Port: port}, nil - } - return &TCPAddr{IP: ip}, nil -} - -// ResolveUDPAddr returns an address of UDP end point. -// -// The network must be a UDP network name. -func ResolveUDPAddr(network, address string) (*UDPAddr, error) { - // TODO: make sure network is 'udp' - // separate domain from port, if any - r := strings.Split(address, ":") - addr, err := ActiveDevice.GetDNS(r[0]) - if err != nil { - return nil, err - } - ip := IP(addr) - if len(r) > 1 { - port, e := strconv.Atoi(r[1]) - if e != nil { - return nil, e - } - return &UDPAddr{IP: ip, Port: port}, nil - } - - return &UDPAddr{IP: ip}, nil -} - -// The following definitions are here to support a Golang standard package -// net-compatible interface for IP until TinyGo can compile the net package. - -// IP is an IP address. Unlike the standard implementation, it is only -// a buffer of bytes that contains the string form of the IP address, not the -// full byte format used by the Go standard . -type IP []byte - -// UDPAddr here to serve as compatible type. until TinyGo can compile the net package. -type UDPAddr struct { - IP IP - Port int - Zone string // IPv6 scoped addressing zone; added in Go 1.1 -} - -// Network returns the address's network name, "udp". -func (a *UDPAddr) Network() string { return "udp" } - -func (a *UDPAddr) String() string { - if a == nil { - return "" - } - if a.Port != 0 { - return a.IP.String() + ":" + strconv.Itoa(a.Port) - } - return a.IP.String() -} - -func (a *UDPAddr) opAddr() Addr { - if a == nil { - return nil - } - return a -} - -// TCPAddr here to serve as compatible type. until TinyGo can compile the net package. -type TCPAddr struct { - IP IP - Port int - Zone string // IPv6 scoped addressing zone -} - -// Network returns the address's network name, "tcp". -func (a *TCPAddr) Network() string { return "tcp" } - -func (a *TCPAddr) String() string { - if a == nil { - return "" - } - if a.Port != 0 { - return a.IP.String() + ":" + strconv.Itoa(a.Port) - } - return a.IP.String() -} - -func (a *TCPAddr) opAddr() Addr { - if a == nil { - return nil - } - return a -} - -// ParseIP parses s as an IP address, returning the result. -func ParseIP(s string) IP { - b := strings.Split(s, ".") - if len(b) == 4 { - ip := make([]byte, 4) - - for i := range ip { - x, _ := strconv.ParseUint(b[i], 10, 8) - ip[i] = byte(x) - } - - return IP(ip) - } - - return IP([]byte(s)) -} - -// String returns the string form of the IP address ip. -func (ip IP) String() string { - if len(ip) < 4 { - return "" - } - return strconv.Itoa(int(ip[0])) + "." + strconv.Itoa(int(ip[1])) + "." + strconv.Itoa(int(ip[2])) + "." + strconv.Itoa(int(ip[3])) -} - -// Conn is a generic stream-oriented network connection. -// This interface is from the Go standard library. -type Conn interface { - // Read reads data from the connection. - // Read can be made to time out and return an Error with Timeout() == true - // after a fixed time limit; see SetDeadline and SetReadDeadline. - Read(b []byte) (n int, err error) - - // Write writes data to the connection. - // Write can be made to time out and return an Error with Timeout() == true - // after a fixed time limit; see SetDeadline and SetWriteDeadline. - Write(b []byte) (n int, err error) - - // Close closes the connection. - // Any blocked Read or Write operations will be unblocked and return errors. - Close() error - - // LocalAddr returns the local network address. - LocalAddr() Addr - - // RemoteAddr returns the remote network address. - RemoteAddr() Addr - - // SetDeadline sets the read and write deadlines associated - // with the connection. It is equivalent to calling both - // SetReadDeadline and SetWriteDeadline. - // - // A deadline is an absolute time after which I/O operations - // fail with a timeout (see type Error) instead of - // blocking. The deadline applies to all future and pending - // I/O, not just the immediately following call to Read or - // Write. After a deadline has been exceeded, the connection - // can be refreshed by setting a deadline in the future. - // - // An idle timeout can be implemented by repeatedly extending - // the deadline after successful Read or Write calls. - // - // A zero value for t means I/O operations will not time out. - SetDeadline(t time.Time) error - - // SetReadDeadline sets the deadline for future Read calls - // and any currently-blocked Read call. - // A zero value for t means Read will not time out. - SetReadDeadline(t time.Time) error - - // SetWriteDeadline sets the deadline for future Write calls - // and any currently-blocked Write call. - // Even if write times out, it may return n > 0, indicating that - // some of the data was successfully written. - // A zero value for t means Write will not time out. - SetWriteDeadline(t time.Time) error -} - -// Addr represents a network end point address. -type Addr interface { - Network() string // name of the network (for example, "tcp", "udp") - String() string // string form of address (for example, "192.0.2.1:25", "[2001:db8::1]:80") -} diff --git a/net/net_test.go b/net/net_test.go deleted file mode 100644 index e9ac6623e..000000000 --- a/net/net_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package net - -import ( - "testing" - - qt "github.com/frankban/quicktest" -) - -func TestIPAddressString(t *testing.T) { - c := qt.New(t) - ipaddr := ParseIP("127.0.0.1") - - c.Assert(ipaddr.String(), qt.Equals, "127.0.0.1") -} diff --git a/net/tls/tls.go b/net/tls/tls.go deleted file mode 100644 index 12ba037ea..000000000 --- a/net/tls/tls.go +++ /dev/null @@ -1,42 +0,0 @@ -// Package tls is intended to provide a minimal set of compatible interfaces with the -// Go standard library's tls package. -package tls - -import ( - "strconv" - "strings" - - "tinygo.org/x/drivers/net" -) - -// Dial makes a TLS network connection. It tries to provide a mostly compatible interface -// to tls.Dial(). -// Dial connects to the given network address. -func Dial(network, address string, config *Config) (*net.TCPSerialConn, error) { - raddr, err := net.ResolveTCPAddr(network, address) - if err != nil { - return nil, err - } - - hostname := strings.Split(address, ":")[0] - sendport := strconv.Itoa(raddr.Port) - if sendport == "0" { - sendport = "443" - } - - // disconnect any old socket - net.ActiveDevice.DisconnectSocket() - - // connect new socket - err = net.ActiveDevice.ConnectSSLSocket(hostname, sendport) - if err != nil { - return nil, err - } - - return net.NewTCPSerialConn(net.SerialConn{Adaptor: net.ActiveDevice}, nil, raddr), nil -} - -// Config is a placeholder for future compatibility with -// tls.Config. -type Config struct { -} diff --git a/netdev/README.md b/netdev/README.md new file mode 100644 index 000000000..980a21d1f --- /dev/null +++ b/netdev/README.md @@ -0,0 +1,184 @@ +# Netdev + +#### Table of Contents + +- [Overview](#overview) +- [Porting Applications from Go "net"](#porting-applications-from-go-net) +- [Writing a New Driver](#writing-a-new-driver) + +## Overview + +Netdev is TinyGo's network device driver model. + +Let's see where netdev fits in the network stack. The diagram below shows the traditional full OS stack vs. different possible embedded stacks for TinyGo. + +![Netdev models](netdev_models.jpg) + +In the traditional full OS stack, the driver that sits above hardware (the "nic") and below TCP/IP is the network driver, the netdev. The netdev provides a raw packet interface to the OS. + +For TinyGo netdev, the netdev includes TCP/IP and provides a socket(2) interface to TinyGo's "net" package. Applications are written to use the net.Conn interfaces. "net" translates net.Conn functions (Dial, Listen, Read, Write) into netdev socket(2) calls. The netdev translates those socket(2) calls into hardware access, ultimately. Let's consider the three use cases: + +#### Firware Offload Model + +Here we are fortunate that hardware includes firmware with a TCP/IP implmentation, and the firmware manages the TCP/IP connection state. The netdev driver translates socket(2) calls to the firmware's TCP/IP calls. Usually, minimal work is required since the firmware is likely to use lwip, which has an socket(2) API. + +The Wifinina (ESP32) and RTL8720dn netdev drivers are examples of the firmware offload model. + +#### Full Stack Model + +Here the netdev includes the TCP/IP stack, maybe some port of lwip/uip to Go? + +#### "Bring-Your-Own-net.Comm" Model + +Here the netdev is the entire stack, accessing hardware on the bottom and serving up net.Conn connections above to applications. + +## Porting Applications from Go "net" + +Ideally, TinyGo's "net" package would just be Go's "net" package and applications using "net" would just work, as-is. Unfortunately, Go's "net" can't fully be ported to TinyGo, so TinyGo's "net" is a subset of Go's. Hopefully, for the embedded space, the subset is sufficient for most needs. + +To view TinyGo's "net" package exports, use ```go doc ./net```, ```go doc ./net/http```, etc. For the most part, Go's "net" documentation applies to TinyGo's "net". There are a few features excluded during the porting process, in particular: + +- No IPv6 support +- No HTTP/2 support +- HTTP client request can't be reused +- No multipart form support +- No TLS support for HTTP servers +- No DualStack support + +Applications using Go's "net" package will need a few (minor) modifications to work with TinyGo's net package. + +### Step 1: Load Netdev + +#### Option 1: + +Import netdev package to load the netdev driver. Import only for side effects using leading underscore. + +```go +import _ "tinygo.org/x/drivers/netdev" +``` + +This will select the netdev driver for the target machine using build tags. For example, when flashing to target Arduino Nano RP2040 Connect, the build tag nano_rp2040 will select the "Wifinina" netdev driver. + +#### Option 2: + +Manually load the netdev driver. Import the driver directly, and then call net.UseNetdev to load the driver. e.g.: + +``` +import "tinygo.org/x/drivers/netdev/wifinina" + +func main() { + net.UseNetdev(wifinina.New("SSID", "PASSPHRASE")) + ... +} +``` + +### Step 2: Connect to the Network + +Call net.Connect() to connect the device to an IP network, via Wifi, cellular, Ethernet, etc. Make this call first, before any net.* or http.* or tls.* calls. + +Here is a simple http server listening on port :8080, before and after porting from Go "net/http": + +#### Before +```go +package main + +import ( + "fmt" + "net/http" +) + +func main() { + http.HandleFunc("/", HelloServer) + http.ListenAndServe(":8080", nil) +} + +func HelloServer(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) +} +``` + +#### After +```go +package main + +import ( + "fmt" + "net" + "net/http" + + _ "tinygo.org/x/drivers/netdev" +) + +func main() { + net.Connect(nil) + http.HandleFunc("/", HelloServer) + http.ListenAndServe(":8080", nil) +} + +func HelloServer(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) +} +``` + +## Writing a New Driver + +:bulb: A reference netdev driver is the Wifinina driver (netdev/wifinina). + +Netdev drivers implement the net.Netdever interface, which includes the net.Socketer interface. The Socketer interface is modeled after BSD socket(2). TinyGo's "net" package translates net.Conn calls into netdev Socketer calls. For example, DialTCP calls netdev.Socket() and netdev.Connect(): + +```go +func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error) { + + fd, _ := netdev.Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) + + addr := NewSockAddr("", uint16(raddr.Port), raddr.IP) + + netdev.Connect(fd, addr) + + return &TCPConn{ + fd: fd, + laddr: laddr, + raddr: raddr, + }, nil +} +``` + +### net.Socketer Interface + +```go +type Socketer interface { + Socket(family AddressFamily, sockType SockType, protocol Protocol) (Sockfd, error) + Bind(sockfd Sockfd, myaddr SockAddr) error + Connect(sockfd Sockfd, servaddr SockAddr) error + Listen(sockfd Sockfd, backlog int) error + Accept(sockfd Sockfd, peer SockAddr) (Sockfd, error) + Send(sockfd Sockfd, buf []byte, flags SockFlags, timeout time.Duration) (int, error) + Recv(sockfd Sockfd, buf []byte, flags SockFlags, timeout time.Duration) (int, error) + Close(sockfd Sockfd) error + SetSockOpt(sockfd Sockfd, level SockOptLevel, opt SockOpt, value any) error +} +``` + +Socketer interface is intended to mimic a subset of BSD socket(2). They've been Go-ified, but should otherwise maintain the semantics of the original socket(2) calls. Send and Recv add a timeout to put a limit on blocking operations. Recv in paricular is blocking and will block until data arrives on the socket or EOF. The timeout is calculated from net.Conn's SetDeadline(), typically. + +#### Locking + +Multiple goroutines may invoke methods on a net.Conn simultaneously, and the "net" package translates net.Conn calls into Socketer calls. It follows that multiple goroutines may invoke Socketer calls, so locking is required to keep Socketer calls from stepping on one another. + +Don't hold a lock while Time.Sleep()'ing waiting for a hardware operation to finish. Unlocking while sleeping let's other goroutines to run. If the sleep period is really small, then you can get away with holding the lock sometimes. + +#### Sockfd + +The Socketer interface uses a socket fd to represent a socket connection end-point. Each net.Conn maps 1:1 to a fd. The number of fds available is a netdev hardware limitation. Wifinina, for example, can hand out 10 socket fds. + +### Packaging + +1. Create a new directory in netdev/foo to hold the driver files. + +2. Add a initialization file netdev/netdev_foo.go to compile and load the driver based on target build tags. + +### Testing + +The netdev driver should minimally pass all of the example/net examples. + +TODO: automate testing to catch regressions. diff --git a/netdev/netdev.go b/netdev/netdev.go new file mode 100644 index 000000000..9e12a785d --- /dev/null +++ b/netdev/netdev.go @@ -0,0 +1,133 @@ +// Netdev is TinyGo's network device driver model. + +package netdev + +import ( + "errors" + "fmt" + "strconv" + "strings" + _ "unsafe" +) + +//go:linkname Use net.useNetdev +func Use(netdev Netdever) + +type HardwareAddr []byte + +const hexDigit = "0123456789abcdef" + +func (a HardwareAddr) String() string { + if len(a) == 0 { + return "" + } + buf := make([]byte, 0, len(a)*3-1) + for i, b := range a { + if i > 0 { + buf = append(buf, ':') + } + buf = append(buf, hexDigit[b>>4]) + buf = append(buf, hexDigit[b&0xF]) + } + return string(buf) +} + +func ParseHardwareAddr(s string) HardwareAddr { + parts := strings.Split(s, ":") + if len(parts) != 6 { + return nil + } + + var mac []byte + for _, part := range parts { + b, err := strconv.ParseInt(part, 16, 64) + if err != nil { + return nil + } + mac = append(mac, byte(b)) + } + + return HardwareAddr(mac) +} + +type Port uint16 // host byte-order + +type IP [4]byte + +func (ip IP) String() string { + return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]) +} + +func ParseIP(s string) IP { + var result IP + octets := strings.Split(s, ".") + if len(octets) != 4 { + return IP{} + } + for i, octet := range octets { + v, err := strconv.Atoi(octet) + if err != nil { + return IP{} + } + result[i] = byte(v) + } + return result +} + +// NetConnect() errors +var ErrConnected = errors.New("Already connected") +var ErrConnectFailed = errors.New("Connect failed") +var ErrConnectTimeout = errors.New("Connect timed out") +var ErrMissingSSID = errors.New("Missing WiFi SSID") +var ErrStartingDHCPClient = errors.New("Error starting DHPC client") + +// GethostByName() errors +var ErrHostUnknown = errors.New("Host unknown") + +// Socketer errors +var ErrFamilyNotSupported = errors.New("Address family not supported") +var ErrProtocolNotSupported = errors.New("Socket protocol/type not supported") +var ErrNoMoreSockets = errors.New("No more sockets") +var ErrClosingSocket = errors.New("Error closing socket") +var ErrNotSupported = errors.New("Not supported") +var ErrRecvTimeout = errors.New("Recv timeout expired") + +type Event int + +// NetNotify network events +const ( + EventNetUp Event = iota + EventNetDown +) + +// Netdev drivers implement the Netdever interface. +// +// A Netdever is passed to the "net" package using netdev.Use(). +// +// Just like a net.Conn, multiple goroutines may invoke methods on a Netdever +// simultaneously. +type Netdever interface { + + // NetConnect device to IP network + NetConnect() error + + // NetDisconnect from IP network + NetDisconnect() + + // NetNotify to register callback for network events + NetNotify(func(Event)) + + // GetHostByName returns the IP address of either a hostname or IPv4 + // address in standard dot notation + GetHostByName(name string) (IP, error) + + // GetHardwareAddr returns device MAC address + GetHardwareAddr() (HardwareAddr, error) + + // GetIPAddr returns IP address assigned to device, either by DHCP or + // statically + GetIPAddr() (IP, error) + + // Socketer is a Berkely Sockets-like interface + Socketer +} diff --git a/netdev/netdev_models.jpg b/netdev/netdev_models.jpg new file mode 100644 index 0000000000000000000000000000000000000000..37e29d7040913eaa633e0568f991f935f2addbae GIT binary patch literal 65672 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 x;4yIE-?7@?bk 0 { - break - } - - _, err = rtl.Rpc_lwip_errno() - if err != nil { - return nil - } - - time.Sleep(100 * time.Millisecond) - } - - buf2 := make([]byte, 4096) - result, err := rtl.Rpc_lwip_recv(socket, &buf2, uint32(len(buf2)), 8, 0) - if err != nil { - return nil - } - if result != -1 && result != 0 { - return fmt.Errorf("Rpc_lwip_recv error") - } - - result, err = rtl.Rpc_lwip_errno() - if err != nil { - return nil - } - if result != 11 { - return fmt.Errorf("Rpc_lwip_errno error") - } - - b := bufio.NewReader(bytes.NewReader(buf)) - req, err := http.ReadRequest(b) - if err != nil { - return err - } - if rtl.debug { - fmt.Printf("%s %s %s\r\n", req.Method, req.RequestURI, req.Proto) - } - - pos := bytes.Index(buf, []byte("\r\n\r\n")) - if pos > 0 { - body := bytes.NewReader(buf[pos+4:]) - req.Body = io.NopCloser(body) - } - - handler, _ := http.DefaultServeMux.Handler(req) - rwx := responseWriter{ - header: http.Header{}, - statusCode: 200, - } - rwx.header.Add(`Content-Type`, `text/html; charset=UTF-8`) - rwx.header.Add(`Connection`, `close`) - handler.ServeHTTP(&rwx, req) - rwx.header.Add(`Content-Length`, fmt.Sprintf("%d", len(rwx.Buf))) - - optval = []byte{0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA5, 0xA5, 0xA5, 0xA5} - _, err = rtl.Rpc_lwip_setsockopt(socket, 0x00000FFF, 0x1006, optval, uint32(len(optval))) - if err != nil { - return nil - } - - optval = []byte{0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA5, 0xA5, 0xA5, 0xA5} - _, err = rtl.Rpc_lwip_setsockopt(socket, 0x00000FFF, 0x1005, optval, uint32(len(optval))) - if err != nil { - return nil - } - - maxfdp1 := int32(2) - writeset := []byte{0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - timeout := []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x42, 0x0F, 0x00, 0x0A, 0x00, 0x00, 0x00} - _, err = rtl.Rpc_lwip_select(maxfdp1, []byte{}, writeset, []byte{}, timeout) - if err != nil { - return nil - } - - msg := rwx.Buf - hb := bytes.Buffer{} - err = rwx.header.Write(&hb) - if err != nil { - return err - } - - data := []byte(fmt.Sprintf("HTTP/1.1 %d OK\n", rwx.statusCode)) - data = append(data, hb.Bytes()...) - data = append(data, byte('\n')) - - _, err = rtl.Rpc_lwip_send(socket, data, 8) - if err != nil { - return nil - } - - if len(msg) > 0 { - timeout = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x42, 0x0F, 0x00, 0x54, 0x00, 0x00, 0x00} - _, err = rtl.Rpc_lwip_select(maxfdp1, []byte{}, writeset, []byte{}, timeout) - if err != nil { - return nil - } - - _, err = rtl.Rpc_lwip_send(socket, msg, 8) - if err != nil { - return nil - } - } - - for i := 0; i < 4; i++ { - buf := make([]byte, 4096) - _, err = rtl.Rpc_lwip_recv(socket, &buf, uint32(len(buf)), 8, 0) - if err != nil { - return nil - } - - _, err = rtl.Rpc_lwip_errno() - if err != nil { - return nil - } - } - - _, err = rtl.Rpc_lwip_close(socket) - if err != nil { - return nil - } - return nil -} - -func (rtl *RTL8720DN) ListenAndServe(addr string, handler http.Handler) error { - err := rtl.setupHTTPServer() - if err != nil { - return err - } - - for { - connected, err := rtl.accept() - if err != nil { - return err - } - - if connected { - err := rtl.handleHTTP() - if err != nil { - return err - } - } - - time.Sleep(100 * time.Millisecond) - } -} - -type responseWriter struct { - Buf []byte - header http.Header - statusCode int -} - -func (r *responseWriter) Header() http.Header { - return r.header -} - -func (r *responseWriter) Write(b []byte) (int, error) { - r.Buf = append(r.Buf, b...) - return len(b), nil -} - -func (r *responseWriter) WriteHeader(statusCode int) { - r.statusCode = statusCode -} diff --git a/rtl8720dn/netdriver.go b/rtl8720dn/netdriver.go deleted file mode 100644 index 73e6cc43b..000000000 --- a/rtl8720dn/netdriver.go +++ /dev/null @@ -1,377 +0,0 @@ -package rtl8720dn - -import ( - "fmt" - "strconv" -) - -// Here is the implementation of tinygo-org/x/drivers/net.DeviceDriver. - -func (d *Driver) GetDNS(domain string) (string, error) { - if d.debug { - fmt.Printf("GetDNS(%q)\r\n", domain) - } - - ipaddr := make([]byte, 4) - _, err := d.Rpc_netconn_gethostbyname(domain, &ipaddr) - if err != nil { - return "", err - } - - ret, err := fmt.Sprintf("%d.%d.%d.%d", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]), nil - if d.debug { - fmt.Printf("-> %s\r\n", ret) - fmt.Printf("-> %02X.%02X.%02X.%02X\r\n", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]) - } - return ret, err -} - -func (d *Driver) ConnectTCPSocket(addr, port string) error { - if d.debug { - fmt.Printf("ConnectTCPSocket(%q, %q)\r\n", addr, port) - } - - ipaddr := make([]byte, 4) - if len(addr) == 4 { - copy(ipaddr, addr) - } else { - _, err := d.Rpc_netconn_gethostbyname(addr, &ipaddr) - if err != nil { - return err - } - } - - portNum, err := strconv.ParseUint(port, 0, 0) - if err != nil { - return err - } - - socket, err := d.Rpc_lwip_socket(0x02, 0x01, 0x00) - if err != nil { - return err - } - d.socket = socket - d.connectionType = ConnectionTypeTCP - - _, err = d.Rpc_lwip_fcntl(socket, 0x00000003, 0x00000000) - if err != nil { - return err - } - - _, err = d.Rpc_lwip_fcntl(socket, 0x00000004, 0x00000001) - if err != nil { - return err - } - - name := []byte{0x00, 0x02, 0x00, 0x50, 0xC0, 0xA8, 0x01, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - name[2] = byte(portNum >> 8) - name[3] = byte(portNum) - name[4] = byte(ipaddr[0]) - name[5] = byte(ipaddr[1]) - name[6] = byte(ipaddr[2]) - name[7] = byte(ipaddr[3]) - - _, err = d.Rpc_lwip_connect(socket, name, uint32(len(name))) - if err != nil { - return err - } - - readset := []byte{} - writeset := []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - exceptset := []byte{} - timeout := []byte{} - _, err = d.Rpc_lwip_select(0x01, readset, writeset, exceptset, timeout) - if err != nil { - return err - } - - optval := make([]byte, 4) - optlen := uint32(len(optval)) - _, err = d.Rpc_lwip_getsockopt(socket, 0x00000FFF, 0x00001007, []byte{0xA5, 0xA5, 0xA5, 0xA5}, &optval, &optlen) - if err != nil { - return err - } - - _, err = d.Rpc_lwip_fcntl(socket, 0x00000003, 0x00000000) - if err != nil { - return err - } - - _, err = d.Rpc_lwip_fcntl(socket, 0x00000004, 0x00000000) - if err != nil { - return err - } - - readset = []byte{} - writeset = []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - exceptset = []byte{} - timeout = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x42, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF} - _, err = d.Rpc_lwip_select(0x01, readset, writeset, exceptset, timeout) - if err != nil { - return err - } - - return nil -} - -func (d *Driver) ConnectSSLSocket(addr, port string) error { - if d.debug { - fmt.Printf("ConnectSSLSocket(%q, %q)\r\n", addr, port) - } - if d.root_ca == nil { - return fmt.Errorf("root_ca is not set") - } - - client, err := d.Rpc_wifi_ssl_client_create() - if err != nil { - return err - } - d.client = client - d.connectionType = ConnectionTypeTLS - - err = d.Rpc_wifi_ssl_init(client) - if err != nil { - return err - } - - err = d.Rpc_wifi_ssl_set_timeout(client, 0x0001D4C0) - if err != nil { - return err - } - - _, err = d.Rpc_wifi_ssl_set_rootCA(client, *d.root_ca) - if err != nil { - return err - } - - // TODO: use port - _, err = d.Rpc_wifi_start_ssl_client(client, addr, 443, 0x0001D4C0) - if err != nil { - return err - } - - _, err = d.Rpc_wifi_ssl_get_socket(client) - if err != nil { - return err - } - return nil -} - -func (d *Driver) ConnectUDPSocket(addr, sendport, listenport string) error { - if d.debug { - fmt.Printf("ConnectUDPSocket(\"%d.%d.%d.%d\", %q, %q)\r\n", byte(addr[0]), byte(addr[1]), byte(addr[2]), byte(addr[3]), sendport, listenport) - } - - socket, err := d.Rpc_lwip_socket(0x02, 0x02, 0x00) - if err != nil { - return err - } - d.socket = socket - d.connectionType = ConnectionTypeUDP - - optval := []byte{0x01, 0x00, 0x00, 0x00} - _, err = d.Rpc_lwip_setsockopt(socket, 0x00000FFF, 0x00000004, optval, uint32(len(optval))) - if err != nil { - return err - } - - port, err := strconv.ParseUint(sendport, 10, 0) - if err != nil { - return err - } - - ip := []byte(addr) - - // remote info - d.udpInfo[0] = byte(port >> 8) - d.udpInfo[1] = byte(port) - d.udpInfo[2] = ip[0] - d.udpInfo[3] = ip[1] - d.udpInfo[4] = ip[2] - d.udpInfo[5] = ip[3] - - port, err = strconv.ParseUint(listenport, 10, 0) - if err != nil { - return err - } - - ip_info := make([]byte, 12) - _, err = d.Rpc_tcpip_adapter_get_ip_info(0, &ip_info) - if err != nil { - return err - } - - name := []byte{0x00, 0x02, 0x0D, 0x05, 0xC0, 0xA8, 0x01, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - name[2] = byte(port >> 8) - name[3] = byte(port) - name[4] = ip_info[0] - name[5] = ip_info[1] - name[6] = ip_info[2] - name[7] = ip_info[3] - - _, err = d.Rpc_lwip_bind(socket, name, uint32(len(name))) - if err != nil { - return err - } - - _, err = d.Rpc_lwip_fcntl(socket, 0x00000004, 0x00000000) - if err != nil { - return err - } - - return nil -} - -func (d *Driver) DisconnectSocket() error { - if d.debug { - fmt.Printf("DisconnectSocket()\r\n") - } - switch d.connectionType { - case ConnectionTypeTCP, ConnectionTypeUDP: - _, err := d.Rpc_lwip_close(d.socket) - if err != nil { - return err - } - case ConnectionTypeTLS: - err := d.Rpc_wifi_stop_ssl_socket(d.client) - if err != nil { - return err - } - - err = d.Rpc_wifi_ssl_client_destroy(d.client) - if err != nil { - return err - } - default: - } - d.connectionType = ConnectionTypeNone - return nil -} - -func (d *Driver) StartSocketSend(size int) error { - if d.debug { - fmt.Printf("StartSocketSend(%d)\r\n", size) - } - // No implementation required - return nil -} - -func (d *Driver) Write(b []byte) (n int, err error) { - if d.debug { - fmt.Printf("Write(%#v)\r\n", b) - } - - switch d.connectionType { - case ConnectionTypeTCP: - sn, err := d.Rpc_lwip_send(d.socket, b, 0x00000008) - if err != nil { - return 0, err - } - n = int(sn) - case ConnectionTypeUDP: - to := []byte{0x00, 0x02, 0x0D, 0x05, 0xC0, 0xA8, 0x01, 0x76, 0xEB, 0x43, 0x00, 0x00, 0xD5, 0x27, 0x01, 0x00} - copy(to[2:], d.udpInfo[:]) - sn, err := d.Rpc_lwip_sendto(d.socket, b, 0x00000000, to, uint32(len(to))) - if err != nil { - return 0, err - } - n = int(sn) - case ConnectionTypeTLS: - sn, err := d.Rpc_wifi_send_ssl_data(d.client, b, uint16(len(b))) - if err != nil { - return 0, err - } - n = int(sn) - default: - return 0, nil - } - return n, nil -} - -func (d *Driver) ReadSocket(b []byte) (n int, err error) { - if d.debug { - //fmt.Printf("ReadSocket(b)\r\n") - } - if d.connectionType == ConnectionTypeNone { - return 0, nil - } - - switch d.connectionType { - case ConnectionTypeTCP: - length := len(b) - if length > maxUartRecvSize-16 { - length = maxUartRecvSize - 16 - } - buf := b[:length] - nn, err := d.Rpc_lwip_recv(d.socket, &buf, uint32(length), 0x00000008, 0x00002800) - if err != nil { - return 0, err - } - - if nn == -1 { - return 0, nil - } else if nn == 0 { - return 0, d.DisconnectSocket() - } - n = int(nn) - case ConnectionTypeUDP: - length := len(b) - if length > maxUartRecvSize-32 { - length = maxUartRecvSize - 32 - } - buf := b[:length] - from := make([]byte, 16) - fromLen := uint32(len(from)) - nn, err := d.Rpc_lwip_recvfrom(d.socket, &buf, uint32(length), 0x00000008, &from, &fromLen, 10000) - if err != nil { - return 0, err - } - - if nn == -1 { - return 0, nil - } - n = int(nn) - case ConnectionTypeTLS: - length := len(b) - if length > maxUartRecvSize-16 { - length = maxUartRecvSize - 16 - } - buf := b[:length] - nn, err := d.Rpc_wifi_get_ssl_receive(d.client, &buf, int32(length)) - if err != nil { - return 0, err - } - if nn < 0 { - return 0, fmt.Errorf("error %d", n) - } else if nn == 0 || nn == -30848 { - return 0, d.DisconnectSocket() - } - n = int(nn) - default: - } - - return n, nil -} - -func (d *Driver) IsSocketDataAvailable() bool { - if d.debug { - fmt.Printf("IsSocketDataAvailable()\r\n") - } - ret, err := d.Rpc_lwip_available(d.socket) - if err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - return false - } - if ret == 1 { - return true - } - return false -} - -func (d *Driver) Response(timeout int) ([]byte, error) { - if d.debug { - fmt.Printf("Response(%d))\r\n", timeout) - } - // No implementation required - return nil, nil -} diff --git a/rtl8720dn/rpc.go b/rtl8720dn/rpc.go index 50498aef2..fc7b2f833 100644 --- a/rtl8720dn/rpc.go +++ b/rtl8720dn/rpc.go @@ -8,21 +8,13 @@ import ( "fmt" ) -func (r *RTL8720DN) Rpc_system_version() (string, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_system_version() string { if r.debug { fmt.Printf("rpc_system_version()\r\n") } msg := startWriteMessage(0x00, 0x01, 0x01, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return "", err - } + r.performRequest(msg) r.read() widx := 8 @@ -32,15 +24,10 @@ func (r *RTL8720DN) Rpc_system_version() (string, error) { result = string(payload[widx : widx+int(result_length)]) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_system_ack(c uint8) (uint8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_system_ack(c uint8) uint8 { if r.debug { fmt.Printf("rpc_system_ack()\r\n") } @@ -49,10 +36,7 @@ func (r *RTL8720DN) Rpc_system_ack(c uint8) (uint8, error) { // c : in uint8 msg = append(msg, byte(c>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -61,24 +45,16 @@ func (r *RTL8720DN) Rpc_system_ack(c uint8) (uint8, error) { result = uint8(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_init() (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_init() bool { if r.debug { fmt.Printf("rpc_ble_init()\r\n") } msg := startWriteMessage(0x00, 0x02, 0x01, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -86,59 +62,38 @@ func (r *RTL8720DN) Rpc_ble_init() (bool, error) { result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_start() error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_start() { if r.debug { fmt.Printf("rpc_ble_start()\r\n") } msg := startWriteMessage(0x00, 0x02, 0x02, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_ble_deinit() error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_deinit() { if r.debug { fmt.Printf("rpc_ble_deinit()\r\n") } msg := startWriteMessage(0x00, 0x02, 0x03, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_gap_set_param(param RPC_T_GAP_PARAM_TYPE, value []byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_set_param(param RPC_T_GAP_PARAM_TYPE, value []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_gap_set_param()\r\n") } @@ -153,10 +108,7 @@ func (r *RTL8720DN) Rpc_gap_set_param(param RPC_T_GAP_PARAM_TYPE, value []byte) msg = append(msg, byte(len(value)), byte(len(value)>>8), byte(len(value)>>16), byte(len(value)>>24)) msg = append(msg, []byte(value)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -165,15 +117,10 @@ func (r *RTL8720DN) Rpc_gap_set_param(param RPC_T_GAP_PARAM_TYPE, value []byte) result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_gap_get_param(param RPC_T_GAP_PARAM_TYPE, value *[]byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_get_param(param RPC_T_GAP_PARAM_TYPE, value []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_gap_get_param()\r\n") } @@ -185,10 +132,7 @@ func (r *RTL8720DN) Rpc_gap_get_param(param RPC_T_GAP_PARAM_TYPE, value *[]byte) msg = append(msg, byte(param>>16)) msg = append(msg, byte(param>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -196,33 +140,24 @@ func (r *RTL8720DN) Rpc_gap_get_param(param RPC_T_GAP_PARAM_TYPE, value *[]byte) value_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if value_length > 0 { - copy(*value, payload[widx:widx+int(value_length)]) + copy(value, payload[widx:widx+int(value_length)]) widx += int(value_length) } - *value = (*value)[:value_length] var result RPC_T_GAP_CAUSE result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_gap_set_pairable_mode() (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_set_pairable_mode() RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_gap_set_pairable_mode()\r\n") } msg := startWriteMessage(0x00, 0x03, 0x03, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -230,15 +165,10 @@ func (r *RTL8720DN) Rpc_gap_set_pairable_mode() (RPC_T_GAP_CAUSE, error) { result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_set_param(param RPC_T_LE_BOND_PARAM_TYPE, value []byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_set_param(param RPC_T_LE_BOND_PARAM_TYPE, value []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_set_param()\r\n") } @@ -253,10 +183,7 @@ func (r *RTL8720DN) Rpc_le_bond_set_param(param RPC_T_LE_BOND_PARAM_TYPE, value msg = append(msg, byte(len(value)), byte(len(value)>>8), byte(len(value)>>16), byte(len(value)>>24)) msg = append(msg, []byte(value)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -265,15 +192,10 @@ func (r *RTL8720DN) Rpc_le_bond_set_param(param RPC_T_LE_BOND_PARAM_TYPE, value result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_get_param(param RPC_T_LE_BOND_PARAM_TYPE, value *[]byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_get_param(param RPC_T_LE_BOND_PARAM_TYPE, value []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_get_param()\r\n") } @@ -285,10 +207,7 @@ func (r *RTL8720DN) Rpc_le_bond_get_param(param RPC_T_LE_BOND_PARAM_TYPE, value msg = append(msg, byte(param>>16)) msg = append(msg, byte(param>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -296,24 +215,18 @@ func (r *RTL8720DN) Rpc_le_bond_get_param(param RPC_T_LE_BOND_PARAM_TYPE, value value_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if value_length > 0 { - copy(*value, payload[widx:widx+int(value_length)]) + copy(value, payload[widx:widx+int(value_length)]) widx += int(value_length) } - *value = (*value)[:value_length] var result RPC_T_GAP_CAUSE result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_pair(conn_id uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_pair(conn_id uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_pair()\r\n") } @@ -322,10 +235,7 @@ func (r *RTL8720DN) Rpc_le_bond_pair(conn_id uint8) (RPC_T_GAP_CAUSE, error) { // conn_id : in uint8 msg = append(msg, byte(conn_id>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -334,15 +244,10 @@ func (r *RTL8720DN) Rpc_le_bond_pair(conn_id uint8) (RPC_T_GAP_CAUSE, error) { result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_get_display_key(conn_id uint8, key *uint32) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_get_display_key(conn_id uint8, key *uint32) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_get_display_key()\r\n") } @@ -351,10 +256,7 @@ func (r *RTL8720DN) Rpc_le_bond_get_display_key(conn_id uint8, key *uint32) (RPC // conn_id : in uint8 msg = append(msg, byte(conn_id>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -366,15 +268,10 @@ func (r *RTL8720DN) Rpc_le_bond_get_display_key(conn_id uint8, key *uint32) (RPC result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_passkey_input_confirm(conn_id uint8, passcode uint32, cause RPC_T_GAP_CFM_CAUSE) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_passkey_input_confirm(conn_id uint8, passcode uint32, cause RPC_T_GAP_CFM_CAUSE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_passkey_input_confirm()\r\n") } @@ -393,10 +290,7 @@ func (r *RTL8720DN) Rpc_le_bond_passkey_input_confirm(conn_id uint8, passcode ui msg = append(msg, byte(cause>>16)) msg = append(msg, byte(cause>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -405,15 +299,10 @@ func (r *RTL8720DN) Rpc_le_bond_passkey_input_confirm(conn_id uint8, passcode ui result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_oob_input_confirm(conn_id uint8, cause RPC_T_GAP_CFM_CAUSE) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_oob_input_confirm(conn_id uint8, cause RPC_T_GAP_CFM_CAUSE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_oob_input_confirm()\r\n") } @@ -427,10 +316,7 @@ func (r *RTL8720DN) Rpc_le_bond_oob_input_confirm(conn_id uint8, cause RPC_T_GAP msg = append(msg, byte(cause>>16)) msg = append(msg, byte(cause>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -439,15 +325,10 @@ func (r *RTL8720DN) Rpc_le_bond_oob_input_confirm(conn_id uint8, cause RPC_T_GAP result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_just_work_confirm(conn_id uint8, cause RPC_T_GAP_CFM_CAUSE) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_just_work_confirm(conn_id uint8, cause RPC_T_GAP_CFM_CAUSE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_just_work_confirm()\r\n") } @@ -461,10 +342,7 @@ func (r *RTL8720DN) Rpc_le_bond_just_work_confirm(conn_id uint8, cause RPC_T_GAP msg = append(msg, byte(cause>>16)) msg = append(msg, byte(cause>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -473,15 +351,10 @@ func (r *RTL8720DN) Rpc_le_bond_just_work_confirm(conn_id uint8, cause RPC_T_GAP result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_passkey_display_confirm(conn_id uint8, cause RPC_T_GAP_CFM_CAUSE) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_passkey_display_confirm(conn_id uint8, cause RPC_T_GAP_CFM_CAUSE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_passkey_display_confirm()\r\n") } @@ -495,10 +368,7 @@ func (r *RTL8720DN) Rpc_le_bond_passkey_display_confirm(conn_id uint8, cause RPC msg = append(msg, byte(cause>>16)) msg = append(msg, byte(cause>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -507,15 +377,10 @@ func (r *RTL8720DN) Rpc_le_bond_passkey_display_confirm(conn_id uint8, cause RPC result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_user_confirm(conn_id uint8, cause RPC_T_GAP_CFM_CAUSE) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_user_confirm(conn_id uint8, cause RPC_T_GAP_CFM_CAUSE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_user_confirm()\r\n") } @@ -529,10 +394,7 @@ func (r *RTL8720DN) Rpc_le_bond_user_confirm(conn_id uint8, cause RPC_T_GAP_CFM_ msg = append(msg, byte(cause>>16)) msg = append(msg, byte(cause>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -541,15 +403,10 @@ func (r *RTL8720DN) Rpc_le_bond_user_confirm(conn_id uint8, cause RPC_T_GAP_CFM_ result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_cfg_local_key_distribute(init_dist uint8, rsp_dist uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_cfg_local_key_distribute(init_dist uint8, rsp_dist uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_cfg_local_key_distribute()\r\n") } @@ -560,10 +417,7 @@ func (r *RTL8720DN) Rpc_le_bond_cfg_local_key_distribute(init_dist uint8, rsp_di // rsp_dist : in uint8 msg = append(msg, byte(rsp_dist>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -572,37 +426,24 @@ func (r *RTL8720DN) Rpc_le_bond_cfg_local_key_distribute(init_dist uint8, rsp_di result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_clear_all_keys() error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_clear_all_keys() { if r.debug { fmt.Printf("rpc_le_bond_clear_all_keys()\r\n") } msg := startWriteMessage(0x00, 0x04, 0x0B, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_le_bond_delete_by_idx(idx uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_delete_by_idx(idx uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_delete_by_idx()\r\n") } @@ -611,10 +452,7 @@ func (r *RTL8720DN) Rpc_le_bond_delete_by_idx(idx uint8) (RPC_T_GAP_CAUSE, error // idx : in uint8 msg = append(msg, byte(idx>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -623,15 +461,10 @@ func (r *RTL8720DN) Rpc_le_bond_delete_by_idx(idx uint8) (RPC_T_GAP_CAUSE, error result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_delete_by_bd(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_delete_by_bd(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_delete_by_bd()\r\n") } @@ -645,10 +478,7 @@ func (r *RTL8720DN) Rpc_le_bond_delete_by_bd(bd_addr uint8, bd_type RPC_T_GAP_RE msg = append(msg, byte(bd_type>>16)) msg = append(msg, byte(bd_type>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -657,15 +487,10 @@ func (r *RTL8720DN) Rpc_le_bond_delete_by_bd(bd_addr uint8, bd_type RPC_T_GAP_RE result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_get_sec_level(conn_id uint8, sec_type RPC_T_GAP_SEC_LEVEL) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_get_sec_level(conn_id uint8, sec_type RPC_T_GAP_SEC_LEVEL) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_get_sec_level()\r\n") } @@ -674,10 +499,7 @@ func (r *RTL8720DN) Rpc_le_bond_get_sec_level(conn_id uint8, sec_type RPC_T_GAP_ // conn_id : in uint8 msg = append(msg, byte(conn_id>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -688,15 +510,10 @@ func (r *RTL8720DN) Rpc_le_bond_get_sec_level(conn_id uint8, sec_type RPC_T_GAP_ result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_gap_init(link_num uint8) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_gap_init(link_num uint8) bool { if r.debug { fmt.Printf("rpc_le_gap_init()\r\n") } @@ -705,10 +522,7 @@ func (r *RTL8720DN) Rpc_le_gap_init(link_num uint8) (bool, error) { // link_num : in uint8 msg = append(msg, byte(link_num>>0)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -717,15 +531,10 @@ func (r *RTL8720DN) Rpc_le_gap_init(link_num uint8) (bool, error) { result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_gap_msg_info_way(use_msg bool) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_gap_msg_info_way(use_msg bool) { if r.debug { fmt.Printf("rpc_le_gap_msg_info_way()\r\n") } @@ -738,32 +547,21 @@ func (r *RTL8720DN) Rpc_le_gap_msg_info_way(use_msg bool) error { msg = append(msg, 0) } - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_le_get_max_link_num() (uint8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_max_link_num() uint8 { if r.debug { fmt.Printf("rpc_le_get_max_link_num()\r\n") } msg := startWriteMessage(0x00, 0x05, 0x03, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -771,15 +569,10 @@ func (r *RTL8720DN) Rpc_le_get_max_link_num() (uint8, error) { result = uint8(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_set_gap_param(param RPC_T_GAP_LE_PARAM_TYPE, value []byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_set_gap_param(param RPC_T_GAP_LE_PARAM_TYPE, value []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_set_gap_param()\r\n") } @@ -794,10 +587,7 @@ func (r *RTL8720DN) Rpc_le_set_gap_param(param RPC_T_GAP_LE_PARAM_TYPE, value [] msg = append(msg, byte(len(value)), byte(len(value)>>8), byte(len(value)>>16), byte(len(value)>>24)) msg = append(msg, []byte(value)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -806,15 +596,10 @@ func (r *RTL8720DN) Rpc_le_set_gap_param(param RPC_T_GAP_LE_PARAM_TYPE, value [] result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_gap_param(param RPC_T_GAP_LE_PARAM_TYPE, value *[]byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_gap_param(param RPC_T_GAP_LE_PARAM_TYPE, value []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_get_gap_param()\r\n") } @@ -826,10 +611,7 @@ func (r *RTL8720DN) Rpc_le_get_gap_param(param RPC_T_GAP_LE_PARAM_TYPE, value *[ msg = append(msg, byte(param>>16)) msg = append(msg, byte(param>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -837,24 +619,18 @@ func (r *RTL8720DN) Rpc_le_get_gap_param(param RPC_T_GAP_LE_PARAM_TYPE, value *[ value_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if value_length > 0 { - copy(*value, payload[widx:widx+int(value_length)]) + copy(value, payload[widx:widx+int(value_length)]) widx += int(value_length) } - *value = (*value)[:value_length] var result RPC_T_GAP_CAUSE result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_modify_white_list(operation RPC_T_GAP_WHITE_LIST_OP, bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_modify_white_list(operation RPC_T_GAP_WHITE_LIST_OP, bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_modify_white_list()\r\n") } @@ -873,10 +649,7 @@ func (r *RTL8720DN) Rpc_le_modify_white_list(operation RPC_T_GAP_WHITE_LIST_OP, msg = append(msg, byte(bd_type>>16)) msg = append(msg, byte(bd_type>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -885,15 +658,10 @@ func (r *RTL8720DN) Rpc_le_modify_white_list(operation RPC_T_GAP_WHITE_LIST_OP, result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_gen_rand_addr(rand_addr_type RPC_T_GAP_RAND_ADDR_TYPE, random_bd *uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_gen_rand_addr(rand_addr_type RPC_T_GAP_RAND_ADDR_TYPE, random_bd *uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_gen_rand_addr()\r\n") } @@ -905,10 +673,7 @@ func (r *RTL8720DN) Rpc_le_gen_rand_addr(rand_addr_type RPC_T_GAP_RAND_ADDR_TYPE msg = append(msg, byte(rand_addr_type>>16)) msg = append(msg, byte(rand_addr_type>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -920,15 +685,10 @@ func (r *RTL8720DN) Rpc_le_gen_rand_addr(rand_addr_type RPC_T_GAP_RAND_ADDR_TYPE result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_set_rand_addr(random_bd uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_set_rand_addr(random_bd uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_set_rand_addr()\r\n") } @@ -937,10 +697,7 @@ func (r *RTL8720DN) Rpc_le_set_rand_addr(random_bd uint8) (RPC_T_GAP_CAUSE, erro // random_bd : in uint8 msg = append(msg, byte(random_bd>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -949,15 +706,10 @@ func (r *RTL8720DN) Rpc_le_set_rand_addr(random_bd uint8) (RPC_T_GAP_CAUSE, erro result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_cfg_local_identity_address(addr uint8, ident_addr_type RPC_T_GAP_IDENT_ADDR_TYPE) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_cfg_local_identity_address(addr uint8, ident_addr_type RPC_T_GAP_IDENT_ADDR_TYPE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_cfg_local_identity_address()\r\n") } @@ -971,10 +723,7 @@ func (r *RTL8720DN) Rpc_le_cfg_local_identity_address(addr uint8, ident_addr_typ msg = append(msg, byte(ident_addr_type>>16)) msg = append(msg, byte(ident_addr_type>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -983,15 +732,10 @@ func (r *RTL8720DN) Rpc_le_cfg_local_identity_address(addr uint8, ident_addr_typ result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_set_host_chann_classif(p_channel_map uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_set_host_chann_classif(p_channel_map uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_set_host_chann_classif()\r\n") } @@ -1000,10 +744,7 @@ func (r *RTL8720DN) Rpc_le_set_host_chann_classif(p_channel_map uint8) (RPC_T_GA // p_channel_map : in uint8 msg = append(msg, byte(p_channel_map>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1012,15 +753,10 @@ func (r *RTL8720DN) Rpc_le_set_host_chann_classif(p_channel_map uint8) (RPC_T_GA result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_write_default_data_len(tx_octets uint16, tx_time uint16) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_write_default_data_len(tx_octets uint16, tx_time uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_write_default_data_len()\r\n") } @@ -1033,10 +769,7 @@ func (r *RTL8720DN) Rpc_le_write_default_data_len(tx_octets uint16, tx_time uint msg = append(msg, byte(tx_time>>0)) msg = append(msg, byte(tx_time>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1045,15 +778,10 @@ func (r *RTL8720DN) Rpc_le_write_default_data_len(tx_octets uint16, tx_time uint result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_gap_config_cccd_not_check(cccd_not_check_flag RPC_T_GAP_CONFIG_GATT_CCCD_NOT_CHECK) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_config_cccd_not_check(cccd_not_check_flag RPC_T_GAP_CONFIG_GATT_CCCD_NOT_CHECK) { if r.debug { fmt.Printf("rpc_gap_config_cccd_not_check()\r\n") } @@ -1065,23 +793,15 @@ func (r *RTL8720DN) Rpc_gap_config_cccd_not_check(cccd_not_check_flag RPC_T_GAP_ msg = append(msg, byte(cccd_not_check_flag>>16)) msg = append(msg, byte(cccd_not_check_flag>>24)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_gap_config_ccc_bits_count(gatt_server_ccc_bits_count uint8, gatt_storage_ccc_bits_count uint8) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_config_ccc_bits_count(gatt_server_ccc_bits_count uint8, gatt_storage_ccc_bits_count uint8) { if r.debug { fmt.Printf("rpc_gap_config_ccc_bits_count()\r\n") } @@ -1092,23 +812,15 @@ func (r *RTL8720DN) Rpc_gap_config_ccc_bits_count(gatt_server_ccc_bits_count uin // gatt_storage_ccc_bits_count : in uint8 msg = append(msg, byte(gatt_storage_ccc_bits_count>>0)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_gap_config_max_attribute_table_count(gatt_max_attribute_table_count uint8) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_config_max_attribute_table_count(gatt_max_attribute_table_count uint8) { if r.debug { fmt.Printf("rpc_gap_config_max_attribute_table_count()\r\n") } @@ -1117,23 +829,15 @@ func (r *RTL8720DN) Rpc_gap_config_max_attribute_table_count(gatt_max_attribute_ // gatt_max_attribute_table_count : in uint8 msg = append(msg, byte(gatt_max_attribute_table_count>>0)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_gap_config_max_mtu_size(att_max_mtu_size uint16) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_config_max_mtu_size(att_max_mtu_size uint16) { if r.debug { fmt.Printf("rpc_gap_config_max_mtu_size()\r\n") } @@ -1143,23 +847,15 @@ func (r *RTL8720DN) Rpc_gap_config_max_mtu_size(att_max_mtu_size uint16) error { msg = append(msg, byte(att_max_mtu_size>>0)) msg = append(msg, byte(att_max_mtu_size>>8)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_gap_config_bte_pool_size(bte_pool_size uint8) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_config_bte_pool_size(bte_pool_size uint8) { if r.debug { fmt.Printf("rpc_gap_config_bte_pool_size()\r\n") } @@ -1168,23 +864,15 @@ func (r *RTL8720DN) Rpc_gap_config_bte_pool_size(bte_pool_size uint8) error { // bte_pool_size : in uint8 msg = append(msg, byte(bte_pool_size>>0)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_gap_config_bt_report_buf_num(bt_report_buf_num uint8) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_config_bt_report_buf_num(bt_report_buf_num uint8) { if r.debug { fmt.Printf("rpc_gap_config_bt_report_buf_num()\r\n") } @@ -1193,23 +881,15 @@ func (r *RTL8720DN) Rpc_gap_config_bt_report_buf_num(bt_report_buf_num uint8) er // bt_report_buf_num : in uint8 msg = append(msg, byte(bt_report_buf_num>>0)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_gap_config_le_key_storage_flag(le_key_storage_flag uint16) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_config_le_key_storage_flag(le_key_storage_flag uint16) { if r.debug { fmt.Printf("rpc_gap_config_le_key_storage_flag()\r\n") } @@ -1219,23 +899,15 @@ func (r *RTL8720DN) Rpc_gap_config_le_key_storage_flag(le_key_storage_flag uint1 msg = append(msg, byte(le_key_storage_flag>>0)) msg = append(msg, byte(le_key_storage_flag>>8)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_gap_config_max_le_paired_device(max_le_paired_device uint8) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_config_max_le_paired_device(max_le_paired_device uint8) { if r.debug { fmt.Printf("rpc_gap_config_max_le_paired_device()\r\n") } @@ -1244,23 +916,15 @@ func (r *RTL8720DN) Rpc_gap_config_max_le_paired_device(max_le_paired_device uin // max_le_paired_device : in uint8 msg = append(msg, byte(max_le_paired_device>>0)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_gap_config_max_le_link_num(le_link_num uint8) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_config_max_le_link_num(le_link_num uint8) { if r.debug { fmt.Printf("rpc_gap_config_max_le_link_num()\r\n") } @@ -1269,23 +933,15 @@ func (r *RTL8720DN) Rpc_gap_config_max_le_link_num(le_link_num uint8) error { // le_link_num : in uint8 msg = append(msg, byte(le_link_num>>0)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_le_adv_set_param(param RPC_T_LE_ADV_PARAM_TYPE, value []byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_adv_set_param(param RPC_T_LE_ADV_PARAM_TYPE, value []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_adv_set_param()\r\n") } @@ -1300,10 +956,7 @@ func (r *RTL8720DN) Rpc_le_adv_set_param(param RPC_T_LE_ADV_PARAM_TYPE, value [] msg = append(msg, byte(len(value)), byte(len(value)>>8), byte(len(value)>>16), byte(len(value)>>24)) msg = append(msg, []byte(value)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1312,15 +965,10 @@ func (r *RTL8720DN) Rpc_le_adv_set_param(param RPC_T_LE_ADV_PARAM_TYPE, value [] result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_adv_get_param(param RPC_T_LE_ADV_PARAM_TYPE, value *[]byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_adv_get_param(param RPC_T_LE_ADV_PARAM_TYPE, value []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_adv_get_param()\r\n") } @@ -1332,10 +980,7 @@ func (r *RTL8720DN) Rpc_le_adv_get_param(param RPC_T_LE_ADV_PARAM_TYPE, value *[ msg = append(msg, byte(param>>16)) msg = append(msg, byte(param>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1343,33 +988,24 @@ func (r *RTL8720DN) Rpc_le_adv_get_param(param RPC_T_LE_ADV_PARAM_TYPE, value *[ value_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if value_length > 0 { - copy(*value, payload[widx:widx+int(value_length)]) + copy(value, payload[widx:widx+int(value_length)]) widx += int(value_length) } - *value = (*value)[:value_length] var result RPC_T_GAP_CAUSE result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_adv_start() (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_adv_start() RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_adv_start()\r\n") } msg := startWriteMessage(0x00, 0x07, 0x03, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1377,24 +1013,16 @@ func (r *RTL8720DN) Rpc_le_adv_start() (RPC_T_GAP_CAUSE, error) { result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_adv_stop() (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_adv_stop() RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_adv_stop()\r\n") } msg := startWriteMessage(0x00, 0x07, 0x04, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1402,24 +1030,16 @@ func (r *RTL8720DN) Rpc_le_adv_stop() (RPC_T_GAP_CAUSE, error) { result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_adv_update_param() (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_adv_update_param() RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_adv_update_param()\r\n") } msg := startWriteMessage(0x00, 0x07, 0x05, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1427,15 +1047,10 @@ func (r *RTL8720DN) Rpc_le_adv_update_param() (RPC_T_GAP_CAUSE, error) { result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_scan_set_param(param RPC_T_LE_SCAN_PARAM_TYPE, value []byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_scan_set_param(param RPC_T_LE_SCAN_PARAM_TYPE, value []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_scan_set_param()\r\n") } @@ -1450,10 +1065,7 @@ func (r *RTL8720DN) Rpc_le_scan_set_param(param RPC_T_LE_SCAN_PARAM_TYPE, value msg = append(msg, byte(len(value)), byte(len(value)>>8), byte(len(value)>>16), byte(len(value)>>24)) msg = append(msg, []byte(value)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1462,15 +1074,10 @@ func (r *RTL8720DN) Rpc_le_scan_set_param(param RPC_T_LE_SCAN_PARAM_TYPE, value result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_scan_get_param(param RPC_T_LE_SCAN_PARAM_TYPE, value *[]byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_scan_get_param(param RPC_T_LE_SCAN_PARAM_TYPE, value []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_scan_get_param()\r\n") } @@ -1482,10 +1089,7 @@ func (r *RTL8720DN) Rpc_le_scan_get_param(param RPC_T_LE_SCAN_PARAM_TYPE, value msg = append(msg, byte(param>>16)) msg = append(msg, byte(param>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1493,33 +1097,24 @@ func (r *RTL8720DN) Rpc_le_scan_get_param(param RPC_T_LE_SCAN_PARAM_TYPE, value value_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if value_length > 0 { - copy(*value, payload[widx:widx+int(value_length)]) + copy(value, payload[widx:widx+int(value_length)]) widx += int(value_length) } - *value = (*value)[:value_length] var result RPC_T_GAP_CAUSE result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_scan_start() (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_scan_start() RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_scan_start()\r\n") } msg := startWriteMessage(0x00, 0x08, 0x03, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1527,15 +1122,10 @@ func (r *RTL8720DN) Rpc_le_scan_start() (RPC_T_GAP_CAUSE, error) { result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_scan_timer_start(tick uint32) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_scan_timer_start(tick uint32) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_scan_timer_start()\r\n") } @@ -1547,10 +1137,7 @@ func (r *RTL8720DN) Rpc_le_scan_timer_start(tick uint32) (RPC_T_GAP_CAUSE, error msg = append(msg, byte(tick>>16)) msg = append(msg, byte(tick>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1559,24 +1146,16 @@ func (r *RTL8720DN) Rpc_le_scan_timer_start(tick uint32) (RPC_T_GAP_CAUSE, error result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_scan_stop() (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_scan_stop() RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_scan_stop()\r\n") } msg := startWriteMessage(0x00, 0x08, 0x05, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1584,15 +1163,10 @@ func (r *RTL8720DN) Rpc_le_scan_stop() (RPC_T_GAP_CAUSE, error) { result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_scan_info_filter(enable bool, offset uint8, length uint8, p_filter uint8) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_scan_info_filter(enable bool, offset uint8, length uint8, p_filter uint8) bool { if r.debug { fmt.Printf("rpc_le_scan_info_filter()\r\n") } @@ -1611,10 +1185,7 @@ func (r *RTL8720DN) Rpc_le_scan_info_filter(enable bool, offset uint8, length ui // p_filter : in uint8 msg = append(msg, byte(p_filter>>0)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1623,15 +1194,10 @@ func (r *RTL8720DN) Rpc_le_scan_info_filter(enable bool, offset uint8, length ui result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_conn_param(param RPC_T_LE_CONN_PARAM_TYPE, value *[]byte, conn_id uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_conn_param(param RPC_T_LE_CONN_PARAM_TYPE, value []byte, conn_id uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_get_conn_param()\r\n") } @@ -1645,10 +1211,7 @@ func (r *RTL8720DN) Rpc_le_get_conn_param(param RPC_T_LE_CONN_PARAM_TYPE, value // conn_id : in uint8 msg = append(msg, byte(conn_id>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1656,24 +1219,18 @@ func (r *RTL8720DN) Rpc_le_get_conn_param(param RPC_T_LE_CONN_PARAM_TYPE, value value_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if value_length > 0 { - copy(*value, payload[widx:widx+int(value_length)]) + copy(value, payload[widx:widx+int(value_length)]) widx += int(value_length) } - *value = (*value)[:value_length] var result RPC_T_GAP_CAUSE result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_conn_info(conn_id uint8, p_conn_info RPC_T_GAP_CONN_INFO) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_conn_info(conn_id uint8, p_conn_info RPC_T_GAP_CONN_INFO) bool { if r.debug { fmt.Printf("rpc_le_get_conn_info()\r\n") } @@ -1682,10 +1239,7 @@ func (r *RTL8720DN) Rpc_le_get_conn_info(conn_id uint8, p_conn_info RPC_T_GAP_CO // conn_id : in uint8 msg = append(msg, byte(conn_id>>0)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1696,15 +1250,10 @@ func (r *RTL8720DN) Rpc_le_get_conn_info(conn_id uint8, p_conn_info RPC_T_GAP_CO result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_conn_addr(conn_id uint8, bd_addr *uint8, bd_type *uint8) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_conn_addr(conn_id uint8, bd_addr *uint8, bd_type *uint8) bool { if r.debug { fmt.Printf("rpc_le_get_conn_addr()\r\n") } @@ -1713,10 +1262,7 @@ func (r *RTL8720DN) Rpc_le_get_conn_addr(conn_id uint8, bd_addr *uint8, bd_type // conn_id : in uint8 msg = append(msg, byte(conn_id>>0)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1731,15 +1277,10 @@ func (r *RTL8720DN) Rpc_le_get_conn_addr(conn_id uint8, bd_addr *uint8, bd_type result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_conn_id(bd_addr uint8, bd_type uint8, p_conn_id *uint8) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_conn_id(bd_addr uint8, bd_type uint8, p_conn_id *uint8) bool { if r.debug { fmt.Printf("rpc_le_get_conn_id()\r\n") } @@ -1750,10 +1291,7 @@ func (r *RTL8720DN) Rpc_le_get_conn_id(bd_addr uint8, bd_type uint8, p_conn_id * // bd_type : in uint8 msg = append(msg, byte(bd_type>>0)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1765,24 +1303,16 @@ func (r *RTL8720DN) Rpc_le_get_conn_id(bd_addr uint8, bd_type uint8, p_conn_id * result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_active_link_num() (uint8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_active_link_num() uint8 { if r.debug { fmt.Printf("rpc_le_get_active_link_num()\r\n") } msg := startWriteMessage(0x00, 0x09, 0x05, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1790,24 +1320,16 @@ func (r *RTL8720DN) Rpc_le_get_active_link_num() (uint8, error) { result = uint8(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_idle_link_num() (uint8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_idle_link_num() uint8 { if r.debug { fmt.Printf("rpc_le_get_idle_link_num()\r\n") } msg := startWriteMessage(0x00, 0x09, 0x06, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1815,15 +1337,10 @@ func (r *RTL8720DN) Rpc_le_get_idle_link_num() (uint8, error) { result = uint8(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_disconnect(conn_id uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_disconnect(conn_id uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_disconnect()\r\n") } @@ -1832,10 +1349,7 @@ func (r *RTL8720DN) Rpc_le_disconnect(conn_id uint8) (RPC_T_GAP_CAUSE, error) { // conn_id : in uint8 msg = append(msg, byte(conn_id>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1844,15 +1358,10 @@ func (r *RTL8720DN) Rpc_le_disconnect(conn_id uint8) (RPC_T_GAP_CAUSE, error) { result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_read_rssi(conn_id uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_read_rssi(conn_id uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_read_rssi()\r\n") } @@ -1861,10 +1370,7 @@ func (r *RTL8720DN) Rpc_le_read_rssi(conn_id uint8) (RPC_T_GAP_CAUSE, error) { // conn_id : in uint8 msg = append(msg, byte(conn_id>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1873,15 +1379,10 @@ func (r *RTL8720DN) Rpc_le_read_rssi(conn_id uint8) (RPC_T_GAP_CAUSE, error) { result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_set_data_len(conn_id uint8, tx_octets uint16, tx_time uint16) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_set_data_len(conn_id uint8, tx_octets uint16, tx_time uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_set_data_len()\r\n") } @@ -1896,10 +1397,7 @@ func (r *RTL8720DN) Rpc_le_set_data_len(conn_id uint8, tx_octets uint16, tx_time msg = append(msg, byte(tx_time>>0)) msg = append(msg, byte(tx_time>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1908,15 +1406,10 @@ func (r *RTL8720DN) Rpc_le_set_data_len(conn_id uint8, tx_octets uint16, tx_time result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_set_phy(conn_id uint8, all_phys uint8, tx_phys uint8, rx_phys uint8, phy_options RPC_T_GAP_PHYS_OPTIONS) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_set_phy(conn_id uint8, all_phys uint8, tx_phys uint8, rx_phys uint8, phy_options RPC_T_GAP_PHYS_OPTIONS) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_set_phy()\r\n") } @@ -1936,10 +1429,7 @@ func (r *RTL8720DN) Rpc_le_set_phy(conn_id uint8, all_phys uint8, tx_phys uint8, msg = append(msg, byte(phy_options>>16)) msg = append(msg, byte(phy_options>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1948,15 +1438,10 @@ func (r *RTL8720DN) Rpc_le_set_phy(conn_id uint8, all_phys uint8, tx_phys uint8, result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_set_conn_param(conn_type RPC_T_GAP_CONN_PARAM_TYPE, p_conn_param RPC_T_GAP_LE_CONN_REQ_PARAM) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_set_conn_param(conn_type RPC_T_GAP_CONN_PARAM_TYPE, p_conn_param RPC_T_GAP_LE_CONN_REQ_PARAM) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_set_conn_param()\r\n") } @@ -1973,10 +1458,7 @@ func (r *RTL8720DN) Rpc_le_set_conn_param(conn_type RPC_T_GAP_CONN_PARAM_TYPE, p msg = append(msg, byte(p_conn_param>>16)) msg = append(msg, byte(p_conn_param>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1985,15 +1467,10 @@ func (r *RTL8720DN) Rpc_le_set_conn_param(conn_type RPC_T_GAP_CONN_PARAM_TYPE, p result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_connect(init_phys uint8, remote_bd uint8, remote_bd_type RPC_T_GAP_REMOTE_ADDR_TYPE, local_bd_type RPC_T_GAP_LOCAL_ADDR_TYPE, scan_timeout uint16) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_connect(init_phys uint8, remote_bd uint8, remote_bd_type RPC_T_GAP_REMOTE_ADDR_TYPE, local_bd_type RPC_T_GAP_LOCAL_ADDR_TYPE, scan_timeout uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_connect()\r\n") } @@ -2017,10 +1494,7 @@ func (r *RTL8720DN) Rpc_le_connect(init_phys uint8, remote_bd uint8, remote_bd_t msg = append(msg, byte(scan_timeout>>0)) msg = append(msg, byte(scan_timeout>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2029,15 +1503,10 @@ func (r *RTL8720DN) Rpc_le_connect(init_phys uint8, remote_bd uint8, remote_bd_t result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_update_conn_param(conn_id uint8, conn_interval_min uint16, conn_interval_max uint16, conn_latency uint16, supervision_timeout uint16, ce_length_min uint16, ce_length_max uint16) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_update_conn_param(conn_id uint8, conn_interval_min uint16, conn_interval_max uint16, conn_latency uint16, supervision_timeout uint16, ce_length_min uint16, ce_length_max uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_update_conn_param()\r\n") } @@ -2064,10 +1533,7 @@ func (r *RTL8720DN) Rpc_le_update_conn_param(conn_id uint8, conn_interval_min ui msg = append(msg, byte(ce_length_max>>0)) msg = append(msg, byte(ce_length_max>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2076,15 +1542,10 @@ func (r *RTL8720DN) Rpc_le_update_conn_param(conn_id uint8, conn_interval_min ui result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_flash_save_local_name(p_data RPC_T_LOCAL_NAME) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_flash_save_local_name(p_data RPC_T_LOCAL_NAME) uint32 { if r.debug { fmt.Printf("rpc_flash_save_local_name()\r\n") } @@ -2096,10 +1557,7 @@ func (r *RTL8720DN) Rpc_flash_save_local_name(p_data RPC_T_LOCAL_NAME) (uint32, msg = append(msg, byte(p_data>>16)) msg = append(msg, byte(p_data>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2108,24 +1566,16 @@ func (r *RTL8720DN) Rpc_flash_save_local_name(p_data RPC_T_LOCAL_NAME) (uint32, result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_flash_load_local_name(p_data RPC_T_LOCAL_NAME) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_flash_load_local_name(p_data RPC_T_LOCAL_NAME) uint32 { if r.debug { fmt.Printf("rpc_flash_load_local_name()\r\n") } msg := startWriteMessage(0x00, 0x0A, 0x02, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2136,15 +1586,10 @@ func (r *RTL8720DN) Rpc_flash_load_local_name(p_data RPC_T_LOCAL_NAME) (uint32, result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_flash_save_local_appearance(p_data RPC_T_LOCAL_APPEARANCE) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_flash_save_local_appearance(p_data RPC_T_LOCAL_APPEARANCE) uint32 { if r.debug { fmt.Printf("rpc_flash_save_local_appearance()\r\n") } @@ -2156,10 +1601,7 @@ func (r *RTL8720DN) Rpc_flash_save_local_appearance(p_data RPC_T_LOCAL_APPEARANC msg = append(msg, byte(p_data>>16)) msg = append(msg, byte(p_data>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2168,24 +1610,16 @@ func (r *RTL8720DN) Rpc_flash_save_local_appearance(p_data RPC_T_LOCAL_APPEARANC result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_flash_load_local_appearance(p_data RPC_T_LOCAL_APPEARANCE) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_flash_load_local_appearance(p_data RPC_T_LOCAL_APPEARANCE) uint32 { if r.debug { fmt.Printf("rpc_flash_load_local_appearance()\r\n") } msg := startWriteMessage(0x00, 0x0A, 0x04, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2196,15 +1630,10 @@ func (r *RTL8720DN) Rpc_flash_load_local_appearance(p_data RPC_T_LOCAL_APPEARANC result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_find_key_entry(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) (RPC_T_LE_KEY_ENTRY, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_find_key_entry(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) RPC_T_LE_KEY_ENTRY { if r.debug { fmt.Printf("rpc_le_find_key_entry()\r\n") } @@ -2218,10 +1647,7 @@ func (r *RTL8720DN) Rpc_le_find_key_entry(bd_addr uint8, bd_type RPC_T_GAP_REMOT msg = append(msg, byte(bd_type>>16)) msg = append(msg, byte(bd_type>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2230,15 +1656,10 @@ func (r *RTL8720DN) Rpc_le_find_key_entry(bd_addr uint8, bd_type RPC_T_GAP_REMOT result = RPC_T_LE_KEY_ENTRY(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_find_key_entry_by_idx(idx uint8) (RPC_T_LE_KEY_ENTRY, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_find_key_entry_by_idx(idx uint8) RPC_T_LE_KEY_ENTRY { if r.debug { fmt.Printf("rpc_le_find_key_entry_by_idx()\r\n") } @@ -2247,10 +1668,7 @@ func (r *RTL8720DN) Rpc_le_find_key_entry_by_idx(idx uint8) (RPC_T_LE_KEY_ENTRY, // idx : in uint8 msg = append(msg, byte(idx>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2259,24 +1677,16 @@ func (r *RTL8720DN) Rpc_le_find_key_entry_by_idx(idx uint8) (RPC_T_LE_KEY_ENTRY, result = RPC_T_LE_KEY_ENTRY(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_bond_dev_num() (uint8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_bond_dev_num() uint8 { if r.debug { fmt.Printf("rpc_le_get_bond_dev_num()\r\n") } msg := startWriteMessage(0x00, 0x0A, 0x07, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2284,24 +1694,16 @@ func (r *RTL8720DN) Rpc_le_get_bond_dev_num() (uint8, error) { result = uint8(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_low_priority_bond() (RPC_T_LE_KEY_ENTRY, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_low_priority_bond() RPC_T_LE_KEY_ENTRY { if r.debug { fmt.Printf("rpc_le_get_low_priority_bond()\r\n") } msg := startWriteMessage(0x00, 0x0A, 0x08, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2309,24 +1711,16 @@ func (r *RTL8720DN) Rpc_le_get_low_priority_bond() (RPC_T_LE_KEY_ENTRY, error) { result = RPC_T_LE_KEY_ENTRY(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_high_priority_bond() (RPC_T_LE_KEY_ENTRY, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_high_priority_bond() RPC_T_LE_KEY_ENTRY { if r.debug { fmt.Printf("rpc_le_get_high_priority_bond()\r\n") } msg := startWriteMessage(0x00, 0x0A, 0x09, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2334,15 +1728,10 @@ func (r *RTL8720DN) Rpc_le_get_high_priority_bond() (RPC_T_LE_KEY_ENTRY, error) result = RPC_T_LE_KEY_ENTRY(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_set_high_priority_bond(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_set_high_priority_bond(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) bool { if r.debug { fmt.Printf("rpc_le_set_high_priority_bond()\r\n") } @@ -2356,10 +1745,7 @@ func (r *RTL8720DN) Rpc_le_set_high_priority_bond(bd_addr uint8, bd_type RPC_T_G msg = append(msg, byte(bd_type>>16)) msg = append(msg, byte(bd_type>>24)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2368,15 +1754,10 @@ func (r *RTL8720DN) Rpc_le_set_high_priority_bond(bd_addr uint8, bd_type RPC_T_G result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_resolve_random_address(unresolved_addr uint8, resolved_addr *uint8, resolved_addr_type RPC_T_GAP_IDENT_ADDR_TYPE) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_resolve_random_address(unresolved_addr uint8, resolved_addr *uint8, resolved_addr_type RPC_T_GAP_IDENT_ADDR_TYPE) bool { if r.debug { fmt.Printf("rpc_le_resolve_random_address()\r\n") } @@ -2392,10 +1773,7 @@ func (r *RTL8720DN) Rpc_le_resolve_random_address(unresolved_addr uint8, resolve msg = append(msg, byte(resolved_addr_type>>16)) msg = append(msg, byte(resolved_addr_type>>24)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2409,15 +1787,10 @@ func (r *RTL8720DN) Rpc_le_resolve_random_address(unresolved_addr uint8, resolve result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_cccd_data(p_entry RPC_T_LE_KEY_ENTRY, p_data RPC_T_LE_CCCD) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_cccd_data(p_entry RPC_T_LE_KEY_ENTRY, p_data RPC_T_LE_CCCD) bool { if r.debug { fmt.Printf("rpc_le_get_cccd_data()\r\n") } @@ -2429,10 +1802,7 @@ func (r *RTL8720DN) Rpc_le_get_cccd_data(p_entry RPC_T_LE_KEY_ENTRY, p_data RPC_ msg = append(msg, byte(p_entry>>16)) msg = append(msg, byte(p_entry>>24)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2443,15 +1813,10 @@ func (r *RTL8720DN) Rpc_le_get_cccd_data(p_entry RPC_T_LE_KEY_ENTRY, p_data RPC_ result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_gen_bond_dev(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE, local_bd_type RPC_T_GAP_LOCAL_ADDR_TYPE, local_ltk []byte, key_type RPC_T_LE_KEY_TYPE, p_cccd RPC_T_LE_CCCD) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_gen_bond_dev(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE, local_bd_type RPC_T_GAP_LOCAL_ADDR_TYPE, local_ltk []byte, key_type RPC_T_LE_KEY_TYPE, p_cccd RPC_T_LE_CCCD) bool { if r.debug { fmt.Printf("rpc_le_gen_bond_dev()\r\n") } @@ -2483,10 +1848,7 @@ func (r *RTL8720DN) Rpc_le_gen_bond_dev(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ msg = append(msg, byte(p_cccd>>16)) msg = append(msg, byte(p_cccd>>24)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2495,24 +1857,16 @@ func (r *RTL8720DN) Rpc_le_gen_bond_dev(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_dev_bond_info_len() (uint16, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_dev_bond_info_len() uint16 { if r.debug { fmt.Printf("rpc_le_get_dev_bond_info_len()\r\n") } msg := startWriteMessage(0x00, 0x0A, 0x0E, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2520,15 +1874,10 @@ func (r *RTL8720DN) Rpc_le_get_dev_bond_info_len() (uint16, error) { result = uint16(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_set_dev_bond_info(p_data []byte, exist *bool) (RPC_T_LE_KEY_ENTRY, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_set_dev_bond_info(p_data []byte, exist *bool) RPC_T_LE_KEY_ENTRY { if r.debug { fmt.Printf("rpc_le_set_dev_bond_info()\r\n") } @@ -2538,10 +1887,7 @@ func (r *RTL8720DN) Rpc_le_set_dev_bond_info(p_data []byte, exist *bool) (RPC_T_ msg = append(msg, byte(len(p_data)), byte(len(p_data)>>8), byte(len(p_data)>>16), byte(len(p_data)>>24)) msg = append(msg, []byte(p_data)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2553,15 +1899,10 @@ func (r *RTL8720DN) Rpc_le_set_dev_bond_info(p_data []byte, exist *bool) (RPC_T_ result = RPC_T_LE_KEY_ENTRY(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_dev_bond_info(p_entry RPC_T_LE_KEY_ENTRY, p_data *[]byte) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_dev_bond_info(p_entry RPC_T_LE_KEY_ENTRY, p_data []byte) bool { if r.debug { fmt.Printf("rpc_le_get_dev_bond_info()\r\n") } @@ -2573,10 +1914,7 @@ func (r *RTL8720DN) Rpc_le_get_dev_bond_info(p_entry RPC_T_LE_KEY_ENTRY, p_data msg = append(msg, byte(p_entry>>16)) msg = append(msg, byte(p_entry>>24)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2584,24 +1922,18 @@ func (r *RTL8720DN) Rpc_le_get_dev_bond_info(p_entry RPC_T_LE_KEY_ENTRY, p_data p_data_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if p_data_length > 0 { - copy(*p_data, payload[widx:widx+int(p_data_length)]) + copy(p_data, payload[widx:widx+int(p_data_length)]) widx += int(p_data_length) } - *p_data = (*p_data)[:p_data_length] var result bool result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_client_init(num uint8) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_client_init(num uint8) bool { if r.debug { fmt.Printf("rpc_ble_client_init()\r\n") } @@ -2610,10 +1942,7 @@ func (r *RTL8720DN) Rpc_ble_client_init(num uint8) (bool, error) { // num : in uint8 msg = append(msg, byte(num>>0)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2622,15 +1951,10 @@ func (r *RTL8720DN) Rpc_ble_client_init(num uint8) (bool, error) { result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_add_client(app_id uint8, link_num uint8) (uint8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_add_client(app_id uint8, link_num uint8) uint8 { if r.debug { fmt.Printf("rpc_ble_add_client()\r\n") } @@ -2641,10 +1965,7 @@ func (r *RTL8720DN) Rpc_ble_add_client(app_id uint8, link_num uint8) (uint8, err // link_num : in uint8 msg = append(msg, byte(link_num>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2653,15 +1974,10 @@ func (r *RTL8720DN) Rpc_ble_add_client(app_id uint8, link_num uint8) (uint8, err result = uint8(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_init(client_num uint8) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_init(client_num uint8) { if r.debug { fmt.Printf("rpc_client_init()\r\n") } @@ -2670,23 +1986,15 @@ func (r *RTL8720DN) Rpc_client_init(client_num uint8) error { // client_num : in uint8 msg = append(msg, byte(client_num>>0)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_client_all_primary_srv_discovery(conn_id uint8, client_id uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_all_primary_srv_discovery(conn_id uint8, client_id uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_all_primary_srv_discovery()\r\n") } @@ -2697,10 +2005,7 @@ func (r *RTL8720DN) Rpc_client_all_primary_srv_discovery(conn_id uint8, client_i // client_id : in uint8 msg = append(msg, byte(client_id>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2709,15 +2014,10 @@ func (r *RTL8720DN) Rpc_client_all_primary_srv_discovery(conn_id uint8, client_i result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_by_uuid_srv_discovery(conn_id uint8, client_id uint8, uuid16 uint16) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_by_uuid_srv_discovery(conn_id uint8, client_id uint8, uuid16 uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_by_uuid_srv_discovery()\r\n") } @@ -2731,10 +2031,7 @@ func (r *RTL8720DN) Rpc_client_by_uuid_srv_discovery(conn_id uint8, client_id ui msg = append(msg, byte(uuid16>>0)) msg = append(msg, byte(uuid16>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2743,15 +2040,10 @@ func (r *RTL8720DN) Rpc_client_by_uuid_srv_discovery(conn_id uint8, client_id ui result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_by_uuid128_srv_discovery(conn_id uint8, client_id uint8, p_uuid128 uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_by_uuid128_srv_discovery(conn_id uint8, client_id uint8, p_uuid128 uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_by_uuid128_srv_discovery()\r\n") } @@ -2764,10 +2056,7 @@ func (r *RTL8720DN) Rpc_client_by_uuid128_srv_discovery(conn_id uint8, client_id // p_uuid128 : in uint8 msg = append(msg, byte(p_uuid128>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2776,15 +2065,10 @@ func (r *RTL8720DN) Rpc_client_by_uuid128_srv_discovery(conn_id uint8, client_id result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_relationship_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_relationship_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_relationship_discovery()\r\n") } @@ -2801,10 +2085,7 @@ func (r *RTL8720DN) Rpc_client_relationship_discovery(conn_id uint8, client_id u msg = append(msg, byte(end_handle>>0)) msg = append(msg, byte(end_handle>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2813,15 +2094,10 @@ func (r *RTL8720DN) Rpc_client_relationship_discovery(conn_id uint8, client_id u result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_all_char_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_all_char_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_all_char_discovery()\r\n") } @@ -2838,10 +2114,7 @@ func (r *RTL8720DN) Rpc_client_all_char_discovery(conn_id uint8, client_id uint8 msg = append(msg, byte(end_handle>>0)) msg = append(msg, byte(end_handle>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2850,15 +2123,10 @@ func (r *RTL8720DN) Rpc_client_all_char_discovery(conn_id uint8, client_id uint8 result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_by_uuid_char_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16, uuid16 uint16) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_by_uuid_char_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16, uuid16 uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_by_uuid_char_discovery()\r\n") } @@ -2878,10 +2146,7 @@ func (r *RTL8720DN) Rpc_client_by_uuid_char_discovery(conn_id uint8, client_id u msg = append(msg, byte(uuid16>>0)) msg = append(msg, byte(uuid16>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2890,15 +2155,10 @@ func (r *RTL8720DN) Rpc_client_by_uuid_char_discovery(conn_id uint8, client_id u result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_by_uuid128_char_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16, p_uuid128 uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_by_uuid128_char_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16, p_uuid128 uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_by_uuid128_char_discovery()\r\n") } @@ -2917,10 +2177,7 @@ func (r *RTL8720DN) Rpc_client_by_uuid128_char_discovery(conn_id uint8, client_i // p_uuid128 : in uint8 msg = append(msg, byte(p_uuid128>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2929,15 +2186,10 @@ func (r *RTL8720DN) Rpc_client_by_uuid128_char_discovery(conn_id uint8, client_i result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_all_char_descriptor_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_all_char_descriptor_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_all_char_descriptor_discovery()\r\n") } @@ -2954,10 +2206,7 @@ func (r *RTL8720DN) Rpc_client_all_char_descriptor_discovery(conn_id uint8, clie msg = append(msg, byte(end_handle>>0)) msg = append(msg, byte(end_handle>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2966,15 +2215,10 @@ func (r *RTL8720DN) Rpc_client_all_char_descriptor_discovery(conn_id uint8, clie result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_attr_read(conn_id uint8, client_id uint8, handle uint16) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_attr_read(conn_id uint8, client_id uint8, handle uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_attr_read()\r\n") } @@ -2988,10 +2232,7 @@ func (r *RTL8720DN) Rpc_client_attr_read(conn_id uint8, client_id uint8, handle msg = append(msg, byte(handle>>0)) msg = append(msg, byte(handle>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3000,15 +2241,10 @@ func (r *RTL8720DN) Rpc_client_attr_read(conn_id uint8, client_id uint8, handle result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_attr_read_using_uuid(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16, uuid16 uint16, p_uuid128 uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_attr_read_using_uuid(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16, uuid16 uint16, p_uuid128 uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_attr_read_using_uuid()\r\n") } @@ -3030,10 +2266,7 @@ func (r *RTL8720DN) Rpc_client_attr_read_using_uuid(conn_id uint8, client_id uin // p_uuid128 : in uint8 msg = append(msg, byte(p_uuid128>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3042,15 +2275,10 @@ func (r *RTL8720DN) Rpc_client_attr_read_using_uuid(conn_id uint8, client_id uin result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_attr_write(conn_id uint8, client_id uint8, write_type RPC_T_GATT_WRITE_TYPE, handle uint16, data []byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_attr_write(conn_id uint8, client_id uint8, write_type RPC_T_GATT_WRITE_TYPE, handle uint16, data []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_attr_write()\r\n") } @@ -3072,10 +2300,7 @@ func (r *RTL8720DN) Rpc_client_attr_write(conn_id uint8, client_id uint8, write_ msg = append(msg, byte(len(data)), byte(len(data)>>8), byte(len(data)>>16), byte(len(data)>>24)) msg = append(msg, []byte(data)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3084,15 +2309,10 @@ func (r *RTL8720DN) Rpc_client_attr_write(conn_id uint8, client_id uint8, write_ result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_attr_ind_confirm(conn_id uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_attr_ind_confirm(conn_id uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_attr_ind_confirm()\r\n") } @@ -3101,10 +2321,7 @@ func (r *RTL8720DN) Rpc_client_attr_ind_confirm(conn_id uint8) (RPC_T_GAP_CAUSE, // conn_id : in uint8 msg = append(msg, byte(conn_id>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3113,15 +2330,10 @@ func (r *RTL8720DN) Rpc_client_attr_ind_confirm(conn_id uint8) (RPC_T_GAP_CAUSE, result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_server_init(num uint8) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_server_init(num uint8) bool { if r.debug { fmt.Printf("rpc_ble_server_init()\r\n") } @@ -3130,10 +2342,7 @@ func (r *RTL8720DN) Rpc_ble_server_init(num uint8) (bool, error) { // num : in uint8 msg = append(msg, byte(num>>0)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3142,15 +2351,10 @@ func (r *RTL8720DN) Rpc_ble_server_init(num uint8) (bool, error) { result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_create_service(uuid uint8, uuid_length uint8, is_primary bool) (uint8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_create_service(uuid uint8, uuid_length uint8, is_primary bool) uint8 { if r.debug { fmt.Printf("rpc_ble_create_service()\r\n") } @@ -3167,10 +2371,7 @@ func (r *RTL8720DN) Rpc_ble_create_service(uuid uint8, uuid_length uint8, is_pri msg = append(msg, 0) } - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3179,15 +2380,10 @@ func (r *RTL8720DN) Rpc_ble_create_service(uuid uint8, uuid_length uint8, is_pri result = uint8(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_delete_service(app_id uint8) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_delete_service(app_id uint8) bool { if r.debug { fmt.Printf("rpc_ble_delete_service()\r\n") } @@ -3196,10 +2392,7 @@ func (r *RTL8720DN) Rpc_ble_delete_service(app_id uint8) (bool, error) { // app_id : in uint8 msg = append(msg, byte(app_id>>0)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3208,15 +2401,10 @@ func (r *RTL8720DN) Rpc_ble_delete_service(app_id uint8) (bool, error) { result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_service_start(app_id uint8) (uint8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_service_start(app_id uint8) uint8 { if r.debug { fmt.Printf("rpc_ble_service_start()\r\n") } @@ -3225,10 +2413,7 @@ func (r *RTL8720DN) Rpc_ble_service_start(app_id uint8) (uint8, error) { // app_id : in uint8 msg = append(msg, byte(app_id>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3237,15 +2422,10 @@ func (r *RTL8720DN) Rpc_ble_service_start(app_id uint8) (uint8, error) { result = uint8(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_get_servie_handle(app_id uint8) (uint8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_get_servie_handle(app_id uint8) uint8 { if r.debug { fmt.Printf("rpc_ble_get_servie_handle()\r\n") } @@ -3254,10 +2434,7 @@ func (r *RTL8720DN) Rpc_ble_get_servie_handle(app_id uint8) (uint8, error) { // app_id : in uint8 msg = append(msg, byte(app_id>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3266,15 +2443,10 @@ func (r *RTL8720DN) Rpc_ble_get_servie_handle(app_id uint8) (uint8, error) { result = uint8(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_create_char(app_id uint8, uuid uint8, uuid_length uint8, properties uint8, permissions uint32) (uint16, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_create_char(app_id uint8, uuid uint8, uuid_length uint8, properties uint8, permissions uint32) uint16 { if r.debug { fmt.Printf("rpc_ble_create_char()\r\n") } @@ -3294,10 +2466,7 @@ func (r *RTL8720DN) Rpc_ble_create_char(app_id uint8, uuid uint8, uuid_length ui msg = append(msg, byte(permissions>>16)) msg = append(msg, byte(permissions>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3306,15 +2475,10 @@ func (r *RTL8720DN) Rpc_ble_create_char(app_id uint8, uuid uint8, uuid_length ui result = uint16(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_create_desc(app_id uint8, char_handle uint16, uuid uint8, uuid_length uint8, flags uint8, permissions uint32, value_length uint16, p_value []byte) (uint16, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_create_desc(app_id uint8, char_handle uint16, uuid uint8, uuid_length uint8, flags uint8, permissions uint32, value_length uint16, p_value []byte) uint16 { if r.debug { fmt.Printf("rpc_ble_create_desc()\r\n") } @@ -3348,10 +2512,7 @@ func (r *RTL8720DN) Rpc_ble_create_desc(app_id uint8, char_handle uint16, uuid u msg = append(msg, []byte(p_value)...) } - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3360,15 +2521,10 @@ func (r *RTL8720DN) Rpc_ble_create_desc(app_id uint8, char_handle uint16, uuid u result = uint16(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_server_send_data(conn_id uint8, service_id uint8, attrib_index uint16, data []byte, pdu_type RPC_T_GATT_PDU_TYPE) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_server_send_data(conn_id uint8, service_id uint8, attrib_index uint16, data []byte, pdu_type RPC_T_GATT_PDU_TYPE) bool { if r.debug { fmt.Printf("rpc_server_send_data()\r\n") } @@ -3390,10 +2546,7 @@ func (r *RTL8720DN) Rpc_server_send_data(conn_id uint8, service_id uint8, attrib msg = append(msg, byte(pdu_type>>16)) msg = append(msg, byte(pdu_type>>24)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3402,15 +2555,10 @@ func (r *RTL8720DN) Rpc_server_send_data(conn_id uint8, service_id uint8, attrib result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_server_get_attr_value(app_id uint8, attr_handle uint16) ([]byte, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_server_get_attr_value(app_id uint8, attr_handle uint16) []byte { if r.debug { fmt.Printf("rpc_ble_server_get_attr_value()\r\n") } @@ -3422,10 +2570,7 @@ func (r *RTL8720DN) Rpc_ble_server_get_attr_value(app_id uint8, attr_handle uint msg = append(msg, byte(attr_handle>>0)) msg = append(msg, byte(attr_handle>>8)) - err := r.performRequest(msg) - if err != nil { - return nil, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3436,15 +2581,10 @@ func (r *RTL8720DN) Rpc_ble_server_get_attr_value(app_id uint8, attr_handle uint copy(result, payload[widx:result_length]) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_server_exec_write_confirm(conn_id uint8, cause uint16, handle uint16) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_server_exec_write_confirm(conn_id uint8, cause uint16, handle uint16) bool { if r.debug { fmt.Printf("rpc_server_exec_write_confirm()\r\n") } @@ -3459,10 +2599,7 @@ func (r *RTL8720DN) Rpc_server_exec_write_confirm(conn_id uint8, cause uint16, h msg = append(msg, byte(handle>>0)) msg = append(msg, byte(handle>>8)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3471,15 +2608,10 @@ func (r *RTL8720DN) Rpc_server_exec_write_confirm(conn_id uint8, cause uint16, h result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_server_attr_write_confirm(conn_id uint8, service_id uint8, attrib_index uint16, cause RPC_T_APP_RESULT) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_server_attr_write_confirm(conn_id uint8, service_id uint8, attrib_index uint16, cause RPC_T_APP_RESULT) bool { if r.debug { fmt.Printf("rpc_server_attr_write_confirm()\r\n") } @@ -3498,10 +2630,7 @@ func (r *RTL8720DN) Rpc_server_attr_write_confirm(conn_id uint8, service_id uint msg = append(msg, byte(cause>>16)) msg = append(msg, byte(cause>>24)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3510,15 +2639,10 @@ func (r *RTL8720DN) Rpc_server_attr_write_confirm(conn_id uint8, service_id uint result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_server_attr_read_confirm(conn_id uint8, service_id uint8, attrib_index uint16, data []byte, cause RPC_T_APP_RESULT) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_server_attr_read_confirm(conn_id uint8, service_id uint8, attrib_index uint16, data []byte, cause RPC_T_APP_RESULT) bool { if r.debug { fmt.Printf("rpc_server_attr_read_confirm()\r\n") } @@ -3540,10 +2664,7 @@ func (r *RTL8720DN) Rpc_server_attr_read_confirm(conn_id uint8, service_id uint8 msg = append(msg, byte(cause>>16)) msg = append(msg, byte(cause>>24)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3552,15 +2673,10 @@ func (r *RTL8720DN) Rpc_server_attr_read_confirm(conn_id uint8, service_id uint8 result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_handle_gap_msg(gap_msg []byte) (RPC_T_APP_RESULT, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_handle_gap_msg(gap_msg []byte) RPC_T_APP_RESULT { if r.debug { fmt.Printf("rpc_ble_handle_gap_msg()\r\n") } @@ -3570,10 +2686,7 @@ func (r *RTL8720DN) Rpc_ble_handle_gap_msg(gap_msg []byte) (RPC_T_APP_RESULT, er msg = append(msg, byte(len(gap_msg)), byte(len(gap_msg)>>8), byte(len(gap_msg)>>16), byte(len(gap_msg)>>24)) msg = append(msg, []byte(gap_msg)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3582,15 +2695,10 @@ func (r *RTL8720DN) Rpc_ble_handle_gap_msg(gap_msg []byte) (RPC_T_APP_RESULT, er result = RPC_T_APP_RESULT(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_gap_callback(cb_type uint8, cb_data []byte) (RPC_T_APP_RESULT, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_gap_callback(cb_type uint8, cb_data []byte) RPC_T_APP_RESULT { if r.debug { fmt.Printf("rpc_ble_gap_callback()\r\n") } @@ -3602,10 +2710,7 @@ func (r *RTL8720DN) Rpc_ble_gap_callback(cb_type uint8, cb_data []byte) (RPC_T_A msg = append(msg, byte(len(cb_data)), byte(len(cb_data)>>8), byte(len(cb_data)>>16), byte(len(cb_data)>>24)) msg = append(msg, []byte(cb_data)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3614,15 +2719,10 @@ func (r *RTL8720DN) Rpc_ble_gap_callback(cb_type uint8, cb_data []byte) (RPC_T_A result = RPC_T_APP_RESULT(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_gattc_callback(gatt_if uint8, conn_id uint8, cb_data []byte, extra_data []byte) (RPC_T_APP_RESULT, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_gattc_callback(gatt_if uint8, conn_id uint8, cb_data []byte, extra_data []byte) RPC_T_APP_RESULT { if r.debug { fmt.Printf("rpc_ble_gattc_callback()\r\n") } @@ -3639,10 +2739,7 @@ func (r *RTL8720DN) Rpc_ble_gattc_callback(gatt_if uint8, conn_id uint8, cb_data msg = append(msg, byte(len(extra_data)), byte(len(extra_data)>>8), byte(len(extra_data)>>16), byte(len(extra_data)>>24)) msg = append(msg, []byte(extra_data)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3651,15 +2748,10 @@ func (r *RTL8720DN) Rpc_ble_gattc_callback(gatt_if uint8, conn_id uint8, cb_data result = RPC_T_APP_RESULT(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_gatts_callback(gatt_if uint8, conn_id uint8, attrib_index uint16, event RPC_T_SERVICE_CALLBACK_TYPE, property uint16, read_cb_data *[]byte, write_cb_data []byte, app_cb_data []byte) (RPC_T_APP_RESULT, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_gatts_callback(gatt_if uint8, conn_id uint8, attrib_index uint16, event RPC_T_SERVICE_CALLBACK_TYPE, property uint16, read_cb_data []byte, write_cb_data []byte, app_cb_data []byte) RPC_T_APP_RESULT { if r.debug { fmt.Printf("rpc_ble_gatts_callback()\r\n") } @@ -3697,10 +2789,7 @@ func (r *RTL8720DN) Rpc_ble_gatts_callback(gatt_if uint8, conn_id uint8, attrib_ msg = append(msg, []byte(app_cb_data)...) } - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3712,24 +2801,18 @@ func (r *RTL8720DN) Rpc_ble_gatts_callback(gatt_if uint8, conn_id uint8, attrib_ widx += 4 } if read_cb_data_length > 0 { - copy(*read_cb_data, payload[widx:widx+int(read_cb_data_length)]) + copy(read_cb_data, payload[widx:widx+int(read_cb_data_length)]) widx += int(read_cb_data_length) } - *read_cb_data = (*read_cb_data)[:read_cb_data_length] var result RPC_T_APP_RESULT result = RPC_T_APP_RESULT(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_connect(ssid string, password string, security_type uint32, key_id int32, semaphore uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_connect(ssid string, password string, security_type uint32, key_id int32, semaphore uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_connect()\r\n") } @@ -3762,33 +2845,20 @@ func (r *RTL8720DN) Rpc_wifi_connect(ssid string, password string, security_type msg = append(msg, byte(semaphore>>16)) msg = append(msg, byte(semaphore>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_connect_bssid(bssid []byte, ssid string, password string, security_type uint32, key_id int32, semaphore uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_connect_bssid(bssid []byte, ssid string, password string, security_type uint32, key_id int32, semaphore uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_connect_bssid()\r\n") } @@ -3824,95 +2894,56 @@ func (r *RTL8720DN) Rpc_wifi_connect_bssid(bssid []byte, ssid string, password s msg = append(msg, byte(semaphore>>16)) msg = append(msg, byte(semaphore>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_disconnect() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_disconnect() int32 { if r.debug { fmt.Printf("rpc_wifi_disconnect()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x03, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_is_connected_to_ap() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_is_connected_to_ap() int32 { if r.debug { fmt.Printf("rpc_wifi_is_connected_to_ap()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x04, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_is_up(itf uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_is_up(itf uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_is_up()\r\n") } @@ -3924,33 +2955,20 @@ func (r *RTL8720DN) Rpc_wifi_is_up(itf uint32) (int32, error) { msg = append(msg, byte(itf>>16)) msg = append(msg, byte(itf>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_is_ready_to_transceive(itf uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_is_ready_to_transceive(itf uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_is_ready_to_transceive()\r\n") } @@ -3962,33 +2980,20 @@ func (r *RTL8720DN) Rpc_wifi_is_ready_to_transceive(itf uint32) (int32, error) { msg = append(msg, byte(itf>>16)) msg = append(msg, byte(itf>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_mac_address(mac []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_mac_address(mac []byte) int32 { if r.debug { fmt.Printf("rpc_wifi_set_mac_address()\r\n") } @@ -3998,205 +3003,124 @@ func (r *RTL8720DN) Rpc_wifi_set_mac_address(mac []byte) (int32, error) { msg = append(msg, byte(len(mac)), byte(len(mac)>>8), byte(len(mac)>>16), byte(len(mac)>>24)) msg = append(msg, []byte(mac)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_mac_address(mac *uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_mac_address(mac []uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_get_mac_address()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x08, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 - // mac : out uint8 - *mac = payload[widx] - widx += 1 + // mac : out []uint8 + copy(mac, payload[widx:widx+18]) + widx += 18 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_enable_powersave() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_enable_powersave() int32 { if r.debug { fmt.Printf("rpc_wifi_enable_powersave()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x09, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_resume_powersave() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_resume_powersave() int32 { if r.debug { fmt.Printf("rpc_wifi_resume_powersave()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x0A, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_disable_powersave() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_disable_powersave() int32 { if r.debug { fmt.Printf("rpc_wifi_disable_powersave()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x0B, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_btcoex_set_bt_on() error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_btcoex_set_bt_on() { if r.debug { fmt.Printf("rpc_wifi_btcoex_set_bt_on()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x0C, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_wifi_btcoex_set_bt_off() error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_btcoex_set_bt_off() { if r.debug { fmt.Printf("rpc_wifi_btcoex_set_bt_off()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x0D, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_wifi_get_associated_client_list(client_list_buffer *[]byte, buffer_length uint16) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_associated_client_list(client_list_buffer []byte, buffer_length uint16) int32 { if r.debug { fmt.Printf("rpc_wifi_get_associated_client_list()\r\n") } @@ -4206,10 +3130,7 @@ func (r *RTL8720DN) Rpc_wifi_get_associated_client_list(client_list_buffer *[]by msg = append(msg, byte(buffer_length>>0)) msg = append(msg, byte(buffer_length>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -4217,39 +3138,25 @@ func (r *RTL8720DN) Rpc_wifi_get_associated_client_list(client_list_buffer *[]by client_list_buffer_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if client_list_buffer_length > 0 { - copy(*client_list_buffer, payload[widx:widx+int(client_list_buffer_length)]) + copy(client_list_buffer, payload[widx:widx+int(client_list_buffer_length)]) widx += int(client_list_buffer_length) } - *client_list_buffer = (*client_list_buffer)[:client_list_buffer_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_ap_bssid(bssid *uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_ap_bssid(bssid *uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_get_ap_bssid()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x0F, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -4259,32 +3166,19 @@ func (r *RTL8720DN) Rpc_wifi_get_ap_bssid(bssid *uint8) (int32, error) { var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_ap_info(ap_info *[]byte, security *uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_ap_info(ap_info []byte, security *uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_get_ap_info()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x10, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -4292,33 +3186,22 @@ func (r *RTL8720DN) Rpc_wifi_get_ap_info(ap_info *[]byte, security *uint32) (int ap_info_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if ap_info_length > 0 { - copy(*ap_info, payload[widx:widx+int(ap_info_length)]) + copy(ap_info, payload[widx:widx+int(ap_info_length)]) widx += int(ap_info_length) } - *ap_info = (*ap_info)[:ap_info_length] // security : out uint32 *security = binary.LittleEndian.Uint32(payload[widx:]) widx += 4 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_country(country_code uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_country(country_code uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_set_country()\r\n") } @@ -4330,42 +3213,26 @@ func (r *RTL8720DN) Rpc_wifi_set_country(country_code uint32) (int32, error) { msg = append(msg, byte(country_code>>16)) msg = append(msg, byte(country_code>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_sta_max_data_rate(inidata_rate *uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_sta_max_data_rate(inidata_rate *uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_get_sta_max_data_rate()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x12, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -4375,32 +3242,19 @@ func (r *RTL8720DN) Rpc_wifi_get_sta_max_data_rate(inidata_rate *uint8) (int32, var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_rssi(pRSSI *int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_rssi(pRSSI *int32) int32 { if r.debug { fmt.Printf("rpc_wifi_get_rssi()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x13, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -4410,23 +3264,13 @@ func (r *RTL8720DN) Rpc_wifi_get_rssi(pRSSI *int32) (int32, error) { var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_channel(channel int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_channel(channel int32) int32 { if r.debug { fmt.Printf("rpc_wifi_set_channel()\r\n") } @@ -4438,42 +3282,26 @@ func (r *RTL8720DN) Rpc_wifi_set_channel(channel int32) (int32, error) { msg = append(msg, byte(channel>>16)) msg = append(msg, byte(channel>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_channel(channel *int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_channel(channel *int32) int32 { if r.debug { fmt.Printf("rpc_wifi_get_channel()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x15, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -4483,23 +3311,13 @@ func (r *RTL8720DN) Rpc_wifi_get_channel(channel *int32) (int32, error) { var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_change_channel_plan(channel_plan uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_change_channel_plan(channel_plan uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_change_channel_plan()\r\n") } @@ -4508,33 +3326,20 @@ func (r *RTL8720DN) Rpc_wifi_change_channel_plan(channel_plan uint8) (int32, err // channel_plan : in uint8 msg = append(msg, byte(channel_plan>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_register_multicast_address(mac uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_register_multicast_address(mac uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_register_multicast_address()\r\n") } @@ -4543,33 +3348,20 @@ func (r *RTL8720DN) Rpc_wifi_register_multicast_address(mac uint8) (int32, error // mac : in uint8 msg = append(msg, byte(mac>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_unregister_multicast_address(mac uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_unregister_multicast_address(mac uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_unregister_multicast_address()\r\n") } @@ -4578,95 +3370,56 @@ func (r *RTL8720DN) Rpc_wifi_unregister_multicast_address(mac uint8) (int32, err // mac : in uint8 msg = append(msg, byte(mac>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_rf_on() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_rf_on() int32 { if r.debug { fmt.Printf("rpc_wifi_rf_on()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x19, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_rf_off() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_rf_off() int32 { if r.debug { fmt.Printf("rpc_wifi_rf_off()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x1A, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_on(mode uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_on(mode uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_on()\r\n") } @@ -4678,64 +3431,38 @@ func (r *RTL8720DN) Rpc_wifi_on(mode uint32) (int32, error) { msg = append(msg, byte(mode>>16)) msg = append(msg, byte(mode>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_off() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_off() int32 { if r.debug { fmt.Printf("rpc_wifi_off()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x1C, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_mode(mode uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_mode(mode uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_set_mode()\r\n") } @@ -4747,64 +3474,38 @@ func (r *RTL8720DN) Rpc_wifi_set_mode(mode uint32) (int32, error) { msg = append(msg, byte(mode>>16)) msg = append(msg, byte(mode>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_off_fastly() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_off_fastly() int32 { if r.debug { fmt.Printf("rpc_wifi_off_fastly()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x1E, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_power_mode(ips_mode uint8, lps_mode uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_power_mode(ips_mode uint8, lps_mode uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_power_mode()\r\n") } @@ -4815,33 +3516,20 @@ func (r *RTL8720DN) Rpc_wifi_set_power_mode(ips_mode uint8, lps_mode uint8) (int // lps_mode : in uint8 msg = append(msg, byte(lps_mode>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_tdma_param(slot_period uint8, rfon_period_len_1 uint8, rfon_period_len_2 uint8, rfon_period_len_3 uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_tdma_param(slot_period uint8, rfon_period_len_1 uint8, rfon_period_len_2 uint8, rfon_period_len_3 uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_tdma_param()\r\n") } @@ -4856,33 +3544,20 @@ func (r *RTL8720DN) Rpc_wifi_set_tdma_param(slot_period uint8, rfon_period_len_1 // rfon_period_len_3 : in uint8 msg = append(msg, byte(rfon_period_len_3>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_lps_dtim(dtim uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_lps_dtim(dtim uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_lps_dtim()\r\n") } @@ -4891,42 +3566,26 @@ func (r *RTL8720DN) Rpc_wifi_set_lps_dtim(dtim uint8) (int32, error) { // dtim : in uint8 msg = append(msg, byte(dtim>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_lps_dtim(dtim *uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_lps_dtim(dtim *uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_get_lps_dtim()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x22, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -4936,23 +3595,13 @@ func (r *RTL8720DN) Rpc_wifi_get_lps_dtim(dtim *uint8) (int32, error) { var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_lps_thresh(mode uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_lps_thresh(mode uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_lps_thresh()\r\n") } @@ -4961,33 +3610,20 @@ func (r *RTL8720DN) Rpc_wifi_set_lps_thresh(mode uint8) (int32, error) { // mode : in uint8 msg = append(msg, byte(mode>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_lps_level(lps_level uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_lps_level(lps_level uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_lps_level()\r\n") } @@ -4996,33 +3632,20 @@ func (r *RTL8720DN) Rpc_wifi_set_lps_level(lps_level uint8) (int32, error) { // lps_level : in uint8 msg = append(msg, byte(lps_level>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_mfp_support(value uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_mfp_support(value uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_mfp_support()\r\n") } @@ -5031,33 +3654,20 @@ func (r *RTL8720DN) Rpc_wifi_set_mfp_support(value uint8) (int32, error) { // value : in uint8 msg = append(msg, byte(value>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_start_ap(ssid string, password string, security_type uint32, channel int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_start_ap(ssid string, password string, security_type uint32, channel int32) int32 { if r.debug { fmt.Printf("rpc_wifi_start_ap()\r\n") } @@ -5085,33 +3695,20 @@ func (r *RTL8720DN) Rpc_wifi_start_ap(ssid string, password string, security_typ msg = append(msg, byte(channel>>16)) msg = append(msg, byte(channel>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_start_ap_with_hidden_ssid(ssid string, password string, security_type uint32, channel int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_start_ap_with_hidden_ssid(ssid string, password string, security_type uint32, channel int32) int32 { if r.debug { fmt.Printf("rpc_wifi_start_ap_with_hidden_ssid()\r\n") } @@ -5139,33 +3736,20 @@ func (r *RTL8720DN) Rpc_wifi_start_ap_with_hidden_ssid(ssid string, password str msg = append(msg, byte(channel>>16)) msg = append(msg, byte(channel>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_pscan_chan(channel_list []byte, pscan_config uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_pscan_chan(channel_list []byte, pscan_config uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_pscan_chan()\r\n") } @@ -5177,33 +3761,20 @@ func (r *RTL8720DN) Rpc_wifi_set_pscan_chan(channel_list []byte, pscan_config ui // pscan_config : in uint8 msg = append(msg, byte(pscan_config>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_setting(ifname string, pSetting *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_setting(ifname string, pSetting []byte) int32 { if r.debug { fmt.Printf("rpc_wifi_get_setting()\r\n") } @@ -5213,10 +3784,7 @@ func (r *RTL8720DN) Rpc_wifi_get_setting(ifname string, pSetting *[]byte) (int32 msg = append(msg, byte(len(ifname)), byte(len(ifname)>>8), byte(len(ifname)>>16), byte(len(ifname)>>24)) msg = append(msg, []byte(ifname)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -5224,30 +3792,19 @@ func (r *RTL8720DN) Rpc_wifi_get_setting(ifname string, pSetting *[]byte) (int32 pSetting_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pSetting_length > 0 { - copy(*pSetting, payload[widx:widx+int(pSetting_length)]) + copy(pSetting, payload[widx:widx+int(pSetting_length)]) widx += int(pSetting_length) } - *pSetting = (*pSetting)[:pSetting_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_network_mode(mode uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_network_mode(mode uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_set_network_mode()\r\n") } @@ -5259,42 +3816,26 @@ func (r *RTL8720DN) Rpc_wifi_set_network_mode(mode uint32) (int32, error) { msg = append(msg, byte(mode>>16)) msg = append(msg, byte(mode>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_network_mode(pmode *uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_network_mode(pmode *uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_get_network_mode()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x2B, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -5304,23 +3845,13 @@ func (r *RTL8720DN) Rpc_wifi_get_network_mode(pmode *uint32) (int32, error) { var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_wps_phase(is_trigger_wps uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_wps_phase(is_trigger_wps uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_wps_phase()\r\n") } @@ -5329,33 +3860,20 @@ func (r *RTL8720DN) Rpc_wifi_set_wps_phase(is_trigger_wps uint8) (int32, error) // is_trigger_wps : in uint8 msg = append(msg, byte(is_trigger_wps>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_restart_ap(ssid []byte, password []byte, security_type uint32, channel int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_restart_ap(ssid []byte, password []byte, security_type uint32, channel int32) int32 { if r.debug { fmt.Printf("rpc_wifi_restart_ap()\r\n") } @@ -5378,33 +3896,20 @@ func (r *RTL8720DN) Rpc_wifi_restart_ap(ssid []byte, password []byte, security_t msg = append(msg, byte(channel>>16)) msg = append(msg, byte(channel>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_config_autoreconnect(mode uint8, retry_times uint8, timeout uint16) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_config_autoreconnect(mode uint8, retry_times uint8, timeout uint16) int32 { if r.debug { fmt.Printf("rpc_wifi_config_autoreconnect()\r\n") } @@ -5418,33 +3923,20 @@ func (r *RTL8720DN) Rpc_wifi_config_autoreconnect(mode uint8, retry_times uint8, msg = append(msg, byte(timeout>>0)) msg = append(msg, byte(timeout>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_autoreconnect(mode uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_autoreconnect(mode uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_autoreconnect()\r\n") } @@ -5453,42 +3945,26 @@ func (r *RTL8720DN) Rpc_wifi_set_autoreconnect(mode uint8) (int32, error) { // mode : in uint8 msg = append(msg, byte(mode>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_autoreconnect(mode *uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_autoreconnect(mode *uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_get_autoreconnect()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x30, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -5498,54 +3974,31 @@ func (r *RTL8720DN) Rpc_wifi_get_autoreconnect(mode *uint8) (int32, error) { var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_last_error() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_last_error() int32 { if r.debug { fmt.Printf("rpc_wifi_get_last_error()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x31, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_add_custom_ie(cus_ie []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_add_custom_ie(cus_ie []byte) int32 { if r.debug { fmt.Printf("rpc_wifi_add_custom_ie()\r\n") } @@ -5555,33 +4008,20 @@ func (r *RTL8720DN) Rpc_wifi_add_custom_ie(cus_ie []byte) (int32, error) { msg = append(msg, byte(len(cus_ie)), byte(len(cus_ie)>>8), byte(len(cus_ie)>>16), byte(len(cus_ie)>>24)) msg = append(msg, []byte(cus_ie)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_update_custom_ie(cus_ie []byte, ie_index int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_update_custom_ie(cus_ie []byte, ie_index int32) int32 { if r.debug { fmt.Printf("rpc_wifi_update_custom_ie()\r\n") } @@ -5596,64 +4036,38 @@ func (r *RTL8720DN) Rpc_wifi_update_custom_ie(cus_ie []byte, ie_index int32) (in msg = append(msg, byte(ie_index>>16)) msg = append(msg, byte(ie_index>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_del_custom_ie() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_del_custom_ie() int32 { if r.debug { fmt.Printf("rpc_wifi_del_custom_ie()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x34, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_indicate_mgnt(enable int32) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_indicate_mgnt(enable int32) { if r.debug { fmt.Printf("rpc_wifi_set_indicate_mgnt()\r\n") } @@ -5665,32 +4079,21 @@ func (r *RTL8720DN) Rpc_wifi_set_indicate_mgnt(enable int32) error { msg = append(msg, byte(enable>>16)) msg = append(msg, byte(enable>>24)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_wifi_get_drv_ability(ability *uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_drv_ability(ability *uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_get_drv_ability()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x36, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -5700,23 +4103,13 @@ func (r *RTL8720DN) Rpc_wifi_get_drv_ability(ability *uint32) (int32, error) { var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_channel_plan(channel_plan uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_channel_plan(channel_plan uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_channel_plan()\r\n") } @@ -5725,42 +4118,26 @@ func (r *RTL8720DN) Rpc_wifi_set_channel_plan(channel_plan uint8) (int32, error) // channel_plan : in uint8 msg = append(msg, byte(channel_plan>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_channel_plan(channel_plan *uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_channel_plan(channel_plan *uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_get_channel_plan()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x38, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -5770,85 +4147,49 @@ func (r *RTL8720DN) Rpc_wifi_get_channel_plan(channel_plan *uint8) (int32, error var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_enable_forwarding() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_enable_forwarding() int32 { if r.debug { fmt.Printf("rpc_wifi_enable_forwarding()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x39, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_disable_forwarding() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_disable_forwarding() int32 { if r.debug { fmt.Printf("rpc_wifi_disable_forwarding()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x3A, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_ch_deauth(enable uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_ch_deauth(enable uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_ch_deauth()\r\n") } @@ -5857,42 +4198,26 @@ func (r *RTL8720DN) Rpc_wifi_set_ch_deauth(enable uint8) (int32, error) { // enable : in uint8 msg = append(msg, byte(enable>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_band_type() (uint8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_band_type() uint8 { if r.debug { fmt.Printf("rpc_wifi_get_band_type()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x3C, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -5900,15 +4225,10 @@ func (r *RTL8720DN) Rpc_wifi_get_band_type() (uint8, error) { result = uint8(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_tx_pause_data(NewState uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_tx_pause_data(NewState uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_set_tx_pause_data()\r\n") } @@ -5920,42 +4240,26 @@ func (r *RTL8720DN) Rpc_wifi_set_tx_pause_data(NewState uint32) (int32, error) { msg = append(msg, byte(NewState>>16)) msg = append(msg, byte(NewState>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_reconnect_data(wifi_info *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_reconnect_data(wifi_info []byte) int32 { if r.debug { fmt.Printf("rpc_wifi_get_reconnect_data()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x3E, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -5963,101 +4267,61 @@ func (r *RTL8720DN) Rpc_wifi_get_reconnect_data(wifi_info *[]byte) (int32, error wifi_info_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if wifi_info_length > 0 { - copy(*wifi_info, payload[widx:widx+int(wifi_info_length)]) + copy(wifi_info, payload[widx:widx+int(wifi_info_length)]) widx += int(wifi_info_length) } - *wifi_info = (*wifi_info)[:wifi_info_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_clear_reconnect_data() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_clear_reconnect_data() int32 { if r.debug { fmt.Printf("rpc_wifi_clear_reconnect_data()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x3F, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_scan_start() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_scan_start() int32 { if r.debug { fmt.Printf("rpc_wifi_scan_start()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x40, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_is_scaning() (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_is_scaning() bool { if r.debug { fmt.Printf("rpc_wifi_is_scaning()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x41, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -6065,15 +4329,10 @@ func (r *RTL8720DN) Rpc_wifi_is_scaning() (bool, error) { result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_scan_get_ap_records(number uint16, _scanResult *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_scan_get_ap_records(number uint16, _scanResult []byte) int32 { if r.debug { fmt.Printf("rpc_wifi_scan_get_ap_records()\r\n") } @@ -6083,10 +4342,7 @@ func (r *RTL8720DN) Rpc_wifi_scan_get_ap_records(number uint16, _scanResult *[]b msg = append(msg, byte(number>>0)) msg = append(msg, byte(number>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -6094,39 +4350,25 @@ func (r *RTL8720DN) Rpc_wifi_scan_get_ap_records(number uint16, _scanResult *[]b _scanResult_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if _scanResult_length > 0 { - copy(*_scanResult, payload[widx:widx+int(_scanResult_length)]) + copy(_scanResult, payload[widx:widx+int(_scanResult_length)]) widx += int(_scanResult_length) } - *_scanResult = (*_scanResult)[:_scanResult_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_scan_get_ap_num() (uint16, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_scan_get_ap_num() uint16 { if r.debug { fmt.Printf("rpc_wifi_scan_get_ap_num()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x43, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -6134,46 +4376,28 @@ func (r *RTL8720DN) Rpc_wifi_scan_get_ap_num() (uint16, error) { result = uint16(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_init() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_init() int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_init()\r\n") } msg := startWriteMessage(0x00, 0x0F, 0x01, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_sta_start(mac []byte, ip_info []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_sta_start(mac []byte, ip_info []byte) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_sta_start()\r\n") } @@ -6186,33 +4410,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_sta_start(mac []byte, ip_info []byte) (int msg = append(msg, byte(len(ip_info)), byte(len(ip_info)>>8), byte(len(ip_info)>>16), byte(len(ip_info)>>24)) msg = append(msg, []byte(ip_info)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_ap_start(mac []byte, ip_info []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_ap_start(mac []byte, ip_info []byte) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_ap_start()\r\n") } @@ -6225,33 +4436,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_ap_start(mac []byte, ip_info []byte) (int3 msg = append(msg, byte(len(ip_info)), byte(len(ip_info)>>8), byte(len(ip_info)>>16), byte(len(ip_info)>>24)) msg = append(msg, []byte(ip_info)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_stop(tcpip_if uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_stop(tcpip_if uint32) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_stop()\r\n") } @@ -6263,33 +4461,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_stop(tcpip_if uint32) (int32, error) { msg = append(msg, byte(tcpip_if>>16)) msg = append(msg, byte(tcpip_if>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_up(tcpip_if uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_up(tcpip_if uint32) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_up()\r\n") } @@ -6301,33 +4486,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_up(tcpip_if uint32) (int32, error) { msg = append(msg, byte(tcpip_if>>16)) msg = append(msg, byte(tcpip_if>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_down(tcpip_if uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_down(tcpip_if uint32) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_down()\r\n") } @@ -6339,33 +4511,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_down(tcpip_if uint32) (int32, error) { msg = append(msg, byte(tcpip_if>>16)) msg = append(msg, byte(tcpip_if>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_get_ip_info(tcpip_if uint32, ip_info *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_get_ip_info(tcpip_if uint32, ip_info []byte) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_get_ip_info()\r\n") } @@ -6377,10 +4536,7 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_get_ip_info(tcpip_if uint32, ip_info *[]by msg = append(msg, byte(tcpip_if>>16)) msg = append(msg, byte(tcpip_if>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -6388,30 +4544,19 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_get_ip_info(tcpip_if uint32, ip_info *[]by ip_info_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if ip_info_length > 0 { - copy(*ip_info, payload[widx:widx+int(ip_info_length)]) + copy(ip_info, payload[widx:widx+int(ip_info_length)]) widx += int(ip_info_length) } - *ip_info = (*ip_info)[:ip_info_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_set_ip_info(tcpip_if uint32, ip_info []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_set_ip_info(tcpip_if uint32, ip_info []byte) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_set_ip_info()\r\n") } @@ -6426,33 +4571,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_set_ip_info(tcpip_if uint32, ip_info []byt msg = append(msg, byte(len(ip_info)), byte(len(ip_info)>>8), byte(len(ip_info)>>16), byte(len(ip_info)>>24)) msg = append(msg, []byte(ip_info)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_set_dns_info(tcpip_if uint32, dns_type uint32, dns []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_set_dns_info(tcpip_if uint32, dns_type uint32, dns []byte) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_set_dns_info()\r\n") } @@ -6472,33 +4604,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_set_dns_info(tcpip_if uint32, dns_type uin msg = append(msg, byte(len(dns)), byte(len(dns)>>8), byte(len(dns)>>16), byte(len(dns)>>24)) msg = append(msg, []byte(dns)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_get_dns_info(tcpip_if uint32, dns_type uint32, dns *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_get_dns_info(tcpip_if uint32, dns_type uint32, dns []byte) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_get_dns_info()\r\n") } @@ -6515,10 +4634,7 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_get_dns_info(tcpip_if uint32, dns_type uin msg = append(msg, byte(dns_type>>16)) msg = append(msg, byte(dns_type>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -6526,30 +4642,19 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_get_dns_info(tcpip_if uint32, dns_type uin dns_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if dns_length > 0 { - copy(*dns, payload[widx:widx+int(dns_length)]) + copy(dns, payload[widx:widx+int(dns_length)]) widx += int(dns_length) } - *dns = (*dns)[:dns_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_dhcps_start(tcpip_if uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_dhcps_start(tcpip_if uint32) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_dhcps_start()\r\n") } @@ -6561,33 +4666,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_dhcps_start(tcpip_if uint32) (int32, error msg = append(msg, byte(tcpip_if>>16)) msg = append(msg, byte(tcpip_if>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_dhcps_stop(tcpip_if uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_dhcps_stop(tcpip_if uint32) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_dhcps_stop()\r\n") } @@ -6599,33 +4691,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_dhcps_stop(tcpip_if uint32) (int32, error) msg = append(msg, byte(tcpip_if>>16)) msg = append(msg, byte(tcpip_if>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_dhcpc_start(tcpip_if uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_dhcpc_start(tcpip_if uint32) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_dhcpc_start()\r\n") } @@ -6637,33 +4716,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_dhcpc_start(tcpip_if uint32) (int32, error msg = append(msg, byte(tcpip_if>>16)) msg = append(msg, byte(tcpip_if>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_dhcpc_stop(tcpip_if uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_dhcpc_stop(tcpip_if uint32) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_dhcpc_stop()\r\n") } @@ -6675,33 +4741,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_dhcpc_stop(tcpip_if uint32) (int32, error) msg = append(msg, byte(tcpip_if>>16)) msg = append(msg, byte(tcpip_if>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_set_hostname(tcpip_if uint32, hostname string) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_set_hostname(tcpip_if uint32, hostname string) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_set_hostname()\r\n") } @@ -6716,33 +4769,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_set_hostname(tcpip_if uint32, hostname str msg = append(msg, byte(len(hostname)), byte(len(hostname)>>8), byte(len(hostname)>>16), byte(len(hostname)>>24)) msg = append(msg, []byte(hostname)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_get_hostname(tcpip_if uint32, hostname string) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_get_hostname(tcpip_if uint32, hostname string) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_get_hostname()\r\n") } @@ -6754,10 +4794,7 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_get_hostname(tcpip_if uint32, hostname str msg = append(msg, byte(tcpip_if>>16)) msg = append(msg, byte(tcpip_if>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -6768,27 +4805,16 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_get_hostname(tcpip_if uint32, hostname str hostname = string(payload[widx : widx+int(hostname_length)]) widx += int(hostname_length) } - hostname = (hostname)[:hostname_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_get_mac(tcpip_if uint32, mac *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_get_mac(tcpip_if uint32, mac []byte) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_get_mac()\r\n") } @@ -6800,10 +4826,7 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_get_mac(tcpip_if uint32, mac *[]byte) (int msg = append(msg, byte(tcpip_if>>16)) msg = append(msg, byte(tcpip_if>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -6811,30 +4834,19 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_get_mac(tcpip_if uint32, mac *[]byte) (int mac_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if mac_length > 0 { - copy(*mac, payload[widx:widx+int(mac_length)]) + copy(mac, payload[widx:widx+int(mac_length)]) widx += int(mac_length) } - *mac = (*mac)[:mac_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_set_mac(tcpip_if uint32, mac []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_set_mac(tcpip_if uint32, mac []byte) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_set_mac()\r\n") } @@ -6849,33 +4861,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_set_mac(tcpip_if uint32, mac []byte) (int3 msg = append(msg, byte(len(mac)), byte(len(mac)>>8), byte(len(mac)>>16), byte(len(mac)>>24)) msg = append(msg, []byte(mac)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_api_call(fn []byte, call []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_api_call(fn []byte, call []byte) int32 { if r.debug { fmt.Printf("rpc_tcpip_api_call()\r\n") } @@ -6888,33 +4887,20 @@ func (r *RTL8720DN) Rpc_tcpip_api_call(fn []byte, call []byte) (int32, error) { msg = append(msg, byte(len(call)), byte(len(call)>>8), byte(len(call)>>16), byte(len(call)>>24)) msg = append(msg, []byte(call)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_connect(pcb_in []byte, pcb_out *[]byte, ipaddr []byte, port uint16, connected []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_connect(pcb_in []byte, pcb_out []byte, ipaddr []byte, port uint16, connected []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_connect()\r\n") } @@ -6933,10 +4919,7 @@ func (r *RTL8720DN) Rpc_tcp_connect(pcb_in []byte, pcb_out *[]byte, ipaddr []byt msg = append(msg, byte(len(connected)), byte(len(connected)>>8), byte(len(connected)>>16), byte(len(connected)>>24)) msg = append(msg, []byte(connected)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -6944,30 +4927,19 @@ func (r *RTL8720DN) Rpc_tcp_connect(pcb_in []byte, pcb_out *[]byte, ipaddr []byt pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_recved(pcb_in []byte, pcb_out *[]byte, length uint16) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_recved(pcb_in []byte, pcb_out []byte, length uint16) int32 { if r.debug { fmt.Printf("rpc_tcp_recved()\r\n") } @@ -6980,10 +4952,7 @@ func (r *RTL8720DN) Rpc_tcp_recved(pcb_in []byte, pcb_out *[]byte, length uint16 msg = append(msg, byte(length>>0)) msg = append(msg, byte(length>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -6991,30 +4960,19 @@ func (r *RTL8720DN) Rpc_tcp_recved(pcb_in []byte, pcb_out *[]byte, length uint16 pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_abort(pcb_in []byte, pcb_out *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_abort(pcb_in []byte, pcb_out []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_abort()\r\n") } @@ -7024,10 +4982,7 @@ func (r *RTL8720DN) Rpc_tcp_abort(pcb_in []byte, pcb_out *[]byte) (int32, error) msg = append(msg, byte(len(pcb_in)), byte(len(pcb_in)>>8), byte(len(pcb_in)>>16), byte(len(pcb_in)>>24)) msg = append(msg, []byte(pcb_in)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7035,30 +4990,19 @@ func (r *RTL8720DN) Rpc_tcp_abort(pcb_in []byte, pcb_out *[]byte) (int32, error) pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_write(pcb_in []byte, pcb_out *[]byte, data []byte, apiflags uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_write(pcb_in []byte, pcb_out []byte, data []byte, apiflags uint8) int32 { if r.debug { fmt.Printf("rpc_tcp_write()\r\n") } @@ -7073,10 +5017,7 @@ func (r *RTL8720DN) Rpc_tcp_write(pcb_in []byte, pcb_out *[]byte, data []byte, a // apiflags : in uint8 msg = append(msg, byte(apiflags>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7084,30 +5025,19 @@ func (r *RTL8720DN) Rpc_tcp_write(pcb_in []byte, pcb_out *[]byte, data []byte, a pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_output(pcb_in []byte, pcb_out *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_output(pcb_in []byte, pcb_out []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_output()\r\n") } @@ -7117,10 +5047,7 @@ func (r *RTL8720DN) Rpc_tcp_output(pcb_in []byte, pcb_out *[]byte) (int32, error msg = append(msg, byte(len(pcb_in)), byte(len(pcb_in)>>8), byte(len(pcb_in)>>16), byte(len(pcb_in)>>24)) msg = append(msg, []byte(pcb_in)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7128,30 +5055,19 @@ func (r *RTL8720DN) Rpc_tcp_output(pcb_in []byte, pcb_out *[]byte) (int32, error pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_close(pcb_in []byte, pcb_out *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_close(pcb_in []byte, pcb_out []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_close()\r\n") } @@ -7161,10 +5077,7 @@ func (r *RTL8720DN) Rpc_tcp_close(pcb_in []byte, pcb_out *[]byte) (int32, error) msg = append(msg, byte(len(pcb_in)), byte(len(pcb_in)>>8), byte(len(pcb_in)>>16), byte(len(pcb_in)>>24)) msg = append(msg, []byte(pcb_in)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7172,30 +5085,19 @@ func (r *RTL8720DN) Rpc_tcp_close(pcb_in []byte, pcb_out *[]byte) (int32, error) pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_bind(pcb_in []byte, pcb_out *[]byte, ipaddr []byte, port uint16) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_bind(pcb_in []byte, pcb_out []byte, ipaddr []byte, port uint16) int32 { if r.debug { fmt.Printf("rpc_tcp_bind()\r\n") } @@ -7211,10 +5113,7 @@ func (r *RTL8720DN) Rpc_tcp_bind(pcb_in []byte, pcb_out *[]byte, ipaddr []byte, msg = append(msg, byte(port>>0)) msg = append(msg, byte(port>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7222,30 +5121,19 @@ func (r *RTL8720DN) Rpc_tcp_bind(pcb_in []byte, pcb_out *[]byte, ipaddr []byte, pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_new_ip_type(ip_type uint8, pcb_out *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_new_ip_type(ip_type uint8, pcb_out []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_new_ip_type()\r\n") } @@ -7254,10 +5142,7 @@ func (r *RTL8720DN) Rpc_tcp_new_ip_type(ip_type uint8, pcb_out *[]byte) (int32, // ip_type : in uint8 msg = append(msg, byte(ip_type>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7265,30 +5150,19 @@ func (r *RTL8720DN) Rpc_tcp_new_ip_type(ip_type uint8, pcb_out *[]byte) (int32, pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_arg(pcb_in []byte, pcb_out *[]byte, func_arg []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_arg(pcb_in []byte, pcb_out []byte, func_arg []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_arg()\r\n") } @@ -7301,10 +5175,7 @@ func (r *RTL8720DN) Rpc_tcp_arg(pcb_in []byte, pcb_out *[]byte, func_arg []byte) msg = append(msg, byte(len(func_arg)), byte(len(func_arg)>>8), byte(len(func_arg)>>16), byte(len(func_arg)>>24)) msg = append(msg, []byte(func_arg)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7312,30 +5183,19 @@ func (r *RTL8720DN) Rpc_tcp_arg(pcb_in []byte, pcb_out *[]byte, func_arg []byte) pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_err(pcb_in []byte, pcb_out *[]byte, func_err []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_err(pcb_in []byte, pcb_out []byte, func_err []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_err()\r\n") } @@ -7348,10 +5208,7 @@ func (r *RTL8720DN) Rpc_tcp_err(pcb_in []byte, pcb_out *[]byte, func_err []byte) msg = append(msg, byte(len(func_err)), byte(len(func_err)>>8), byte(len(func_err)>>16), byte(len(func_err)>>24)) msg = append(msg, []byte(func_err)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7359,30 +5216,19 @@ func (r *RTL8720DN) Rpc_tcp_err(pcb_in []byte, pcb_out *[]byte, func_err []byte) pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_recv(pcb_in []byte, pcb_out *[]byte, func_recv []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_recv(pcb_in []byte, pcb_out []byte, func_recv []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_recv()\r\n") } @@ -7395,10 +5241,7 @@ func (r *RTL8720DN) Rpc_tcp_recv(pcb_in []byte, pcb_out *[]byte, func_recv []byt msg = append(msg, byte(len(func_recv)), byte(len(func_recv)>>8), byte(len(func_recv)>>16), byte(len(func_recv)>>24)) msg = append(msg, []byte(func_recv)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7406,30 +5249,19 @@ func (r *RTL8720DN) Rpc_tcp_recv(pcb_in []byte, pcb_out *[]byte, func_recv []byt pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_sent(pcb_in []byte, pcb_out *[]byte, func_sent []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_sent(pcb_in []byte, pcb_out []byte, func_sent []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_sent()\r\n") } @@ -7442,10 +5274,7 @@ func (r *RTL8720DN) Rpc_tcp_sent(pcb_in []byte, pcb_out *[]byte, func_sent []byt msg = append(msg, byte(len(func_sent)), byte(len(func_sent)>>8), byte(len(func_sent)>>16), byte(len(func_sent)>>24)) msg = append(msg, []byte(func_sent)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7453,30 +5282,19 @@ func (r *RTL8720DN) Rpc_tcp_sent(pcb_in []byte, pcb_out *[]byte, func_sent []byt pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_accept(pcb_in []byte, pcb_out *[]byte, func_accept []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_accept(pcb_in []byte, pcb_out []byte, func_accept []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_accept()\r\n") } @@ -7489,10 +5307,7 @@ func (r *RTL8720DN) Rpc_tcp_accept(pcb_in []byte, pcb_out *[]byte, func_accept [ msg = append(msg, byte(len(func_accept)), byte(len(func_accept)>>8), byte(len(func_accept)>>16), byte(len(func_accept)>>24)) msg = append(msg, []byte(func_accept)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7500,30 +5315,19 @@ func (r *RTL8720DN) Rpc_tcp_accept(pcb_in []byte, pcb_out *[]byte, func_accept [ pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_poll(pcb_in []byte, pcb_out *[]byte, func_poll []byte, interval uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_poll(pcb_in []byte, pcb_out []byte, func_poll []byte, interval uint8) int32 { if r.debug { fmt.Printf("rpc_tcp_poll()\r\n") } @@ -7538,10 +5342,7 @@ func (r *RTL8720DN) Rpc_tcp_poll(pcb_in []byte, pcb_out *[]byte, func_poll []byt // interval : in uint8 msg = append(msg, byte(interval>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7549,30 +5350,19 @@ func (r *RTL8720DN) Rpc_tcp_poll(pcb_in []byte, pcb_out *[]byte, func_poll []byt pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_listen_with_backlog(pcb_in []byte, pcb_out *[]byte, backlog uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_listen_with_backlog(pcb_in []byte, pcb_out []byte, backlog uint8) int32 { if r.debug { fmt.Printf("rpc_tcp_listen_with_backlog()\r\n") } @@ -7584,10 +5374,7 @@ func (r *RTL8720DN) Rpc_tcp_listen_with_backlog(pcb_in []byte, pcb_out *[]byte, // backlog : in uint8 msg = append(msg, byte(backlog>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7595,30 +5382,19 @@ func (r *RTL8720DN) Rpc_tcp_listen_with_backlog(pcb_in []byte, pcb_out *[]byte, pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_pbuf_free(p []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_pbuf_free(p []byte) int32 { if r.debug { fmt.Printf("rpc_pbuf_free()\r\n") } @@ -7628,33 +5404,20 @@ func (r *RTL8720DN) Rpc_pbuf_free(p []byte) (int32, error) { msg = append(msg, byte(len(p)), byte(len(p)>>8), byte(len(p)>>16), byte(len(p)>>24)) msg = append(msg, []byte(p)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ip4addr_ntoa(ip4_addr_in []byte) (string, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ip4addr_ntoa(ip4_addr_in []byte) string { if r.debug { fmt.Printf("rpc_ip4addr_ntoa()\r\n") } @@ -7664,10 +5427,7 @@ func (r *RTL8720DN) Rpc_ip4addr_ntoa(ip4_addr_in []byte) (string, error) { msg = append(msg, byte(len(ip4_addr_in)), byte(len(ip4_addr_in)>>8), byte(len(ip4_addr_in)>>16), byte(len(ip4_addr_in)>>24)) msg = append(msg, []byte(ip4_addr_in)...) - err := r.performRequest(msg) - if err != nil { - return "", err - } + r.performRequest(msg) r.read() widx := 8 @@ -7678,15 +5438,10 @@ func (r *RTL8720DN) Rpc_ip4addr_ntoa(ip4_addr_in []byte) (string, error) { result = string(payload[widx : widx+int(result_length)]) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_inet_chksum(dataptr_in []byte) (uint16, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_inet_chksum(dataptr_in []byte) uint16 { if r.debug { fmt.Printf("rpc_inet_chksum()\r\n") } @@ -7696,10 +5451,7 @@ func (r *RTL8720DN) Rpc_inet_chksum(dataptr_in []byte) (uint16, error) { msg = append(msg, byte(len(dataptr_in)), byte(len(dataptr_in)>>8), byte(len(dataptr_in)>>16), byte(len(dataptr_in)>>24)) msg = append(msg, []byte(dataptr_in)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7708,15 +5460,10 @@ func (r *RTL8720DN) Rpc_inet_chksum(dataptr_in []byte) (uint16, error) { result = uint16(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_accept(s int32, addr []byte, addrlen *uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_accept(s int32, addr []byte, addrlen *uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_accept()\r\n") } @@ -7736,10 +5483,7 @@ func (r *RTL8720DN) Rpc_lwip_accept(s int32, addr []byte, addrlen *uint32) (int3 msg = append(msg, byte(*addrlen>>16)) msg = append(msg, byte(*addrlen>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7749,23 +5493,13 @@ func (r *RTL8720DN) Rpc_lwip_accept(s int32, addr []byte, addrlen *uint32) (int3 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_bind(s int32, name []byte, namelen uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_bind(s int32, name []byte, namelen uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_bind()\r\n") } @@ -7785,33 +5519,20 @@ func (r *RTL8720DN) Rpc_lwip_bind(s int32, name []byte, namelen uint32) (int32, msg = append(msg, byte(namelen>>16)) msg = append(msg, byte(namelen>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_shutdown(s int32, how int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_shutdown(s int32, how int32) int32 { if r.debug { fmt.Printf("rpc_lwip_shutdown()\r\n") } @@ -7828,33 +5549,20 @@ func (r *RTL8720DN) Rpc_lwip_shutdown(s int32, how int32) (int32, error) { msg = append(msg, byte(how>>16)) msg = append(msg, byte(how>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_getpeername(s int32, name *[]byte, namelen *uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_getpeername(s int32, name []byte, namelen *uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_getpeername()\r\n") } @@ -7871,10 +5579,7 @@ func (r *RTL8720DN) Rpc_lwip_getpeername(s int32, name *[]byte, namelen *uint32) msg = append(msg, byte(*namelen>>16)) msg = append(msg, byte(*namelen>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7882,33 +5587,22 @@ func (r *RTL8720DN) Rpc_lwip_getpeername(s int32, name *[]byte, namelen *uint32) name_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if name_length > 0 { - copy(*name, payload[widx:widx+int(name_length)]) + copy(name, payload[widx:widx+int(name_length)]) widx += int(name_length) } - *name = (*name)[:name_length] // namelen : inout uint32 *namelen = binary.LittleEndian.Uint32(payload[widx:]) widx += 4 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_getsockname(s int32, name *[]byte, namelen *uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_getsockname(s int32, name []byte, namelen *uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_getsockname()\r\n") } @@ -7925,10 +5619,7 @@ func (r *RTL8720DN) Rpc_lwip_getsockname(s int32, name *[]byte, namelen *uint32) msg = append(msg, byte(*namelen>>16)) msg = append(msg, byte(*namelen>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7936,33 +5627,22 @@ func (r *RTL8720DN) Rpc_lwip_getsockname(s int32, name *[]byte, namelen *uint32) name_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if name_length > 0 { - copy(*name, payload[widx:widx+int(name_length)]) + copy(name, payload[widx:widx+int(name_length)]) widx += int(name_length) } - *name = (*name)[:name_length] // namelen : inout uint32 *namelen = binary.LittleEndian.Uint32(payload[widx:]) widx += 4 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_getsockopt(s int32, level int32, optname int32, in_optval []byte, out_optval *[]byte, optlen *uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_getsockopt(s int32, level int32, optname int32, in_optval []byte, out_optval []byte, optlen *uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_getsockopt()\r\n") } @@ -7992,10 +5672,7 @@ func (r *RTL8720DN) Rpc_lwip_getsockopt(s int32, level int32, optname int32, in_ msg = append(msg, byte(*optlen>>16)) msg = append(msg, byte(*optlen>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -8003,33 +5680,22 @@ func (r *RTL8720DN) Rpc_lwip_getsockopt(s int32, level int32, optname int32, in_ out_optval_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if out_optval_length > 0 { - copy(*out_optval, payload[widx:widx+int(out_optval_length)]) + copy(out_optval, payload[widx:widx+int(out_optval_length)]) widx += int(out_optval_length) } - *out_optval = (*out_optval)[:out_optval_length] // optlen : inout uint32 *optlen = binary.LittleEndian.Uint32(payload[widx:]) widx += 4 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_setsockopt(s int32, level int32, optname int32, optval []byte, optlen uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_setsockopt(s int32, level int32, optname int32, optval []byte, optlen uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_setsockopt()\r\n") } @@ -8059,33 +5725,20 @@ func (r *RTL8720DN) Rpc_lwip_setsockopt(s int32, level int32, optname int32, opt msg = append(msg, byte(optlen>>16)) msg = append(msg, byte(optlen>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_close(s int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_close(s int32) int32 { if r.debug { fmt.Printf("rpc_lwip_close()\r\n") } @@ -8097,33 +5750,20 @@ func (r *RTL8720DN) Rpc_lwip_close(s int32) (int32, error) { msg = append(msg, byte(s>>16)) msg = append(msg, byte(s>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_connect(s int32, name []byte, namelen uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_connect(s int32, name []byte, namelen uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_connect()\r\n") } @@ -8143,33 +5783,20 @@ func (r *RTL8720DN) Rpc_lwip_connect(s int32, name []byte, namelen uint32) (int3 msg = append(msg, byte(namelen>>16)) msg = append(msg, byte(namelen>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_listen(s int32, backlog int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_listen(s int32, backlog int32) int32 { if r.debug { fmt.Printf("rpc_lwip_listen()\r\n") } @@ -8186,33 +5813,20 @@ func (r *RTL8720DN) Rpc_lwip_listen(s int32, backlog int32) (int32, error) { msg = append(msg, byte(backlog>>16)) msg = append(msg, byte(backlog>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_available(s int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_available(s int32) int32 { if r.debug { fmt.Printf("rpc_lwip_available()\r\n") } @@ -8224,33 +5838,20 @@ func (r *RTL8720DN) Rpc_lwip_available(s int32) (int32, error) { msg = append(msg, byte(s>>16)) msg = append(msg, byte(s>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_recv(s int32, mem *[]byte, length uint32, flags int32, timeout uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_recv(s int32, mem []byte, length uint32, flags int32, timeout uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_recv()\r\n") } @@ -8277,10 +5878,7 @@ func (r *RTL8720DN) Rpc_lwip_recv(s int32, mem *[]byte, length uint32, flags int msg = append(msg, byte(timeout>>16)) msg = append(msg, byte(timeout>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -8288,30 +5886,19 @@ func (r *RTL8720DN) Rpc_lwip_recv(s int32, mem *[]byte, length uint32, flags int mem_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if mem_length > 0 { - copy(*mem, payload[widx:widx+int(mem_length)]) + copy(mem, payload[widx:widx+int(mem_length)]) widx += int(mem_length) } - *mem = (*mem)[:mem_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_read(s int32, mem *[]byte, length uint32, timeout uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_read(s int32, mem []byte, length uint32, timeout uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_read()\r\n") } @@ -8333,10 +5920,7 @@ func (r *RTL8720DN) Rpc_lwip_read(s int32, mem *[]byte, length uint32, timeout u msg = append(msg, byte(timeout>>16)) msg = append(msg, byte(timeout>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -8344,30 +5928,19 @@ func (r *RTL8720DN) Rpc_lwip_read(s int32, mem *[]byte, length uint32, timeout u mem_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if mem_length > 0 { - copy(*mem, payload[widx:widx+int(mem_length)]) + copy(mem, payload[widx:widx+int(mem_length)]) widx += int(mem_length) } - *mem = (*mem)[:mem_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_recvfrom(s int32, mem *[]byte, length uint32, flags int32, from *[]byte, fromlen *uint32, timeout uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_recvfrom(s int32, mem []byte, length uint32, flags int32, from []byte, fromlen *uint32, timeout uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_recvfrom()\r\n") } @@ -8399,10 +5972,7 @@ func (r *RTL8720DN) Rpc_lwip_recvfrom(s int32, mem *[]byte, length uint32, flags msg = append(msg, byte(timeout>>16)) msg = append(msg, byte(timeout>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -8410,41 +5980,29 @@ func (r *RTL8720DN) Rpc_lwip_recvfrom(s int32, mem *[]byte, length uint32, flags mem_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if mem_length > 0 { - copy(*mem, payload[widx:widx+int(mem_length)]) + copy(mem, payload[widx:widx+int(mem_length)]) widx += int(mem_length) } - *mem = (*mem)[:mem_length] // from : out []byte from_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if from_length > 0 { - copy(*from, payload[widx:widx+int(from_length)]) + copy(from, payload[widx:widx+int(from_length)]) widx += int(from_length) } - *from = (*from)[:from_length] // fromlen : inout uint32 *fromlen = binary.LittleEndian.Uint32(payload[widx:]) widx += 4 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_send(s int32, dataptr []byte, flags int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_send(s int32, dataptr []byte, flags int32) int32 { if r.debug { fmt.Printf("rpc_lwip_send()\r\n") } @@ -8464,33 +6022,20 @@ func (r *RTL8720DN) Rpc_lwip_send(s int32, dataptr []byte, flags int32) (int32, msg = append(msg, byte(flags>>16)) msg = append(msg, byte(flags>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_sendmsg(s int32, msg_name []byte, msg_iov []byte, msg_control []byte, msg_flags int32, flags int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_sendmsg(s int32, msg_name []byte, msg_iov []byte, msg_control []byte, msg_flags int32, flags int32) int32 { if r.debug { fmt.Printf("rpc_lwip_sendmsg()\r\n") } @@ -8521,33 +6066,20 @@ func (r *RTL8720DN) Rpc_lwip_sendmsg(s int32, msg_name []byte, msg_iov []byte, m msg = append(msg, byte(flags>>16)) msg = append(msg, byte(flags>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_sendto(s int32, dataptr []byte, flags int32, to []byte, tolen uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_sendto(s int32, dataptr []byte, flags int32, to []byte, tolen uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_sendto()\r\n") } @@ -8575,33 +6107,20 @@ func (r *RTL8720DN) Rpc_lwip_sendto(s int32, dataptr []byte, flags int32, to []b msg = append(msg, byte(tolen>>16)) msg = append(msg, byte(tolen>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_socket(domain int32, l_type int32, protocol int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_socket(domain int32, l_type int32, protocol int32) int32 { if r.debug { fmt.Printf("rpc_lwip_socket()\r\n") } @@ -8623,33 +6142,20 @@ func (r *RTL8720DN) Rpc_lwip_socket(domain int32, l_type int32, protocol int32) msg = append(msg, byte(protocol>>16)) msg = append(msg, byte(protocol>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_write(s int32, dataptr []byte, size uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_write(s int32, dataptr []byte, size uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_write()\r\n") } @@ -8669,33 +6175,20 @@ func (r *RTL8720DN) Rpc_lwip_write(s int32, dataptr []byte, size uint32) (int32, msg = append(msg, byte(size>>16)) msg = append(msg, byte(size>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_writev(s int32, iov []byte, iovcnt int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_writev(s int32, iov []byte, iovcnt int32) int32 { if r.debug { fmt.Printf("rpc_lwip_writev()\r\n") } @@ -8715,33 +6208,20 @@ func (r *RTL8720DN) Rpc_lwip_writev(s int32, iov []byte, iovcnt int32) (int32, e msg = append(msg, byte(iovcnt>>16)) msg = append(msg, byte(iovcnt>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_select(maxfdp1 int32, readset []byte, writeset []byte, exceptset []byte, timeout []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_select(maxfdp1 int32, readset []byte, writeset []byte, exceptset []byte, timeout []byte) int32 { if r.debug { fmt.Printf("rpc_lwip_select()\r\n") } @@ -8785,33 +6265,20 @@ func (r *RTL8720DN) Rpc_lwip_select(maxfdp1 int32, readset []byte, writeset []by msg = append(msg, []byte(timeout)...) } - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_ioctl(s int32, cmd uint32, in_argp []byte, out_argp *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_ioctl(s int32, cmd uint32, in_argp []byte, out_argp []byte) int32 { if r.debug { fmt.Printf("rpc_lwip_ioctl()\r\n") } @@ -8831,10 +6298,7 @@ func (r *RTL8720DN) Rpc_lwip_ioctl(s int32, cmd uint32, in_argp []byte, out_argp msg = append(msg, byte(len(in_argp)), byte(len(in_argp)>>8), byte(len(in_argp)>>16), byte(len(in_argp)>>24)) msg = append(msg, []byte(in_argp)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -8842,30 +6306,19 @@ func (r *RTL8720DN) Rpc_lwip_ioctl(s int32, cmd uint32, in_argp []byte, out_argp out_argp_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if out_argp_length > 0 { - copy(*out_argp, payload[widx:widx+int(out_argp_length)]) + copy(out_argp, payload[widx:widx+int(out_argp_length)]) widx += int(out_argp_length) } - *out_argp = (*out_argp)[:out_argp_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_fcntl(s int32, cmd int32, val int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_fcntl(s int32, cmd int32, val int32) int32 { if r.debug { fmt.Printf("rpc_lwip_fcntl()\r\n") } @@ -8887,64 +6340,38 @@ func (r *RTL8720DN) Rpc_lwip_fcntl(s int32, cmd int32, val int32) (int32, error) msg = append(msg, byte(val>>16)) msg = append(msg, byte(val>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_errno() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_errno() int32 { if r.debug { fmt.Printf("rpc_lwip_errno()\r\n") } msg := startWriteMessage(0x00, 0x10, 0x18, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_netconn_gethostbyname(name string, addr *[]byte) (int8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_netconn_gethostbyname(name string, addr []byte) int8 { if r.debug { fmt.Printf("rpc_netconn_gethostbyname()\r\n") } @@ -8954,10 +6381,7 @@ func (r *RTL8720DN) Rpc_netconn_gethostbyname(name string, addr *[]byte) (int8, msg = append(msg, byte(len(name)), byte(len(name)>>8), byte(len(name)>>16), byte(len(name)>>24)) msg = append(msg, []byte(name)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -8965,30 +6389,19 @@ func (r *RTL8720DN) Rpc_netconn_gethostbyname(name string, addr *[]byte) (int8, addr_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if addr_length > 0 { - copy(*addr, payload[widx:widx+int(addr_length)]) + copy(addr, payload[widx:widx+int(addr_length)]) widx += int(addr_length) } - *addr = (*addr)[:addr_length] var result int8 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80 { - result = int8(int(x) * -1) - } else { - result = int8(int(x)) - } result = int8(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_dns_gethostbyname_addrtype(hostname string, addr *[]byte, found uint32, callback_arg []byte, dns_addrtype uint8) (int8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_dns_gethostbyname_addrtype(hostname string, addr []byte, found uint32, callback_arg []byte, dns_addrtype uint8) int8 { if r.debug { fmt.Printf("rpc_dns_gethostbyname_addrtype()\r\n") } @@ -9013,10 +6426,7 @@ func (r *RTL8720DN) Rpc_dns_gethostbyname_addrtype(hostname string, addr *[]byte // dns_addrtype : in uint8 msg = append(msg, byte(dns_addrtype>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9024,39 +6434,25 @@ func (r *RTL8720DN) Rpc_dns_gethostbyname_addrtype(hostname string, addr *[]byte addr_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if addr_length > 0 { - copy(*addr, payload[widx:widx+int(addr_length)]) + copy(addr, payload[widx:widx+int(addr_length)]) widx += int(addr_length) } - *addr = (*addr)[:addr_length] var result int8 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80 { - result = int8(int(x) * -1) - } else { - result = int8(int(x)) - } result = int8(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_client_create() (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_client_create() uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_client_create()\r\n") } msg := startWriteMessage(0x00, 0x11, 0x01, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9064,15 +6460,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_client_create() (uint32, error) { result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_client_destroy(ssl_client uint32) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_client_destroy(ssl_client uint32) { if r.debug { fmt.Printf("rpc_wifi_ssl_client_destroy()\r\n") } @@ -9084,23 +6475,15 @@ func (r *RTL8720DN) Rpc_wifi_ssl_client_destroy(ssl_client uint32) error { msg = append(msg, byte(ssl_client>>16)) msg = append(msg, byte(ssl_client>>24)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_wifi_ssl_init(ssl_client uint32) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_init(ssl_client uint32) { if r.debug { fmt.Printf("rpc_wifi_ssl_init()\r\n") } @@ -9112,23 +6495,15 @@ func (r *RTL8720DN) Rpc_wifi_ssl_init(ssl_client uint32) error { msg = append(msg, byte(ssl_client>>16)) msg = append(msg, byte(ssl_client>>24)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_wifi_ssl_set_socket(ssl_client uint32, socket int32) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_set_socket(ssl_client uint32, socket int32) { if r.debug { fmt.Printf("rpc_wifi_ssl_set_socket()\r\n") } @@ -9145,23 +6520,15 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_socket(ssl_client uint32, socket int32) err msg = append(msg, byte(socket>>16)) msg = append(msg, byte(socket>>24)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_wifi_ssl_set_timeout(ssl_client uint32, timeout uint32) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_set_timeout(ssl_client uint32, timeout uint32) { if r.debug { fmt.Printf("rpc_wifi_ssl_set_timeout()\r\n") } @@ -9178,23 +6545,15 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_timeout(ssl_client uint32, timeout uint32) msg = append(msg, byte(timeout>>16)) msg = append(msg, byte(timeout>>24)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_wifi_ssl_get_socket(ssl_client uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_get_socket(ssl_client uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_ssl_get_socket()\r\n") } @@ -9206,33 +6565,20 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_socket(ssl_client uint32) (int32, error) { msg = append(msg, byte(ssl_client>>16)) msg = append(msg, byte(ssl_client>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_get_timeout(ssl_client uint32) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_get_timeout(ssl_client uint32) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_get_timeout()\r\n") } @@ -9244,10 +6590,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_timeout(ssl_client uint32) (uint32, error) msg = append(msg, byte(ssl_client>>16)) msg = append(msg, byte(ssl_client>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9256,15 +6599,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_timeout(ssl_client uint32) (uint32, error) result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_set_rootCA(ssl_client uint32, rootCABuff string) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_set_rootCA(ssl_client uint32, rootCABuff string) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_set_rootCA()\r\n") } @@ -9279,10 +6617,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_rootCA(ssl_client uint32, rootCABuff string msg = append(msg, byte(len(rootCABuff)), byte(len(rootCABuff)>>8), byte(len(rootCABuff)>>16), byte(len(rootCABuff)>>24)) msg = append(msg, []byte(rootCABuff)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9291,15 +6626,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_rootCA(ssl_client uint32, rootCABuff string result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_get_rootCA(ssl_client uint32, rootCABuff string) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_get_rootCA(ssl_client uint32, rootCABuff string) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_get_rootCA()\r\n") } @@ -9311,10 +6641,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_rootCA(ssl_client uint32, rootCABuff string msg = append(msg, byte(ssl_client>>16)) msg = append(msg, byte(ssl_client>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9329,21 +6656,15 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_rootCA(ssl_client uint32, rootCABuff string rootCABuff = string(payload[widx : widx+int(rootCABuff_length)]) widx += int(rootCABuff_length) } - rootCABuff = (rootCABuff)[:rootCABuff_length] var result uint32 result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_set_cliCert(ssl_client uint32, cli_cert string) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_set_cliCert(ssl_client uint32, cli_cert string) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_set_cliCert()\r\n") } @@ -9358,10 +6679,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_cliCert(ssl_client uint32, cli_cert string) msg = append(msg, byte(len(cli_cert)), byte(len(cli_cert)>>8), byte(len(cli_cert)>>16), byte(len(cli_cert)>>24)) msg = append(msg, []byte(cli_cert)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9370,15 +6688,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_cliCert(ssl_client uint32, cli_cert string) result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_get_cliCert(ssl_client uint32, cli_cert string) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_get_cliCert(ssl_client uint32, cli_cert string) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_get_cliCert()\r\n") } @@ -9398,10 +6711,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_cliCert(ssl_client uint32, cli_cert string) msg = append(msg, []byte(cli_cert)...) } - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9410,15 +6720,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_cliCert(ssl_client uint32, cli_cert string) result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_set_cliKey(ssl_client uint32, cli_key string) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_set_cliKey(ssl_client uint32, cli_key string) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_set_cliKey()\r\n") } @@ -9433,10 +6738,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_cliKey(ssl_client uint32, cli_key string) ( msg = append(msg, byte(len(cli_key)), byte(len(cli_key)>>8), byte(len(cli_key)>>16), byte(len(cli_key)>>24)) msg = append(msg, []byte(cli_key)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9445,15 +6747,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_cliKey(ssl_client uint32, cli_key string) ( result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_get_cliKey(ssl_client uint32, cli_key string) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_get_cliKey(ssl_client uint32, cli_key string) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_get_cliKey()\r\n") } @@ -9473,10 +6770,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_cliKey(ssl_client uint32, cli_key string) ( msg = append(msg, []byte(cli_key)...) } - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9485,15 +6779,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_cliKey(ssl_client uint32, cli_key string) ( result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_set_pskIdent(ssl_client uint32, pskIdent string) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_set_pskIdent(ssl_client uint32, pskIdent string) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_set_pskIdent()\r\n") } @@ -9508,10 +6797,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_pskIdent(ssl_client uint32, pskIdent string msg = append(msg, byte(len(pskIdent)), byte(len(pskIdent)>>8), byte(len(pskIdent)>>16), byte(len(pskIdent)>>24)) msg = append(msg, []byte(pskIdent)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9520,15 +6806,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_pskIdent(ssl_client uint32, pskIdent string result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_get_pskIdent(ssl_client uint32, pskIdent string) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_get_pskIdent(ssl_client uint32, pskIdent string) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_get_pskIdent()\r\n") } @@ -9548,10 +6829,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_pskIdent(ssl_client uint32, pskIdent string msg = append(msg, []byte(pskIdent)...) } - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9560,15 +6838,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_pskIdent(ssl_client uint32, pskIdent string result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_set_psKey(ssl_client uint32, psKey string) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_set_psKey(ssl_client uint32, psKey string) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_set_psKey()\r\n") } @@ -9583,10 +6856,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_psKey(ssl_client uint32, psKey string) (uin msg = append(msg, byte(len(psKey)), byte(len(psKey)>>8), byte(len(psKey)>>16), byte(len(psKey)>>24)) msg = append(msg, []byte(psKey)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9595,15 +6865,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_psKey(ssl_client uint32, psKey string) (uin result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_get_psKey(ssl_client uint32, psKey string) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_get_psKey(ssl_client uint32, psKey string) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_get_psKey()\r\n") } @@ -9623,10 +6888,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_psKey(ssl_client uint32, psKey string) (uin msg = append(msg, []byte(psKey)...) } - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9635,15 +6897,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_psKey(ssl_client uint32, psKey string) (uin result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_start_ssl_client(ssl_client uint32, host string, port uint32, timeout int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_start_ssl_client(ssl_client uint32, host string, port uint32, timeout int32) int32 { if r.debug { fmt.Printf("rpc_wifi_start_ssl_client()\r\n") } @@ -9673,33 +6930,20 @@ func (r *RTL8720DN) Rpc_wifi_start_ssl_client(ssl_client uint32, host string, po msg = append(msg, byte(timeout>>16)) msg = append(msg, byte(timeout>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_stop_ssl_socket(ssl_client uint32) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_stop_ssl_socket(ssl_client uint32) { if r.debug { fmt.Printf("rpc_wifi_stop_ssl_socket()\r\n") } @@ -9711,23 +6955,15 @@ func (r *RTL8720DN) Rpc_wifi_stop_ssl_socket(ssl_client uint32) error { msg = append(msg, byte(ssl_client>>16)) msg = append(msg, byte(ssl_client>>24)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_wifi_data_to_read(ssl_client uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_data_to_read(ssl_client uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_data_to_read()\r\n") } @@ -9739,33 +6975,20 @@ func (r *RTL8720DN) Rpc_wifi_data_to_read(ssl_client uint32) (int32, error) { msg = append(msg, byte(ssl_client>>16)) msg = append(msg, byte(ssl_client>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_send_ssl_data(ssl_client uint32, data []byte, length uint16) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_send_ssl_data(ssl_client uint32, data []byte, length uint16) int32 { if r.debug { fmt.Printf("rpc_wifi_send_ssl_data()\r\n") } @@ -9783,33 +7006,20 @@ func (r *RTL8720DN) Rpc_wifi_send_ssl_data(ssl_client uint32, data []byte, lengt msg = append(msg, byte(length>>0)) msg = append(msg, byte(length>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_ssl_receive(ssl_client uint32, data *[]byte, length int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_ssl_receive(ssl_client uint32, data []byte, length int32) int32 { if r.debug { fmt.Printf("rpc_wifi_get_ssl_receive()\r\n") } @@ -9826,10 +7036,7 @@ func (r *RTL8720DN) Rpc_wifi_get_ssl_receive(ssl_client uint32, data *[]byte, le msg = append(msg, byte(length>>16)) msg = append(msg, byte(length>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9837,30 +7044,19 @@ func (r *RTL8720DN) Rpc_wifi_get_ssl_receive(ssl_client uint32, data *[]byte, le data_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if data_length > 0 { - copy(*data, payload[widx:widx+int(data_length)]) + copy(data, payload[widx:widx+int(data_length)]) widx += int(data_length) } - *data = (*data)[:data_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_verify_ssl_fingerprint(ssl_client uint32, fp string, domain_name string) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_verify_ssl_fingerprint(ssl_client uint32, fp string, domain_name string) bool { if r.debug { fmt.Printf("rpc_wifi_verify_ssl_fingerprint()\r\n") } @@ -9878,10 +7074,7 @@ func (r *RTL8720DN) Rpc_wifi_verify_ssl_fingerprint(ssl_client uint32, fp string msg = append(msg, byte(len(domain_name)), byte(len(domain_name)>>8), byte(len(domain_name)>>16), byte(len(domain_name)>>24)) msg = append(msg, []byte(domain_name)...) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9890,15 +7083,10 @@ func (r *RTL8720DN) Rpc_wifi_verify_ssl_fingerprint(ssl_client uint32, fp string result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_verify_ssl_dn(ssl_client uint32, domain_name string) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_verify_ssl_dn(ssl_client uint32, domain_name string) bool { if r.debug { fmt.Printf("rpc_wifi_verify_ssl_dn()\r\n") } @@ -9913,10 +7101,7 @@ func (r *RTL8720DN) Rpc_wifi_verify_ssl_dn(ssl_client uint32, domain_name string msg = append(msg, byte(len(domain_name)), byte(len(domain_name)>>8), byte(len(domain_name)>>16), byte(len(domain_name)>>24)) msg = append(msg, []byte(domain_name)...) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9925,15 +7110,10 @@ func (r *RTL8720DN) Rpc_wifi_verify_ssl_dn(ssl_client uint32, domain_name string result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_strerror(errnum int32, buffer *[]byte, buflen uint32) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_strerror(errnum int32, buffer []byte, buflen uint32) { if r.debug { fmt.Printf("rpc_wifi_ssl_strerror()\r\n") } @@ -9950,10 +7130,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_strerror(errnum int32, buffer *[]byte, buflen u msg = append(msg, byte(buflen>>16)) msg = append(msg, byte(buflen>>24)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() widx := 8 @@ -9961,83 +7138,51 @@ func (r *RTL8720DN) Rpc_wifi_ssl_strerror(errnum int32, buffer *[]byte, buflen u buffer_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if buffer_length > 0 { - copy(*buffer, payload[widx:widx+int(buffer_length)]) + copy(buffer, payload[widx:widx+int(buffer_length)]) widx += int(buffer_length) } - *buffer = (*buffer)[:buffer_length] r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_mdns_init() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_init() int32 { if r.debug { fmt.Printf("rpc_mdns_init()\r\n") } msg := startWriteMessage(0x00, 0x12, 0x01, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_free() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_free() int32 { if r.debug { fmt.Printf("rpc_mdns_free()\r\n") } msg := startWriteMessage(0x00, 0x12, 0x02, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_service_add(instance_name string, service_type string, proto string, port uint16) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_service_add(instance_name string, service_type string, proto string, port uint16) int32 { if r.debug { fmt.Printf("rpc_mdns_service_add()\r\n") } @@ -10056,33 +7201,20 @@ func (r *RTL8720DN) Rpc_mdns_service_add(instance_name string, service_type stri msg = append(msg, byte(port>>0)) msg = append(msg, byte(port>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_service_remove(service_type string, proto string) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_service_remove(service_type string, proto string) int32 { if r.debug { fmt.Printf("rpc_mdns_service_remove()\r\n") } @@ -10095,33 +7227,20 @@ func (r *RTL8720DN) Rpc_mdns_service_remove(service_type string, proto string) ( msg = append(msg, byte(len(proto)), byte(len(proto)>>8), byte(len(proto)>>16), byte(len(proto)>>24)) msg = append(msg, []byte(proto)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_service_txt_item_set(service_type string, proto string, key string, value string) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_service_txt_item_set(service_type string, proto string, key string, value string) int32 { if r.debug { fmt.Printf("rpc_mdns_service_txt_item_set()\r\n") } @@ -10140,33 +7259,20 @@ func (r *RTL8720DN) Rpc_mdns_service_txt_item_set(service_type string, proto str msg = append(msg, byte(len(value)), byte(len(value)>>8), byte(len(value)>>16), byte(len(value)>>24)) msg = append(msg, []byte(value)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_service_instance_name_set(service string, proto string, instance string) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_service_instance_name_set(service string, proto string, instance string) int32 { if r.debug { fmt.Printf("rpc_mdns_service_instance_name_set()\r\n") } @@ -10182,33 +7288,20 @@ func (r *RTL8720DN) Rpc_mdns_service_instance_name_set(service string, proto str msg = append(msg, byte(len(instance)), byte(len(instance)>>8), byte(len(instance)>>16), byte(len(instance)>>24)) msg = append(msg, []byte(instance)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_instance_name_set(instance_name string) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_instance_name_set(instance_name string) int32 { if r.debug { fmt.Printf("rpc_mdns_instance_name_set()\r\n") } @@ -10218,33 +7311,20 @@ func (r *RTL8720DN) Rpc_mdns_instance_name_set(instance_name string) (int32, err msg = append(msg, byte(len(instance_name)), byte(len(instance_name)>>8), byte(len(instance_name)>>16), byte(len(instance_name)>>24)) msg = append(msg, []byte(instance_name)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_hostname_set(hostname string) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_hostname_set(hostname string) int32 { if r.debug { fmt.Printf("rpc_mdns_hostname_set()\r\n") } @@ -10254,33 +7334,20 @@ func (r *RTL8720DN) Rpc_mdns_hostname_set(hostname string) (int32, error) { msg = append(msg, byte(len(hostname)), byte(len(hostname)>>8), byte(len(hostname)>>16), byte(len(hostname)>>24)) msg = append(msg, []byte(hostname)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_query_a(host_name string, timeout uint32, addr *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_query_a(host_name string, timeout uint32, addr []byte) int32 { if r.debug { fmt.Printf("rpc_mdns_query_a()\r\n") } @@ -10295,10 +7362,7 @@ func (r *RTL8720DN) Rpc_mdns_query_a(host_name string, timeout uint32, addr *[]b msg = append(msg, byte(timeout>>16)) msg = append(msg, byte(timeout>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -10306,30 +7370,19 @@ func (r *RTL8720DN) Rpc_mdns_query_a(host_name string, timeout uint32, addr *[]b addr_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if addr_length > 0 { - copy(*addr, payload[widx:widx+int(addr_length)]) + copy(addr, payload[widx:widx+int(addr_length)]) widx += int(addr_length) } - *addr = (*addr)[:addr_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_query_ptr(service_type string, proto string, timeout uint32, max_results int32, result_total *int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_query_ptr(service_type string, proto string, timeout uint32, max_results int32, result_total *int32) int32 { if r.debug { fmt.Printf("rpc_mdns_query_ptr()\r\n") } @@ -10352,10 +7405,7 @@ func (r *RTL8720DN) Rpc_mdns_query_ptr(service_type string, proto string, timeou msg = append(msg, byte(max_results>>16)) msg = append(msg, byte(max_results>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -10365,23 +7415,13 @@ func (r *RTL8720DN) Rpc_mdns_query_ptr(service_type string, proto string, timeou var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_query_ptr_result_basic(result_target int32, scan_result *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_query_ptr_result_basic(result_target int32, scan_result []byte) int32 { if r.debug { fmt.Printf("rpc_mdns_query_ptr_result_basic()\r\n") } @@ -10393,10 +7433,7 @@ func (r *RTL8720DN) Rpc_mdns_query_ptr_result_basic(result_target int32, scan_re msg = append(msg, byte(result_target>>16)) msg = append(msg, byte(result_target>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -10404,30 +7441,19 @@ func (r *RTL8720DN) Rpc_mdns_query_ptr_result_basic(result_target int32, scan_re scan_result_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if scan_result_length > 0 { - copy(*scan_result, payload[widx:widx+int(scan_result_length)]) + copy(scan_result, payload[widx:widx+int(scan_result_length)]) widx += int(scan_result_length) } - *scan_result = (*scan_result)[:scan_result_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_query_ptr_result_txt(result_target int32, txt_target int32, txt *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_query_ptr_result_txt(result_target int32, txt_target int32, txt []byte) int32 { if r.debug { fmt.Printf("rpc_mdns_query_ptr_result_txt()\r\n") } @@ -10444,10 +7470,7 @@ func (r *RTL8720DN) Rpc_mdns_query_ptr_result_txt(result_target int32, txt_targe msg = append(msg, byte(txt_target>>16)) msg = append(msg, byte(txt_target>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -10455,30 +7478,19 @@ func (r *RTL8720DN) Rpc_mdns_query_ptr_result_txt(result_target int32, txt_targe txt_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if txt_length > 0 { - copy(*txt, payload[widx:widx+int(txt_length)]) + copy(txt, payload[widx:widx+int(txt_length)]) widx += int(txt_length) } - *txt = (*txt)[:txt_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_query_ptr_result_addr(result_target int32, addr_target int32, addr *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_query_ptr_result_addr(result_target int32, addr_target int32, addr []byte) int32 { if r.debug { fmt.Printf("rpc_mdns_query_ptr_result_addr()\r\n") } @@ -10495,10 +7507,7 @@ func (r *RTL8720DN) Rpc_mdns_query_ptr_result_addr(result_target int32, addr_tar msg = append(msg, byte(addr_target>>16)) msg = append(msg, byte(addr_target>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -10506,61 +7515,37 @@ func (r *RTL8720DN) Rpc_mdns_query_ptr_result_addr(result_target int32, addr_tar addr_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if addr_length > 0 { - copy(*addr, payload[widx:widx+int(addr_length)]) + copy(addr, payload[widx:widx+int(addr_length)]) widx += int(addr_length) } - *addr = (*addr)[:addr_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_query_results_free() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_query_results_free() int32 { if r.debug { fmt.Printf("rpc_mdns_query_results_free()\r\n") } msg := startWriteMessage(0x00, 0x12, 0x0E, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_event_callback(event []byte) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_event_callback(event []byte) { if r.debug { fmt.Printf("rpc_wifi_event_callback()\r\n") } @@ -10570,23 +7555,15 @@ func (r *RTL8720DN) Rpc_wifi_event_callback(event []byte) error { msg = append(msg, byte(len(event)), byte(len(event)>>8), byte(len(event)>>16), byte(len(event)>>24)) msg = append(msg, []byte(event)...) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_wifi_dns_found(hostname string, ipaddr []byte, arg []byte) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_dns_found(hostname string, ipaddr []byte, arg []byte) { if r.debug { fmt.Printf("rpc_wifi_dns_found()\r\n") } @@ -10607,23 +7584,15 @@ func (r *RTL8720DN) Rpc_wifi_dns_found(hostname string, ipaddr []byte, arg []byt msg = append(msg, []byte(arg)...) } - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_tcpip_api_call_fn(fn uint32, call []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_api_call_fn(fn uint32, call []byte) int32 { if r.debug { fmt.Printf("rpc_tcpip_api_call_fn()\r\n") } @@ -10638,33 +7607,20 @@ func (r *RTL8720DN) Rpc_tcpip_api_call_fn(fn uint32, call []byte) (int32, error) msg = append(msg, byte(len(call)), byte(len(call)>>8), byte(len(call)>>16), byte(len(call)>>24)) msg = append(msg, []byte(call)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_connected_fn(fn uint32, arg []byte, tpcb []byte, err_val int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_connected_fn(fn uint32, arg []byte, tpcb []byte, err_val int32) int32 { if r.debug { fmt.Printf("rpc_tcp_connected_fn()\r\n") } @@ -10687,33 +7643,20 @@ func (r *RTL8720DN) Rpc_tcp_connected_fn(fn uint32, arg []byte, tpcb []byte, err msg = append(msg, byte(err_val>>16)) msg = append(msg, byte(err_val>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_recv_fn(fn uint32, arg []byte, tpcb []byte, p_data []byte, p_addr []byte, err_val int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_recv_fn(fn uint32, arg []byte, tpcb []byte, p_data []byte, p_addr []byte, err_val int32) int32 { if r.debug { fmt.Printf("rpc_tcp_recv_fn()\r\n") } @@ -10742,33 +7685,20 @@ func (r *RTL8720DN) Rpc_tcp_recv_fn(fn uint32, arg []byte, tpcb []byte, p_data [ msg = append(msg, byte(err_val>>16)) msg = append(msg, byte(err_val>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_accept_fn(fn uint32, arg []byte, newpcb []byte, err_val int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_accept_fn(fn uint32, arg []byte, newpcb []byte, err_val int32) int32 { if r.debug { fmt.Printf("rpc_tcp_accept_fn()\r\n") } @@ -10791,33 +7721,20 @@ func (r *RTL8720DN) Rpc_tcp_accept_fn(fn uint32, arg []byte, newpcb []byte, err_ msg = append(msg, byte(err_val>>16)) msg = append(msg, byte(err_val>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_err_fn(fn uint32, arg []byte, err_val int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_err_fn(fn uint32, arg []byte, err_val int32) int32 { if r.debug { fmt.Printf("rpc_tcp_err_fn()\r\n") } @@ -10837,33 +7754,20 @@ func (r *RTL8720DN) Rpc_tcp_err_fn(fn uint32, arg []byte, err_val int32) (int32, msg = append(msg, byte(err_val>>16)) msg = append(msg, byte(err_val>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_sent_fn(fn uint32, arg []byte, tpcb []byte, length uint16) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_sent_fn(fn uint32, arg []byte, tpcb []byte, length uint16) int32 { if r.debug { fmt.Printf("rpc_tcp_sent_fn()\r\n") } @@ -10884,33 +7788,20 @@ func (r *RTL8720DN) Rpc_tcp_sent_fn(fn uint32, arg []byte, tpcb []byte, length u msg = append(msg, byte(length>>0)) msg = append(msg, byte(length>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_poll_fn(fn uint32, arg []byte, tpcb []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_poll_fn(fn uint32, arg []byte, tpcb []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_poll_fn()\r\n") } @@ -10928,23 +7819,15 @@ func (r *RTL8720DN) Rpc_tcp_poll_fn(fn uint32, arg []byte, tpcb []byte) (int32, msg = append(msg, byte(len(tpcb)), byte(len(tpcb)>>8), byte(len(tpcb)>>16), byte(len(tpcb)>>24)) msg = append(msg, []byte(tpcb)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } diff --git a/rtl8720dn/rpc_util.go b/rtl8720dn/rpc_util.go index ee894224a..68efebbe0 100644 --- a/rtl8720dn/rpc_util.go +++ b/rtl8720dn/rpc_util.go @@ -9,7 +9,7 @@ var ( headerBuf [4]byte readBuf [4]byte startWriteMessageBuf [1024]byte - payload [1024 + 256]byte + payload [2048]byte ) const ( @@ -30,7 +30,7 @@ func startWriteMessage(msgType, service, requestNumber, sequence uint32) []byte return startWriteMessageBuf[:8] } -func (r *RTL8720DN) performRequest(msg []byte) error { +func (r *rtl8720dn) performRequest(msg []byte) { crc := computeCRC16(msg) headerBuf[0] = byte(len(msg)) headerBuf[1] = byte(len(msg) >> 8) @@ -43,16 +43,14 @@ func (r *RTL8720DN) performRequest(msg []byte) error { fmt.Printf("\r\n") } - r.port.Write(headerBuf[:]) + r.uart.Write(headerBuf[:]) if r.debug { fmt.Printf("tx : %2d : ", len(msg)) dumpHex(msg) fmt.Printf("\r\n") } - r.port.Write(msg) - - return nil + r.uart.Write(msg) } func dumpHex(b []byte) { @@ -65,9 +63,9 @@ func dumpHex(b []byte) { } } -func (r *RTL8720DN) read() { +func (r *rtl8720dn) read() { for { - n, _ := io.ReadFull(r.port, readBuf[:4]) + n, _ := io.ReadFull(r.uart, readBuf[:4]) if n == 0 { continue } @@ -81,7 +79,7 @@ func (r *RTL8720DN) read() { length := uint16(readBuf[0]) + uint16(readBuf[1])<<8 crc := uint16(readBuf[2]) + uint16(readBuf[3])<<8 - n, _ = io.ReadFull(r.port, payload[:length]) + n, _ = io.ReadFull(r.uart, payload[:length]) if r.debug { fmt.Printf("rx : %2d : ", length) dumpHex(payload[0:n]) diff --git a/rtl8720dn/rtl8720dn.go b/rtl8720dn/rtl8720dn.go index 8a21bde97..35bfff496 100644 --- a/rtl8720dn/rtl8720dn.go +++ b/rtl8720dn/rtl8720dn.go @@ -1,69 +1,783 @@ -package rtl8720dn +// Package rtl8720dn implements TCP wireless communication over UART +// talking to a RealTek rtl8720dn module. +// +// 01/2023 sfeldma@gmail.com Heavily modified to use netdev interface -import ( - "machine" +package rtl8720dn // import "tinygo.org/x/drivers/rtl8720dn" +import ( + "encoding/hex" + "errors" + "fmt" "io" + "machine" + "strings" + "sync" "time" + + "tinygo.org/x/drivers/netdev" ) -const maxUartRecvSize = 128 +var _debug debug = debugBasic + +//var _debug debug = debugBasic | debugNetdev +//var _debug debug = debugBasic | debugNetdev | debugRpc + +var ( + version = "0.0.1" + driverName = "Realtek rtl8720dn Wifi network device driver (rtl8720dn)" +) + +const ( + F_SETFL = 4 + O_NONBLOCK = 1 + RTW_MODE_STA = 0x00000001 +) + +type macAddress netdev.HardwareAddr +type sock int32 + +type socket struct { + protocol netdev.Protocol + inuse bool +} + +type Config struct { + // AP creditials + Ssid string + Passphrase string + + // Enable + En machine.Pin + + // UART config + Uart *machine.UART + Tx machine.Pin + Rx machine.Pin + Baudrate uint32 + + // Retries is how many attempts to connect before returning with a + // "Connect failed" error. Zero means infinite retries. + Retries int + + // Watchdog ticker duration. On tick, the watchdog will check for + // downed connection and try to recover the connection. Default is + // 0secs, which means no watchdog. Set to non-zero to enable + // watchodog. + WatchdogTimeo time.Duration +} + +type rtl8720dn struct { + cfg *Config + notifyCb func(netdev.Event) + mu sync.Mutex + + uart *machine.UART + seq uint64 -type RTL8720DN struct { - port io.ReadWriter - seq uint64 - sema chan bool debug bool - connectionType ConnectionType - socket int32 - client uint32 - length int - root_ca *string - udpInfo [6]byte // Port: [2]byte + IP: [4]byte + netConnected bool + driverShown bool + deviceShown bool + + killWatchdog chan bool + + // keyed by sock as returned by rpc_lwip_socket() + sockets map[sock]*socket } -type ConnectionType int +func newSocket(protocol netdev.Protocol) *socket { + return &socket{protocol: protocol, inuse: true} +} -const ( - ConnectionTypeNone ConnectionType = iota - ConnectionTypeTCP - ConnectionTypeUDP - ConnectionTypeTLS -) +func New(cfg *Config) *rtl8720dn { + return &rtl8720dn{ + debug: (_debug & debugRpc) != 0, + cfg: cfg, + sockets: make(map[sock]*socket), + killWatchdog: make(chan bool), + } +} + +func (r *rtl8720dn) startDhcpc() error { + if result := r.rpc_tcpip_adapter_dhcpc_start(0); result == -1 { + return netdev.ErrStartingDHCPClient + } + return nil +} + +func (r *rtl8720dn) connectToAP() error { + + if len(r.cfg.Ssid) == 0 { + return netdev.ErrMissingSSID + } + + if debugging(debugBasic) { + fmt.Printf("Connecting to Wifi SSID '%s'...", r.cfg.Ssid) + } -func (d *Driver) SetSeq(s uint64) { - d.seq = s + // Start the connection process + securityType := uint32(0x00400004) + result := r.rpc_wifi_connect(r.cfg.Ssid, r.cfg.Passphrase, securityType, -1, 0) + if result == -1 { + if debugging(debugBasic) { + fmt.Printf("FAILED\r\n") + } + return netdev.ErrConnectFailed + } + + if debugging(debugBasic) { + fmt.Printf("CONNECTED\r\n") + } + + if r.notifyCb != nil { + r.notifyCb(netdev.EventNetUp) + } + + return r.startDhcpc() } -func (d *Driver) Debug(b bool) { - d.debug = b +func (r *rtl8720dn) showDriver() { + if r.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) + } + r.driverShown = true } -func (d *Driver) SetRootCA(s *string) { - d.root_ca = s +func (r *rtl8720dn) initWifi() error { + if result := r.rpc_tcpip_adapter_init(); result == -1 { + return fmt.Errorf("TCP/IP adapter init failed") + } + if result := r.rpc_wifi_off(); result == -1 { + return errors.New("Error turning off WiFi") + } + if result := r.rpc_wifi_on(RTW_MODE_STA); result == -1 { + return errors.New("Error turning on WiFi") + } + if result := r.rpc_wifi_disconnect(); result == -1 { + return errors.New("Error disconnecting WiFi") + } + return nil } -func (d *Driver) Version() (string, error) { - return d.Rpc_system_version() +func (r *rtl8720dn) setupUART() { + r.uart = r.cfg.Uart + r.uart.Configure(machine.UARTConfig{TX: r.cfg.Tx, + RX: r.cfg.Rx, BaudRate: r.cfg.Baudrate}) } -func enable(en machine.Pin) { +func (r *rtl8720dn) start() error { + en := r.cfg.En + if en == 0 { + return fmt.Errorf("Must set Config.En") + } en.Configure(machine.PinConfig{Mode: machine.PinOutput}) en.Low() time.Sleep(100 * time.Millisecond) en.High() time.Sleep(1000 * time.Millisecond) + r.setupUART() + return r.initWifi() +} + +func (r *rtl8720dn) stop() { + r.rpc_tcpip_adapter_stop(0) + r.cfg.En.Low() +} + +func (r *rtl8720dn) showDevice() { + if r.deviceShown { + return + } + if debugging(debugBasic) { + fmt.Printf("RTL8720 firmware version : %s\r\n", r.getFwVersion()) + fmt.Printf("MAC address : %s\r\n", r.getMACAddr()) + fmt.Printf("\r\n") + } + r.deviceShown = true +} + +func (r *rtl8720dn) showIP() { + if debugging(debugBasic) { + ip, subnet, gateway, _ := r.getIP() + fmt.Printf("\r\n") + fmt.Printf("DHCP-assigned IP : %s\r\n", netdev.IP(ip).String()) + fmt.Printf("DHCP-assigned subnet : %s\r\n", netdev.IP(subnet).String()) + fmt.Printf("DHCP-assigned gateway : %s\r\n", netdev.IP(gateway).String()) + fmt.Printf("\r\n") + } +} + +func (r *rtl8720dn) networkDown() bool { + result := r.rpc_wifi_is_connected_to_ap() + return result != 0 +} + +func (r *rtl8720dn) watchdog() { + ticker := time.NewTicker(r.cfg.WatchdogTimeo) + for { + select { + case <-r.killWatchdog: + return + case <-ticker.C: + r.mu.Lock() + if r.networkDown() { + if debugging(debugBasic) { + fmt.Printf("Watchdog: Wifi NOT CONNECTED, trying again...\r\n") + } + if r.notifyCb != nil { + r.notifyCb(netdev.EventNetDown) + } + r.netConnect(false) + } + r.mu.Unlock() + } + } +} + +func (r *rtl8720dn) netConnect(reset bool) error { + if reset { + if err := r.start(); err != nil { + return err + } + } + r.showDevice() + + for i := 0; r.cfg.Retries == 0 || i < r.cfg.Retries; i++ { + if err := r.connectToAP(); err != nil { + if err == netdev.ErrConnectFailed { + continue + } + return err + } + break + } + + if r.networkDown() { + return netdev.ErrConnectFailed + } + + r.showIP() + return nil +} + +func (r *rtl8720dn) NetConnect() error { + + r.mu.Lock() + defer r.mu.Unlock() + + if r.netConnected { + return netdev.ErrConnected + } + + r.showDriver() + + if err := r.netConnect(true); err != nil { + return err + } + + r.netConnected = true + + if r.cfg.WatchdogTimeo != 0 { + go r.watchdog() + } + + return nil +} + +func (r *rtl8720dn) netDisconnect() { + r.disconnect() +} + +func (r *rtl8720dn) NetDisconnect() { + + r.mu.Lock() + defer r.mu.Unlock() + + if !r.netConnected { + return + } + + if r.cfg.WatchdogTimeo != 0 { + r.killWatchdog <- true + } + r.netDisconnect() + r.stop() + + r.netConnected = false + + if debugging(debugBasic) { + fmt.Printf("\r\nDisconnected from Wifi SSID '%s'\r\n\r\n", r.cfg.Ssid) + } + + if r.notifyCb != nil { + r.notifyCb(netdev.EventNetDown) + } +} + +func (r *rtl8720dn) NetNotify(cb func(netdev.Event)) { + r.notifyCb = cb +} + +func (r *rtl8720dn) GetHostByName(name string) (netdev.IP, error) { + + if debugging(debugNetdev) { + fmt.Printf("[GetHostByName] name: %s\r\n", name) + } + + r.mu.Lock() + defer r.mu.Unlock() + + var addr [4]byte + result := r.rpc_netconn_gethostbyname(name, addr[:]) + if result == -1 { + return netdev.IP{}, fmt.Errorf("Get IP of host '%s' failed", name) + } + + var ip netdev.IP + copy(ip[:], addr[:]) + + return ip, nil +} + +func (r *rtl8720dn) GetHardwareAddr() (netdev.HardwareAddr, error) { + + if debugging(debugNetdev) { + fmt.Printf("[GetHardwareAddr]\r\n") + } + + r.mu.Lock() + defer r.mu.Unlock() + + mac := strings.ReplaceAll(r.getMACAddr(), ":", "") + addr, err := hex.DecodeString(mac) + + return netdev.HardwareAddr(addr), err +} + +func (r *rtl8720dn) GetIPAddr() (netdev.IP, error) { + + if debugging(debugNetdev) { + fmt.Printf("[GetIPAddr]\r\n") + } + + r.mu.Lock() + defer r.mu.Unlock() + + ip, _, _, err := r.getIP() + + return netdev.IP(ip), err +} + +func (r *rtl8720dn) clientTLS() uint32 { + client := r.rpc_wifi_ssl_client_create() + r.rpc_wifi_ssl_init(client) + r.rpc_wifi_ssl_set_timeout(client, 120*1000 /* usec? */) + return client +} + +// 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 (r *rtl8720dn) Socket(family netdev.AddressFamily, sockType netdev.SockType, + protocol netdev.Protocol) (netdev.Sockfd, error) { + + if debugging(debugNetdev) { + fmt.Printf("[Socket] family: %s, sockType: %s, protocol: %s\r\n", + family, sockType, protocol) + } + + switch family { + case netdev.AF_INET: + default: + return -1, netdev.ErrFamilyNotSupported + } + + var newSock int32 + + r.mu.Lock() + defer r.mu.Unlock() + + switch { + case protocol == netdev.IPPROTO_TCP && sockType == netdev.SOCK_STREAM: + newSock = r.rpc_lwip_socket(netdev.AF_INET, netdev.SOCK_STREAM, + netdev.IPPROTO_TCP) + case protocol == netdev.IPPROTO_TLS && sockType == netdev.SOCK_STREAM: + // TODO Investigate: using client number as socket number; + // TODO this may cause a problem if mixing TLS and non-TLS sockets? + newSock = int32(r.clientTLS()) + case protocol == netdev.IPPROTO_UDP && sockType == netdev.SOCK_DGRAM: + newSock = r.rpc_lwip_socket(netdev.AF_INET, netdev.SOCK_DGRAM, + netdev.IPPROTO_UDP) + default: + return -1, netdev.ErrProtocolNotSupported + } + + if newSock == -1 { + return -1, netdev.ErrNoMoreSockets + } + + socket := newSocket(protocol) + r.sockets[sock(newSock)] = socket + + return netdev.Sockfd(newSock), nil +} + +func addrToName(addr netdev.SockAddr) []byte { + port := addr.Port() + ip := addr.IpBytes() + + name := make([]byte, 16) + name[0] = 0x00 + name[1] = netdev.AF_INET + name[2] = byte(port >> 8) + name[3] = byte(port) + name[4] = byte(ip[0]) + name[5] = byte(ip[1]) + name[6] = byte(ip[2]) + name[7] = byte(ip[3]) + + return name +} + +func (r *rtl8720dn) Bind(sockfd netdev.Sockfd, addr netdev.SockAddr) error { + + if debugging(debugNetdev) { + fmt.Printf("[Bind] sockfd: %d, addr: %s\r\n", sockfd, addr) + } + + r.mu.Lock() + defer r.mu.Unlock() + + var sock = sock(sockfd) + var socket = r.sockets[sock] + var name = addrToName(addr) + + switch socket.protocol { + case netdev.IPPROTO_TCP, netdev.IPPROTO_UDP: + result := r.rpc_lwip_bind(int32(sock), name, uint32(len(name))) + if result == -1 { + return fmt.Errorf("Bind to %s failed", addr.String()) + } + default: + return netdev.ErrProtocolNotSupported + } + + return nil +} + +func (r *rtl8720dn) Connect(sockfd netdev.Sockfd, servaddr netdev.SockAddr) error { + + if debugging(debugNetdev) { + fmt.Printf("[Connect] sockfd: %d, servaddr: %s\r\n", sockfd, servaddr) + } + + r.mu.Lock() + defer r.mu.Unlock() + + var sock = sock(sockfd) + var socket = r.sockets[sock] + var name = addrToName(servaddr) + + // Start the connection + switch socket.protocol { + case netdev.IPPROTO_TCP, netdev.IPPROTO_UDP: + result := r.rpc_lwip_connect(int32(sock), name, uint32(len(name))) + if result == -1 { + return fmt.Errorf("Connect to %s failed", servaddr.String()) + } + case netdev.IPPROTO_TLS: + result := r.rpc_wifi_start_ssl_client(uint32(sock), + servaddr.Host(), uint32(servaddr.Port()), 0) + if result == -1 { + return fmt.Errorf("Connect to %s failed", servaddr.String()) + } + } + + return nil +} + +func (r *rtl8720dn) Listen(sockfd netdev.Sockfd, backlog int) error { + + if debugging(debugNetdev) { + fmt.Printf("[Listen] sockfd: %d\r\n", sockfd) + } + + r.mu.Lock() + defer r.mu.Unlock() + + var sock = sock(sockfd) + var socket = r.sockets[sock] + + switch socket.protocol { + case netdev.IPPROTO_TCP: + result := r.rpc_lwip_listen(int32(sock), int32(backlog)) + if result == -1 { + return fmt.Errorf("Listen failed") + } + result = r.rpc_lwip_fcntl(int32(sock), F_SETFL, O_NONBLOCK) + if result == -1 { + return fmt.Errorf("Fcntl failed") + } + case netdev.IPPROTO_UDP: + result := r.rpc_lwip_listen(int32(sock), int32(backlog)) + if result == -1 { + return fmt.Errorf("Listen failed") + } + default: + return netdev.ErrProtocolNotSupported + } + + return nil +} + +func (r *rtl8720dn) Accept(sockfd netdev.Sockfd, peer netdev.SockAddr) (netdev.Sockfd, error) { + + if debugging(debugNetdev) { + fmt.Printf("[Accept] sockfd: %d, peer: %s\r\n", sockfd, peer) + } + + r.mu.Lock() + defer r.mu.Unlock() + + var newSock int32 + var lsock = sock(sockfd) + var socket = r.sockets[lsock] + var addr = addrToName(peer) + + switch socket.protocol { + case netdev.IPPROTO_TCP: + default: + return -1, netdev.ErrProtocolNotSupported + } + + for { + // Accept() will be sleeping most of the time, checking for a + // new clients every 1/10 sec. + r.mu.Unlock() + time.Sleep(100 * time.Millisecond) + r.mu.Lock() + + // Check if a client connected. O_NONBLOCK is set on lsock. + addrlen := uint32(len(addr)) + newSock = r.rpc_lwip_accept(int32(lsock), addr, &addrlen) + if newSock == -1 { + // No new client + time.Sleep(100 * time.Millisecond) + continue + } + + // If we've already seen this socket, we can re-use + // the socket and return it. But, only if the socket + // is closed. If it's not closed, we'll just come back + // later to reuse it. + + clientSocket, ok := r.sockets[sock(newSock)] + if ok { + // Wait for client to Close + if clientSocket.inuse { + continue + } + // Reuse client socket + return netdev.Sockfd(newSock), nil + } + + // Create new socket for client and return fd + r.sockets[sock(newSock)] = newSocket(socket.protocol) + return netdev.Sockfd(newSock), nil + } +} + +func (r *rtl8720dn) sendChunk(sockfd netdev.Sockfd, buf []byte) (int, error) { + var sock = sock(sockfd) + var socket = r.sockets[sock] + + switch socket.protocol { + case netdev.IPPROTO_TCP, netdev.IPPROTO_UDP: + result := r.rpc_lwip_send(int32(sock), buf, 0x00000008) + if result == -1 { + return -1, fmt.Errorf("Send error") + } + return int(result), nil + case netdev.IPPROTO_TLS: + result := r.rpc_wifi_send_ssl_data(uint32(sock), buf, uint16(len(buf))) + if result == -1 { + return -1, fmt.Errorf("TLS Send error") + } + return int(result), nil + } + + return -1, netdev.ErrProtocolNotSupported +} + +func (r *rtl8720dn) Send(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, + timeout time.Duration) (int, error) { + + if debugging(debugNetdev) { + fmt.Printf("[Send] sockfd: %d, len(buf): %d, flags: %d\r\n", + sockfd, len(buf), flags) + } + + r.mu.Lock() + defer r.mu.Unlock() + + // Break large bufs into chunks + + // TODO handle timeout + + chunkSize := 1436 + for i := 0; i < len(buf); i += chunkSize { + end := i + chunkSize + if end > len(buf) { + end = len(buf) + } + _, err := r.sendChunk(sockfd, buf[i:end]) + if err != nil { + return -1, err + } + } + + return len(buf), nil +} + +func (r *rtl8720dn) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, + timeout time.Duration) (int, error) { + + if debugging(debugNetdev) { + fmt.Printf("[Recv] sockfd: %d, len(buf): %d, flags: %d\r\n", + sockfd, len(buf), flags) + } + + r.mu.Lock() + defer r.mu.Unlock() + + var sock = sock(sockfd) + var socket = r.sockets[sock] + var length = len(buf) + var expire = time.Now().Add(timeout) + var n int32 + + // Limit length read size to chunk large read requests + if length > 1436 { + length = 1436 + } + + for { + // Check if we've timed out + if timeout > 0 { + if time.Now().Before(expire) { + return -1, netdev.ErrRecvTimeout + } + } + + switch socket.protocol { + case netdev.IPPROTO_TCP, netdev.IPPROTO_UDP: + n = r.rpc_lwip_recv(int32(sock), buf[:length], + uint32(length), 0x00000008, 0) + case netdev.IPPROTO_TLS: + n = r.rpc_wifi_get_ssl_receive(uint32(sock), + buf[:length], int32(length)) + } + + if n < 0 { + r.mu.Unlock() + time.Sleep(100 * time.Millisecond) + r.mu.Lock() + continue + } else if n == 0 { + if debugging(debugNetdev) { + fmt.Printf("[<--Recv] sockfd: %d, n: %d EOF\r\n", + sock, n) + } + return -1, io.EOF + } + + if debugging(debugNetdev) { + fmt.Printf("[<--Recv] sockfd: %d, n: %d\r\n", + sock, n) + } + + return int(n), nil + } +} + +func (r *rtl8720dn) Close(sockfd netdev.Sockfd) error { + + if debugging(debugNetdev) { + fmt.Printf("[Close] sockfd: %d\r\n", sockfd) + } + + r.mu.Lock() + defer r.mu.Unlock() + + var sock = sock(sockfd) + var socket = r.sockets[sock] + var result int32 + + if !socket.inuse { + return nil + } + + switch socket.protocol { + case netdev.IPPROTO_TCP, netdev.IPPROTO_UDP: + result = r.rpc_lwip_close(int32(sock)) + case netdev.IPPROTO_TLS: + r.rpc_wifi_stop_ssl_socket(uint32(sock)) + r.rpc_wifi_ssl_client_destroy(uint32(sock)) + } + + if result == -1 { + return netdev.ErrClosingSocket + } + + socket.inuse = false + + return nil +} + +func (r *rtl8720dn) SetSockOpt(sockfd netdev.Sockfd, level netdev.SockOptLevel, + opt netdev.SockOpt, value any) error { + + if debugging(debugNetdev) { + fmt.Printf("[SetSockOpt] sockfd: %d\r\n", sockfd) + } + + return netdev.ErrNotSupported +} + +func (r *rtl8720dn) disconnect() error { + result := r.rpc_wifi_disconnect() + if result == -1 { + return fmt.Errorf("Error disconnecting Wifi") + } + return nil +} + +func (r *rtl8720dn) getFwVersion() string { + return r.rpc_system_version() } -type UARTx struct { - *machine.UART +func (r *rtl8720dn) getMACAddr() string { + var mac [18]uint8 + r.rpc_wifi_get_mac_address(mac[:]) + return string(mac[:]) } -func (u *UARTx) Read(p []byte) (n int, err error) { - if u.Buffered() == 0 { - time.Sleep(1 * time.Millisecond) - return 0, nil +func (r *rtl8720dn) getIP() (ip, subnet, gateway netdev.IP, err error) { + var ip_info [12]byte + result := r.rpc_tcpip_adapter_get_ip_info(0, ip_info[:]) + if result == -1 { + err = fmt.Errorf("Get IP info failed") + return } - return u.UART.Read(p) + copy(ip[:], ip_info[0:4]) + copy(subnet[:], ip_info[4:8]) + copy(gateway[:], ip_info[8:12]) + return } diff --git a/rtl8720dn/util.go b/rtl8720dn/util.go deleted file mode 100644 index 588106c5c..000000000 --- a/rtl8720dn/util.go +++ /dev/null @@ -1,51 +0,0 @@ -package rtl8720dn - -import ( - "bytes" - "encoding/binary" - "fmt" - "strconv" - "strings" -) - -type httpHeader []byte - -func (h httpHeader) ContentLength() int { - contentLength := -1 - idx := bytes.Index(h, []byte("Content-Length: ")) - if 0 <= idx { - _, err := fmt.Sscanf(string(h[idx+16:]), "%d", &contentLength) - if err != nil { - return -1 - } - } - return contentLength -} - -// TODO: IPAddress implementation should be moved under drivers/net -// The same implementation exists in wifinina. -type IPAddress []byte - -func (addr IPAddress) String() string { - if len(addr) < 4 { - return "" - } - return strconv.Itoa(int(addr[0])) + "." + strconv.Itoa(int(addr[1])) + "." + strconv.Itoa(int(addr[2])) + "." + strconv.Itoa(int(addr[3])) -} - -func ParseIPv4(s string) (IPAddress, error) { - v := strings.Split(s, ".") - v0, _ := strconv.Atoi(v[0]) - v1, _ := strconv.Atoi(v[1]) - v2, _ := strconv.Atoi(v[2]) - v3, _ := strconv.Atoi(v[3]) - return IPAddress([]byte{byte(v0), byte(v1), byte(v2), byte(v3)}), nil -} - -func (addr IPAddress) AsUint32() uint32 { - if len(addr) < 4 { - return 0 - } - b := []byte(string(addr)) - return binary.BigEndian.Uint32(b[0:4]) -} diff --git a/rtl8720dn/wifi.go b/rtl8720dn/wifi.go deleted file mode 100644 index 08764210c..000000000 --- a/rtl8720dn/wifi.go +++ /dev/null @@ -1,72 +0,0 @@ -package rtl8720dn - -import ( - "fmt" - "time" -) - -func (d *Driver) ConnectToAP(ssid string, password string) error { - if len(ssid) == 0 || len(password) == 0 { - return fmt.Errorf("connection failed: either ssid or password not set") - } - - _, err := d.Rpc_wifi_off() - if err != nil { - return err - } - _, err = d.Rpc_wifi_on(0x00000001) - if err != nil { - return err - } - - _, err = d.Rpc_wifi_disconnect() - if err != nil { - return err - } - - numTry := 5 - securityType := uint32(0x00400004) - for i := 0; i < numTry; i++ { - ret, err := d.Rpc_wifi_connect(ssid, password, securityType, -1, 0) - if err != nil { - return err - } - if ret != 0 { - if i == numTry-1 { - return fmt.Errorf("connection failed: rpc_wifi_connect failed") - } - time.Sleep(100 * time.Millisecond) - } else { - break - } - } - - _, err = d.Rpc_tcpip_adapter_dhcpc_start(0) - if err != nil { - return err - } - - for i := 0; i < 3; i++ { - _, err = d.Rpc_wifi_is_connected_to_ap() - if err != nil { - return err - } - time.Sleep(1 * time.Second) - } - - return nil -} - -func (d *Driver) GetIP() (ip, subnet, gateway IPAddress, err error) { - ip_info := make([]byte, 12) - _, err = d.Rpc_tcpip_adapter_get_ip_info(0, &ip_info) - if err != nil { - return nil, nil, nil, err - } - - ip = IPAddress(ip_info[0:4]) - subnet = IPAddress(ip_info[4:8]) - gateway = IPAddress(ip_info[8:12]) - - return ip, subnet, gateway, nil -} diff --git a/wifinina/README.md b/wifinina/README.md index b851fd72e..3be01be1a 100644 --- a/wifinina/README.md +++ b/wifinina/README.md @@ -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 diff --git a/wifinina/adapter.go b/wifinina/adapter.go deleted file mode 100644 index 063ca0771..000000000 --- a/wifinina/adapter.go +++ /dev/null @@ -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 -} diff --git a/wifinina/debug.go b/wifinina/debug.go new file mode 100644 index 000000000..0def23f57 --- /dev/null +++ b/wifinina/debug.go @@ -0,0 +1,17 @@ +package wifinina + +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 +} diff --git a/wifinina/http.go b/wifinina/http.go deleted file mode 100644 index e1358a0f3..000000000 --- a/wifinina/http.go +++ /dev/null @@ -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() != TCPStateClosed; i++ { - time.Sleep(100 * time.Millisecond) - } - - if c.status() != 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 -} diff --git a/wifinina/pins.go b/wifinina/pins.go index a1afbcbde..5d7ca2b4f 100644 --- a/wifinina/pins.go +++ b/wifinina/pins.go @@ -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 { diff --git a/wifinina/protocol/readme.md b/wifinina/protocol/readme.md deleted file mode 100644 index 0f2a5e395..000000000 --- a/wifinina/protocol/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -WiFiNINA protocol -================= - diff --git a/wifinina/tcp.go b/wifinina/tcp.go deleted file mode 100644 index ba5f41a5d..000000000 --- a/wifinina/tcp.go +++ /dev/null @@ -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 == TCPStateListen || s == TCPStateClosed || - s == TCPStateFinWait1 || s == TCPStateFinWait2 || s == TCPStateTimeWait || - s == TCPStateSynSent || s == TCPStateSynRcvd || s == 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 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 == TCPStateClosed { - break - } - time.Sleep(1 * time.Millisecond) - } - d.sock = NoSocketAvail - return nil -} diff --git a/wifinina/wifinina.go b/wifinina/wifinina.go index c493035e0..ea6f008df 100644 --- a/wifinina/wifinina.go +++ b/wifinina/wifinina.go @@ -1,1247 +1,1893 @@ -// Package wifinina implements TCP wireless communication over SPI -// with an attached separate ESP32 board using the Arduino WiFiNINA protocol. +// 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 +// 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" // used only in debug printouts and is optimized out when debugging is disabled - "strconv" - "strings" + "fmt" + "io" + "machine" + "math/bits" "sync" "time" - "machine" - "tinygo.org/x/drivers" - "tinygo.org/x/drivers/net" + "tinygo.org/x/drivers/netdev" ) -const _debug = false +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 ( - MaxSockets = 4 - MaxNetworks = 10 - MaxAttempts = 10 - - MaxLengthSSID = 32 - MaxLengthWPAKey = 63 - MaxLengthWEPKey = 13 - - LengthMacAddress = 6 - LengthIPV4 = 4 - - WlFailure = -1 - WlSuccess = 1 - - 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 - /* - // Default state value for Wifi state field - #define NA_STATE -1 - */ - - FlagCmd = 0 - FlagReply = 1 << 7 - FlagData = 0x40 - - NinaCmdPos = 1 - NinaParamLenPos = 2 - - CmdStart = 0xE0 - CmdEnd = 0xEE - CmdErr = 0xEF + 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 - // TEST_CMD = 0x13 - - 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 - // GET_IDX_SSID_CMD = 0x31, - // GET_TEST_CMD = 0x38 - - // All command with DATA_FLAG 0x40 send a 16bit Len - CmdSendDataTCP = 0x44 - CmdGetDatabufTCP = 0x45 - CmdInsertDataBuf = 0x46 - - // regular format commands - CmdSetPinMode = 0x50 - CmdSetDigitalWrite = 0x51 - CmdSetAnalogWrite = 0x52 - - ErrTimeoutChipReady Error = 0x01 - ErrTimeoutChipSelect Error = 0x02 - ErrCheckStartCmd Error = 0x03 - ErrWaitRsp Error = 0x04 - ErrUnexpectedLength Error = 0xE0 - ErrNoParamsReturned Error = 0xE1 - ErrIncorrectSentinel Error = 0xE2 - ErrCmdErrorReceived Error = 0xEF - ErrNotImplemented Error = 0xF0 - ErrUnknownHost Error = 0xF1 - ErrSocketAlreadySet Error = 0xF2 - ErrConnectionTimeout Error = 0xF3 - ErrNoData Error = 0xF4 - ErrDataNotWritten Error = 0xF5 - ErrCheckDataError Error = 0xF6 - ErrBufferTooSmall Error = 0xF7 - ErrNoSocketAvail Error = 0xFF - - NoSocketAvail uint8 = 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 + protoModeTCP = iota + protoModeUDP + protoModeTLS + protoModeMul ) -type ConnectionStatus uint8 - -func (c ConnectionStatus) String() string { - switch c { - case StatusIdle: - return "Idle" - case StatusNoSSIDAvail: - return "No SSID Available" - case StatusScanCompleted: - return "Scan Completed" - case StatusConnected: - return "Connected" - case StatusConnectFailed: - return "Connect Failed" - case StatusConnectionLost: - return "Connection Lost" - case StatusDisconnected: - return "Disconnected" - case StatusNoShield: - return "No Shield" - default: - return "Unknown" +type connectionStatus uint8 +type encryptionType uint8 +type macAddress netdev.HardwareAddr +type sock uint8 +type hwerr uint8 + +type socket struct { + protocol netdev.Protocol + laddr netdev.SockAddr + inuse bool +} + +type Config struct { + // AP creditials + Ssid string + Passphrase string + + // 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 + + // Retries is how many attempts to connect before returning with a + // "Connect failed" error. Zero means infinite retries. + Retries int + + // Timeout duration for each connection attempt. Default is 10sec. + ConnectTimeo time.Duration + + // Watchdog ticker duration. On tick, the watchdog will check for + // downed connection or hardware fault and try to recover the + // connection. Default is 0secs, which means no watchdog. Set to + // non-zero to enable watchodog. + WatchdogTimeo time.Duration +} + +type wifinina struct { + cfg *Config + notifyCb func(netdev.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 + + netConnected bool + driverShown bool + deviceShown bool + spiSetup bool + + killWatchdog chan bool + fault error + + sockets map[sock]*socket // keyed by sock as returned by getSocket() +} + +func newSocket(protocol netdev.Protocol) *socket { + return &socket{protocol: protocol, inuse: true} +} + +func New(cfg *Config) *wifinina { + w := wifinina{ + cfg: cfg, + sockets: make(map[sock]*socket), + killWatchdog: make(chan bool), + cs: cfg.Cs, + ack: cfg.Ack, + gpio0: cfg.Gpio0, + resetn: cfg.Resetn, } + + if w.cfg.ConnectTimeo == 0 { + w.cfg.ConnectTimeo = 10 * time.Second + } + + return &w } -type EncryptionType uint8 +func (err hwerr) Error() string { + return "[wifinina] error: 0x" + hex.EncodeToString([]byte{uint8(err)}) +} -func (e EncryptionType) String() string { - switch e { - case EncTypeTKIP: - return "TKIP" - case EncTypeCCMP: - return "WPA2" - case EncTypeWEP: - return "WEP" - case EncTypeNone: - return "None" - case EncTypeAuto: - return "Auto" - default: - return "Unknown" +func (w *wifinina) connectToAP(timeout time.Duration) error { + + if len(w.cfg.Ssid) == 0 { + return netdev.ErrMissingSSID + } + + if debugging(debugBasic) { + fmt.Printf("Connecting to Wifi SSID '%s'...", w.cfg.Ssid) + } + + start := time.Now() + + // Start the connection process + w.setPassphrase(w.cfg.Ssid, w.cfg.Passphrase) + + // Check if we connected + for time.Since(start) < timeout { + status := w.getConnectionStatus() + if status == statusConnected { + if debugging(debugBasic) { + fmt.Printf("CONNECTED\r\n") + } + if w.notifyCb != nil { + w.notifyCb(netdev.EventNetUp) + } + return nil + } + time.Sleep(1 * time.Second) } + + if debugging(debugBasic) { + fmt.Printf("FAILED\r\n") + } + + return netdev.ErrConnectTimeout } -type IPAddress string // TODO: does WiFiNINA support ipv6??? +func (w *wifinina) netDisconnect() { + w.disconnect() +} -func (addr IPAddress) String() string { - if len(addr) < 4 { - return "" +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) } - return strconv.Itoa(int(addr[0])) + "." + strconv.Itoa(int(addr[1])) + "." + strconv.Itoa(int(addr[2])) + "." + strconv.Itoa(int(addr[3])) + w.driverShown = true } -func ParseIPv4(s string) (IPAddress, error) { - v := strings.Split(s, ".") - v0, _ := strconv.Atoi(v[0]) - v1, _ := strconv.Atoi(v[1]) - v2, _ := strconv.Atoi(v[2]) - v3, _ := strconv.Atoi(v[3]) - return IPAddress([]byte{byte(v0), byte(v1), byte(v2), byte(v3)}), nil +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 (addr IPAddress) AsUint32() uint32 { - if len(addr) < 4 { - return 0 +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.Low() + time.Sleep(10 * time.Millisecond) + w.resetn.High() + 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) { + mac := netdev.HardwareAddr(w.getMACAddr()) + fmt.Printf("ESP32 firmware version : %s\r\n", w.getFwVersion()) + 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", netdev.IP(ip).String()) + fmt.Printf("DHCP-assigned subnet : %s\r\n", netdev.IP(subnet).String()) + fmt.Printf("DHCP-assigned gateway : %s\r\n", netdev.IP(gateway).String()) + fmt.Printf("\r\n") + } +} + +func (w *wifinina) networkDown() bool { + return w.getConnectionStatus() != statusConnected +} + +func (w *wifinina) watchdog() { + ticker := time.NewTicker(w.cfg.WatchdogTimeo) + 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(netdev.EventNetDown) + } + w.netConnect(false) + } + w.mu.Unlock() + } } - b := []byte(string(addr)) - return binary.BigEndian.Uint32(b[0:4]) } -type MACAddress uint64 +func (w *wifinina) netConnect(reset bool) error { + if reset { + w.start() + } + w.showDevice() -func (addr MACAddress) String() string { - b := make([]byte, 8) - binary.BigEndian.PutUint64(b, uint64(addr)) - encoded := hex.EncodeToString(b) - result := "" - for i := 2; i < 8; i++ { - result += encoded[i*2 : i*2+2] - if i < 7 { - result += ":" + for i := 0; w.cfg.Retries == 0 || i < w.cfg.Retries; i++ { + if err := w.connectToAP(w.cfg.ConnectTimeo); err != nil { + if err == netdev.ErrConnectTimeout { + continue + } + return err } + break } - return result + + if w.networkDown() { + return netdev.ErrConnectFailed + } + + w.showIP() + return nil } -type Error uint8 +func (w *wifinina) NetConnect() error { + + w.mu.Lock() + defer w.mu.Unlock() + + if w.netConnected { + return netdev.ErrConnected + } + + w.showDriver() + w.setupSPI() + + if err := w.netConnect(true); err != nil { + return err + } + + w.netConnected = true + + if w.cfg.WatchdogTimeo != 0 { + go w.watchdog() + } -func (err Error) Error() string { - return "wifinina error: 0x" + hex.EncodeToString([]byte{uint8(err)}) + return nil } -// Cmd Struct Message */ -// ._______________________________________________________________________. -// | START CMD | C/R | CMD | N.PARAM | PARAM LEN | PARAM | .. | END CMD | -// |___________|______|______|_________|___________|________|____|_________| -// | 8 bit | 1bit | 7bit | 8bit | 8bit | nbytes | .. | 8bit | -// |___________|______|______|_________|___________|________|____|_________| -type command struct { - cmd uint8 - reply bool - params []int - paramData []byte +func (w *wifinina) NetDisconnect() { + + w.mu.Lock() + defer w.mu.Unlock() + + if !w.netConnected { + return + } + + if w.cfg.WatchdogTimeo != 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.cfg.Ssid) + } + + if w.notifyCb != nil { + w.notifyCb(netdev.EventNetDown) + } +} + +func (w *wifinina) NetNotify(cb func(netdev.Event)) { + w.notifyCb = cb } -type Device struct { - SPI drivers.SPI - CS machine.Pin - ACK machine.Pin - GPIO0 machine.Pin - RESET machine.Pin +func (w *wifinina) GetHostByName(name string) (netdev.IP, error) { - buf [64]byte - ssids [10]string + if debugging(debugNetdev) { + fmt.Printf("[GetHostByName] name: %s\r\n", name) + } + + w.mu.Lock() + defer w.mu.Unlock() + + ip := w.getHostByName(name) + if ip == "" { + return netdev.IP{}, netdev.ErrHostUnknown + } - sock uint8 - readBuf readBuffer + var hostIp netdev.IP + copy(hostIp[:], []byte(ip)) - proto uint8 - ip uint32 - port uint16 - mu sync.Mutex + return hostIp, nil } -// New returns a new Wifinina device. -func New(bus drivers.SPI, csPin, ackPin, gpio0Pin, resetPin machine.Pin) *Device { - return &Device{ - SPI: bus, - CS: csPin, - ACK: ackPin, - GPIO0: gpio0Pin, - RESET: resetPin, +func (w *wifinina) GetHardwareAddr() (netdev.HardwareAddr, error) { + + if debugging(debugNetdev) { + fmt.Printf("[GetHardwareAddr]\r\n") } + + w.mu.Lock() + defer w.mu.Unlock() + + return netdev.HardwareAddr(w.getMACAddr()), nil } -func (d *Device) Configure() { - net.UseDriver(d) - pinUseDevice(d) +func (w *wifinina) GetIPAddr() (netdev.IP, error) { - d.CS.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.ACK.Configure(machine.PinConfig{Mode: machine.PinInput}) - d.RESET.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.GPIO0.Configure(machine.PinConfig{Mode: machine.PinOutput}) + if debugging(debugNetdev) { + fmt.Printf("[GetIPAddr]\r\n") + } - d.GPIO0.High() - d.CS.High() - d.RESET.Low() - time.Sleep(1 * time.Millisecond) - d.RESET.High() - time.Sleep(1 * time.Millisecond) + w.mu.Lock() + defer w.mu.Unlock() - d.GPIO0.Low() - d.GPIO0.Configure(machine.PinConfig{Mode: machine.PinInput}) + ip, _, _ := w.getIP() + return netdev.IP(ip), nil } -// ----------- client methods (should this be a separate struct?) ------------ +// 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(family netdev.AddressFamily, sockType netdev.SockType, + protocol netdev.Protocol) (netdev.Sockfd, error) { -func (d *Device) StartClient(hostname string, addr uint32, port uint16, sock uint8, mode uint8) error { - if _debug { - fmt.Printf("[StartClient] hostname: %s addr: % 02X, port: %d, sock: %d\r\n", hostname, addr, port, sock) + if debugging(debugNetdev) { + fmt.Printf("[Socket] family: %s, sockType: %s, protocol: %s\r\n", + family, sockType, protocol) } - if err := d.waitForChipSelect(); err != nil { - d.spiChipDeselect() - return err + + switch family { + case netdev.AF_INET: + default: + return -1, netdev.ErrFamilyNotSupported } - if len(hostname) > 0 { - d.sendCmd(CmdStartClientTCP, 5) - d.sendParamStr(hostname, false) - } else { - d.sendCmd(CmdStartClientTCP, 4) + switch { + case protocol == netdev.IPPROTO_TCP && sockType == netdev.SOCK_STREAM: + case protocol == netdev.IPPROTO_TLS && sockType == netdev.SOCK_STREAM: + case protocol == netdev.IPPROTO_UDP && sockType == netdev.SOCK_DGRAM: + default: + return -1, netdev.ErrProtocolNotSupported } - d.sendParam32(addr, false) - d.sendParam16(port, false) - d.sendParam8(sock, false) - d.sendParam8(mode, true) - if len(hostname) > 0 { - d.padTo4(17 + len(hostname)) + w.mu.Lock() + defer w.mu.Unlock() + + sock := w.getSocket() + if sock == noSocketAvail { + return -1, netdev.ErrNoMoreSockets } - d.spiChipDeselect() + socket := newSocket(protocol) + w.sockets[sock] = socket - _, err := d.waitRspCmd1(CmdStartClientTCP) - return err + return netdev.Sockfd(sock), nil } -func (d *Device) GetSocket() (uint8, error) { - return d.getUint8(d.req0(CmdGetSocket)) +func (w *wifinina) Bind(sockfd netdev.Sockfd, addr netdev.SockAddr) error { + + if debugging(debugNetdev) { + fmt.Printf("[Bind] sockfd: %d, addr: %s\r\n", sockfd, addr) + } + + w.mu.Lock() + defer w.mu.Unlock() + + var sock = sock(sockfd) + var socket = w.sockets[sock] + + switch socket.protocol { + case netdev.IPPROTO_TCP: + case netdev.IPPROTO_TLS: + case netdev.IPPROTO_UDP: + w.startServer(sock, addr.Port(), protoModeUDP) + } + + socket.laddr = addr + + return nil } -func (d *Device) GetClientState(sock uint8) (uint8, error) { - return d.getUint8(d.reqUint8(CmdGetClientStateTCP, sock)) +func (w *wifinina) Connect(sockfd netdev.Sockfd, servaddr netdev.SockAddr) error { + + if debugging(debugNetdev) { + fmt.Printf("[Connect] sockfd: %d, servaddr: %s\r\n", sockfd, servaddr) + } + + w.mu.Lock() + defer w.mu.Unlock() + + var sock = sock(sockfd) + var socket = w.sockets[sock] + + // Start the connection + switch socket.protocol { + case netdev.IPPROTO_TCP: + w.startClient(sock, "", servaddr.IpUint32(), servaddr.Port(), protoModeTCP) + case netdev.IPPROTO_TLS: + w.startClient(sock, servaddr.Host(), 0, servaddr.Port(), protoModeTLS) + case netdev.IPPROTO_UDP: + w.startClient(sock, "", servaddr.IpUint32(), servaddr.Port(), protoModeUDP) + return nil + } + + // Wait for up to 10s to connect... + expire := time.Now().Add(10 * time.Second) + + for time.Now().Before(expire) { + if w.isConnected(sock) { + return nil + } + + // Check if we've faulted + if w.fault != nil { + w.stopClient(sock) + return w.fault + } + + w.mu.Unlock() + time.Sleep(100 * time.Millisecond) + w.mu.Lock() + } + + w.stopClient(sock) + + return fmt.Errorf("Connect to %s timed out", servaddr.String()) } -func (d *Device) SendData(buf []byte, sock uint8) (uint16, error) { - d.mu.Lock() - defer d.mu.Unlock() +func (w *wifinina) Listen(sockfd netdev.Sockfd, backlog int) error { + + if debugging(debugNetdev) { + fmt.Printf("[Listen] sockfd: %d\r\n", sockfd) + } + + w.mu.Lock() + defer w.mu.Unlock() - if err := d.waitForChipSelect(); err != nil { - d.spiChipDeselect() - return 0, err + var sock = sock(sockfd) + var socket = w.sockets[sock] + + switch socket.protocol { + case netdev.IPPROTO_TCP: + w.startServer(sock, socket.laddr.Port(), protoModeTCP) + case netdev.IPPROTO_UDP: + default: + return netdev.ErrProtocolNotSupported } - l := d.sendCmd(CmdSendDataTCP, 2) - l += d.sendParamBuf([]byte{sock}, false) - l += d.sendParamBuf(buf, true) - d.addPadding(l) - d.spiChipDeselect() - return d.getUint16(d.waitRspCmd1(CmdSendDataTCP)) + + return nil } -func (d *Device) CheckDataSent(sock uint8) (bool, error) { - d.mu.Lock() - defer d.mu.Unlock() +func (w *wifinina) Accept(sockfd netdev.Sockfd, peer netdev.SockAddr) (netdev.Sockfd, error) { - var lastErr error - for timeout := 0; timeout < 10; timeout++ { - sent, err := d.getUint8(d.reqUint8(CmdDataSentTCP, sock)) - if err != nil { - lastErr = err + if debugging(debugNetdev) { + fmt.Printf("[Accept] sockfd: %d, peer: %s\r\n", sockfd, peer) + } + + w.mu.Lock() + defer w.mu.Unlock() + + var client sock + var sock = sock(sockfd) + var socket = w.sockets[sock] + + switch socket.protocol { + case netdev.IPPROTO_TCP: + default: + return -1, netdev.ErrProtocolNotSupported + } + + for { + // Accept() will be sleeping most of the time, checking for a + // 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, 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 + client = w.accept(sock) + if client == noSocketAvail { + // None ready + continue } - if sent > 0 { - return true, nil + + // If we've already seen this socket, we can reuse + // the socket and return it. But, only if the socket + // is closed. If it's not closed, we'll just come back + // later to reuse it. + + clientSocket, ok := w.sockets[client] + if ok { + // Wait for client to Close + if clientSocket.inuse { + continue + } + // Reuse client socket + return netdev.Sockfd(client), nil } - time.Sleep(100 * time.Microsecond) + + // Create new socket for client and return fd + w.sockets[client] = newSocket(socket.protocol) + return netdev.Sockfd(client), nil + } +} + +func (w *wifinina) sockDown(sock sock) bool { + state := w.getClientState(sock) + if state == tcpStateEstablished { + return false } - return false, lastErr + return true } -func (d *Device) GetDataBuf(sock uint8, buf []byte) (int, error) { - d.mu.Lock() - defer d.mu.Unlock() +func (w *wifinina) sendTCP(sock sock, buf []byte, timeout time.Duration) (int, error) { - if err := d.waitForChipSelect(); err != nil { - d.spiChipDeselect() - return 0, err + var timeoutDataSent = 25 + var expire = time.Now().Add(timeout) + + // Send it + n := int(w.sendData(sock, buf)) + if n == 0 { + return -1, io.EOF } - p := uint16(len(buf)) - l := d.sendCmd(CmdGetDatabufTCP, 2) - l += d.sendParamBuf([]byte{sock}, false) - l += d.sendParamBuf([]byte{uint8(p & 0x00FF), uint8((p) >> 8)}, true) - d.addPadding(l) - d.spiChipDeselect() - if err := d.waitForChipSelect(); err != nil { - d.spiChipDeselect() - return 0, err + + // 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 timeout > 0 { + if time.Now().Before(expire) { + return -1, fmt.Errorf("Send timeout expired") + } + } + + // Check if socket went down + if w.sockDown(sock) { + 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() } - n, err := d.waitRspBuf16(CmdGetDatabufTCP, buf) - d.spiChipDeselect() - return int(n), err + + return -1, fmt.Errorf("Send timed out") } -func (d *Device) StopClient(sock uint8) error { - d.mu.Lock() - defer d.mu.Unlock() +func (w *wifinina) sendUDP(sock sock, buf []byte, timeout time.Duration) (int, error) { + + // Queue it + ok := w.insertDataBuf(sock, buf) + if !ok { + return -1, fmt.Errorf("Insert UDP data failed, len(buf)=%d", len(buf)) + } - if _debug { - println("[StopClient] called StopClient()\r") + // Send it + ok = w.sendUDPData(sock) + if !ok { + return -1, fmt.Errorf("Send UDP data failed, len(buf)=%d", len(buf)) } - _, err := d.getUint8(d.reqUint8(CmdStopClientTCP, sock)) - return err + + return len(buf), nil } -func (d *Device) StartServer(port uint16, sock uint8, mode uint8) error { - d.mu.Lock() - defer d.mu.Unlock() +func (w *wifinina) sendChunk(sockfd netdev.Sockfd, buf []byte, timeout time.Duration) (int, error) { + var sock = sock(sockfd) + var socket = w.sockets[sock] - if err := d.waitForChipSelect(); err != nil { - d.spiChipDeselect() - return err + switch socket.protocol { + case netdev.IPPROTO_TCP, netdev.IPPROTO_TLS: + return w.sendTCP(sock, buf, timeout) + case netdev.IPPROTO_UDP: + return w.sendUDP(sock, buf, timeout) } - l := d.sendCmd(CmdStartServerTCP, 3) - l += d.sendParam16(port, false) - l += d.sendParam8(sock, false) - l += d.sendParam8(mode, true) - d.addPadding(l) - d.spiChipDeselect() - _, err := d.waitRspCmd1(CmdStartClientTCP) - return err + + return -1, netdev.ErrProtocolNotSupported } -// InsertDataBuf adds data to the buffer used for sending UDP data -func (d *Device) InsertDataBuf(buf []byte, sock uint8) (bool, error) { - d.mu.Lock() - defer d.mu.Unlock() +func (w *wifinina) Send(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, + timeout time.Duration) (int, error) { - if err := d.waitForChipSelect(); err != nil { - d.spiChipDeselect() - return false, err + if debugging(debugNetdev) { + fmt.Printf("[Send] sockfd: %d, len(buf): %d, flags: %d\r\n", + sockfd, len(buf), flags) } - l := d.sendCmd(CmdInsertDataBuf, 2) - l += d.sendParamBuf([]byte{sock}, false) - l += d.sendParamBuf(buf, true) - d.addPadding(l) - d.spiChipDeselect() - n, err := d.getUint8(d.waitRspCmd1(CmdInsertDataBuf)) - return n == 1, err -} -// SendUDPData sends the data previously added to the UDP buffer -func (d *Device) SendUDPData(sock uint8) (bool, error) { - d.mu.Lock() - defer d.mu.Unlock() + w.mu.Lock() + defer w.mu.Unlock() - if err := d.waitForChipSelect(); err != nil { - d.spiChipDeselect() - return false, err + // 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], timeout) + if err != nil { + return -1, err + } } - l := d.sendCmd(CmdSendDataUDP, 1) - l += d.sendParam8(sock, true) - d.addPadding(l) - d.spiChipDeselect() - n, err := d.getUint8(d.waitRspCmd1(CmdSendDataUDP)) - return n == 1, err + + return len(buf), nil } -// ---------- /client methods (should this be a separate struct?) ------------ +func (w *wifinina) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, + timeout time.Duration) (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() -/* - static bool startServer(uint16_t port, uint8_t sock); - static uint8_t getServerState(uint8_t sock); - static bool getData(uint8_t connId, uint8_t *data, bool peek, bool* connClose); - static int getDataBuf(uint8_t connId, uint8_t *buf, uint16_t bufSize); - static bool sendData(uint8_t sock, const uint8_t *data, uint16_t len); - static bool sendDataUdp(uint8_t sock, const char* host, uint16_t port, const uint8_t *data, uint16_t len); - static uint16_t availData(uint8_t connId); + var sock = sock(sockfd) + var socket = w.sockets[sock] + var max = len(buf) + // Limit max read size to chunk large read requests + if max > 1436 { + max = 1436 + } - static bool ping(const char *host); - static void reset(); + expire := time.Now().Add(timeout) - static void getRemoteIpAddress(IPAddress& ip); - static uint16_t getRemotePort(); -*/ + for { + // Check if we've timed out + if timeout > 0 { + if time.Now().Before(expire) { + return -1, netdev.ErrRecvTimeout + } + } -func (d *Device) Disconnect() error { - _, err := d.req1(CmdDisconnect) - return err + // 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(sock, buf[:max])) + if n > 0 { + if debugging(debugNetdev) { + fmt.Printf("[<--Recv] sockfd: %d, n: %d\r\n", + sock, n) + } + return n, nil + } + + // Check if socket went down + if socket.protocol != netdev.IPPROTO_UDP && w.sockDown(sock) { + // Get any last bytes + n = int(w.getDataBuf(sock, buf[:max])) + if debugging(debugNetdev) { + fmt.Printf("[<--Recv] sockfd: %d, n: %d, EOF\r\n", + sock, 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 (d *Device) GetFwVersion() (string, error) { - return d.getString(d.req0(CmdGetFwVersion)) +func (w *wifinina) Close(sockfd netdev.Sockfd) error { + + if debugging(debugNetdev) { + fmt.Printf("[Close] sockfd: %d\r\n", sockfd) + } + + w.mu.Lock() + defer w.mu.Unlock() + + var sock = sock(sockfd) + var socket = w.sockets[sock] + + if !socket.inuse { + return nil + } + + w.stopClient(sock) + + if socket.protocol == netdev.IPPROTO_UDP { + socket.inuse = false + return nil + } + + start := time.Now() + for time.Since(start) < 5*time.Second { + + state := w.getClientState(sock) + if state == tcpStateClosed { + socket.inuse = false + return nil + } + + w.mu.Unlock() + time.Sleep(100 * time.Millisecond) + w.mu.Lock() + } + + return netdev.ErrClosingSocket } -func (d *Device) GetConnectionStatus() (ConnectionStatus, error) { - status, err := d.getUint8(d.req0(CmdGetConnStatus)) - return ConnectionStatus(status), err +func (w *wifinina) SetSockOpt(sockfd netdev.Sockfd, level netdev.SockOptLevel, + opt netdev.SockOpt, value any) error { + + if debugging(debugNetdev) { + fmt.Printf("[SetSockOpt] sockfd: %d\r\n", sockfd) + } + + return netdev.ErrNotSupported } -func (d *Device) GetCurrentEncryptionType() (EncryptionType, error) { - enctype, err := d.getUint8(d.req1(CmdGetCurrEncrType)) - return EncryptionType(enctype), err +// Is TCP/TLS socket connected? +func (w *wifinina) isConnected(sock sock) bool { + s := w.getClientState(sock) + + connected := !(s == tcpStateListen || s == tcpStateClosed || + s == tcpStateFinWait1 || s == tcpStateFinWait2 || s == tcpStateTimeWait || + s == tcpStateSynSent || s == tcpStateSynRcvd || s == tcpStateCloseWait) + + return connected } -func (d *Device) GetCurrentBSSID() (MACAddress, error) { - return d.getMACAddress(d.req1(CmdGetCurrBSSID)) +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 (d *Device) GetCurrentRSSI() (int32, error) { - return d.getInt32(d.req1(CmdGetCurrRSSI)) +func (w *wifinina) getSocket() sock { + if debugging(debugCmd) { + fmt.Printf(" [cmdGetSocket]\r\n") + } + return sock(w.getUint8(w.req0(cmdGetSocket))) } -func (d *Device) GetCurrentSSID() (string, error) { - return d.getString(d.req1(CmdGetCurrSSID)) +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 (d *Device) GetMACAddress() (MACAddress, error) { - return d.getMACAddress(d.req1(CmdGetMACAddr)) +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 (d *Device) GetIP() (ip, subnet, gateway IPAddress, err error) { - sl := make([]string, 3) - if l, err := d.reqRspStr1(CmdGetIPAddr, dummyData, sl); err != nil { - return "", "", "", err - } else if l != 3 { - return "", "", "", ErrUnexpectedLength +func (w *wifinina) checkDataSent(sock sock) bool { + if debugging(debugCmd) { + fmt.Printf(" [cmdDataSentTCP] sock: %d\r\n", sock) } - return IPAddress(sl[0]), IPAddress(sl[1]), IPAddress(sl[2]), err + sent := w.getUint8(w.reqUint8(cmdDataSentTCP, uint8(sock))) + return sent > 0 } -func (d *Device) GetHostByName(hostname string) (IPAddress, error) { - ok, err := d.getUint8(d.reqStr(CmdReqHostByName, hostname)) - if err != nil { - return "", err +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)) } - if ok != 1 { - return "", ErrUnknownHost + + 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) + } } - ip, err := d.getString(d.req0(CmdGetHostByName)) - return IPAddress(ip), err + + return n } -func (d *Device) GetNetworkBSSID(idx int) (MACAddress, error) { - if idx < 0 || idx >= MaxNetworks { - return 0, nil +func (w *wifinina) stopClient(sock sock) { + if debugging(debugCmd) { + fmt.Printf(" [cmdStopClientTCP] sock: %d\r\n", sock) } - return d.getMACAddress(d.reqUint8(CmdGetIdxBSSID, uint8(idx))) + w.getUint8(w.reqUint8(cmdStopClientTCP, uint8(sock))) } -func (d *Device) GetNetworkChannel(idx int) (uint8, error) { - if idx < 0 || idx >= MaxNetworks { - return 0, nil +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) } - return d.getUint8(d.reqUint8(CmdGetIdxChannel, uint8(idx))) + + 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 (d *Device) GetNetworkEncrType(idx int) (EncryptionType, error) { - if idx < 0 || idx >= MaxNetworks { - return 0, nil +func (w *wifinina) accept(s sock) sock { + + if debugging(debugCmd) { + fmt.Printf(" [cmdAvailDataTCP] sock: %d\r\n", s) } - enctype, err := d.getUint8(d.reqUint8(CmdGetIdxEncrType, uint8(idx))) - return EncryptionType(enctype), err + + 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 (d *Device) GetNetworkRSSI(idx int) (int32, error) { - if idx < 0 || idx >= MaxNetworks { - return 0, nil +// 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)) } - return d.getInt32(d.reqUint8(CmdGetIdxRSSI, uint8(idx))) + + 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 } -func (d *Device) GetNetworkSSID(idx int) string { - if idx < 0 || idx >= MaxNetworks { - return "" +// 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) } - return d.ssids[idx] + + 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 (d *Device) GetReasonCode() (uint8, error) { - return d.getUint8(d.req0(CmdGetReasonCode)) +func (w *wifinina) disconnect() { + if debugging(debugCmd) { + fmt.Printf(" [cmdDisconnect]\r\n") + } + w.req1(cmdDisconnect) } -// GetTime is the time as a Unix timestamp -func (d *Device) GetTime() (uint32, error) { - return d.getUint32(d.req0(CmdGetTime)) +func (w *wifinina) getFwVersion() string { + if debugging(debugCmd) { + fmt.Printf(" [cmdGetFwVersion]\r\n") + } + return w.getString(w.req0(cmdGetFwVersion)) } -func (d *Device) GetTemperature() (float32, error) { - return d.getFloat32(d.req0(CmdGetTemperature)) +func (w *wifinina) getConnectionStatus() connectionStatus { + if debugging(debugCmd) { + fmt.Printf(" [cmdGetConnStatus]\r\n") + } + status := w.getUint8(w.req0(cmdGetConnStatus)) + return connectionStatus(status) } -func (d *Device) Ping(ip IPAddress, ttl uint8) int16 { - return 0 +func (w *wifinina) getCurrentencryptionType() encryptionType { + enctype := w.getUint8(w.req1(cmdGetCurrEncrType)) + return encryptionType(enctype) } -func (d *Device) SetDebug(on bool) error { - var v uint8 - if on { - v = 1 - } - _, err := d.reqUint8(CmdSetDebug, v) - return err +func (w *wifinina) getCurrentBSSID() macAddress { + return w.getMACAddress(w.req1(cmdGetCurrBSSID)) } -func (d *Device) SetNetwork(ssid string) error { - _, err := d.reqStr(CmdSetNet, ssid) - return err +func (w *wifinina) getCurrentRSSI() int32 { + return w.getInt32(w.req1(cmdGetCurrRSSI)) } -func (d *Device) SetPassphrase(ssid string, passphrase string) error { - _, err := d.reqStr2(CmdSetPassphrase, ssid, passphrase) - return err +func (w *wifinina) getCurrentSSID() string { + return w.getString(w.req1(cmdGetCurrSSID)) } -func (d *Device) SetKey(ssid string, index uint8, key string) error { - defer d.spiChipDeselect() - if err := d.waitForChipSelect(); err != nil { - return err +func (w *wifinina) getMACAddr() macAddress { + if debugging(debugCmd) { + fmt.Printf(" [cmdGetMACAddr]\r\n") } + return w.getMACAddress(w.req1(cmdGetMACAddr)) +} - d.sendCmd(CmdSetKey, 3) - d.sendParamStr(ssid, false) - d.sendParam8(index, false) - d.sendParamStr(key, true) +func (w *wifinina) faultf(f string, args ...any) { + // Only record the first fault + if w.fault == nil { + w.fault = fmt.Errorf(f, args...) + } +} - d.padTo4(8 + len(ssid) + len(key)) +func (w *wifinina) getIP() (ip, subnet, gateway netdev.IP) { + 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 + } + copy(ip[:], sl[0]) + copy(subnet[:], sl[1]) + copy(gateway[:], sl[2]) + return +} - _, err := d.waitRspCmd1(CmdSetKey) - if err != nil { - return err +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)) +} - return nil +func (w *wifinina) getNetworkBSSID(idx int) macAddress { + if idx < 0 || idx >= maxNetworks { + return macAddress{} + } + return w.getMACAddress(w.reqUint8(cmdGetIdxBSSID, uint8(idx))) } -func (d *Device) SetNetworkForAP(ssid string) error { - _, err := d.reqStr(CmdSetAPNet, ssid) - return err +func (w *wifinina) getNetworkChannel(idx int) uint8 { + if idx < 0 || idx >= maxNetworks { + return 0 + } + return w.getUint8(w.reqUint8(cmdGetIdxChannel, uint8(idx))) } -func (d *Device) SetPassphraseForAP(ssid string, passphrase string) error { - _, err := d.reqStr2(CmdSetAPPassphrase, ssid, passphrase) - return err +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 (d *Device) SetIP(which uint8, ip uint32, gw uint32, subnet uint32) error { - return ErrNotImplemented +func (w *wifinina) getNetworkRSSI(idx int) int32 { + if idx < 0 || idx >= maxNetworks { + return 0 + } + return w.getInt32(w.reqUint8(cmdGetIdxRSSI, uint8(idx))) } -func (d *Device) SetDNS(which uint8, dns1 uint32, dns2 uint32) error { - defer d.spiChipDeselect() - if err := d.waitForChipSelect(); err != nil { - return err +func (w *wifinina) getNetworkSSID(idx int) string { + if idx < 0 || idx >= maxNetworks { + return "" } + return w.ssids[idx] +} - d.sendCmd(CmdSetDNSConfig, 3) - d.sendParam8(which, false) - d.sendParam32(dns1, false) - d.sendParam32(dns2, true) +func (w *wifinina) getReasonCode() uint8 { + return w.getUint8(w.req0(cmdGetReasonCode)) +} - _, err := d.waitRspCmd1(CmdSetDNSConfig) - if err != nil { - return err - } +// getTime is the time as a Unix timestamp +func (w *wifinina) getTime() uint32 { + return w.getUint32(w.req0(cmdGetTime)) +} - return nil +func (w *wifinina) getTemperature() float32 { + return w.getFloat32(w.req0(cmdGetTemperature)) } -func (d *Device) SetHostname(hostname string) error { - defer d.spiChipDeselect() - if err := d.waitForChipSelect(); err != nil { - return err +func (w *wifinina) setDebug(on bool) { + var v uint8 + if on { + v = 1 } + w.reqUint8(cmdSetDebug, v) +} - d.sendCmd(CmdSetHostname, 3) - d.sendParamStr(hostname, true) +func (w *wifinina) setNetwork(ssid string) { + w.reqStr(cmdSetNet, ssid) +} - d.padTo4(5 + len(hostname)) +func (w *wifinina) setPassphrase(ssid string, passphrase string) { - _, err := d.waitRspCmd1(CmdSetHostname) - if err != nil { - return err + if debugging(debugCmd) { + fmt.Printf(" [cmdSetPassphrase] ssid: %s, passphrase: ******\r\n", + ssid) } - return nil + // Dont' show passphrase in debug output + saveDebug := _debug + _debug = _debug & ^debugDetail + w.reqStr2(cmdSetPassphrase, ssid, passphrase) + _debug = saveDebug } -func (d *Device) SetPowerMode(mode uint8) error { - _, err := d.reqUint8(CmdSetPowerMode, mode) - return err +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 (d *Device) ScanNetworks() (uint8, error) { - return d.reqRspStr0(CmdScanNetworks, d.ssids[:]) +func (w *wifinina) setNetworkForAP(ssid string) { + w.reqStr(cmdSetAPNet, ssid) } -func (d *Device) StartScanNetworks() (uint8, error) { - return d.getUint8(d.req0(CmdStartScanNetworks)) +func (w *wifinina) setPassphraseForAP(ssid string, passphrase string) { + w.reqStr2(cmdSetAPPassphrase, ssid, passphrase) } -func (d *Device) PinMode(pin uint8, mode uint8) error { - _, err := d.req2Uint8(CmdSetPinMode, pin, mode) - return err +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 (d *Device) DigitalWrite(pin uint8, value uint8) error { - _, err := d.req2Uint8(CmdSetDigitalWrite, pin, value) - return err +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 (d *Device) AnalogWrite(pin uint8, value uint8) error { - _, err := d.req2Uint8(CmdSetAnalogWrite, pin, value) - return err +func (w *wifinina) setPowerMode(mode uint8) { + w.reqUint8(cmdSetPowerMode, mode) } -// ------------- End of public device interface ---------------------------- +func (w *wifinina) scanNetworks() uint8 { + return w.reqRspStr0(cmdScanNetworks, w.ssids[:]) +} -func (d *Device) getString(l uint8, err error) (string, error) { - if err != nil { - return "", err - } - return string(d.buf[0:l]), err +func (w *wifinina) startScanNetworks() uint8 { + return w.getUint8(w.req0(cmdStartScanNetworks)) } -func (d *Device) getUint8(l uint8, err error) (uint8, error) { - if err != nil { - return 0, err - } - if l != 1 { - if _debug { - println("expected length 1, was actually", l, "\r") - } - return 0, ErrUnexpectedLength +func (w *wifinina) PinMode(pin uint8, mode uint8) { + if debugging(debugCmd) { + fmt.Printf(" [cmdSetPinMode] pin: %d, mode: %d\r\n", pin, mode) } - return d.buf[0], err + w.req2Uint8(cmdSetPinMode, pin, mode) } -func (d *Device) getUint16(l uint8, err error) (uint16, error) { - if err != nil { - return 0, err +func (w *wifinina) DigitalWrite(pin uint8, value uint8) { + if debugging(debugCmd) { + fmt.Printf(" [cmdSetDigitialWrite] pin: %d, value: %d\r\n", pin, value) } - if l != 2 { - if _debug { - println("expected length 2, was actually", l, "\r") - } - return 0, ErrUnexpectedLength + 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] } - return binary.BigEndian.Uint16(d.buf[0:2]), err + w.faultf("expected length 1, was actually %d", l) + return 0 } -func (d *Device) getUint32(l uint8, err error) (uint32, error) { - if err != nil { - return 0, err +func (w *wifinina) getUint16(l uint8) uint16 { + if l == 2 { + return binary.BigEndian.Uint16(w.buf[0:2]) } - if l != 4 { - return 0, ErrUnexpectedLength + 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]) } - return binary.LittleEndian.Uint32(d.buf[0:4]), err + w.faultf("expected length 4, was actually %d", l) + return 0 } -func (d *Device) getInt32(l uint8, err error) (int32, error) { - i, err := d.getUint32(l, err) - return int32(i), err +func (w *wifinina) getInt32(l uint8) int32 { + return int32(w.getUint32(l)) } -func (d *Device) getFloat32(l uint8, err error) (float32, error) { - i, err := d.getUint32(l, err) - return float32(i), err +func (w *wifinina) getFloat32(l uint8) float32 { + return float32(w.getUint32(l)) } -func (d *Device) getMACAddress(l uint8, err error) (MACAddress, error) { - if err != nil { - return 0, err +func (w *wifinina) getMACAddress(l uint8) macAddress { + 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 } - if l != 6 { - return 0, ErrUnexpectedLength + w.faultf("expected length 6, was actually %d", l) + return macAddress{} +} + +func (w *wifinina) transfer(b byte) byte { + v, err := w.spi.Transfer(b) + if err != nil { + w.faultf("SPI.Transfer") + return 0 } - return MACAddress(binary.LittleEndian.Uint64(d.buf[0:8]) & 0xFFFFFFFFFFFF), err + 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 (d *Device) req0(cmd uint8) (l uint8, err error) { - if err := d.sendCmd0(cmd); err != nil { - return 0, err - } - return d.waitRspCmd1(cmd) +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 (d *Device) req1(cmd uint8) (l uint8, err error) { - return d.reqUint8(cmd, dummyData) +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 (d *Device) reqUint8(cmd uint8, data uint8) (l uint8, err error) { - if err := d.sendCmdPadded1(cmd, data); err != nil { - return 0, err - } - return d.waitRspCmd1(cmd) +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 (d *Device) req2Uint8(cmd, p1, p2 uint8) (l uint8, err error) { - if err := d.sendCmdPadded2(cmd, p1, p2); err != nil { - return 0, err - } - return d.waitRspCmd1(cmd) +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 (d *Device) reqStr(cmd uint8, p1 string) (uint8, error) { - if err := d.sendCmdStr(cmd, p1); err != nil { - return 0, err - } - return d.waitRspCmd1(cmd) +func (w *wifinina) reqStr(cmd uint8, p1 string) uint8 { + w.sendCmdStr(cmd, p1) + return w.waitRspCmd1(cmd) } -// reqStr sends a command to the device with 2 string parameters -func (d *Device) reqStr2(cmd uint8, p1 string, p2 string) (uint8, error) { - if err := d.sendCmdStr2(cmd, p1, p2); err != nil { - return 0, err - } - return d.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 (d *Device) reqRspStr0(cmd uint8, sl []string) (l uint8, err error) { - if err := d.sendCmd0(cmd); err != nil { - return 0, err - } - defer d.spiChipDeselect() - if err = d.waitForChipSelect(); err != nil { - return - } - return d.waitRspStr(cmd, sl) +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 (d *Device) reqRspStr1(cmd uint8, data uint8, sl []string) (uint8, error) { - if err := d.sendCmdPadded1(cmd, data); err != nil { - return 0, err - } - defer d.spiChipDeselect() - if err := d.waitForChipSelect(); err != nil { - return 0, err - } - return d.waitRspStr(cmd, sl) -} - -func (d *Device) sendCmd0(cmd uint8) error { - defer d.spiChipDeselect() - if err := d.waitForChipSelect(); err != nil { - return err - } - d.sendCmd(cmd, 0) - return nil +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 (d *Device) sendCmdPadded1(cmd uint8, data uint8) error { - defer d.spiChipDeselect() - if err := d.waitForChipSelect(); err != nil { - return err - } - d.sendCmd(cmd, 1) - d.sendParam8(data, true) - d.SPI.Transfer(dummyData) - d.SPI.Transfer(dummyData) - return nil +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 (d *Device) sendCmdPadded2(cmd, data1, data2 uint8) error { - defer d.spiChipDeselect() - if err := d.waitForChipSelect(); err != nil { - return err - } - l := d.sendCmd(cmd, 1) - l += d.sendParam8(data1, false) - l += d.sendParam8(data2, true) - d.SPI.Transfer(dummyData) - return nil +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 (d *Device) sendCmdStr(cmd uint8, p1 string) (err error) { - defer d.spiChipDeselect() - if err := d.waitForChipSelect(); err != nil { - return err - } - l := d.sendCmd(cmd, 1) - l += d.sendParamStr(p1, true) - d.padTo4(5 + len(p1)) - return nil +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 (d *Device) sendCmdStr2(cmd uint8, p1 string, p2 string) (err error) { - defer d.spiChipDeselect() - if err := d.waitForChipSelect(); err != nil { - return err - } - d.sendCmd(cmd, 2) - d.sendParamStr(p1, false) - d.sendParamStr(p2, true) - d.padTo4(6 + len(p1) + len(p2)) - return nil +func (w *wifinina) waitRspCmd1(cmd uint8) uint8 { + w.waitForChipReady() + w.spiChipSelect() + l := w.waitRspCmd(cmd, 1) + w.spiChipDeselect() + return l } -func (d *Device) waitRspCmd1(cmd uint8) (l uint8, err error) { - defer d.spiChipDeselect() - if err = d.waitForChipSelect(); err != nil { - return +func (w *wifinina) sendCmd(cmd uint8, numParam uint8) (l int) { + if debugging(debugDetail) { + fmt.Printf(" sendCmd: %02X %02X %02X", + cmdStart, cmd & ^(uint8(flagReply)), numParam) } - return d.waitRspCmd(cmd, 1) -} -func (d *Device) sendCmd(cmd uint8, numParam uint8) (l int) { - if _debug { - fmt.Printf( - "sendCmd: %02X %02X %02X", - CmdStart, cmd & ^(uint8(FlagReply)), numParam) - } l = 3 - d.SPI.Transfer(CmdStart) - d.SPI.Transfer(cmd & ^(uint8(FlagReply))) - d.SPI.Transfer(numParam) + w.transfer(cmdStart) + w.transfer(cmd & ^(uint8(flagReply))) + w.transfer(numParam) if numParam == 0 { - d.SPI.Transfer(CmdEnd) + w.transfer(cmdEnd) l += 1 - if _debug { - fmt.Printf(" %02X", CmdEnd) + if debugging(debugDetail) { + fmt.Printf(" %02X", cmdEnd) } } - if _debug { + + if debugging(debugDetail) { fmt.Printf(" (%d)\r\n", l) } return } -func (d *Device) sendParamLen16(p uint16) (l int) { - d.SPI.Transfer(uint8(p >> 8)) - d.SPI.Transfer(uint8(p & 0xFF)) - if _debug { - fmt.Printf(" %02X %02X", uint8(p>>8), uint8(p&0xFF)) +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 (d *Device) sendParamBuf(p []byte, isLastParam bool) (l int) { - if _debug { - println("sendParamBuf:") +func (w *wifinina) sendParamBuf(p []byte, isLastParam bool) (l int) { + if debugging(debugDetail) { + fmt.Printf(" sendParamBuf:") } - l += d.sendParamLen16(uint16(len(p))) + l += w.sendParamLen16(uint16(len(p))) for _, b := range p { - if _debug { + if debugging(debugDetail) { fmt.Printf(" %02X", b) } - d.SPI.Transfer(b) + w.transfer(b) l += 1 } if isLastParam { - if _debug { - fmt.Printf(" %02X", CmdEnd) + if debugging(debugDetail) { + fmt.Printf(" %02X", cmdEnd) } - d.SPI.Transfer(CmdEnd) + w.transfer(cmdEnd) l += 1 } - if _debug { + if debugging(debugDetail) { fmt.Printf(" (%d) \r\n", l) } return } -func (d *Device) sendParamStr(p string, isLastParam bool) (l int) { +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) - d.SPI.Transfer(uint8(l)) + w.transfer(uint8(l)) if l > 0 { - d.SPI.Tx([]byte(p), nil) + w.spi.Tx([]byte(p), nil) } if isLastParam { - d.SPI.Transfer(CmdEnd) + w.transfer(cmdEnd) l += 1 } return } -func (d *Device) sendParam8(p uint8, isLastParam bool) (l int) { - if _debug { - println("sendParam8:", p, "lastParam:", isLastParam, "\r") +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 - d.SPI.Transfer(1) - d.SPI.Transfer(p) + w.transfer(1) + w.transfer(p) if isLastParam { - d.SPI.Transfer(CmdEnd) + w.transfer(cmdEnd) l += 1 } return } -func (d *Device) sendParam16(p uint16, isLastParam bool) (l int) { +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 - d.SPI.Transfer(2) - d.SPI.Transfer(uint8(p >> 8)) - d.SPI.Transfer(uint8(p & 0xFF)) + w.transfer(2) + w.transfer(uint8(p >> 8)) + w.transfer(uint8(p & 0xFF)) if isLastParam { - d.SPI.Transfer(CmdEnd) + w.transfer(cmdEnd) l += 1 } return } -func (d *Device) sendParam32(p uint32, isLastParam bool) (l int) { +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 - d.SPI.Transfer(4) - d.SPI.Transfer(uint8(p >> 24)) - d.SPI.Transfer(uint8(p >> 16)) - d.SPI.Transfer(uint8(p >> 8)) - d.SPI.Transfer(uint8(p & 0xFF)) + 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 { - d.SPI.Transfer(CmdEnd) + w.transfer(cmdEnd) l += 1 } return } -func (d *Device) checkStartCmd() (bool, error) { - check, err := d.waitSpiChar(CmdStart) - if err != nil { - return false, err - } - if !check { - return false, ErrCheckStartCmd +func (w *wifinina) waitForChipReady() { + if debugging(debugDetail) { + fmt.Printf(" waitForChipReady\r\n") } - return true, nil -} -func (d *Device) waitForChipSelect() (err error) { - if err = d.waitForChipReady(); err == nil { - err = d.spiChipSelect() - } - return -} - -func (d *Device) waitForChipReady() error { - if _debug { - println("waitForChipReady()\r") - } - start := time.Now() - for time.Since(start) < 10*time.Second { - if !d.ACK.Get() { - return nil - } + for i := 0; w.ack.Get(); i++ { time.Sleep(1 * time.Millisecond) + if i == 10000 { + w.faultf("hung in waitForChipReady") + return + } } - return ErrTimeoutChipReady } -func (d *Device) spiChipSelect() error { - if _debug { - println("spiChipSelect()\r") +func (w *wifinina) spiChipSelect() { + if debugging(debugDetail) { + fmt.Printf(" spiChipSelect\r\n") } - d.CS.Low() + w.cs.Low() start := time.Now() for time.Since(start) < 5*time.Millisecond { - if d.ACK.Get() { - return nil + if w.ack.Get() { + return } time.Sleep(100 * time.Microsecond) } - return ErrTimeoutChipSelect + w.faultf("hung in spiChipSelect") } -func (d *Device) spiChipDeselect() { - if _debug { - println("spiChipDeselect\r") +func (w *wifinina) spiChipDeselect() { + if debugging(debugDetail) { + fmt.Printf(" spiChipDeselect\r\n") } - d.CS.High() + w.cs.High() } -func (d *Device) waitSpiChar(wait byte) (bool, error) { - var timeout = 1000 +func (w *wifinina) waitSpiChar(wait byte) { + + if debugging(debugDetail) { + fmt.Printf(" waitSpiChar: wait: %02X\r\n", wait) + } + var read byte - for first := true; first || (timeout > 0 && read != wait); timeout-- { - first = false - d.readParam(&read) - if read == CmdErr { - return false, ErrCmdErrorReceived + + for timeout := 1000; read != wait && timeout > 0; timeout-- { + w.readParam(&read) + if read == cmdErr { + w.faultf("cmdErr received, waiting for %d", wait) + return } } - if _debug && read != wait { - fmt.Printf("read: %02X, wait: %02X\r\n", read, wait) + + if read != wait { + w.faultf("timeout waiting for SPI char %02X\r\n", wait) } - return read == wait, nil } -func (d *Device) waitRspCmd(cmd uint8, np uint8) (l uint8, err error) { - if _debug { - println("waitRspCmd") +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 check bool + var data byte - if check, err = d.checkStartCmd(); !check { - return - } - if check = d.readAndCheckByte(cmd|FlagReply, &data); !check { + + w.waitSpiChar(cmdStart) + + if !w.readAndCheckByte(cmd|flagReply, &data) { + w.faultf("expected cmd %02X, read %02X", cmd, data) return } - if check = d.readAndCheckByte(np, &data); check { - d.readParam(&l) + + if w.readAndCheckByte(np, &data) { + + w.readParam(&l) for i := uint8(0); i < l; i++ { - d.readParam(&d.buf[i]) + w.readParam(&w.buf[i]) + } + + if !w.readAndCheckByte(cmdEnd, &data) { + w.faultf("expected cmdEnd, read %02X", data) } } - if !d.readAndCheckByte(CmdEnd, &data) { - err = ErrIncorrectSentinel - } + return } -func (d *Device) waitRspBuf16(cmd uint8, buf []byte) (l uint16, err error) { - if _debug { - println("waitRspBuf16") +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 check bool + var data byte - if check, err = d.checkStartCmd(); !check { - return - } - if check = d.readAndCheckByte(cmd|FlagReply, &data); !check { + + w.waitSpiChar(cmdStart) + + if !w.readAndCheckByte(cmd|flagReply, &data) { + w.faultf("expected cmd %02X, read %02X", cmd, data) return } - if check = d.readAndCheckByte(1, &data); check { - l, _ = d.readParamLen16() + + if w.readAndCheckByte(1, &data) { + l = w.readParamLen16() for i := uint16(0); i < l; i++ { - d.readParam(&buf[i]) + w.readParam(&buf[i]) + } + if !w.readAndCheckByte(cmdEnd, &data) { + w.faultf("expected cmdEnd, read %02X", data) } } - if !d.readAndCheckByte(CmdEnd, &data) { - err = ErrIncorrectSentinel - } + return } -func (d *Device) waitRspStr(cmd uint8, sl []string) (numRead uint8, err error) { - if _debug { - println("waitRspStr") +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 check bool + var data byte - if check, err = d.checkStartCmd(); !check { - return - } - if check = d.readAndCheckByte(cmd|FlagReply, &data); !check { + + w.waitSpiChar(cmdStart) + + if !w.readAndCheckByte(cmd|flagReply, &data) { + w.faultf("expected cmd %02X, read %02X", cmd, data) return } - numRead, _ = d.SPI.Transfer(dummyData) + + numRead = w.transfer(dummyData) if numRead == 0 { - return 0, ErrNoParamsReturned + w.faultf("waitRspStr numRead == 0") + return } + maxNumRead := uint8(len(sl)) for j, l := uint8(0), uint8(0); j < numRead; j++ { - d.readParam(&l) + w.readParam(&l) for i := uint8(0); i < l; i++ { - d.readParam(&d.buf[i]) + w.readParam(&w.buf[i]) } if j < maxNumRead { - sl[j] = string(d.buf[0:l]) - if _debug { - fmt.Printf("str %d (%d) - %08X\r\n", j, l, []byte(sl[j])) + 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 _debug { - println("str", j, "\"\"\r") + if debugging(debugDetail) { + fmt.Printf(" str: ", j, "\"\"\r") } sl[j] = "" } - if !d.readAndCheckByte(CmdEnd, &data) { - err = ErrIncorrectSentinel + + if !w.readAndCheckByte(cmdEnd, &data) { + w.faultf("expected cmdEnd, read %02X", data) + return } + if numRead > maxNumRead { numRead = maxNumRead } return } -func (d *Device) readAndCheckByte(check byte, read *byte) bool { - d.readParam(read) - return (*read == check) +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 (d *Device) readParamLen16() (v uint16, err error) { - if b, err := d.SPI.Transfer(0xFF); err == nil { - v |= uint16(b) << 8 - if b, err = d.SPI.Transfer(0xFF); err == nil { - v |= uint16(b) - } - } +func (w *wifinina) readParamLen16() (v uint16) { + b := w.transfer(0xFF) + v = uint16(b) << 8 + b = w.transfer(0xFF) + v |= uint16(b) return } -func (d *Device) readParam(b *byte) (err error) { - *b, err = d.SPI.Transfer(0xFF) - return +func (w *wifinina) readParam(b *byte) { + *b = w.transfer(0xFF) } -func (d *Device) addPadding(l int) { - if _debug { - println("addPadding", l, "\r") +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 _debug { - println("padding\r") + if debugging(debugDetail) { + fmt.Printf(" padding\r\n") } - d.SPI.Transfer(dummyData) + w.transfer(dummyData) } } -func (d *Device) padTo4(l int) { - if _debug { - println("padTo4", l, "\r") +func (w *wifinina) padTo4(l int) { + if debugging(debugDetail) { + fmt.Printf(" padTo4: l: %d\r\n", l) } for l%4 != 0 { - d.SPI.Transfer(dummyData) + if debugging(debugDetail) { + fmt.Printf(" padding\r\n") + } + w.transfer(dummyData) l++ } } From 9a05fc46548b68b42831667f6e9988e31308306e Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Fri, 10 Feb 2023 00:37:24 -0800 Subject: [PATCH 02/47] websocket/dial: remove work-around-code for no-RNG on rp2040 --- examples/net/websocket/dial/main.go | 30 ----------------------------- 1 file changed, 30 deletions(-) diff --git a/examples/net/websocket/dial/main.go b/examples/net/websocket/dial/main.go index 72be95749..c8cf2f00a 100644 --- a/examples/net/websocket/dial/main.go +++ b/examples/net/websocket/dial/main.go @@ -9,7 +9,6 @@ package main import ( - // "crypto/rand" "fmt" "log" "machine" @@ -31,35 +30,6 @@ func waitSerial() { } } -/* -func init() { - rand.Reader = &reader{} -} - -type reader struct {} - -func (r *reader) Read(b []byte) (n int, err error) { - if len(b) == 0 { - return - } - - var randomByte uint32 - for i := range b { - if i%4 == 0 { - randomByte, err = machine.GetRNG() - if err != nil { - return n, err - } - } else { - randomByte >>= 8 - } - b[i] = byte(randomByte) - } - - return len(b), nil -} -*/ - func main() { waitSerial() From 656fd2324adf5798f4b5a600c6eba77259988bc3 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Fri, 10 Feb 2023 13:17:34 -0800 Subject: [PATCH 03/47] Documentation cleanup --- netdev/netdev.go | 8 ++++++-- netdev/socket.go | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/netdev/netdev.go b/netdev/netdev.go index 9e12a785d..3c22c835b 100644 --- a/netdev/netdev.go +++ b/netdev/netdev.go @@ -1,4 +1,6 @@ -// Netdev is TinyGo's network device driver model. +// Netdev is TinyGo's network device driver model. TinyGo's "net" package +// interfaces to the netdev driver directly to provide TCPConn, UDPConn, +// and TLSConn socket connections. package netdev @@ -96,7 +98,9 @@ type Event int // NetNotify network events const ( + // The device's network connection is now UP EventNetUp Event = iota + // The device's network connection is now DOWN EventNetDown ) @@ -111,7 +115,7 @@ type Netdever interface { // NetConnect device to IP network NetConnect() error - // NetDisconnect from IP network + // NetDisconnect device from IP network NetDisconnect() // NetNotify to register callback for network events diff --git a/netdev/socket.go b/netdev/socket.go index 3b3d2dd9f..2f3e112a4 100644 --- a/netdev/socket.go +++ b/netdev/socket.go @@ -1,3 +1,5 @@ +// Netdev socket interface + package netdev import ( From 9e7006d6c4f8ec07390b5b9a97bf296892f3540d Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Fri, 10 Feb 2023 14:21:13 -0800 Subject: [PATCH 04/47] add raw socket example --- examples/net/rawsocket/main.go | 101 ++++++++++++++++++++++++++++ examples/net/rawsocket/rtl8720dn.go | 37 ++++++++++ examples/net/rawsocket/wifinina.go | 41 +++++++++++ 3 files changed, 179 insertions(+) create mode 100644 examples/net/rawsocket/main.go create mode 100644 examples/net/rawsocket/rtl8720dn.go create mode 100644 examples/net/rawsocket/wifinina.go diff --git a/examples/net/rawsocket/main.go b/examples/net/rawsocket/main.go new file mode 100644 index 000000000..92f6e04f4 --- /dev/null +++ b/examples/net/rawsocket/main.go @@ -0,0 +1,101 @@ +// This example opens a TCP connection and sends some data, for the purpose of +// testing speed and connectivity. +// +// You can open a server to accept connections from this program using: +// +// nc -lk 8080 + +package main + +import ( + "bytes" + "fmt" + "log" + "machine" + "strconv" + "strings" + "time" + + "tinygo.org/x/drivers/netdev" +) + +var ( + ssid string + pass string + addr string = "10.0.0.100:8080" +) + +var buf = &bytes.Buffer{} + +func main() { + + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + for { + sendBatch() + time.Sleep(500 * time.Millisecond) + } +} + +func sendBatch() { + + parts := strings.Split(addr, ":") + ip := netdev.ParseIP(parts[0]) + port, _ := strconv.Atoi(parts[1]) + sockAddr := netdev.NewSockAddr("", netdev.Port(port), ip) + + // make TCP connection + message("---------------\r\nDialing TCP connection") + sock, _ := dev.Socket(netdev.AF_INET, netdev.SOCK_STREAM, netdev.IPPROTO_TCP) + err := dev.Connect(sock, sockAddr) + for ; err != nil; err = dev.Connect(sock, sockAddr) { + message(err.Error()) + time.Sleep(5 * time.Second) + } + + n := 0 + w := 0 + start := time.Now() + + // send data + message("Sending data") + + for i := 0; i < 1000; i++ { + buf.Reset() + fmt.Fprint(buf, + "\r---------------------------- i == ", i, " ----------------------------"+ + "\r---------------------------- i == ", i, " ----------------------------") + if w, err = dev.Send(sock, buf.Bytes(), 0, 0); err != nil { + println("error:", err.Error(), "\r") + break + } + n += w + } + + buf.Reset() + ms := time.Now().Sub(start).Milliseconds() + fmt.Fprint(buf, "\nWrote ", n, " bytes in ", ms, " ms\r\n") + message(buf.String()) + + if _, err := dev.Send(sock, buf.Bytes(), 0, 0); err != nil { + println("error:", err.Error(), "\r") + } + + println("Disconnecting TCP...") + dev.Close(sock) +} + +func message(msg string) { + println(msg, "\r") +} + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} diff --git a/examples/net/rawsocket/rtl8720dn.go b/examples/net/rawsocket/rtl8720dn.go new file mode 100644 index 000000000..a6dcb77fa --- /dev/null +++ b/examples/net/rawsocket/rtl8720dn.go @@ -0,0 +1,37 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/rawsocket/wifinina.go b/examples/net/rawsocket/wifinina.go new file mode 100644 index 000000000..1011d75cf --- /dev/null +++ b/examples/net/rawsocket/wifinina.go @@ -0,0 +1,41 @@ +//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal arduino_nano33 nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} From 11e7534f3917ce1e8e1d6c73410a82beadf9f22c Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Mon, 13 Feb 2023 13:28:36 -0800 Subject: [PATCH 05/47] fix header comment on raw socket test --- examples/net/rawsocket/main.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/net/rawsocket/main.go b/examples/net/rawsocket/main.go index 92f6e04f4..d502a9499 100644 --- a/examples/net/rawsocket/main.go +++ b/examples/net/rawsocket/main.go @@ -1,5 +1,4 @@ -// This example opens a TCP connection and sends some data, for the purpose of -// testing speed and connectivity. +// This example opens a TCP connection and sends some data using raw netdev sockets. // // You can open a server to accept connections from this program using: // From e101de2bd329368491fe7c741b9ce6ed5b5cd947 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Mon, 13 Feb 2023 13:39:58 -0800 Subject: [PATCH 06/47] gofmt -l -w -s --- espat/espat.go | 12 ++++++------ netdev/netdev.go | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/espat/espat.go b/espat/espat.go index c24731f76..e949db96e 100644 --- a/espat/espat.go +++ b/espat/espat.go @@ -23,8 +23,8 @@ package espat // import "tinygo.org/x/drivers/espat" import ( "errors" "fmt" - "strconv" "machine" + "strconv" "strings" "time" @@ -48,21 +48,21 @@ type Config struct { } type Device struct { - cfg *Config + cfg *Config uart *machine.UART // command responses that come back from the ESP8266/ESP32 response []byte // data received from a TCP/UDP connection forwarded by the ESP8266/ESP32 - socketdata []byte - socketInUse bool + socketdata []byte + socketInUse bool socketProtocol netdev.Protocol socketLaddr netdev.SockAddr } func New(cfg *Config) *Device { d := Device{ - cfg: cfg, - response: make([]byte, 512), + cfg: cfg, + response: make([]byte, 512), socketdata: make([]byte, 0, 1024), } return &d diff --git a/netdev/netdev.go b/netdev/netdev.go index 3c22c835b..4527c62f9 100644 --- a/netdev/netdev.go +++ b/netdev/netdev.go @@ -99,7 +99,7 @@ type Event int // NetNotify network events const ( // The device's network connection is now UP - EventNetUp Event = iota + EventNetUp Event = iota // The device's network connection is now DOWN EventNetDown ) From cdba1aa572e2a92fc85ab7e8326522cae9a1957b Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Mon, 13 Feb 2023 14:55:42 -0800 Subject: [PATCH 07/47] README updates: porting applications from Go's "net" package --- netdev/README.md | 93 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 29 deletions(-) diff --git a/netdev/README.md b/netdev/README.md index 980a21d1f..b97a7166e 100644 --- a/netdev/README.md +++ b/netdev/README.md @@ -34,47 +34,78 @@ Here the netdev is the entire stack, accessing hardware on the bottom and servin ## Porting Applications from Go "net" -Ideally, TinyGo's "net" package would just be Go's "net" package and applications using "net" would just work, as-is. Unfortunately, Go's "net" can't fully be ported to TinyGo, so TinyGo's "net" is a subset of Go's. Hopefully, for the embedded space, the subset is sufficient for most needs. +Ideally, TinyGo's "net" package would just be Go's "net" package and applications using "net" would just work, as-is. Unfortunately, Go's "net" can't fully be ported to TinyGo, so TinyGo's "net" is a subset of Go's. To view TinyGo's "net" package exports, use ```go doc ./net```, ```go doc ./net/http```, etc. For the most part, Go's "net" documentation applies to TinyGo's "net". There are a few features excluded during the porting process, in particular: - No IPv6 support - No HTTP/2 support - HTTP client request can't be reused -- No multipart form support -- No TLS support for HTTP servers +- No TLS support for HTTP servers (no https servers) - No DualStack support -Applications using Go's "net" package will need a few (minor) modifications to work with TinyGo's net package. +Applications using Go's "net" package will need a few setup modifications to work with TinyGo's "net" package. -### Step 1: Load Netdev +### Step 1: Create the netdev for your target device. -#### Option 1: +The available netdev are: -Import netdev package to load the netdev driver. Import only for side effects using leading underscore. +- wifinina: SPI to ESP32 WiFi co-controller running Arduino WiFiNINA firmware +- rtl8720dn: UART to RealTek WiFi rtl8720dn co-controller +- espat: UART to ESP32/ESP8266 WiFi co-controller running Espressif AT firmware + +This example configures and creates a wifinina netdev using New(). -```go -import _ "tinygo.org/x/drivers/netdev" ``` +import "tinygo.org/x/drivers/wifinina" -This will select the netdev driver for the target machine using build tags. For example, when flashing to target Arduino Nano RP2040 Connect, the build tag nano_rp2040 will select the "Wifinina" netdev driver. +func main() { + cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} + dev := wifinina.New(&cfg) + ... +} +``` + +The Config structure is netdev-specific; consult the netdev package for Config details. In this case, the WiFi credentials are passed. -#### Option 2: +### Step 2: Hook the netdev into the "net" package -Manually load the netdev driver. Import the driver directly, and then call net.UseNetdev to load the driver. e.g.: +Tell the "net" package to use the netdev. Continuing with the wifinina example: ``` -import "tinygo.org/x/drivers/netdev/wifinina" +import "tinygo.org/x/drivers/netdev" +import "tinygo.org/x/drivers/wifinina" func main() { - net.UseNetdev(wifinina.New("SSID", "PASSPHRASE")) + cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} + dev := wifinina.New(&cfg) + netdev.Use(dev) ... } ``` -### Step 2: Connect to the Network +### Step 3: Connect to an IP Network -Call net.Connect() to connect the device to an IP network, via Wifi, cellular, Ethernet, etc. Make this call first, before any net.* or http.* or tls.* calls. +Before the "net" package is fully functional, connect the netdev to an underlying IP network. For example, a WiFi netdev would connect to a WiFi access point or become a WiFi access point; either way, once connected, the netdev has a station IP address and is connected on the IP network. + +Call dev.NetConnect() to connect the device to an IP network. Call dev.NetDisconnect() to disconnect. Continuing example: + +``` +import "tinygo.org/x/drivers/netdev" +import "tinygo.org/x/drivers/wifinina" + +func main() { + cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} + dev := wifinina.New(&cfg) + netdev.Use(dev) + + dev.NetConnect() + + // "net" package calls here + + dev.NetDisconnect() +} +``` Here is a simple http server listening on port :8080, before and after porting from Go "net/http": @@ -83,17 +114,17 @@ Here is a simple http server listening on port :8080, before and after porting f package main import ( - "fmt" - "net/http" + "fmt" + "net/http" ) func main() { - http.HandleFunc("/", HelloServer) - http.ListenAndServe(":8080", nil) + http.HandleFunc("/", HelloServer) + http.ListenAndServe(":8080", nil) } func HelloServer(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) + fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) } ``` @@ -102,21 +133,25 @@ func HelloServer(w http.ResponseWriter, r *http.Request) { package main import ( - "fmt" - "net" - "net/http" + "fmt" + "net/http" - _ "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" ) func main() { - net.Connect(nil) - http.HandleFunc("/", HelloServer) - http.ListenAndServe(":8080", nil) + cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} + dev := wifinina.New(&cfg) + netdev.Use(dev) + dev.NetConnect() + + http.HandleFunc("/", HelloServer) + http.ListenAndServe(":8080", nil) } func HelloServer(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) + fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) } ``` From 1fd33c2c69817c2ee7065aa50e34f40188460eba Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Mon, 13 Feb 2023 15:08:37 -0800 Subject: [PATCH 08/47] README: add raw socket section and example --- netdev/README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/netdev/README.md b/netdev/README.md index b97a7166e..57624053b 100644 --- a/netdev/README.md +++ b/netdev/README.md @@ -4,6 +4,7 @@ - [Overview](#overview) - [Porting Applications from Go "net"](#porting-applications-from-go-net) +- [Using Raw Sockets](#using-raw-sockets) - [Writing a New Driver](#writing-a-new-driver) ## Overview @@ -155,6 +156,37 @@ func HelloServer(w http.ResponseWriter, r *http.Request) { } ``` +## Using Raw Sockets + +A netdev implements the Socketer interface so an application can make raw socket calls, bypassing the "net" package. + +Here is a simple TCP application using raw sockets: + +```go +package main + +import ( + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +func main() { + cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} + dev := wifinina.New(&cfg) + netdev.Use(dev) + dev.NetConnect() + + sock, _ := dev.Socket(netdev.AF_INET, netdev.SOCK_STREAM, netdev.IPPROTO_TCP) + + sockAddr := netdev.NewSockAddr("", netdev.Port(8080), netdev.ParseIP("10.0.0.100") + dev.Connect(sock, sockAddr) + + dev.Send(sock, []bytes("hello"), 0, 0) + + dev.Close(sock) +} +``` + ## Writing a New Driver :bulb: A reference netdev driver is the Wifinina driver (netdev/wifinina). From 306ad05d916e213f194ced0fd3ee3e667bc9ef1f Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Mon, 13 Feb 2023 22:14:49 -0800 Subject: [PATCH 09/47] rtl8720dn: pull in latest rpc.go from sago35/go-erpcgen --- rtl8720dn/rpc.go | 150 +++++++++++++++++++++++------------------------ 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/rtl8720dn/rpc.go b/rtl8720dn/rpc.go index fc7b2f833..b554e8b0f 100644 --- a/rtl8720dn/rpc.go +++ b/rtl8720dn/rpc.go @@ -464,14 +464,14 @@ func (r *rtl8720dn) rpc_le_bond_delete_by_idx(idx uint8) RPC_T_GAP_CAUSE { return result } -func (r *rtl8720dn) rpc_le_bond_delete_by_bd(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) RPC_T_GAP_CAUSE { +func (r *rtl8720dn) rpc_le_bond_delete_by_bd(bd_addr []uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_delete_by_bd()\r\n") } msg := startWriteMessage(0x00, 0x04, 0x0D, uint32(r.seq)) - // bd_addr : in uint8 - msg = append(msg, byte(bd_addr>>0)) + // bd_addr : in []uint8 (6) + msg = append(msg, bd_addr...) // bd_type : in RPC_T_GAP_REMOTE_ADDR_TYPE msg = append(msg, byte(bd_type>>0)) msg = append(msg, byte(bd_type>>8)) @@ -630,7 +630,7 @@ func (r *rtl8720dn) rpc_le_get_gap_param(param RPC_T_GAP_LE_PARAM_TYPE, value [] return result } -func (r *rtl8720dn) rpc_le_modify_white_list(operation RPC_T_GAP_WHITE_LIST_OP, bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) RPC_T_GAP_CAUSE { +func (r *rtl8720dn) rpc_le_modify_white_list(operation RPC_T_GAP_WHITE_LIST_OP, bd_addr []uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_modify_white_list()\r\n") } @@ -641,8 +641,8 @@ func (r *rtl8720dn) rpc_le_modify_white_list(operation RPC_T_GAP_WHITE_LIST_OP, msg = append(msg, byte(operation>>8)) msg = append(msg, byte(operation>>16)) msg = append(msg, byte(operation>>24)) - // bd_addr : in uint8 - msg = append(msg, byte(bd_addr>>0)) + // bd_addr : in []uint8 (6) + msg = append(msg, bd_addr...) // bd_type : in RPC_T_GAP_REMOTE_ADDR_TYPE msg = append(msg, byte(bd_type>>0)) msg = append(msg, byte(bd_type>>8)) @@ -661,7 +661,7 @@ func (r *rtl8720dn) rpc_le_modify_white_list(operation RPC_T_GAP_WHITE_LIST_OP, return result } -func (r *rtl8720dn) rpc_le_gen_rand_addr(rand_addr_type RPC_T_GAP_RAND_ADDR_TYPE, random_bd *uint8) RPC_T_GAP_CAUSE { +func (r *rtl8720dn) rpc_le_gen_rand_addr(rand_addr_type RPC_T_GAP_RAND_ADDR_TYPE, random_bd []uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_gen_rand_addr()\r\n") } @@ -677,9 +677,9 @@ func (r *rtl8720dn) rpc_le_gen_rand_addr(rand_addr_type RPC_T_GAP_RAND_ADDR_TYPE r.read() widx := 8 - // random_bd : out uint8 - *random_bd = payload[widx] - widx += 1 + // random_bd : out []uint8 (6) + copy(random_bd, payload[widx:widx+6]) + widx += 6 var result RPC_T_GAP_CAUSE result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) @@ -688,14 +688,14 @@ func (r *rtl8720dn) rpc_le_gen_rand_addr(rand_addr_type RPC_T_GAP_RAND_ADDR_TYPE return result } -func (r *rtl8720dn) rpc_le_set_rand_addr(random_bd uint8) RPC_T_GAP_CAUSE { +func (r *rtl8720dn) rpc_le_set_rand_addr(random_bd []uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_set_rand_addr()\r\n") } msg := startWriteMessage(0x00, 0x05, 0x08, uint32(r.seq)) - // random_bd : in uint8 - msg = append(msg, byte(random_bd>>0)) + // random_bd : in []uint8 (6) + msg = append(msg, random_bd...) r.performRequest(msg) @@ -709,14 +709,14 @@ func (r *rtl8720dn) rpc_le_set_rand_addr(random_bd uint8) RPC_T_GAP_CAUSE { return result } -func (r *rtl8720dn) rpc_le_cfg_local_identity_address(addr uint8, ident_addr_type RPC_T_GAP_IDENT_ADDR_TYPE) RPC_T_GAP_CAUSE { +func (r *rtl8720dn) rpc_le_cfg_local_identity_address(addr []uint8, ident_addr_type RPC_T_GAP_IDENT_ADDR_TYPE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_cfg_local_identity_address()\r\n") } msg := startWriteMessage(0x00, 0x05, 0x09, uint32(r.seq)) - // addr : in uint8 - msg = append(msg, byte(addr>>0)) + // addr : in []uint8 (6) + msg = append(msg, addr...) // ident_addr_type : in RPC_T_GAP_IDENT_ADDR_TYPE msg = append(msg, byte(ident_addr_type>>0)) msg = append(msg, byte(ident_addr_type>>8)) @@ -1166,7 +1166,7 @@ func (r *rtl8720dn) rpc_le_scan_stop() RPC_T_GAP_CAUSE { return result } -func (r *rtl8720dn) rpc_le_scan_info_filter(enable bool, offset uint8, length uint8, p_filter uint8) bool { +func (r *rtl8720dn) rpc_le_scan_info_filter(enable bool, offset uint8, length uint8, p_filter []uint8) bool { if r.debug { fmt.Printf("rpc_le_scan_info_filter()\r\n") } @@ -1182,8 +1182,8 @@ func (r *rtl8720dn) rpc_le_scan_info_filter(enable bool, offset uint8, length ui msg = append(msg, byte(offset>>0)) // length : in uint8 msg = append(msg, byte(length>>0)) - // p_filter : in uint8 - msg = append(msg, byte(p_filter>>0)) + // p_filter : in []uint8 (31) + msg = append(msg, p_filter...) r.performRequest(msg) @@ -1253,7 +1253,7 @@ func (r *rtl8720dn) rpc_le_get_conn_info(conn_id uint8, p_conn_info RPC_T_GAP_CO return result } -func (r *rtl8720dn) rpc_le_get_conn_addr(conn_id uint8, bd_addr *uint8, bd_type *uint8) bool { +func (r *rtl8720dn) rpc_le_get_conn_addr(conn_id uint8, bd_addr []uint8, bd_type *uint8) bool { if r.debug { fmt.Printf("rpc_le_get_conn_addr()\r\n") } @@ -1266,9 +1266,9 @@ func (r *rtl8720dn) rpc_le_get_conn_addr(conn_id uint8, bd_addr *uint8, bd_type r.read() widx := 8 - // bd_addr : out uint8 - *bd_addr = payload[widx] - widx += 1 + // bd_addr : out []uint8 (6) + copy(bd_addr, payload[widx:widx+6]) + widx += 6 // bd_type : out uint8 *bd_type = payload[widx] widx += 1 @@ -1280,14 +1280,14 @@ func (r *rtl8720dn) rpc_le_get_conn_addr(conn_id uint8, bd_addr *uint8, bd_type return result } -func (r *rtl8720dn) rpc_le_get_conn_id(bd_addr uint8, bd_type uint8, p_conn_id *uint8) bool { +func (r *rtl8720dn) rpc_le_get_conn_id(bd_addr []uint8, bd_type uint8, p_conn_id *uint8) bool { if r.debug { fmt.Printf("rpc_le_get_conn_id()\r\n") } msg := startWriteMessage(0x00, 0x09, 0x04, uint32(r.seq)) - // bd_addr : in uint8 - msg = append(msg, byte(bd_addr>>0)) + // bd_addr : in []uint8 (6) + msg = append(msg, bd_addr...) // bd_type : in uint8 msg = append(msg, byte(bd_type>>0)) @@ -1470,7 +1470,7 @@ func (r *rtl8720dn) rpc_le_set_conn_param(conn_type RPC_T_GAP_CONN_PARAM_TYPE, p return result } -func (r *rtl8720dn) rpc_le_connect(init_phys uint8, remote_bd uint8, remote_bd_type RPC_T_GAP_REMOTE_ADDR_TYPE, local_bd_type RPC_T_GAP_LOCAL_ADDR_TYPE, scan_timeout uint16) RPC_T_GAP_CAUSE { +func (r *rtl8720dn) rpc_le_connect(init_phys uint8, remote_bd []uint8, remote_bd_type RPC_T_GAP_REMOTE_ADDR_TYPE, local_bd_type RPC_T_GAP_LOCAL_ADDR_TYPE, scan_timeout uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_connect()\r\n") } @@ -1478,8 +1478,8 @@ func (r *rtl8720dn) rpc_le_connect(init_phys uint8, remote_bd uint8, remote_bd_t // init_phys : in uint8 msg = append(msg, byte(init_phys>>0)) - // remote_bd : in uint8 - msg = append(msg, byte(remote_bd>>0)) + // remote_bd : in []uint8 (6) + msg = append(msg, remote_bd...) // remote_bd_type : in RPC_T_GAP_REMOTE_ADDR_TYPE msg = append(msg, byte(remote_bd_type>>0)) msg = append(msg, byte(remote_bd_type>>8)) @@ -1633,14 +1633,14 @@ func (r *rtl8720dn) rpc_flash_load_local_appearance(p_data RPC_T_LOCAL_APPEARANC return result } -func (r *rtl8720dn) rpc_le_find_key_entry(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) RPC_T_LE_KEY_ENTRY { +func (r *rtl8720dn) rpc_le_find_key_entry(bd_addr []uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) RPC_T_LE_KEY_ENTRY { if r.debug { fmt.Printf("rpc_le_find_key_entry()\r\n") } msg := startWriteMessage(0x00, 0x0A, 0x05, uint32(r.seq)) - // bd_addr : in uint8 - msg = append(msg, byte(bd_addr>>0)) + // bd_addr : in []uint8 (6) + msg = append(msg, bd_addr...) // bd_type : in RPC_T_GAP_REMOTE_ADDR_TYPE msg = append(msg, byte(bd_type>>0)) msg = append(msg, byte(bd_type>>8)) @@ -1731,14 +1731,14 @@ func (r *rtl8720dn) rpc_le_get_high_priority_bond() RPC_T_LE_KEY_ENTRY { return result } -func (r *rtl8720dn) rpc_le_set_high_priority_bond(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) bool { +func (r *rtl8720dn) rpc_le_set_high_priority_bond(bd_addr []uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) bool { if r.debug { fmt.Printf("rpc_le_set_high_priority_bond()\r\n") } msg := startWriteMessage(0x00, 0x0A, 0x0A, uint32(r.seq)) - // bd_addr : in uint8 - msg = append(msg, byte(bd_addr>>0)) + // bd_addr : in []uint8 (6) + msg = append(msg, bd_addr...) // bd_type : in RPC_T_GAP_REMOTE_ADDR_TYPE msg = append(msg, byte(bd_type>>0)) msg = append(msg, byte(bd_type>>8)) @@ -1757,16 +1757,16 @@ func (r *rtl8720dn) rpc_le_set_high_priority_bond(bd_addr uint8, bd_type RPC_T_G return result } -func (r *rtl8720dn) rpc_le_resolve_random_address(unresolved_addr uint8, resolved_addr *uint8, resolved_addr_type RPC_T_GAP_IDENT_ADDR_TYPE) bool { +func (r *rtl8720dn) rpc_le_resolve_random_address(unresolved_addr []uint8, resolved_addr []uint8, resolved_addr_type RPC_T_GAP_IDENT_ADDR_TYPE) bool { if r.debug { fmt.Printf("rpc_le_resolve_random_address()\r\n") } msg := startWriteMessage(0x00, 0x0A, 0x0B, uint32(r.seq)) - // unresolved_addr : in uint8 - msg = append(msg, byte(unresolved_addr>>0)) - // resolved_addr : inout uint8 - msg = append(msg, byte(*resolved_addr>>0)) + // unresolved_addr : in []uint8 (6) + msg = append(msg, unresolved_addr...) + // resolved_addr : inout []uint8 (6) + msg = append(msg, resolved_addr...) // resolved_addr_type : inout RPC_T_GAP_IDENT_ADDR_TYPE msg = append(msg, byte(resolved_addr_type>>0)) msg = append(msg, byte(resolved_addr_type>>8)) @@ -1777,9 +1777,9 @@ func (r *rtl8720dn) rpc_le_resolve_random_address(unresolved_addr uint8, resolve r.read() widx := 8 - // resolved_addr : inout uint8 - *resolved_addr = payload[widx] - widx += 1 + // resolved_addr : inout []uint8 (6) + copy(resolved_addr, payload[widx:widx+6]) + widx += 6 // resolved_addr_type : inout RPC_T_GAP_IDENT_ADDR_TYPE // not impl (a.Size() > 0) @@ -1816,14 +1816,14 @@ func (r *rtl8720dn) rpc_le_get_cccd_data(p_entry RPC_T_LE_KEY_ENTRY, p_data RPC_ return result } -func (r *rtl8720dn) rpc_le_gen_bond_dev(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE, local_bd_type RPC_T_GAP_LOCAL_ADDR_TYPE, local_ltk []byte, key_type RPC_T_LE_KEY_TYPE, p_cccd RPC_T_LE_CCCD) bool { +func (r *rtl8720dn) rpc_le_gen_bond_dev(bd_addr []uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE, local_bd_type RPC_T_GAP_LOCAL_ADDR_TYPE, local_ltk []byte, key_type RPC_T_LE_KEY_TYPE, p_cccd RPC_T_LE_CCCD) bool { if r.debug { fmt.Printf("rpc_le_gen_bond_dev()\r\n") } msg := startWriteMessage(0x00, 0x0A, 0x0D, uint32(r.seq)) - // bd_addr : in uint8 - msg = append(msg, byte(bd_addr>>0)) + // bd_addr : in []uint8 (6) + msg = append(msg, bd_addr...) // bd_type : in RPC_T_GAP_REMOTE_ADDR_TYPE msg = append(msg, byte(bd_type>>0)) msg = append(msg, byte(bd_type>>8)) @@ -2043,7 +2043,7 @@ func (r *rtl8720dn) rpc_client_by_uuid_srv_discovery(conn_id uint8, client_id ui return result } -func (r *rtl8720dn) rpc_client_by_uuid128_srv_discovery(conn_id uint8, client_id uint8, p_uuid128 uint8) RPC_T_GAP_CAUSE { +func (r *rtl8720dn) rpc_client_by_uuid128_srv_discovery(conn_id uint8, client_id uint8, p_uuid128 []uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_by_uuid128_srv_discovery()\r\n") } @@ -2053,8 +2053,8 @@ func (r *rtl8720dn) rpc_client_by_uuid128_srv_discovery(conn_id uint8, client_id msg = append(msg, byte(conn_id>>0)) // client_id : in uint8 msg = append(msg, byte(client_id>>0)) - // p_uuid128 : in uint8 - msg = append(msg, byte(p_uuid128>>0)) + // p_uuid128 : in []uint8 (16) + msg = append(msg, p_uuid128...) r.performRequest(msg) @@ -2158,7 +2158,7 @@ func (r *rtl8720dn) rpc_client_by_uuid_char_discovery(conn_id uint8, client_id u return result } -func (r *rtl8720dn) rpc_client_by_uuid128_char_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16, p_uuid128 uint8) RPC_T_GAP_CAUSE { +func (r *rtl8720dn) rpc_client_by_uuid128_char_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16, p_uuid128 []uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_by_uuid128_char_discovery()\r\n") } @@ -2174,8 +2174,8 @@ func (r *rtl8720dn) rpc_client_by_uuid128_char_discovery(conn_id uint8, client_i // end_handle : in uint16 msg = append(msg, byte(end_handle>>0)) msg = append(msg, byte(end_handle>>8)) - // p_uuid128 : in uint8 - msg = append(msg, byte(p_uuid128>>0)) + // p_uuid128 : in []uint8 (16) + msg = append(msg, p_uuid128...) r.performRequest(msg) @@ -2244,7 +2244,7 @@ func (r *rtl8720dn) rpc_client_attr_read(conn_id uint8, client_id uint8, handle return result } -func (r *rtl8720dn) rpc_client_attr_read_using_uuid(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16, uuid16 uint16, p_uuid128 uint8) RPC_T_GAP_CAUSE { +func (r *rtl8720dn) rpc_client_attr_read_using_uuid(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16, uuid16 uint16, p_uuid128 []uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_attr_read_using_uuid()\r\n") } @@ -2263,8 +2263,8 @@ func (r *rtl8720dn) rpc_client_attr_read_using_uuid(conn_id uint8, client_id uin // uuid16 : in uint16 msg = append(msg, byte(uuid16>>0)) msg = append(msg, byte(uuid16>>8)) - // p_uuid128 : in uint8 - msg = append(msg, byte(p_uuid128>>0)) + // p_uuid128 : in []uint8 (16) + msg = append(msg, p_uuid128...) r.performRequest(msg) @@ -2354,14 +2354,14 @@ func (r *rtl8720dn) rpc_ble_server_init(num uint8) bool { return result } -func (r *rtl8720dn) rpc_ble_create_service(uuid uint8, uuid_length uint8, is_primary bool) uint8 { +func (r *rtl8720dn) rpc_ble_create_service(uuid []uint8, uuid_length uint8, is_primary bool) uint8 { if r.debug { fmt.Printf("rpc_ble_create_service()\r\n") } msg := startWriteMessage(0x00, 0x0C, 0x02, uint32(r.seq)) - // uuid : in uint8 - msg = append(msg, byte(uuid>>0)) + // uuid : in []uint8 (16) + msg = append(msg, uuid...) // uuid_length : in uint8 msg = append(msg, byte(uuid_length>>0)) // is_primary : in bool @@ -2446,7 +2446,7 @@ func (r *rtl8720dn) rpc_ble_get_servie_handle(app_id uint8) uint8 { return result } -func (r *rtl8720dn) rpc_ble_create_char(app_id uint8, uuid uint8, uuid_length uint8, properties uint8, permissions uint32) uint16 { +func (r *rtl8720dn) rpc_ble_create_char(app_id uint8, uuid []uint8, uuid_length uint8, properties uint8, permissions uint32) uint16 { if r.debug { fmt.Printf("rpc_ble_create_char()\r\n") } @@ -2454,8 +2454,8 @@ func (r *rtl8720dn) rpc_ble_create_char(app_id uint8, uuid uint8, uuid_length ui // app_id : in uint8 msg = append(msg, byte(app_id>>0)) - // uuid : in uint8 - msg = append(msg, byte(uuid>>0)) + // uuid : in []uint8 (16) + msg = append(msg, uuid...) // uuid_length : in uint8 msg = append(msg, byte(uuid_length>>0)) // properties : in uint8 @@ -2478,7 +2478,7 @@ func (r *rtl8720dn) rpc_ble_create_char(app_id uint8, uuid uint8, uuid_length ui return result } -func (r *rtl8720dn) rpc_ble_create_desc(app_id uint8, char_handle uint16, uuid uint8, uuid_length uint8, flags uint8, permissions uint32, value_length uint16, p_value []byte) uint16 { +func (r *rtl8720dn) rpc_ble_create_desc(app_id uint8, char_handle uint16, uuid []uint8, uuid_length uint8, flags uint8, permissions uint32, value_length uint16, p_value []byte) uint16 { if r.debug { fmt.Printf("rpc_ble_create_desc()\r\n") } @@ -2489,8 +2489,8 @@ func (r *rtl8720dn) rpc_ble_create_desc(app_id uint8, char_handle uint16, uuid u // char_handle : in uint16 msg = append(msg, byte(char_handle>>0)) msg = append(msg, byte(char_handle>>8)) - // uuid : in uint8 - msg = append(msg, byte(uuid>>0)) + // uuid : in []uint8 (16) + msg = append(msg, uuid...) // uuid_length : in uint8 msg = append(msg, byte(uuid_length>>0)) // flags : in uint8 @@ -3026,7 +3026,7 @@ func (r *rtl8720dn) rpc_wifi_get_mac_address(mac []uint8) int32 { r.read() widx := 8 - // mac : out []uint8 + // mac : out []uint8 (18) copy(mac, payload[widx:widx+18]) widx += 18 @@ -3150,7 +3150,7 @@ func (r *rtl8720dn) rpc_wifi_get_associated_client_list(client_list_buffer []byt return result } -func (r *rtl8720dn) rpc_wifi_get_ap_bssid(bssid *uint8) int32 { +func (r *rtl8720dn) rpc_wifi_get_ap_bssid(bssid []uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_get_ap_bssid()\r\n") } @@ -3160,9 +3160,9 @@ func (r *rtl8720dn) rpc_wifi_get_ap_bssid(bssid *uint8) int32 { r.read() widx := 8 - // bssid : out uint8 - *bssid = payload[widx] - widx += 1 + // bssid : out []uint8 (6) + copy(bssid, payload[widx:widx+6]) + widx += 6 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) @@ -3339,14 +3339,14 @@ func (r *rtl8720dn) rpc_wifi_change_channel_plan(channel_plan uint8) int32 { return result } -func (r *rtl8720dn) rpc_wifi_register_multicast_address(mac uint8) int32 { +func (r *rtl8720dn) rpc_wifi_register_multicast_address(mac []uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_register_multicast_address()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x17, uint32(r.seq)) - // mac : in uint8 - msg = append(msg, byte(mac>>0)) + // mac : in []uint8 (6) + msg = append(msg, mac...) r.performRequest(msg) @@ -3361,14 +3361,14 @@ func (r *rtl8720dn) rpc_wifi_register_multicast_address(mac uint8) int32 { return result } -func (r *rtl8720dn) rpc_wifi_unregister_multicast_address(mac uint8) int32 { +func (r *rtl8720dn) rpc_wifi_unregister_multicast_address(mac []uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_unregister_multicast_address()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x18, uint32(r.seq)) - // mac : in uint8 - msg = append(msg, byte(mac>>0)) + // mac : in []uint8 (6) + msg = append(msg, mac...) r.performRequest(msg) From 8dc0872ca1f1795ba3b39c5044c970687d1638a4 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Mon, 13 Feb 2023 23:02:10 -0800 Subject: [PATCH 10/47] README: updates and cleanups --- netdev/README.md | 57 +++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/netdev/README.md b/netdev/README.md index 57624053b..e77693ff8 100644 --- a/netdev/README.md +++ b/netdev/README.md @@ -35,25 +35,33 @@ Here the netdev is the entire stack, accessing hardware on the bottom and servin ## Porting Applications from Go "net" -Ideally, TinyGo's "net" package would just be Go's "net" package and applications using "net" would just work, as-is. Unfortunately, Go's "net" can't fully be ported to TinyGo, so TinyGo's "net" is a subset of Go's. +Ideally, TinyGo's "net" package would just be Go's "net" package and applications using "net" would just work, as-is. Unfortunately, Go's net package can't fully be ported to TinyGo, so TinyGo's net package is a subset. To view TinyGo's "net" package exports, use ```go doc ./net```, ```go doc ./net/http```, etc. For the most part, Go's "net" documentation applies to TinyGo's "net". There are a few features excluded during the porting process, in particular: - No IPv6 support - No HTTP/2 support -- HTTP client request can't be reused - No TLS support for HTTP servers (no https servers) - No DualStack support +- HTTP client request can't be reused -Applications using Go's "net" package will need a few setup modifications to work with TinyGo's "net" package. +Applications using Go's net package will need a few setup modifications to work with TinyGo's net package. ### Step 1: Create the netdev for your target device. The available netdev are: -- wifinina: SPI to ESP32 WiFi co-controller running Arduino WiFiNINA firmware -- rtl8720dn: UART to RealTek WiFi rtl8720dn co-controller -- espat: UART to ESP32/ESP8266 WiFi co-controller running Espressif AT firmware +- [wifinina]: SPI to ESP32 WiFi co-controller running Arduino WiFiNINA firmware + + targets: pyportal arduino_nano33 nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +- [rtl8720dn]: UART to RealTek WiFi rtl8720dn co-controller + + targets: wioterminal + +- [espat]: UART to ESP32/ESP8266 WiFi co-controller running Espressif AT firmware + + targets: TBD This example configures and creates a wifinina netdev using New(). @@ -69,9 +77,9 @@ func main() { The Config structure is netdev-specific; consult the netdev package for Config details. In this case, the WiFi credentials are passed. -### Step 2: Hook the netdev into the "net" package +### Step 2: Hook the netdev into the net package -Tell the "net" package to use the netdev. Continuing with the wifinina example: +Tell the net package to use the netdev by calling netdev.Use(). Continuing with the wifinina example: ``` import "tinygo.org/x/drivers/netdev" @@ -85,9 +93,13 @@ func main() { } ``` +Now, the net package is linked to the netdev so any net I/O will go through the netdev. Calls to net.Dial(), net.Listen() etc will translate to netdev socket calls. + +The last step is to connect the netdev to an IP network. + ### Step 3: Connect to an IP Network -Before the "net" package is fully functional, connect the netdev to an underlying IP network. For example, a WiFi netdev would connect to a WiFi access point or become a WiFi access point; either way, once connected, the netdev has a station IP address and is connected on the IP network. +Before the net package is fully functional, connect the netdev to an underlying IP network. For example, a WiFi netdev would connect to a WiFi access point or become a WiFi access point; either way, once connected, the netdev has a station IP address and is connected on the IP network. Call dev.NetConnect() to connect the device to an IP network. Call dev.NetDisconnect() to disconnect. Continuing example: @@ -102,7 +114,7 @@ func main() { dev.NetConnect() - // "net" package calls here + // net package calls here dev.NetDisconnect() } @@ -158,7 +170,7 @@ func HelloServer(w http.ResponseWriter, r *http.Request) { ## Using Raw Sockets -A netdev implements the Socketer interface so an application can make raw socket calls, bypassing the "net" package. +A netdev implements the Socketer interface so an application can make raw socket calls, bypassing the net package. Here is a simple TCP application using raw sockets: @@ -174,6 +186,9 @@ func main() { cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} dev := wifinina.New(&cfg) netdev.Use(dev) + + // ignoring error handling + dev.NetConnect() sock, _ := dev.Socket(netdev.AF_INET, netdev.SOCK_STREAM, netdev.IPPROTO_TCP) @@ -189,9 +204,7 @@ func main() { ## Writing a New Driver -:bulb: A reference netdev driver is the Wifinina driver (netdev/wifinina). - -Netdev drivers implement the net.Netdever interface, which includes the net.Socketer interface. The Socketer interface is modeled after BSD socket(2). TinyGo's "net" package translates net.Conn calls into netdev Socketer calls. For example, DialTCP calls netdev.Socket() and netdev.Connect(): +Netdev drivers implement the netdev.Netdever interface, which includes the netdev.Socketer interface. The Socketer interface is modeled after BSD socket(2). TinyGo's net package translates net.Conn calls into netdev Socketer calls. For example, DialTCP calls netdev.Socket() and netdev.Connect(): ```go func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error) { @@ -226,26 +239,20 @@ type Socketer interface { } ``` -Socketer interface is intended to mimic a subset of BSD socket(2). They've been Go-ified, but should otherwise maintain the semantics of the original socket(2) calls. Send and Recv add a timeout to put a limit on blocking operations. Recv in paricular is blocking and will block until data arrives on the socket or EOF. The timeout is calculated from net.Conn's SetDeadline(), typically. +Socketer interface is intended to mimic a subset of BSD socket(2). They've been Go-ified, but should otherwise maintain the semantics of the original socket(2) calls. Send and Recv add a timeout to put a limit on blocking operations. Recv in paricular is blocking and will block until data arrives on the socket or EOF. The timeout value is calculated from net.Conn's SetDeadline(), typically. #### Locking -Multiple goroutines may invoke methods on a net.Conn simultaneously, and the "net" package translates net.Conn calls into Socketer calls. It follows that multiple goroutines may invoke Socketer calls, so locking is required to keep Socketer calls from stepping on one another. +Multiple goroutines may invoke methods on a net.Conn simultaneously, and since the net package translates net.Conn calls into Socketer calls, it follows that multiple goroutines may invoke Socketer calls, so locking is required to keep Socketer calls from stepping on one another. -Don't hold a lock while Time.Sleep()'ing waiting for a hardware operation to finish. Unlocking while sleeping let's other goroutines to run. If the sleep period is really small, then you can get away with holding the lock sometimes. +Don't hold a lock while Time.Sleep()ing waiting for a hardware operation to finish. Unlocking while sleeping let's other goroutines make progress. If the sleep period is really small, then you can get away with holding the lock. #### Sockfd -The Socketer interface uses a socket fd to represent a socket connection end-point. Each net.Conn maps 1:1 to a fd. The number of fds available is a netdev hardware limitation. Wifinina, for example, can hand out 10 socket fds. - -### Packaging - -1. Create a new directory in netdev/foo to hold the driver files. - -2. Add a initialization file netdev/netdev_foo.go to compile and load the driver based on target build tags. +The Socketer interface uses a socket fd to represent a socket connection (end-point). Each net.Conn maps 1:1 to a fd. The number of fds available is a netdev hardware limitation. Wifinina, for example, can hand out 10 socket fds. ### Testing -The netdev driver should minimally pass all of the example/net examples. +The netdev driver should minimally run all of the example/net examples. TODO: automate testing to catch regressions. From b92ad4e997216628e1bd74d3a2c2500c114a872f Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Tue, 14 Feb 2023 00:01:26 -0800 Subject: [PATCH 11/47] README: add http and tls documentation --- netdev/README.md | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/netdev/README.md b/netdev/README.md index e77693ff8..89dd99bdd 100644 --- a/netdev/README.md +++ b/netdev/README.md @@ -3,7 +3,9 @@ #### Table of Contents - [Overview](#overview) -- [Porting Applications from Go "net"](#porting-applications-from-go-net) +- [Using "net" Package](#using-net-package) +- [Using "net/http" Package](#using-nethttp-package) +- [Using "crypto/tls" Package](#using-cryptotls-package) - [Using Raw Sockets](#using-raw-sockets) - [Writing a New Driver](#writing-a-new-driver) @@ -33,11 +35,9 @@ Here the netdev includes the TCP/IP stack, maybe some port of lwip/uip to Go? Here the netdev is the entire stack, accessing hardware on the bottom and serving up net.Conn connections above to applications. -## Porting Applications from Go "net" +## Using "net" Package -Ideally, TinyGo's "net" package would just be Go's "net" package and applications using "net" would just work, as-is. Unfortunately, Go's net package can't fully be ported to TinyGo, so TinyGo's net package is a subset. - -To view TinyGo's "net" package exports, use ```go doc ./net```, ```go doc ./net/http```, etc. For the most part, Go's "net" documentation applies to TinyGo's "net". There are a few features excluded during the porting process, in particular: +Ideally, TinyGo's "net" package would just be Go's "net" package and applications using "net" would just work, as-is. TinyGo's net package is a partial port from Go's net package, replacing OS socket syscalls with netdev socket calls. TinyGo's net package is a subset of Go's net package. There are a few features excluded during the porting process, in particular: - No IPv6 support - No HTTP/2 support @@ -45,7 +45,9 @@ To view TinyGo's "net" package exports, use ```go doc ./net```, ```go doc ./net/ - No DualStack support - HTTP client request can't be reused -Applications using Go's net package will need a few setup modifications to work with TinyGo's net package. +Run ```go doc -all ./src/net``` on tinygo directory to see full listing. + +Applications using Go's net package will need a few setup steps to work with TinyGo's net package. ### Step 1: Create the netdev for your target device. @@ -168,6 +170,26 @@ func HelloServer(w http.ResponseWriter, r *http.Request) { } ``` +## Using "net/http" Package + +TinyGo's net/http package is a partial port of Go's net/http package, providing a subset of the full net/http package. + +HTTP client methods (http.Get, http.Head, http.Post, and http.PostForm) are functional. Dial clients support both HTTP and HTTPS transactions. + +HTTP server methods and objects are mostly ported, but for HTTP only. HTTPS servers are not supported. + +HTTP request and response handling code is 100% ported, so all the intricacy of parsing and writing headers is handled as in the full net/http package. + +Run ```go doc -all ./src/net/http``` on tinygo directory to see full listing. + +## Using "crypto/tls" Package + +TinyGo's TLS support (crypto/tls) relies on hardware offload of the TLS protocol. This is different from Go's crypto/tls package which handles the TLS protocol in software. + +TinyGo's TLS support is only available for client applications. You can http.Get() to an http:// or https:// address, but you cannot http.ListenAndServe() an https server. + +The offloading hardware has pre-defined TLS certificates built-in, so software does not need to supply certificates. + ## Using Raw Sockets A netdev implements the Socketer interface so an application can make raw socket calls, bypassing the net package. From 3583f351a051f57811afe826d6a9664f87b71665 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Tue, 14 Feb 2023 01:10:13 -0800 Subject: [PATCH 12/47] README cleanups --- netdev/README.md | 67 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/netdev/README.md b/netdev/README.md index 89dd99bdd..0fb8a442f 100644 --- a/netdev/README.md +++ b/netdev/README.md @@ -67,7 +67,7 @@ The available netdev are: This example configures and creates a wifinina netdev using New(). -``` +```go import "tinygo.org/x/drivers/wifinina" func main() { @@ -83,7 +83,7 @@ The Config structure is netdev-specific; consult the netdev package for Config d Tell the net package to use the netdev by calling netdev.Use(). Continuing with the wifinina example: -``` +```go import "tinygo.org/x/drivers/netdev" import "tinygo.org/x/drivers/wifinina" @@ -105,7 +105,7 @@ Before the net package is fully functional, connect the netdev to an underlying Call dev.NetConnect() to connect the device to an IP network. Call dev.NetDisconnect() to disconnect. Continuing example: -``` +```go import "tinygo.org/x/drivers/netdev" import "tinygo.org/x/drivers/wifinina" @@ -122,6 +122,18 @@ func main() { } ``` +Get notified of IP network connects and disconnects: + +```go + dev.Notify(func(e netdev.Event) { + switch e { + case netdev.EventNetUp: + println("Network UP") + case netdev.EventNetDown: + println("Network DOWN") + }) +``` + Here is a simple http server listening on port :8080, before and after porting from Go "net/http": #### Before @@ -174,11 +186,11 @@ func HelloServer(w http.ResponseWriter, r *http.Request) { TinyGo's net/http package is a partial port of Go's net/http package, providing a subset of the full net/http package. -HTTP client methods (http.Get, http.Head, http.Post, and http.PostForm) are functional. Dial clients support both HTTP and HTTPS transactions. +HTTP client methods (http.Get, http.Head, http.Post, and http.PostForm) are functional. Dial clients support both HTTP and HTTPS URLs. -HTTP server methods and objects are mostly ported, but for HTTP only. HTTPS servers are not supported. +HTTP server methods and objects are mostly ported, but for HTTP only; HTTPS servers are not supported. -HTTP request and response handling code is 100% ported, so all the intricacy of parsing and writing headers is handled as in the full net/http package. +HTTP request and response handling code is mostly ported, so most the intricacy of parsing and writing headers is handled as in the full net/http package. Run ```go doc -all ./src/net/http``` on tinygo directory to see full listing. @@ -245,9 +257,50 @@ func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error) { } ``` -### net.Socketer Interface +### Netdever Interface + +A netdev driver implements the Netdever interface: + +```go +// Netdev drivers implement the Netdever interface. +// +// A Netdever is passed to the "net" package using netdev.Use(). +// +// Just like a net.Conn, multiple goroutines may invoke methods on a Netdever +// simultaneously. +type Netdever interface { + + // NetConnect device to IP network + NetConnect() error + + // NetDisconnect device from IP network + NetDisconnect() + + // NetNotify to register callback for network events + NetNotify(func(Event)) + + // GetHostByName returns the IP address of either a hostname or IPv4 + // address in standard dot notation + GetHostByName(name string) (IP, error) + + // GetHardwareAddr returns device MAC address + GetHardwareAddr() (HardwareAddr, error) + + // GetIPAddr returns IP address assigned to device, either by DHCP or + // statically + GetIPAddr() (IP, error) + + // Socketer is a Berkely Sockets-like interface + Socketer +} +``` + +### Socketer Interface ```go +// Berkely Sockets-like interface. See man page for socket(2), etc. +// +// Multiple goroutines may invoke methods on a Socketer simultaneously. type Socketer interface { Socket(family AddressFamily, sockType SockType, protocol Protocol) (Sockfd, error) Bind(sockfd Sockfd, myaddr SockAddr) error From a278a34560a2c0be97e5f0601f5842590dd32721 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Tue, 14 Feb 2023 01:28:42 -0800 Subject: [PATCH 13/47] README: update netdev models jpg --- netdev/netdev_models.jpg | Bin 65672 -> 67370 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/netdev/netdev_models.jpg b/netdev/netdev_models.jpg index 37e29d7040913eaa633e0568f991f935f2addbae..063f3afe7790cffad27f74f393d6fdf32070bb62 100644 GIT binary patch delta 40481 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%0AMIAs>Xqbp4j`$2v8} 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 Date: Tue, 14 Feb 2023 01:57:16 -0800 Subject: [PATCH 14/47] README more updates --- netdev/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/netdev/README.md b/netdev/README.md index 0fb8a442f..1a827f9f8 100644 --- a/netdev/README.md +++ b/netdev/README.md @@ -13,31 +13,31 @@ Netdev is TinyGo's network device driver model. -Let's see where netdev fits in the network stack. The diagram below shows the traditional full OS stack vs. different possible embedded stacks for TinyGo. +Let's see where netdev fits in the network stack. The diagram below shows the traditional full OS stack vs. different possible embedded stacks for TinyGo. The application is written to the same net.Conn interface in each case. ![Netdev models](netdev_models.jpg) -In the traditional full OS stack, the driver that sits above hardware (the "nic") and below TCP/IP is the network driver, the netdev. The netdev provides a raw packet interface to the OS. +In the (1) Go full OS stack, the network driver, aka netdev, sits above hardware (the "nic") and below TCP/IP. The netdev provides a raw packet interface to TCP/IP. -For TinyGo netdev, the netdev includes TCP/IP and provides a socket(2) interface to TinyGo's "net" package. Applications are written to use the net.Conn interfaces. "net" translates net.Conn functions (Dial, Listen, Read, Write) into netdev socket(2) calls. The netdev translates those socket(2) calls into hardware access, ultimately. Let's consider the three use cases: +For TinyGo, the netdev includes TCP/IP and provides a socket(2) interface to the "net" package. Applications are written to the net.Conn interface: TCPConn, UDPConn, and TLSConn. net.Conn functions calls translate to netdev socket(2) calls, which in turn call into firmware/hardware. Let's consider the three use cases: -#### Firware Offload Model +#### (2) Firware Offload Model Here we are fortunate that hardware includes firmware with a TCP/IP implmentation, and the firmware manages the TCP/IP connection state. The netdev driver translates socket(2) calls to the firmware's TCP/IP calls. Usually, minimal work is required since the firmware is likely to use lwip, which has an socket(2) API. The Wifinina (ESP32) and RTL8720dn netdev drivers are examples of the firmware offload model. -#### Full Stack Model +#### (3) Full Stack Model Here the netdev includes the TCP/IP stack, maybe some port of lwip/uip to Go? -#### "Bring-Your-Own-net.Comm" Model +#### (4) "Bring-Your-Own-net.Comm" Model Here the netdev is the entire stack, accessing hardware on the bottom and serving up net.Conn connections above to applications. ## Using "net" Package -Ideally, TinyGo's "net" package would just be Go's "net" package and applications using "net" would just work, as-is. TinyGo's net package is a partial port from Go's net package, replacing OS socket syscalls with netdev socket calls. TinyGo's net package is a subset of Go's net package. There are a few features excluded during the porting process, in particular: +Ideally, TinyGo's "net" package would be Go's "net" package and applications using "net" would just work, as-is. TinyGo's net package is a partial port from Go's net package, replacing OS socket syscalls with netdev socket calls. TinyGo's net package is a subset of Go's net package. There are a few features excluded during the porting process, in particular: - No IPv6 support - No HTTP/2 support From b46bdf086d51a6a93b19ccf61096c1257155bc59 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Wed, 15 Feb 2023 19:01:45 -0800 Subject: [PATCH 15/47] increase the delay waitin for serial --- examples/net/webserver/main.go | 2 +- examples/net/webstatic/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/net/webserver/main.go b/examples/net/webserver/main.go index 9a32cf444..c24af38ef 100644 --- a/examples/net/webserver/main.go +++ b/examples/net/webserver/main.go @@ -28,7 +28,7 @@ var led = machine.LED func main() { // wait a bit for serial - time.Sleep(time.Second) + time.Sleep(2 * time.Second) led.Configure(machine.PinConfig{Mode: machine.PinOutput}) diff --git a/examples/net/webstatic/main.go b/examples/net/webstatic/main.go index d2c522bc6..5bebb0a61 100644 --- a/examples/net/webstatic/main.go +++ b/examples/net/webstatic/main.go @@ -23,7 +23,7 @@ var fs embed.FS func main() { // wait a bit for console - time.Sleep(time.Second) + time.Sleep(2 * time.Second) if err := NetConnect(); err != nil { log.Fatal(err) From b065846da09296cabe379db3f49a3ab20d3a1f48 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Wed, 15 Feb 2023 19:29:41 -0800 Subject: [PATCH 16/47] Update README.md --- netdev/README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/netdev/README.md b/netdev/README.md index 1a827f9f8..b4969e5ef 100644 --- a/netdev/README.md +++ b/netdev/README.md @@ -13,27 +13,29 @@ Netdev is TinyGo's network device driver model. -Let's see where netdev fits in the network stack. The diagram below shows the traditional full OS stack vs. different possible embedded stacks for TinyGo. The application is written to the same net.Conn interface in each case. +Let's see where netdev fits in the network stack. The diagram below shows the traditional full OS stack vs. different possible embedded stacks for TinyGo. Notice the application is written to the same net.Conn interface for all cases. ![Netdev models](netdev_models.jpg) In the (1) Go full OS stack, the network driver, aka netdev, sits above hardware (the "nic") and below TCP/IP. The netdev provides a raw packet interface to TCP/IP. -For TinyGo, the netdev includes TCP/IP and provides a socket(2) interface to the "net" package. Applications are written to the net.Conn interface: TCPConn, UDPConn, and TLSConn. net.Conn functions calls translate to netdev socket(2) calls, which in turn call into firmware/hardware. Let's consider the three use cases: +For TinyGo, the netdev includes TCP/IP and provides a socket(2) interface to the "net" package. Applications are written to the net.Conn interfaces: TCPConn, UDPConn, and TLSConn. net.Conn functions call netdev socket(2) calls, which in turn call into firmware/hardware. Let's consider the three use cases: #### (2) Firware Offload Model -Here we are fortunate that hardware includes firmware with a TCP/IP implmentation, and the firmware manages the TCP/IP connection state. The netdev driver translates socket(2) calls to the firmware's TCP/IP calls. Usually, minimal work is required since the firmware is likely to use lwip, which has an socket(2) API. +Here the networking device is a co-controller installed with firmware running a full TCP/IP stack. Firmware manages the TCP/IP connection state with the network. -The Wifinina (ESP32) and RTL8720dn netdev drivers are examples of the firmware offload model. +The netdev driver runs on the main controller and talks to the co-controller's firmware interface using UART/SPI/etc. The netdev driver translates socket(2) calls to the firmware's TCP/IP calls. + +The wifinina (ESP32), espat (ESP32-AT), and rtl8720dn netdev drivers are examples of the firmware offload model. #### (3) Full Stack Model -Here the netdev includes the TCP/IP stack, maybe some port of lwip/uip to Go? +Here the netdev includes the TCP/IP stack. There is no co-controller. #### (4) "Bring-Your-Own-net.Comm" Model -Here the netdev is the entire stack, accessing hardware on the bottom and serving up net.Conn connections above to applications. +Here the netdev is the entire stack, accessing hardware on the bottom and serving up net.Conn connections to applications. ## Using "net" Package @@ -122,7 +124,7 @@ func main() { } ``` -Get notified of IP network connects and disconnects: +Optionally, get notified of IP network connects and disconnects: ```go dev.Notify(func(e netdev.Event) { @@ -198,9 +200,9 @@ Run ```go doc -all ./src/net/http``` on tinygo directory to see full listing. TinyGo's TLS support (crypto/tls) relies on hardware offload of the TLS protocol. This is different from Go's crypto/tls package which handles the TLS protocol in software. -TinyGo's TLS support is only available for client applications. You can http.Get() to an http:// or https:// address, but you cannot http.ListenAndServe() an https server. +TinyGo's TLS support is only available for client applications. You can http.Get() to an http:// or https:// address, but you cannot http.ListenAndServeTLS() an https server. -The offloading hardware has pre-defined TLS certificates built-in, so software does not need to supply certificates. +The offloading hardware has pre-defined TLS certificates built-in. ## Using Raw Sockets From 98b0b081ccb0e375a729663029e9be548fc0caa4 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Wed, 15 Feb 2023 19:30:48 -0800 Subject: [PATCH 17/47] update netdev model diagram --- netdev/netdev_models.jpg | Bin 67370 -> 65432 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/netdev/netdev_models.jpg b/netdev/netdev_models.jpg index 063f3afe7790cffad27f74f393d6fdf32070bb62..04abc822d6fa0880abbdfa641094ed8b4aa889fc 100644 GIT binary patch delta 26407 zcmb@tbyQqWlr`D}i{S2VK?A{Ef(0kI2bbWk7YPttIzWO$(BQ${X(Z6NyE`=QeEpl5 z_ukBWv%Z=4*88JYv+m+n-Ksiu&OZC>`W=h(;~BAXL=z}-D9`y*oq4==C4pp57Z>dD zI~okBEC4(7zbYyv#U$|pKDCDpPN?E2@iUNh?I*69e|5>x37W-~hD1Mz*%gU=pAXbE zi59^bXlq442t0Q&EDfx0yD(k%Zvyf=LK{?!CMkGuS&sim#bPaz^;=0$2G zZ{HNBgV|UY{!$CcQq3)bh24M!N1DChaja6lC#C@wx?brP zA;AW{Wgt^-q!jpU#21kAbR|zpzy5?n6ULj+e}Tw&tBN$KjhNrNfr)byfQcowPF<+v zjn!B)F+R>0gdL`4Zizgb5FM{gyy~p~>87k^tMX6lghRRez|q%poxMT&vyQbiS9}Cd zCoUIX%Rs@8EMhX#UoMY0ISvi~D*lcBLoFLK8Q(wXjrNi%a$pj8a4xh-NvZUYQ)`Ot zK=gOEb8Hhbh*{z6rwgV2rrLuz404;)<^(p%ct^JPnOHALZw|+dj(wSg`;R&eyPf;# zj;JehzM(`x$!#i)d(EtDiflb`b!7pZSxGuf#AJj&a)`&*v_*Z^h=sWg5iQ1^xIsl6 z-68Bi)Q!##RAXsppTd~VQJ%}-`Fn+F)|%pz?65n0G)>jD8RVqEFXmRUTml6;Uad#& zY_^7`hwK?<1v&=NGF%jD42LG>$;{c9Mj6(K^|_5j!ghe^_O?(nx~xj(8`%~tBT4M7 zP|0m^dj9^ycJAekTGr z#CLxN<)${h-g)p6zwuocS21MYl5OOChj^Zi5#0C;A_n>~7M{AA9G{ZA4Ut`+xSm1r zEj-%@sD?3q)iP{)> zcTSP3tfJBYwdRRqH1@q2JF_rKRcAwVJcPf6ffwAT$2qw_jw#4L9Jgf3|J=!FxJ{KB z5Dvf}55xlrV!XA^M*DiY6}+1oo9H)FKS{F2QBIt;W5#Lv5S!(TIEoq$Z6A-dATw@CAQ^{ZPcXj)_ZJg>WrAGIjkN( z?T(=G3=|}ZWQG=5lYCuf@*QBz5bd^d2z!&CC@EE1K=AsdiM(Y$)g6M-khBkXwSa_f z-`&cN$GzwYbQo*Rb@PtEOs;+tz7sH5}G-a z00bppiMB@%qtULEPS!ZtkX?v$oG$j2aF54ln&Yk`p;_#B8HAnGwm_vfThGnEt+Y^`2cJ-slM{8(Ut3Y@$t}+5q#%-!a)}>F1fu=-M5rlD;D>(w!KT2VgR)26c-XVh zro&JrPZ1QF$l3VwW1s>W0m4wZtVAbyzU!0FGbqt}-S{5a5ZEoqru{#Am49(6;G6++ z6@h!`Et3!5V{pYY=)ykV4IjD;3`mW&AL0YgpkMYZ+1VHX1UOR>R(J-PlvF(8O^s>` zZOmw%qJG2vfs&?@`V!)qFq+GBN&f@kvOE%8HJ}W3BFB>2d~l>2Gq|xhI^1sgZd+FO zk{Gf5jvgv`SkkeRDCh$-v~Yc*hC8{Pug{=E7RILpLoN4WnOXt$%yA_#+3bnx#xyXK z3H2NN^Jmb0x#8U7b{m-9Bousa9Kzsz7595z&FesOWzAoQJJmn&&u!h$m=E>*mXA;G zTzt$Q3aOkkE5M4TM1ba(XV7NOf!NjS%4g76z5A$kb69sb=0|Ru!#XbedJosS-MS&s zX38*k+Tszv`e6Ar?=Gtw1x2+_fgU9o8O+;RB`ZdTFRf0u3r)V_?idVwQS}WtwsagX zHp**KiFEs!-P~l&qwtfhus+!`@!bcUkL5uTb8$@3?5M*N`EDfc4bj9rkMeXD#xLz0 z5U2!)t-`K}8&J>jZNZNgWGI)#(jbckFsUUe8G1o|k%6HsS{uRCk=h8_go3r2$jHE4 zJC&VnB$&+Nk@(VQ+J>FYELGpe-mp*cJwAIT#Ek3d!{j8m^Nay}s)H~2@3(0ZVN{!M z<0lCvI)qt@?l?6wa|q8uN`Nr_;g>TaV<8=;I!j(T&}V#xSoaL7EKhp||ZlB&I$2tqHDm^Rq|$+ZjuMjw&*K^${FfxbaOK4)c>Zv>Z$^*rJ(YU z$Vlv2BNCkGPqH27g>k&Gy$0mMUszkzynNd5+h6&C&(l}pjULn56F@mxm~7l`PnNtc z2Q0HOeR>8VnCX32MKKI3@%G;FexjT#^{ZKIw@zHV4Zknh8eDvI64@{HdCEIuuK&tY zgA{03P~CN(Zh(9HdO0_c%N^b5Ze+I`HJnH20z@e|uV50sa1CY&_zs*L=|d0OYo9?~ zV#VI~SC3JD>(8AZ4{w_}pFuC6wYF;F#DW18{@!lXS>34_!B{F8uJlzBNB50C+doz? zv=`Gwm#m@NchI5gmYyze@PVF8>x;6{njf{!paW;ZV#UVZ5sht7`i3*;wf^5E?>lqf zF~{G_N@mW^jNz9|^mtLn`ttORiH_GF2Q`3>A6of%2vOj+&%_Aq;Kk2GLi!r;Iscy) z<&#ayH6N^hMo?)xU1ODgqO122l0+N#H?kG1!Yhe@4-QJA22@ccr14ZyX*W(t`GQ%$ zu=k}qs;{s1Qt9&k7^M~BU)_0~w-H&rVNBS&M)_)}4C26{zl9}PQ9lg*8}NnV1&Tuk zLA9{IBmQ>%>b=dV@OxVqS(?n~f@M|NEd${)_u?BX?Qsso`Ue^k`p1^??g$3+L?-=} z>(?reHQAVo(}17Gyz$pE9l{ZSvzyOh3%oYL`t(qiLSAMQ!!$_dj2~hG%(?c zOMv>cSwh)LZbEt}$g9TCBPb{*J~w9_<@^os8#{`|YB_$@7=AyM z#gJ0^3^Kc!=}0kMTv^s{D=W+|Un33wGC<<6836ME>VZ9Rayzf?UPOhbB?>I#!So^Me3Bh2|2oFY}fuBrVAG-r9snLgl_m zI-^#PO#oSxZDBh`RzNq-I8o15XEoO9C z{0w@z9EaIUj;Tn1)tZ^Lwif`!`%OlTE0cXbxdz0EULY*@7U?S29pP`aM7d^BB^*To%sBQFT6#;+201u3`B#^ zC6n!jOd=6LY(p-lQAiev#`WsPap2Uf{QS1_h0xe*VYa#NdC^W9mKw zpi0f~rTDe_Rv&G#*48T@j6igJA!EEPqHaL1Q^N( z>6Wt_eFeLhGHVIZmlyn-Y=^?qRs>Ntu2!Y_d4lw%ubTk*P&&I% z13@KPtU*+w&Nl-)JbWQ70wp@bs}uf(1GqL7D7v^Mh; z;U)5)#nOs&dpu5Rld^T4UiTe?G|jS_bSReWJkERP39<_INkW(IdhH4g-q;mduUxy3 zHyD^Po>Wj_hor9YwDK9$o&rE*3nyy$@yRjIo3x)t<-gu;34Yu<>#3xkp)(_k=j`ks zg<*!A@BpN()%wY&PlY6lEAk0FW4&||&c_tA&!8{6h?SGVD!*)87JGetszgdTq3>oP zw$1p;A&GzUkjb(>78Ax?5Zk-I32)3?a=ZCDqqaD&fR@nb8aBI66cPgTqcmiQ$-(UF zLm1l{Y7*R=(}v6&bv`(9>2(l@jG^9oW|VJ;tz7>02@mHPf~`kTP1OFxxt1W9h88Qv zasPQmlo03?vc2ZDtKtJXfy6!tPpd8EPoB|Lb7SRsxN?Il5#o}AJOx)n8YRds%luii zs-{b|&Q;Qt5KQxqm^HP6IujVt_2HkA$LFW@<$sNDV?znn zLvmDDWgL{aRjb?Hd|O;Qssiceo3`7Pai)D@eIDVi6eI zg%zP3Jhe#$#@A&65NUe_=T3J*FWJn++0oO{Ildb^Ja~*?h%uMs-27S4Jk`62-RAfs zOU5NKDP2!Q!qp|s7#pgjag+57qJ#7pUv6IZ8+NmbQa7Bc)B30M@Dbd7qjIV|8XgBP zFkp!t)q*@IuwmIS!jNqk@?+MO?Ly0tuoA;Fh*d^;T^SX~IZ`=QiGoJC-A7ne+KZl{ zDqWQ^oZ)=XrS)6&-*W5-W`R;Dz=6W6#T#v9&Zr~L9?#Q<%}^a2OK*7yf5&TJxoqlb z=@ilK*1CL|(E55;?huCPBRj zdq~8-Uc7DLsj$^*a>&VFR3>)V!uB4kDcz%SxbFxfe?P2~Vl4VTPO2_;P{CJdNwXlj z-@Bov?r}B61{7+8WP&(*KZ*=qU%@r>iqjf*s{k-`7N0?uL|Qf$lZv`FG`jCllf8&( zPxGSINlA5jM-uHFH>Rn_E7&rfx^Nj@CK#Y`tp@W`hFZ*8--Q*!X#2LIJ%samc~_s4 z=lRnD?7`qxqPT9n6?`vQrExbAN)^k@8{?bAafP`GGNHw4UW`K!XCMj)5f5Saot=-; z6i`~Pb(isd8bsq?g9#~#tu5ei$k0q$F&q+iXp+cnT^eW9AMyNAp zRvWeqRXd$8-9!5<;V3Z4i%J?OOle!OYFDy?dvFD|weC_u)2)qO2w!fsd`s*5bo>rD zDg*hr)P4rt@s2mW24Cya0L)?aZ0RH@zj$-g4$W$l$_c06ptPs&e(H*RdU#Wn`V2<`StNoe$E_BCD&nsgYS(BH|BswM;xyzKcRIcjFHtKD}V9< zusH{7DeT)7GqZW3Cs`n-@@#oQW2?S$V*_j_>|`UbwKk8igtf(#*BQ$giVkPBy8C>9 zS7V>F2utlV&A$AmiXXH@3ZYD@4|u{HiiWAhk==ONcta|A z6Fj)&+d27A)P(|Fi$wlS<9Ag^L4kR|(cwFhBE%*~^4&L%_8jx{$P~G;ghL2r>u&wwL!d zhY&}PMD;QAK|b_ZQEo!_=v;TxkLGogOTTKIN#ouXnJ!`{foRRs5h8=T`|eN*0Y@u) zcTV5370W~gd9%n3M8=_n?<}XURfH|j7G8SHpV^}`{V=o)Cfrc&YEF8gZf>&fGd3PGf1wy zB)ixr?+3#-T%@>uEm_RXc~p`g@g%flpkisrk&pn4*Z*Kd{C0YM`x}Se<6~5`X__ z_K7AMl{Q}Fy^b+h)0K+h%VXHYk+JPPE2Z@_D05k7<@T~zi)n0W3SW1`ASf6@v@;Wk zYyv*=pr%A%5osu@h|JPA&2Rz_Omvs{_-A zWfVPwEFN>dH6-)n&y5A4p~%fcXt#HW_VjppRc)pTu6V1y9UZOfdhz2} zjhSt+kSc53kjW02XW1P%$WsCw_664#`6y2?G^RIc&@p(OAyOK;tVKk7#*HBI?^w-$ z_=gJXE4|n(MEw<8d4CdQ9kPX6mq4+$&Tp<0@g*xWk)rQ)F~i3;g1uWZB>Oz&sBMf* z4L=-97O^`0Rt7Ofg8priG{$?h@@CeX(r%xFOSv%Do`Y3J!>1#mM=dHT}^I6o){RwJ*ULD8cq^9$m!jg>wQ@4O@7vZp|6Vt?Sc+LzgPaTIKT~0TkOA^ z7=rgd*jWY+j5VJ@Pcb!c>?~w2BVq;7b05wx&#lzot4|oBpUx2Z6XtCn8E7FrKusBx znKTXG{*Crq#!UKu-kf=gwB2RNfU&g|mn|6V@=i;#u=Gb0@bR4uHekQ&{wl6O&`Fz- zZ6(0pxmyI55LQY?MPLL|xp4lcUlO1ZAonUb=(;0?7VdcphF&D}o9aw(LTX)%WS;O5 zvjURrIt5#&TUT3YHCyMd(n{PDm7+oiF7Oaqr9XQ?dPcp(x>Dx1dMwqxgqVwO_*T9C zCDKUCIKk5|>kBhoUY^iOhDx_bT1xSP9nf6*V)kfx`7mzj{H+lun9A*XL;+DauqxlE znR5Keo+-xHA4!-rCDzi3&~eREU$C#G*O!5kQ@99mwGljt)fth(Jns`yMgGkKsc9#< z={9PeMs90fvlnDx`)J3%-X?>?m9y4%E#iEM(n>SY!>jaLAy>t%qGVn#zjvJO3*KAA zZKP;3k?K^?TyDdglW#1ZMSvHV;R~I@@ymtcoJ4sV3)5oAAjV6fpnVqE4hs4<7*A|9 z&&^k!%nD}F0%NspUtT4W)T6@wiMy2ZNu{qPmmNuFA0Z!=P zZ$X4$%<-3AeKqOGD=g}^owdDjF!$w*hGbevr6?Ar|?U;MO>+_U^QCL`0VZ;Ad zg-(h1^FAWTdDnLo`HrQJa-Adc($GvdPQLB*GVjiRW1%y51t*12Liv5;5423@xTnUw zlQ#O8_kBL7K)B-}$-M6_Qc7T0d?zi(5XpBWzTr+2Im8J#9z(|O&x&ZnCFz(DtH~PX z6Ei;#fHH!2+~&6J7qTFDk5=olp>F6}N_?u5kp@!r(6iM<=@8GQ<-t65S9nBgVhKuH zN7M6+m*l~CnzxzKS&kTRh)bZKWdHKMFj^=`c7Na-wUTW zy&_@Ve0vh-urV7s3>$_|4L23j+-{SNe9U7e~W zVcwh}!Ny)hkenmv$0+F=@R~7}ZE?Zg-A~`267M3Y5;@L0Hn;o)%@Ff+>|N%J%<6St|M$A>lYX*mQAKR5^hqRrd;qgJSLrIw zM-yIgVmO2zEh-17+Zen6*>Uca8J-XK7Ta0mtGrj^Nk86!{-8I4($gu#561IaZc0|~ zUzJlgOy=vd2~nF zvAV$!_Avt>0#p<|vfsFAr_6b!^*!;lOdhu|%}d9|nYmn5ww?*KPE>q?EE&nRi`-l6 zw-Y-YJs`~(TSP*8oLA~HgxN?3@LZ@#XU*z}OmJsJoW&^yq`jsJdydqfL47_(A@;xZ zen|fwc6OOJ7<4dayM6(_ud0muaf%U;H11`#uJjA=;~w`@SdS{QFKqQTdxfN^82zUy z@Ecdpnl#7<9slqAgZOFdN_hF@?4^eO8{RpWaaxw1xxQ&hK67v&C)Ac3puBO_CtR^? zvrg^f5Zz+W@gm`eWo+_Oa6*UQBn4qVgV>0IE-ECZ+o*0lyB!@JON{ZFld(C&h>Wx}Agq!hHShpm zoZHwc3&|vz;MOPCvzMnBe+aF4M=Ljx1xRZmrF(9KbZ)~_1@s4n+R2j^Lr;HidxFqB zVG3zTa{q#%K6TKJ$6B-r87z7fHCN3*;~9j_4Nr%f9-l(o+wK*s5_kF=gElOv-%vAm z;1nVz=r7|AYNStLOHd>n;Qf&iOd8~Eo~jZdOu6Cv=+G@Lcv<)Bv!mlTE4lesG~x~L z!eLNVdgPuciI?qlSLUnQiCUcCqr60K+UPA)_MSu3h1>0>J0`@T2i();ez9-t!fI;^ z0uF`KR+-ZZU*(B97Cy#H@Dnd}Zs$+ctYGgM<6e?mDUGhE*G_Tw0yXkDZqZUyVvvR% zN8*0e1;)sWCMLu_KcUquY+ZQ1gr40np-VT;!M@pM%!8R&eyDM}xQRW%GyA|Zddc=i zW#-ppSxocdfhV78@ZdtOeY1YBb2-!7DOo7W#(aAdb-H~H5f!Mi#OW?z_{pl|N?P=WS0~$I zU&cup)uDC~4sb0VSS%>oEv}T5km}-r`1VCM9>8;lvyK%v4^j#AgrXlhYIkQ z1ECiu1YY_^FbzG=(DmuH8TQcO+8!`f#H;C(1F@h{2wtv+hi%=A-5jPs76`a7#rP<_;C(m4FiOvN%7WuB)vbG@4F~o!AQ- zGE^gfuVx~2iZ-#Ld*OX>Vl)FksmMEDAEY;8ta?bMmXqD6U2}O;?2sl{gh%uPt{2kT zWmXK##&hb&{q#$WyToxWr!SIP~RjMNAaqRo&) zS@=OB+fFGy(67njFO|eAexmxEF#71fi`bhy zqzS8t6SlGu$#rZJ_stOh8Hp^1!&iXhe%#-Wo@YZasX2=e%HwW{MxOjELLi^4V}NG zYQe!?|?AZn70Ll7EbZ0&@YVeMDW9-+TLE*TzP0CroT1fex zC!dR<4itI(gV{=4qWJO;vPXX}Xv2-?I^B&wx61;=wS3=a-!>(_nX?!0 z{&7j0bv=UmX(~9r@Cw(Q5>}u~7ECH7DG0MK+nM4}_g2iA40S2>*dYgXP&{Ziz(XFN zW8O`@)?^C`dkgct9|dK+tn2&5o`uTD=6J-6cB%(RfnDVdIM-J27|6)!Em$C4UeK#+ z{EnizKytjem@q*bH0<$R6&1>7{JQ01o#^{uA%f(0QFghq+u4|J3?tIW9vTqa1ZV7A z3%$TyZNih6_n4TxjrPCiBry8Ru_>12M%gEtZ zU;I4IYo5qSlIi4!90NmSNH6J?sDALG&CV0RO6ySJRI#Y@)x8RlV+h#=)=Dr-D6_CRXp znt#J6RoN0X*i=^)_STG!yc{mQB6G*Dmj>a%&EQ`fSg21KOhxtX|HsC}kKgZgJTjx2 z(ydirH@dhW|GVzSQhax|rjeHF)Eo4KKQqIzfqC-`I)&>%jo}3Mu?@uS?U%n5U}z#W z5=%k<4p<^lo^-IdXg^s{8on6p>$QOm5_DxX4kE||5*}6nIEaKC)j>@<-tY$imcXY1 z=f$fVTX@MTR*tt=lrQW}^G-X}WtnaZc_s6(nl~Y>i4%8_-M>C60gnQkKGSev7O@#z z1mfeugrehyr7U=`ZFt2fqu`hw$$ zA@6kM!OQ3wG^?%l3>qIOF>NwUN`Nwe5%B4l-_E^0?1mQ&-*>HCuvxF%AP_!-aAhB; zGYOtSId*WCv*Ts^P3mH96cv0?Xg<`~*D8<>EtCk^HRLzeFtgLh49Up#_aCKeHn9f( z&pVO_pClp!Q825o4}5m3OD^jx-FF(Fqze~?U1Tjt{=AF%Mimqy>_HzW!TM< z?QEh=LaidTBfg<#)4zgznJ#mELjOnq3w;cA=XgY7OReUHYxRu4+2rg%ZX^?^f)AGe zlvaGxgpRAOU(9_kBtYLlaP>#!eovz>@av~wtmYT~(mv9bd#UtwN$y2U(sX~IMF#%4 zR^NkN19N0Rx8g*Imw8it8L=eLUcoYGx9=;~@~UJNH`DiA^lYTB+d@-v9T%GCMU)`*g6opsQNmiBM>e>RAP*JAY(UeZkXs@P$)Lm z6`54yf1S$*>Tj{ocl?yuRm1MbY*IN@K7VzsF}XdlRU4*|_)~*9K>Fox6lE@FaL};Q zPd#E)TMDWP?iCC>zNR6gn2)~J>}6=!rM3(mC|tjZ@ae4RLZC17n(sFJ_*Pe{_nOF3 zW3F<upUu(ZPZH3d{<-g#nIIvc)*^T3qFb8nR0P+Y!?M<#Y_1B@|(5&UWD%jd>KJRI>|(Fvj;Ja7`f0Fr|NMJ=y& zNrCo+`;lue-#n74EhC^5cK$JO|D->HxIQm3{IipDqVJ~3-Rg|=nxV|q=6+MaFaE}c z2@dr3c}e7R)!e2;RD{Y|{*B0imecg`-g(Kr3eSSsSsQgJ{N7^YrMinQ@{X{<zdoY71sl1Z(E4!(DwZdlgCw>G#$ z6rSzEs}Lu4!T) zbrxa(B5BVP72Jb3$}5~y4R}Fa(VMzSkuACSC8A-I_x{pmVsWGjBwb$N9|isH-19us zs2gx2etDQ17QP8vY<7}W7wG+RlOhc^tgNk%wD{KTZ||mnwkgfrNU2VU4_+5T!o=Pe zEUjL8x!N|qUZslbDznGZPl+1LH3ab^0aXBw8_mVXlqy-uHNj85@>YMaY~MP?s=iPg ztmyB+@_EP4&xl{V$|j{c<%j-j#nyzh_{hv7UUY*&gM23_*k_~G%K8bbHRO|i$!raa z+(x~Zq>abx7?yXP9v;L7XlvS&o0&CTzhaj&ITA@_Jqk_IezS*2P-gjpfZXeYuBIdL|h=ao2@O$Dv%gV$aBB)N35PF zH2qsr6L`>&h|u>wI#uU^wAcp6MtfccD5@t(%BZQU$1Do7Xp$$B>xp;y%HQ87PNs@z zyp9-UrxEwz=cND-!}!*p^GWYdFaQ2r8O;HaFNgE@_tGtkWe%xK>fK8v@Q}Va@D;G9 zYS2Zz?e*Xi08vopAST4Voz8sO9J$~3wEaZv7*>9lzd>iA77?2JGK4NLyW-QxE8s+B z@`2FVzi7W+!4s$KIOnt1Z;dVKMx~DXj21YHBqa?a-#Tve{AJsuI&qR+AG?R-N#mBt zXsPOHZ-Au{%z2Z#TSCmOOW0R!*XDAoZZkIbt?K;6AE9GtAvCV5=D6%vOKsw^R+73z z@aLbo&KKs?;gi_nxM~K^pa<$EW8mS@F6%}F?p8J?fi6-wY=%z5X{nHfl_!yZ7)mHy zK>8n_!lPT0@xOZuF6gCRR~RysG=8J8$?felaOw`P9YWyb%vTlVXHZrKT!n|Ko%P_+mA>%A4zSe_IoK z_Q}(y?so(gJsu9JYy7Lecl-m}#oZgEM|M3=*kJjnCwjr8vPm#qVyHdr56g=eK&C2q zbS*+OaKiEiaCY=W-b(jjJA|kNkF1NRPxhzf8nAwor40#gJry7-IrWlKt7 z8_1`F864MPvleqoZV?D0QxzhT-wtdBs%)?vZ20C_3;ItcyVzcp&S@@DJ#S3-OEgNa_!Ldl+mgKTE zJUsP`4cE?4amJ2di<7mTN`2m?1Glqd)pdlWq^c!Kb>gVP1e)ZGGnf{JF%61-LaB@k zW8k`}eHx>DPOc_hvUweU$ z9a<4&kimaO@2mc2Q@y@Pvib4U#>|T~wh^MqGZzeNGX(?d>1UYFWxhbY5FMSm#(>nYZ zTo~I|{iQ{-{D5*UIhnUk>v|?Rrbupv`7nMVWdUpcCF|i!#&{HTp&XrKrg98Q5<5z> z#`-js8WN!W3UZ~{Vn^%!;nsFrM#N*oxnqZj+`BA>LwCv)Mr*CN=wUCMT(G~q{+H`l zkx6O3P9hNwrus{fqNX?G#1=-SsKlFB`K9QN-72L6-F>pS|&8 zB6-6ZuX-2X!8! zgM9GuAp;vXxZUFixm{A9*}C-iO2?7xrp;>Jp(0>qhQ^62qY@8HJ^1@Alf%ecc&DeE zrjmbLSEl(+HN*{V5^678f4@}!CF~lK3c9dmiG`O1FIBL2Q!V=J;q{`cq;+XdN6v_| zb@f?dN`K(nrDmzW-pr>lhl}7$s`eI`$kAO^HQmvbXUN~bf!!G;B#^8$;(oHtQPjLK zgQb|m=>XwLZX0@Us-_x4nH68$t@vewqW2F1XR{c+sZAe2|Kv9RwEx{1{8#7>_&0c` zER>^QY4n?9JKfzMQEh@i0@p}y02Cj6w4Co6doygD+oWvv0$%rz^UbjlbZXS##ho8C zw)DJZ&5O2*J}O!}?e5L@N`xMLRIU;}D}lRb&25?D5Ga42uePi-0XdW(<^V>%Fd{vz zA;ig0$4cmj$;hywSqcC@G6S(Vqj&gAo|wJ30W6#PQ=<%qRlU}R#?;YaTi7wuhkG4- z6K|sohzLtVZc@1GaEriN^RU^6wie&LUoH0bwWM$6?5|F`m{B;>$z#GvUHEBJ_qkhN zKf3SC@TASMa|A%Ibj<3$Lh#g*(H7rxvdWR|)Uo8`e*HSDE#?TU)d$b?IHe_t&7Ti2 zXJqZye}I3sn5Rh7jD)lh0;J+A$Hgu8K`X=46bIYIP0j7}{^9`Me#q7r6u6}DTA1w9 z@x$9<1M6gGatre*wpxTDww_T-nhh~3@s6+ECTKg62iCd$>^+7Hgx<(i1BlV%W77Zr zXj1(5oIy2v9wP$K4dYEJvaPG6kFHZJrhY$fybxj1DEV@J(HzT3f+9V{U?l7G>iT1C zU1Md-pZdu%_g9W4K{SqkWd2aatZ9x&#=lQ^FxaT|!N)`Bg09+2sor}O`W7t|G>Z(^ z{>4Q5(1|)Xz*+EWvE$euLVMyIt&y4%$^*sL|CqX*V?KBd5R}(B_4Ogk^pV%C>P?mxW~TDE~Gq$)Ud z@g$KA5E_XC7F-xW6k@~gdBQM1vpic*!gHftLQn0)p%eE1$wMCMc`S6%O(1+aG+IUNa$r3A3m_mt_AJFO-br+!)%e z)}CHy`Ap|uEo3N`)-w&_JKH$9B)l_6+8{RRdEq2uH)r<)=K1i|j}7i_;knhgQ0nGi z=8u^;rK^XN<}<$)i%JF4t|4lGex1H8FMRE3k0)o?`9t_3`p z#vP(dJ%tThJw3voz#^A&%4qERzyi<%hj>ly9p^9hT<@1e7 zn3PYlt&UHf$&`1z$4yO-EN{yMXPMoZg$BxW2e`kd%Kk%h0fxvvbjWzxjO&zS72)i} z?0y2}N}yzZ_5EdjMRlQgJa!AzV#IiXf7DGq;}252`ObJ;yCl6q;J>%!>(R_d3zNU< z_#8H7?A#ziZvR~Shc~TB4zmfjg_6Cbc@}FUtCt9-zhCh~1DIY6#XSj)uYUH#40ZS`n)S7cLizRj<_iG9NJm-Z%x<8m4qzY4`) zmHJ|mdpUk;iyT}~r?mdD&Yet5v+R%H4$87Eh6z2%*EK(b;KcL&yHYWHv2Uw&6C5oP z1mEX*l7PpS^T(jP^KMjxm&@;Dwuuqb-OBz~Z|51+RM)QSAV}{>uTli5(owo}5NXm0 zC`AZWnsgRLK#(2*=}1R<34zc-nn-UNdWQ&v4$|Ct-|ySs*=Ls@XY4V~uZ%U5u`;s8 zv*w)Zey;n@!KWvNW!&6oiHT`}C_SXA5}k?80iN=9!5IiB&k2)-1kd}W8P5o&@S;Ec@*O$yI7D<}jz zUf!T1D+*_-1y-AZgNsTE358KEpDXDp~TsDfvYJXwS0UEI5du;BqE9W_}T`dZq z5s!A+Yry_8!lLcJ_WVgG(Uu>@=QpCUZftiK5`tmye4;BdR!0V9WZU#$}h9m@C(7(EWw1`TYAlP3yWZR#xWzelJj1dl zw5^Ui7QSH`vCA7{Coz2APf6^JCU9Yef+ez@HEolzwE8x~M_Y-waqmG)FOOqa%6%*` zvw17{OkK|veE+rw(R9?c_T5OK@|0d&M<@dVcJeu@OIsiFEU3J73H_;hu2mZLg2(xB)< z9^ebgIH6?cXhF@+(|>%8=Irycgk(I%_sqwGOoSkqihCm~Indy(adHv(aP_`;LmBnR zMcy6Ccg{C&m(#P zrC^iCbX4%6(HO|Ce93}QTW)im$A(y&&0qPZQk<%8D3|gYwpRbF$lN0~-tZyCnlOS_eVbH)y?ilHCBX@MKuSPJ9}EUOOjVwMgA0$9T|}e)hg~nB57`;0k1tig@FGx|cBw+(o6j zl$t|4FiMdvCQW2wb$>SStXL_P=;wh3UzUx`W?QKYPT`zJ&>8I3f9|q5bCaR{eyf~J z>eX=UwpL!z*(8UpvX;a_Q{R!Q`~r5)w2txgwPI=4O2E+YlNn_9u~9>^{DYMYlaE=Y ziPWHVmVO|-U%bt4MJpY)-lVY{Bp1YYC3y7sUPXkQHNC`jUiN&f%|sc{EwE~uH_k5z z6OgU&$}D|gte&Oyrj`ZUGeYWQM5pKl?}|a}bc0sF84&je^gIo>P%+?;)G*B;XV5F$ zzU0}Vn6YHHbY?i~l5o|d#{=|NKlaR1 zPB@U(0AW>q9(L-D8h4npYDr5V?I(*IjhtOu_Cs zB{gW+xs!m35QMX5^(tn>2{dorpi>1m6EH&u$+;DUdS>P@PgRL0ko|NtHtI%dm0pkI zb3aCwq>Q%DQ|&uFPcqb_rg-`#7K97B2U~xU{BbU(Ztur9rstt60&O=E1ZLwnour9K z^w~HOlW2XXfM8FL-2{rbHw5!Ws{M2$qM^x>Vpr*I{MJo5-vS<5*i~-vfFGl4(}ON# z|9}Vw4r!mvM&Q|eX;-V_-`6?5gCIP8*4>1TwmK3t1%6oe{{d-IwlKtC+BAt=UxmMg zhDRhRfjZY&0>Xzu?kvJdz-%9bM|lmN^3({{K8{ocGoM=hgtHtIYUTqwiWW_53{5x9 z@JgOuNICMO*9)D)rmKE|@l*O4nYyedtEeiBgd|qR^~gnGbF@E{>vh_g>pydNYfk>s zir<-N zawKu1noZWs%}7%5BDmMby9BA@Np{&>6k$5K{ngg6j6);ecU{GIp4#qJV&^H2zb;<# zc8xFR{;nXY`=P!hSysD?Y^>=(!AJn7d#Y)jfUy3jiAy zy&LGV`??d-$@+=3NiMAI^{o1&vnyv)!{na8R^na1`w@Csq#=nSkVD9sE?uld>ft9P z{1{c-&VGJc?ASztelyUKjR33)Dj9_&5Of)hE@`N5NImItrLZ~1@fc8I<+n`?Vag)> zmRRS|E;T6tY{DAD_=fqM&A>ekkEFkcUN=g0E!%@U$f)-`cNkozTN}?Af(pm9xidLE zo>o--0WGq{Qtd-%)g~!L86Y!G^e^=D?}FYv){FJROWJn84x3%Ay|4l7DpA7H z=2}c)Xj8f0#+r@kf}Hi_cEu^A>qA=+csQ}1@Jcrf>m&g6D-;6mdB;7N1y2Zi4UP37 zSKg{qG-lb$uy)tJI-p5~lMng$lsn$Rqg10l`^-Nc#moqi;taq*Bd>I*%(DFMKSwC=(5ED7eF!6!s-SUz^LU{M^8Q=feDGj6dGW>R>{xaEK+hSR5nFm);DFUHT9d2y{RT zb20br={Zk@8$WWa5i=^=eC~%Uc6*3p&vMc0AXjF?d;`EffRzQS^a>*d<`LEUvgLIq zu`eK+vH7Qq=N)Q0<6b{9RnH%z8Fy$cxx!|jf~5xSWX6Zw{p}3Nc^gfzvU}4#3b0v) zD}$_hW}bAL{ciW7G81-k`T!yJP)Z7Ki2wRVae2zel7GzkBW$G1jeLpI#?#w(z*rBZ z`0|l-Ml(=^x(tTdpS4NsOMjBQ#~)QdnirpU#V|NZMd;pOVZ->V3JtW+z*m}1oWEP) zViuqu=LqN8(I-|?$(w$!UuPK8A$u>^U)nGcdle%ugtQx5lTWwW5iQBndn^IvsY$Wg zvDGrfHnIM>X4|C|w{Xj0&o-d?=qk}8zcJT_lqtO!`NJHS+^jpXk8XWW7c_WDSg%4o z`e1$koE{oh!wxJRxE=6bRu61^bq#B3V>M`=xYh{DLz-xpnPd)fJGEGY>LFPJJAy}8 z_p{&wrZoz@Qlk)zo*1rh+lz4aKsFXYe|;DXxdE|81SO4l{e_vhFH-*jC87J}Pk%dM z^ugL$>J_lW5*WZiIx228x-cAR+=nna7i!ZncFF^fFz9Ay^{aR!esQY9N*vdxaK0}g z7NH>~=ba!=y`GYv=AU9|QV;2L3+*_FYBdJ2u|w4HSf;Qx8cQ!#-(A95ll@p24ynJt zB1)aJ9j#N|Dm8TJk(Ds8_^>VJVr-U@Ki;ZTKIrbVsjerJL`9@NfA*AufSJJ6e->C} zCDQ`RcSIqnl1Y%p+1T!jerNjPxIQNABv``vds%;j(>B6@8^Og4VTJB<$ zY;;G9BC5tEPx0Aw@lxl9OMY5h)GaBudyVy;rIF019w(f{&`b>g_-eIO2ETZAIx3|7PDdj~WC!zp&5GCm21{j@3$?zi{O>wp4Ft$`GROfD( zaGvnhH%8zM zUJj^Qr){EPPn{yD1i4`MmQ{`#NPFDRQLG>zp~+uqynE63E-3Rxm4g+r#wv8~%ju2` zk?~QV5OBUGTahRK%Ei!vni+5Vnew5*&2yu{KDW7H%}uAT)c4yaa!dVC4({Y;fA0)l z*U$1fptQElwBY6ES8>Mcx@sW!b9JEl4MR}!T)(S;z*mY#zN-{*rJd{K!Xb)Vaxk0> znk0dFxCg{Y$W!EA>TCGZ{lb@#&bDDvU3sMJvpop6XgRiv&!;9i%Zki-q0cI4L=LjQ zgQN!Owvgq%&sWb)XHK$7PZx5#%Xr#tva}uvkTlCR$I9gD^Nsgr=|*SF@VpesP$9H6 z>6fCjSflSRdhP3}2vyaZ`!LkoHdVcKykBJ`#3M{dVQ9=v_$7~22V+u_*oI<>$V11v zsDPxysUvgkmGsXI8T%^J=Hv>x(m=jNJW*XWvmZy1`)kb$-l40y>vY|Vqym-KCaAPD zANGe0B&vgtbh6#Kn)UXMH2Y6VSuLh0RSgfSYL+Rn`5QmExuTM}bL>#rNXU%@rltC$dioIIbFxWbR^~;k_z~ z0Ce%awvUy=C5s*31tEH-G|1kQbKzd@$Wtv_n{cr@vlJcAMr(l0p?O9quQ{3LTJnoU zeilJ~@)X}y+$8qzP@Nx|XNarW-x0(9d=f#=_ZfM(waIjIG~`ZO-9Ww_m>?pnD_|pz z#w>ebHgn6tKO(1hzCEBbu;*sH&S>jbC|c{M<~%a!*LC-13Uy4zW*#PuD8l<4PKaqy z1162=xwcNw7fUmXPoKxc8K*ksyjxRObl{`Bxf8iHDSO`M)yG5`UPf+b*-K|`$c~y* zGU>NVb#CFq{paWp=q1gvDH?o#bkc1)qlN~<+OcEPMP|f$#%o7og_5oNA7@~HZhk-10!AxoL6FIUlB~ZS9M|5*uwbT{#NT@ zaSP|A_i%WeT(7dR0KK)$P<7_HYY)>}mG2I?GPpJwzqengFxMum1RC7Wg@q4YA&|wS zh7kc$xx|<=J~_rDAtv0WYMYD4t>pg3vjqMf347haI4r+Z4#Zn!%Hjv|V06?Ke!kSh z(~5J3yRAuc8r~$8H*LeitxlNT6X)GdtJSqEpI&bwIiw@+hfL3^e`fP z=Ws~iodm6})%+(jal4N^CUu>zz~j5MbB9&>)YHqgEZog?P03e1`g1!wbV>+^H8Q5} z1x;>ZOn*SNmXjKZ_&BjSVQqw_x%C!F5o@+Ef8-u4z`MoVSt;UF=2&N}Xwtjmj*>e; zvV(w|T~lJ!(^0m#PtYH!uPZx{NGRnfHpZ&Q@#C1ynl(^)MQ6;+TkELh z?RK3$bXqgz{I`Pt*V6F6Q1CHMTqf}tHJ8DREPNoe!G$eh1Jd8#_V1DFqi)y9^~p_v^I z?if7!t91ZWZjU6B%OQiGPlo%Cl_eYJaH5P??XvR}A{!i#`;7hxYBuZ>W0 zFh=6heBo53#tKvxBC7?o0`Dk_CwI+F6P%}2}dqxe}aL~dpX zp2b*yXV*4q07y-xGlV^nTDyLt&f8nDy5 z)=qxRdA>||vLe%-HC5okwRL?{`DNp_${y0-8sOk zF&W=qLEm+_JdfD*2D?%LUmPWNJO$R1B|pV3Kf+i1eP=2`i^9umg`fh;ab_? zgR+H-HQkz;b|7Z$Sp+PAXwJ#>WxT=0w|a`&@%IA*++kAe6++>z4Ji|XV$vIu$UsW- z7AstDga)0cE89AA&l`@CXVwMKE@Swc1&hTch0E;C&rUwlYV;;4Y<7GMcQm%ixXZ*_ zSx6c-CoSV7e#6Ec(uCXu;oOVcnld^SJC~84OHgM<@_<9sGpJtZ9+yWf`Er3PXp7TB znjvziH)B(bXhBW8?!)sLTlE6+@55rxw!uWUFT)$wNz+kII>ra{$W*v{a2oyx!6dn? zd*O*A-;{!IAJ+R?L&Oq@e-gd#aX6kE;&{2L$mINv?AexhMh-=WF~)NPjT4`~R*$}<21(f_C4 zyxHXTp9b2G!+}ua>0mWMDCbBMP$9tM! zd#$h;zA_k})ZXSFkTKuLXVz_vt`fR$=6vs4R1E8=lPHetu(f`$? z&y(MpX&z?owBTmi9Pgs}ji}MH41!3C-_jv~3;)(W%5K_z5u}(J2kV@`_7rEVaD#gRaM1#nO3@&;l89Oq?oC8_a7ED`VD&DP) zA|3p>-8qd*Y3Z1ik#ldhCC77yH!`h!4mBLu_=`=#SO@ap02i#l8Sfw1-6UYcKB_Y% z!@tNgBd*n={mfE*Vv!%6=}}KjfwS>;`R-hJ*42y!Ev?q7_R}T31+qxHL*hZ1u|Zgl zN7+WyeqqW2%PN?xQ6iMHBo>>{bOd z%|GzXwK%D?sciWPgo&zj;_XX=(oU`RMIpds~G{ z(NrI7Hc-Q^Xq#cN#M@rHs)(!A0IA}t+x?)^$avmEkuOp8u~gqf_$6#O>vFThsRn?b z#TBPecn=)SsF(`>0b!>qV!|P*g(6$I0v<%IEbRs9qs*OnUN*&dn@|&b@)F~@wLYbM zBh`oN88ZXzCLdi!?-ICiEwqeCx|bRqw~k7F?;5zeUX7ma?yk(vX?$yB(K@?_s5|Y@ z)zqf4K+Qf}3ji}YU-2WHX9bR~=YSbyz0qR2*^D0=_bl&SI3Gif94Vr?ha2=N;<)#h z@@qkB)p9A@{yq`=mDf)*77Hr1fVYhaknlTSL+MkR^u{)x`)|p0zVP=6do{TyBY9_W zU`+`^PoN;P@NH;RCI~%|!t|Wwou5HR(P&N-6tBKyuZ~d-%e-;9B;uE-n&rUT|VD=xLAOHVu1Hxi> zswlf_1+VX~T0D_{QMvqDHo34b2s3=k?l%J+!UJ zd|wZ{n$!?$Fy4mQJ$-Lfi=~c^1dWm&jzeiPG7k%y^KeSfl=vSEbIJAlocn*C{S#u| zcZxjx$H>11PDn{^?^+rDX8IKOU)klnDxj<52)p?&79B&37S$X7WZHrX7pnhz!~y2@ zu82kdhmLiQ6}KzGj@zX9S)KoMObWSOGry+%`*{k}kf`n(RmKoWbkHtyC1{~8IaV>W zYxLfKIN{&f3IE|32P23r{B!c45=uVo{boAv`+8M5ds^J+TNn2CEr~RacM2w_yRw&r zYu{}iE~EN*_xh}$ysVUBJkW1b_sPnSfZbM~>)*Mz7(`)UANq1C11d6=Dk-oH9LfFy z&WQh!%{x7tv)#^Fr^AG*m_ZTaRV*r=II z046<*VtJ<$eoF252V_-0*_tcItZ}`**kM2E%H8<~)jv6J`JLS~H#e`7! ztY*uNpa3mDm(#btBLq=uEh%K<$?PR4g0d>{D`_XjfWiAyu?9E23EEA`ink|@5zt$l zyMKQx!%a@XmLEAvW4-w7N;T6RSM5s9T5xSw#IYh+Xwo*x#@xl3hV2fLc{M&r;{!Iz z0oxyw>@TBYQ@p_Fw>-7bi6?;sf8QCs((40;E?#HfblBgd-4v{&ELCqZ5v}Bo4(n8z*@BxmYd2*ahS#Eo>xaRaOmU3nPPS!iDPw3l z_ibepc|8TjDI@T0hkc2kT;LPv@4>d<(c*cX&|3^Q)^Nqd04qU}qm1%bahjDmV@WLL zG_VhEzsFQwtcD0c-Qm(&RR+ql0u_7cy>-|$7-~2rGIMG8eZbR4cx6JQq}rzcISg}a z5MX%0i(k1H9IcqPx#4eBn=~8l*m9l;=yUxmp_U`*&r(N~+8NM{Nj8$z+o677G#vvE z3SGzivlguZ`#AY^<$w8iJS|_XqREk?%WZF|>t_sepEv2+j_;2v3fF98Jh~T<3_^n! z+B@t$ZQT8so+QSQwbw!56a&CQT%st(ilkWGwDL^c@tz~^n0t$PF~Tov8tGy(p5air z;#ZQw9*t|d@Rzi!quG66Y2IV~20wliI66YHt4gW~e6QmO=KTgR=rjPk0DWF~;H6E>Z4zBsq!ncS@^#2A&0F?7!ZW|sXI@~Y}Fxup-ap6f0A$ zB^qx!hphmmo=5V9j|ScQBq)0-^=J;YdzeK^e$@8v~7)%Adrb{`toZe3^&v*vde%0;RGAq=iMlKzCOiu`9ii_52wY ziRmG0^lq!hv3TUu)IUju7{BUzOQ2z+&87Q z;MSx+xkAd#B;WJHa!KZX)z(JNm^`cA%mV0YG5%=6M8!)6&~$!N!i5Q~{tt;-$cn7d znxdgdP=lBwn(hxM^#g&+hWo&_`WAJY=c<-i&4iGE{KV?CeI%oG6k~YUT=Oous0}hi z!l8;`sQ)sx$oet@0d|h!zQ+s2iw4D{KUL6z~Q~oN!l`hyiMBL$vF+*Rb!w6jR zZ0uvmd`CJPxYnL=#9uIAT3U$a0?>oE10q)JPDhvTTrdxcOn*N?v;la1Zq zr4k}xq(`eZTf4l>+&$8KfI}ePydM++fgn>-X)0~gGJ>WgjYbpezOH;s>SFR?`H1BR Nt?d`BFIbD>_%}{AuSNg> delta 28790 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 Date: Wed, 15 Feb 2023 20:20:27 -0800 Subject: [PATCH 18/47] gofmt --- examples/net/espat/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/net/espat/main.go b/examples/net/espat/main.go index a1891fe0e..539049a9a 100644 --- a/examples/net/espat/main.go +++ b/examples/net/espat/main.go @@ -8,8 +8,8 @@ import ( "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/espat" + "tinygo.org/x/drivers/netdev" ) var ( From 2ed069fb12b411d5db0562c4d61bc3ecb0b45f72 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Wed, 15 Feb 2023 21:47:10 -0800 Subject: [PATCH 19/47] fix make test --- Makefile | 52 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index 9b874f5b8..022de7cb4 100644 --- a/Makefile +++ b/Makefile @@ -43,12 +43,6 @@ smoke-test: @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=microbit ./examples/easystepper/main.go @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/espat/espconsole/main.go - @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/espat/esphub/main.go - @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/espat/espstation/main.go - @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/flash/console/spi @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/flash/console/qspi @@ -155,14 +149,6 @@ smoke-test: @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd4in2/main.go @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/ntpclient/main.go - @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/udpstation/main.go - @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/tcpclient/main.go - @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/webclient/main.go - @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/ws2812 @md5sum ./build/test.hex tinygo build -size short -o ./build/test.bin -target=m5stamp-c3 ./examples/ws2812 @@ -221,12 +207,6 @@ endif @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/sdcard/tinyfs/ @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/rtl8720dn/webclient/ - @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/rtl8720dn/webserver/ - @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/rtl8720dn/mqttsub/ - @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/i2csoft/adt7410/ @md5sum ./build/test.hex tinygo build -size short -o ./build/test.elf -target=wioterminal ./examples/axp192/m5stack-core2-blinky/ @@ -251,6 +231,38 @@ endif @md5sum ./build/test.uf2 tinygo build -size short -o ./build/test.hex -target=nucleo-wl55jc ./examples/lora/lorawan/atcmd/ @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/net/http-get + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/net/http-head + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=nano-rp2040 ./examples/net/http-post + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=metro-m4-airlift ./examples/net/http-postform + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=matrixportal-m4 ./examples/net/mqttclient + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/net/ntpclient + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/net/rawsocket + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/net/tcpclient + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=nano-rp2040 ./examples/net/tcpecho + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=metro-m4-airlift ./examples/net/tlsclient + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=matrixportal-m4 ./examples/net/webclient + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/net/webclient-tinyterm + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/net/webserver + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=nano-rp2040 ./examples/net/websocket/dial + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=metro-m4-airlift ./examples/net/websocket/handler + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/net/webstatic + @md5sum ./build/test.hex # rwildcard is a recursive version of $(wildcard) From 2ed9db9bdb9ae4d14e51ab8658e87694807cd43b Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Tue, 21 Feb 2023 12:19:14 -0800 Subject: [PATCH 20/47] move netdev to tinygo/src/net --- examples/net/http-get/rtl8720dn.go | 4 +- examples/net/http-get/wifinina.go | 4 +- examples/net/http-head/rtl8720dn.go | 4 +- examples/net/http-head/wifinina.go | 4 +- examples/net/http-post/rtl8720dn.go | 4 +- examples/net/http-post/wifinina.go | 4 +- examples/net/http-postform/rtl8720dn.go | 4 +- examples/net/http-postform/wifinina.go | 4 +- examples/net/mqttclient/rtl8720dn.go | 4 +- examples/net/mqttclient/wifinina.go | 4 +- examples/net/ntpclient/rtl8720dn.go | 4 +- examples/net/ntpclient/wifinina.go | 4 +- examples/net/rawsocket/main.go | 3 +- examples/net/tcpclient/rtl8720dn.go | 4 +- examples/net/tcpclient/wifinina.go | 4 +- examples/net/tcpecho/rtl8720dn.go | 4 +- examples/net/tcpecho/wifinina.go | 4 +- examples/net/tlsclient/rtl8720dn.go | 4 +- examples/net/tlsclient/wifinina.go | 4 +- examples/net/webclient-tinyterm/main.go | 5 +- examples/net/webclient/rtl8720dn.go | 4 +- examples/net/webclient/wifinina.go | 4 +- examples/net/webserver/rtl8720dn.go | 4 +- examples/net/webserver/wifinina.go | 4 +- examples/net/websocket/dial/rtl8720dn.go | 4 +- examples/net/websocket/dial/wifinina.go | 4 +- examples/net/websocket/handler/rtl8720dn.go | 4 +- examples/net/websocket/handler/wifinina.go | 4 +- examples/net/webstatic/rtl8720dn.go | 4 +- examples/net/webstatic/wifinina.go | 4 +- netdev/README.md | 335 -------------------- netdev/netdev.go | 137 -------- netdev/netdev_models.jpg | Bin 65432 -> 0 bytes netdev/socket.go | 143 --------- rtl8720dn/rtl8720dn.go | 3 +- wifinina/wifinina.go | 2 +- 36 files changed, 62 insertions(+), 678 deletions(-) delete mode 100644 netdev/README.md delete mode 100644 netdev/netdev.go delete mode 100644 netdev/netdev_models.jpg delete mode 100644 netdev/socket.go diff --git a/examples/net/http-get/rtl8720dn.go b/examples/net/http-get/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/http-get/rtl8720dn.go +++ b/examples/net/http-get/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/http-get/wifinina.go b/examples/net/http-get/wifinina.go index 658492e71..c3f8e9199 100644 --- a/examples/net/http-get/wifinina.go +++ b/examples/net/http-get/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/http-head/rtl8720dn.go b/examples/net/http-head/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/http-head/rtl8720dn.go +++ b/examples/net/http-head/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/http-head/wifinina.go b/examples/net/http-head/wifinina.go index 658492e71..c3f8e9199 100644 --- a/examples/net/http-head/wifinina.go +++ b/examples/net/http-head/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/http-post/rtl8720dn.go b/examples/net/http-post/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/http-post/rtl8720dn.go +++ b/examples/net/http-post/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/http-post/wifinina.go b/examples/net/http-post/wifinina.go index 658492e71..c3f8e9199 100644 --- a/examples/net/http-post/wifinina.go +++ b/examples/net/http-post/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/http-postform/rtl8720dn.go b/examples/net/http-postform/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/http-postform/rtl8720dn.go +++ b/examples/net/http-postform/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/http-postform/wifinina.go b/examples/net/http-postform/wifinina.go index 658492e71..c3f8e9199 100644 --- a/examples/net/http-postform/wifinina.go +++ b/examples/net/http-postform/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/mqttclient/rtl8720dn.go b/examples/net/mqttclient/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/mqttclient/rtl8720dn.go +++ b/examples/net/mqttclient/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/mqttclient/wifinina.go b/examples/net/mqttclient/wifinina.go index 658492e71..c3f8e9199 100644 --- a/examples/net/mqttclient/wifinina.go +++ b/examples/net/mqttclient/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/ntpclient/rtl8720dn.go b/examples/net/ntpclient/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/ntpclient/rtl8720dn.go +++ b/examples/net/ntpclient/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/ntpclient/wifinina.go b/examples/net/ntpclient/wifinina.go index 07859b1a0..e9151a53c 100644 --- a/examples/net/ntpclient/wifinina.go +++ b/examples/net/ntpclient/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/rawsocket/main.go b/examples/net/rawsocket/main.go index d502a9499..8194f2a26 100644 --- a/examples/net/rawsocket/main.go +++ b/examples/net/rawsocket/main.go @@ -11,11 +11,10 @@ import ( "fmt" "log" "machine" + "net/netdev" "strconv" "strings" "time" - - "tinygo.org/x/drivers/netdev" ) var ( diff --git a/examples/net/tcpclient/rtl8720dn.go b/examples/net/tcpclient/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/tcpclient/rtl8720dn.go +++ b/examples/net/tcpclient/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/tcpclient/wifinina.go b/examples/net/tcpclient/wifinina.go index 07859b1a0..e9151a53c 100644 --- a/examples/net/tcpclient/wifinina.go +++ b/examples/net/tcpclient/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/tcpecho/rtl8720dn.go b/examples/net/tcpecho/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/tcpecho/rtl8720dn.go +++ b/examples/net/tcpecho/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/tcpecho/wifinina.go b/examples/net/tcpecho/wifinina.go index 07859b1a0..e9151a53c 100644 --- a/examples/net/tcpecho/wifinina.go +++ b/examples/net/tcpecho/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/tlsclient/rtl8720dn.go b/examples/net/tlsclient/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/tlsclient/rtl8720dn.go +++ b/examples/net/tlsclient/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/tlsclient/wifinina.go b/examples/net/tlsclient/wifinina.go index 07859b1a0..e9151a53c 100644 --- a/examples/net/tlsclient/wifinina.go +++ b/examples/net/tlsclient/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/webclient-tinyterm/main.go b/examples/net/webclient-tinyterm/main.go index 697101282..507cd820c 100644 --- a/examples/net/webclient-tinyterm/main.go +++ b/examples/net/webclient-tinyterm/main.go @@ -16,13 +16,14 @@ import ( "io" "log" "machine" + "net" "net/http" + "net/netdev" "net/url" "strings" "time" "tinygo.org/x/drivers/ili9341" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" "tinygo.org/x/tinyfont/proggy" "tinygo.org/x/tinyterm" @@ -101,7 +102,7 @@ func main() { fmt.Fprintf(terminal, "Connecting to %s...\r\n", ssid) dev.NetNotify(notify) - netdev.Use(dev) + net.UseNetdev(dev) if err := dev.NetConnect(); err != nil { log.Fatal(err) diff --git a/examples/net/webclient/rtl8720dn.go b/examples/net/webclient/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/webclient/rtl8720dn.go +++ b/examples/net/webclient/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/webclient/wifinina.go b/examples/net/webclient/wifinina.go index 07859b1a0..e9151a53c 100644 --- a/examples/net/webclient/wifinina.go +++ b/examples/net/webclient/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/webserver/rtl8720dn.go b/examples/net/webserver/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/webserver/rtl8720dn.go +++ b/examples/net/webserver/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/webserver/wifinina.go b/examples/net/webserver/wifinina.go index 658492e71..c3f8e9199 100644 --- a/examples/net/webserver/wifinina.go +++ b/examples/net/webserver/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/websocket/dial/rtl8720dn.go b/examples/net/websocket/dial/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/websocket/dial/rtl8720dn.go +++ b/examples/net/websocket/dial/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/websocket/dial/wifinina.go b/examples/net/websocket/dial/wifinina.go index 658492e71..c3f8e9199 100644 --- a/examples/net/websocket/dial/wifinina.go +++ b/examples/net/websocket/dial/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/websocket/handler/rtl8720dn.go b/examples/net/websocket/handler/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/websocket/handler/rtl8720dn.go +++ b/examples/net/websocket/handler/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/websocket/handler/wifinina.go b/examples/net/websocket/handler/wifinina.go index 658492e71..c3f8e9199 100644 --- a/examples/net/websocket/handler/wifinina.go +++ b/examples/net/websocket/handler/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/webstatic/rtl8720dn.go b/examples/net/webstatic/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/webstatic/rtl8720dn.go +++ b/examples/net/webstatic/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/webstatic/wifinina.go b/examples/net/webstatic/wifinina.go index 658492e71..c3f8e9199 100644 --- a/examples/net/webstatic/wifinina.go +++ b/examples/net/webstatic/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/netdev/README.md b/netdev/README.md deleted file mode 100644 index b4969e5ef..000000000 --- a/netdev/README.md +++ /dev/null @@ -1,335 +0,0 @@ -# Netdev - -#### Table of Contents - -- [Overview](#overview) -- [Using "net" Package](#using-net-package) -- [Using "net/http" Package](#using-nethttp-package) -- [Using "crypto/tls" Package](#using-cryptotls-package) -- [Using Raw Sockets](#using-raw-sockets) -- [Writing a New Driver](#writing-a-new-driver) - -## Overview - -Netdev is TinyGo's network device driver model. - -Let's see where netdev fits in the network stack. The diagram below shows the traditional full OS stack vs. different possible embedded stacks for TinyGo. Notice the application is written to the same net.Conn interface for all cases. - -![Netdev models](netdev_models.jpg) - -In the (1) Go full OS stack, the network driver, aka netdev, sits above hardware (the "nic") and below TCP/IP. The netdev provides a raw packet interface to TCP/IP. - -For TinyGo, the netdev includes TCP/IP and provides a socket(2) interface to the "net" package. Applications are written to the net.Conn interfaces: TCPConn, UDPConn, and TLSConn. net.Conn functions call netdev socket(2) calls, which in turn call into firmware/hardware. Let's consider the three use cases: - -#### (2) Firware Offload Model - -Here the networking device is a co-controller installed with firmware running a full TCP/IP stack. Firmware manages the TCP/IP connection state with the network. - -The netdev driver runs on the main controller and talks to the co-controller's firmware interface using UART/SPI/etc. The netdev driver translates socket(2) calls to the firmware's TCP/IP calls. - -The wifinina (ESP32), espat (ESP32-AT), and rtl8720dn netdev drivers are examples of the firmware offload model. - -#### (3) Full Stack Model - -Here the netdev includes the TCP/IP stack. There is no co-controller. - -#### (4) "Bring-Your-Own-net.Comm" Model - -Here the netdev is the entire stack, accessing hardware on the bottom and serving up net.Conn connections to applications. - -## Using "net" Package - -Ideally, TinyGo's "net" package would be Go's "net" package and applications using "net" would just work, as-is. TinyGo's net package is a partial port from Go's net package, replacing OS socket syscalls with netdev socket calls. TinyGo's net package is a subset of Go's net package. There are a few features excluded during the porting process, in particular: - -- No IPv6 support -- No HTTP/2 support -- No TLS support for HTTP servers (no https servers) -- No DualStack support -- HTTP client request can't be reused - -Run ```go doc -all ./src/net``` on tinygo directory to see full listing. - -Applications using Go's net package will need a few setup steps to work with TinyGo's net package. - -### Step 1: Create the netdev for your target device. - -The available netdev are: - -- [wifinina]: SPI to ESP32 WiFi co-controller running Arduino WiFiNINA firmware - - targets: pyportal arduino_nano33 nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 - -- [rtl8720dn]: UART to RealTek WiFi rtl8720dn co-controller - - targets: wioterminal - -- [espat]: UART to ESP32/ESP8266 WiFi co-controller running Espressif AT firmware - - targets: TBD - -This example configures and creates a wifinina netdev using New(). - -```go -import "tinygo.org/x/drivers/wifinina" - -func main() { - cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} - dev := wifinina.New(&cfg) - ... -} -``` - -The Config structure is netdev-specific; consult the netdev package for Config details. In this case, the WiFi credentials are passed. - -### Step 2: Hook the netdev into the net package - -Tell the net package to use the netdev by calling netdev.Use(). Continuing with the wifinina example: - -```go -import "tinygo.org/x/drivers/netdev" -import "tinygo.org/x/drivers/wifinina" - -func main() { - cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} - dev := wifinina.New(&cfg) - netdev.Use(dev) - ... -} -``` - -Now, the net package is linked to the netdev so any net I/O will go through the netdev. Calls to net.Dial(), net.Listen() etc will translate to netdev socket calls. - -The last step is to connect the netdev to an IP network. - -### Step 3: Connect to an IP Network - -Before the net package is fully functional, connect the netdev to an underlying IP network. For example, a WiFi netdev would connect to a WiFi access point or become a WiFi access point; either way, once connected, the netdev has a station IP address and is connected on the IP network. - -Call dev.NetConnect() to connect the device to an IP network. Call dev.NetDisconnect() to disconnect. Continuing example: - -```go -import "tinygo.org/x/drivers/netdev" -import "tinygo.org/x/drivers/wifinina" - -func main() { - cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} - dev := wifinina.New(&cfg) - netdev.Use(dev) - - dev.NetConnect() - - // net package calls here - - dev.NetDisconnect() -} -``` - -Optionally, get notified of IP network connects and disconnects: - -```go - dev.Notify(func(e netdev.Event) { - switch e { - case netdev.EventNetUp: - println("Network UP") - case netdev.EventNetDown: - println("Network DOWN") - }) -``` - -Here is a simple http server listening on port :8080, before and after porting from Go "net/http": - -#### Before -```go -package main - -import ( - "fmt" - "net/http" -) - -func main() { - http.HandleFunc("/", HelloServer) - http.ListenAndServe(":8080", nil) -} - -func HelloServer(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) -} -``` - -#### After -```go -package main - -import ( - "fmt" - "net/http" - - "tinygo.org/x/drivers/netdev" - "tinygo.org/x/drivers/wifinina" -) - -func main() { - cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} - dev := wifinina.New(&cfg) - netdev.Use(dev) - dev.NetConnect() - - http.HandleFunc("/", HelloServer) - http.ListenAndServe(":8080", nil) -} - -func HelloServer(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) -} -``` - -## Using "net/http" Package - -TinyGo's net/http package is a partial port of Go's net/http package, providing a subset of the full net/http package. - -HTTP client methods (http.Get, http.Head, http.Post, and http.PostForm) are functional. Dial clients support both HTTP and HTTPS URLs. - -HTTP server methods and objects are mostly ported, but for HTTP only; HTTPS servers are not supported. - -HTTP request and response handling code is mostly ported, so most the intricacy of parsing and writing headers is handled as in the full net/http package. - -Run ```go doc -all ./src/net/http``` on tinygo directory to see full listing. - -## Using "crypto/tls" Package - -TinyGo's TLS support (crypto/tls) relies on hardware offload of the TLS protocol. This is different from Go's crypto/tls package which handles the TLS protocol in software. - -TinyGo's TLS support is only available for client applications. You can http.Get() to an http:// or https:// address, but you cannot http.ListenAndServeTLS() an https server. - -The offloading hardware has pre-defined TLS certificates built-in. - -## Using Raw Sockets - -A netdev implements the Socketer interface so an application can make raw socket calls, bypassing the net package. - -Here is a simple TCP application using raw sockets: - -```go -package main - -import ( - "tinygo.org/x/drivers/netdev" - "tinygo.org/x/drivers/wifinina" -) - -func main() { - cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} - dev := wifinina.New(&cfg) - netdev.Use(dev) - - // ignoring error handling - - dev.NetConnect() - - sock, _ := dev.Socket(netdev.AF_INET, netdev.SOCK_STREAM, netdev.IPPROTO_TCP) - - sockAddr := netdev.NewSockAddr("", netdev.Port(8080), netdev.ParseIP("10.0.0.100") - dev.Connect(sock, sockAddr) - - dev.Send(sock, []bytes("hello"), 0, 0) - - dev.Close(sock) -} -``` - -## Writing a New Driver - -Netdev drivers implement the netdev.Netdever interface, which includes the netdev.Socketer interface. The Socketer interface is modeled after BSD socket(2). TinyGo's net package translates net.Conn calls into netdev Socketer calls. For example, DialTCP calls netdev.Socket() and netdev.Connect(): - -```go -func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error) { - - fd, _ := netdev.Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) - - addr := NewSockAddr("", uint16(raddr.Port), raddr.IP) - - netdev.Connect(fd, addr) - - return &TCPConn{ - fd: fd, - laddr: laddr, - raddr: raddr, - }, nil -} -``` - -### Netdever Interface - -A netdev driver implements the Netdever interface: - -```go -// Netdev drivers implement the Netdever interface. -// -// A Netdever is passed to the "net" package using netdev.Use(). -// -// Just like a net.Conn, multiple goroutines may invoke methods on a Netdever -// simultaneously. -type Netdever interface { - - // NetConnect device to IP network - NetConnect() error - - // NetDisconnect device from IP network - NetDisconnect() - - // NetNotify to register callback for network events - NetNotify(func(Event)) - - // GetHostByName returns the IP address of either a hostname or IPv4 - // address in standard dot notation - GetHostByName(name string) (IP, error) - - // GetHardwareAddr returns device MAC address - GetHardwareAddr() (HardwareAddr, error) - - // GetIPAddr returns IP address assigned to device, either by DHCP or - // statically - GetIPAddr() (IP, error) - - // Socketer is a Berkely Sockets-like interface - Socketer -} -``` - -### Socketer Interface - -```go -// Berkely Sockets-like interface. See man page for socket(2), etc. -// -// Multiple goroutines may invoke methods on a Socketer simultaneously. -type Socketer interface { - Socket(family AddressFamily, sockType SockType, protocol Protocol) (Sockfd, error) - Bind(sockfd Sockfd, myaddr SockAddr) error - Connect(sockfd Sockfd, servaddr SockAddr) error - Listen(sockfd Sockfd, backlog int) error - Accept(sockfd Sockfd, peer SockAddr) (Sockfd, error) - Send(sockfd Sockfd, buf []byte, flags SockFlags, timeout time.Duration) (int, error) - Recv(sockfd Sockfd, buf []byte, flags SockFlags, timeout time.Duration) (int, error) - Close(sockfd Sockfd) error - SetSockOpt(sockfd Sockfd, level SockOptLevel, opt SockOpt, value any) error -} -``` - -Socketer interface is intended to mimic a subset of BSD socket(2). They've been Go-ified, but should otherwise maintain the semantics of the original socket(2) calls. Send and Recv add a timeout to put a limit on blocking operations. Recv in paricular is blocking and will block until data arrives on the socket or EOF. The timeout value is calculated from net.Conn's SetDeadline(), typically. - -#### Locking - -Multiple goroutines may invoke methods on a net.Conn simultaneously, and since the net package translates net.Conn calls into Socketer calls, it follows that multiple goroutines may invoke Socketer calls, so locking is required to keep Socketer calls from stepping on one another. - -Don't hold a lock while Time.Sleep()ing waiting for a hardware operation to finish. Unlocking while sleeping let's other goroutines make progress. If the sleep period is really small, then you can get away with holding the lock. - -#### Sockfd - -The Socketer interface uses a socket fd to represent a socket connection (end-point). Each net.Conn maps 1:1 to a fd. The number of fds available is a netdev hardware limitation. Wifinina, for example, can hand out 10 socket fds. - -### Testing - -The netdev driver should minimally run all of the example/net examples. - -TODO: automate testing to catch regressions. diff --git a/netdev/netdev.go b/netdev/netdev.go deleted file mode 100644 index 4527c62f9..000000000 --- a/netdev/netdev.go +++ /dev/null @@ -1,137 +0,0 @@ -// Netdev is TinyGo's network device driver model. TinyGo's "net" package -// interfaces to the netdev driver directly to provide TCPConn, UDPConn, -// and TLSConn socket connections. - -package netdev - -import ( - "errors" - "fmt" - "strconv" - "strings" - _ "unsafe" -) - -//go:linkname Use net.useNetdev -func Use(netdev Netdever) - -type HardwareAddr []byte - -const hexDigit = "0123456789abcdef" - -func (a HardwareAddr) String() string { - if len(a) == 0 { - return "" - } - buf := make([]byte, 0, len(a)*3-1) - for i, b := range a { - if i > 0 { - buf = append(buf, ':') - } - buf = append(buf, hexDigit[b>>4]) - buf = append(buf, hexDigit[b&0xF]) - } - return string(buf) -} - -func ParseHardwareAddr(s string) HardwareAddr { - parts := strings.Split(s, ":") - if len(parts) != 6 { - return nil - } - - var mac []byte - for _, part := range parts { - b, err := strconv.ParseInt(part, 16, 64) - if err != nil { - return nil - } - mac = append(mac, byte(b)) - } - - return HardwareAddr(mac) -} - -type Port uint16 // host byte-order - -type IP [4]byte - -func (ip IP) String() string { - return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]) -} - -func ParseIP(s string) IP { - var result IP - octets := strings.Split(s, ".") - if len(octets) != 4 { - return IP{} - } - for i, octet := range octets { - v, err := strconv.Atoi(octet) - if err != nil { - return IP{} - } - result[i] = byte(v) - } - return result -} - -// NetConnect() errors -var ErrConnected = errors.New("Already connected") -var ErrConnectFailed = errors.New("Connect failed") -var ErrConnectTimeout = errors.New("Connect timed out") -var ErrMissingSSID = errors.New("Missing WiFi SSID") -var ErrStartingDHCPClient = errors.New("Error starting DHPC client") - -// GethostByName() errors -var ErrHostUnknown = errors.New("Host unknown") - -// Socketer errors -var ErrFamilyNotSupported = errors.New("Address family not supported") -var ErrProtocolNotSupported = errors.New("Socket protocol/type not supported") -var ErrNoMoreSockets = errors.New("No more sockets") -var ErrClosingSocket = errors.New("Error closing socket") -var ErrNotSupported = errors.New("Not supported") -var ErrRecvTimeout = errors.New("Recv timeout expired") - -type Event int - -// NetNotify network events -const ( - // The device's network connection is now UP - EventNetUp Event = iota - // The device's network connection is now DOWN - EventNetDown -) - -// Netdev drivers implement the Netdever interface. -// -// A Netdever is passed to the "net" package using netdev.Use(). -// -// Just like a net.Conn, multiple goroutines may invoke methods on a Netdever -// simultaneously. -type Netdever interface { - - // NetConnect device to IP network - NetConnect() error - - // NetDisconnect device from IP network - NetDisconnect() - - // NetNotify to register callback for network events - NetNotify(func(Event)) - - // GetHostByName returns the IP address of either a hostname or IPv4 - // address in standard dot notation - GetHostByName(name string) (IP, error) - - // GetHardwareAddr returns device MAC address - GetHardwareAddr() (HardwareAddr, error) - - // GetIPAddr returns IP address assigned to device, either by DHCP or - // statically - GetIPAddr() (IP, error) - - // Socketer is a Berkely Sockets-like interface - Socketer -} diff --git a/netdev/netdev_models.jpg b/netdev/netdev_models.jpg deleted file mode 100644 index 04abc822d6fa0880abbdfa641094ed8b4aa889fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65432 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 diff --git a/netdev/socket.go b/netdev/socket.go deleted file mode 100644 index 2f3e112a4..000000000 --- a/netdev/socket.go +++ /dev/null @@ -1,143 +0,0 @@ -// Netdev socket interface - -package netdev - -import ( - "fmt" - "time" -) - -type AddressFamily int - -const ( - AF_UNSPEC = 0 - AF_INET = 2 - // Currently not supporting AF_INET6 (IPv6) -) - -func (f AddressFamily) String() string { - switch f { - case AF_INET: - return "INET" - } - return "Unknown" -} - -type SockType int - -const ( - SOCK_STREAM = 1 - SOCK_DGRAM = 2 -) - -func (t SockType) String() string { - switch t { - case SOCK_STREAM: - return "STREAM" - case SOCK_DGRAM: - return "DATAGRAM" - } - return "Unknown" -} - -type Protocol int - -const ( - IPPROTO_TCP = 0x06 - IPPROTO_UDP = 0x11 - // Made up, not a real IP protocol number. This is used to create - // a TLS socket on the device, assuming the device supports mbed TLS. - IPPROTO_TLS = 0xFE -) - -func (p Protocol) String() string { - switch p { - case IPPROTO_TCP: - return "TCP" - case IPPROTO_UDP: - return "UDP" - case IPPROTO_TLS: - return "TLS" - } - return "Unknown" -} - -// sockaddr_in -type SockAddr struct { - host string - port Port - ip IP -} - -func NewSockAddr(host string, port Port, ip IP) SockAddr { - return SockAddr{host: host, port: port, ip: ip} -} - -func (a SockAddr) String() string { - if a.host == "" { - return fmt.Sprintf("%s:%d", a.ip.String(), a.port) - } - return fmt.Sprintf("%s:%d", a.host, a.port) -} - -func (a SockAddr) Port() uint16 { - return uint16(a.port) -} - -func (a SockAddr) IpUint32() uint32 { - return uint32(a.ip[0])<<24 | - uint32(a.ip[1])<<16 | - uint32(a.ip[2])<<8 | - uint32(a.ip[3]) -} - -func (a SockAddr) IpBytes() []byte { - return a.ip[:] -} - -func (a SockAddr) Host() string { - return a.host -} - -func (a SockAddr) Ip() IP { - return a.ip -} - -type SockFlags int - -const ( - MSG_OOB SockFlags = 1 - MSG_PEEK = 2 - MSG_DONTROUTE = 4 -) - -type SockOpt int - -const ( - SO_KEEPALIVE SockOpt = 9 // Value: bool - TCP_KEEPINTVL = 5 // Interval between keepalives, value (1/2 sec units) -) - -type SockOptLevel int - -const ( - SOL_SOCKET SockOptLevel = 1 - SOL_TCP = 6 -) - -type Sockfd int - -// Berkely Sockets-like interface. See man page for socket(2), etc. -// -// Multiple goroutines may invoke methods on a Socketer simultaneously. -type Socketer interface { - Socket(family AddressFamily, sockType SockType, protocol Protocol) (Sockfd, error) - Bind(sockfd Sockfd, myaddr SockAddr) error - Connect(sockfd Sockfd, servaddr SockAddr) error - Listen(sockfd Sockfd, backlog int) error - Accept(sockfd Sockfd, peer SockAddr) (Sockfd, error) - Send(sockfd Sockfd, buf []byte, flags SockFlags, timeout time.Duration) (int, error) - Recv(sockfd Sockfd, buf []byte, flags SockFlags, timeout time.Duration) (int, error) - Close(sockfd Sockfd) error - SetSockOpt(sockfd Sockfd, level SockOptLevel, opt SockOpt, value any) error -} diff --git a/rtl8720dn/rtl8720dn.go b/rtl8720dn/rtl8720dn.go index 35bfff496..cc1c1e71a 100644 --- a/rtl8720dn/rtl8720dn.go +++ b/rtl8720dn/rtl8720dn.go @@ -11,11 +11,10 @@ import ( "fmt" "io" "machine" + "net/netdev" "strings" "sync" "time" - - "tinygo.org/x/drivers/netdev" ) var _debug debug = debugBasic diff --git a/wifinina/wifinina.go b/wifinina/wifinina.go index ea6f008df..98c77005d 100644 --- a/wifinina/wifinina.go +++ b/wifinina/wifinina.go @@ -16,11 +16,11 @@ import ( "io" "machine" "math/bits" + "net/netdev" "sync" "time" "tinygo.org/x/drivers" - "tinygo.org/x/drivers/netdev" ) var _debug debug = debugBasic From 0adc7e78aa69bcbd4e2aeaf88000231ba22da365 Mon Sep 17 00:00:00 2001 From: Brian Park Date: Wed, 1 Mar 2023 16:59:14 -0800 Subject: [PATCH 21/47] ds3231: Fix negative temperature conversion I also verified my interpretation of the DS3231 datasheet and my implementation of this formula by placing a DS3231 chip in the freezer section of my refrigerator for an hour or two, then reading the temperature. I got -16C, which is close enough to the -18C that the freezer was set to. --- ds3231/ds3231.go | 22 ++++++++++++- ds3231/ds3231_test.go | 76 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 ds3231/ds3231_test.go diff --git a/ds3231/ds3231.go b/ds3231/ds3231.go index d29495b75..aad4d19b1 100644 --- a/ds3231/ds3231.go +++ b/ds3231/ds3231.go @@ -140,7 +140,27 @@ func (d *Device) ReadTemperature() (int32, error) { if err != nil { return 0, err } - return int32(data[0])*1000 + int32((data[1]>>6)*25)*10, nil + return milliCelsius(data[0], data[1]), nil +} + +// milliCelsius converts the raw temperature bytes (msb and lsb) from the DS3231 +// into a 32-bit signed integer in units of milli Celsius (1/1000 deg C). +// +// According to the DS3231 datasheet: "Temperature is represented as a 10-bit +// code with a resolution of 0.25 deg C and is accessible at location 11h and +// 12h. The temperature is encoded in two's complement format. The upper 8 bits, +// the integer portion, are at location 11h and the lower 2 bits, the fractional +// portion, are in the upper nibble at location 12h." +// +// In other words, the msb and lsb bytes should be treated as a signed 16-bit +// integer in units of (1/256 deg C). It is possible to convert this into a +// 16-bit signed integer in units of centi Celsius (1/100 deg C) with no loss of +// precision or dynamic range. But for backwards compatibility, let's instead +// convert this into a 32-bit signed integer in units of milli Celsius. +func milliCelsius(msb uint8, lsb uint8) int32 { + t256 := int16(uint16(msb)<<8 | uint16(lsb)) + t1000 := int32(t256) / 64 * 250 + return t1000 } // uint8ToBCD converts a byte to BCD for the DS3231 diff --git a/ds3231/ds3231_test.go b/ds3231/ds3231_test.go new file mode 100644 index 000000000..91ba3c704 --- /dev/null +++ b/ds3231/ds3231_test.go @@ -0,0 +1,76 @@ +package ds3231 + +import ( + "testing" +) + +func TestPositiveMilliCelsius(t *testing.T) { + t1000 := milliCelsius(0, 0) + if t1000 != 0 { + t.Fatal(t1000) + } + + t1000 = milliCelsius(0, 0b01000000) + if t1000 != 250 { + t.Fatal(t1000) + } + + t1000 = milliCelsius(0, 0b10000000) + if t1000 != 500 { + t.Fatal(t1000) + } + + t1000 = milliCelsius(0, 0b11000000) + if t1000 != 750 { + t.Fatal(t1000) + } + + t1000 = milliCelsius(1, 0b00000000) + if t1000 != 1000 { + t.Fatal(t1000) + } + + t1000 = milliCelsius(2, 0b00000000) + if t1000 != 2000 { + t.Fatal(t1000) + } + + // highest temperature is 127.750C + t1000 = milliCelsius(0x7f, 0b11000000) + if t1000 != 127750 { + t.Fatal(t1000) + } +} + +func TestNegativeMilliCelsius(t *testing.T) { + t1000 := milliCelsius(0xff, 0b11000000) + if t1000 != -250 { + t.Fatal(t1000) + } + + t1000 = milliCelsius(0xff, 0b10000000) + if t1000 != -500 { + t.Fatal(t1000) + } + + t1000 = milliCelsius(0xff, 0b01000000) + if t1000 != -750 { + t.Fatal(t1000) + } + + t1000 = milliCelsius(0xff, 0b00000000) + if t1000 != -1000 { + t.Fatal(t1000) + } + + t1000 = milliCelsius(0xfe, 0b00000000) + if t1000 != -2000 { + t.Fatal(t1000) + } + + // lowest temperature is -128.000C + t1000 = milliCelsius(0x80, 0b00000000) + if t1000 != -128000 { + t.Fatal(t1000) + } +} From 6c6cf3490d8343faa416dbc37a9f71dfa9733f3f Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Sat, 4 Mar 2023 16:06:16 -0800 Subject: [PATCH 22/47] Use mirrored netdever interface Changes based on feedback from Ben and Patricio: 0) Move netdever interface to "drivers" package 1) Mirror netdever interface with same in drivers repo 2) Use go:linkname to link to net.useNetdev() 3) Use native Go types in netdever interface 4) Add Netlinker interface to "drivers" package 5) Update documentation, add new README-net.md file --- README-net.md | 289 ++++++++++++++++++++ espat/espat.go | 124 +++++---- espat/wifi.go | 3 +- examples/net/espat/main.go | 9 +- examples/net/http-get/main.go | 2 +- examples/net/http-get/rtl8720dn.go | 12 +- examples/net/http-get/wifinina.go | 12 +- examples/net/http-head/main.go | 4 +- examples/net/http-head/rtl8720dn.go | 12 +- examples/net/http-head/wifinina.go | 12 +- examples/net/http-post/main.go | 4 +- examples/net/http-post/rtl8720dn.go | 12 +- examples/net/http-post/wifinina.go | 12 +- examples/net/http-postform/main.go | 2 +- examples/net/http-postform/rtl8720dn.go | 12 +- examples/net/http-postform/wifinina.go | 12 +- examples/net/mqttclient/main.go | 2 +- examples/net/mqttclient/rtl8720dn.go | 12 +- examples/net/mqttclient/wifinina.go | 12 +- examples/net/ntpclient/main.go | 4 +- examples/net/ntpclient/rtl8720dn.go | 12 +- examples/net/ntpclient/wifinina.go | 12 +- examples/net/rawsocket/main.go | 25 +- examples/net/rawsocket/rtl8720dn.go | 10 +- examples/net/rawsocket/wifinina.go | 10 +- examples/net/tcpclient/main.go | 2 +- examples/net/tcpclient/rtl8720dn.go | 12 +- examples/net/tcpclient/wifinina.go | 12 +- examples/net/tcpecho/main.go | 2 +- examples/net/tcpecho/rtl8720dn.go | 12 +- examples/net/tcpecho/wifinina.go | 12 +- examples/net/tlsclient/main.go | 2 +- examples/net/tlsclient/rtl8720dn.go | 12 +- examples/net/tlsclient/wifinina.go | 12 +- examples/net/webclient-tinyterm/main.go | 16 +- examples/net/webclient/main.go | 2 +- examples/net/webclient/rtl8720dn.go | 12 +- examples/net/webclient/wifinina.go | 12 +- examples/net/webserver/main.go | 2 +- examples/net/webserver/rtl8720dn.go | 12 +- examples/net/webserver/wifinina.go | 12 +- examples/net/websocket/dial/main.go | 2 +- examples/net/websocket/dial/rtl8720dn.go | 12 +- examples/net/websocket/dial/wifinina.go | 12 +- examples/net/websocket/handler/main.go | 2 +- examples/net/websocket/handler/rtl8720dn.go | 12 +- examples/net/websocket/handler/wifinina.go | 12 +- examples/net/webstatic/main.go | 2 +- examples/net/webstatic/rtl8720dn.go | 12 +- examples/net/webstatic/wifinina.go | 12 +- netdev.go | 59 ++++ netlink.go | 49 ++++ rtl8720dn/rtl8720dn.go | 213 ++++++++------- wifinina/wifinina.go | 198 ++++++++------ 54 files changed, 750 insertions(+), 625 deletions(-) create mode 100644 README-net.md create mode 100644 netdev.go create mode 100644 netlink.go diff --git a/README-net.md b/README-net.md new file mode 100644 index 000000000..2a126282b --- /dev/null +++ b/README-net.md @@ -0,0 +1,289 @@ +#### Table of Contents + +- ["net" Package](#net-package) +- [Using "net" Package](#using-net-package) +- [Using "net/http" Package](#using-nethttp-package) +- [Using "crypto/tls" Package](#using-cryptotls-package) +- [Using Raw Sockets](#using-raw-sockets) +- [Netdev and Netlink](#netdev-and-netlink) +- [Writing a New Netdev Driver](#writing-a-new-netdev-driver) + +## "net" Package + +TinyGo's "net" package is ported from Go. The port offers a subset of Go's +"net" package. The subset maintains Go 1 compatiblity guarantee. A Go +application that uses "net" will most-likey just work on TinyGo if the usage is +within the subset offered. (There may be external constraints such as limited +SRAM on embedded environment that may limit full functionality). + +Continue below for details on using "net" and "net/http" packages. + +See src/net/READMD.md in the TinyGo repo for more details on maintaining +TinyGo's "net" package. + +## Using "net" Package + +Ideally, TinyGo's "net" package would be Go's "net" package and applications +using "net" would just work, as-is. TinyGo's net package is a partial port +from Go's net package, replacing OS socket syscalls with netdev socket calls. + +Netdev is TinyGo's network device driver model; read more about +[Netdev](#netdev-and-netlink). + +There are a few features excluded during the porting process, in particular: + +- No IPv6 support +- No DualStack support + +Run ```go doc -all ./src/net``` in TinyGo repo to see full listing. + +Applications using Go's net package will need a few setup steps to work with +TinyGo's net package. + +### Step 1: Create the netdev for your target device. + +The available netdev are: + +- [wifinina]: ESP32 WiFi co-controller running Arduino WiFiNINA firmware + + targets: pyportal arduino_nano33 nano_rp2040 metro_m4_airlift + arduino_mkrwifi1010 matrixportal_m4 + +- [rtl8720dn]: RealTek WiFi rtl8720dn co-controller + + targets: wioterminal + +- [espat]: ESP32/ESP8266 WiFi co-controller running Espressif AT firmware + + targets: TBD + +This example configures and creates a wifinina netdev using New(). + +```go +import "tinygo.org/x/drivers/wifinina" + +func main() { + cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} + netdev := wifinina.New(&cfg) + ... +} +``` + +New() registers the netdev with the "net" package using net.useNetdev(). + +The Config structure is netdev-specific; consult the specific netdev package +for Config details. In this case, the WiFi credentials are passed, but other +settings are typically passed such as device configuration. + +### Step 2: Connect to an IP Network + +Before the net package is fully functional, connect the netdev to an underlying +IP network. For example, a WiFi netdev would connect to a WiFi access point or +become a WiFi access point; either way, once connected, the netdev has a +station IP address and is connected on the IP network. Similarly, a LTE netdev +would connect to a LTE provider, giving the device an IP address on the LTE +network. + +Using the Netlinker interface, Call netdev.NetConnect() to connect the device +to an IP network. Call netdev.NetDisconnect() to disconnect. Continuing example: + +```go +import ( + "tinygo.org/x/drivers/wifinina" +) + +func main() { + cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} + netdev := wifinina.New(&cfg) + + netdev.NetConnect() + + // "net" package calls here + + netdev.NetDisconnect() +} +``` + +Optionally, get notified of IP network connects and disconnects: + +```go + netdev.Notify(func(e drivers.NetlinkEvent) { + switch e { + case drivers.NetlinkEventNetUp: + println("Network UP") + case drivers.NetlinkEventNetDown: + println("Network DOWN") + }) +``` + +Here is a simple example of an http server listening on port :8080, before and +after: + +#### Before +```go +package main + +import ( + "fmt" + "net/http" +) + +func main() { + http.HandleFunc("/", HelloServer) + http.ListenAndServe(":8080", nil) +} + +func HelloServer(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) +} +``` + +#### After +```go +package main + +import ( + "fmt" + "net/http" + + "tinygo.org/x/drivers/wifinina" +) + +func main() { + cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} + netdev := wifinina.New(&cfg) + netdev.NetConnect() + + http.HandleFunc("/", HelloServer) + http.ListenAndServe(":8080", nil) +} + +func HelloServer(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) +} +``` + +## Using "net/http" Package + +TinyGo's net/http package is a partial port of Go's net/http package, providing +a subset of the full net/http package. There are a few features excluded +during the porting process, in particular: + +- No HTTP/2 support +- No TLS support for HTTP servers (no https servers) +- HTTP client request can't be reused + +HTTP client methods (http.Get, http.Head, http.Post, and http.PostForm) are +functional. Dial clients support both HTTP and HTTPS URLs. + +HTTP server methods and objects are mostly ported, but for HTTP only; HTTPS +servers are not supported. + +HTTP request and response handling code is mostly ported, so most the intricacy +of parsing and writing headers is handled as in the full net/http package. + +Run ```go doc -all ./src/net/http``` in TinyGo repo to see full listing. + +## Using "crypto/tls" Package + +TinyGo's TLS support (crypto/tls) relies on hardware offload of the TLS +protocol. This is different from Go's crypto/tls package which handles the TLS +protocol in software. + +TinyGo's TLS support is only available for client applications. You can +http.Get() to an http:// or https:// address, but you cannot +http.ListenAndServeTLS() an https server. + +The offloading hardware has pre-defined TLS certificates built-in. + +## Using Raw Sockets + +A netdev implements a BSD socket-like interface so an application can make raw +socket calls, bypassing the net package. + +Here is a simple TCP application using raw sockets: + +```go +package main + +import ( + "net" // only need to parse IP address + "syscall" + + "tinygo.org/x/drivers/wifinina" +) + +func main() { + cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} + netdev := wifinina.New(&cfg) + + // ignoring error handling + + netdev.NetConnect() + + sock, _ := netdev.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + + netdev.Connect(sock, "", net.ParseIP("10.0.0.100"), 8080) + netdev.Send(sock, []bytes("hello"), 0, 0) + + netdev.Close(sock) +} +``` + +## Netdev and Netlink + +Netdev is TinyGo's network device driver model. Network drivers implement the +netdever interface, providing a common network I/O interface to TinyGo's "net" +package. The interface is modeled after the BSD socket interface. net.Conn +implementations (TCPConn, UDPConn, and TLSConn) use the netdev interface for +device I/O access. For example, net.DialTCP, which returns a net.TCPConn, +calls netdev.Socket() and netdev.Connect(): + +```go +func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error) { + + fd, _ := netdev.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + + netdev.Connect(fd, "", raddr.IP, raddr.Port) + + return &TCPConn{ + fd: fd, + laddr: laddr, + raddr: raddr, + }, nil +} +``` + +Network drivers also (optionally) implement the Netlinker interface. This +interface is not used by TinyGo's "net" package, but rather provides the TinyGo +application direct access to the network device for common settings and control +that fall outside of netdev's socket interface. + +## Writing a New Netdev Driver + +A new netdev driver will implement the netdever and optionally the Netlinker +interfaces. See the wifinina or rtl8720dn drivers for examples. + +#### Locking + +Multiple goroutines may invoke methods on a net.Conn simultaneously, and since +the net package translates net.Conn calls into netdev socket calls, it follows +that multiple goroutines may invoke socket calls, so locking is required to +keep socket calls from stepping on one another. + +Don't hold a lock while Time.Sleep()ing waiting for a hardware operation to +finish. Unlocking while sleeping let's other goroutines make progress. If the +sleep period is really small, then you can get away with holding the lock. + +#### Sockfd + +The netdev socket interface uses a socket fd (int) to represent a socket +connection (end-point). Each net.Conn maps 1:1 to a fd. The number of fds +available is a hardware limitation. Wifinina, for example, can hand out 10 +fds. + +### Testing + +The netdev driver should minimally run all of the example/net examples. + +TODO: automate testing to catch regressions. diff --git a/espat/espat.go b/espat/espat.go index e949db96e..f1ca0b5bd 100644 --- a/espat/espat.go +++ b/espat/espat.go @@ -24,8 +24,10 @@ import ( "errors" "fmt" "machine" + "net" "strconv" "strings" + "syscall" "time" "tinygo.org/x/drivers/netdev" @@ -47,24 +49,31 @@ type Config struct { Rx machine.Pin } +type socket struct { + inUse bool + protocol int + lip net.IP + lport int +} + type Device struct { cfg *Config uart *machine.UART // command responses that come back from the ESP8266/ESP32 response []byte // data received from a TCP/UDP connection forwarded by the ESP8266/ESP32 - socketdata []byte - socketInUse bool - socketProtocol netdev.Protocol - socketLaddr netdev.SockAddr + data []byte + socket socket } func New(cfg *Config) *Device { d := Device{ - cfg: cfg, - response: make([]byte, 512), - socketdata: make([]byte, 0, 1024), + cfg: cfg, + response: make([]byte, 512), + data: make([]byte, 0, 1024), } + netdev.Use(&d) + var _ netdev.Ifacer = (*Device)(nil) return &d } @@ -136,84 +145,84 @@ func (d *Device) NetNotify(cb func(netdev.Event)) { // Not supported } -func (d *Device) GetHostByName(name string) (netdev.IP, error) { +func (d *Device) GetHostByName(name string) (net.IP, error) { ip, err := d.GetDNS(name) - return netdev.ParseIP(ip), err + return net.ParseIP(ip), err } -func (d *Device) GetHardwareAddr() (netdev.HardwareAddr, error) { - return netdev.ParseHardwareAddr(d.GetMacAddress()), nil +func (d *Device) GetHardwareAddr() (net.HardwareAddr, error) { + return net.ParseMAC(d.GetMacAddress()) } -func (d *Device) GetIPAddr() (netdev.IP, error) { +func (d *Device) GetIPAddr() (net.IP, error) { ip, err := d.GetClientIP() - return netdev.ParseIP(ip), err + return net.ParseIP(ip), err } -func (d *Device) Socket(family netdev.AddressFamily, sockType netdev.SockType, - protocol netdev.Protocol) (netdev.Sockfd, error) { +func (d *Device) Socket(domain int, stype int, protocol int) (int, error) { - switch family { - case netdev.AF_INET: + switch domain { + case syscall.AF_INET: default: return -1, netdev.ErrFamilyNotSupported } switch { - case protocol == netdev.IPPROTO_TCP && sockType == netdev.SOCK_STREAM: - case protocol == netdev.IPPROTO_TLS && sockType == netdev.SOCK_STREAM: - case protocol == netdev.IPPROTO_UDP && sockType == netdev.SOCK_DGRAM: + case protocol == syscall.IPPROTO_TCP && stype == syscall.SOCK_STREAM: + case protocol == syscall.IPPROTO_TLS && stype == syscall.SOCK_STREAM: + case protocol == syscall.IPPROTO_UDP && stype == syscall.SOCK_DGRAM: default: return -1, netdev.ErrProtocolNotSupported } // Only supporting single connection mode, so only one socket at a time - if d.socketInUse { + if d.socket.inUse { return -1, netdev.ErrNoMoreSockets } - d.socketInUse = true - d.socketProtocol = protocol + d.socket.inUse = true + d.socket.protocol = protocol - return netdev.Sockfd(0), nil + return 0, nil } -func (d *Device) Bind(sockfd netdev.Sockfd, addr netdev.SockAddr) error { - d.socketLaddr = addr +func (d *Device) Bind(sockfd int, ip net.IP, port int) error { + d.socket.lip = ip + d.socket.lport = port return nil } -func (d *Device) Connect(sockfd netdev.Sockfd, servaddr netdev.SockAddr) error { +func (d *Device) Connect(sockfd int, host string, ip net.IP, port int) error { var err error - var addr = servaddr.Ip().String() - var port = fmt.Sprintf("%d", servaddr.Port()) - var lport = fmt.Sprintf("%d", d.socketLaddr.Port()) - - switch d.socketProtocol { - case netdev.IPPROTO_TCP: - err = d.ConnectTCPSocket(addr, port) - case netdev.IPPROTO_UDP: - err = d.ConnectUDPSocket(addr, port, lport) - case netdev.IPPROTO_TLS: - err = d.ConnectSSLSocket(addr, port) + var addr = ip.String() + var sport = strconv.Itoa(port) + var lport = strconv.Itoa(d.socket.lport) + + switch d.socket.protocol { + case syscall.IPPROTO_TCP: + err = d.ConnectTCPSocket(addr, sport) + case syscall.IPPROTO_UDP: + err = d.ConnectUDPSocket(addr, sport, lport) + case syscall.IPPROTO_TLS: + err = d.ConnectSSLSocket(addr, sport) } return err } -func (d *Device) Listen(sockfd netdev.Sockfd, backlog int) error { - switch d.socketProtocol { - case netdev.IPPROTO_UDP: +func (d *Device) Listen(sockfd int, backlog int) error { + switch d.socket.protocol { + case syscall.IPPROTO_UDP: default: return netdev.ErrProtocolNotSupported } return nil } -func (d *Device) Accept(sockfd netdev.Sockfd, peer netdev.SockAddr) (netdev.Sockfd, error) { +func (d *Device) Accept(sockfd int, ip net.IP, port int) (int, error) { return -1, netdev.ErrNotSupported } -func (d *Device) sendChunk(sockfd netdev.Sockfd, buf []byte, timeout time.Duration) (int, error) { +func (d *Device) sendChunk(sockfd int, buf []byte, timeout time.Duration) (int, error) { err := d.StartSocketSend(len(buf)) if err != nil { return -1, err @@ -229,8 +238,7 @@ func (d *Device) sendChunk(sockfd netdev.Sockfd, buf []byte, timeout time.Durati return n, err } -func (d *Device) Send(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, - timeout time.Duration) (int, error) { +func (d *Device) Send(sockfd int, buf []byte, flags int, timeout time.Duration) (int, error) { // Break large bufs into chunks so we don't overrun the hw queue @@ -249,8 +257,7 @@ func (d *Device) Send(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, return len(buf), nil } -func (d *Device) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, - timeout time.Duration) (int, error) { +func (d *Device) Recv(sockfd int, buf []byte, flags int, timeout time.Duration) (int, error) { var length = len(buf) var expire = time.Now().Add(timeout) @@ -281,12 +288,11 @@ func (d *Device) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, } } -func (d *Device) Close(sockfd netdev.Sockfd) error { +func (d *Device) Close(sockfd int) error { return d.DisconnectSocket() } -func (d *Device) SetSockOpt(sockfd netdev.Sockfd, level netdev.SockOptLevel, - opt netdev.SockOpt, value any) error { +func (d *Device) SetSockOpt(sockfd int, level int, opt int, value interface{}) error { return netdev.ErrNotSupported } @@ -370,16 +376,16 @@ func (d *Device) ReadSocket(b []byte) (n int, err error) { d.Response(300) count := len(b) - if len(b) >= len(d.socketdata) { + if len(b) >= len(d.data) { // copy it all, then clear socket data - count = len(d.socketdata) - copy(b, d.socketdata[:count]) - d.socketdata = d.socketdata[:0] + count = len(d.data) + copy(b, d.data[:count]) + d.data = d.data[:0] } else { // copy all we can, then keep the remaining socket data around - copy(b, d.socketdata[:count]) - copy(d.socketdata, d.socketdata[count:]) - d.socketdata = d.socketdata[:len(d.socketdata)-count] + copy(b, d.data[:count]) + copy(d.data, d.data[count:]) + d.data = d.data[:len(d.data)-count] } return count, nil @@ -449,11 +455,11 @@ func (d *Device) parseIPD(end int) error { } // load up the socket data - d.socketdata = append(d.socketdata, d.response[e+1:end]...) + d.data = append(d.data, d.response[e+1:end]...) return nil } // IsSocketDataAvailable returns of there is socket data available func (d *Device) IsSocketDataAvailable() bool { - return len(d.socketdata) > 0 || d.uart.Buffered() > 0 + return len(d.data) > 0 || d.uart.Buffered() > 0 } diff --git a/espat/wifi.go b/espat/wifi.go index ff752234f..203dd9390 100644 --- a/espat/wifi.go +++ b/espat/wifi.go @@ -54,8 +54,7 @@ func (d *Device) DisconnectFromAP() error { return err } -// GetClientIP returns the ESP8266/ESP32 current station MAC addess when -// connected to an Access Point. +// GetMacAddress returns the ESP8266/ESP32 current station MAC addess func (d *Device) GetMacAddress() string { d.Query(SetStationMACAddress) r, err := d.Response(1000) diff --git a/examples/net/espat/main.go b/examples/net/espat/main.go index 539049a9a..2c9660756 100644 --- a/examples/net/espat/main.go +++ b/examples/net/espat/main.go @@ -9,7 +9,6 @@ import ( "time" "tinygo.org/x/drivers/espat" - "tinygo.org/x/drivers/netdev" ) var ( @@ -19,15 +18,14 @@ var ( ) var ( - netcfg = espat.Config{ + cfg = espat.Config{ Ssid: ssid, Passphrase: pass, Uart: machine.UART2, Tx: machine.TX1, Rx: machine.RX0, } - - dev = espat.New(&netcfg) + netdev = espat.New(&cfg) ) var buf = &bytes.Buffer{} @@ -36,8 +34,7 @@ func main() { waitSerial() - netdev.Use(dev) - if err := dev.NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/http-get/main.go b/examples/net/http-get/main.go index 9028da578..210c48f91 100644 --- a/examples/net/http-get/main.go +++ b/examples/net/http-get/main.go @@ -31,7 +31,7 @@ func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/http-get/rtl8720dn.go b/examples/net/http-get/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/http-get/rtl8720dn.go +++ b/examples/net/http-get/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/http-get/wifinina.go b/examples/net/http-get/wifinina.go index c3f8e9199..fd8b29129 100644 --- a/examples/net/http-get/wifinina.go +++ b/examples/net/http-get/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/http-head/main.go b/examples/net/http-head/main.go index 641f7c227..1b0dcae3e 100644 --- a/examples/net/http-head/main.go +++ b/examples/net/http-head/main.go @@ -30,7 +30,7 @@ func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } @@ -46,7 +46,7 @@ func main() { } fmt.Println(string(buf.Bytes())) - NetDisconnect() + netdev.NetDisconnect() } // Wait for user to open serial console diff --git a/examples/net/http-head/rtl8720dn.go b/examples/net/http-head/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/http-head/rtl8720dn.go +++ b/examples/net/http-head/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/http-head/wifinina.go b/examples/net/http-head/wifinina.go index c3f8e9199..fd8b29129 100644 --- a/examples/net/http-head/wifinina.go +++ b/examples/net/http-head/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/http-post/main.go b/examples/net/http-post/main.go index 79d2134d7..e1506a338 100644 --- a/examples/net/http-post/main.go +++ b/examples/net/http-post/main.go @@ -30,7 +30,7 @@ func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } @@ -50,7 +50,7 @@ func main() { fmt.Println(string(body)) - NetDisconnect() + netdev.NetDisconnect() } // Wait for user to open serial console diff --git a/examples/net/http-post/rtl8720dn.go b/examples/net/http-post/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/http-post/rtl8720dn.go +++ b/examples/net/http-post/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/http-post/wifinina.go b/examples/net/http-post/wifinina.go index c3f8e9199..fd8b29129 100644 --- a/examples/net/http-post/wifinina.go +++ b/examples/net/http-post/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/http-postform/main.go b/examples/net/http-postform/main.go index a88d560dd..4c7ac59be 100644 --- a/examples/net/http-postform/main.go +++ b/examples/net/http-postform/main.go @@ -30,7 +30,7 @@ func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/http-postform/rtl8720dn.go b/examples/net/http-postform/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/http-postform/rtl8720dn.go +++ b/examples/net/http-postform/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/http-postform/wifinina.go b/examples/net/http-postform/wifinina.go index c3f8e9199..fd8b29129 100644 --- a/examples/net/http-postform/wifinina.go +++ b/examples/net/http-postform/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/mqttclient/main.go b/examples/net/mqttclient/main.go index cd7a0059f..c2b2ad44a 100644 --- a/examples/net/mqttclient/main.go +++ b/examples/net/mqttclient/main.go @@ -36,7 +36,7 @@ var connectionLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/mqttclient/rtl8720dn.go b/examples/net/mqttclient/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/mqttclient/rtl8720dn.go +++ b/examples/net/mqttclient/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/mqttclient/wifinina.go b/examples/net/mqttclient/wifinina.go index c3f8e9199..fd8b29129 100644 --- a/examples/net/mqttclient/wifinina.go +++ b/examples/net/mqttclient/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/ntpclient/main.go b/examples/net/ntpclient/main.go index 1d74feb00..e6f9d6b45 100644 --- a/examples/net/ntpclient/main.go +++ b/examples/net/ntpclient/main.go @@ -30,7 +30,7 @@ func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } @@ -49,7 +49,7 @@ func main() { } conn.Close() - NetDisconnect() + netdev.NetDisconnect() runtime.AdjustTimeOffset(-1 * int64(time.Since(t))) diff --git a/examples/net/ntpclient/rtl8720dn.go b/examples/net/ntpclient/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/ntpclient/rtl8720dn.go +++ b/examples/net/ntpclient/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/ntpclient/wifinina.go b/examples/net/ntpclient/wifinina.go index e9151a53c..bcb524a9c 100644 --- a/examples/net/ntpclient/wifinina.go +++ b/examples/net/ntpclient/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/rawsocket/main.go b/examples/net/rawsocket/main.go index 8194f2a26..fa3619d69 100644 --- a/examples/net/rawsocket/main.go +++ b/examples/net/rawsocket/main.go @@ -11,9 +11,9 @@ import ( "fmt" "log" "machine" - "net/netdev" + "net" "strconv" - "strings" + "syscall" "time" ) @@ -29,7 +29,7 @@ func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } @@ -41,16 +41,15 @@ func main() { func sendBatch() { - parts := strings.Split(addr, ":") - ip := netdev.ParseIP(parts[0]) - port, _ := strconv.Atoi(parts[1]) - sockAddr := netdev.NewSockAddr("", netdev.Port(port), ip) + host, sport, _ := net.SplitHostPort(addr) + ip := net.ParseIP(host).To4() + port, _ := strconv.Atoi(sport) // make TCP connection message("---------------\r\nDialing TCP connection") - sock, _ := dev.Socket(netdev.AF_INET, netdev.SOCK_STREAM, netdev.IPPROTO_TCP) - err := dev.Connect(sock, sockAddr) - for ; err != nil; err = dev.Connect(sock, sockAddr) { + fd, _ := netdev.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + err := netdev.Connect(fd, "", ip, port) + for ; err != nil; err = netdev.Connect(fd, "", ip, port) { message(err.Error()) time.Sleep(5 * time.Second) } @@ -67,7 +66,7 @@ func sendBatch() { fmt.Fprint(buf, "\r---------------------------- i == ", i, " ----------------------------"+ "\r---------------------------- i == ", i, " ----------------------------") - if w, err = dev.Send(sock, buf.Bytes(), 0, 0); err != nil { + if w, err = netdev.Send(fd, buf.Bytes(), 0, 0); err != nil { println("error:", err.Error(), "\r") break } @@ -79,12 +78,12 @@ func sendBatch() { fmt.Fprint(buf, "\nWrote ", n, " bytes in ", ms, " ms\r\n") message(buf.String()) - if _, err := dev.Send(sock, buf.Bytes(), 0, 0); err != nil { + if _, err := netdev.Send(fd, buf.Bytes(), 0, 0); err != nil { println("error:", err.Error(), "\r") } println("Disconnecting TCP...") - dev.Close(sock) + netdev.Close(fd) } func message(msg string) { diff --git a/examples/net/rawsocket/rtl8720dn.go b/examples/net/rawsocket/rtl8720dn.go index a6dcb77fa..df94a8e4f 100644 --- a/examples/net/rawsocket/rtl8720dn.go +++ b/examples/net/rawsocket/rtl8720dn.go @@ -26,12 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/rawsocket/wifinina.go b/examples/net/rawsocket/wifinina.go index 1011d75cf..bcb524a9c 100644 --- a/examples/net/rawsocket/wifinina.go +++ b/examples/net/rawsocket/wifinina.go @@ -30,12 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/tcpclient/main.go b/examples/net/tcpclient/main.go index 2e603d879..76fd56261 100644 --- a/examples/net/tcpclient/main.go +++ b/examples/net/tcpclient/main.go @@ -28,7 +28,7 @@ func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/tcpclient/rtl8720dn.go b/examples/net/tcpclient/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/tcpclient/rtl8720dn.go +++ b/examples/net/tcpclient/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/tcpclient/wifinina.go b/examples/net/tcpclient/wifinina.go index e9151a53c..bcb524a9c 100644 --- a/examples/net/tcpclient/wifinina.go +++ b/examples/net/tcpclient/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/tcpecho/main.go b/examples/net/tcpecho/main.go index 6ffe81d57..f91c6df90 100644 --- a/examples/net/tcpecho/main.go +++ b/examples/net/tcpecho/main.go @@ -36,7 +36,7 @@ func main() { time.Sleep(time.Second) - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/tcpecho/rtl8720dn.go b/examples/net/tcpecho/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/tcpecho/rtl8720dn.go +++ b/examples/net/tcpecho/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/tcpecho/wifinina.go b/examples/net/tcpecho/wifinina.go index e9151a53c..bcb524a9c 100644 --- a/examples/net/tcpecho/wifinina.go +++ b/examples/net/tcpecho/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/tlsclient/main.go b/examples/net/tlsclient/main.go index ec8ae0630..57caad4ef 100644 --- a/examples/net/tlsclient/main.go +++ b/examples/net/tlsclient/main.go @@ -80,7 +80,7 @@ func makeRequest() { func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/tlsclient/rtl8720dn.go b/examples/net/tlsclient/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/tlsclient/rtl8720dn.go +++ b/examples/net/tlsclient/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/tlsclient/wifinina.go b/examples/net/tlsclient/wifinina.go index e9151a53c..bcb524a9c 100644 --- a/examples/net/tlsclient/wifinina.go +++ b/examples/net/tlsclient/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/webclient-tinyterm/main.go b/examples/net/webclient-tinyterm/main.go index 507cd820c..47dfa48a7 100644 --- a/examples/net/webclient-tinyterm/main.go +++ b/examples/net/webclient-tinyterm/main.go @@ -16,13 +16,12 @@ import ( "io" "log" "machine" - "net" "net/http" - "net/netdev" "net/url" "strings" "time" + "tinygo.org/x/drivers" "tinygo.org/x/drivers/ili9341" "tinygo.org/x/drivers/rtl8720dn" "tinygo.org/x/tinyfont/proggy" @@ -45,7 +44,7 @@ var ( Baudrate: 614400, } - dev = rtl8720dn.New(&netcfg) + netdev = rtl8720dn.New(&netcfg) display = ili9341.NewSPI( machine.SPI3, @@ -67,12 +66,12 @@ var ( font = &proggy.TinySZ8pt7b ) -func notify(e netdev.Event) { +func notify(e drivers.NetlinkEvent) { switch e { - case netdev.EventNetUp: + case drivers.NetlinkEventNetUp: fmt.Println("Wifi connection UP") fmt.Fprintf(terminal, "Wifi connection UP") - case netdev.EventNetDown: + case drivers.NetlinkEventNetDown: fmt.Println("Wifi connection DOWN") fmt.Fprintf(terminal, "Wifi connection DOWN") } @@ -101,10 +100,9 @@ func main() { fmt.Fprintf(terminal, "Connecting to %s...\r\n", ssid) - dev.NetNotify(notify) - net.UseNetdev(dev) + netdev.NetNotify(notify) - if err := dev.NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/webclient/main.go b/examples/net/webclient/main.go index 05b6a63dc..15b1d2c91 100644 --- a/examples/net/webclient/main.go +++ b/examples/net/webclient/main.go @@ -91,7 +91,7 @@ func closeConnection() { func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/webclient/rtl8720dn.go b/examples/net/webclient/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/webclient/rtl8720dn.go +++ b/examples/net/webclient/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/webclient/wifinina.go b/examples/net/webclient/wifinina.go index e9151a53c..bcb524a9c 100644 --- a/examples/net/webclient/wifinina.go +++ b/examples/net/webclient/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/webserver/main.go b/examples/net/webserver/main.go index c24af38ef..1aa587f58 100644 --- a/examples/net/webserver/main.go +++ b/examples/net/webserver/main.go @@ -32,7 +32,7 @@ func main() { led.Configure(machine.PinConfig{Mode: machine.PinOutput}) - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/webserver/rtl8720dn.go b/examples/net/webserver/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/webserver/rtl8720dn.go +++ b/examples/net/webserver/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/webserver/wifinina.go b/examples/net/webserver/wifinina.go index c3f8e9199..fd8b29129 100644 --- a/examples/net/webserver/wifinina.go +++ b/examples/net/webserver/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/websocket/dial/main.go b/examples/net/websocket/dial/main.go index c8cf2f00a..d53d6e89d 100644 --- a/examples/net/websocket/dial/main.go +++ b/examples/net/websocket/dial/main.go @@ -33,7 +33,7 @@ func waitSerial() { func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/websocket/dial/rtl8720dn.go b/examples/net/websocket/dial/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/websocket/dial/rtl8720dn.go +++ b/examples/net/websocket/dial/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/websocket/dial/wifinina.go b/examples/net/websocket/dial/wifinina.go index c3f8e9199..fd8b29129 100644 --- a/examples/net/websocket/dial/wifinina.go +++ b/examples/net/websocket/dial/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/websocket/handler/main.go b/examples/net/websocket/handler/main.go index 5a50e9b6d..4ff4fc85f 100644 --- a/examples/net/websocket/handler/main.go +++ b/examples/net/websocket/handler/main.go @@ -40,7 +40,7 @@ func waitSerial() { func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/websocket/handler/rtl8720dn.go b/examples/net/websocket/handler/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/websocket/handler/rtl8720dn.go +++ b/examples/net/websocket/handler/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/websocket/handler/wifinina.go b/examples/net/websocket/handler/wifinina.go index c3f8e9199..fd8b29129 100644 --- a/examples/net/websocket/handler/wifinina.go +++ b/examples/net/websocket/handler/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/webstatic/main.go b/examples/net/webstatic/main.go index 5bebb0a61..cab727955 100644 --- a/examples/net/webstatic/main.go +++ b/examples/net/webstatic/main.go @@ -25,7 +25,7 @@ func main() { // wait a bit for console time.Sleep(2 * time.Second) - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/webstatic/rtl8720dn.go b/examples/net/webstatic/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/webstatic/rtl8720dn.go +++ b/examples/net/webstatic/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/webstatic/wifinina.go b/examples/net/webstatic/wifinina.go index c3f8e9199..fd8b29129 100644 --- a/examples/net/webstatic/wifinina.go +++ b/examples/net/webstatic/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/netdev.go b/netdev.go new file mode 100644 index 000000000..f41362f76 --- /dev/null +++ b/netdev.go @@ -0,0 +1,59 @@ +package drivers + +import ( + "errors" + "net" + "time" + _ "unsafe" // to use go:linkname +) + +// GethostByName() errors +var ( + ErrHostUnknown = errors.New("Host unknown") +) + +// Socket errors +var ( + ErrFamilyNotSupported = errors.New("Address family not supported") + ErrProtocolNotSupported = errors.New("Socket protocol/type not supported") + ErrNoMoreSockets = errors.New("No more sockets") + ErrClosingSocket = errors.New("Error closing socket") + ErrNotSupported = errors.New("Not supported") + ErrRecvTimeout = errors.New("Recv timeout expired") +) + +//go:linkname UseNetdev net.useNetdev +func UseNetdev(dev netdever) + +// Netdev is TinyGo's network device driver model. Network drivers implement +// the netdever interface, providing a common network I/O interface to TinyGo's +// "net" package. The interface is modeled after the BSD socket interface. +// net.Conn implementations (TCPConn, UDPConn, and TLSConn) use the netdev +// interface for device I/O access. +// +// A netdever is passed to the "net" package using UseNetdev(). +// +// Just like a net.Conn, multiple goroutines may invoke methods on a netdever +// simultaneously. +// +// NOTE: The netdever interface is mirrored in tinygo/src/net/netdev.go. +// NOTE: If making changes to this interface, mirror the changes in +// NOTE: tinygo/src/net/netdev.go, and visa-versa. + +type netdever interface { + + // GetHostByName returns the IP address of either a hostname or IPv4 + // address in standard dot notation + GetHostByName(name string) (net.IP, error) + + // Berkely Sockets-like interface, Go-ified. See man page for socket(2), etc. + Socket(domain int, stype int, protocol int) (int, error) + Bind(sockfd int, ip net.IP, port int) error + Connect(sockfd int, host string, ip net.IP, port int) error + Listen(sockfd int, backlog int) error + Accept(sockfd int, ip net.IP, port int) (int, error) + Send(sockfd int, buf []byte, flags int, timeout time.Duration) (int, error) + Recv(sockfd int, buf []byte, flags int, timeout time.Duration) (int, error) + Close(sockfd int) error + SetSockOpt(sockfd int, level int, opt int, value interface{}) error +} diff --git a/netlink.go b/netlink.go new file mode 100644 index 000000000..955289fa5 --- /dev/null +++ b/netlink.go @@ -0,0 +1,49 @@ +package drivers + +import ( + "errors" + "net" +) + +// NetConnect() errors +var ( + ErrConnected = errors.New("Already connected") + ErrConnectFailed = errors.New("Connect failed") + ErrConnectTimeout = errors.New("Connect timed out") + ErrMissingSSID = errors.New("Missing WiFi SSID") + ErrStartingDHCPClient = errors.New("Error starting DHPC client") +) + +type NetlinkEvent int + +// Netlink network events +const ( + // The device's network connection is now UP + NetlinkEventNetUp NetlinkEvent = iota + // The device's network connection is now DOWN + NetlinkEventNetDown +) + +// Network drivers (optionally) implement the Netlinker interface. This +// interface is not used by TinyGo's "net" package, but rather provides the +// TinyGo application direct access to the network device for common settings +// and control that fall outside of netdev's socket interface. + +type Netlinker interface { + + // NetConnect device to IP network + NetConnect() error + + // NetDisconnect device from IP network + NetDisconnect() + + // NetNotify to register callback for network events + NetNotify(func(NetlinkEvent)) + + // GetHardwareAddr returns device MAC address + GetHardwareAddr() (net.HardwareAddr, error) + + // GetIPAddr returns IP address assigned to device, either by DHCP or + // statically + GetIPAddr() (net.IP, error) +} diff --git a/rtl8720dn/rtl8720dn.go b/rtl8720dn/rtl8720dn.go index cc1c1e71a..1295368fe 100644 --- a/rtl8720dn/rtl8720dn.go +++ b/rtl8720dn/rtl8720dn.go @@ -1,7 +1,7 @@ // Package rtl8720dn implements TCP wireless communication over UART // talking to a RealTek rtl8720dn module. // -// 01/2023 sfeldma@gmail.com Heavily modified to use netdev interface +// 01/2023 sfeldma@gmail.com Heavily modified to use netdevinterface package rtl8720dn // import "tinygo.org/x/drivers/rtl8720dn" @@ -11,10 +11,13 @@ import ( "fmt" "io" "machine" - "net/netdev" + "net" "strings" "sync" + "syscall" "time" + + "tinygo.org/x/drivers" ) var _debug debug = debugBasic @@ -28,16 +31,14 @@ var ( ) const ( - F_SETFL = 4 - O_NONBLOCK = 1 + O_NONBLOCK = 1 // note: different value than syscall.O_NONBLOCK (0x800) RTW_MODE_STA = 0x00000001 ) -type macAddress netdev.HardwareAddr type sock int32 type socket struct { - protocol netdev.Protocol + protocol int inuse bool } @@ -68,7 +69,7 @@ type Config struct { type rtl8720dn struct { cfg *Config - notifyCb func(netdev.Event) + notifyCb func(drivers.NetlinkEvent) mu sync.Mutex uart *machine.UART @@ -86,22 +87,29 @@ type rtl8720dn struct { sockets map[sock]*socket } -func newSocket(protocol netdev.Protocol) *socket { +func newSocket(protocol int) *socket { return &socket{protocol: protocol, inuse: true} } func New(cfg *Config) *rtl8720dn { - return &rtl8720dn{ + r := rtl8720dn{ debug: (_debug & debugRpc) != 0, cfg: cfg, sockets: make(map[sock]*socket), killWatchdog: make(chan bool), } + + drivers.UseNetdev(&r) + + // assert that rtl8720dn implements Netlinker + var _ drivers.Netlinker = (*rtl8720dn)(nil) + + return &r } func (r *rtl8720dn) startDhcpc() error { if result := r.rpc_tcpip_adapter_dhcpc_start(0); result == -1 { - return netdev.ErrStartingDHCPClient + return drivers.ErrStartingDHCPClient } return nil } @@ -109,7 +117,7 @@ func (r *rtl8720dn) startDhcpc() error { func (r *rtl8720dn) connectToAP() error { if len(r.cfg.Ssid) == 0 { - return netdev.ErrMissingSSID + return drivers.ErrMissingSSID } if debugging(debugBasic) { @@ -123,7 +131,7 @@ func (r *rtl8720dn) connectToAP() error { if debugging(debugBasic) { fmt.Printf("FAILED\r\n") } - return netdev.ErrConnectFailed + return drivers.ErrConnectFailed } if debugging(debugBasic) { @@ -131,7 +139,7 @@ func (r *rtl8720dn) connectToAP() error { } if r.notifyCb != nil { - r.notifyCb(netdev.EventNetUp) + r.notifyCb(drivers.NetlinkEventNetUp) } return r.startDhcpc() @@ -206,9 +214,9 @@ func (r *rtl8720dn) showIP() { if debugging(debugBasic) { ip, subnet, gateway, _ := r.getIP() fmt.Printf("\r\n") - fmt.Printf("DHCP-assigned IP : %s\r\n", netdev.IP(ip).String()) - fmt.Printf("DHCP-assigned subnet : %s\r\n", netdev.IP(subnet).String()) - fmt.Printf("DHCP-assigned gateway : %s\r\n", netdev.IP(gateway).String()) + fmt.Printf("DHCP-assigned IP : %s\r\n", ip.String()) + fmt.Printf("DHCP-assigned subnet : %s\r\n", subnet.String()) + fmt.Printf("DHCP-assigned gateway : %s\r\n", gateway.String()) fmt.Printf("\r\n") } } @@ -231,7 +239,7 @@ func (r *rtl8720dn) watchdog() { fmt.Printf("Watchdog: Wifi NOT CONNECTED, trying again...\r\n") } if r.notifyCb != nil { - r.notifyCb(netdev.EventNetDown) + r.notifyCb(drivers.NetlinkEventNetDown) } r.netConnect(false) } @@ -250,7 +258,7 @@ func (r *rtl8720dn) netConnect(reset bool) error { for i := 0; r.cfg.Retries == 0 || i < r.cfg.Retries; i++ { if err := r.connectToAP(); err != nil { - if err == netdev.ErrConnectFailed { + if err == drivers.ErrConnectFailed { continue } return err @@ -259,7 +267,7 @@ func (r *rtl8720dn) netConnect(reset bool) error { } if r.networkDown() { - return netdev.ErrConnectFailed + return drivers.ErrConnectFailed } r.showIP() @@ -272,7 +280,7 @@ func (r *rtl8720dn) NetConnect() error { defer r.mu.Unlock() if r.netConnected { - return netdev.ErrConnected + return drivers.ErrConnected } r.showDriver() @@ -316,15 +324,15 @@ func (r *rtl8720dn) NetDisconnect() { } if r.notifyCb != nil { - r.notifyCb(netdev.EventNetDown) + r.notifyCb(drivers.NetlinkEventNetDown) } } -func (r *rtl8720dn) NetNotify(cb func(netdev.Event)) { +func (r *rtl8720dn) NetNotify(cb func(drivers.NetlinkEvent)) { r.notifyCb = cb } -func (r *rtl8720dn) GetHostByName(name string) (netdev.IP, error) { +func (r *rtl8720dn) GetHostByName(name string) (net.IP, error) { if debugging(debugNetdev) { fmt.Printf("[GetHostByName] name: %s\r\n", name) @@ -333,19 +341,16 @@ func (r *rtl8720dn) GetHostByName(name string) (netdev.IP, error) { r.mu.Lock() defer r.mu.Unlock() - var addr [4]byte - result := r.rpc_netconn_gethostbyname(name, addr[:]) + var ip [4]byte + result := r.rpc_netconn_gethostbyname(name, ip[:]) if result == -1 { - return netdev.IP{}, fmt.Errorf("Get IP of host '%s' failed", name) + return net.IP{}, drivers.ErrHostUnknown } - var ip netdev.IP - copy(ip[:], addr[:]) - - return ip, nil + return net.IP(ip[:]), nil } -func (r *rtl8720dn) GetHardwareAddr() (netdev.HardwareAddr, error) { +func (r *rtl8720dn) GetHardwareAddr() (net.HardwareAddr, error) { if debugging(debugNetdev) { fmt.Printf("[GetHardwareAddr]\r\n") @@ -357,10 +362,10 @@ func (r *rtl8720dn) GetHardwareAddr() (netdev.HardwareAddr, error) { mac := strings.ReplaceAll(r.getMACAddr(), ":", "") addr, err := hex.DecodeString(mac) - return netdev.HardwareAddr(addr), err + return net.HardwareAddr(addr), err } -func (r *rtl8720dn) GetIPAddr() (netdev.IP, error) { +func (r *rtl8720dn) GetIPAddr() (net.IP, error) { if debugging(debugNetdev) { fmt.Printf("[GetIPAddr]\r\n") @@ -371,7 +376,7 @@ func (r *rtl8720dn) GetIPAddr() (netdev.IP, error) { ip, _, _, err := r.getIP() - return netdev.IP(ip), err + return net.IP(ip), err } func (r *rtl8720dn) clientTLS() uint32 { @@ -384,18 +389,17 @@ func (r *rtl8720dn) clientTLS() uint32 { // 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 (r *rtl8720dn) Socket(family netdev.AddressFamily, sockType netdev.SockType, - protocol netdev.Protocol) (netdev.Sockfd, error) { +func (r *rtl8720dn) Socket(domain int, stype int, protocol int) (int, error) { if debugging(debugNetdev) { - fmt.Printf("[Socket] family: %s, sockType: %s, protocol: %s\r\n", - family, sockType, protocol) + fmt.Printf("[Socket] domain: %d, type: %d, protocol: %d\r\n", + domain, stype, protocol) } - switch family { - case netdev.AF_INET: + switch domain { + case syscall.AF_INET: default: - return -1, netdev.ErrFamilyNotSupported + return -1, drivers.ErrFamilyNotSupported } var newSock int32 @@ -404,51 +408,50 @@ func (r *rtl8720dn) Socket(family netdev.AddressFamily, sockType netdev.SockType defer r.mu.Unlock() switch { - case protocol == netdev.IPPROTO_TCP && sockType == netdev.SOCK_STREAM: - newSock = r.rpc_lwip_socket(netdev.AF_INET, netdev.SOCK_STREAM, - netdev.IPPROTO_TCP) - case protocol == netdev.IPPROTO_TLS && sockType == netdev.SOCK_STREAM: + case protocol == syscall.IPPROTO_TCP && stype == syscall.SOCK_STREAM: + newSock = r.rpc_lwip_socket(syscall.AF_INET, syscall.SOCK_STREAM, + syscall.IPPROTO_TCP) + case protocol == syscall.IPPROTO_TLS && stype == syscall.SOCK_STREAM: // TODO Investigate: using client number as socket number; // TODO this may cause a problem if mixing TLS and non-TLS sockets? newSock = int32(r.clientTLS()) - case protocol == netdev.IPPROTO_UDP && sockType == netdev.SOCK_DGRAM: - newSock = r.rpc_lwip_socket(netdev.AF_INET, netdev.SOCK_DGRAM, - netdev.IPPROTO_UDP) + case protocol == syscall.IPPROTO_UDP && stype == syscall.SOCK_DGRAM: + newSock = r.rpc_lwip_socket(syscall.AF_INET, syscall.SOCK_DGRAM, + syscall.IPPROTO_UDP) default: - return -1, netdev.ErrProtocolNotSupported + return -1, drivers.ErrProtocolNotSupported } if newSock == -1 { - return -1, netdev.ErrNoMoreSockets + return -1, drivers.ErrNoMoreSockets } socket := newSocket(protocol) r.sockets[sock(newSock)] = socket - return netdev.Sockfd(newSock), nil + return int(newSock), nil } -func addrToName(addr netdev.SockAddr) []byte { - port := addr.Port() - ip := addr.IpBytes() - +func addrToName(ip net.IP, port int) []byte { name := make([]byte, 16) name[0] = 0x00 - name[1] = netdev.AF_INET + name[1] = syscall.AF_INET name[2] = byte(port >> 8) name[3] = byte(port) - name[4] = byte(ip[0]) - name[5] = byte(ip[1]) - name[6] = byte(ip[2]) - name[7] = byte(ip[3]) + if len(ip) == 4 { + name[4] = byte(ip[0]) + name[5] = byte(ip[1]) + name[6] = byte(ip[2]) + name[7] = byte(ip[3]) + } return name } -func (r *rtl8720dn) Bind(sockfd netdev.Sockfd, addr netdev.SockAddr) error { +func (r *rtl8720dn) Bind(sockfd int, ip net.IP, port int) error { if debugging(debugNetdev) { - fmt.Printf("[Bind] sockfd: %d, addr: %s\r\n", sockfd, addr) + fmt.Printf("[Bind] sockfd: %d, addr: %s:%d\r\n", sockfd, ip, port) } r.mu.Lock() @@ -456,25 +459,29 @@ func (r *rtl8720dn) Bind(sockfd netdev.Sockfd, addr netdev.SockAddr) error { var sock = sock(sockfd) var socket = r.sockets[sock] - var name = addrToName(addr) + var name = addrToName(ip, port) switch socket.protocol { - case netdev.IPPROTO_TCP, netdev.IPPROTO_UDP: + case syscall.IPPROTO_TCP, syscall.IPPROTO_UDP: result := r.rpc_lwip_bind(int32(sock), name, uint32(len(name))) if result == -1 { - return fmt.Errorf("Bind to %s failed", addr.String()) + return fmt.Errorf("Bind to %s:%d failed", ip, port) } default: - return netdev.ErrProtocolNotSupported + return drivers.ErrProtocolNotSupported } return nil } -func (r *rtl8720dn) Connect(sockfd netdev.Sockfd, servaddr netdev.SockAddr) error { +func (r *rtl8720dn) Connect(sockfd int, host string, ip net.IP, port int) error { if debugging(debugNetdev) { - fmt.Printf("[Connect] sockfd: %d, servaddr: %s\r\n", sockfd, servaddr) + if host == "" { + fmt.Printf("[Connect] sockfd: %d, addr: %s:%d\r\n", sockfd, ip, port) + } else { + fmt.Printf("[Connect] sockfd: %d, host: %s:%d\r\n", sockfd, host, port) + } } r.mu.Lock() @@ -482,27 +489,27 @@ func (r *rtl8720dn) Connect(sockfd netdev.Sockfd, servaddr netdev.SockAddr) erro var sock = sock(sockfd) var socket = r.sockets[sock] - var name = addrToName(servaddr) + var name = addrToName(ip, port) // Start the connection switch socket.protocol { - case netdev.IPPROTO_TCP, netdev.IPPROTO_UDP: + case syscall.IPPROTO_TCP, syscall.IPPROTO_UDP: result := r.rpc_lwip_connect(int32(sock), name, uint32(len(name))) if result == -1 { - return fmt.Errorf("Connect to %s failed", servaddr.String()) + return fmt.Errorf("Connect to %s:%d failed", ip, port) } - case netdev.IPPROTO_TLS: + case syscall.IPPROTO_TLS: result := r.rpc_wifi_start_ssl_client(uint32(sock), - servaddr.Host(), uint32(servaddr.Port()), 0) + host, uint32(port), 0) if result == -1 { - return fmt.Errorf("Connect to %s failed", servaddr.String()) + return fmt.Errorf("Connect to %s:%d failed", host, port) } } return nil } -func (r *rtl8720dn) Listen(sockfd netdev.Sockfd, backlog int) error { +func (r *rtl8720dn) Listen(sockfd int, backlog int) error { if debugging(debugNetdev) { fmt.Printf("[Listen] sockfd: %d\r\n", sockfd) @@ -515,31 +522,31 @@ func (r *rtl8720dn) Listen(sockfd netdev.Sockfd, backlog int) error { var socket = r.sockets[sock] switch socket.protocol { - case netdev.IPPROTO_TCP: + case syscall.IPPROTO_TCP: result := r.rpc_lwip_listen(int32(sock), int32(backlog)) if result == -1 { return fmt.Errorf("Listen failed") } - result = r.rpc_lwip_fcntl(int32(sock), F_SETFL, O_NONBLOCK) + result = r.rpc_lwip_fcntl(int32(sock), syscall.F_SETFL, O_NONBLOCK) if result == -1 { return fmt.Errorf("Fcntl failed") } - case netdev.IPPROTO_UDP: + case syscall.IPPROTO_UDP: result := r.rpc_lwip_listen(int32(sock), int32(backlog)) if result == -1 { return fmt.Errorf("Listen failed") } default: - return netdev.ErrProtocolNotSupported + return drivers.ErrProtocolNotSupported } return nil } -func (r *rtl8720dn) Accept(sockfd netdev.Sockfd, peer netdev.SockAddr) (netdev.Sockfd, error) { +func (r *rtl8720dn) Accept(sockfd int, ip net.IP, port int) (int, error) { if debugging(debugNetdev) { - fmt.Printf("[Accept] sockfd: %d, peer: %s\r\n", sockfd, peer) + fmt.Printf("[Accept] sockfd: %d, peer: %s:%d\r\n", sockfd, ip, port) } r.mu.Lock() @@ -548,12 +555,12 @@ func (r *rtl8720dn) Accept(sockfd netdev.Sockfd, peer netdev.SockAddr) (netdev.S var newSock int32 var lsock = sock(sockfd) var socket = r.sockets[lsock] - var addr = addrToName(peer) + var addr = addrToName(ip, port) switch socket.protocol { - case netdev.IPPROTO_TCP: + case syscall.IPPROTO_TCP: default: - return -1, netdev.ErrProtocolNotSupported + return -1, drivers.ErrProtocolNotSupported } for { @@ -584,27 +591,27 @@ func (r *rtl8720dn) Accept(sockfd netdev.Sockfd, peer netdev.SockAddr) (netdev.S continue } // Reuse client socket - return netdev.Sockfd(newSock), nil + return int(newSock), nil } // Create new socket for client and return fd r.sockets[sock(newSock)] = newSocket(socket.protocol) - return netdev.Sockfd(newSock), nil + return int(newSock), nil } } -func (r *rtl8720dn) sendChunk(sockfd netdev.Sockfd, buf []byte) (int, error) { +func (r *rtl8720dn) sendChunk(sockfd int, buf []byte) (int, error) { var sock = sock(sockfd) var socket = r.sockets[sock] switch socket.protocol { - case netdev.IPPROTO_TCP, netdev.IPPROTO_UDP: + case syscall.IPPROTO_TCP, syscall.IPPROTO_UDP: result := r.rpc_lwip_send(int32(sock), buf, 0x00000008) if result == -1 { return -1, fmt.Errorf("Send error") } return int(result), nil - case netdev.IPPROTO_TLS: + case syscall.IPPROTO_TLS: result := r.rpc_wifi_send_ssl_data(uint32(sock), buf, uint16(len(buf))) if result == -1 { return -1, fmt.Errorf("TLS Send error") @@ -612,10 +619,10 @@ func (r *rtl8720dn) sendChunk(sockfd netdev.Sockfd, buf []byte) (int, error) { return int(result), nil } - return -1, netdev.ErrProtocolNotSupported + return -1, drivers.ErrProtocolNotSupported } -func (r *rtl8720dn) Send(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, +func (r *rtl8720dn) Send(sockfd int, buf []byte, flags int, timeout time.Duration) (int, error) { if debugging(debugNetdev) { @@ -645,7 +652,7 @@ func (r *rtl8720dn) Send(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlag return len(buf), nil } -func (r *rtl8720dn) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, +func (r *rtl8720dn) Recv(sockfd int, buf []byte, flags int, timeout time.Duration) (int, error) { if debugging(debugNetdev) { @@ -671,15 +678,15 @@ func (r *rtl8720dn) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlag // Check if we've timed out if timeout > 0 { if time.Now().Before(expire) { - return -1, netdev.ErrRecvTimeout + return -1, drivers.ErrRecvTimeout } } switch socket.protocol { - case netdev.IPPROTO_TCP, netdev.IPPROTO_UDP: + case syscall.IPPROTO_TCP, syscall.IPPROTO_UDP: n = r.rpc_lwip_recv(int32(sock), buf[:length], uint32(length), 0x00000008, 0) - case netdev.IPPROTO_TLS: + case syscall.IPPROTO_TLS: n = r.rpc_wifi_get_ssl_receive(uint32(sock), buf[:length], int32(length)) } @@ -706,7 +713,7 @@ func (r *rtl8720dn) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlag } } -func (r *rtl8720dn) Close(sockfd netdev.Sockfd) error { +func (r *rtl8720dn) Close(sockfd int) error { if debugging(debugNetdev) { fmt.Printf("[Close] sockfd: %d\r\n", sockfd) @@ -724,15 +731,15 @@ func (r *rtl8720dn) Close(sockfd netdev.Sockfd) error { } switch socket.protocol { - case netdev.IPPROTO_TCP, netdev.IPPROTO_UDP: + case syscall.IPPROTO_TCP, syscall.IPPROTO_UDP: result = r.rpc_lwip_close(int32(sock)) - case netdev.IPPROTO_TLS: + case syscall.IPPROTO_TLS: r.rpc_wifi_stop_ssl_socket(uint32(sock)) r.rpc_wifi_ssl_client_destroy(uint32(sock)) } if result == -1 { - return netdev.ErrClosingSocket + return drivers.ErrClosingSocket } socket.inuse = false @@ -740,14 +747,13 @@ func (r *rtl8720dn) Close(sockfd netdev.Sockfd) error { return nil } -func (r *rtl8720dn) SetSockOpt(sockfd netdev.Sockfd, level netdev.SockOptLevel, - opt netdev.SockOpt, value any) error { +func (r *rtl8720dn) SetSockOpt(sockfd int, level int, opt int, value interface{}) error { if debugging(debugNetdev) { fmt.Printf("[SetSockOpt] sockfd: %d\r\n", sockfd) } - return netdev.ErrNotSupported + return drivers.ErrNotSupported } func (r *rtl8720dn) disconnect() error { @@ -768,13 +774,14 @@ func (r *rtl8720dn) getMACAddr() string { return string(mac[:]) } -func (r *rtl8720dn) getIP() (ip, subnet, gateway netdev.IP, err error) { +func (r *rtl8720dn) getIP() (ip, subnet, gateway net.IP, err error) { var ip_info [12]byte result := r.rpc_tcpip_adapter_get_ip_info(0, ip_info[:]) if result == -1 { err = fmt.Errorf("Get IP info failed") return } + ip, subnet, gateway = make([]byte, 4), make([]byte, 4), make([]byte, 4) copy(ip[:], ip_info[0:4]) copy(subnet[:], ip_info[4:8]) copy(gateway[:], ip_info[8:12]) diff --git a/wifinina/wifinina.go b/wifinina/wifinina.go index 98c77005d..2a306feee 100644 --- a/wifinina/wifinina.go +++ b/wifinina/wifinina.go @@ -16,8 +16,9 @@ import ( "io" "machine" "math/bits" - "net/netdev" + "net" "sync" + "syscall" "time" "tinygo.org/x/drivers" @@ -157,13 +158,13 @@ const ( type connectionStatus uint8 type encryptionType uint8 -type macAddress netdev.HardwareAddr type sock uint8 type hwerr uint8 type socket struct { - protocol netdev.Protocol - laddr netdev.SockAddr + protocol int + ip net.IP + port int inuse bool } @@ -201,7 +202,7 @@ type Config struct { type wifinina struct { cfg *Config - notifyCb func(netdev.Event) + notifyCb func(drivers.NetlinkEvent) mu sync.Mutex spi drivers.SPI @@ -224,7 +225,7 @@ type wifinina struct { sockets map[sock]*socket // keyed by sock as returned by getSocket() } -func newSocket(protocol netdev.Protocol) *socket { +func newSocket(protocol int) *socket { return &socket{protocol: protocol, inuse: true} } @@ -243,6 +244,11 @@ func New(cfg *Config) *wifinina { w.cfg.ConnectTimeo = 10 * time.Second } + drivers.UseNetdev(&w) + + // assert that wifinina implements Netlinker + var _ drivers.Netlinker = (*wifinina)(nil) + return &w } @@ -253,7 +259,7 @@ func (err hwerr) Error() string { func (w *wifinina) connectToAP(timeout time.Duration) error { if len(w.cfg.Ssid) == 0 { - return netdev.ErrMissingSSID + return drivers.ErrMissingSSID } if debugging(debugBasic) { @@ -273,7 +279,7 @@ func (w *wifinina) connectToAP(timeout time.Duration) error { fmt.Printf("CONNECTED\r\n") } if w.notifyCb != nil { - w.notifyCb(netdev.EventNetUp) + w.notifyCb(drivers.NetlinkEventNetUp) } return nil } @@ -284,7 +290,7 @@ func (w *wifinina) connectToAP(timeout time.Duration) error { fmt.Printf("FAILED\r\n") } - return netdev.ErrConnectTimeout + return drivers.ErrConnectTimeout } func (w *wifinina) netDisconnect() { @@ -348,7 +354,7 @@ func (w *wifinina) showDevice() { return } if debugging(debugBasic) { - mac := netdev.HardwareAddr(w.getMACAddr()) + mac := w.getMACAddr() fmt.Printf("ESP32 firmware version : %s\r\n", w.getFwVersion()) fmt.Printf("MAC address : %s\r\n", mac.String()) fmt.Printf("\r\n") @@ -360,9 +366,9 @@ func (w *wifinina) showIP() { if debugging(debugBasic) { ip, subnet, gateway := w.getIP() fmt.Printf("\r\n") - fmt.Printf("DHCP-assigned IP : %s\r\n", netdev.IP(ip).String()) - fmt.Printf("DHCP-assigned subnet : %s\r\n", netdev.IP(subnet).String()) - fmt.Printf("DHCP-assigned gateway : %s\r\n", netdev.IP(gateway).String()) + fmt.Printf("DHCP-assigned IP : %s\r\n", ip.String()) + fmt.Printf("DHCP-assigned subnet : %s\r\n", subnet.String()) + fmt.Printf("DHCP-assigned gateway : %s\r\n", gateway.String()) fmt.Printf("\r\n") } } @@ -391,7 +397,7 @@ func (w *wifinina) watchdog() { fmt.Printf("Watchdog: Wifi NOT CONNECTED, trying again...\r\n") } if w.notifyCb != nil { - w.notifyCb(netdev.EventNetDown) + w.notifyCb(drivers.NetlinkEventNetDown) } w.netConnect(false) } @@ -408,7 +414,7 @@ func (w *wifinina) netConnect(reset bool) error { for i := 0; w.cfg.Retries == 0 || i < w.cfg.Retries; i++ { if err := w.connectToAP(w.cfg.ConnectTimeo); err != nil { - if err == netdev.ErrConnectTimeout { + if err == drivers.ErrConnectTimeout { continue } return err @@ -417,7 +423,7 @@ func (w *wifinina) netConnect(reset bool) error { } if w.networkDown() { - return netdev.ErrConnectFailed + return drivers.ErrConnectFailed } w.showIP() @@ -430,7 +436,7 @@ func (w *wifinina) NetConnect() error { defer w.mu.Unlock() if w.netConnected { - return netdev.ErrConnected + return drivers.ErrConnected } w.showDriver() @@ -472,15 +478,15 @@ func (w *wifinina) NetDisconnect() { } if w.notifyCb != nil { - w.notifyCb(netdev.EventNetDown) + w.notifyCb(drivers.NetlinkEventNetDown) } } -func (w *wifinina) NetNotify(cb func(netdev.Event)) { +func (w *wifinina) NetNotify(cb func(drivers.NetlinkEvent)) { w.notifyCb = cb } -func (w *wifinina) GetHostByName(name string) (netdev.IP, error) { +func (w *wifinina) GetHostByName(name string) (net.IP, error) { if debugging(debugNetdev) { fmt.Printf("[GetHostByName] name: %s\r\n", name) @@ -491,16 +497,13 @@ func (w *wifinina) GetHostByName(name string) (netdev.IP, error) { ip := w.getHostByName(name) if ip == "" { - return netdev.IP{}, netdev.ErrHostUnknown + return net.IP{}, drivers.ErrHostUnknown } - var hostIp netdev.IP - copy(hostIp[:], []byte(ip)) - - return hostIp, nil + return net.IP([]byte(ip)), nil } -func (w *wifinina) GetHardwareAddr() (netdev.HardwareAddr, error) { +func (w *wifinina) GetHardwareAddr() (net.HardwareAddr, error) { if debugging(debugNetdev) { fmt.Printf("[GetHardwareAddr]\r\n") @@ -509,10 +512,10 @@ func (w *wifinina) GetHardwareAddr() (netdev.HardwareAddr, error) { w.mu.Lock() defer w.mu.Unlock() - return netdev.HardwareAddr(w.getMACAddr()), nil + return w.getMACAddr(), nil } -func (w *wifinina) GetIPAddr() (netdev.IP, error) { +func (w *wifinina) GetIPAddr() (net.IP, error) { if debugging(debugNetdev) { fmt.Printf("[GetIPAddr]\r\n") @@ -523,32 +526,31 @@ func (w *wifinina) GetIPAddr() (netdev.IP, error) { ip, _, _ := w.getIP() - return netdev.IP(ip), nil + return net.IP(ip), nil } // 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(family netdev.AddressFamily, sockType netdev.SockType, - protocol netdev.Protocol) (netdev.Sockfd, error) { +func (w *wifinina) Socket(domain int, stype int, protocol int) (int, error) { if debugging(debugNetdev) { - fmt.Printf("[Socket] family: %s, sockType: %s, protocol: %s\r\n", - family, sockType, protocol) + fmt.Printf("[Socket] domain: %d, type: %d, protocol: %d\r\n", + domain, stype, protocol) } - switch family { - case netdev.AF_INET: + switch domain { + case syscall.AF_INET: default: - return -1, netdev.ErrFamilyNotSupported + return -1, drivers.ErrFamilyNotSupported } switch { - case protocol == netdev.IPPROTO_TCP && sockType == netdev.SOCK_STREAM: - case protocol == netdev.IPPROTO_TLS && sockType == netdev.SOCK_STREAM: - case protocol == netdev.IPPROTO_UDP && sockType == netdev.SOCK_DGRAM: + case protocol == syscall.IPPROTO_TCP && stype == syscall.SOCK_STREAM: + case protocol == syscall.IPPROTO_TLS && stype == syscall.SOCK_STREAM: + case protocol == syscall.IPPROTO_UDP && stype == syscall.SOCK_DGRAM: default: - return -1, netdev.ErrProtocolNotSupported + return -1, drivers.ErrProtocolNotSupported } w.mu.Lock() @@ -556,19 +558,19 @@ func (w *wifinina) Socket(family netdev.AddressFamily, sockType netdev.SockType, sock := w.getSocket() if sock == noSocketAvail { - return -1, netdev.ErrNoMoreSockets + return -1, drivers.ErrNoMoreSockets } socket := newSocket(protocol) w.sockets[sock] = socket - return netdev.Sockfd(sock), nil + return int(sock), nil } -func (w *wifinina) Bind(sockfd netdev.Sockfd, addr netdev.SockAddr) error { +func (w *wifinina) Bind(sockfd int, ip net.IP, port int) error { if debugging(debugNetdev) { - fmt.Printf("[Bind] sockfd: %d, addr: %s\r\n", sockfd, addr) + fmt.Printf("[Bind] sockfd: %d, addr: %s:%d\r\n", sockfd, ip, port) } w.mu.Lock() @@ -578,21 +580,33 @@ func (w *wifinina) Bind(sockfd netdev.Sockfd, addr netdev.SockAddr) error { var socket = w.sockets[sock] switch socket.protocol { - case netdev.IPPROTO_TCP: - case netdev.IPPROTO_TLS: - case netdev.IPPROTO_UDP: - w.startServer(sock, addr.Port(), protoModeUDP) + case syscall.IPPROTO_TCP: + case syscall.IPPROTO_TLS: + case syscall.IPPROTO_UDP: + w.startServer(sock, uint16(port), protoModeUDP) } - socket.laddr = addr + socket.ip, socket.port = ip, port return nil } -func (w *wifinina) Connect(sockfd netdev.Sockfd, servaddr netdev.SockAddr) error { +func toUint32(ip net.IP) uint32 { + ip = ip.To4() + 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 net.IP, port int) error { if debugging(debugNetdev) { - fmt.Printf("[Connect] sockfd: %d, servaddr: %s\r\n", sockfd, servaddr) + if host == "" { + fmt.Printf("[Connect] sockfd: %d, addr: %s:%d\r\n", sockfd, ip, port) + } else { + fmt.Printf("[Connect] sockfd: %d, host: %s:%d\r\n", sockfd, host, port) + } } w.mu.Lock() @@ -603,12 +617,12 @@ func (w *wifinina) Connect(sockfd netdev.Sockfd, servaddr netdev.SockAddr) error // Start the connection switch socket.protocol { - case netdev.IPPROTO_TCP: - w.startClient(sock, "", servaddr.IpUint32(), servaddr.Port(), protoModeTCP) - case netdev.IPPROTO_TLS: - w.startClient(sock, servaddr.Host(), 0, servaddr.Port(), protoModeTLS) - case netdev.IPPROTO_UDP: - w.startClient(sock, "", servaddr.IpUint32(), servaddr.Port(), protoModeUDP) + case syscall.IPPROTO_TCP: + w.startClient(sock, "", toUint32(ip), uint16(port), protoModeTCP) + case syscall.IPPROTO_TLS: + w.startClient(sock, host, 0, uint16(port), protoModeTLS) + case syscall.IPPROTO_UDP: + w.startClient(sock, "", toUint32(ip), uint16(port), protoModeUDP) return nil } @@ -633,10 +647,14 @@ func (w *wifinina) Connect(sockfd netdev.Sockfd, servaddr netdev.SockAddr) error w.stopClient(sock) - return fmt.Errorf("Connect to %s timed out", servaddr.String()) + if host == "" { + return fmt.Errorf("Connect to %s:%d timed out", ip, port) + } else { + return fmt.Errorf("Connect to %s:%d timed out", host, port) + } } -func (w *wifinina) Listen(sockfd netdev.Sockfd, backlog int) error { +func (w *wifinina) Listen(sockfd int, backlog int) error { if debugging(debugNetdev) { fmt.Printf("[Listen] sockfd: %d\r\n", sockfd) @@ -649,20 +667,20 @@ func (w *wifinina) Listen(sockfd netdev.Sockfd, backlog int) error { var socket = w.sockets[sock] switch socket.protocol { - case netdev.IPPROTO_TCP: - w.startServer(sock, socket.laddr.Port(), protoModeTCP) - case netdev.IPPROTO_UDP: + case syscall.IPPROTO_TCP: + w.startServer(sock, uint16(socket.port), protoModeTCP) + case syscall.IPPROTO_UDP: default: - return netdev.ErrProtocolNotSupported + return drivers.ErrProtocolNotSupported } return nil } -func (w *wifinina) Accept(sockfd netdev.Sockfd, peer netdev.SockAddr) (netdev.Sockfd, error) { +func (w *wifinina) Accept(sockfd int, ip net.IP, port int) (int, error) { if debugging(debugNetdev) { - fmt.Printf("[Accept] sockfd: %d, peer: %s\r\n", sockfd, peer) + fmt.Printf("[Accept] sockfd: %d, peer: %s:%d\r\n", sockfd, ip, port) } w.mu.Lock() @@ -673,9 +691,9 @@ func (w *wifinina) Accept(sockfd netdev.Sockfd, peer netdev.SockAddr) (netdev.So var socket = w.sockets[sock] switch socket.protocol { - case netdev.IPPROTO_TCP: + case syscall.IPPROTO_TCP: default: - return -1, netdev.ErrProtocolNotSupported + return -1, drivers.ErrProtocolNotSupported } for { @@ -715,12 +733,12 @@ func (w *wifinina) Accept(sockfd netdev.Sockfd, peer netdev.SockAddr) (netdev.So continue } // Reuse client socket - return netdev.Sockfd(client), nil + return int(client), nil } // Create new socket for client and return fd w.sockets[client] = newSocket(socket.protocol) - return netdev.Sockfd(client), nil + return int(client), nil } } @@ -793,21 +811,21 @@ func (w *wifinina) sendUDP(sock sock, buf []byte, timeout time.Duration) (int, e return len(buf), nil } -func (w *wifinina) sendChunk(sockfd netdev.Sockfd, buf []byte, timeout time.Duration) (int, error) { +func (w *wifinina) sendChunk(sockfd int, buf []byte, timeout time.Duration) (int, error) { var sock = sock(sockfd) var socket = w.sockets[sock] switch socket.protocol { - case netdev.IPPROTO_TCP, netdev.IPPROTO_TLS: + case syscall.IPPROTO_TCP, syscall.IPPROTO_TLS: return w.sendTCP(sock, buf, timeout) - case netdev.IPPROTO_UDP: + case syscall.IPPROTO_UDP: return w.sendUDP(sock, buf, timeout) } - return -1, netdev.ErrProtocolNotSupported + return -1, drivers.ErrProtocolNotSupported } -func (w *wifinina) Send(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, +func (w *wifinina) Send(sockfd int, buf []byte, flags int, timeout time.Duration) (int, error) { if debugging(debugNetdev) { @@ -835,7 +853,7 @@ func (w *wifinina) Send(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags return len(buf), nil } -func (w *wifinina) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, +func (w *wifinina) Recv(sockfd int, buf []byte, flags int, timeout time.Duration) (int, error) { if debugging(debugNetdev) { @@ -861,7 +879,7 @@ func (w *wifinina) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags // Check if we've timed out if timeout > 0 { if time.Now().Before(expire) { - return -1, netdev.ErrRecvTimeout + return -1, drivers.ErrRecvTimeout } } @@ -880,7 +898,7 @@ func (w *wifinina) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags } // Check if socket went down - if socket.protocol != netdev.IPPROTO_UDP && w.sockDown(sock) { + if socket.protocol != syscall.IPPROTO_UDP && w.sockDown(sock) { // Get any last bytes n = int(w.getDataBuf(sock, buf[:max])) if debugging(debugNetdev) { @@ -905,7 +923,7 @@ func (w *wifinina) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags } } -func (w *wifinina) Close(sockfd netdev.Sockfd) error { +func (w *wifinina) Close(sockfd int) error { if debugging(debugNetdev) { fmt.Printf("[Close] sockfd: %d\r\n", sockfd) @@ -923,7 +941,7 @@ func (w *wifinina) Close(sockfd netdev.Sockfd) error { w.stopClient(sock) - if socket.protocol == netdev.IPPROTO_UDP { + if socket.protocol == syscall.IPPROTO_UDP { socket.inuse = false return nil } @@ -942,17 +960,16 @@ func (w *wifinina) Close(sockfd netdev.Sockfd) error { w.mu.Lock() } - return netdev.ErrClosingSocket + return drivers.ErrClosingSocket } -func (w *wifinina) SetSockOpt(sockfd netdev.Sockfd, level netdev.SockOptLevel, - opt netdev.SockOpt, value any) error { +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 + return drivers.ErrNotSupported } // Is TCP/TLS socket connected? @@ -1181,7 +1198,7 @@ func (w *wifinina) getCurrentencryptionType() encryptionType { return encryptionType(enctype) } -func (w *wifinina) getCurrentBSSID() macAddress { +func (w *wifinina) getCurrentBSSID() net.HardwareAddr { return w.getMACAddress(w.req1(cmdGetCurrBSSID)) } @@ -1193,7 +1210,7 @@ func (w *wifinina) getCurrentSSID() string { return w.getString(w.req1(cmdGetCurrSSID)) } -func (w *wifinina) getMACAddr() macAddress { +func (w *wifinina) getMACAddr() net.HardwareAddr { if debugging(debugCmd) { fmt.Printf(" [cmdGetMACAddr]\r\n") } @@ -1207,7 +1224,7 @@ func (w *wifinina) faultf(f string, args ...any) { } } -func (w *wifinina) getIP() (ip, subnet, gateway netdev.IP) { +func (w *wifinina) getIP() (ip, subnet, gateway net.IP) { if debugging(debugCmd) { fmt.Printf(" [cmdGetIPAddr]\r\n") } @@ -1217,6 +1234,7 @@ func (w *wifinina) getIP() (ip, subnet, gateway netdev.IP) { w.faultf("getIP wanted l=3, got l=%d", l) return } + ip, subnet, gateway = make([]byte, 4), make([]byte, 4), make([]byte, 4) copy(ip[:], sl[0]) copy(subnet[:], sl[1]) copy(gateway[:], sl[2]) @@ -1234,9 +1252,9 @@ func (w *wifinina) getHostByName(name string) string { return w.getString(w.req0(cmdGetHostByName)) } -func (w *wifinina) getNetworkBSSID(idx int) macAddress { +func (w *wifinina) getNetworkBSSID(idx int) net.HardwareAddr { if idx < 0 || idx >= maxNetworks { - return macAddress{} + return net.HardwareAddr{} } return w.getMACAddress(w.reqUint8(cmdGetIdxBSSID, uint8(idx))) } @@ -1421,7 +1439,7 @@ func (w *wifinina) getFloat32(l uint8) float32 { return float32(w.getUint32(l)) } -func (w *wifinina) getMACAddress(l uint8) macAddress { +func (w *wifinina) getMACAddress(l uint8) net.HardwareAddr { if l == 6 { mac := w.buf[0:6] // Reverse the bytes @@ -1431,7 +1449,7 @@ func (w *wifinina) getMACAddress(l uint8) macAddress { return mac } w.faultf("expected length 6, was actually %d", l) - return macAddress{} + return net.HardwareAddr{} } func (w *wifinina) transfer(b byte) byte { From a91cf1c9657e5847d46895f63cde224cc175beb3 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Wed, 8 Mar 2023 17:37:42 -0800 Subject: [PATCH 23/47] get espat working with example tests --- espat/espat.go | 119 ++++++++++++++++++------------- espat/tcp.go | 2 +- espat/wifi.go | 10 --- examples/net/espat/main.go | 98 ------------------------- examples/net/mqttclient/espat.go | 23 ++++++ examples/net/mqttclient/main.go | 29 +++++++- examples/net/ntpclient/espat.go | 23 ++++++ examples/net/rawsocket/espat.go | 23 ++++++ examples/net/tcpclient/espat.go | 23 ++++++ 9 files changed, 190 insertions(+), 160 deletions(-) delete mode 100644 examples/net/espat/main.go create mode 100644 examples/net/mqttclient/espat.go create mode 100644 examples/net/ntpclient/espat.go create mode 100644 examples/net/rawsocket/espat.go create mode 100644 examples/net/tcpclient/espat.go diff --git a/espat/espat.go b/espat/espat.go index f1ca0b5bd..be90d57af 100644 --- a/espat/espat.go +++ b/espat/espat.go @@ -27,15 +27,11 @@ import ( "net" "strconv" "strings" + "sync" "syscall" "time" - "tinygo.org/x/drivers/netdev" -) - -var ( - version = "0.0.1" - driverName = "Espressif ESP8266/ESP32 AT Wifi network device driver (espat)" + "tinygo.org/x/drivers" ) type Config struct { @@ -64,27 +60,28 @@ type Device struct { // data received from a TCP/UDP connection forwarded by the ESP8266/ESP32 data []byte socket socket + mu sync.Mutex } func New(cfg *Config) *Device { d := Device{ cfg: cfg, - response: make([]byte, 512), - data: make([]byte, 0, 1024), + response: make([]byte, 1500), + data: make([]byte, 0, 1500), } - netdev.Use(&d) - var _ netdev.Ifacer = (*Device)(nil) + + drivers.UseNetdev(&d) + + // assert that driver implements Netlinker + var _ drivers.Netlinker = (*Device)(nil) + return &d } func (d *Device) NetConnect() error { - fmt.Printf("\r\n") - fmt.Printf("%s\r\n\r\n", driverName) - fmt.Printf("Driver version : %s\r\n\r\n", version) - if len(d.cfg.Ssid) == 0 { - return netdev.ErrMissingSSID + return drivers.ErrMissingSSID } d.uart = d.cfg.Uart @@ -102,35 +99,29 @@ func (d *Device) NetConnect() error { if !d.Connected() { fmt.Printf("FAILED\r\n") - return netdev.ErrConnectFailed + return drivers.ErrConnectFailed } fmt.Printf("CONNECTED\r\n") - fmt.Printf("\r\n") - fmt.Printf("ESP8266/ESP32 firmware version : %s\r\n", string(d.Version())) - fmt.Printf("MAC address : %s\r\n", d.GetMacAddress()) - fmt.Printf("\r\n") - // Connect to Wifi AP fmt.Printf("Connecting to Wifi SSID '%s'...", d.cfg.Ssid) d.SetWifiMode(WifiModeClient) + err := d.ConnectToAP(d.cfg.Ssid, d.cfg.Passphrase, 10 /* secs */) if err != nil { fmt.Printf("FAILED\r\n") - return netdev.ErrConnectFailed + return err } fmt.Printf("CONNECTED\r\n") - ip, err := d.GetClientIP() + ip, err := d.GetIPAddr() if err != nil { - return netdev.ErrConnectFailed + return err } - - fmt.Printf("\r\n") - fmt.Printf("DHCP-assigned IP : %s\r\n", ip) + fmt.Printf("DHCP-assigned IP: %s\r\n", ip) fmt.Printf("\r\n") return nil @@ -141,7 +132,7 @@ func (d *Device) NetDisconnect() { fmt.Printf("\r\nDisconnected from Wifi SSID '%s'\r\n\r\n", d.cfg.Ssid) } -func (d *Device) NetNotify(cb func(netdev.Event)) { +func (d *Device) NetNotify(cb func(drivers.NetlinkEvent)) { // Not supported } @@ -151,12 +142,22 @@ func (d *Device) GetHostByName(name string) (net.IP, error) { } func (d *Device) GetHardwareAddr() (net.HardwareAddr, error) { - return net.ParseMAC(d.GetMacAddress()) + return net.HardwareAddr{}, drivers.ErrNotSupported } func (d *Device) GetIPAddr() (net.IP, error) { - ip, err := d.GetClientIP() - return net.ParseIP(ip), err + resp, err := d.GetClientIP() + if err != nil { + return net.IP{}, err + } + prefix := "+CIPSTA:ip:" + for _, line := range strings.Split(resp, "\n") { + if ok := strings.HasPrefix(line, prefix); ok { + ip := line[len(prefix)+1:len(line)-2] + return net.ParseIP(ip), nil + } + } + return net.IP{}, fmt.Errorf("Error getting IP address") } func (d *Device) Socket(domain int, stype int, protocol int) (int, error) { @@ -164,7 +165,7 @@ func (d *Device) Socket(domain int, stype int, protocol int) (int, error) { switch domain { case syscall.AF_INET: default: - return -1, netdev.ErrFamilyNotSupported + return -1, drivers.ErrFamilyNotSupported } switch { @@ -172,12 +173,12 @@ func (d *Device) Socket(domain int, stype int, protocol int) (int, error) { case protocol == syscall.IPPROTO_TLS && stype == syscall.SOCK_STREAM: case protocol == syscall.IPPROTO_UDP && stype == syscall.SOCK_DGRAM: default: - return -1, netdev.ErrProtocolNotSupported + return -1, drivers.ErrProtocolNotSupported } // Only supporting single connection mode, so only one socket at a time if d.socket.inUse { - return -1, netdev.ErrNoMoreSockets + return -1, drivers.ErrNoMoreSockets } d.socket.inUse = true d.socket.protocol = protocol @@ -194,32 +195,40 @@ func (d *Device) Bind(sockfd int, ip net.IP, port int) error { func (d *Device) Connect(sockfd int, host string, ip net.IP, port int) error { var err error var addr = ip.String() - var sport = strconv.Itoa(port) + var rport = strconv.Itoa(port) var lport = strconv.Itoa(d.socket.lport) switch d.socket.protocol { case syscall.IPPROTO_TCP: - err = d.ConnectTCPSocket(addr, sport) + err = d.ConnectTCPSocket(addr, rport) case syscall.IPPROTO_UDP: - err = d.ConnectUDPSocket(addr, sport, lport) + err = d.ConnectUDPSocket(addr, rport, lport) case syscall.IPPROTO_TLS: - err = d.ConnectSSLSocket(addr, sport) + err = d.ConnectSSLSocket(host, rport) } - return err + if err != nil { + if host == "" { + return fmt.Errorf("Connect to %s:%d timed out", ip, port) + } else { + return fmt.Errorf("Connect to %s:%d timed out", host, port) + } + } + + return nil } func (d *Device) Listen(sockfd int, backlog int) error { switch d.socket.protocol { case syscall.IPPROTO_UDP: default: - return netdev.ErrProtocolNotSupported + return drivers.ErrProtocolNotSupported } return nil } func (d *Device) Accept(sockfd int, ip net.IP, port int) (int, error) { - return -1, netdev.ErrNotSupported + return -1, drivers.ErrNotSupported } func (d *Device) sendChunk(sockfd int, buf []byte, timeout time.Duration) (int, error) { @@ -240,6 +249,9 @@ func (d *Device) sendChunk(sockfd int, buf []byte, timeout time.Duration) (int, func (d *Device) Send(sockfd int, buf []byte, flags int, timeout time.Duration) (int, error) { + d.mu.Lock() + defer d.mu.Unlock() + // Break large bufs into chunks so we don't overrun the hw queue chunkSize := 1436 @@ -259,6 +271,9 @@ func (d *Device) Send(sockfd int, buf []byte, flags int, timeout time.Duration) func (d *Device) Recv(sockfd int, buf []byte, flags int, timeout time.Duration) (int, error) { + d.mu.Lock() + defer d.mu.Unlock() + var length = len(buf) var expire = time.Now().Add(timeout) @@ -271,7 +286,7 @@ func (d *Device) Recv(sockfd int, buf []byte, flags int, timeout time.Duration) // Check if we've timed out if timeout > 0 { if time.Now().Before(expire) { - return -1, netdev.ErrRecvTimeout + return -1, drivers.ErrRecvTimeout } } @@ -280,7 +295,9 @@ func (d *Device) Recv(sockfd int, buf []byte, flags int, timeout time.Duration) return -1, err } if n == 0 { + d.mu.Unlock() time.Sleep(100 * time.Millisecond) + d.mu.Lock() continue } @@ -289,11 +306,15 @@ func (d *Device) Recv(sockfd int, buf []byte, flags int, timeout time.Duration) } func (d *Device) Close(sockfd int) error { + d.mu.Lock() + defer d.mu.Unlock() + + d.socket.inUse = false return d.DisconnectSocket() } func (d *Device) SetSockOpt(sockfd int, level int, opt int, value interface{}) error { - return netdev.ErrNotSupported + return drivers.ErrNotSupported } // Connected checks if there is communication with the ESP8266/ESP32. @@ -301,7 +322,7 @@ func (d *Device) Connected() bool { d.Execute(Test) // handle response here, should include "OK" - _, err := d.Response(100) + _, err := d.Response(1000) if err != nil { return false } @@ -344,9 +365,10 @@ func (d Device) Set(cmd, params string) error { // Version returns the ESP8266/ESP32 firmware version info. func (d Device) Version() []byte { d.Execute(Version) - r, err := d.Response(100) + r, err := d.Response(2000) if err != nil { - return []byte("unknown") + //return []byte("unknown") + return []byte(err.Error()) } return r } @@ -448,14 +470,15 @@ func (d *Device) parseIPD(end int) error { val := string(d.response[s+5 : e]) // TODO: verify count - _, err := strconv.Atoi(val) + v, err := strconv.Atoi(val) if err != nil { // not expected data here. what to do? return err } // load up the socket data - d.data = append(d.data, d.response[e+1:end]...) + //d.data = append(d.data, d.response[e+1:end]...) + d.data = append(d.data, d.response[e+1:e+1+v]...) return nil } diff --git a/espat/tcp.go b/espat/tcp.go index b2c87e507..9c8524d9a 100644 --- a/espat/tcp.go +++ b/espat/tcp.go @@ -51,7 +51,7 @@ func (d *Device) ConnectTCPSocket(addr, port string) error { // ConnectUDPSocket creates a new UDP connection for the ESP8266/ESP32. func (d *Device) ConnectUDPSocket(addr, sendport, listenport string) error { protocol := "UDP" - val := "\"" + protocol + "\",\"" + addr + "\"," + sendport + "," + listenport + ",2" + val := "\"" + protocol + "\",\"" + addr + "\"," + sendport + "," + listenport + ",0" err := d.Set(TCPConnect, val) if err != nil { return err diff --git a/espat/wifi.go b/espat/wifi.go index 203dd9390..808cc0762 100644 --- a/espat/wifi.go +++ b/espat/wifi.go @@ -54,16 +54,6 @@ func (d *Device) DisconnectFromAP() error { return err } -// GetMacAddress returns the ESP8266/ESP32 current station MAC addess -func (d *Device) GetMacAddress() string { - d.Query(SetStationMACAddress) - r, err := d.Response(1000) - if err != nil { - return "unknown" - } - return string(r) -} - // GetClientIP returns the ESP8266/ESP32 current client IP addess when connected to an Access Point. func (d *Device) GetClientIP() (string, error) { d.Query(SetStationIP) diff --git a/examples/net/espat/main.go b/examples/net/espat/main.go deleted file mode 100644 index 2c9660756..000000000 --- a/examples/net/espat/main.go +++ /dev/null @@ -1,98 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "log" - "machine" - "net" - "time" - - "tinygo.org/x/drivers/espat" -) - -var ( - ssid string - pass string - addr string = "10.0.0.100:8080" -) - -var ( - cfg = espat.Config{ - Ssid: ssid, - Passphrase: pass, - Uart: machine.UART2, - Tx: machine.TX1, - Rx: machine.RX0, - } - netdev = espat.New(&cfg) -) - -var buf = &bytes.Buffer{} - -func main() { - - waitSerial() - - if err := netdev.NetConnect(); err != nil { - log.Fatal(err) - } - - for { - sendBatch() - time.Sleep(500 * time.Millisecond) - } -} - -func sendBatch() { - - // make TCP connection - message("---------------\r\nDialing TCP connection") - conn, err := net.Dial("tcp", addr) - for ; err != nil; conn, err = net.Dial("tcp", addr) { - message(err.Error()) - time.Sleep(5 * time.Second) - } - - n := 0 - w := 0 - start := time.Now() - - // send data - message("Sending data") - - for i := 0; i < 1000; i++ { - buf.Reset() - fmt.Fprint(buf, - "\r---------------------------- i == ", i, " ----------------------------"+ - "\r---------------------------- i == ", i, " ----------------------------") - if w, err = conn.Write(buf.Bytes()); err != nil { - println("error:", err.Error(), "\r") - break - } - n += w - } - - buf.Reset() - ms := time.Now().Sub(start).Milliseconds() - fmt.Fprint(buf, "\nWrote ", n, " bytes in ", ms, " ms\r\n") - message(buf.String()) - - if _, err := conn.Write(buf.Bytes()); err != nil { - println("error:", err.Error(), "\r") - } - - println("Disconnecting TCP...") - conn.Close() -} - -func message(msg string) { - println(msg, "\r") -} - -// Wait for user to open serial console -func waitSerial() { - for !machine.Serial.DTR() { - time.Sleep(100 * time.Millisecond) - } -} diff --git a/examples/net/mqttclient/espat.go b/examples/net/mqttclient/espat.go new file mode 100644 index 000000000..aad922746 --- /dev/null +++ b/examples/net/mqttclient/espat.go @@ -0,0 +1,23 @@ +//go:build challenger_rp2040 + +// +build: challenger_rp2040 + +package main + +import ( + "machine" + + "tinygo.org/x/drivers/espat" +) + +var cfg = espat.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // UART + Uart: machine.UART1, + Tx: machine.UART1_TX_PIN, + Rx: machine.UART1_RX_PIN, +} + +var netdev = espat.New(&cfg) diff --git a/examples/net/mqttclient/main.go b/examples/net/mqttclient/main.go index c2b2ad44a..a09ccc6a9 100644 --- a/examples/net/mqttclient/main.go +++ b/examples/net/mqttclient/main.go @@ -9,6 +9,7 @@ package main import ( "fmt" "log" + "math/rand" "machine" "time" @@ -40,13 +41,17 @@ func main() { log.Fatal(err) } + clientId := "tinygo-client-" + randomString(10) + fmt.Printf("ClientId: %s\n", clientId) + options := mqtt.NewClientOptions() options.AddBroker(broker) - options.SetClientID("tinygo_mqtt_example") + options.SetClientID(clientId) options.SetDefaultPublishHandler(messagePubHandler) options.OnConnect = connectHandler options.OnConnectionLost = connectionLostHandler + fmt.Printf("Connecting to MQTT broker at %s\n", broker) client := mqtt.NewClient(options) token := client.Connect() if token.Wait() && token.Error() != nil { @@ -55,20 +60,38 @@ func main() { topic := "cpu/freq" token = client.Subscribe(topic, 1, nil) - token.Wait() + if token.Wait() && token.Error() != nil { + panic(token.Error()) + } fmt.Printf("Subscribed to topic %s\n", topic) for i := 0; i < 10; i++ { freq := float32(machine.CPUFrequency()) / 1000000 payload := fmt.Sprintf("%.02fMhz", freq) token = client.Publish(topic, 0, false, payload) - token.Wait() + if token.Wait() && token.Error() != nil { + panic(token.Error()) + } time.Sleep(time.Second) } client.Disconnect(100) } +// Returns an int >= min, < max +func randomInt(min, max int) int { + return min + rand.Intn(max-min) +} + +// Generate a random string of A-Z chars with len = l +func randomString(len int) string { + bytes := make([]byte, len) + for i := 0; i < len; i++ { + bytes[i] = byte(randomInt(65, 90)) + } + return string(bytes) +} + // Wait for user to open serial console func waitSerial() { for !machine.Serial.DTR() { diff --git a/examples/net/ntpclient/espat.go b/examples/net/ntpclient/espat.go new file mode 100644 index 000000000..aad922746 --- /dev/null +++ b/examples/net/ntpclient/espat.go @@ -0,0 +1,23 @@ +//go:build challenger_rp2040 + +// +build: challenger_rp2040 + +package main + +import ( + "machine" + + "tinygo.org/x/drivers/espat" +) + +var cfg = espat.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // UART + Uart: machine.UART1, + Tx: machine.UART1_TX_PIN, + Rx: machine.UART1_RX_PIN, +} + +var netdev = espat.New(&cfg) diff --git a/examples/net/rawsocket/espat.go b/examples/net/rawsocket/espat.go new file mode 100644 index 000000000..aad922746 --- /dev/null +++ b/examples/net/rawsocket/espat.go @@ -0,0 +1,23 @@ +//go:build challenger_rp2040 + +// +build: challenger_rp2040 + +package main + +import ( + "machine" + + "tinygo.org/x/drivers/espat" +) + +var cfg = espat.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // UART + Uart: machine.UART1, + Tx: machine.UART1_TX_PIN, + Rx: machine.UART1_RX_PIN, +} + +var netdev = espat.New(&cfg) diff --git a/examples/net/tcpclient/espat.go b/examples/net/tcpclient/espat.go new file mode 100644 index 000000000..aad922746 --- /dev/null +++ b/examples/net/tcpclient/espat.go @@ -0,0 +1,23 @@ +//go:build challenger_rp2040 + +// +build: challenger_rp2040 + +package main + +import ( + "machine" + + "tinygo.org/x/drivers/espat" +) + +var cfg = espat.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // UART + Uart: machine.UART1, + Tx: machine.UART1_TX_PIN, + Rx: machine.UART1_RX_PIN, +} + +var netdev = espat.New(&cfg) From 56208a28d513cfc25a7d5b32f450fa5289d0cc92 Mon Sep 17 00:00:00 2001 From: Olivier Fauchon Date: Thu, 23 Feb 2023 14:15:07 +0100 Subject: [PATCH 24/47] sx127x/sx126x: Remove heap alloc in interrupt, add non blocking channel send/receive, and other cleanups --- sx126x/sx126x.go | 62 ++++++++++++++++++++++++++-------------- sx127x/sx127x.go | 74 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 91 insertions(+), 45 deletions(-) diff --git a/sx126x/sx126x.go b/sx126x/sx126x.go index 653c71599..db814a0da 100644 --- a/sx126x/sx126x.go +++ b/sx126x/sx126x.go @@ -34,8 +34,9 @@ const ( ) const ( - PERIOD_PER_SEC = (uint32)(1000000 / 15.625) // SX1261 DS 13.1.4 - SPI_BUFFER_SIZE = 256 + PERIOD_PER_SEC = (uint32)(1000000 / 15.625) // SX1261 DS 13.1.4 + SPI_BUFFER_SIZE = 256 + RADIOEVENTCHAN_SIZE = 1 ) // Device wraps an SPI connection to a SX126x device. @@ -47,14 +48,18 @@ type Device struct { controller RadioController // to manage interactions with the radio deepSleep bool // Internal Sleep state deviceType int // sx1261,sx1262,sx1268 (defaults sx1261) - spiBuffer [SPI_BUFFER_SIZE]uint8 + spiTxBuf []byte // global Tx buffer to avoid heap allocations in interrupt + spiRxBuf []byte // global Rx buffer to avoid heap allocations in interrupt + } // New creates a new SX126x connection. func New(spi drivers.SPI) *Device { return &Device{ spi: spi, - radioEventChan: make(chan lora.RadioEvent, 10), + radioEventChan: make(chan lora.RadioEvent, RADIOEVENTCHAN_SIZE), + spiTxBuf: make([]byte, SPI_BUFFER_SIZE), + spiRxBuf: make([]byte, SPI_BUFFER_SIZE), } } @@ -250,21 +255,25 @@ func (d *Device) ReadRegister(addr, size uint16) ([]uint8, error) { d.CheckDeviceReady() d.controller.SetNss(false) // Send command - cmd := []uint8{SX126X_CMD_READ_REGISTER, uint8((addr & 0xFF00) >> 8), uint8(addr & 0x00FF), 0x00} - d.spi.Tx(cmd, nil) - ret := d.spiBuffer[0:size] - d.spi.Tx(nil, ret) + d.spiTxBuf = d.spiTxBuf[:0] + d.spiTxBuf = append(d.spiTxBuf, SX126X_CMD_READ_REGISTER, uint8((addr&0xFF00)>>8), uint8(addr&0x00FF), 0x00) + d.spi.Tx(d.spiTxBuf, nil) + // Read registers + d.spiRxBuf = d.spiRxBuf[0:size] + d.spi.Tx(nil, d.spiRxBuf) d.controller.SetNss(true) d.controller.WaitWhileBusy() - return ret, nil + return d.spiRxBuf, nil } // WriteRegister writes value to register func (d *Device) WriteRegister(addr uint16, data []uint8) { d.CheckDeviceReady() d.controller.SetNss(false) - cmd := []uint8{SX126X_CMD_WRITE_REGISTER, uint8((addr & 0xFF00) >> 8), uint8(addr & 0x00FF)} - d.spi.Tx(append(cmd, data...), nil) + d.spiTxBuf = d.spiTxBuf[:0] + d.spiTxBuf = append(d.spiTxBuf, SX126X_CMD_WRITE_REGISTER, uint8((addr&0xFF00)>>8), uint8(addr&0x00FF)) + d.spiTxBuf = append(d.spiTxBuf, data...) + d.spi.Tx(d.spiTxBuf, nil) d.controller.SetNss(true) d.controller.WaitWhileBusy() } @@ -497,7 +506,10 @@ func (d *Device) ExecSetCommand(cmd uint8, buf []uint8) { } d.controller.SetNss(false) // Send command and params - d.spi.Tx(append([]uint8{cmd}, buf...), nil) + d.spiTxBuf = d.spiTxBuf[:0] + d.spiTxBuf = append(d.spiTxBuf, cmd) + d.spiTxBuf = append(d.spiTxBuf, buf...) + d.spi.Tx(d.spiTxBuf, nil) d.controller.SetNss(true) if cmd != SX126X_CMD_SET_SLEEP { d.controller.WaitWhileBusy() @@ -509,11 +521,15 @@ func (d *Device) ExecGetCommand(cmd uint8, size uint8) []uint8 { d.CheckDeviceReady() d.controller.SetNss(false) // Send the command and flush first status byte (as not used) - d.spi.Tx([]uint8{cmd, 0x00}, nil) - d.spi.Tx(nil, d.spiBuffer[:size]) + d.spiTxBuf = d.spiTxBuf[:0] + d.spiTxBuf = append(d.spiTxBuf, cmd, 0x00) + d.spi.Tx(d.spiTxBuf, nil) + // Read resp + d.spiRxBuf = d.spiRxBuf[:size] + d.spi.Tx(nil, d.spiRxBuf) d.controller.SetNss(true) d.controller.WaitWhileBusy() - return d.spiBuffer[:size] + return d.spiRxBuf } // @@ -685,22 +701,26 @@ func (d *Device) HandleInterrupt() { st := d.GetIrqStatus() d.ClearIrqStatus(SX126X_IRQ_ALL) - rChan := d.GetRadioEventChan() - if (st & SX126X_IRQ_RX_DONE) > 0 { - rChan <- lora.NewRadioEvent(lora.RadioEventRxDone, st, nil) + e := lora.RadioEvent{lora.RadioEventRxDone, uint16(st), nil} + d.radioEventChan <- e } if (st & SX126X_IRQ_TX_DONE) > 0 { - rChan <- lora.NewRadioEvent(lora.RadioEventTxDone, st, nil) + e := lora.RadioEvent{lora.RadioEventTxDone, uint16(st), nil} + d.radioEventChan <- e } if (st & SX126X_IRQ_TIMEOUT) > 0 { - rChan <- lora.NewRadioEvent(lora.RadioEventTimeout, st, nil) + e := lora.RadioEvent{lora.RadioEventTimeout, uint16(st), nil} + d.radioEventChan <- e + } if (st & SX126X_IRQ_CRC_ERR) > 0 { - rChan <- lora.NewRadioEvent(lora.RadioEventCrcError, st, nil) + e := lora.RadioEvent{lora.RadioEventCrcError, uint16(st), nil} + d.radioEventChan <- e + } } diff --git a/sx127x/sx127x.go b/sx127x/sx127x.go index 1753149ac..60d5b4481 100644 --- a/sx127x/sx127x.go +++ b/sx127x/sx127x.go @@ -15,7 +15,8 @@ import ( // So we can keep track of the origin of interruption const ( - SPI_BUFFER_SIZE = 256 + RADIOEVENTCHAN_SIZE = 1 + SPI_BUFFER_SIZE = 256 ) // Device wraps an SPI connection to a SX127x device. @@ -27,8 +28,8 @@ type Device struct { controller RadioController // to manage interactions with the radio deepSleep bool // Internal Sleep state deviceType int // sx1261,sx1262,sx1268 (defaults sx1261) - spiBuffer [SPI_BUFFER_SIZE]uint8 - packetIndex uint8 // FIXME ... useless ? + spiTxBuf []byte // global Tx buffer to avoid heap allocations in interrupt + spiRxBuf []byte // global Rx buffer to avoid heap allocations in interrupt } // -------------------------------------------------- @@ -46,7 +47,9 @@ func New(spi machine.SPI, rstPin machine.Pin) *Device { k := Device{ spi: spi, rstPin: rstPin, - radioEventChan: make(chan lora.RadioEvent, 10), + radioEventChan: make(chan lora.RadioEvent, RADIOEVENTCHAN_SIZE), + spiTxBuf: make([]byte, SPI_BUFFER_SIZE), + spiRxBuf: make([]byte, SPI_BUFFER_SIZE), } return &k } @@ -79,21 +82,37 @@ func (d *Device) DetectDevice() bool { // ReadRegister reads register value func (d *Device) ReadRegister(reg uint8) uint8 { d.controller.SetNss(false) - d.spi.Tx([]byte{reg & 0x7f}, nil) - var value [1]byte - d.spi.Tx(nil, value[:]) + // Send register + //d.spiTxBuf = []byte{reg & 0x7f} + d.spiTxBuf = d.spiTxBuf[:0] + d.spiTxBuf = append(d.spiTxBuf, byte(reg&0x7f)) + //println("R1 : ", len(d.spiTxBuf)) + d.spi.Tx(d.spiTxBuf, nil) + // Read value + //d.spiRxBuf = []byte{reg & 0x00} + d.spiRxBuf = d.spiRxBuf[:0] + d.spiRxBuf = append(d.spiRxBuf, 0) + //println("R2 : ", len(d.spiTxBuf)) + d.spi.Tx(nil, d.spiRxBuf) d.controller.SetNss(true) - return value[0] + return d.spiRxBuf[0] } // WriteRegister writes value to register func (d *Device) WriteRegister(reg uint8, value uint8) uint8 { - var response [1]byte d.controller.SetNss(false) - d.spi.Tx([]byte{reg | 0x80}, nil) - d.spi.Tx([]byte{value}, response[:]) + // Send register + d.spiTxBuf = d.spiTxBuf[:0] + d.spiTxBuf = append(d.spiTxBuf, byte(reg|0x80)) + d.spi.Tx(d.spiTxBuf, nil) + // Send value + d.spiTxBuf = d.spiTxBuf[:0] + d.spiTxBuf = append(d.spiTxBuf, byte(value)) + d.spiRxBuf = d.spiRxBuf[:0] + d.spiRxBuf = append(d.spiRxBuf, 0) + d.spi.Tx(d.spiTxBuf, d.spiRxBuf) d.controller.SetNss(true) - return response[0] + return d.spiRxBuf[0] } // SetOpMode changes the sx1276 mode @@ -434,16 +453,13 @@ func (d *Device) Rx(timeoutMs uint32) ([]uint8, error) { // Mask all but RxDone d.WriteRegister(SX127X_REG_IRQ_FLAGS_MASK, ^(SX127X_IRQ_LORA_RXDONE_MASK | SX127X_IRQ_LORA_RXTOUT_MASK)) - // Get Radio Event Channel - radioCh := d.GetRadioEventChan() - // Single RX mode don't properly handle Timeouts on sx127x, so we use Continuous RX // Go routine is a workaround to stop the Continuous RX and fire a timeout Event d.SetOpMode(SX127X_OPMODE_RX) var msg lora.RadioEvent select { - case msg = <-radioCh: + case msg = <-d.radioEventChan: if msg.EventType != lora.RadioEventRxDone { return nil, errors.New("Unexpected Radio Event while RX " + string(0x30+msg.EventType)) } @@ -459,10 +475,11 @@ func (d *Device) Rx(timeoutMs uint32) ([]uint8, error) { pLen := d.ReadRegister(SX127X_REG_RX_NB_BYTES) d.WriteRegister(SX127X_REG_FIFO_ADDR_PTR, d.ReadRegister(SX127X_REG_FIFO_RX_CURRENT_ADDR)) + rxData := []uint8{} for i := uint8(0); i < pLen; i++ { - d.spiBuffer[i] = d.ReadRegister(SX127X_REG_FIFO) + rxData = append(rxData, d.ReadRegister(SX127X_REG_FIFO)) } - return d.spiBuffer[:pLen], nil + return rxData, nil } // SetTxContinuousMode enable Continuous Tx mode @@ -506,26 +523,35 @@ func (d *Device) RandomU32() uint32 { // HandleInterrupt must be called by main code on DIO state change. func (d *Device) HandleInterrupt() { + // Get IRQ and clear st := d.ReadRegister(SX127X_REG_IRQ_FLAGS) d.WriteRegister(SX127X_REG_IRQ_FLAGS, 0xFF) - rChan := d.GetRadioEventChan() - if (st & SX127X_IRQ_LORA_RXDONE_MASK) > 0 { - rChan <- lora.NewRadioEvent(lora.RadioEventRxDone, uint16(st), nil) + e := lora.RadioEvent{lora.RadioEventRxDone, uint16(st), nil} + d.radioEventChan <- e } if (st & SX127X_IRQ_LORA_TXDONE_MASK) > 0 { - rChan <- lora.NewRadioEvent(lora.RadioEventTxDone, uint16(st), nil) + e := lora.RadioEvent{lora.RadioEventTxDone, uint16(st), nil} + d.radioEventChan <- e } if (st & SX127X_IRQ_LORA_RXTOUT_MASK) > 0 { - rChan <- lora.NewRadioEvent(lora.RadioEventTimeout, uint16(st), nil) + e := lora.RadioEvent{lora.RadioEventTimeout, uint16(st), nil} + select { + case d.radioEventChan <- e: + default: + } } if (st & SX127X_IRQ_LORA_CRCERR_MASK) > 0 { - rChan <- lora.NewRadioEvent(lora.RadioEventCrcError, uint16(st), nil) + e := lora.RadioEvent{lora.RadioEventCrcError, uint16(st), nil} + select { + case d.radioEventChan <- e: + default: + } } } From 94017b543795c8667b2363f4d858c441e17fe0ed Mon Sep 17 00:00:00 2001 From: Olivier Fauchon Date: Thu, 23 Feb 2023 21:33:37 +0100 Subject: [PATCH 25/47] sx126x/sx27x: Reduce spi buffer size, add missing select when using channels --- sx126x/sx126x.go | 24 ++++++++++++++++-------- sx127x/sx127x.go | 23 +++++++++++------------ 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/sx126x/sx126x.go b/sx126x/sx126x.go index db814a0da..0cee91b1d 100644 --- a/sx126x/sx126x.go +++ b/sx126x/sx126x.go @@ -702,25 +702,33 @@ func (d *Device) HandleInterrupt() { d.ClearIrqStatus(SX126X_IRQ_ALL) if (st & SX126X_IRQ_RX_DONE) > 0 { - e := lora.RadioEvent{lora.RadioEventRxDone, uint16(st), nil} - d.radioEventChan <- e + select { + case d.radioEventChan <- lora.RadioEvent{lora.RadioEventRxDone, uint16(st), nil}: + default: + } } if (st & SX126X_IRQ_TX_DONE) > 0 { - e := lora.RadioEvent{lora.RadioEventTxDone, uint16(st), nil} - d.radioEventChan <- e + select { + case d.radioEventChan <- lora.RadioEvent{lora.RadioEventTxDone, uint16(st), nil}: + default: + } } if (st & SX126X_IRQ_TIMEOUT) > 0 { - e := lora.RadioEvent{lora.RadioEventTimeout, uint16(st), nil} - d.radioEventChan <- e + select { + case d.radioEventChan <- lora.RadioEvent{lora.RadioEventTimeout, uint16(st), nil}: + default: + } } if (st & SX126X_IRQ_CRC_ERR) > 0 { - e := lora.RadioEvent{lora.RadioEventCrcError, uint16(st), nil} - d.radioEventChan <- e + select { + case d.radioEventChan <- lora.RadioEvent{lora.RadioEventCrcError, uint16(st), nil}: + default: + } } } diff --git a/sx127x/sx127x.go b/sx127x/sx127x.go index 60d5b4481..bced11ae8 100644 --- a/sx127x/sx127x.go +++ b/sx127x/sx127x.go @@ -16,7 +16,7 @@ import ( // So we can keep track of the origin of interruption const ( RADIOEVENTCHAN_SIZE = 1 - SPI_BUFFER_SIZE = 256 + SPI_BUFFER_SIZE = 5 ) // Device wraps an SPI connection to a SX127x device. @@ -86,13 +86,10 @@ func (d *Device) ReadRegister(reg uint8) uint8 { //d.spiTxBuf = []byte{reg & 0x7f} d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, byte(reg&0x7f)) - //println("R1 : ", len(d.spiTxBuf)) d.spi.Tx(d.spiTxBuf, nil) // Read value - //d.spiRxBuf = []byte{reg & 0x00} d.spiRxBuf = d.spiRxBuf[:0] d.spiRxBuf = append(d.spiRxBuf, 0) - //println("R2 : ", len(d.spiTxBuf)) d.spi.Tx(nil, d.spiRxBuf) d.controller.SetNss(true) return d.spiRxBuf[0] @@ -529,27 +526,29 @@ func (d *Device) HandleInterrupt() { d.WriteRegister(SX127X_REG_IRQ_FLAGS, 0xFF) if (st & SX127X_IRQ_LORA_RXDONE_MASK) > 0 { - e := lora.RadioEvent{lora.RadioEventRxDone, uint16(st), nil} - d.radioEventChan <- e + select { + case d.radioEventChan <- lora.RadioEvent{lora.RadioEventRxDone, uint16(st), nil}: + default: + } } if (st & SX127X_IRQ_LORA_TXDONE_MASK) > 0 { - e := lora.RadioEvent{lora.RadioEventTxDone, uint16(st), nil} - d.radioEventChan <- e + select { + case d.radioEventChan <- lora.RadioEvent{lora.RadioEventTxDone, uint16(st), nil}: + default: + } } if (st & SX127X_IRQ_LORA_RXTOUT_MASK) > 0 { - e := lora.RadioEvent{lora.RadioEventTimeout, uint16(st), nil} select { - case d.radioEventChan <- e: + case d.radioEventChan <- lora.RadioEvent{lora.RadioEventTimeout, uint16(st), nil}: default: } } if (st & SX127X_IRQ_LORA_CRCERR_MASK) > 0 { - e := lora.RadioEvent{lora.RadioEventCrcError, uint16(st), nil} select { - case d.radioEventChan <- e: + case d.radioEventChan <- lora.RadioEvent{lora.RadioEventCrcError, uint16(st), nil}: default: } } From b0a971573b5d2a15b6728dc65311a38594bdab20 Mon Sep 17 00:00:00 2001 From: Brian Park Date: Fri, 3 Mar 2023 07:54:35 -0800 Subject: [PATCH 26/47] ds3231: Document incorrect leap year 2100 The current code interprets the 'century' flag as the year 2100. However the DS3231 hardware does not incorporate this flag in its leap year calculation, so will incorrectly consider the year 2100 as a leap year and increment from 2100-02-28 to 2100-02-29 instead of the correct 2100-03-01. The 'century' bit is not useful for anything as far as I can tell. But instead of removing the code that uses the 'century' bit, I thought it would be less intrusive to just document the current behavior. --- ds3231/ds3231.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/ds3231/ds3231.go b/ds3231/ds3231.go index aad4d19b1..e3b052a47 100644 --- a/ds3231/ds3231.go +++ b/ds3231/ds3231.go @@ -73,7 +73,17 @@ func (d *Device) SetRunning(isRunning bool) error { return nil } -// SetTime sets the date and time in the DS3231 +// SetTime sets the date and time in the DS3231. The DS3231 hardware supports +// only a 2-digit year field, so the current year will be stored as an offset +// from the year 2000, which supports the year 2000 until 2100. +// +// The DS3231 also supports a one-bit 'century' flag which is set by the chip +// when the year field rolls over from 99 to 00. The current code interprets +// this flag to be the year 2100, which appears to extend the range of years +// until the year 2200. However the DS3231 does not incorporate the 'century' +// flag in its leap year calculation, so it will incorrectly identify the year +// 2100 as a leap year, causing it to increment from 2100-02-28 to 2100-02-29 +// instead of 2100-03-01. func (d *Device) SetTime(dt time.Time) error { data := []byte{0} err := d.bus.ReadRegister(uint8(d.Address), REG_STATUS, data) @@ -92,6 +102,10 @@ func (d *Device) SetTime(dt time.Time) error { data[2] = uint8ToBCD(uint8(dt.Hour())) year := uint8(dt.Year() - 2000) + // This code interprets the centuryFlag to be the year 2100. Warning: The + // DS3231 does not incorporate the centuryFlag in its leap year calculation. + // It will increment from 2100-02-28 to 2100-02-29, which is incorrect because + // the year 2100 is not a leap year in the Gregorian calendar. centuryFlag := uint8(0) if year >= 100 { year -= 100 From ed04ebc5a7477d737bece2ce69d272ee09b2e369 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Fri, 10 Feb 2023 00:17:31 -0800 Subject: [PATCH 27/47] fresh netdev checkin preparing for PR --- espat/adapter.go | 20 - espat/espat.go | 278 +- espat/wifi.go | 16 +- examples/espat/espconsole/main.go | 145 - examples/espat/esphub/main.go | 131 - examples/espat/espstation/main.go | 110 - examples/espat/mqttclient/main.go | 144 - examples/espat/mqttsub/main.go | 165 - examples/espat/tcpclient/main.go | 113 - examples/net/espat/main.go | 101 + examples/net/http-get/main.go | 77 + examples/net/http-get/rtl8720dn.go | 39 + examples/net/http-get/wifinina.go | 43 + examples/net/http-head/main.go | 57 + examples/net/http-head/rtl8720dn.go | 39 + examples/net/http-head/wifinina.go | 43 + examples/net/http-post/main.go | 61 + examples/net/http-post/rtl8720dn.go | 39 + examples/net/http-post/wifinina.go | 43 + examples/net/http-postform/main.go | 62 + examples/net/http-postform/rtl8720dn.go | 39 + examples/net/http-postform/wifinina.go | 43 + examples/net/mqttclient/main.go | 77 + examples/net/mqttclient/rtl8720dn.go | 39 + examples/net/mqttclient/wifinina.go | 43 + examples/net/ntpclient/main.go | 104 + examples/net/ntpclient/rtl8720dn.go | 39 + examples/net/ntpclient/wifinina.go | 43 + examples/{wifi => net}/tcpclient/main.go | 66 +- examples/net/tcpclient/rtl8720dn.go | 39 + examples/net/tcpclient/wifinina.go | 43 + examples/net/tcpecho/main.go | 56 + examples/net/tcpecho/rtl8720dn.go | 39 + examples/net/tcpecho/wifinina.go | 43 + examples/net/tlsclient/main.go | 95 + examples/net/tlsclient/rtl8720dn.go | 39 + examples/net/tlsclient/wifinina.go | 43 + examples/net/webclient-tinyterm/main.go | 142 + examples/net/webclient/main.go | 106 + examples/net/webclient/rtl8720dn.go | 39 + examples/net/webclient/wifinina.go | 43 + examples/{wifinina => net}/webserver/main.go | 81 +- examples/net/webserver/rtl8720dn.go | 39 + examples/net/webserver/wifinina.go | 43 + examples/net/websocket/dial/main.go | 84 + examples/net/websocket/dial/rtl8720dn.go | 39 + examples/net/websocket/dial/wifinina.go | 43 + examples/net/websocket/handler/main.go | 52 + examples/net/websocket/handler/rtl8720dn.go | 39 + examples/net/websocket/handler/wifinina.go | 43 + examples/net/webstatic/images/tinygo-logo.png | Bin 0 -> 30898 bytes examples/net/webstatic/index.html | 28 + examples/net/webstatic/main.go | 38 + examples/net/webstatic/rtl8720dn.go | 39 + examples/net/webstatic/wifinina.go | 43 + examples/rtl8720dn/mqttclient/main.go | 131 - examples/rtl8720dn/mqttsub/main.go | 154 - examples/rtl8720dn/ntpclient/main.go | 143 - examples/rtl8720dn/tcpclient/main.go | 121 - examples/rtl8720dn/tlsclient/main.go | 137 - examples/rtl8720dn/udpstation/main.go | 91 - examples/rtl8720dn/version/main.go | 40 - examples/rtl8720dn/webclient-tinyterm/main.go | 162 - examples/rtl8720dn/webclient/main.go | 106 - examples/rtl8720dn/webserver/main.go | 193 - examples/wifi/tcpclient/espat.go | 27 - examples/wifi/tcpclient/rtl8720dn.go | 76 - examples/wifi/tcpclient/wifinina.go | 35 - examples/wifinina/connect/main.go | 144 - examples/wifinina/http-get/main.go | 158 - examples/wifinina/mqttclient/main.go | 145 - examples/wifinina/mqttsub/main.go | 158 - examples/wifinina/ntpclient/main.go | 177 - examples/wifinina/pins/main.go | 90 - examples/wifinina/tcpclient/main.go | 139 - examples/wifinina/tlsclient/main.go | 149 - examples/wifinina/udpstation/main.go | 101 - examples/wifinina/webclient/main.go | 150 - net/adapter.go | 44 - net/http/client.go | 253 - net/http/cookie.go | 435 -- net/http/cookiejar/jar.go | 504 -- net/http/cookiejar/punycode.go | 159 - net/http/driver.go | 15 - net/http/header.go | 259 - net/http/http.go | 162 - net/http/jar.go | 27 - net/http/request.go | 769 --- net/http/response.go | 120 - net/http/server.go | 581 -- net/http/status.go | 152 - net/http/tinygo.go | 308 - net/http/transefer.go | 34 - net/ipsocki.go | 26 - net/mqtt/mqtt.go | 396 -- net/mqtt/paho.go | 299 - net/mqtt/router.go | 178 - net/mqtt/token.go | 19 - net/net.go | 423 -- net/net_test.go | 14 - net/tls/tls.go | 42 - netdev/README.md | 184 + netdev/netdev.go | 133 + netdev/netdev_models.jpg | Bin 0 -> 65672 bytes netdev/socket.go | 141 + rtl8720dn/README.md | 7 +- rtl8720dn/adapter.go | 57 - rtl8720dn/debug.go | 16 + rtl8720dn/http.go | 236 - rtl8720dn/netdriver.go | 377 -- rtl8720dn/rpc.go | 4963 +++-------------- rtl8720dn/rpc_util.go | 16 +- rtl8720dn/rtl8720dn.go | 788 ++- rtl8720dn/util.go | 51 - rtl8720dn/wifi.go | 72 - wifinina/README.md | 2 +- wifinina/adapter.go | 31 - wifinina/debug.go | 17 + wifinina/http.go | 323 -- wifinina/pins.go | 12 +- wifinina/protocol/readme.md | 3 - wifinina/tcp.go | 270 - wifinina/wifinina.go | 2326 +++++--- 123 files changed, 6272 insertions(+), 15356 deletions(-) delete mode 100644 espat/adapter.go delete mode 100644 examples/espat/espconsole/main.go delete mode 100644 examples/espat/esphub/main.go delete mode 100644 examples/espat/espstation/main.go delete mode 100644 examples/espat/mqttclient/main.go delete mode 100644 examples/espat/mqttsub/main.go delete mode 100644 examples/espat/tcpclient/main.go create mode 100644 examples/net/espat/main.go create mode 100644 examples/net/http-get/main.go create mode 100644 examples/net/http-get/rtl8720dn.go create mode 100644 examples/net/http-get/wifinina.go create mode 100644 examples/net/http-head/main.go create mode 100644 examples/net/http-head/rtl8720dn.go create mode 100644 examples/net/http-head/wifinina.go create mode 100644 examples/net/http-post/main.go create mode 100644 examples/net/http-post/rtl8720dn.go create mode 100644 examples/net/http-post/wifinina.go create mode 100644 examples/net/http-postform/main.go create mode 100644 examples/net/http-postform/rtl8720dn.go create mode 100644 examples/net/http-postform/wifinina.go create mode 100644 examples/net/mqttclient/main.go create mode 100644 examples/net/mqttclient/rtl8720dn.go create mode 100644 examples/net/mqttclient/wifinina.go create mode 100644 examples/net/ntpclient/main.go create mode 100644 examples/net/ntpclient/rtl8720dn.go create mode 100644 examples/net/ntpclient/wifinina.go rename examples/{wifi => net}/tcpclient/main.go (51%) create mode 100644 examples/net/tcpclient/rtl8720dn.go create mode 100644 examples/net/tcpclient/wifinina.go create mode 100644 examples/net/tcpecho/main.go create mode 100644 examples/net/tcpecho/rtl8720dn.go create mode 100644 examples/net/tcpecho/wifinina.go create mode 100644 examples/net/tlsclient/main.go create mode 100644 examples/net/tlsclient/rtl8720dn.go create mode 100644 examples/net/tlsclient/wifinina.go create mode 100644 examples/net/webclient-tinyterm/main.go create mode 100644 examples/net/webclient/main.go create mode 100644 examples/net/webclient/rtl8720dn.go create mode 100644 examples/net/webclient/wifinina.go rename examples/{wifinina => net}/webserver/main.go (77%) create mode 100644 examples/net/webserver/rtl8720dn.go create mode 100644 examples/net/webserver/wifinina.go create mode 100644 examples/net/websocket/dial/main.go create mode 100644 examples/net/websocket/dial/rtl8720dn.go create mode 100644 examples/net/websocket/dial/wifinina.go create mode 100644 examples/net/websocket/handler/main.go create mode 100644 examples/net/websocket/handler/rtl8720dn.go create mode 100644 examples/net/websocket/handler/wifinina.go create mode 100644 examples/net/webstatic/images/tinygo-logo.png create mode 100644 examples/net/webstatic/index.html create mode 100644 examples/net/webstatic/main.go create mode 100644 examples/net/webstatic/rtl8720dn.go create mode 100644 examples/net/webstatic/wifinina.go delete mode 100644 examples/rtl8720dn/mqttclient/main.go delete mode 100644 examples/rtl8720dn/mqttsub/main.go delete mode 100644 examples/rtl8720dn/ntpclient/main.go delete mode 100644 examples/rtl8720dn/tcpclient/main.go delete mode 100644 examples/rtl8720dn/tlsclient/main.go delete mode 100644 examples/rtl8720dn/udpstation/main.go delete mode 100644 examples/rtl8720dn/version/main.go delete mode 100644 examples/rtl8720dn/webclient-tinyterm/main.go delete mode 100644 examples/rtl8720dn/webclient/main.go delete mode 100644 examples/rtl8720dn/webserver/main.go delete mode 100644 examples/wifi/tcpclient/espat.go delete mode 100644 examples/wifi/tcpclient/rtl8720dn.go delete mode 100644 examples/wifi/tcpclient/wifinina.go delete mode 100644 examples/wifinina/connect/main.go delete mode 100644 examples/wifinina/http-get/main.go delete mode 100644 examples/wifinina/mqttclient/main.go delete mode 100644 examples/wifinina/mqttsub/main.go delete mode 100644 examples/wifinina/ntpclient/main.go delete mode 100644 examples/wifinina/pins/main.go delete mode 100644 examples/wifinina/tcpclient/main.go delete mode 100644 examples/wifinina/tlsclient/main.go delete mode 100644 examples/wifinina/udpstation/main.go delete mode 100644 examples/wifinina/webclient/main.go delete mode 100644 net/adapter.go delete mode 100644 net/http/client.go delete mode 100644 net/http/cookie.go delete mode 100644 net/http/cookiejar/jar.go delete mode 100644 net/http/cookiejar/punycode.go delete mode 100644 net/http/driver.go delete mode 100644 net/http/header.go delete mode 100644 net/http/http.go delete mode 100644 net/http/jar.go delete mode 100644 net/http/request.go delete mode 100644 net/http/response.go delete mode 100644 net/http/server.go delete mode 100644 net/http/status.go delete mode 100644 net/http/tinygo.go delete mode 100644 net/http/transefer.go delete mode 100644 net/ipsocki.go delete mode 100644 net/mqtt/mqtt.go delete mode 100644 net/mqtt/paho.go delete mode 100644 net/mqtt/router.go delete mode 100644 net/mqtt/token.go delete mode 100644 net/net.go delete mode 100644 net/net_test.go delete mode 100644 net/tls/tls.go create mode 100644 netdev/README.md create mode 100644 netdev/netdev.go create mode 100644 netdev/netdev_models.jpg create mode 100644 netdev/socket.go delete mode 100644 rtl8720dn/adapter.go create mode 100644 rtl8720dn/debug.go delete mode 100644 rtl8720dn/http.go delete mode 100644 rtl8720dn/netdriver.go delete mode 100644 rtl8720dn/util.go delete mode 100644 rtl8720dn/wifi.go delete mode 100644 wifinina/adapter.go create mode 100644 wifinina/debug.go delete mode 100644 wifinina/http.go delete mode 100644 wifinina/protocol/readme.md delete mode 100644 wifinina/tcp.go diff --git a/espat/adapter.go b/espat/adapter.go deleted file mode 100644 index 3df9180e9..000000000 --- a/espat/adapter.go +++ /dev/null @@ -1,20 +0,0 @@ -package espat - -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 - } - - d.SetWifiMode(WifiModeClient) - return d.ConnectToAP(ssid, pass, int(timeout.Seconds())) -} - -func (d *Device) Disconnect() error { - return d.DisconnectFromAP() -} diff --git a/espat/espat.go b/espat/espat.go index cdb97cc80..c24731f76 100644 --- a/espat/espat.go +++ b/espat/espat.go @@ -15,41 +15,279 @@ // // AT command set: // https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf +// +// 02/2023 sfeldma@gmail.com Heavily modified to use netdev interface + package espat // import "tinygo.org/x/drivers/espat" import ( "errors" + "fmt" "strconv" + "machine" "strings" "time" - "tinygo.org/x/drivers" - "tinygo.org/x/drivers/net" + "tinygo.org/x/drivers/netdev" ) -// Device wraps UART connection to the ESP8266/ESP32. -type Device struct { - bus drivers.UART +var ( + version = "0.0.1" + driverName = "Espressif ESP8266/ESP32 AT Wifi network device driver (espat)" +) + +type Config struct { + // AP creditials + Ssid string + Passphrase string + + // UART config + Uart *machine.UART + Tx machine.Pin + Rx machine.Pin +} +type Device struct { + cfg *Config + uart *machine.UART // command responses that come back from the ESP8266/ESP32 response []byte - // data received from a TCP/UDP connection forwarded by the ESP8266/ESP32 socketdata []byte + socketInUse bool + socketProtocol netdev.Protocol + socketLaddr netdev.SockAddr +} + +func New(cfg *Config) *Device { + d := Device{ + cfg: cfg, + response: make([]byte, 512), + socketdata: make([]byte, 0, 1024), + } + return &d +} + +func (d *Device) NetConnect() error { + + fmt.Printf("\r\n") + fmt.Printf("%s\r\n\r\n", driverName) + fmt.Printf("Driver version : %s\r\n\r\n", version) + + if len(d.cfg.Ssid) == 0 { + return netdev.ErrMissingSSID + } + + d.uart = d.cfg.Uart + d.uart.Configure(machine.UARTConfig{TX: d.cfg.Tx, RX: d.cfg.Rx}) + + // Connect to ESP8266/ESP32 + fmt.Printf("Connecting to device...") + + for i := 0; i < 5; i++ { + if d.Connected() { + break + } + time.Sleep(1 * time.Second) + } + + if !d.Connected() { + fmt.Printf("FAILED\r\n") + return netdev.ErrConnectFailed + } + + fmt.Printf("CONNECTED\r\n") + + fmt.Printf("\r\n") + fmt.Printf("ESP8266/ESP32 firmware version : %s\r\n", string(d.Version())) + fmt.Printf("MAC address : %s\r\n", d.GetMacAddress()) + fmt.Printf("\r\n") + + // Connect to Wifi AP + fmt.Printf("Connecting to Wifi SSID '%s'...", d.cfg.Ssid) + + d.SetWifiMode(WifiModeClient) + err := d.ConnectToAP(d.cfg.Ssid, d.cfg.Passphrase, 10 /* secs */) + if err != nil { + fmt.Printf("FAILED\r\n") + return netdev.ErrConnectFailed + } + + fmt.Printf("CONNECTED\r\n") + + ip, err := d.GetClientIP() + if err != nil { + return netdev.ErrConnectFailed + } + + fmt.Printf("\r\n") + fmt.Printf("DHCP-assigned IP : %s\r\n", ip) + fmt.Printf("\r\n") + + return nil } -// ActiveDevice is the currently configured Device in use. There can only be one. -var ActiveDevice *Device +func (d *Device) NetDisconnect() { + d.DisconnectFromAP() + fmt.Printf("\r\nDisconnected from Wifi SSID '%s'\r\n\r\n", d.cfg.Ssid) +} + +func (d *Device) NetNotify(cb func(netdev.Event)) { + // Not supported +} + +func (d *Device) GetHostByName(name string) (netdev.IP, error) { + ip, err := d.GetDNS(name) + return netdev.ParseIP(ip), err +} + +func (d *Device) GetHardwareAddr() (netdev.HardwareAddr, error) { + return netdev.ParseHardwareAddr(d.GetMacAddress()), nil +} + +func (d *Device) GetIPAddr() (netdev.IP, error) { + ip, err := d.GetClientIP() + return netdev.ParseIP(ip), err +} + +func (d *Device) Socket(family netdev.AddressFamily, sockType netdev.SockType, + protocol netdev.Protocol) (netdev.Sockfd, error) { + + switch family { + case netdev.AF_INET: + default: + return -1, netdev.ErrFamilyNotSupported + } + + switch { + case protocol == netdev.IPPROTO_TCP && sockType == netdev.SOCK_STREAM: + case protocol == netdev.IPPROTO_TLS && sockType == netdev.SOCK_STREAM: + case protocol == netdev.IPPROTO_UDP && sockType == netdev.SOCK_DGRAM: + default: + return -1, netdev.ErrProtocolNotSupported + } + + // Only supporting single connection mode, so only one socket at a time + if d.socketInUse { + return -1, netdev.ErrNoMoreSockets + } + d.socketInUse = true + d.socketProtocol = protocol + + return netdev.Sockfd(0), nil +} + +func (d *Device) Bind(sockfd netdev.Sockfd, addr netdev.SockAddr) error { + d.socketLaddr = addr + return nil +} + +func (d *Device) Connect(sockfd netdev.Sockfd, servaddr netdev.SockAddr) error { + var err error + var addr = servaddr.Ip().String() + var port = fmt.Sprintf("%d", servaddr.Port()) + var lport = fmt.Sprintf("%d", d.socketLaddr.Port()) + + switch d.socketProtocol { + case netdev.IPPROTO_TCP: + err = d.ConnectTCPSocket(addr, port) + case netdev.IPPROTO_UDP: + err = d.ConnectUDPSocket(addr, port, lport) + case netdev.IPPROTO_TLS: + err = d.ConnectSSLSocket(addr, port) + } + + return err +} + +func (d *Device) Listen(sockfd netdev.Sockfd, backlog int) error { + switch d.socketProtocol { + case netdev.IPPROTO_UDP: + default: + return netdev.ErrProtocolNotSupported + } + return nil +} + +func (d *Device) Accept(sockfd netdev.Sockfd, peer netdev.SockAddr) (netdev.Sockfd, error) { + return -1, netdev.ErrNotSupported +} + +func (d *Device) sendChunk(sockfd netdev.Sockfd, buf []byte, timeout time.Duration) (int, error) { + err := d.StartSocketSend(len(buf)) + if err != nil { + return -1, err + } + n, err := d.Write(buf) + if err != nil { + return -1, err + } + _, err = d.Response(1000) + if err != nil { + return -1, err + } + return n, err +} + +func (d *Device) Send(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, + timeout time.Duration) (int, error) { + + // 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 := d.sendChunk(sockfd, buf[i:end], timeout) + if err != nil { + return -1, err + } + } + + return len(buf), nil +} + +func (d *Device) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, + timeout time.Duration) (int, error) { + + var length = len(buf) + var expire = time.Now().Add(timeout) + + // Limit length read size to chunk large read requests + if length > 1436 { + length = 1436 + } + + for { + // Check if we've timed out + if timeout > 0 { + if time.Now().Before(expire) { + return -1, netdev.ErrRecvTimeout + } + } + + n, err := d.ReadSocket(buf[:length]) + if err != nil { + return -1, err + } + if n == 0 { + time.Sleep(100 * time.Millisecond) + continue + } + + return n, nil + } +} -// New returns a new espat driver. Pass in a fully configured UART bus. -func New(b drivers.UART) *Device { - return &Device{bus: b, response: make([]byte, 512), socketdata: make([]byte, 0, 1024)} +func (d *Device) Close(sockfd netdev.Sockfd) error { + return d.DisconnectSocket() } -// Configure sets up the device for communication. -func (d Device) Configure() { - ActiveDevice = &d - net.ActiveDevice = ActiveDevice +func (d *Device) SetSockOpt(sockfd netdev.Sockfd, level netdev.SockOptLevel, + opt netdev.SockOpt, value any) error { + return netdev.ErrNotSupported } // Connected checks if there is communication with the ESP8266/ESP32. @@ -66,12 +304,12 @@ func (d *Device) Connected() bool { // Write raw bytes to the UART. func (d *Device) Write(b []byte) (n int, err error) { - return d.bus.Write(b) + return d.uart.Write(b) } // Read raw bytes from the UART. func (d *Device) Read(b []byte) (n int, err error) { - return d.bus.Read(b) + return d.uart.Read(b) } // how long in milliseconds to pause after sending AT commands @@ -157,11 +395,11 @@ func (d *Device) Response(timeout int) ([]byte, error) { retries := timeout / pause for { - size = d.bus.Buffered() + size = d.uart.Buffered() if size > 0 { end += size - d.bus.Read(d.response[start:end]) + d.uart.Read(d.response[start:end]) // if "+IPD" then read socket data if strings.Contains(string(d.response[:end]), "+IPD") { @@ -217,5 +455,5 @@ func (d *Device) parseIPD(end int) error { // IsSocketDataAvailable returns of there is socket data available func (d *Device) IsSocketDataAvailable() bool { - return len(d.socketdata) > 0 || d.bus.Buffered() > 0 + return len(d.socketdata) > 0 || d.uart.Buffered() > 0 } diff --git a/espat/wifi.go b/espat/wifi.go index ea37a8e8f..ff752234f 100644 --- a/espat/wifi.go +++ b/espat/wifi.go @@ -44,10 +44,7 @@ func (d *Device) ConnectToAP(ssid, pwd string, ws int) error { d.Set(ConnectAP, val) _, err := d.Response(ws * 1000) - if err != nil { - return err - } - return nil + return err } // DisconnectFromAP disconnects the ESP8266/ESP32 from the current access point. @@ -57,6 +54,17 @@ func (d *Device) DisconnectFromAP() error { return err } +// GetClientIP returns the ESP8266/ESP32 current station MAC addess when +// connected to an Access Point. +func (d *Device) GetMacAddress() string { + d.Query(SetStationMACAddress) + r, err := d.Response(1000) + if err != nil { + return "unknown" + } + return string(r) +} + // GetClientIP returns the ESP8266/ESP32 current client IP addess when connected to an Access Point. func (d *Device) GetClientIP() (string, error) { d.Query(SetStationIP) diff --git a/examples/espat/espconsole/main.go b/examples/espat/espconsole/main.go deleted file mode 100644 index ad550062c..000000000 --- a/examples/espat/espconsole/main.go +++ /dev/null @@ -1,145 +0,0 @@ -// This is a console to a ESP8266/ESP32 running on the device UART1. -// Allows you to type AT commands from your computer via the microcontroller. -// -// In other words: -// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> INTERNET -// -// More information on the Espressif AT command set at: -// https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf -package main - -import ( - "machine" - "time" - - "tinygo.org/x/drivers/espat" -) - -// change actAsAP to true to act as an access point instead of connecting to one. -const actAsAP = false - -var ( - // access point info - ssid string - pass string -) - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - uart = machine.UART1 - tx = machine.PA22 - rx = machine.PA23 - - console = machine.Serial - - adaptor *espat.Device -) - -func main() { - uart.Configure(machine.UARTConfig{TX: tx, RX: rx}) - - // Init esp8266 - adaptor = espat.New(uart) - adaptor.Configure() - - // first check if connected - if connectToESP() { - println("Connected to wifi adaptor.") - adaptor.Echo(false) - - connectToAP() - } else { - println("") - failMessage("Unable to connect to wifi adaptor.") - return - } - - println("Type an AT command then press enter:") - prompt() - - input := make([]byte, 64) - i := 0 - for { - if console.Buffered() > 0 { - data, _ := console.ReadByte() - - switch data { - case 13: - // return key - console.Write([]byte("\r\n")) - - // send command to ESP8266 - input[i] = byte('\r') - input[i+1] = byte('\n') - adaptor.Write(input[:i+2]) - - // display response - r, _ := adaptor.Response(500) - console.Write(r) - - // prompt - prompt() - - i = 0 - continue - default: - // just echo the character - console.WriteByte(data) - input[i] = data - i++ - } - } - time.Sleep(10 * time.Millisecond) - } -} - -func prompt() { - print("ESPAT>") -} - -// connect to ESP8266/ESP32 -func connectToESP() bool { - for i := 0; i < 5; i++ { - println("Connecting to wifi adaptor...") - if adaptor.Connected() { - return true - } - time.Sleep(1 * time.Second) - } - return false -} - -// connect to access point -func connectToAP() { - println("Connecting to wifi network '" + ssid + "'") - - if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil { - failMessage(err.Error()) - } - - println("Connected.") - ip, err := adaptor.GetClientIP() - if err != nil { - failMessage(err.Error()) - } - - println(ip) -} - -// provide access point -func provideAP() { - println("Starting wifi network as access point '" + ssid + "'...") - adaptor.SetWifiMode(espat.WifiModeAP) - adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK) - println("Ready.") - ip, _ := adaptor.GetAPIP() - println(ip) -} - -func failMessage(msg string) { - for { - println(msg) - time.Sleep(1 * time.Second) - } -} diff --git a/examples/espat/esphub/main.go b/examples/espat/esphub/main.go deleted file mode 100644 index e253955ea..000000000 --- a/examples/espat/esphub/main.go +++ /dev/null @@ -1,131 +0,0 @@ -// This is a sensor hub that uses a ESP8266/ESP32 running on the device UART1. -// It creates a UDP "server" you can use to get info to/from your computer via the microcontroller. -// -// In other words: -// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> INTERNET -package main - -import ( - "machine" - "time" - - "tinygo.org/x/drivers/espat" - "tinygo.org/x/drivers/net" -) - -// change actAsAP to true to act as an access point instead of connecting to one. -const actAsAP = false - -var ( - // access point info - ssid string - pass string -) - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - uart = machine.UART1 - tx = machine.PA22 - rx = machine.PA23 - - adaptor *espat.Device -) - -func main() { - uart.Configure(machine.UARTConfig{TX: tx, RX: rx}) - - // Init esp8266 - adaptor = espat.New(uart) - adaptor.Configure() - - readyled := machine.LED - readyled.Configure(machine.PinConfig{Mode: machine.PinOutput}) - readyled.High() - - // first check if connected - if connectToESP() { - println("Connected to wifi adaptor.") - adaptor.Echo(false) - - connectToAP() - } else { - println("") - failMessage("Unable to connect to wifi adaptor.") - return - } - - // now make UDP connection - laddr := &net.UDPAddr{Port: 2222} - println("Loading UDP listener...") - conn, _ := net.ListenUDP("UDP", laddr) - - println("Waiting for data...") - data := make([]byte, 50) - blink := true - for { - n, _ := conn.Read(data) - if n > 0 { - println(string(data[:n])) - conn.Write([]byte("hello back\r\n")) - } - blink = !blink - if blink { - readyled.High() - } else { - readyled.Low() - } - time.Sleep(500 * time.Millisecond) - } - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting UDP...") - conn.Close() - println("Done.") -} - -// connect to ESP8266/ESP32 -func connectToESP() bool { - for i := 0; i < 5; i++ { - println("Connecting to wifi adaptor...") - if adaptor.Connected() { - return true - } - time.Sleep(1 * time.Second) - } - return false -} - -// connect to access point -func connectToAP() { - println("Connecting to wifi network '" + ssid + "'") - - if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil { - failMessage(err.Error()) - } - - println("Connected.") - ip, err := adaptor.GetClientIP() - if err != nil { - failMessage(err.Error()) - } - - println(ip) -} - -// provide access point -func provideAP() { - println("Starting wifi network as access point '" + ssid + "'...") - adaptor.SetWifiMode(espat.WifiModeAP) - adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK) - println("Ready.") - ip, _ := adaptor.GetAPIP() - println(ip) -} - -func failMessage(msg string) { - for { - println(msg) - time.Sleep(1 * time.Second) - } -} diff --git a/examples/espat/espstation/main.go b/examples/espat/espstation/main.go deleted file mode 100644 index 5ae2e3d69..000000000 --- a/examples/espat/espstation/main.go +++ /dev/null @@ -1,110 +0,0 @@ -// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1. -// It creates a UDP connection you can use to get info to/from your computer via the microcontroller. -// -// In other words: -// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 -package main - -import ( - "machine" - "time" - - "tinygo.org/x/drivers/espat" - "tinygo.org/x/drivers/net" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the listener aka "hub". Replace with your own info. -const hubIP = "0.0.0.0" - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - uart = machine.UART1 - tx = machine.PA22 - rx = machine.PA23 - - adaptor *espat.Device -) - -func main() { - uart.Configure(machine.UARTConfig{TX: tx, RX: rx}) - - // Init esp8266/esp32 - adaptor = espat.New(uart) - adaptor.Configure() - - // first check if connected - if connectToESP() { - println("Connected to wifi adaptor.") - adaptor.Echo(false) - - connectToAP() - } else { - println("") - failMessage("Unable to connect to wifi adaptor.") - return - } - - // now make UDP connection - ip := net.ParseIP(hubIP) - raddr := &net.UDPAddr{IP: ip, Port: 2222} - laddr := &net.UDPAddr{Port: 2222} - - println("Dialing UDP connection...") - conn, _ := net.DialUDP("udp", laddr, raddr) - - for { - // send data - println("Sending data...") - conn.Write([]byte("hello\r\n")) - - time.Sleep(1000 * time.Millisecond) - } - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting UDP...") - conn.Close() - println("Done.") -} - -// connect to ESP8266/ESP32 -func connectToESP() bool { - for i := 0; i < 5; i++ { - println("Connecting to wifi adaptor...") - if adaptor.Connected() { - return true - } - time.Sleep(1 * time.Second) - } - return false -} - -// connect to access point -func connectToAP() { - println("Connecting to wifi network '" + ssid + "'") - - if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil { - failMessage(err.Error()) - } - - println("Connected.") - ip, err := adaptor.GetClientIP() - if err != nil { - failMessage(err.Error()) - } - - println(ip) -} - -func failMessage(msg string) { - for { - println(msg) - time.Sleep(1 * time.Second) - } -} diff --git a/examples/espat/mqttclient/main.go b/examples/espat/mqttclient/main.go deleted file mode 100644 index aaa88554e..000000000 --- a/examples/espat/mqttclient/main.go +++ /dev/null @@ -1,144 +0,0 @@ -// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1. -// It creates an MQTT connection that publishes a message every second -// to an MQTT broker. -// -// In other words: -// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> Internet <--> MQTT broker. -// -// You must install the Paho MQTT package to build this program: -// -// go get -u github.com/eclipse/paho.mqtt.golang -package main - -import ( - "machine" - "math/rand" - "time" - - "tinygo.org/x/drivers/espat" - "tinygo.org/x/drivers/net/mqtt" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the MQTT broker to use. Replace with your own info. -const server = "tcp://test.mosquitto.org:1883" - -//const server = "ssl://test.mosquitto.org:8883" - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - uart = machine.UART2 - tx = machine.PA22 - rx = machine.PA23 - - console = machine.Serial - - adaptor *espat.Device - topic = "tinygo" -) - -func main() { - time.Sleep(3000 * time.Millisecond) - - uart.Configure(machine.UARTConfig{TX: tx, RX: rx}) - rand.Seed(time.Now().UnixNano()) - - // Init esp8266/esp32 - adaptor = espat.New(uart) - adaptor.Configure() - - // first check if connected - if connectToESP() { - println("Connected to wifi adaptor.") - adaptor.Echo(false) - - connectToAP() - } else { - println("") - failMessage("Unable to connect to wifi adaptor.") - return - } - - opts := mqtt.NewClientOptions() - opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10)) - - println("Connecting to MQTT broker at", server) - cl := mqtt.NewClient(opts) - if token := cl.Connect(); token.Wait() && token.Error() != nil { - failMessage(token.Error().Error()) - } - - for { - println("Publishing MQTT message...") - data := []byte("{\"e\":[{ \"n\":\"hello\", \"v\":101 }]}") - token := cl.Publish(topic, 0, false, data) - token.Wait() - if token.Error() != nil { - println(token.Error().Error()) - } - - time.Sleep(1000 * time.Millisecond) - } - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting MQTT...") - cl.Disconnect(100) - - println("Done.") -} - -// connect to ESP8266/ESP32 -func connectToESP() bool { - for i := 0; i < 5; i++ { - println("Connecting to wifi adaptor...") - if adaptor.Connected() { - return true - } - time.Sleep(1 * time.Second) - } - return false -} - -// connect to access point -func connectToAP() { - println("Connecting to wifi network '" + ssid + "'") - - if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil { - failMessage(err.Error()) - } - - println("Connected.") - ip, err := adaptor.GetClientIP() - if err != nil { - failMessage(err.Error()) - } - - println(ip) -} - -// Returns an int >= min, < max -func randomInt(min, max int) int { - return min + rand.Intn(max-min) -} - -// Generate a random string of A-Z chars with len = l -func randomString(len int) string { - bytes := make([]byte, len) - for i := 0; i < len; i++ { - bytes[i] = byte(randomInt(65, 90)) - } - return string(bytes) -} - -func failMessage(msg string) { - for { - println(msg) - time.Sleep(1 * time.Second) - } -} diff --git a/examples/espat/mqttsub/main.go b/examples/espat/mqttsub/main.go deleted file mode 100644 index 65753852e..000000000 --- a/examples/espat/mqttsub/main.go +++ /dev/null @@ -1,165 +0,0 @@ -// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1. -// It creates an MQTT connection that publishes a message every second -// to an MQTT broker. -// -// In other words: -// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> Internet <--> MQTT broker. -// -// You must also install the Paho MQTT package to build this program: -// -// go get -u github.com/eclipse/paho.mqtt.golang -package main - -import ( - "fmt" - "machine" - "math/rand" - "time" - - "tinygo.org/x/drivers/espat" - "tinygo.org/x/drivers/net/mqtt" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the MQTT broker to use. Replace with your own info. -//const server = "tcp://test.mosquitto.org:1883" - -const server = "ssl://test.mosquitto.org:8883" - -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - // these are defaults for the Arduino Nano33 IoT. - uart = machine.UART1 - tx = machine.PA22 - rx = machine.PA23 - - console = machine.Serial - - adaptor *espat.Device - cl mqtt.Client - topicTx = "tinygo/tx" - topicRx = "tinygo/rx" -) - -func subHandler(client mqtt.Client, msg mqtt.Message) { - fmt.Printf("[%s] ", msg.Topic()) - fmt.Printf("%s\r\n", msg.Payload()) -} - -func main() { - time.Sleep(3000 * time.Millisecond) - - uart.Configure(machine.UARTConfig{TX: tx, RX: rx}) - rand.Seed(time.Now().UnixNano()) - - // Init esp8266/esp32 - adaptor = espat.New(uart) - adaptor.Configure() - - // first check if connected - if connectToESP() { - println("Connected to wifi adaptor.") - adaptor.Echo(false) - - connectToAP() - } else { - println("") - failMessage("Unable to connect to wifi adaptor.") - return - } - - opts := mqtt.NewClientOptions() - opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10)) - - println("Connecting to MQTT broker at", server) - cl = mqtt.NewClient(opts) - if token := cl.Connect(); token.Wait() && token.Error() != nil { - failMessage(token.Error().Error()) - } - - // subscribe - token := cl.Subscribe(topicRx, 0, subHandler) - token.Wait() - if token.Error() != nil { - failMessage(token.Error().Error()) - } - - go publishing() - - select {} - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting MQTT...") - cl.Disconnect(100) - - println("Done.") -} - -func publishing() { - for { - println("Publishing MQTT message...") - data := []byte("{\"e\":[{ \"n\":\"hello\", \"v\":101 }]}") - token := cl.Publish(topicTx, 0, false, data) - token.Wait() - if token.Error() != nil { - println(token.Error().Error()) - } - - time.Sleep(1000 * time.Millisecond) - } -} - -// connect to ESP8266/ESP32 -func connectToESP() bool { - for i := 0; i < 5; i++ { - println("Connecting to wifi adaptor...") - if adaptor.Connected() { - return true - } - time.Sleep(1 * time.Second) - } - return false -} - -// connect to access point -func connectToAP() { - println("Connecting to wifi network '" + ssid + "'") - - if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil { - failMessage(err.Error()) - } - - println("Connected.") - ip, err := adaptor.GetClientIP() - if err != nil { - failMessage(err.Error()) - } - - println(ip) -} - -// Returns an int >= min, < max -func randomInt(min, max int) int { - return min + rand.Intn(max-min) -} - -// Generate a random string of A-Z chars with len = l -func randomString(len int) string { - bytes := make([]byte, len) - for i := 0; i < len; i++ { - bytes[i] = byte(randomInt(65, 90)) - } - return string(bytes) -} - -func failMessage(msg string) { - for { - println(msg) - time.Sleep(1 * time.Second) - } -} diff --git a/examples/espat/tcpclient/main.go b/examples/espat/tcpclient/main.go deleted file mode 100644 index db1bcb8cc..000000000 --- a/examples/espat/tcpclient/main.go +++ /dev/null @@ -1,113 +0,0 @@ -// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1. -// It creates a UDP connection you can use to get info to/from your computer via the microcontroller. -// -// In other words: -// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 -package main - -import ( - "machine" - "time" - - "tinygo.org/x/drivers/espat" - "tinygo.org/x/drivers/net" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the server aka "hub". Replace with your own info. -const serverIP = "0.0.0.0" - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - uart = machine.UART1 - tx = machine.PA22 - rx = machine.PA23 - - adaptor *espat.Device -) - -func main() { - uart.Configure(machine.UARTConfig{TX: tx, RX: rx}) - - // Init esp8266/esp32 - adaptor = espat.New(uart) - adaptor.Configure() - - // first check if connected - if connectToESP() { - println("Connected to wifi adaptor.") - adaptor.Echo(false) - - connectToAP() - } else { - println("") - failMessage("Unable to connect to wifi adaptor.") - return - } - - // now make TCP connection - ip := net.ParseIP(serverIP) - raddr := &net.TCPAddr{IP: ip, Port: 8080} - laddr := &net.TCPAddr{Port: 8080} - - println("Dialing TCP connection...") - conn, err := net.DialTCP("tcp", laddr, raddr) - if err != nil { - failMessage(err.Error()) - } - - for { - // send data - println("Sending data...") - conn.Write([]byte("hello\r\n")) - - time.Sleep(1000 * time.Millisecond) - } - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting TCP...") - conn.Close() - println("Done.") -} - -// connect to ESP8266/ESP32 -func connectToESP() bool { - for i := 0; i < 5; i++ { - println("Connecting to wifi adaptor...") - if adaptor.Connected() { - return true - } - time.Sleep(1 * time.Second) - } - return false -} - -// connect to access point -func connectToAP() { - println("Connecting to wifi network '" + ssid + "'") - - if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil { - failMessage(err.Error()) - } - - println("Connected.") - ip, err := adaptor.GetClientIP() - if err != nil { - failMessage(err.Error()) - } - - println(ip) -} - -func failMessage(msg string) { - for { - println(msg) - time.Sleep(1 * time.Second) - } -} diff --git a/examples/net/espat/main.go b/examples/net/espat/main.go new file mode 100644 index 000000000..a1891fe0e --- /dev/null +++ b/examples/net/espat/main.go @@ -0,0 +1,101 @@ +package main + +import ( + "bytes" + "fmt" + "log" + "machine" + "net" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/espat" +) + +var ( + ssid string + pass string + addr string = "10.0.0.100:8080" +) + +var ( + netcfg = espat.Config{ + Ssid: ssid, + Passphrase: pass, + Uart: machine.UART2, + Tx: machine.TX1, + Rx: machine.RX0, + } + + dev = espat.New(&netcfg) +) + +var buf = &bytes.Buffer{} + +func main() { + + waitSerial() + + netdev.Use(dev) + if err := dev.NetConnect(); err != nil { + log.Fatal(err) + } + + for { + sendBatch() + time.Sleep(500 * time.Millisecond) + } +} + +func sendBatch() { + + // make TCP connection + message("---------------\r\nDialing TCP connection") + conn, err := net.Dial("tcp", addr) + for ; err != nil; conn, err = net.Dial("tcp", addr) { + message(err.Error()) + time.Sleep(5 * time.Second) + } + + n := 0 + w := 0 + start := time.Now() + + // send data + message("Sending data") + + for i := 0; i < 1000; i++ { + buf.Reset() + fmt.Fprint(buf, + "\r---------------------------- i == ", i, " ----------------------------"+ + "\r---------------------------- i == ", i, " ----------------------------") + if w, err = conn.Write(buf.Bytes()); err != nil { + println("error:", err.Error(), "\r") + break + } + n += w + } + + buf.Reset() + ms := time.Now().Sub(start).Milliseconds() + fmt.Fprint(buf, "\nWrote ", n, " bytes in ", ms, " ms\r\n") + message(buf.String()) + + if _, err := conn.Write(buf.Bytes()); err != nil { + println("error:", err.Error(), "\r") + } + + println("Disconnecting TCP...") + conn.Close() +} + +func message(msg string) { + println(msg, "\r") +} + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} diff --git a/examples/net/http-get/main.go b/examples/net/http-get/main.go new file mode 100644 index 000000000..9028da578 --- /dev/null +++ b/examples/net/http-get/main.go @@ -0,0 +1,77 @@ +// This example gets an URL using http.Get(). URL scheme can be http or https. +// +// Note: It may be necessary to increase the stack size when using "net/http". +// Use the -stack-size=4KB command line option. +// +// Some targets (Arduino Nano33 IoT) don't have enough SRAM to run http.Get(). +// Use the following for those targets: +// +// examples/net/webclient (for HTTP) +// examples/net/tlsclient (for HTTPS) + +package main + +import ( + "fmt" + "io" + "log" + "machine" + "net/http" + "net/url" + "strings" + "time" +) + +var ( + ssid string + pass string +) + +func main() { + + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + name := "John Doe" + occupation := "gardener" + + params := "name=" + url.QueryEscape(name) + "&" + + "occupation=" + url.QueryEscape(occupation) + + path := fmt.Sprintf("https://httpbin.org/get?%s", params) + + cnt := 0 + for { + fmt.Printf("Getting %s\r\n\r\n", path) + resp, err := http.Get(path) + if err != nil { + fmt.Printf("%s\r\n", err.Error()) + time.Sleep(10 * time.Second) + continue + } + + fmt.Printf("%s %s\r\n", resp.Proto, resp.Status) + for k, v := range resp.Header { + fmt.Printf("%s: %s\r\n", k, strings.Join(v, " ")) + } + fmt.Printf("\r\n") + + body, err := io.ReadAll(resp.Body) + println(string(body)) + resp.Body.Close() + + cnt++ + fmt.Printf("-------- %d --------\r\n", cnt) + time.Sleep(10 * time.Second) + } +} + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} diff --git a/examples/net/http-get/rtl8720dn.go b/examples/net/http-get/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/http-get/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/http-get/wifinina.go b/examples/net/http-get/wifinina.go new file mode 100644 index 000000000..658492e71 --- /dev/null +++ b/examples/net/http-get/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/http-head/main.go b/examples/net/http-head/main.go new file mode 100644 index 000000000..641f7c227 --- /dev/null +++ b/examples/net/http-head/main.go @@ -0,0 +1,57 @@ +// This example gets an URL using http.Head(). URL scheme can be http or https. +// +// Note: It may be necessary to increase the stack size when using "net/http". +// Use the -stack-size=4KB command line option. +// +// Some targets (Arduino Nano33 IoT) don't have enough SRAM to run http.Head(). +// Use the following for those targets: +// +// examples/net/webclient (for HTTP) +// examples/net/tlsclient (for HTTPS) + +package main + +import ( + "bytes" + "fmt" + "log" + "machine" + "net/http" + "time" +) + +var ( + ssid string + pass string + url string = "https://httpbin.org" +) + +func main() { + + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + resp, err := http.Head(url) + if err != nil { + log.Fatal(err) + } + defer resp.Body.Close() + + var buf bytes.Buffer + if err := resp.Write(&buf); err != nil { + log.Fatal(err) + } + fmt.Println(string(buf.Bytes())) + + NetDisconnect() +} + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} diff --git a/examples/net/http-head/rtl8720dn.go b/examples/net/http-head/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/http-head/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/http-head/wifinina.go b/examples/net/http-head/wifinina.go new file mode 100644 index 000000000..658492e71 --- /dev/null +++ b/examples/net/http-head/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/http-post/main.go b/examples/net/http-post/main.go new file mode 100644 index 000000000..79d2134d7 --- /dev/null +++ b/examples/net/http-post/main.go @@ -0,0 +1,61 @@ +// This example posts an URL using http.Post(). URL scheme can be http or https. +// +// Note: It may be necessary to increase the stack size when using "net/http". +// Use the -stack-size=4KB command line option. +// +// Some targets (Arduino Nano33 IoT) don't have enough SRAM to run http.Post(). +// Use the following for those targets: +// +// examples/net/webclient (for HTTP) +// examples/net/tlsclient (for HTTPS) + +package main + +import ( + "bytes" + "fmt" + "io" + "log" + "machine" + "net/http" + "time" +) + +var ( + ssid string + pass string +) + +func main() { + + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + path := "https://httpbin.org/post" + data := []byte("{\"name\":\"John Doe\",\"occupation\":\"gardener\"}") + + resp, err := http.Post(path, "application/json", bytes.NewBuffer(data)) + if err != nil { + log.Fatal(err) + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Fatal(err) + } + + fmt.Println(string(body)) + + NetDisconnect() +} + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} diff --git a/examples/net/http-post/rtl8720dn.go b/examples/net/http-post/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/http-post/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/http-post/wifinina.go b/examples/net/http-post/wifinina.go new file mode 100644 index 000000000..658492e71 --- /dev/null +++ b/examples/net/http-post/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/http-postform/main.go b/examples/net/http-postform/main.go new file mode 100644 index 000000000..a88d560dd --- /dev/null +++ b/examples/net/http-postform/main.go @@ -0,0 +1,62 @@ +// This example posts an URL using http.PostForm(). URL scheme can be http or https. +// +// Note: It may be necessary to increase the stack size when using "net/http". +// Use the -stack-size=4KB command line option. +// +// Some targets (Arduino Nano33 IoT) don't have enough SRAM to run +// http.PostForm(). Use the following for those targets: +// +// examples/net/webclient (for HTTP) +// examples/net/tlsclient (for HTTPS) + +package main + +import ( + "fmt" + "io" + "log" + "machine" + "net/http" + "net/url" + "time" +) + +var ( + ssid string + pass string +) + +func main() { + + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + path := "https://httpbin.org/post" + data := url.Values{ + "name": {"John Doe"}, + "occupation": {"gardener"}, + } + + resp, err := http.PostForm(path, data) + if err != nil { + log.Fatal(err) + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Fatal(err) + } + + fmt.Println(string(body)) +} + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} diff --git a/examples/net/http-postform/rtl8720dn.go b/examples/net/http-postform/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/http-postform/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/http-postform/wifinina.go b/examples/net/http-postform/wifinina.go new file mode 100644 index 000000000..658492e71 --- /dev/null +++ b/examples/net/http-postform/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/mqttclient/main.go b/examples/net/mqttclient/main.go new file mode 100644 index 000000000..cd7a0059f --- /dev/null +++ b/examples/net/mqttclient/main.go @@ -0,0 +1,77 @@ +// This example is a MQTT client. It sends machine.ReadTemparature() readings +// to the broker every second for 10 seconds. +// +// Note: It may be necessary to increase the stack size when using +// paho.mqtt.golang. Use the -stack-size=4KB command line option. + +package main + +import ( + "fmt" + "log" + "machine" + "time" + + mqtt "github.com/eclipse/paho.mqtt.golang" +) + +var ( + ssid string + pass string + broker string = "tcp://test.mosquitto.org:1883" +) + +var messagePubHandler mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) { + fmt.Printf("Message %s received on topic %s\n", msg.Payload(), msg.Topic()) +} + +var connectHandler mqtt.OnConnectHandler = func(client mqtt.Client) { + fmt.Println("Connected") +} + +var connectionLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, err error) { + fmt.Printf("Connection Lost: %s\n", err.Error()) +} + +func main() { + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + options := mqtt.NewClientOptions() + options.AddBroker(broker) + options.SetClientID("tinygo_mqtt_example") + options.SetDefaultPublishHandler(messagePubHandler) + options.OnConnect = connectHandler + options.OnConnectionLost = connectionLostHandler + + client := mqtt.NewClient(options) + token := client.Connect() + if token.Wait() && token.Error() != nil { + panic(token.Error()) + } + + topic := "cpu/freq" + token = client.Subscribe(topic, 1, nil) + token.Wait() + fmt.Printf("Subscribed to topic %s\n", topic) + + for i := 0; i < 10; i++ { + freq := float32(machine.CPUFrequency()) / 1000000 + payload := fmt.Sprintf("%.02fMhz", freq) + token = client.Publish(topic, 0, false, payload) + token.Wait() + time.Sleep(time.Second) + } + + client.Disconnect(100) +} + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} diff --git a/examples/net/mqttclient/rtl8720dn.go b/examples/net/mqttclient/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/mqttclient/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/mqttclient/wifinina.go b/examples/net/mqttclient/wifinina.go new file mode 100644 index 000000000..658492e71 --- /dev/null +++ b/examples/net/mqttclient/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/ntpclient/main.go b/examples/net/ntpclient/main.go new file mode 100644 index 000000000..1d74feb00 --- /dev/null +++ b/examples/net/ntpclient/main.go @@ -0,0 +1,104 @@ +// This is an example of an NTP client. +// +// 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. + +package main + +import ( + "fmt" + "io" + "log" + "machine" + "net" + "runtime" + "time" +) + +var ( + ssid string + pass string + // IP address of the server aka "hub". Replace with your own info. + ntpHost string = "0.pool.ntp.org:123" +) + +const NTP_PACKET_SIZE = 48 + +var response = make([]byte, NTP_PACKET_SIZE) + +func main() { + + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + conn, err := net.Dial("udp", ntpHost) + if err != nil { + log.Fatal(err) + } + + println("Requesting NTP time...") + + t, err := getCurrentTime(conn) + if err != nil { + log.Fatal(fmt.Sprintf("Error getting current time: %v", err)) + } else { + message("NTP time: %v", t) + } + + conn.Close() + NetDisconnect() + + runtime.AdjustTimeOffset(-1 * int64(time.Since(t))) + + for { + message("Current time: %v", time.Now()) + time.Sleep(time.Minute) + } +} + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} + +func getCurrentTime(conn net.Conn) (time.Time, error) { + if err := sendNTPpacket(conn); err != nil { + return time.Time{}, err + } + + n, err := conn.Read(response) + if err != nil && err != io.EOF { + return time.Time{}, err + } + if n != NTP_PACKET_SIZE { + return time.Time{}, fmt.Errorf("expected NTP packet size of %d: %d", NTP_PACKET_SIZE, n) + } + + return parseNTPpacket(response), nil +} + +func sendNTPpacket(conn net.Conn) error { + var request = [48]byte{ + 0xe3, + } + + _, err := conn.Write(request[:]) + return err +} + +func parseNTPpacket(r []byte) time.Time { + // the timestamp starts at byte 40 of the received packet and is four bytes, + // this is NTP time (seconds since Jan 1 1900): + t := uint32(r[40])<<24 | uint32(r[41])<<16 | uint32(r[42])<<8 | uint32(r[43]) + const seventyYears = 2208988800 + return time.Unix(int64(t-seventyYears), 0) +} + +func message(format string, args ...interface{}) { + println(fmt.Sprintf(format, args...), "\r") +} diff --git a/examples/net/ntpclient/rtl8720dn.go b/examples/net/ntpclient/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/ntpclient/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/ntpclient/wifinina.go b/examples/net/ntpclient/wifinina.go new file mode 100644 index 000000000..07859b1a0 --- /dev/null +++ b/examples/net/ntpclient/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal arduino_nano33 nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/wifi/tcpclient/main.go b/examples/net/tcpclient/main.go similarity index 51% rename from examples/wifi/tcpclient/main.go rename to examples/net/tcpclient/main.go index 74eb1916f..2e603d879 100644 --- a/examples/wifi/tcpclient/main.go +++ b/examples/net/tcpclient/main.go @@ -1,52 +1,49 @@ -// This example opens a TCP connection and sends some data, -// for the purpose of testing speed and connectivity. +// This example opens a TCP connection and sends some data, for the purpose of +// testing speed and connectivity. // // You can open a server to accept connections from this program using: // -// nc -w 5 -lk 8080 +// nc -lk 8080 + package main import ( "bytes" "fmt" + "log" + "machine" + "net" "time" - - "tinygo.org/x/drivers/net" ) var ( - // access point info ssid string pass string + addr string = "10.0.0.100:8080" ) -// IP address of the server aka "hub". Replace with your own info. -const serverIP = "" - var buf = &bytes.Buffer{} func main() { - initAdaptor() - connectToAP() + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } for { sendBatch() time.Sleep(500 * time.Millisecond) } - println("Done.") } func sendBatch() { // make TCP connection - ip := net.ParseIP(serverIP) - raddr := &net.TCPAddr{IP: ip, Port: 8080} - laddr := &net.TCPAddr{Port: 8080} - message("---------------\r\nDialing TCP connection") - conn, err := net.DialTCP("tcp", laddr, raddr) - for ; err != nil; conn, err = net.DialTCP("tcp", laddr, raddr) { + conn, err := net.Dial("tcp", addr) + for ; err != nil; conn, err = net.Dial("tcp", addr) { message(err.Error()) time.Sleep(5 * time.Second) } @@ -65,7 +62,7 @@ func sendBatch() { "\r---------------------------- i == ", i, " ----------------------------") if w, err = conn.Write(buf.Bytes()); err != nil { println("error:", err.Error(), "\r") - continue + break } n += w } @@ -79,34 +76,17 @@ func sendBatch() { println("error:", err.Error(), "\r") } - // Right now this code is never reached. Need a way to trigger it... println("Disconnecting TCP...") conn.Close() } -// connect to access point -func connectToAP() { - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { // error connecting to AP - for { - println(err) - time.Sleep(1 * time.Second) - } - } - - println("Connected.") - - time.Sleep(2 * time.Second) - ip, err := adaptor.GetClientIP() - for ; err != nil; ip, err = adaptor.GetClientIP() { - message(err.Error()) - time.Sleep(1 * time.Second) - } - message(ip) -} - func message(msg string) { println(msg, "\r") } + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} diff --git a/examples/net/tcpclient/rtl8720dn.go b/examples/net/tcpclient/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/tcpclient/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/tcpclient/wifinina.go b/examples/net/tcpclient/wifinina.go new file mode 100644 index 000000000..07859b1a0 --- /dev/null +++ b/examples/net/tcpclient/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal arduino_nano33 nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/tcpecho/main.go b/examples/net/tcpecho/main.go new file mode 100644 index 000000000..6ffe81d57 --- /dev/null +++ b/examples/net/tcpecho/main.go @@ -0,0 +1,56 @@ +// This example listens on port :8080 for client connections. Bytes +// received from the client are echo'ed back to the client. Multiple +// clients can connect as the same time, each consuming a client socket, +// and being serviced by it's own go func. +// +// Example test using nc as client to copy file: +// +// $ nc 10.0.0.2 8080 copy ; cmp file copy + +package main + +import ( + "io" + "log" + "net" + "time" +) + +var ( + ssid string + pass string + port string = ":8080" +) + +var buf [1024]byte + +func echo(conn net.Conn) { + defer conn.Close() + _, err := io.CopyBuffer(conn, conn, buf[:]) + if err != nil && err != io.EOF { + log.Fatal(err.Error()) + } +} + +func main() { + + time.Sleep(time.Second) + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + l, err := net.Listen("tcp", port) + if err != nil { + log.Fatal(err.Error()) + } + defer l.Close() + + for { + conn, err := l.Accept() + if err != nil { + log.Fatal(err.Error()) + } + go echo(conn) + } +} diff --git a/examples/net/tcpecho/rtl8720dn.go b/examples/net/tcpecho/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/tcpecho/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/tcpecho/wifinina.go b/examples/net/tcpecho/wifinina.go new file mode 100644 index 000000000..07859b1a0 --- /dev/null +++ b/examples/net/tcpecho/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal arduino_nano33 nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/tlsclient/main.go b/examples/net/tlsclient/main.go new file mode 100644 index 000000000..ec8ae0630 --- /dev/null +++ b/examples/net/tlsclient/main.go @@ -0,0 +1,95 @@ +// This example uses TLS to send an HTTPS request to retrieve a webpage +// +// You shall see "strict-transport-security" header in the response, +// this confirms communication is indeed over HTTPS +// +// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security + +package main + +import ( + "bufio" + "crypto/tls" + "fmt" + "io" + "log" + "machine" + "net" + "strings" + "time" +) + +var ( + ssid string + pass string + // HTTPS server address to hit with a GET / request + address string = "httpbin.org:443" +) + +var conn net.Conn + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} + +func check(err error) { + if err != nil { + println("Hit an error:", err.Error()) + panic("BYE") + } +} + +func readResponse() { + r := bufio.NewReader(conn) + resp, err := io.ReadAll(r) + check(err) + println(string(resp)) +} + +func closeConnection() { + conn.Close() +} + +func dialConnection() { + var err error + + println("\r\n---------------\r\nDialing TLS connection") + conn, err = tls.Dial("tcp", address, nil) + for ; err != nil; conn, err = tls.Dial("tcp", address, nil) { + println("Connection failed:", err.Error()) + time.Sleep(5 * time.Second) + } + println("Connected!\r") +} + +func makeRequest() { + print("Sending HTTPS request...") + w := bufio.NewWriter(conn) + fmt.Fprintln(w, "GET /get HTTP/1.1") + fmt.Fprintln(w, "Host:", strings.Split(address, ":")[0]) + fmt.Fprintln(w, "User-Agent: TinyGo") + fmt.Fprintln(w, "Connection: close") + fmt.Fprintln(w) + check(w.Flush()) + println("Sent!\r\n\r") +} + +func main() { + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + for i := 0; ; i++ { + dialConnection() + makeRequest() + readResponse() + closeConnection() + println("--------", i, "--------\r\n") + time.Sleep(10 * time.Second) + } +} diff --git a/examples/net/tlsclient/rtl8720dn.go b/examples/net/tlsclient/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/tlsclient/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/tlsclient/wifinina.go b/examples/net/tlsclient/wifinina.go new file mode 100644 index 000000000..07859b1a0 --- /dev/null +++ b/examples/net/tlsclient/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal arduino_nano33 nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/webclient-tinyterm/main.go b/examples/net/webclient-tinyterm/main.go new file mode 100644 index 000000000..697101282 --- /dev/null +++ b/examples/net/webclient-tinyterm/main.go @@ -0,0 +1,142 @@ +// This example runs on wioterminal. It gets an URL using http.Get() and +// displays the output on the wioterminal LCD screen. +// +// Note: It may be necessary to increase the stack size when using "net/http". +// Use the -stack-size=4KB command line option. + +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "fmt" + "image/color" + "io" + "log" + "machine" + "net/http" + "net/url" + "strings" + "time" + + "tinygo.org/x/drivers/ili9341" + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" + "tinygo.org/x/tinyfont/proggy" + "tinygo.org/x/tinyterm" +) + +var ( + ssid string + pass string +) + +var ( + netcfg = rtl8720dn.Config{ + Ssid: ssid, + Passphrase: pass, + En: machine.RTL8720D_CHIP_PU, + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + } + + dev = rtl8720dn.New(&netcfg) + + display = ili9341.NewSPI( + machine.SPI3, + machine.LCD_DC, + machine.LCD_SS_PIN, + machine.LCD_RESET, + ) + + backlight = machine.LCD_BACKLIGHT + + terminal = tinyterm.NewTerminal(display) + + black = color.RGBA{0, 0, 0, 255} + white = color.RGBA{255, 255, 255, 255} + red = color.RGBA{255, 0, 0, 255} + blue = color.RGBA{0, 0, 255, 255} + green = color.RGBA{0, 255, 0, 255} + + font = &proggy.TinySZ8pt7b +) + +func notify(e netdev.Event) { + switch e { + case netdev.EventNetUp: + fmt.Println("Wifi connection UP") + fmt.Fprintf(terminal, "Wifi connection UP") + case netdev.EventNetDown: + fmt.Println("Wifi connection DOWN") + fmt.Fprintf(terminal, "Wifi connection DOWN") + } +} + +func main() { + + machine.SPI3.Configure(machine.SPIConfig{ + SCK: machine.LCD_SCK_PIN, + SDO: machine.LCD_SDO_PIN, + SDI: machine.LCD_SDI_PIN, + Frequency: 40000000, + }) + + display.Configure(ili9341.Config{}) + display.FillScreen(black) + + backlight.Configure(machine.PinConfig{machine.PinOutput}) + backlight.High() + + terminal.Configure(&tinyterm.Config{ + Font: font, + FontHeight: 10, + FontOffset: 6, + }) + + fmt.Fprintf(terminal, "Connecting to %s...\r\n", ssid) + + dev.NetNotify(notify) + netdev.Use(dev) + + if err := dev.NetConnect(); err != nil { + log.Fatal(err) + } + + name := "John Doe" + occupation := "gardener" + + params := "name=" + url.QueryEscape(name) + "&" + + "occupation=" + url.QueryEscape(occupation) + + path := fmt.Sprintf("https://httpbin.org/get?%s", params) + + cnt := 0 + for { + fmt.Fprintf(terminal, "Getting %s\r\n\r\n", path) + resp, err := http.Get(path) + if err != nil { + fmt.Fprintf(terminal, "%s\r\n", err.Error()) + time.Sleep(10 * time.Second) + continue + } + + fmt.Fprintf(terminal, "%s %s\r\n", resp.Proto, resp.Status) + for k, v := range resp.Header { + fmt.Fprintf(terminal, "%s: %s\r\n", k, strings.Join(v, " ")) + } + fmt.Fprintf(terminal, "\r\n") + + body, err := io.ReadAll(resp.Body) + fmt.Fprintf(terminal, string(body)) + resp.Body.Close() + + cnt++ + fmt.Fprintf(terminal, "-------- %d --------\r\n", cnt) + time.Sleep(10 * time.Second) + } +} diff --git a/examples/net/webclient/main.go b/examples/net/webclient/main.go new file mode 100644 index 000000000..05b6a63dc --- /dev/null +++ b/examples/net/webclient/main.go @@ -0,0 +1,106 @@ +// This example uses TCP to send an HTTP request to retrieve a webpage. The +// HTTP request is hand-rolled to avoid the overhead of using http.Get() from +// the "net/http" package. See example/net/http-get for the full http.Get() +// functionality. +// +// Example HTTP server: +// --------------------------------------------------------------------------- +// package main +// +// import "net/http" +// +// func main() { +// http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { +// w.Write([]byte("hello")) +// }) +// http.ListenAndServe(":8080", nil) +// } +// --------------------------------------------------------------------------- + +package main + +import ( + "bufio" + "fmt" + "io" + "log" + "machine" + "net" + "strings" + "time" +) + +var ( + ssid string + pass string + // HTTP server address to hit with a GET / request + address string = "10.0.0.100:8080" +) + +var conn net.Conn + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} + +func dialConnection() { + var err error + + println("\r\n---------------\r\nDialing TCP connection") + conn, err = net.Dial("tcp", address) + for ; err != nil; conn, err = net.Dial("tcp", address) { + println("Connection failed:", err.Error()) + time.Sleep(5 * time.Second) + } + println("Connected!\r") +} + +func check(err error) { + if err != nil { + println("Hit an error:", err.Error()) + panic("BYE") + } +} + +func makeRequest() { + println("Sending HTTP request...") + w := bufio.NewWriter(conn) + fmt.Fprintln(w, "GET / HTTP/1.1") + fmt.Fprintln(w, "Host:", strings.Split(address, ":")[0]) + fmt.Fprintln(w, "User-Agent: TinyGo") + fmt.Fprintln(w, "Connection: close") + fmt.Fprintln(w) + check(w.Flush()) + println("Sent!\r\n\r") +} + +func readResponse() { + r := bufio.NewReader(conn) + resp, err := io.ReadAll(r) + check(err) + println(string(resp)) +} + +func closeConnection() { + conn.Close() +} + +func main() { + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + for i := 0; ; i++ { + dialConnection() + makeRequest() + readResponse() + closeConnection() + println("--------", i, "--------\r\n") + time.Sleep(10 * time.Second) + } +} diff --git a/examples/net/webclient/rtl8720dn.go b/examples/net/webclient/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/webclient/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/webclient/wifinina.go b/examples/net/webclient/wifinina.go new file mode 100644 index 000000000..07859b1a0 --- /dev/null +++ b/examples/net/webclient/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal arduino_nano33 nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/wifinina/webserver/main.go b/examples/net/webserver/main.go similarity index 77% rename from examples/wifinina/webserver/main.go rename to examples/net/webserver/main.go index f8bf65eba..9a32cf444 100644 --- a/examples/wifinina/webserver/main.go +++ b/examples/net/webserver/main.go @@ -1,82 +1,41 @@ +// This example listens on port :80 serving a web page. Multiple clients +// may connect and be serviced at the same time. IPv4 only. HTTP only. +// +// $ curl http://10.0.0.2 +// +// Note: It may be necessary to increase the stack size when using "net/http". +// Use the -stack-size=4KB command line option. + package main import ( "fmt" + "log" "machine" + "net/http" "strconv" "time" - - "tinygo.org/x/drivers/net/http" - "tinygo.org/x/drivers/wifinina" ) -// You can override the settings with the init() in another source code: -// -// func init() { -// ssid = "your-ssid" -// pass = "your-password" -// } -// -// Or use -ldflags option on tinygo command to set at compile-time: -// -// tinygo flash ... -ldflags '-X "main.ssid=xxx" -X "main.pass=xxx"' ... -// - var ( ssid string pass string + port string = ":80" ) var led = machine.LED func main() { - led.Configure(machine.PinConfig{Mode: machine.PinOutput}) - - err := run() - for err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} -func run() error { - - spi := machine.NINA_SPI - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - adaptor := wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() - - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { - return err - } + // wait a bit for serial + time.Sleep(time.Second) - println("Connected.") + led.Configure(machine.PinConfig{Mode: machine.PinOutput}) - time.Sleep(2 * time.Second) - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - return err + if err := NetConnect(); err != nil { + log.Fatal(err) } - fmt.Printf("IP Address : %s\r\n", ip) - fmt.Printf("Mask : %s\r\n", subnet) - fmt.Printf("Gateway : %s\r\n", gateway) - - http.UseDriver(adaptor) - http.HandleFunc("/", root) http.HandleFunc("/hello", hello) http.HandleFunc("/cnt", cnt) @@ -84,7 +43,11 @@ func run() error { http.HandleFunc("/off", LED_OFF) http.HandleFunc("/on", LED_ON) - return http.ListenAndServe(":80", nil) + err := http.ListenAndServe(port, nil) + for err != nil { + fmt.Printf("error: %s\r\n", err.Error()) + time.Sleep(5 * time.Second) + } } func root(w http.ResponseWriter, r *http.Request) { @@ -159,7 +122,7 @@ func root(w http.ResponseWriter, r *http.Request) {

- `, access) +`, access) } func sixlines(w http.ResponseWriter, r *http.Request) { diff --git a/examples/net/webserver/rtl8720dn.go b/examples/net/webserver/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/webserver/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/webserver/wifinina.go b/examples/net/webserver/wifinina.go new file mode 100644 index 000000000..658492e71 --- /dev/null +++ b/examples/net/webserver/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/websocket/dial/main.go b/examples/net/websocket/dial/main.go new file mode 100644 index 000000000..72be95749 --- /dev/null +++ b/examples/net/websocket/dial/main.go @@ -0,0 +1,84 @@ +// This example is a websocket client. It connects to a websocket server +// which echos messages back to the client. For server, see +// +// https://pkg.go.dev/golang.org/x/net/websocket#example-Handler +// +// 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. + +package main + +import ( + // "crypto/rand" + "fmt" + "log" + "machine" + "time" + + "golang.org/x/net/websocket" +) + +var ( + ssid string + pass string + url string = "ws://10.0.0.100:8080/echo" +) + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} + +/* +func init() { + rand.Reader = &reader{} +} + +type reader struct {} + +func (r *reader) Read(b []byte) (n int, err error) { + if len(b) == 0 { + return + } + + var randomByte uint32 + for i := range b { + if i%4 == 0 { + randomByte, err = machine.GetRNG() + if err != nil { + return n, err + } + } else { + randomByte >>= 8 + } + b[i] = byte(randomByte) + } + + return len(b), nil +} +*/ + +func main() { + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + origin := "http://localhost/" + ws, err := websocket.Dial(url, "", origin) + if err != nil { + log.Fatal(err) + } + if _, err := ws.Write([]byte("hello, world!\n")); err != nil { + log.Fatal(err) + } + var msg = make([]byte, 512) + var n int + if n, err = ws.Read(msg); err != nil { + log.Fatal(err) + } + fmt.Printf("Received: %s", msg[:n]) +} diff --git a/examples/net/websocket/dial/rtl8720dn.go b/examples/net/websocket/dial/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/websocket/dial/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/websocket/dial/wifinina.go b/examples/net/websocket/dial/wifinina.go new file mode 100644 index 000000000..658492e71 --- /dev/null +++ b/examples/net/websocket/dial/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/websocket/handler/main.go b/examples/net/websocket/handler/main.go new file mode 100644 index 000000000..5a50e9b6d --- /dev/null +++ b/examples/net/websocket/handler/main.go @@ -0,0 +1,52 @@ +// This example is a websocket server. It listens for websocket clients +// to connect and echos messages back to the client. For client, see +// +// https://pkg.go.dev/golang.org/x/net/websocket#example-Dial +// +// 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. + +package main + +import ( + "io" + "log" + "machine" + "net/http" + "time" + + "golang.org/x/net/websocket" +) + +var ( + ssid string + pass string + port string = ":8080" +) + +// Echo the data received on the WebSocket. +func EchoServer(ws *websocket.Conn) { + io.Copy(ws, ws) +} + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} + +// This example demonstrates a trivial echo server. +func main() { + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + http.Handle("/echo", websocket.Handler(EchoServer)) + err := http.ListenAndServe(port, nil) + if err != nil { + panic("ListenAndServe: " + err.Error()) + } +} diff --git a/examples/net/websocket/handler/rtl8720dn.go b/examples/net/websocket/handler/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/websocket/handler/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/websocket/handler/wifinina.go b/examples/net/websocket/handler/wifinina.go new file mode 100644 index 000000000..658492e71 --- /dev/null +++ b/examples/net/websocket/handler/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/webstatic/images/tinygo-logo.png b/examples/net/webstatic/images/tinygo-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d362856fd97eb03e150096cce163d1ef2a116c04 GIT binary patch literal 30898 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 + + TinyGo - A Go Compiler For Small Places + + + +

TinyGo - A Go Compiler For Small Places

+ + + diff --git a/examples/net/webstatic/main.go b/examples/net/webstatic/main.go new file mode 100644 index 000000000..d2c522bc6 --- /dev/null +++ b/examples/net/webstatic/main.go @@ -0,0 +1,38 @@ +// This example is an HTTP server serving up a static file system +// +// Note: It may be necessary to increase the stack size when using "net/http". +// Use the -stack-size=4KB command line option. + +package main + +import ( + "embed" + "log" + "net/http" + "time" +) + +var ( + ssid string + pass string + port string = ":80" +) + +//go:embed index.html main.go images +var fs embed.FS + +func main() { + // wait a bit for console + time.Sleep(time.Second) + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + hfs := http.FileServer(http.FS(fs)) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + hfs.ServeHTTP(w, r) + }) + + log.Fatal(http.ListenAndServe(port, nil)) +} diff --git a/examples/net/webstatic/rtl8720dn.go b/examples/net/webstatic/rtl8720dn.go new file mode 100644 index 000000000..73da6a867 --- /dev/null +++ b/examples/net/webstatic/rtl8720dn.go @@ -0,0 +1,39 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/webstatic/wifinina.go b/examples/net/webstatic/wifinina.go new file mode 100644 index 000000000..658492e71 --- /dev/null +++ b/examples/net/webstatic/wifinina.go @@ -0,0 +1,43 @@ +//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + netdev.Use(dev) + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/rtl8720dn/mqttclient/main.go b/examples/rtl8720dn/mqttclient/main.go deleted file mode 100644 index f4e8d8484..000000000 --- a/examples/rtl8720dn/mqttclient/main.go +++ /dev/null @@ -1,131 +0,0 @@ -// This is a sensor station that uses a RTL8720DN running on the device UART2. -// It creates an MQTT connection that publishes a message every second -// to an MQTT broker. -// -// In other words: -// Your computer <--> USB-CDC <--> MCU <--> UART2 <--> RTL8720DN <--> Internet <--> MQTT broker. -// -// You must install the Paho MQTT package to build this program: -// -// go get -u github.com/eclipse/paho.mqtt.golang -// -// You can check that mqttpub is running successfully with the following command. -// -// mosquitto_sub -h test.mosquitto.org -t tinygo -package main - -import ( - "machine" - - "fmt" - "math/rand" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/net/mqtt" - "tinygo.org/x/drivers/rtl8720dn" -) - -// You can override the setting with the init() in another source code. -// func init() { -// ssid = "your-ssid" -// pass = "your-password" -// debug = true -// server = "tinygo.org" -// } - -var ( - ssid string - pass string - server string = "tcp://test.mosquitto.org:1883" - debug = false -) - -var buf [0x400]byte - -var lastRequestTime time.Time -var conn net.Conn -var adaptor *rtl8720dn.Driver - -func main() { - err := run() - for err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} - -var ( - topic = "tinygo" -) - -func run() error { - // change the UART and pins as needed for platforms other than the WioTerminal. - adaptor = rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) - adaptor.Debug(debug) - adaptor.Configure() - - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { - return err - } - - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - return err - } - fmt.Printf("IP Address : %s\r\n", ip) - fmt.Printf("Mask : %s\r\n", subnet) - fmt.Printf("Gateway : %s\r\n", gateway) - - rand.Seed(time.Now().UnixNano()) - - opts := mqtt.NewClientOptions() - opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10)) - - println("Connectng to MQTT...") - cl := mqtt.NewClient(opts) - if token := cl.Connect(); token.Wait() && token.Error() != nil { - failMessage(token.Error().Error()) - } - - for i := 0; ; i++ { - println("Publishing MQTT message...") - data := []byte(fmt.Sprintf(`{"e":[{"n":"hello %d","v":101}]}`, i)) - token := cl.Publish(topic, 0, false, data) - token.Wait() - if err := token.Error(); err != nil { - return err - } - time.Sleep(100 * time.Millisecond) - } - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting MQTT...") - cl.Disconnect(100) - - println("Done.") - - return nil -} - -// Returns an int >= min, < max -func randomInt(min, max int) int { - return min + rand.Intn(max-min) -} - -// Generate a random string of A-Z chars with len = l -func randomString(len int) string { - bytes := make([]byte, len) - for i := 0; i < len; i++ { - bytes[i] = byte(randomInt(65, 90)) - } - return string(bytes) -} - -func failMessage(msg string) { - for { - println(msg) - time.Sleep(1 * time.Second) - } -} diff --git a/examples/rtl8720dn/mqttsub/main.go b/examples/rtl8720dn/mqttsub/main.go deleted file mode 100644 index 6c53313df..000000000 --- a/examples/rtl8720dn/mqttsub/main.go +++ /dev/null @@ -1,154 +0,0 @@ -// This is a sensor station that uses a RTL8720DN running on the device UART2. -// It creates an MQTT connection that publishes a message every second -// to an MQTT broker. -// -// In other words: -// Your computer <--> USB-CDC <--> MCU <--> UART2 <--> RTL8720DN <--> Internet <--> MQTT broker. -// -// You must also install the Paho MQTT package to build this program: -// -// go get -u github.com/eclipse/paho.mqtt.golang -// -// You can check that mqttpub/mqttsub is running successfully with the following command. -// -// mosquitto_sub -h test.mosquitto.org -t tinygo/tx -// mosquitto_pub -h test.mosquitto.org -t tinygo/rx -m "hello world" -package main - -import ( - "machine" - - "fmt" - "math/rand" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/net/mqtt" - "tinygo.org/x/drivers/rtl8720dn" -) - -// You can override the setting with the init() in another source code. -// func init() { -// ssid = "your-ssid" -// pass = "your-password" -// debug = true -// server = "tinygo.org" -// } - -var ( - ssid string - pass string - server string = "tcp://test.mosquitto.org:1883" - debug = false -) - -var buf [0x400]byte - -var lastRequestTime time.Time -var conn net.Conn -var adaptor *rtl8720dn.Driver - -func main() { - err := run() - for err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} - -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - cl mqtt.Client - topicTx = "tinygo/tx" - topicRx = "tinygo/rx" -) - -func subHandler(client mqtt.Client, msg mqtt.Message) { - fmt.Printf("[%s] ", msg.Topic()) - fmt.Printf("%s\r\n", msg.Payload()) -} - -func run() error { - // change the UART and pins as needed for platforms other than the WioTerminal. - adaptor = rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) - adaptor.Debug(debug) - adaptor.Configure() - - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { - return err - } - - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - return err - } - fmt.Printf("IP Address : %s\r\n", ip) - fmt.Printf("Mask : %s\r\n", subnet) - fmt.Printf("Gateway : %s\r\n", gateway) - - rand.Seed(time.Now().UnixNano()) - - opts := mqtt.NewClientOptions() - opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10)) - - println("Connecting to MQTT broker at", server) - cl = mqtt.NewClient(opts) - if token := cl.Connect(); token.Wait() && token.Error() != nil { - failMessage(token.Error().Error()) - } - - // subscribe - token := cl.Subscribe(topicRx, 0, subHandler) - token.Wait() - if token.Error() != nil { - failMessage(token.Error().Error()) - } - - go publishing() - - select {} - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting MQTT...") - cl.Disconnect(100) - - println("Done.") - - return nil -} - -func publishing() { - for i := 0; ; i++ { - println("Publishing MQTT message...") - data := []byte(fmt.Sprintf(`{"e":[{"n":"hello %d","v":101}]}`, i)) - token := cl.Publish(topicTx, 0, false, data) - token.Wait() - if token.Error() != nil { - println(token.Error().Error()) - } - - time.Sleep(20 * 100 * time.Millisecond) - } -} - -// Returns an int >= min, < max -func randomInt(min, max int) int { - return min + rand.Intn(max-min) -} - -// Generate a random string of A-Z chars with len = l -func randomString(len int) string { - bytes := make([]byte, len) - for i := 0; i < len; i++ { - bytes[i] = byte(randomInt(65, 90)) - } - return string(bytes) -} - -func failMessage(msg string) { - for { - println(msg) - time.Sleep(1 * time.Second) - } -} diff --git a/examples/rtl8720dn/ntpclient/main.go b/examples/rtl8720dn/ntpclient/main.go deleted file mode 100644 index 7149afcb9..000000000 --- a/examples/rtl8720dn/ntpclient/main.go +++ /dev/null @@ -1,143 +0,0 @@ -// This is an example of using the rtl8720dn driver to implement a NTP client. -// It creates a UDP connection to request the current time and parse the -// response from a NTP server. -package main - -import ( - "machine" - - "errors" - "fmt" - "runtime" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/rtl8720dn" -) - -// IP address of the server aka "hub". Replace with your own info. -// You can override the setting with the init() in another source code. -// func init() { -// ssid = "your-ssid" -// pass = "your-password" -// ntpHost = "129.6.15.29" -// debug = true -// } - -var ( - ssid string - pass string - ntpHost = "129.6.15.29" - debug = false -) - -const NTP_PACKET_SIZE = 48 - -var b = make([]byte, NTP_PACKET_SIZE) - -func main() { - err := run() - for err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} - -func run() error { - adaptor := rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) - adaptor.Debug(debug) - adaptor.Configure() - - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { - return err - } - - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - return err - } - fmt.Printf("IP Address : %s\r\n", ip) - fmt.Printf("Mask : %s\r\n", subnet) - fmt.Printf("Gateway : %s\r\n", gateway) - - // now make UDP connection - hip := net.ParseIP(ntpHost) - raddr := &net.UDPAddr{IP: hip, Port: 123} - laddr := &net.UDPAddr{Port: 2390} - conn, err := net.DialUDP("udp", laddr, raddr) - if err != nil { - return err - } - - for { - // send data - println("Requesting NTP time...") - t, err := getCurrentTime(conn) - if err != nil { - message("Error getting current time: %v", err) - } else { - message("NTP time: %v", t) - } - runtime.AdjustTimeOffset(-1 * int64(time.Since(t))) - for i := 0; i < 10; i++ { - message("Current time: %v", time.Now()) - time.Sleep(1 * time.Second) - } - } - -} - -func getCurrentTime(conn *net.UDPSerialConn) (time.Time, error) { - if err := sendNTPpacket(conn); err != nil { - return time.Time{}, err - } - clearBuffer() - for now := time.Now(); time.Since(now) < time.Second; { - time.Sleep(5 * time.Millisecond) - if n, err := conn.Read(b); err != nil { - return time.Time{}, fmt.Errorf("error reading UDP packet: %w", err) - } else if n == 0 { - continue // no packet received yet - } else if n != NTP_PACKET_SIZE { - return time.Time{}, fmt.Errorf("expected NTP packet size of %d: %d", NTP_PACKET_SIZE, n) - } - return parseNTPpacket(), nil - } - return time.Time{}, errors.New("no packet received after 1 second") -} - -func sendNTPpacket(conn *net.UDPSerialConn) error { - clearBuffer() - b[0] = 0b11100011 // LI, Version, Mode - b[1] = 0 // Stratum, or type of clock - b[2] = 6 // Polling Interval - b[3] = 0xEC // Peer Clock Precision - // 8 bytes of zero for Root Delay & Root Dispersion - b[12] = 49 - b[13] = 0x4E - b[14] = 49 - b[15] = 52 - if _, err := conn.Write(b); err != nil { - return err - } - return nil -} - -func parseNTPpacket() time.Time { - // the timestamp starts at byte 40 of the received packet and is four bytes, - // this is NTP time (seconds since Jan 1 1900): - t := uint32(b[40])<<24 | uint32(b[41])<<16 | uint32(b[42])<<8 | uint32(b[43]) - const seventyYears = 2208988800 - return time.Unix(int64(t-seventyYears), 0) -} - -func clearBuffer() { - for i := range b { - b[i] = 0 - } -} - -func message(format string, args ...interface{}) { - println(fmt.Sprintf(format, args...), "\r") -} diff --git a/examples/rtl8720dn/tcpclient/main.go b/examples/rtl8720dn/tcpclient/main.go deleted file mode 100644 index 650f41531..000000000 --- a/examples/rtl8720dn/tcpclient/main.go +++ /dev/null @@ -1,121 +0,0 @@ -// This example opens a TCP connection using a device with RTL8720DN firmware -// and sends some data, for the purpose of testing speed and connectivity. -// -// You can open a server to accept connections from this program using: -// -// nc -w 5 -lk 8080 -package main - -import ( - "machine" - - "bytes" - "fmt" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/rtl8720dn" -) - -// You can override the setting with the init() in another source code. -// func init() { -// ssid = "your-ssid" -// pass = "your-password" -// serverIP = "192.168.1.119" -// debug = true -// } - -var ( - ssid string - pass string - serverIP = "" - debug = false -) - -var buf = &bytes.Buffer{} - -func main() { - err := run() - for err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} - -func run() error { - adaptor := rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) - adaptor.Debug(debug) - adaptor.Configure() - - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { - return err - } - - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - return err - } - fmt.Printf("IP Address : %s\r\n", ip) - fmt.Printf("Mask : %s\r\n", subnet) - fmt.Printf("Gateway : %s\r\n", gateway) - - for { - sendBatch() - time.Sleep(500 * time.Millisecond) - } - println("Done.") - - return nil -} - -func sendBatch() { - - // make TCP connection - ip := net.ParseIP(serverIP) - raddr := &net.TCPAddr{IP: ip, Port: 8080} - laddr := &net.TCPAddr{Port: 8080} - - message("---------------\r\nDialing TCP connection") - conn, err := net.DialTCP("tcp", laddr, raddr) - for ; err != nil; conn, err = net.DialTCP("tcp", laddr, raddr) { - message(err.Error()) - time.Sleep(5 * time.Second) - } - - n := 0 - w := 0 - start := time.Now() - - // send data - message("Sending data") - - for i := 0; i < 1000; i++ { - buf.Reset() - fmt.Fprint(buf, - "\r---------------------------- i == ", i, " ----------------------------"+ - "\r---------------------------- i == ", i, " ----------------------------") - if w, err = conn.Write(buf.Bytes()); err != nil { - println("error:", err.Error(), "\r") - continue - } - n += w - } - - buf.Reset() - ms := time.Now().Sub(start).Milliseconds() - fmt.Fprint(buf, "\nWrote ", n, " bytes in ", ms, " ms\r\n") - message(buf.String()) - - if _, err := conn.Write(buf.Bytes()); err != nil { - println("error:", err.Error(), "\r") - } - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting TCP...") - conn.Close() -} - -func message(msg string) { - println(msg, "\r") -} diff --git a/examples/rtl8720dn/tlsclient/main.go b/examples/rtl8720dn/tlsclient/main.go deleted file mode 100644 index e86dd93c7..000000000 --- a/examples/rtl8720dn/tlsclient/main.go +++ /dev/null @@ -1,137 +0,0 @@ -package main - -import ( - "machine" - - "bufio" - "fmt" - "strings" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/net/http" - "tinygo.org/x/drivers/rtl8720dn" -) - -// You can override the setting with the init() in another source code. -// func init() { -// ssid = "your-ssid" -// pass = "your-password" -// debug = true -// url = "https://www.example.com" -// test_root_ca = "..." -// } - -var ( - ssid string - pass string - url string = "https://www.example.com" - debug = false -) - -// Set the test_root_ca created by the following command -// $ openssl s_client -showcerts -verify 5 -connect www.example.com:443 < /dev/null -var test_root_ca = `-----BEGIN CERTIFICATE----- -MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD -QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT -MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j -b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB -CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 -nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt -43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P -T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 -gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO -BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR -TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw -DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr -hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg -06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF -PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls -YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk -CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= ------END CERTIFICATE----- -` - -var buf [0x1000]byte - -var lastRequestTime time.Time -var conn net.Conn - -func main() { - err := run() - for err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} - -func run() error { - adaptor := rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) - adaptor.Debug(debug) - adaptor.Configure() - - adaptor.SetRootCA(&test_root_ca) - http.SetBuf(buf[:]) - - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { - return err - } - - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - return err - } - fmt.Printf("IP Address : %s\r\n", ip) - fmt.Printf("Mask : %s\r\n", subnet) - fmt.Printf("Gateway : %s\r\n", gateway) - - // You can send and receive cookies in the following way - // import "tinygo.org/x/drivers/net/http/cookiejar" - // jar, err := cookiejar.New(nil) - // if err != nil { - // return err - // } - // client := &http.Client{Jar: jar} - // http.DefaultClient = client - - cnt := 0 - for { - // Various examples are as follows - // - // -- Get - // resp, err := http.Get(url) - // - // -- Post - // body := `cnt=12` - // resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(body)) - // - // -- Post with JSON - // body := `{"msg": "hello"}` - // resp, err := http.Post(url, "application/json", strings.NewReader(body)) - - resp, err := http.Get(url) - if err != nil { - return err - } - - fmt.Printf("%s %s\r\n", resp.Proto, resp.Status) - for k, v := range resp.Header { - fmt.Printf("%s: %s\r\n", k, strings.Join(v, " ")) - } - fmt.Printf("\r\n") - - scanner := bufio.NewScanner(resp.Body) - for scanner.Scan() { - fmt.Printf("%s\r\n", scanner.Text()) - } - resp.Body.Close() - - cnt++ - fmt.Printf("-------- %d --------\r\n", cnt) - time.Sleep(10 * time.Second) - } -} diff --git a/examples/rtl8720dn/udpstation/main.go b/examples/rtl8720dn/udpstation/main.go deleted file mode 100644 index fead6c548..000000000 --- a/examples/rtl8720dn/udpstation/main.go +++ /dev/null @@ -1,91 +0,0 @@ -package main - -import ( - "machine" - - "fmt" - "strconv" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/net/http" - "tinygo.org/x/drivers/rtl8720dn" -) - -// IP address of the server aka "hub". Replace with your own info. -// You can override the setting with the init() in another source code. -// func init() { -// ssid = "your-ssid" -// pass = "your-password" -// hubIP = "192.168.1.118" -// debug = true -// } - -var ( - ssid string - pass string - hubIP = "" - debug = false -) - -var buf [0x400]byte - -func main() { - err := run() - for err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} - -func run() error { - adaptor := rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) - adaptor.Debug(debug) - adaptor.Configure() - - http.SetBuf(buf[:]) - - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { - return err - } - - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - return err - } - fmt.Printf("IP Address : %s\r\n", ip) - fmt.Printf("Mask : %s\r\n", subnet) - fmt.Printf("Gateway : %s\r\n", gateway) - - // now make UDP connection - hip := net.ParseIP(hubIP) - raddr := &net.UDPAddr{IP: hip, Port: 2222} - laddr := &net.UDPAddr{Port: 2222} - - println("Dialing UDP connection...") - conn, err := net.DialUDP("udp", laddr, raddr) - if err != nil { - return err - } - - for { - // send data - println("Sending data...") - for i := 0; i < 25; i++ { - conn.Write([]byte("hello " + strconv.Itoa(i) + "\r\n")) - } - time.Sleep(1000 * time.Millisecond) - } - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting UDP...") - conn.Close() - println("Done.") - - return nil -} - -func message(msg string) { - println(msg, "\r") -} diff --git a/examples/rtl8720dn/version/main.go b/examples/rtl8720dn/version/main.go deleted file mode 100644 index 7a02e2f20..000000000 --- a/examples/rtl8720dn/version/main.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "machine" - - "fmt" - "time" - - "tinygo.org/x/drivers/rtl8720dn" -) - -var ( - debug = false -) - -func main() { - err := run() - for err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} - -func run() error { - adaptor := rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) - adaptor.Debug(debug) - adaptor.Configure() - - ver, err := adaptor.Version() - if err != nil { - return nil - } - - for { - fmt.Printf("RTL8270DN Firmware Version: %s\r\n", ver) - time.Sleep(10 * time.Second) - } - - return nil -} diff --git a/examples/rtl8720dn/webclient-tinyterm/main.go b/examples/rtl8720dn/webclient-tinyterm/main.go deleted file mode 100644 index 060aaf7f4..000000000 --- a/examples/rtl8720dn/webclient-tinyterm/main.go +++ /dev/null @@ -1,162 +0,0 @@ -package main - -import ( - "machine" - - "bufio" - "fmt" - "image/color" - "strings" - "time" - - "tinygo.org/x/drivers/ili9341" - "tinygo.org/x/drivers/net/http" - "tinygo.org/x/drivers/rtl8720dn" - - "tinygo.org/x/tinyfont/proggy" - "tinygo.org/x/tinyterm" -) - -// You can override the setting with the init() in another source code. -// If debug is enabled, a serial connection is required. -// func init() { -// ssid = "your-ssid" -// pass = "your-password" -// debug = false // true -// server = "tinygo.org" -// } - -var ( - ssid string - pass string - url = "http://tinygo.org/" - debug = false -) - -var ( - display = ili9341.NewSPI( - machine.SPI3, - machine.LCD_DC, - machine.LCD_SS_PIN, - machine.LCD_RESET, - ) - - backlight = machine.LCD_BACKLIGHT - - terminal = tinyterm.NewTerminal(display) - - black = color.RGBA{0, 0, 0, 255} - white = color.RGBA{255, 255, 255, 255} - red = color.RGBA{255, 0, 0, 255} - blue = color.RGBA{0, 0, 255, 255} - green = color.RGBA{0, 255, 0, 255} - - font = &proggy.TinySZ8pt7b -) - -var buf [0x400]byte - -func main() { - display.FillScreen(black) - backlight.High() - - terminal.Configure(&tinyterm.Config{ - Font: font, - FontHeight: 10, - FontOffset: 6, - }) - - err := run() - for err != nil { - fmt.Fprintf(terminal, "error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} - -func run() error { - fmt.Fprintf(terminal, "setupRTL8720DN()\r\n") - if debug { - fmt.Fprintf(terminal, "Running in debug mode.\r\n") - fmt.Fprintf(terminal, "A serial connection is required to continue execution.\r\n") - } - - adaptor := rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) - adaptor.Debug(debug) - adaptor.Configure() - - http.UseDriver(adaptor) - http.SetBuf(buf[:]) - - fmt.Fprintf(terminal, "ConnectToAP()\r\n") - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { - return err - } - fmt.Fprintf(terminal, "connected\r\n\r\n") - - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - return err - } - fmt.Fprintf(terminal, "IP Address : %s\r\n", ip) - fmt.Fprintf(terminal, "Mask : %s\r\n", subnet) - fmt.Fprintf(terminal, "Gateway : %s\r\n", gateway) - - // You can send and receive cookies in the following way - // import "tinygo.org/x/drivers/net/http/cookiejar" - // jar, err := cookiejar.New(nil) - // if err != nil { - // return err - // } - // client := &http.Client{Jar: jar} - // http.DefaultClient = client - - cnt := 0 - for { - // Various examples are as follows - // - // -- Get - // resp, err := http.Get(url) - // - // -- Post - // body := `cnt=12` - // resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(body)) - // - // -- Post with JSON - // body := `{"msg": "hello"}` - // resp, err := http.Post(url, "application/json", strings.NewReader(body)) - - resp, err := http.Get(url) - if err != nil { - return err - } - - fmt.Fprintf(terminal, "%s %s\r\n", resp.Proto, resp.Status) - for k, v := range resp.Header { - fmt.Fprintf(terminal, "%s: %s\r\n", k, strings.Join(v, " ")) - } - fmt.Printf("\r\n") - - scanner := bufio.NewScanner(resp.Body) - for scanner.Scan() { - fmt.Fprintf(terminal, "%s\r\n", scanner.Text()) - } - resp.Body.Close() - - cnt++ - fmt.Fprintf(terminal, "-------- %d --------\r\n", cnt) - time.Sleep(10 * time.Second) - } -} - -func init() { - machine.SPI3.Configure(machine.SPIConfig{ - SCK: machine.LCD_SCK_PIN, - SDO: machine.LCD_SDO_PIN, - SDI: machine.LCD_SDI_PIN, - Frequency: 40000000, - }) - display.Configure(ili9341.Config{}) - - backlight.Configure(machine.PinConfig{machine.PinOutput}) -} diff --git a/examples/rtl8720dn/webclient/main.go b/examples/rtl8720dn/webclient/main.go deleted file mode 100644 index 5c7dabea1..000000000 --- a/examples/rtl8720dn/webclient/main.go +++ /dev/null @@ -1,106 +0,0 @@ -package main - -import ( - "machine" - - "bufio" - "fmt" - "strings" - "time" - - "tinygo.org/x/drivers/net/http" - "tinygo.org/x/drivers/rtl8720dn" -) - -// You can override the setting with the init() in another source code. -// func init() { -// ssid = "your-ssid" -// pass = "your-password" -// url = "http://tinygo.org/" -// debug = true -// } - -var ( - ssid string - pass string - url = "http://tinygo.org/" - debug = false -) - -var buf [0x400]byte - -func main() { - err := run() - for err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} - -func run() error { - adaptor := rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) - adaptor.Debug(debug) - adaptor.Configure() - - http.UseDriver(adaptor) - http.SetBuf(buf[:]) - - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { - return err - } - - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - return err - } - fmt.Printf("IP Address : %s\r\n", ip) - fmt.Printf("Mask : %s\r\n", subnet) - fmt.Printf("Gateway : %s\r\n", gateway) - - // You can send and receive cookies in the following way - // import "tinygo.org/x/drivers/net/http/cookiejar" - // jar, err := cookiejar.New(nil) - // if err != nil { - // return err - // } - // client := &http.Client{Jar: jar} - // http.DefaultClient = client - - cnt := 0 - for { - // Various examples are as follows - // - // -- Get - // resp, err := http.Get(url) - // - // -- Post - // body := `cnt=12` - // resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(body)) - // - // -- Post with JSON - // body := `{"msg": "hello"}` - // resp, err := http.Post(url, "application/json", strings.NewReader(body)) - - resp, err := http.Get(url) - if err != nil { - return err - } - - fmt.Printf("%s %s\r\n", resp.Proto, resp.Status) - for k, v := range resp.Header { - fmt.Printf("%s: %s\r\n", k, strings.Join(v, " ")) - } - fmt.Printf("\r\n") - - scanner := bufio.NewScanner(resp.Body) - for scanner.Scan() { - fmt.Printf("%s\r\n", scanner.Text()) - } - resp.Body.Close() - - cnt++ - fmt.Printf("-------- %d --------\r\n", cnt) - time.Sleep(10 * time.Second) - } -} diff --git a/examples/rtl8720dn/webserver/main.go b/examples/rtl8720dn/webserver/main.go deleted file mode 100644 index 6ae17c712..000000000 --- a/examples/rtl8720dn/webserver/main.go +++ /dev/null @@ -1,193 +0,0 @@ -package main - -import ( - "fmt" - "machine" - "strconv" - "time" - - "tinygo.org/x/drivers/net/http" - "tinygo.org/x/drivers/rtl8720dn" -) - -// You can override the setting with the init() in another source code. -// func init() { -// ssid = "your-ssid" -// pass = "your-password" -// debug = true -// } - -var ( - ssid string - pass string - debug = false -) - -var led = machine.LED -var backlight = machine.LCD_BACKLIGHT - -func main() { - led.Configure(machine.PinConfig{Mode: machine.PinOutput}) - backlight.Configure(machine.PinConfig{Mode: machine.PinOutput}) - - err := run() - for err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - time.Sleep(5 * time.Second) - } -} - -func run() error { - adaptor := rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) - adaptor.Debug(debug) - adaptor.Configure() - - http.UseDriver(adaptor) - - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { - return err - } - - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - return err - } - fmt.Printf("IP Address : %s\r\n", ip) - fmt.Printf("Mask : %s\r\n", subnet) - fmt.Printf("Gateway : %s\r\n", gateway) - - http.HandleFunc("/", root) - http.HandleFunc("/hello", hello) - http.HandleFunc("/cnt", cnt) - http.HandleFunc("/6", sixlines) - http.HandleFunc("/off", LED_OFF) - http.HandleFunc("/on", LED_ON) - if err := http.ListenAndServe(":80", nil); err != nil { - message(err.Error()) - } - return nil -} - -func root(w http.ResponseWriter, r *http.Request) { - access := 1 - - cookie, err := r.Cookie("access") - if err != nil { - if err == http.ErrNoCookie { - cookie = &http.Cookie{ - Name: "access", - Value: "1", - } - } else { - http.Error(w, fmt.Sprintf("%s", err.Error()), http.StatusBadRequest) - return - } - } else { - v, err := strconv.ParseInt(cookie.Value, 10, 0) - if err != nil { - http.Error(w, fmt.Sprintf("invalid cookie.Value : %s", cookie.Value), http.StatusBadRequest) - return - } - cookie.Value = fmt.Sprintf("%d", v+1) - access = int(v) + 1 - } - http.SetCookie(w, cookie) - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - - - TinyGo HTTP Server - - - -
TinyGo HTTP Server
- -

- access: %d -

- -
/hello
- /6
- -

- LED
- /on
- /off
-

- - -

- /cnt
- cnt:
- incrCnt()
-

- - -
-

- - - `, access) -} - -func sixlines(w http.ResponseWriter, r *http.Request) { - // https://fukuno.jig.jp/3267 - fmt.Fprint(w, ``) -} - -func LED_ON(w http.ResponseWriter, r *http.Request) { - led.High() - backlight.High() - w.Header().Set(`Content-Type`, `text/plain; charset=UTF-8`) - fmt.Fprintf(w, "led.High()") -} - -func LED_OFF(w http.ResponseWriter, r *http.Request) { - led.Low() - backlight.Low() - w.Header().Set(`Content-Type`, `text/plain; charset=UTF-8`) - fmt.Fprintf(w, "led.Low()") -} - -func hello(w http.ResponseWriter, r *http.Request) { - w.Header().Set(`Content-Type`, `text/plain; charset=UTF-8`) - fmt.Fprintf(w, "hello") -} - -var counter int - -func cnt(w http.ResponseWriter, r *http.Request) { - r.ParseForm() - if r.Method == "POST" { - c := r.Form.Get("cnt") - if c != "" { - i64, _ := strconv.ParseInt(c, 0, 0) - counter = int(i64) - } - } - - w.Header().Set(`Content-Type`, `application/json`) - fmt.Fprintf(w, `{"cnt": %d}`, counter) -} - -func message(msg string) { - println(msg, "\r") -} diff --git a/examples/wifi/tcpclient/espat.go b/examples/wifi/tcpclient/espat.go deleted file mode 100644 index 1ae8c1a30..000000000 --- a/examples/wifi/tcpclient/espat.go +++ /dev/null @@ -1,27 +0,0 @@ -//go:build espat - -package main - -import ( - "machine" - "tinygo.org/x/drivers/espat" -) - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - uart = machine.UART1 - tx = machine.PA22 - rx = machine.PA23 - - adaptor *espat.Device -) - -func initAdaptor() *espat.Device { - uart.Configure(machine.UARTConfig{TX: tx, RX: rx}) - - adaptor = espat.New(uart) - adaptor.Configure() - - return adaptor -} diff --git a/examples/wifi/tcpclient/rtl8720dn.go b/examples/wifi/tcpclient/rtl8720dn.go deleted file mode 100644 index 7a3078995..000000000 --- a/examples/wifi/tcpclient/rtl8720dn.go +++ /dev/null @@ -1,76 +0,0 @@ -//go:build rtl8720dn - -package main - -import ( - "device/sam" - "machine" - "runtime/interrupt" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/rtl8720dn" -) - -var ( - adaptor *rtl8720dn.RTL8720DN - - uart UARTx -) - -func initAdaptor() *rtl8720dn.RTL8720DN { - adaptor, err := setupRTL8720DN() - if err != nil { - return nil - } - net.UseDriver(adaptor) - - return adaptor -} - -func handleInterrupt(interrupt.Interrupt) { - // should reset IRQ - uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF))) - uart.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INT_INTFLAG_RXC) -} - -func setupRTL8720DN() (*rtl8720dn.RTL8720DN, error) { - machine.RTL8720D_CHIP_PU.Configure(machine.PinConfig{Mode: machine.PinOutput}) - machine.RTL8720D_CHIP_PU.Low() - time.Sleep(100 * time.Millisecond) - machine.RTL8720D_CHIP_PU.High() - time.Sleep(1000 * time.Millisecond) - - uart = UARTx{ - UART: &machine.UART{ - Buffer: machine.NewRingBuffer(), - Bus: sam.SERCOM0_USART_INT, - SERCOM: 0, - }, - } - - uart.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, handleInterrupt) - uart.Configure(machine.UARTConfig{TX: machine.PB24, RX: machine.PC24, BaudRate: 614400}) - - rtl := rtl8720dn.New(uart) - //rtl.Debug(debug) - - _, err := rtl.Rpc_tcpip_adapter_init() - if err != nil { - return nil, err - } - - return rtl, nil -} - -type UARTx struct { - *machine.UART -} - -func (u UARTx) Read(p []byte) (n int, err error) { - if u.Buffered() == 0 { - time.Sleep(1 * time.Millisecond) - return 0, nil - } - return u.UART.Read(p) -} diff --git a/examples/wifi/tcpclient/wifinina.go b/examples/wifi/tcpclient/wifinina.go deleted file mode 100644 index a6308765a..000000000 --- a/examples/wifi/tcpclient/wifinina.go +++ /dev/null @@ -1,35 +0,0 @@ -//go:build wifinina - -package main - -import ( - "machine" - "tinygo.org/x/drivers/wifinina" -) - -var ( - // default interface for the Arduino Nano33 IoT. - spi = machine.NINA_SPI - - // ESP32/ESP8266 chip that has the WIFININA firmware flashed on it - adaptor *wifinina.Device -) - -func initAdaptor() *wifinina.Device { - // Configure SPI for 8Mhz, Mode 0, MSB First - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - adaptor = wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() - - return adaptor -} diff --git a/examples/wifinina/connect/main.go b/examples/wifinina/connect/main.go deleted file mode 100644 index e758ce4f6..000000000 --- a/examples/wifinina/connect/main.go +++ /dev/null @@ -1,144 +0,0 @@ -// This example connects to Access Point and prints some info -package main - -import ( - "machine" - "strconv" - "time" - - "tinygo.org/x/drivers/wifinina" -) - -var ( - // access point info - ssid string - pass string -) - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - - // these are the default pins for the Arduino Nano33 IoT. - spi = machine.NINA_SPI - - // this is the ESP chip that has the WIFININA firmware flashed on it - adaptor *wifinina.Device -) - -func setup() { - - // Configure SPI for 8Mhz, Mode 0, MSB First - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - adaptor = wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() -} - -func main() { - - setup() - - waitSerial() - - connectToAP() - - for { - println("----------------------------------------") - printSSID() - printRSSI() - printMac() - printIPs() - printTime() - time.Sleep(10 * time.Second) - } - -} - -func printSSID() { - print("SSID: ") - ssid, err := adaptor.GetCurrentSSID() - if err != nil { - println("Unknown (error: ", err.Error(), ")") - return - } - println(ssid) -} - -func printRSSI() { - print("RSSI: ") - rssi, err := adaptor.GetCurrentRSSI() - if err != nil { - println("Unknown (error: ", err.Error(), ")") - return - } - println(strconv.Itoa(int(rssi))) -} - -func printIPs() { - ip, subnet, gateway, err := adaptor.GetIP() - if err != nil { - println("IP: Unknown (error: ", err.Error(), ")") - return - } - println("IP: ", ip.String()) - println("Subnet: ", subnet.String()) - println("Gateway: ", gateway.String()) -} - -func printTime() { - print("Time: ") - t, err := adaptor.GetTime() - for { - if err != nil { - println("Unknown (error: ", err.Error(), ")") - return - } - if t != 0 { - break - } - time.Sleep(time.Second) - t, err = adaptor.GetTime() - } - println(time.Unix(int64(t), 0).String()) -} - -func printMac() { - print("MAC: ") - mac, err := adaptor.GetMACAddress() - if err != nil { - println("Unknown (", err.Error(), ")") - } - println(mac.String()) -} - -// Wait for user to open serial console -func waitSerial() { - for !machine.Serial.DTR() { - time.Sleep(100 * time.Millisecond) - } -} - -// connect to access point -func connectToAP() { - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { // error connecting to AP - for { - println(err) - time.Sleep(1 * time.Second) - } - } - - println("Connected.") -} diff --git a/examples/wifinina/http-get/main.go b/examples/wifinina/http-get/main.go deleted file mode 100644 index cc44142ab..000000000 --- a/examples/wifinina/http-get/main.go +++ /dev/null @@ -1,158 +0,0 @@ -// This example opens a TCP connection using a device with WiFiNINA firmware -// and sends a HTTP request to retrieve a webpage, based on the following -// Arduino example: -// -// https://github.com/arduino-libraries/WiFiNINA/blob/master/examples/WiFiWebClientRepeating/ -// -// This example will not work with samd21 or other systems with less than 32KB -// of RAM. Use the following if you want to run wifinina on samd21, etc. -// -// examples/wifinina/webclient -// examples/wifinina/tlsclient -package main - -import ( - "bufio" - "fmt" - "machine" - "strings" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/net/http" - "tinygo.org/x/drivers/wifinina" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the server aka "hub". Replace with your own info. -// Can specify a URL starting with http or https -const url = "http://tinygo.org/" - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - - // these are the default pins for the Arduino Nano33 IoT. - spi = machine.NINA_SPI - - // this is the ESP chip that has the WIFININA firmware flashed on it - adaptor *wifinina.Device -) - -var buf [0x400]byte - -var lastRequestTime time.Time -var conn net.Conn - -func setup() { - // Configure SPI for 8Mhz, Mode 0, MSB First - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - adaptor = wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() -} - -func main() { - - setup() - http.SetBuf(buf[:]) - - waitSerial() - - connectToAP() - - // You can send and receive cookies in the following way - // import "tinygo.org/x/drivers/net/http/cookiejar" - // jar, err := cookiejar.New(nil) - // if err != nil { - // return err - // } - // client := &http.Client{Jar: jar} - // http.DefaultClient = client - - cnt := 0 - for { - // Various examples are as follows - // - // -- Get - // resp, err := http.Get(url) - // - // -- Post - // body := `cnt=12` - // resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(body)) - // - // -- Post with JSON - // body := `{"msg": "hello"}` - // resp, err := http.Post(url, "application/json", strings.NewReader(body)) - - resp, err := http.Get(url) - if err != nil { - fmt.Printf("%s\r\n", err.Error()) - continue - } - - fmt.Printf("%s %s\r\n", resp.Proto, resp.Status) - for k, v := range resp.Header { - fmt.Printf("%s: %s\r\n", k, strings.Join(v, " ")) - } - fmt.Printf("\r\n") - - scanner := bufio.NewScanner(resp.Body) - for scanner.Scan() { - fmt.Printf("%s\r\n", scanner.Text()) - } - resp.Body.Close() - - cnt++ - fmt.Printf("-------- %d --------\r\n", cnt) - time.Sleep(10 * time.Second) - } - -} - -// Wait for user to open serial console -func waitSerial() { - for !machine.Serial.DTR() { - time.Sleep(100 * time.Millisecond) - } -} - -// connect to access point -func connectToAP() { - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { // error connecting to AP - for { - println(err) - time.Sleep(1 * time.Second) - } - } - - println("Connected.") - - ip, _, _, err := adaptor.GetIP() - for ; err != nil; ip, _, _, err = adaptor.GetIP() { - message(err.Error()) - time.Sleep(1 * time.Second) - } - message(ip.String()) -} - -func message(msg string) { - println(msg, "\r") -} diff --git a/examples/wifinina/mqttclient/main.go b/examples/wifinina/mqttclient/main.go deleted file mode 100644 index c03ed92cb..000000000 --- a/examples/wifinina/mqttclient/main.go +++ /dev/null @@ -1,145 +0,0 @@ -// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1. -// It creates an MQTT connection that publishes a message every second -// to an MQTT broker. -// -// In other words: -// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> Internet <--> MQTT broker. -// -// You must install the Paho MQTT package to build this program: -// -// go get -u github.com/eclipse/paho.mqtt.golang -package main - -import ( - "fmt" - "machine" - "math/rand" - "time" - - "tinygo.org/x/drivers/net/mqtt" - "tinygo.org/x/drivers/wifinina" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the MQTT broker to use. Replace with your own info. -const server = "tcp://test.mosquitto.org:1883" - -//const server = "ssl://test.mosquitto.org:8883" - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - - // these are the default pins for the Arduino Nano33 IoT. - spi = machine.NINA_SPI - - // this is the ESP chip that has the WIFININA firmware flashed on it - adaptor *wifinina.Device - topic = "tinygo" -) - -func main() { - time.Sleep(3000 * time.Millisecond) - - rand.Seed(time.Now().UnixNano()) - - // Configure SPI for 8Mhz, Mode 0, MSB First - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - // Init esp8266/esp32 - adaptor = wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() - - connectToAP() - - opts := mqtt.NewClientOptions() - opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10)) - - println("Connectng to MQTT...") - cl := mqtt.NewClient(opts) - if token := cl.Connect(); token.Wait() && token.Error() != nil { - failMessage(token.Error().Error()) - } - - for i := 0; ; i++ { - println("Publishing MQTT message...") - data := []byte(fmt.Sprintf(`{"e":[{"n":"hello %d","v":101}]}`, i)) - token := cl.Publish(topic, 0, false, data) - token.Wait() - if err := token.Error(); err != nil { - switch t := err.(type) { - case wifinina.Error: - println(t.Error(), "attempting to reconnect") - if token := cl.Connect(); token.Wait() && token.Error() != nil { - failMessage(token.Error().Error()) - } - default: - println(err.Error()) - } - } - time.Sleep(100 * time.Millisecond) - } - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting MQTT...") - cl.Disconnect(100) - - println("Done.") -} - -// connect to access point -func connectToAP() { - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { // error connecting to AP - for { - println(err) - time.Sleep(1 * time.Second) - } - } - - println("Connected.") - - ip, _, _, err := adaptor.GetIP() - for ; err != nil; ip, _, _, err = adaptor.GetIP() { - println(err.Error()) - time.Sleep(1 * time.Second) - } - println(ip.String()) -} - -// Returns an int >= min, < max -func randomInt(min, max int) int { - return min + rand.Intn(max-min) -} - -// Generate a random string of A-Z chars with len = l -func randomString(len int) string { - bytes := make([]byte, len) - for i := 0; i < len; i++ { - bytes[i] = byte(randomInt(65, 90)) - } - return string(bytes) -} - -func failMessage(msg string) { - for { - println(msg) - time.Sleep(1 * time.Second) - } -} diff --git a/examples/wifinina/mqttsub/main.go b/examples/wifinina/mqttsub/main.go deleted file mode 100644 index b19ed1ecf..000000000 --- a/examples/wifinina/mqttsub/main.go +++ /dev/null @@ -1,158 +0,0 @@ -// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1. -// It creates an MQTT connection that publishes a message every second -// to an MQTT broker. -// -// In other words: -// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> Internet <--> MQTT broker. -// -// You must also install the Paho MQTT package to build this program: -// -// go get -u github.com/eclipse/paho.mqtt.golang -package main - -import ( - "fmt" - "machine" - "math/rand" - "time" - - "tinygo.org/x/drivers/net/mqtt" - "tinygo.org/x/drivers/wifinina" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the MQTT broker to use. Replace with your own info. -const server = "tcp://test.mosquitto.org:1883" - -//const server = "ssl://test.mosquitto.org:8883" - -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - // these are the default pins for the Arduino Nano33 IoT. - spi = machine.NINA_SPI - - // this is the ESP chip that has the WIFININA firmware flashed on it - adaptor *wifinina.Device - - cl mqtt.Client - topicTx = "tinygo/tx" - topicRx = "tinygo/rx" -) - -func subHandler(client mqtt.Client, msg mqtt.Message) { - fmt.Printf("[%s] ", msg.Topic()) - fmt.Printf("%s\r\n", msg.Payload()) -} - -func main() { - time.Sleep(3000 * time.Millisecond) - - rand.Seed(time.Now().UnixNano()) - - // Configure SPI for 8Mhz, Mode 0, MSB First - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - // Init esp8266/esp32 - adaptor = wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() - - connectToAP() - - opts := mqtt.NewClientOptions() - opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10)) - - println("Connecting to MQTT broker at", server) - cl = mqtt.NewClient(opts) - if token := cl.Connect(); token.Wait() && token.Error() != nil { - failMessage(token.Error().Error()) - } - - // subscribe - token := cl.Subscribe(topicRx, 0, subHandler) - token.Wait() - if token.Error() != nil { - failMessage(token.Error().Error()) - } - - go publishing() - - select {} - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting MQTT...") - cl.Disconnect(100) - - println("Done.") -} - -func publishing() { - for i := 0; ; i++ { - println("Publishing MQTT message...") - data := []byte(fmt.Sprintf(`{"e":[{"n":"hello %d","v":101}]}`, i)) - token := cl.Publish(topicRx, 0, false, data) - token.Wait() - if token.Error() != nil { - println(token.Error().Error()) - } - - time.Sleep(100 * time.Millisecond) - } -} - -// connect to access point -func connectToAP() { - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { // error connecting to AP - for { - println(err) - time.Sleep(1 * time.Second) - } - } - - println("Connected.") - - time.Sleep(2 * time.Second) - ip, _, _, err := adaptor.GetIP() - for ; err != nil; ip, _, _, err = adaptor.GetIP() { - println(err.Error()) - time.Sleep(1 * time.Second) - } - println(ip.String()) -} - -// Returns an int >= min, < max -func randomInt(min, max int) int { - return min + rand.Intn(max-min) -} - -// Generate a random string of A-Z chars with len = l -func randomString(len int) string { - bytes := make([]byte, len) - for i := 0; i < len; i++ { - bytes[i] = byte(randomInt(65, 90)) - } - return string(bytes) -} - -func failMessage(msg string) { - for { - println(msg) - time.Sleep(1 * time.Second) - } -} diff --git a/examples/wifinina/ntpclient/main.go b/examples/wifinina/ntpclient/main.go deleted file mode 100644 index 606e54bbf..000000000 --- a/examples/wifinina/ntpclient/main.go +++ /dev/null @@ -1,177 +0,0 @@ -// This is an example of using the wifinina driver to implement a NTP client. -// It creates a UDP connection to request the current time and parse the -// response from a NTP server. -package main - -import ( - "errors" - "fmt" - "machine" - "runtime" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/wifinina" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the server aka "hub". Replace with your own info. -const ntpHost = "129.6.15.29" - -const NTP_PACKET_SIZE = 48 - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - - // these are the default pins for the Arduino Nano33 IoT. - spi = machine.NINA_SPI - - // this is the ESP chip that has the WIFININA firmware flashed on it - adaptor *wifinina.Device - b = make([]byte, NTP_PACKET_SIZE) -) - -func setup() { - // Configure SPI for 8Mhz, Mode 0, MSB First - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - adaptor = wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() -} - -func main() { - - setup() - - waitSerial() - - connectToAP() - - // now make UDP connection - ip := net.ParseIP(ntpHost) - raddr := &net.UDPAddr{IP: ip, Port: 123} - laddr := &net.UDPAddr{Port: 2390} - conn, err := net.DialUDP("udp", laddr, raddr) - if err != nil { - for { - time.Sleep(time.Second) - println(err) - } - } - - for { - // send data - println("Requesting NTP time...") - t, err := getCurrentTime(conn) - if err != nil { - message("Error getting current time: %v", err) - } else { - message("NTP time: %v", t) - } - runtime.AdjustTimeOffset(-1 * int64(time.Since(t))) - for i := 0; i < 10; i++ { - message("Current time: %v", time.Now()) - time.Sleep(1 * time.Second) - } - } - -} - -// Wait for user to open serial console -func waitSerial() { - for !machine.Serial.DTR() { - time.Sleep(100 * time.Millisecond) - } -} - -func getCurrentTime(conn *net.UDPSerialConn) (time.Time, error) { - if err := sendNTPpacket(conn); err != nil { - return time.Time{}, err - } - clearBuffer() - for now := time.Now(); time.Since(now) < time.Second; { - time.Sleep(5 * time.Millisecond) - if n, err := conn.Read(b); err != nil { - return time.Time{}, fmt.Errorf("error reading UDP packet: %w", err) - } else if n == 0 { - continue // no packet received yet - } else if n != NTP_PACKET_SIZE { - return time.Time{}, fmt.Errorf("expected NTP packet size of %d: %d", NTP_PACKET_SIZE, n) - } - return parseNTPpacket(), nil - } - return time.Time{}, errors.New("no packet received after 1 second") -} - -func sendNTPpacket(conn *net.UDPSerialConn) error { - clearBuffer() - b[0] = 0b11100011 // LI, Version, Mode - b[1] = 0 // Stratum, or type of clock - b[2] = 6 // Polling Interval - b[3] = 0xEC // Peer Clock Precision - // 8 bytes of zero for Root Delay & Root Dispersion - b[12] = 49 - b[13] = 0x4E - b[14] = 49 - b[15] = 52 - if _, err := conn.Write(b); err != nil { - return err - } - return nil -} - -func parseNTPpacket() time.Time { - // the timestamp starts at byte 40 of the received packet and is four bytes, - // this is NTP time (seconds since Jan 1 1900): - t := uint32(b[40])<<24 | uint32(b[41])<<16 | uint32(b[42])<<8 | uint32(b[43]) - const seventyYears = 2208988800 - return time.Unix(int64(t-seventyYears), 0) -} - -func clearBuffer() { - for i := range b { - b[i] = 0 - } -} - -// connect to access point -func connectToAP() { - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { // error connecting to AP - for { - println(err) - time.Sleep(1 * time.Second) - } - } - - println("Connected.") - - time.Sleep(2 * time.Second) - ip, _, _, err := adaptor.GetIP() - for ; err != nil; ip, _, _, err = adaptor.GetIP() { - message(err.Error()) - time.Sleep(1 * time.Second) - } - message(ip.String()) -} - -func message(format string, args ...interface{}) { - println(fmt.Sprintf(format, args...), "\r") -} diff --git a/examples/wifinina/pins/main.go b/examples/wifinina/pins/main.go deleted file mode 100644 index 0285e6f05..000000000 --- a/examples/wifinina/pins/main.go +++ /dev/null @@ -1,90 +0,0 @@ -//go:build nano_rp2040 - -// This examples shows how to control RGB LED connected to -// NINA-W102 chip on Arduino Nano RP2040 Connect board -// Built-in LED code added for API comparison - -package main - -import ( - "machine" - "time" - - "tinygo.org/x/drivers/wifinina" -) - -const ( - LED = machine.LED - - // Arduino Nano RP2040 Connect board RGB LED pins - // See https://docs.arduino.cc/static/3525d638b5c76a2d19588d6b41cd02a0/ABX00053-full-pinout.pdf - LED_R wifinina.Pin = 27 - LED_G wifinina.Pin = 25 - LED_B wifinina.Pin = 26 -) - -var ( - - // these are the default pins for the Arduino Nano-RP2040 Connect - spi = machine.NINA_SPI - - // this is the ESP chip that has the WIFININA firmware flashed on it - device *wifinina.Device -) - -func setup() { - - // Configure SPI for 8Mhz, Mode 0, MSB First - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - device = wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - device.Configure() - - time.Sleep(time.Second) - - LED.Configure(machine.PinConfig{Mode: machine.PinOutput}) - LED_R.Configure(wifinina.PinConfig{Mode: wifinina.PinOutput}) - LED_G.Configure(wifinina.PinConfig{Mode: wifinina.PinOutput}) - LED_B.Configure(wifinina.PinConfig{Mode: wifinina.PinOutput}) -} - -func main() { - - setup() - - LED.Low() // OFF - LED_R.High() // OFF - LED_G.High() // OFF - LED_B.High() // OFF - - go func() { - for { - LED.Low() - time.Sleep(time.Second) - LED.High() - time.Sleep(time.Second) - } - }() - - for { - LED_R.Low() // ON - time.Sleep(time.Second) - LED_R.High() // OFF - LED_G.Low() // ON - time.Sleep(time.Second) - LED_G.High() // OFF - LED_B.Low() // ON - time.Sleep(time.Second) - LED_B.High() // OFF - } - -} diff --git a/examples/wifinina/tcpclient/main.go b/examples/wifinina/tcpclient/main.go deleted file mode 100644 index a51a91006..000000000 --- a/examples/wifinina/tcpclient/main.go +++ /dev/null @@ -1,139 +0,0 @@ -// This example opens a TCP connection using a device with WiFiNINA firmware -// and sends some data, for the purpose of testing speed and connectivity. -// -// You can open a server to accept connections from this program using: -// -// nc -w 5 -lk 8080 -package main - -import ( - "bytes" - "fmt" - "machine" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/wifinina" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the server aka "hub". Replace with your own info. -const serverIP = "" - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - - // these are the default pins for the Arduino Nano33 IoT. - spi = machine.NINA_SPI - - // this is the ESP chip that has the WIFININA firmware flashed on it - adaptor *wifinina.Device -) - -var buf = &bytes.Buffer{} - -func main() { - - // Configure SPI for 8Mhz, Mode 0, MSB First - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - adaptor = wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() - - connectToAP() - - for { - sendBatch() - time.Sleep(500 * time.Millisecond) - } - println("Done.") -} - -func sendBatch() { - - // make TCP connection - ip := net.ParseIP(serverIP) - raddr := &net.TCPAddr{IP: ip, Port: 8080} - laddr := &net.TCPAddr{Port: 8080} - - message("---------------\r\nDialing TCP connection") - conn, err := net.DialTCP("tcp", laddr, raddr) - for ; err != nil; conn, err = net.DialTCP("tcp", laddr, raddr) { - message(err.Error()) - time.Sleep(5 * time.Second) - } - - n := 0 - w := 0 - start := time.Now() - - // send data - message("Sending data") - - for i := 0; i < 1000; i++ { - buf.Reset() - fmt.Fprint(buf, - "\r---------------------------- i == ", i, " ----------------------------"+ - "\r---------------------------- i == ", i, " ----------------------------") - if w, err = conn.Write(buf.Bytes()); err != nil { - println("error:", err.Error(), "\r") - continue - } - n += w - } - - buf.Reset() - ms := time.Now().Sub(start).Milliseconds() - fmt.Fprint(buf, "\nWrote ", n, " bytes in ", ms, " ms\r\n") - message(buf.String()) - - if _, err := conn.Write(buf.Bytes()); err != nil { - println("error:", err.Error(), "\r") - } - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting TCP...") - conn.Close() -} - -// connect to access point -func connectToAP() { - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { // error connecting to AP - for { - println(err) - time.Sleep(1 * time.Second) - } - } - - println("Connected.") - - time.Sleep(2 * time.Second) - ip, _, _, err := adaptor.GetIP() - for ; err != nil; ip, _, _, err = adaptor.GetIP() { - message(err.Error()) - time.Sleep(1 * time.Second) - } - message(ip.String()) -} - -func message(msg string) { - println(msg, "\r") -} diff --git a/examples/wifinina/tlsclient/main.go b/examples/wifinina/tlsclient/main.go deleted file mode 100644 index 9e50e8970..000000000 --- a/examples/wifinina/tlsclient/main.go +++ /dev/null @@ -1,149 +0,0 @@ -// This example opens a TCP connection using a device with WiFiNINA firmware -// and sends a HTTPS request to retrieve a webpage -// -// You shall see "strict-transport-security" header in the response, -// this confirms communication is indeed over HTTPS -// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security -package main - -import ( - "fmt" - "machine" - "strings" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/net/tls" - "tinygo.org/x/drivers/wifinina" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the server aka "hub". Replace with your own info. -const server = "tinygo.org" - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - - // these are the default pins for the Arduino Nano33 IoT. - spi = machine.NINA_SPI - - // this is the ESP chip that has the WIFININA firmware flashed on it - adaptor *wifinina.Device -) - -var buf [256]byte - -var lastRequestTime time.Time -var conn net.Conn - -func setup() { - // Configure SPI for 8Mhz, Mode 0, MSB First - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - adaptor = wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() -} - -func main() { - - setup() - - waitSerial() - - connectToAP() - - for { - readConnection() - if time.Now().Sub(lastRequestTime).Milliseconds() >= 10000 { - makeHTTPSRequest() - } - } - -} - -// Wait for user to open serial console -func waitSerial() { - for !machine.Serial.DTR() { - time.Sleep(100 * time.Millisecond) - } -} - -func readConnection() { - if conn != nil { - for n, err := conn.Read(buf[:]); n > 0; n, err = conn.Read(buf[:]) { - if err != nil { - println("Read error: " + err.Error()) - } else { - print(string(buf[0:n])) - } - } - } -} - -func makeHTTPSRequest() { - - var err error - if conn != nil { - conn.Close() - } - - message("\r\n---------------\r\nDialing TCP connection") - conn, err = tls.Dial("tcp", server, nil) - for ; err != nil; conn, err = tls.Dial("tcp", server, nil) { - message("Connection failed: " + err.Error()) - time.Sleep(5 * time.Second) - } - println("Connected!\r") - - print("Sending HTTPS request...") - fmt.Fprintln(conn, "GET / HTTP/1.1") - fmt.Fprintln(conn, "Host:", strings.Split(server, ":")[0]) - fmt.Fprintln(conn, "User-Agent: TinyGo") - fmt.Fprintln(conn, "Connection: close") - fmt.Fprintln(conn) - println("Sent!\r\n\r") - - lastRequestTime = time.Now() -} - -// connect to access point -func connectToAP() { - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { // error connecting to AP - for { - println(err) - time.Sleep(1 * time.Second) - } - } - - println("Connected.") - - time.Sleep(2 * time.Second) - ip, _, _, err := adaptor.GetIP() - for ; err != nil; ip, _, _, err = adaptor.GetIP() { - message(err.Error()) - time.Sleep(1 * time.Second) - } - message(ip.String()) -} - -func message(msg string) { - println(msg, "\r") -} diff --git a/examples/wifinina/udpstation/main.go b/examples/wifinina/udpstation/main.go deleted file mode 100644 index 7c921068e..000000000 --- a/examples/wifinina/udpstation/main.go +++ /dev/null @@ -1,101 +0,0 @@ -// This is a sensor station that uses a ESP32 running nina-fw over SPI. -// It creates a UDP connection you can use to get info to/from your computer via the microcontroller. -// -// In other words: -// Your computer <--> UART0 <--> MCU <--> SPI <--> ESP32 -package main - -import ( - "machine" - "strconv" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/wifinina" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the server aka "hub". Replace with your own info. -const hubIP = "" - -var ( - // this is the ESP chip that has the WIFININA firmware flashed on it - adaptor *wifinina.Device -) - -func main() { - - // Init esp8266/esp32 - // Configure SPI for 8Mhz, Mode 0, MSB First - machine.NINA_SPI.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - // these are the default pins for the Arduino Nano33 IoT. - // change these to connect to a different UART or pins for the ESP8266/ESP32 - adaptor = wifinina.New(machine.NINA_SPI, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() - - // connect to access point - connectToAP() - - // now make UDP connection - ip := net.ParseIP(hubIP) - raddr := &net.UDPAddr{IP: ip, Port: 2222} - laddr := &net.UDPAddr{Port: 2222} - - println("Dialing UDP connection...") - conn, _ := net.DialUDP("udp", laddr, raddr) - - for { - // send data - println("Sending data...") - for i := 0; i < 25; i++ { - conn.Write([]byte("hello " + strconv.Itoa(i) + "\r\n")) - } - time.Sleep(1000 * time.Millisecond) - } - - // Right now this code is never reached. Need a way to trigger it... - println("Disconnecting UDP...") - conn.Close() - println("Done.") -} - -// connect to access point -func connectToAP() { - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { // error connecting to AP - for { - println(err) - time.Sleep(1 * time.Second) - } - } - - println("Connected.") - - ip, _, _, err := adaptor.GetIP() - for ; err != nil; ip, _, _, err = adaptor.GetIP() { - message(err.Error()) - time.Sleep(1 * time.Second) - } - message(ip.String()) -} - -func message(msg string) { - println(msg, "\r") -} diff --git a/examples/wifinina/webclient/main.go b/examples/wifinina/webclient/main.go deleted file mode 100644 index e8968fefd..000000000 --- a/examples/wifinina/webclient/main.go +++ /dev/null @@ -1,150 +0,0 @@ -// This example opens a TCP connection using a device with WiFiNINA firmware -// and sends a HTTP request to retrieve a webpage, based on the following -// Arduino example: -// -// https://github.com/arduino-libraries/WiFiNINA/blob/master/examples/WiFiWebClientRepeating/ -package main - -import ( - "fmt" - "machine" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/wifinina" -) - -var ( - // access point info - ssid string - pass string -) - -// IP address of the server aka "hub". Replace with your own info. -const server = "tinygo.org" - -// these are the default pins for the Arduino Nano33 IoT. -// change these to connect to a different UART or pins for the ESP8266/ESP32 -var ( - - // these are the default pins for the Arduino Nano33 IoT. - spi = machine.NINA_SPI - - // this is the ESP chip that has the WIFININA firmware flashed on it - adaptor *wifinina.Device -) - -var buf [256]byte - -var lastRequestTime time.Time -var conn net.Conn - -func setup() { - // Configure SPI for 8Mhz, Mode 0, MSB First - spi.Configure(machine.SPIConfig{ - Frequency: 8 * 1e6, - SDO: machine.NINA_SDO, - SDI: machine.NINA_SDI, - SCK: machine.NINA_SCK, - }) - - adaptor = wifinina.New(spi, - machine.NINA_CS, - machine.NINA_ACK, - machine.NINA_GPIO0, - machine.NINA_RESETN) - adaptor.Configure() -} - -func main() { - - setup() - - waitSerial() - - connectToAP() - - for { - readConnection() - if time.Now().Sub(lastRequestTime).Milliseconds() >= 10000 { - makeHTTPRequest() - } - } - -} - -// Wait for user to open serial console -func waitSerial() { - for !machine.Serial.DTR() { - time.Sleep(100 * time.Millisecond) - } -} - -func readConnection() { - if conn != nil { - for n, err := conn.Read(buf[:]); n > 0; n, err = conn.Read(buf[:]) { - if err != nil { - println("Read error: " + err.Error()) - } else { - print(string(buf[0:n])) - } - } - } -} - -func makeHTTPRequest() { - - var err error - if conn != nil { - conn.Close() - } - - // make TCP connection - ip := net.ParseIP(server) - raddr := &net.TCPAddr{IP: ip, Port: 80} - laddr := &net.TCPAddr{Port: 8080} - - message("\r\n---------------\r\nDialing TCP connection") - conn, err = net.DialTCP("tcp", laddr, raddr) - for ; err != nil; conn, err = net.DialTCP("tcp", laddr, raddr) { - message("Connection failed: " + err.Error()) - time.Sleep(5 * time.Second) - } - println("Connected!\r") - - print("Sending HTTP request...") - fmt.Fprintln(conn, "GET / HTTP/1.1") - fmt.Fprintln(conn, "Host:", server) - fmt.Fprintln(conn, "User-Agent: TinyGo") - fmt.Fprintln(conn, "Connection: close") - fmt.Fprintln(conn) - println("Sent!\r\n\r") - - lastRequestTime = time.Now() -} - -// connect to access point -func connectToAP() { - time.Sleep(2 * time.Second) - println("Connecting to " + ssid) - err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second) - if err != nil { // error connecting to AP - for { - println(err) - time.Sleep(1 * time.Second) - } - } - - println("Connected.") - - ip, _, _, err := adaptor.GetIP() - for ; err != nil; ip, _, _, err = adaptor.GetIP() { - message(err.Error()) - time.Sleep(1 * time.Second) - } - message(ip.String()) -} - -func message(msg string) { - println(msg, "\r") -} diff --git a/net/adapter.go b/net/adapter.go deleted file mode 100644 index 501def85d..000000000 --- a/net/adapter.go +++ /dev/null @@ -1,44 +0,0 @@ -package net - -import ( - "errors" - "time" -) - -var ( - ErrWiFiMissingSSID = errors.New("missing SSID") - ErrWiFiConnectTimeout = errors.New("WiFi connect timeout") -) - -// Adapter interface is used to communicate with the network adapter. -type Adapter interface { - // functions used to connect/disconnect to/from an access point - ConnectToAccessPoint(ssid, pass string, timeout time.Duration) error - Disconnect() error - GetClientIP() (string, error) - - // these functions are used once the adapter is connected to the network - GetDNS(domain string) (string, error) - ConnectTCPSocket(addr, port string) error - ConnectSSLSocket(addr, port string) error - ConnectUDPSocket(addr, sendport, listenport string) error - DisconnectSocket() error - StartSocketSend(size int) error - Write(b []byte) (n int, err error) - ReadSocket(b []byte) (n int, err error) - IsSocketDataAvailable() bool - - // FIXME: this is really specific to espat, and maybe shouldn't be part - // of the driver interface - Response(timeout int) ([]byte, error) -} - -var ActiveDevice Adapter - -func UseDriver(a Adapter) { - // TODO: rethink and refactor this - if ActiveDevice != nil { - panic("net.ActiveDevice is already set") - } - ActiveDevice = a -} diff --git a/net/http/client.go b/net/http/client.go deleted file mode 100644 index ab1d81dfe..000000000 --- a/net/http/client.go +++ /dev/null @@ -1,253 +0,0 @@ -package http - -import ( - "io" - "net/url" - "strings" - "time" -) - -// A Client is an HTTP client. Its zero value (DefaultClient) is a -// usable client that uses DefaultTransport. -// -// The Client's Transport typically has internal state (cached TCP -// connections), so Clients should be reused instead of created as -// needed. Clients are safe for concurrent use by multiple goroutines. -// -// A Client is higher-level than a RoundTripper (such as Transport) -// and additionally handles HTTP details such as cookies and -// redirects. -// -// When following redirects, the Client will forward all headers set on the -// initial Request except: -// -// • when forwarding sensitive headers like "Authorization", -// "WWW-Authenticate", and "Cookie" to untrusted targets. -// These headers will be ignored when following a redirect to a domain -// that is not a subdomain match or exact match of the initial domain. -// For example, a redirect from "foo.com" to either "foo.com" or "sub.foo.com" -// will forward the sensitive headers, but a redirect to "bar.com" will not. -// -// • when forwarding the "Cookie" header with a non-nil cookie Jar. -// Since each redirect may mutate the state of the cookie jar, -// a redirect may possibly alter a cookie set in the initial request. -// When forwarding the "Cookie" header, any mutated cookies will be omitted, -// with the expectation that the Jar will insert those mutated cookies -// with the updated values (assuming the origin matches). -// If Jar is nil, the initial cookies are forwarded without change. -type Client struct { - // Transport specifies the mechanism by which individual - // HTTP requests are made. - // If nil, DefaultTransport is used. - Transport RoundTripper - - // CheckRedirect specifies the policy for handling redirects. - // If CheckRedirect is not nil, the client calls it before - // following an HTTP redirect. The arguments req and via are - // the upcoming request and the requests made already, oldest - // first. If CheckRedirect returns an error, the Client's Get - // method returns both the previous Response (with its Body - // closed) and CheckRedirect's error (wrapped in a url.Error) - // instead of issuing the Request req. - // As a special case, if CheckRedirect returns ErrUseLastResponse, - // then the most recent response is returned with its body - // unclosed, along with a nil error. - // - // If CheckRedirect is nil, the Client uses its default policy, - // which is to stop after 10 consecutive requests. - CheckRedirect func(req *Request, via []*Request) error - - // Jar specifies the cookie jar. - // - // The Jar is used to insert relevant cookies into every - // outbound Request and is updated with the cookie values - // of every inbound Response. The Jar is consulted for every - // redirect that the Client follows. - // - // If Jar is nil, cookies are only sent if they are explicitly - // set on the Request. - Jar CookieJar - - // Timeout specifies a time limit for requests made by this - // Client. The timeout includes connection time, any - // redirects, and reading the response body. The timer remains - // running after Get, Head, Post, or Do return and will - // interrupt reading of the Response.Body. - // - // A Timeout of zero means no timeout. - // - // The Client cancels requests to the underlying Transport - // as if the Request's Context ended. - // - // For compatibility, the Client will also use the deprecated - // CancelRequest method on Transport if found. New - // RoundTripper implementations should use the Request's Context - // for cancellation instead of implementing CancelRequest. - Timeout time.Duration -} - -// DefaultClient is the default Client and is used by Get, Head, and Post. -var DefaultClient = &Client{} - -// RoundTripper is an interface representing the ability to execute a -// single HTTP transaction, obtaining the Response for a given Request. -// -// A RoundTripper must be safe for concurrent use by multiple -// goroutines. -type RoundTripper interface { - // RoundTrip executes a single HTTP transaction, returning - // a Response for the provided Request. - // - // RoundTrip should not attempt to interpret the response. In - // particular, RoundTrip must return err == nil if it obtained - // a response, regardless of the response's HTTP status code. - // A non-nil err should be reserved for failure to obtain a - // response. Similarly, RoundTrip should not attempt to - // handle higher-level protocol details such as redirects, - // authentication, or cookies. - // - // RoundTrip should not modify the request, except for - // consuming and closing the Request's Body. RoundTrip may - // read fields of the request in a separate goroutine. Callers - // should not mutate or reuse the request until the Response's - // Body has been closed. - // - // RoundTrip must always close the body, including on errors, - // but depending on the implementation may do so in a separate - // goroutine even after RoundTrip returns. This means that - // callers wanting to reuse the body for subsequent requests - // must arrange to wait for the Close call before doing so. - // - // The Request's URL and Header fields must be initialized. - RoundTrip(*Request) (*Response, error) -} - -// Get issues a GET to the specified URL. If the response is one of -// the following redirect codes, Get follows the redirect, up to a -// maximum of 10 redirects: -// -// 301 (Moved Permanently) -// 302 (Found) -// 303 (See Other) -// 307 (Temporary Redirect) -// 308 (Permanent Redirect) -// -// An error is returned if there were too many redirects or if there -// was an HTTP protocol error. A non-2xx response doesn't cause an -// error. Any returned error will be of type *url.Error. The url.Error -// value's Timeout method will report true if request timed out or was -// canceled. -// -// When err is nil, resp always contains a non-nil resp.Body. -// Caller should close resp.Body when done reading from it. -// -// Get is a wrapper around DefaultClient.Get. -// -// To make a request with custom headers, use NewRequest and -// DefaultClient.Do. -func Get(url string) (resp *Response, err error) { - return DefaultClient.Get(url) -} - -// Get issues a GET to the specified URL. If the response is one of the -// following redirect codes, Get follows the redirect after calling the -// Client's CheckRedirect function: -// -// 301 (Moved Permanently) -// 302 (Found) -// 303 (See Other) -// 307 (Temporary Redirect) -// 308 (Permanent Redirect) -// -// An error is returned if the Client's CheckRedirect function fails -// or if there was an HTTP protocol error. A non-2xx response doesn't -// cause an error. Any returned error will be of type *url.Error. The -// url.Error value's Timeout method will report true if the request -// timed out. -// -// When err is nil, resp always contains a non-nil resp.Body. -// Caller should close resp.Body when done reading from it. -// -// To make a request with custom headers, use NewRequest and Client.Do. -func (c *Client) Get(url string) (resp *Response, err error) { - req, err := NewRequest("GET", url, nil) - if err != nil { - return nil, err - } - return c.Do(req) -} - -// Post issues a POST to the specified URL. -// -// Caller should close resp.Body when done reading from it. -// -// If the provided body is an io.Closer, it is closed after the -// request. -// -// Post is a wrapper around DefaultClient.Post. -// -// To set custom headers, use NewRequest and DefaultClient.Do. -// -// See the Client.Do method documentation for details on how redirects -// are handled. -func Post(url, contentType string, body io.Reader) (resp *Response, err error) { - return DefaultClient.Post(url, contentType, body) -} - -// Post issues a POST to the specified URL. -// -// Caller should close resp.Body when done reading from it. -// -// If the provided body is an io.Closer, it is closed after the -// request. -// -// To set custom headers, use NewRequest and Client.Do. -// -// See the Client.Do method documentation for details on how redirects -// are handled. -func (c *Client) Post(url, contentType string, body io.Reader) (resp *Response, err error) { - req, err := NewRequest("POST", url, body) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", contentType) - return c.Do(req) -} - -// PostForm issues a POST to the specified URL, with data's keys and -// values URL-encoded as the request body. -// -// The Content-Type header is set to application/x-www-form-urlencoded. -// To set other headers, use NewRequest and DefaultClient.Do. -// -// When err is nil, resp always contains a non-nil resp.Body. -// Caller should close resp.Body when done reading from it. -// -// PostForm is a wrapper around DefaultClient.PostForm. -// -// See the Client.Do method documentation for details on how redirects -// are handled. -// -// To make a request with a specified context.Context, use NewRequestWithContext -// and DefaultClient.Do. -func PostForm(url string, data url.Values) (resp *Response, err error) { - return DefaultClient.PostForm(url, data) -} - -// PostForm issues a POST to the specified URL, -// with data's keys and values URL-encoded as the request body. -// -// The Content-Type header is set to application/x-www-form-urlencoded. -// To set other headers, use NewRequest and Client.Do. -// -// When err is nil, resp always contains a non-nil resp.Body. -// Caller should close resp.Body when done reading from it. -// -// See the Client.Do method documentation for details on how redirects -// are handled. -// -// To make a request with a specified context.Context, use NewRequestWithContext -// and Client.Do. -func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error) { - return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) -} diff --git a/net/http/cookie.go b/net/http/cookie.go deleted file mode 100644 index 2155289b7..000000000 --- a/net/http/cookie.go +++ /dev/null @@ -1,435 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package http - -import ( - "log" - "net" - "net/textproto" - "strconv" - "strings" - "time" -) - -// A Cookie represents an HTTP cookie as sent in the Set-Cookie header of an -// HTTP response or the Cookie header of an HTTP request. -// -// See https://tools.ietf.org/html/rfc6265 for details. -type Cookie struct { - Name string - Value string - - Path string // optional - Domain string // optional - Expires time.Time // optional - RawExpires string // for reading cookies only - - // MaxAge=0 means no 'Max-Age' attribute specified. - // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0' - // MaxAge>0 means Max-Age attribute present and given in seconds - MaxAge int - Secure bool - HttpOnly bool - SameSite SameSite - Raw string - Unparsed []string // Raw text of unparsed attribute-value pairs -} - -// SameSite allows a server to define a cookie attribute making it impossible for -// the browser to send this cookie along with cross-site requests. The main -// goal is to mitigate the risk of cross-origin information leakage, and provide -// some protection against cross-site request forgery attacks. -// -// See https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00 for details. -type SameSite int - -const ( - SameSiteDefaultMode SameSite = iota + 1 - SameSiteLaxMode - SameSiteStrictMode - SameSiteNoneMode -) - -// readSetCookies parses all "Set-Cookie" values from -// the header h and returns the successfully parsed Cookies. -func readSetCookies(h Header) []*Cookie { - cookieCount := len(h["Set-Cookie"]) - if cookieCount == 0 { - return []*Cookie{} - } - cookies := make([]*Cookie, 0, cookieCount) - for _, line := range h["Set-Cookie"] { - parts := strings.Split(textproto.TrimString(line), ";") - if len(parts) == 1 && parts[0] == "" { - continue - } - parts[0] = textproto.TrimString(parts[0]) - j := strings.Index(parts[0], "=") - if j < 0 { - continue - } - name, value := parts[0][:j], parts[0][j+1:] - if !isCookieNameValid(name) { - continue - } - value, ok := parseCookieValue(value, true) - if !ok { - continue - } - c := &Cookie{ - Name: name, - Value: value, - Raw: line, - } - for i := 1; i < len(parts); i++ { - parts[i] = textproto.TrimString(parts[i]) - if len(parts[i]) == 0 { - continue - } - - attr, val := parts[i], "" - if j := strings.Index(attr, "="); j >= 0 { - attr, val = attr[:j], attr[j+1:] - } - lowerAttr := strings.ToLower(attr) - val, ok = parseCookieValue(val, false) - if !ok { - c.Unparsed = append(c.Unparsed, parts[i]) - continue - } - switch lowerAttr { - case "samesite": - lowerVal := strings.ToLower(val) - switch lowerVal { - case "lax": - c.SameSite = SameSiteLaxMode - case "strict": - c.SameSite = SameSiteStrictMode - case "none": - c.SameSite = SameSiteNoneMode - default: - c.SameSite = SameSiteDefaultMode - } - continue - case "secure": - c.Secure = true - continue - case "httponly": - c.HttpOnly = true - continue - case "domain": - c.Domain = val - continue - case "max-age": - secs, err := strconv.Atoi(val) - if err != nil || secs != 0 && val[0] == '0' { - break - } - if secs <= 0 { - secs = -1 - } - c.MaxAge = secs - continue - case "expires": - c.RawExpires = val - exptime, err := time.Parse(time.RFC1123, val) - if err != nil { - exptime, err = time.Parse("Mon, 02-Jan-2006 15:04:05 MST", val) - if err != nil { - c.Expires = time.Time{} - break - } - } - c.Expires = exptime.UTC() - continue - case "path": - c.Path = val - continue - } - c.Unparsed = append(c.Unparsed, parts[i]) - } - cookies = append(cookies, c) - } - return cookies -} - -// SetCookie adds a Set-Cookie header to the provided ResponseWriter's headers. -// The provided cookie must have a valid Name. Invalid cookies may be -// silently dropped. -func SetCookie(w ResponseWriter, cookie *Cookie) { - if v := cookie.String(); v != "" { - w.Header().Add("Set-Cookie", v) - } -} - -// String returns the serialization of the cookie for use in a Cookie -// header (if only Name and Value are set) or a Set-Cookie response -// header (if other fields are set). -// If c is nil or c.Name is invalid, the empty string is returned. -func (c *Cookie) String() string { - if c == nil || !isCookieNameValid(c.Name) { - return "" - } - // extraCookieLength derived from typical length of cookie attributes - // see RFC 6265 Sec 4.1. - const extraCookieLength = 110 - var b strings.Builder - b.Grow(len(c.Name) + len(c.Value) + len(c.Domain) + len(c.Path) + extraCookieLength) - b.WriteString(c.Name) - b.WriteRune('=') - b.WriteString(sanitizeCookieValue(c.Value)) - - if len(c.Path) > 0 { - b.WriteString("; Path=") - b.WriteString(sanitizeCookiePath(c.Path)) - } - if len(c.Domain) > 0 { - if validCookieDomain(c.Domain) { - // A c.Domain containing illegal characters is not - // sanitized but simply dropped which turns the cookie - // into a host-only cookie. A leading dot is okay - // but won't be sent. - d := c.Domain - if d[0] == '.' { - d = d[1:] - } - b.WriteString("; Domain=") - b.WriteString(d) - } else { - log.Printf("net/http: invalid Cookie.Domain %q; dropping domain attribute", c.Domain) - } - } - var buf [len(TimeFormat)]byte - if validCookieExpires(c.Expires) { - b.WriteString("; Expires=") - b.Write(c.Expires.UTC().AppendFormat(buf[:0], TimeFormat)) - } - if c.MaxAge > 0 { - b.WriteString("; Max-Age=") - b.Write(strconv.AppendInt(buf[:0], int64(c.MaxAge), 10)) - } else if c.MaxAge < 0 { - b.WriteString("; Max-Age=0") - } - if c.HttpOnly { - b.WriteString("; HttpOnly") - } - if c.Secure { - b.WriteString("; Secure") - } - switch c.SameSite { - case SameSiteDefaultMode: - // Skip, default mode is obtained by not emitting the attribute. - case SameSiteNoneMode: - b.WriteString("; SameSite=None") - case SameSiteLaxMode: - b.WriteString("; SameSite=Lax") - case SameSiteStrictMode: - b.WriteString("; SameSite=Strict") - } - return b.String() -} - -// readCookies parses all "Cookie" values from the header h and -// returns the successfully parsed Cookies. -// -// if filter isn't empty, only cookies of that name are returned -func readCookies(h Header, filter string) []*Cookie { - lines := h["Cookie"] - if len(lines) == 0 { - return []*Cookie{} - } - - cookies := make([]*Cookie, 0, len(lines)+strings.Count(lines[0], ";")) - for _, line := range lines { - line = textproto.TrimString(line) - - var part string - for len(line) > 0 { // continue since we have rest - if splitIndex := strings.Index(line, ";"); splitIndex > 0 { - part, line = line[:splitIndex], line[splitIndex+1:] - } else { - part, line = line, "" - } - part = textproto.TrimString(part) - if len(part) == 0 { - continue - } - name, val := part, "" - if j := strings.Index(part, "="); j >= 0 { - name, val = name[:j], name[j+1:] - } - if !isCookieNameValid(name) { - continue - } - if filter != "" && filter != name { - continue - } - val, ok := parseCookieValue(val, true) - if !ok { - continue - } - cookies = append(cookies, &Cookie{Name: name, Value: val}) - } - } - return cookies -} - -// validCookieDomain reports whether v is a valid cookie domain-value. -func validCookieDomain(v string) bool { - if isCookieDomainName(v) { - return true - } - if net.ParseIP(v) != nil && !strings.Contains(v, ":") { - return true - } - return false -} - -// validCookieExpires reports whether v is a valid cookie expires-value. -func validCookieExpires(t time.Time) bool { - // IETF RFC 6265 Section 5.1.1.5, the year must not be less than 1601 - return t.Year() >= 1601 -} - -// isCookieDomainName reports whether s is a valid domain name or a valid -// domain name with a leading dot '.'. It is almost a direct copy of -// package net's isDomainName. -func isCookieDomainName(s string) bool { - if len(s) == 0 { - return false - } - if len(s) > 255 { - return false - } - - if s[0] == '.' { - // A cookie a domain attribute may start with a leading dot. - s = s[1:] - } - last := byte('.') - ok := false // Ok once we've seen a letter. - partlen := 0 - for i := 0; i < len(s); i++ { - c := s[i] - switch { - default: - return false - case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z': - // No '_' allowed here (in contrast to package net). - ok = true - partlen++ - case '0' <= c && c <= '9': - // fine - partlen++ - case c == '-': - // Byte before dash cannot be dot. - if last == '.' { - return false - } - partlen++ - case c == '.': - // Byte before dot cannot be dot, dash. - if last == '.' || last == '-' { - return false - } - if partlen > 63 || partlen == 0 { - return false - } - partlen = 0 - } - last = c - } - if last == '-' || partlen > 63 { - return false - } - - return ok -} - -var cookieNameSanitizer = strings.NewReplacer("\n", "-", "\r", "-") - -func sanitizeCookieName(n string) string { - return cookieNameSanitizer.Replace(n) -} - -// sanitizeCookieValue produces a suitable cookie-value from v. -// https://tools.ietf.org/html/rfc6265#section-4.1.1 -// cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE ) -// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E -// -// ; US-ASCII characters excluding CTLs, -// ; whitespace DQUOTE, comma, semicolon, -// ; and backslash -// -// We loosen this as spaces and commas are common in cookie values -// but we produce a quoted cookie-value if and only if v contains -// commas or spaces. -// See https://golang.org/issue/7243 for the discussion. -func sanitizeCookieValue(v string) string { - v = sanitizeOrWarn("Cookie.Value", validCookieValueByte, v) - if len(v) == 0 { - return v - } - if strings.IndexByte(v, ' ') >= 0 || strings.IndexByte(v, ',') >= 0 { - return `"` + v + `"` - } - return v -} - -func validCookieValueByte(b byte) bool { - return 0x20 <= b && b < 0x7f && b != '"' && b != ';' && b != '\\' -} - -// path-av = "Path=" path-value -// path-value = -func sanitizeCookiePath(v string) string { - return sanitizeOrWarn("Cookie.Path", validCookiePathByte, v) -} - -func validCookiePathByte(b byte) bool { - return 0x20 <= b && b < 0x7f && b != ';' -} - -func sanitizeOrWarn(fieldName string, valid func(byte) bool, v string) string { - ok := true - for i := 0; i < len(v); i++ { - if valid(v[i]) { - continue - } - log.Printf("net/http: invalid byte %q in %s; dropping invalid bytes", v[i], fieldName) - ok = false - break - } - if ok { - return v - } - buf := make([]byte, 0, len(v)) - for i := 0; i < len(v); i++ { - if b := v[i]; valid(b) { - buf = append(buf, b) - } - } - return string(buf) -} - -func parseCookieValue(raw string, allowDoubleQuote bool) (string, bool) { - // Strip the quotes, if present. - if allowDoubleQuote && len(raw) > 1 && raw[0] == '"' && raw[len(raw)-1] == '"' { - raw = raw[1 : len(raw)-1] - } - for i := 0; i < len(raw); i++ { - if !validCookieValueByte(raw[i]) { - return "", false - } - } - return raw, true -} - -func isCookieNameValid(raw string) bool { - if raw == "" { - return false - } - return strings.IndexFunc(raw, isNotToken) < 0 -} diff --git a/net/http/cookiejar/jar.go b/net/http/cookiejar/jar.go deleted file mode 100644 index 9a773f8a7..000000000 --- a/net/http/cookiejar/jar.go +++ /dev/null @@ -1,504 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package cookiejar implements an in-memory RFC 6265-compliant http.CookieJar. -package cookiejar - -import ( - "errors" - "fmt" - "net/url" - "sort" - "strings" - "sync" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/net/http" -) - -// PublicSuffixList provides the public suffix of a domain. For example: -// - the public suffix of "example.com" is "com", -// - the public suffix of "foo1.foo2.foo3.co.uk" is "co.uk", and -// - the public suffix of "bar.pvt.k12.ma.us" is "pvt.k12.ma.us". -// -// Implementations of PublicSuffixList must be safe for concurrent use by -// multiple goroutines. -// -// An implementation that always returns "" is valid and may be useful for -// testing but it is not secure: it means that the HTTP server for foo.com can -// set a cookie for bar.com. -// -// A public suffix list implementation is in the package -// golang.org/x/net/publicsuffix. -type PublicSuffixList interface { - // PublicSuffix returns the public suffix of domain. - // - // TODO: specify which of the caller and callee is responsible for IP - // addresses, for leading and trailing dots, for case sensitivity, and - // for IDN/Punycode. - PublicSuffix(domain string) string - - // String returns a description of the source of this public suffix - // list. The description will typically contain something like a time - // stamp or version number. - String() string -} - -// Options are the options for creating a new Jar. -type Options struct { - // PublicSuffixList is the public suffix list that determines whether - // an HTTP server can set a cookie for a domain. - // - // A nil value is valid and may be useful for testing but it is not - // secure: it means that the HTTP server for foo.co.uk can set a cookie - // for bar.co.uk. - PublicSuffixList PublicSuffixList -} - -// Jar implements the http.CookieJar interface from the net/http package. -type Jar struct { - psList PublicSuffixList - - // mu locks the remaining fields. - mu sync.Mutex - - // entries is a set of entries, keyed by their eTLD+1 and subkeyed by - // their name/domain/path. - entries map[string]map[string]entry - - // nextSeqNum is the next sequence number assigned to a new cookie - // created SetCookies. - nextSeqNum uint64 -} - -// New returns a new cookie jar. A nil *Options is equivalent to a zero -// Options. -func New(o *Options) (*Jar, error) { - jar := &Jar{ - entries: make(map[string]map[string]entry), - } - if o != nil { - jar.psList = o.PublicSuffixList - } - return jar, nil -} - -// entry is the internal representation of a cookie. -// -// This struct type is not used outside of this package per se, but the exported -// fields are those of RFC 6265. -type entry struct { - Name string - Value string - Domain string - Path string - SameSite string - Secure bool - HttpOnly bool - Persistent bool - HostOnly bool - Expires time.Time - Creation time.Time - LastAccess time.Time - - // seqNum is a sequence number so that Cookies returns cookies in a - // deterministic order, even for cookies that have equal Path length and - // equal Creation time. This simplifies testing. - seqNum uint64 -} - -// id returns the domain;path;name triple of e as an id. -func (e *entry) id() string { - return fmt.Sprintf("%s;%s;%s", e.Domain, e.Path, e.Name) -} - -// shouldSend determines whether e's cookie qualifies to be included in a -// request to host/path. It is the caller's responsibility to check if the -// cookie is expired. -func (e *entry) shouldSend(https bool, host, path string) bool { - return e.domainMatch(host) && e.pathMatch(path) && (https || !e.Secure) -} - -// domainMatch implements "domain-match" of RFC 6265 section 5.1.3. -func (e *entry) domainMatch(host string) bool { - if e.Domain == host { - return true - } - return !e.HostOnly && hasDotSuffix(host, e.Domain) -} - -// pathMatch implements "path-match" according to RFC 6265 section 5.1.4. -func (e *entry) pathMatch(requestPath string) bool { - if requestPath == e.Path { - return true - } - if strings.HasPrefix(requestPath, e.Path) { - if e.Path[len(e.Path)-1] == '/' { - return true // The "/any/" matches "/any/path" case. - } else if requestPath[len(e.Path)] == '/' { - return true // The "/any" matches "/any/path" case. - } - } - return false -} - -// hasDotSuffix reports whether s ends in "."+suffix. -func hasDotSuffix(s, suffix string) bool { - return len(s) > len(suffix) && s[len(s)-len(suffix)-1] == '.' && s[len(s)-len(suffix):] == suffix -} - -// Cookies implements the Cookies method of the http.CookieJar interface. -// -// It returns an empty slice if the URL's scheme is not HTTP or HTTPS. -func (j *Jar) Cookies(u *url.URL) (cookies []*http.Cookie) { - return j.cookies(u, time.Now()) -} - -// cookies is like Cookies but takes the current time as a parameter. -func (j *Jar) cookies(u *url.URL, now time.Time) (cookies []*http.Cookie) { - if u.Scheme != "http" && u.Scheme != "https" { - return cookies - } - host, err := canonicalHost(u.Host) - if err != nil { - return cookies - } - key := jarKey(host, j.psList) - - j.mu.Lock() - defer j.mu.Unlock() - - submap := j.entries[key] - if submap == nil { - return cookies - } - - https := u.Scheme == "https" - path := u.Path - if path == "" { - path = "/" - } - - modified := false - var selected []entry - for id, e := range submap { - if e.Persistent && !e.Expires.After(now) { - delete(submap, id) - modified = true - continue - } - if !e.shouldSend(https, host, path) { - continue - } - e.LastAccess = now - submap[id] = e - selected = append(selected, e) - modified = true - } - if modified { - if len(submap) == 0 { - delete(j.entries, key) - } else { - j.entries[key] = submap - } - } - - // sort according to RFC 6265 section 5.4 point 2: by longest - // path and then by earliest creation time. - sort.Slice(selected, func(i, j int) bool { - s := selected - if len(s[i].Path) != len(s[j].Path) { - return len(s[i].Path) > len(s[j].Path) - } - if !s[i].Creation.Equal(s[j].Creation) { - return s[i].Creation.Before(s[j].Creation) - } - return s[i].seqNum < s[j].seqNum - }) - for _, e := range selected { - cookies = append(cookies, &http.Cookie{Name: e.Name, Value: e.Value}) - } - - return cookies -} - -// SetCookies implements the SetCookies method of the http.CookieJar interface. -// -// It does nothing if the URL's scheme is not HTTP or HTTPS. -func (j *Jar) SetCookies(u *url.URL, cookies []*http.Cookie) { - j.setCookies(u, cookies, time.Now()) -} - -// setCookies is like SetCookies but takes the current time as parameter. -func (j *Jar) setCookies(u *url.URL, cookies []*http.Cookie, now time.Time) { - if len(cookies) == 0 { - return - } - if u.Scheme != "http" && u.Scheme != "https" { - return - } - host, err := canonicalHost(u.Host) - if err != nil { - return - } - key := jarKey(host, j.psList) - defPath := defaultPath(u.Path) - - j.mu.Lock() - defer j.mu.Unlock() - - submap := j.entries[key] - - modified := false - for _, cookie := range cookies { - e, remove, err := j.newEntry(cookie, now, defPath, host) - if err != nil { - continue - } - id := e.id() - if remove { - if submap != nil { - if _, ok := submap[id]; ok { - delete(submap, id) - modified = true - } - } - continue - } - if submap == nil { - submap = make(map[string]entry) - } - - if old, ok := submap[id]; ok { - e.Creation = old.Creation - e.seqNum = old.seqNum - } else { - e.Creation = now - e.seqNum = j.nextSeqNum - j.nextSeqNum++ - } - e.LastAccess = now - submap[id] = e - modified = true - } - - if modified { - if len(submap) == 0 { - delete(j.entries, key) - } else { - j.entries[key] = submap - } - } -} - -// canonicalHost strips port from host if present and returns the canonicalized -// host name. -func canonicalHost(host string) (string, error) { - var err error - host = strings.ToLower(host) - if hasPort(host) { - host, _, err = net.SplitHostPort(host) - if err != nil { - return "", err - } - } - if strings.HasSuffix(host, ".") { - // Strip trailing dot from fully qualified domain names. - host = host[:len(host)-1] - } - return toASCII(host) -} - -// hasPort reports whether host contains a port number. host may be a host -// name, an IPv4 or an IPv6 address. -func hasPort(host string) bool { - colons := strings.Count(host, ":") - if colons == 0 { - return false - } - if colons == 1 { - return true - } - return host[0] == '[' && strings.Contains(host, "]:") -} - -// jarKey returns the key to use for a jar. -func jarKey(host string, psl PublicSuffixList) string { - if isIP(host) { - return host - } - - var i int - if psl == nil { - i = strings.LastIndex(host, ".") - if i <= 0 { - return host - } - } else { - suffix := psl.PublicSuffix(host) - if suffix == host { - return host - } - i = len(host) - len(suffix) - if i <= 0 || host[i-1] != '.' { - // The provided public suffix list psl is broken. - // Storing cookies under host is a safe stopgap. - return host - } - // Only len(suffix) is used to determine the jar key from - // here on, so it is okay if psl.PublicSuffix("www.buggy.psl") - // returns "com" as the jar key is generated from host. - } - prevDot := strings.LastIndex(host[:i-1], ".") - return host[prevDot+1:] -} - -// isIP reports whether host is an IP address. -func isIP(host string) bool { - return net.ParseIP(host) != nil -} - -// defaultPath returns the directory part of an URL's path according to -// RFC 6265 section 5.1.4. -func defaultPath(path string) string { - if len(path) == 0 || path[0] != '/' { - return "/" // Path is empty or malformed. - } - - i := strings.LastIndex(path, "/") // Path starts with "/", so i != -1. - if i == 0 { - return "/" // Path has the form "/abc". - } - return path[:i] // Path is either of form "/abc/xyz" or "/abc/xyz/". -} - -// newEntry creates an entry from a http.Cookie c. now is the current time and -// is compared to c.Expires to determine deletion of c. defPath and host are the -// default-path and the canonical host name of the URL c was received from. -// -// remove records whether the jar should delete this cookie, as it has already -// expired with respect to now. In this case, e may be incomplete, but it will -// be valid to call e.id (which depends on e's Name, Domain and Path). -// -// A malformed c.Domain will result in an error. -func (j *Jar) newEntry(c *http.Cookie, now time.Time, defPath, host string) (e entry, remove bool, err error) { - e.Name = c.Name - - if c.Path == "" || c.Path[0] != '/' { - e.Path = defPath - } else { - e.Path = c.Path - } - - e.Domain, e.HostOnly, err = j.domainAndType(host, c.Domain) - if err != nil { - return e, false, err - } - - // MaxAge takes precedence over Expires. - if c.MaxAge < 0 { - return e, true, nil - } else if c.MaxAge > 0 { - e.Expires = now.Add(time.Duration(c.MaxAge) * time.Second) - e.Persistent = true - } else { - if c.Expires.IsZero() { - e.Expires = endOfTime - e.Persistent = false - } else { - if !c.Expires.After(now) { - return e, true, nil - } - e.Expires = c.Expires - e.Persistent = true - } - } - - e.Value = c.Value - e.Secure = c.Secure - e.HttpOnly = c.HttpOnly - - switch c.SameSite { - case http.SameSiteDefaultMode: - e.SameSite = "SameSite" - case http.SameSiteStrictMode: - e.SameSite = "SameSite=Strict" - case http.SameSiteLaxMode: - e.SameSite = "SameSite=Lax" - } - - return e, false, nil -} - -var ( - errIllegalDomain = errors.New("cookiejar: illegal cookie domain attribute") - errMalformedDomain = errors.New("cookiejar: malformed cookie domain attribute") - errNoHostname = errors.New("cookiejar: no host name available (IP only)") -) - -// endOfTime is the time when session (non-persistent) cookies expire. -// This instant is representable in most date/time formats (not just -// Go's time.Time) and should be far enough in the future. -var endOfTime = time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC) - -// domainAndType determines the cookie's domain and hostOnly attribute. -func (j *Jar) domainAndType(host, domain string) (string, bool, error) { - if domain == "" { - // No domain attribute in the SetCookie header indicates a - // host cookie. - return host, true, nil - } - - if isIP(host) { - // According to RFC 6265 domain-matching includes not being - // an IP address. - // TODO: This might be relaxed as in common browsers. - return "", false, errNoHostname - } - - // From here on: If the cookie is valid, it is a domain cookie (with - // the one exception of a public suffix below). - // See RFC 6265 section 5.2.3. - if domain[0] == '.' { - domain = domain[1:] - } - - if len(domain) == 0 || domain[0] == '.' { - // Received either "Domain=." or "Domain=..some.thing", - // both are illegal. - return "", false, errMalformedDomain - } - domain = strings.ToLower(domain) - - if domain[len(domain)-1] == '.' { - // We received stuff like "Domain=www.example.com.". - // Browsers do handle such stuff (actually differently) but - // RFC 6265 seems to be clear here (e.g. section 4.1.2.3) in - // requiring a reject. 4.1.2.3 is not normative, but - // "Domain Matching" (5.1.3) and "Canonicalized Host Names" - // (5.1.2) are. - return "", false, errMalformedDomain - } - - // See RFC 6265 section 5.3 #5. - if j.psList != nil { - if ps := j.psList.PublicSuffix(domain); ps != "" && !hasDotSuffix(domain, ps) { - if host == domain { - // This is the one exception in which a cookie - // with a domain attribute is a host cookie. - return host, true, nil - } - return "", false, errIllegalDomain - } - } - - // The domain must domain-match host: www.mycompany.com cannot - // set cookies for .ourcompetitors.com. - if host != domain && !hasDotSuffix(host, domain) { - return "", false, errIllegalDomain - } - - return domain, false, nil -} diff --git a/net/http/cookiejar/punycode.go b/net/http/cookiejar/punycode.go deleted file mode 100644 index a9cc666e8..000000000 --- a/net/http/cookiejar/punycode.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cookiejar - -// This file implements the Punycode algorithm from RFC 3492. - -import ( - "fmt" - "strings" - "unicode/utf8" -) - -// These parameter values are specified in section 5. -// -// All computation is done with int32s, so that overflow behavior is identical -// regardless of whether int is 32-bit or 64-bit. -const ( - base int32 = 36 - damp int32 = 700 - initialBias int32 = 72 - initialN int32 = 128 - skew int32 = 38 - tmax int32 = 26 - tmin int32 = 1 -) - -// encode encodes a string as specified in section 6.3 and prepends prefix to -// the result. -// -// The "while h < length(input)" line in the specification becomes "for -// remaining != 0" in the Go code, because len(s) in Go is in bytes, not runes. -func encode(prefix, s string) (string, error) { - output := make([]byte, len(prefix), len(prefix)+1+2*len(s)) - copy(output, prefix) - delta, n, bias := int32(0), initialN, initialBias - b, remaining := int32(0), int32(0) - for _, r := range s { - if r < utf8.RuneSelf { - b++ - output = append(output, byte(r)) - } else { - remaining++ - } - } - h := b - if b > 0 { - output = append(output, '-') - } - for remaining != 0 { - m := int32(0x7fffffff) - for _, r := range s { - if m > r && r >= n { - m = r - } - } - delta += (m - n) * (h + 1) - if delta < 0 { - return "", fmt.Errorf("cookiejar: invalid label %q", s) - } - n = m - for _, r := range s { - if r < n { - delta++ - if delta < 0 { - return "", fmt.Errorf("cookiejar: invalid label %q", s) - } - continue - } - if r > n { - continue - } - q := delta - for k := base; ; k += base { - t := k - bias - if t < tmin { - t = tmin - } else if t > tmax { - t = tmax - } - if q < t { - break - } - output = append(output, encodeDigit(t+(q-t)%(base-t))) - q = (q - t) / (base - t) - } - output = append(output, encodeDigit(q)) - bias = adapt(delta, h+1, h == b) - delta = 0 - h++ - remaining-- - } - delta++ - n++ - } - return string(output), nil -} - -func encodeDigit(digit int32) byte { - switch { - case 0 <= digit && digit < 26: - return byte(digit + 'a') - case 26 <= digit && digit < 36: - return byte(digit + ('0' - 26)) - } - panic("cookiejar: internal error in punycode encoding") -} - -// adapt is the bias adaptation function specified in section 6.1. -func adapt(delta, numPoints int32, firstTime bool) int32 { - if firstTime { - delta /= damp - } else { - delta /= 2 - } - delta += delta / numPoints - k := int32(0) - for delta > ((base-tmin)*tmax)/2 { - delta /= base - tmin - k += base - } - return k + (base-tmin+1)*delta/(delta+skew) -} - -// Strictly speaking, the remaining code below deals with IDNA (RFC 5890 and -// friends) and not Punycode (RFC 3492) per se. - -// acePrefix is the ASCII Compatible Encoding prefix. -const acePrefix = "xn--" - -// toASCII converts a domain or domain label to its ASCII form. For example, -// toASCII("bücher.example.com") is "xn--bcher-kva.example.com", and -// toASCII("golang") is "golang". -func toASCII(s string) (string, error) { - if ascii(s) { - return s, nil - } - labels := strings.Split(s, ".") - for i, label := range labels { - if !ascii(label) { - a, err := encode(acePrefix, label) - if err != nil { - return "", err - } - labels[i] = a - } - } - return strings.Join(labels, "."), nil -} - -func ascii(s string) bool { - for i := 0; i < len(s); i++ { - if s[i] >= utf8.RuneSelf { - return false - } - } - return true -} diff --git a/net/http/driver.go b/net/http/driver.go deleted file mode 100644 index d12593985..000000000 --- a/net/http/driver.go +++ /dev/null @@ -1,15 +0,0 @@ -package http - -type DeviceDriver interface { - ListenAndServe(addr string, handler Handler) error -} - -var ActiveDevice DeviceDriver - -func UseDriver(driver DeviceDriver) { - // TODO: rethink and refactor this - if ActiveDevice != nil { - panic("net.ActiveDevice is already set") - } - ActiveDevice = driver -} diff --git a/net/http/header.go b/net/http/header.go deleted file mode 100644 index 0cc0e556c..000000000 --- a/net/http/header.go +++ /dev/null @@ -1,259 +0,0 @@ -package http - -import ( - "io" - "net/http/httptrace" - "net/textproto" - "sort" - "strings" - "sync" - "time" -) - -// A Header represents the key-value pairs in an HTTP header. -// -// The keys should be in canonical form, as returned by -// CanonicalHeaderKey. -type Header map[string][]string - -// Add adds the key, value pair to the header. -// It appends to any existing values associated with key. -// The key is case insensitive; it is canonicalized by -// CanonicalHeaderKey. -func (h Header) Add(key, value string) { - textproto.MIMEHeader(h).Add(key, value) -} - -// Set sets the header entries associated with key to the -// single element value. It replaces any existing values -// associated with key. The key is case insensitive; it is -// canonicalized by textproto.CanonicalMIMEHeaderKey. -// To use non-canonical keys, assign to the map directly. -func (h Header) Set(key, value string) { - textproto.MIMEHeader(h).Set(key, value) -} - -// Get gets the first value associated with the given key. If -// there are no values associated with the key, Get returns "". -// It is case insensitive; textproto.CanonicalMIMEHeaderKey is -// used to canonicalize the provided key. To use non-canonical keys, -// access the map directly. -func (h Header) Get(key string) string { - return textproto.MIMEHeader(h).Get(key) -} - -// Values returns all values associated with the given key. -// It is case insensitive; textproto.CanonicalMIMEHeaderKey is -// used to canonicalize the provided key. To use non-canonical -// keys, access the map directly. -// The returned slice is not a copy. -func (h Header) Values(key string) []string { - return textproto.MIMEHeader(h).Values(key) -} - -// get is like Get, but key must already be in CanonicalHeaderKey form. -func (h Header) get(key string) string { - if v := h[key]; len(v) > 0 { - return v[0] - } - return "" -} - -// has reports whether h has the provided key defined, even if it's -// set to 0-length slice. -func (h Header) has(key string) bool { - _, ok := h[key] - return ok -} - -// Del deletes the values associated with key. -// The key is case insensitive; it is canonicalized by -// CanonicalHeaderKey. -func (h Header) Del(key string) { - textproto.MIMEHeader(h).Del(key) -} - -// Write writes a header in wire format. -func (h Header) Write(w io.Writer) error { - return h.write(w, nil) -} - -func (h Header) write(w io.Writer, trace *httptrace.ClientTrace) error { - return h.writeSubset(w, nil, trace) -} - -// Clone returns a copy of h or nil if h is nil. -func (h Header) Clone() Header { - if h == nil { - return nil - } - - // Find total number of values. - nv := 0 - for _, vv := range h { - nv += len(vv) - } - sv := make([]string, nv) // shared backing array for headers' values - h2 := make(Header, len(h)) - for k, vv := range h { - n := copy(sv, vv) - h2[k] = sv[:n:n] - sv = sv[n:] - } - return h2 -} - -var timeFormats = []string{ - TimeFormat, - time.RFC850, - time.ANSIC, -} - -// ParseTime parses a time header (such as the Date: header), -// trying each of the three formats allowed by HTTP/1.1: -// TimeFormat, time.RFC850, and time.ANSIC. -func ParseTime(text string) (t time.Time, err error) { - for _, layout := range timeFormats { - t, err = time.Parse(layout, text) - if err == nil { - return - } - } - return -} - -var headerNewlineToSpace = strings.NewReplacer("\n", " ", "\r", " ") - -// stringWriter implements WriteString on a Writer. -type stringWriter struct { - w io.Writer -} - -func (w stringWriter) WriteString(s string) (n int, err error) { - return w.w.Write([]byte(s)) -} - -type keyValues struct { - key string - values []string -} - -// A headerSorter implements sort.Interface by sorting a []keyValues -// by key. It's used as a pointer, so it can fit in a sort.Interface -// interface value without allocation. -type headerSorter struct { - kvs []keyValues -} - -func (s *headerSorter) Len() int { return len(s.kvs) } -func (s *headerSorter) Swap(i, j int) { s.kvs[i], s.kvs[j] = s.kvs[j], s.kvs[i] } -func (s *headerSorter) Less(i, j int) bool { return s.kvs[i].key < s.kvs[j].key } - -var headerSorterPool = sync.Pool{ - New: func() interface{} { return new(headerSorter) }, -} - -// sortedKeyValues returns h's keys sorted in the returned kvs -// slice. The headerSorter used to sort is also returned, for possible -// return to headerSorterCache. -func (h Header) sortedKeyValues(exclude map[string]bool) (kvs []keyValues, hs *headerSorter) { - hs = headerSorterPool.Get().(*headerSorter) - if cap(hs.kvs) < len(h) { - hs.kvs = make([]keyValues, 0, len(h)) - } - kvs = hs.kvs[:0] - for k, vv := range h { - if !exclude[k] { - kvs = append(kvs, keyValues{k, vv}) - } - } - hs.kvs = kvs - sort.Sort(hs) - return kvs, hs -} - -// WriteSubset writes a header in wire format. -// If exclude is not nil, keys where exclude[key] == true are not written. -// Keys are not canonicalized before checking the exclude map. -func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error { - return h.writeSubset(w, exclude, nil) -} - -func (h Header) writeSubset(w io.Writer, exclude map[string]bool, trace *httptrace.ClientTrace) error { - ws, ok := w.(io.StringWriter) - if !ok { - ws = stringWriter{w} - } - kvs, sorter := h.sortedKeyValues(exclude) - var formattedVals []string - for _, kv := range kvs { - for _, v := range kv.values { - v = headerNewlineToSpace.Replace(v) - v = textproto.TrimString(v) - for _, s := range []string{kv.key, ": ", v, "\r\n"} { - if _, err := ws.WriteString(s); err != nil { - headerSorterPool.Put(sorter) - return err - } - } - if trace != nil && trace.WroteHeaderField != nil { - formattedVals = append(formattedVals, v) - } - } - if trace != nil && trace.WroteHeaderField != nil { - trace.WroteHeaderField(kv.key, formattedVals) - formattedVals = nil - } - } - headerSorterPool.Put(sorter) - return nil -} - -// CanonicalHeaderKey returns the canonical format of the -// header key s. The canonicalization converts the first -// letter and any letter following a hyphen to upper case; -// the rest are converted to lowercase. For example, the -// canonical key for "accept-encoding" is "Accept-Encoding". -// If s contains a space or invalid header field bytes, it is -// returned without modifications. -func CanonicalHeaderKey(s string) string { return textproto.CanonicalMIMEHeaderKey(s) } - -// hasToken reports whether token appears with v, ASCII -// case-insensitive, with space or comma boundaries. -// token must be all lowercase. -// v may contain mixed cased. -func hasToken(v, token string) bool { - if len(token) > len(v) || token == "" { - return false - } - if v == token { - return true - } - for sp := 0; sp <= len(v)-len(token); sp++ { - // Check that first character is good. - // The token is ASCII, so checking only a single byte - // is sufficient. We skip this potential starting - // position if both the first byte and its potential - // ASCII uppercase equivalent (b|0x20) don't match. - // False positives ('^' => '~') are caught by EqualFold. - if b := v[sp]; b != token[0] && b|0x20 != token[0] { - continue - } - // Check that start pos is on a valid token boundary. - if sp > 0 && !isTokenBoundary(v[sp-1]) { - continue - } - // Check that end pos is on a valid token boundary. - if endPos := sp + len(token); endPos != len(v) && !isTokenBoundary(v[endPos]) { - continue - } - if strings.EqualFold(v[sp:sp+len(token)], token) { - return true - } - } - return false -} - -func isTokenBoundary(b byte) bool { - return b == ' ' || b == ',' || b == '\t' -} diff --git a/net/http/http.go b/net/http/http.go deleted file mode 100644 index 926869870..000000000 --- a/net/http/http.go +++ /dev/null @@ -1,162 +0,0 @@ -package http - -import ( - "io" - "strconv" - "strings" - "time" - "unicode/utf8" - - "golang.org/x/net/http/httpguts" -) - -// incomparable is a zero-width, non-comparable type. Adding it to a struct -// makes that struct also non-comparable, and generally doesn't add -// any size (as long as it's first). -type incomparable [0]func() - -// maxInt64 is the effective "infinite" value for the Server and -// Transport's byte-limiting readers. -const maxInt64 = 1<<63 - 1 - -// aLongTimeAgo is a non-zero time, far in the past, used for -// immediate cancellation of network operations. -var aLongTimeAgo = time.Unix(1, 0) - -// omitBundledHTTP2 is set by omithttp2.go when the nethttpomithttp2 -// build tag is set. That means h2_bundle.go isn't compiled in and we -// shouldn't try to use it. -var omitBundledHTTP2 bool - -// TODO(bradfitz): move common stuff here. The other files have accumulated -// generic http stuff in random places. - -// contextKey is a value for use with context.WithValue. It's used as -// a pointer so it fits in an interface{} without allocation. -type contextKey struct { - name string -} - -func (k *contextKey) String() string { return "net/http context value " + k.name } - -// Given a string of the form "host", "host:port", or "[ipv6::address]:port", -// return true if the string includes a port. -func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") } - -// removeEmptyPort strips the empty port in ":port" to "" -// as mandated by RFC 3986 Section 6.2.3. -func removeEmptyPort(host string) string { - if hasPort(host) { - return strings.TrimSuffix(host, ":") - } - return host -} - -func isNotToken(r rune) bool { - return !httpguts.IsTokenRune(r) -} - -func isASCII(s string) bool { - for i := 0; i < len(s); i++ { - if s[i] >= utf8.RuneSelf { - return false - } - } - return true -} - -// stringContainsCTLByte reports whether s contains any ASCII control character. -func stringContainsCTLByte(s string) bool { - for i := 0; i < len(s); i++ { - b := s[i] - if b < ' ' || b == 0x7f { - return true - } - } - return false -} - -func hexEscapeNonASCII(s string) string { - newLen := 0 - for i := 0; i < len(s); i++ { - if s[i] >= utf8.RuneSelf { - newLen += 3 - } else { - newLen++ - } - } - if newLen == len(s) { - return s - } - b := make([]byte, 0, newLen) - for i := 0; i < len(s); i++ { - if s[i] >= utf8.RuneSelf { - b = append(b, '%') - b = strconv.AppendInt(b, int64(s[i]), 16) - } else { - b = append(b, s[i]) - } - } - return string(b) -} - -// NoBody is an io.ReadCloser with no bytes. Read always returns EOF -// and Close always returns nil. It can be used in an outgoing client -// request to explicitly signal that a request has zero bytes. -// An alternative, however, is to simply set Request.Body to nil. -var NoBody = noBody{} - -type noBody struct{} - -func (noBody) Read([]byte) (int, error) { return 0, io.EOF } -func (noBody) Close() error { return nil } -func (noBody) WriteTo(io.Writer) (int64, error) { return 0, nil } - -var ( - // verify that an io.Copy from NoBody won't require a buffer: - _ io.WriterTo = NoBody - _ io.ReadCloser = NoBody -) - -// PushOptions describes options for Pusher.Push. -type PushOptions struct { - // Method specifies the HTTP method for the promised request. - // If set, it must be "GET" or "HEAD". Empty means "GET". - Method string - - // Header specifies additional promised request headers. This cannot - // include HTTP/2 pseudo header fields like ":path" and ":scheme", - // which will be added automatically. - Header Header -} - -// Pusher is the interface implemented by ResponseWriters that support -// HTTP/2 server push. For more background, see -// https://tools.ietf.org/html/rfc7540#section-8.2. -type Pusher interface { - // Push initiates an HTTP/2 server push. This constructs a synthetic - // request using the given target and options, serializes that request - // into a PUSH_PROMISE frame, then dispatches that request using the - // server's request handler. If opts is nil, default options are used. - // - // The target must either be an absolute path (like "/path") or an absolute - // URL that contains a valid host and the same scheme as the parent request. - // If the target is a path, it will inherit the scheme and host of the - // parent request. - // - // The HTTP/2 spec disallows recursive pushes and cross-authority pushes. - // Push may or may not detect these invalid pushes; however, invalid - // pushes will be detected and canceled by conforming clients. - // - // Handlers that wish to push URL X should call Push before sending any - // data that may trigger a request for URL X. This avoids a race where the - // client issues requests for X before receiving the PUSH_PROMISE for X. - // - // Push will run in a separate goroutine making the order of arrival - // non-deterministic. Any required synchronization needs to be implemented - // by the caller. - // - // Push returns ErrNotSupported if the client has disabled push or if push - // is not supported on the underlying connection. - Push(target string, opts *PushOptions) error -} diff --git a/net/http/jar.go b/net/http/jar.go deleted file mode 100644 index 5c3de0dad..000000000 --- a/net/http/jar.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package http - -import ( - "net/url" -) - -// A CookieJar manages storage and use of cookies in HTTP requests. -// -// Implementations of CookieJar must be safe for concurrent use by multiple -// goroutines. -// -// The net/http/cookiejar package provides a CookieJar implementation. -type CookieJar interface { - // SetCookies handles the receipt of the cookies in a reply for the - // given URL. It may or may not choose to save the cookies, depending - // on the jar's policy and implementation. - SetCookies(u *url.URL, cookies []*Cookie) - - // Cookies returns the cookies to send in a request for the given URL. - // It is up to the implementation to honor the standard cookie use - // restrictions such as in RFC 6265. - Cookies(u *url.URL) []*Cookie -} diff --git a/net/http/request.go b/net/http/request.go deleted file mode 100644 index ac67be2b3..000000000 --- a/net/http/request.go +++ /dev/null @@ -1,769 +0,0 @@ -package http - -import ( - "bufio" - "bytes" - "context" - "crypto/tls" - "errors" - "fmt" - "io" - "mime" - "mime/multipart" - "net/textproto" - "net/url" - urlpkg "net/url" - "strconv" - "strings" - "sync" -) - -func badStringError(what, val string) error { return fmt.Errorf("%s %q", what, val) } - -type Request struct { - // Method specifies the HTTP method (GET, POST, PUT, etc.). - // For client requests, an empty string means GET. - // - // Go's HTTP client does not support sending a request with - // the CONNECT method. See the documentation on Transport for - // details. - Method string - - // URL specifies either the URI being requested (for server - // requests) or the URL to access (for client requests). - // - // For server requests, the URL is parsed from the URI - // supplied on the Request-Line as stored in RequestURI. For - // most requests, fields other than Path and RawQuery will be - // empty. (See RFC 7230, Section 5.3) - // - // For client requests, the URL's Host specifies the server to - // connect to, while the Request's Host field optionally - // specifies the Host header value to send in the HTTP - // request. - URL *url.URL - - // The protocol version for incoming server requests. - // - // For client requests, these fields are ignored. The HTTP - // client code always uses either HTTP/1.1 or HTTP/2. - // See the docs on Transport for details. - Proto string // "HTTP/1.0" - ProtoMajor int // 1 - ProtoMinor int // 0 - - // Header contains the request header fields either received - // by the server or to be sent by the client. - // - // If a server received a request with header lines, - // - // Host: example.com - // accept-encoding: gzip, deflate - // Accept-Language: en-us - // fOO: Bar - // foo: two - // - // then - // - // Header = map[string][]string{ - // "Accept-Encoding": {"gzip, deflate"}, - // "Accept-Language": {"en-us"}, - // "Foo": {"Bar", "two"}, - // } - // - // For incoming requests, the Host header is promoted to the - // Request.Host field and removed from the Header map. - // - // HTTP defines that header names are case-insensitive. The - // request parser implements this by using CanonicalHeaderKey, - // making the first character and any characters following a - // hyphen uppercase and the rest lowercase. - // - // For client requests, certain headers such as Content-Length - // and Connection are automatically written when needed and - // values in Header may be ignored. See the documentation - // for the Request.Write method. - Header Header - - // Body is the request's body. - // - // For client requests, a nil body means the request has no - // body, such as a GET request. The HTTP Client's Transport - // is responsible for calling the Close method. - // - // For server requests, the Request Body is always non-nil - // but will return EOF immediately when no body is present. - // The Server will close the request body. The ServeHTTP - // Handler does not need to. - // - // Body must allow Read to be called concurrently with Close. - // In particular, calling Close should unblock a Read waiting - // for input. - Body io.ReadCloser - - // GetBody defines an optional func to return a new copy of - // Body. It is used for client requests when a redirect requires - // reading the body more than once. Use of GetBody still - // requires setting Body. - // - // For server requests, it is unused. - GetBody func() (io.ReadCloser, error) - - // ContentLength records the length of the associated content. - // The value -1 indicates that the length is unknown. - // Values >= 0 indicate that the given number of bytes may - // be read from Body. - // - // For client requests, a value of 0 with a non-nil Body is - // also treated as unknown. - ContentLength int64 - - // TransferEncoding lists the transfer encodings from outermost to - // innermost. An empty list denotes the "identity" encoding. - // TransferEncoding can usually be ignored; chunked encoding is - // automatically added and removed as necessary when sending and - // receiving requests. - TransferEncoding []string - - // Close indicates whether to close the connection after - // replying to this request (for servers) or after sending this - // request and reading its response (for clients). - // - // For server requests, the HTTP server handles this automatically - // and this field is not needed by Handlers. - // - // For client requests, setting this field prevents re-use of - // TCP connections between requests to the same hosts, as if - // Transport.DisableKeepAlives were set. - Close bool - - // For server requests, Host specifies the host on which the - // URL is sought. For HTTP/1 (per RFC 7230, section 5.4), this - // is either the value of the "Host" header or the host name - // given in the URL itself. For HTTP/2, it is the value of the - // ":authority" pseudo-header field. - // It may be of the form "host:port". For international domain - // names, Host may be in Punycode or Unicode form. Use - // golang.org/x/net/idna to convert it to either format if - // needed. - // To prevent DNS rebinding attacks, server Handlers should - // validate that the Host header has a value for which the - // Handler considers itself authoritative. The included - // ServeMux supports patterns registered to particular host - // names and thus protects its registered Handlers. - // - // For client requests, Host optionally overrides the Host - // header to send. If empty, the Request.Write method uses - // the value of URL.Host. Host may contain an international - // domain name. - Host string - - // Form contains the parsed form data, including both the URL - // field's query parameters and the PATCH, POST, or PUT form data. - // This field is only available after ParseForm is called. - // The HTTP client ignores Form and uses Body instead. - Form url.Values - - // PostForm contains the parsed form data from PATCH, POST - // or PUT body parameters. - // - // This field is only available after ParseForm is called. - // The HTTP client ignores PostForm and uses Body instead. - PostForm url.Values - - // MultipartForm is the parsed multipart form, including file uploads. - // This field is only available after ParseMultipartForm is called. - // The HTTP client ignores MultipartForm and uses Body instead. - MultipartForm *multipart.Form - - // Trailer specifies additional headers that are sent after the request - // body. - // - // For server requests, the Trailer map initially contains only the - // trailer keys, with nil values. (The client declares which trailers it - // will later send.) While the handler is reading from Body, it must - // not reference Trailer. After reading from Body returns EOF, Trailer - // can be read again and will contain non-nil values, if they were sent - // by the client. - // - // For client requests, Trailer must be initialized to a map containing - // the trailer keys to later send. The values may be nil or their final - // values. The ContentLength must be 0 or -1, to send a chunked request. - // After the HTTP request is sent the map values can be updated while - // the request body is read. Once the body returns EOF, the caller must - // not mutate Trailer. - // - // Few HTTP clients, servers, or proxies support HTTP trailers. - Trailer Header - - // RemoteAddr allows HTTP servers and other software to record - // the network address that sent the request, usually for - // logging. This field is not filled in by ReadRequest and - // has no defined format. The HTTP server in this package - // sets RemoteAddr to an "IP:port" address before invoking a - // handler. - // This field is ignored by the HTTP client. - RemoteAddr string - - // RequestURI is the unmodified request-target of the - // Request-Line (RFC 7230, Section 3.1.1) as sent by the client - // to a server. Usually the URL field should be used instead. - // It is an error to set this field in an HTTP client request. - RequestURI string - - // TLS allows HTTP servers and other software to record - // information about the TLS connection on which the request - // was received. This field is not filled in by ReadRequest. - // The HTTP server in this package sets the field for - // TLS-enabled connections before invoking a handler; - // otherwise it leaves the field nil. - // This field is ignored by the HTTP client. - TLS *tls.ConnectionState - - // Cancel is an optional channel whose closure indicates that the client - // request should be regarded as canceled. Not all implementations of - // RoundTripper may support Cancel. - // - // For server requests, this field is not applicable. - // - // Deprecated: Set the Request's context with NewRequestWithContext - // instead. If a Request's Cancel field and context are both - // set, it is undefined whether Cancel is respected. - Cancel <-chan struct{} - - // Response is the redirect response which caused this request - // to be created. This field is only populated during client - // redirects. - Response *Response - - // ctx is either the client or server context. It should only - // be modified via copying the whole Request using WithContext. - // It is unexported to prevent people from using Context wrong - // and mutating the contexts held by callers of the same request. - ctx context.Context -} - -// ProtoAtLeast reports whether the HTTP protocol used -// in the request is at least major.minor. -func (r *Request) ProtoAtLeast(major, minor int) bool { - return r.ProtoMajor > major || - r.ProtoMajor == major && r.ProtoMinor >= minor -} - -// UserAgent returns the client's User-Agent, if sent in the request. -func (r *Request) UserAgent() string { - return r.Header.Get("User-Agent") -} - -// Cookies parses and returns the HTTP cookies sent with the request. -func (r *Request) Cookies() []*Cookie { - return readCookies(r.Header, "") -} - -// ErrNoCookie is returned by Request's Cookie method when a cookie is not found. -var ErrNoCookie = errors.New("http: named cookie not present") - -// Cookie returns the named cookie provided in the request or -// ErrNoCookie if not found. -// If multiple cookies match the given name, only one cookie will -// be returned. -func (r *Request) Cookie(name string) (*Cookie, error) { - for _, c := range readCookies(r.Header, name) { - return c, nil - } - return nil, ErrNoCookie -} - -// AddCookie adds a cookie to the request. Per RFC 6265 section 5.4, -// AddCookie does not attach more than one Cookie header field. That -// means all cookies, if any, are written into the same line, -// separated by semicolon. -// AddCookie only sanitizes c's name and value, and does not sanitize -// a Cookie header already present in the request. -func (r *Request) AddCookie(c *Cookie) { - s := fmt.Sprintf("%s=%s", sanitizeCookieName(c.Name), sanitizeCookieValue(c.Value)) - if c := r.Header.Get("Cookie"); c != "" { - r.Header.Set("Cookie", c+"; "+s) - } else { - r.Header.Set("Cookie", s) - } -} - -// Referer returns the referring URL, if sent in the request. -// -// Referer is misspelled as in the request itself, a mistake from the -// earliest days of HTTP. This value can also be fetched from the -// Header map as Header["Referer"]; the benefit of making it available -// as a method is that the compiler can diagnose programs that use the -// alternate (correct English) spelling req.Referrer() but cannot -// diagnose programs that use Header["Referrer"]. -func (r *Request) Referer() string { - return r.Header.Get("Referer") -} - -// isH2Upgrade reports whether r represents the http2 "client preface" -// magic string. -func (r *Request) isH2Upgrade() bool { - return r.Method == "PRI" && len(r.Header) == 0 && r.URL.Path == "*" && r.Proto == "HTTP/2.0" -} - -// ParseHTTPVersion parses an HTTP version string. -// "HTTP/1.0" returns (1, 0, true). -func ParseHTTPVersion(vers string) (major, minor int, ok bool) { - const Big = 1000000 // arbitrary upper bound - switch vers { - case "HTTP/1.1": - return 1, 1, true - case "HTTP/1.0": - return 1, 0, true - } - if !strings.HasPrefix(vers, "HTTP/") { - return 0, 0, false - } - dot := strings.Index(vers, ".") - if dot < 0 { - return 0, 0, false - } - major, err := strconv.Atoi(vers[5:dot]) - if err != nil || major < 0 || major > Big { - return 0, 0, false - } - minor, err = strconv.Atoi(vers[dot+1:]) - if err != nil || minor < 0 || minor > Big { - return 0, 0, false - } - return major, minor, true -} - -func validMethod(method string) bool { - /* - Method = "OPTIONS" ; Section 9.2 - | "GET" ; Section 9.3 - | "HEAD" ; Section 9.4 - | "POST" ; Section 9.5 - | "PUT" ; Section 9.6 - | "DELETE" ; Section 9.7 - | "TRACE" ; Section 9.8 - | "CONNECT" ; Section 9.9 - | extension-method - extension-method = token - token = 1* - */ - return len(method) > 0 && strings.IndexFunc(method, isNotToken) == -1 -} - -// NewRequest wraps NewRequestWithContext using the background context. -func NewRequest(method, url string, body io.Reader) (*Request, error) { - return NewRequestWithContext(context.Background(), method, url, body) -} - -// NewRequestWithContext returns a new Request given a method, URL, and -// optional body. -// -// If the provided body is also an io.Closer, the returned -// Request.Body is set to body and will be closed by the Client -// methods Do, Post, and PostForm, and Transport.RoundTrip. -// -// NewRequestWithContext returns a Request suitable for use with -// Client.Do or Transport.RoundTrip. To create a request for use with -// testing a Server Handler, either use the NewRequest function in the -// net/http/httptest package, use ReadRequest, or manually update the -// Request fields. For an outgoing client request, the context -// controls the entire lifetime of a request and its response: -// obtaining a connection, sending the request, and reading the -// response headers and body. See the Request type's documentation for -// the difference between inbound and outbound request fields. -// -// If body is of type *bytes.Buffer, *bytes.Reader, or -// *strings.Reader, the returned request's ContentLength is set to its -// exact value (instead of -1), GetBody is populated (so 307 and 308 -// redirects can replay the body), and Body is set to NoBody if the -// ContentLength is 0. -func NewRequestWithContext(ctx context.Context, method, url string, body io.Reader) (*Request, error) { - if method == "" { - // We document that "" means "GET" for Request.Method, and people have - // relied on that from NewRequest, so keep that working. - // We still enforce validMethod for non-empty methods. - method = "GET" - } - if !validMethod(method) { - return nil, fmt.Errorf("net/http: invalid method %q", method) - } - if ctx == nil { - return nil, errors.New("net/http: nil Context") - } - u, err := urlpkg.Parse(url) - if err != nil { - return nil, err - } - rc, ok := body.(io.ReadCloser) - if !ok && body != nil { - rc = io.NopCloser(body) - } - // The host's colon:port should be normalized. See Issue 14836. - u.Host = removeEmptyPort(u.Host) - req := &Request{ - ctx: ctx, - Method: method, - URL: u, - Proto: "HTTP/1.1", - ProtoMajor: 1, - ProtoMinor: 1, - Header: make(Header), - Body: rc, - Host: u.Host, - } - if body != nil { - switch v := body.(type) { - case *bytes.Buffer: - req.ContentLength = int64(v.Len()) - buf := v.Bytes() - req.GetBody = func() (io.ReadCloser, error) { - r := bytes.NewReader(buf) - return io.NopCloser(r), nil - } - case *bytes.Reader: - req.ContentLength = int64(v.Len()) - snapshot := *v - req.GetBody = func() (io.ReadCloser, error) { - r := snapshot - return io.NopCloser(&r), nil - } - case *strings.Reader: - req.ContentLength = int64(v.Len()) - snapshot := *v - req.GetBody = func() (io.ReadCloser, error) { - r := snapshot - return io.NopCloser(&r), nil - } - default: - // This is where we'd set it to -1 (at least - // if body != NoBody) to mean unknown, but - // that broke people during the Go 1.8 testing - // period. People depend on it being 0 I - // guess. Maybe retry later. See Issue 18117. - } - // For client requests, Request.ContentLength of 0 - // means either actually 0, or unknown. The only way - // to explicitly say that the ContentLength is zero is - // to set the Body to nil. But turns out too much code - // depends on NewRequest returning a non-nil Body, - // so we use a well-known ReadCloser variable instead - // and have the http package also treat that sentinel - // variable to mean explicitly zero. - if req.GetBody != nil && req.ContentLength == 0 { - req.Body = NoBody - req.GetBody = func() (io.ReadCloser, error) { return NoBody, nil } - } - } - - return req, nil -} - -// parseRequestLine parses "GET /foo HTTP/1.1" into its three parts. -func parseRequestLine(line string) (method, requestURI, proto string, ok bool) { - s1 := strings.Index(line, " ") - s2 := strings.Index(line[s1+1:], " ") - if s1 < 0 || s2 < 0 { - return - } - s2 += s1 + 1 - return line[:s1], line[s1+1 : s2], line[s2+1:], true -} - -var textprotoReaderPool sync.Pool - -func newTextprotoReader(br *bufio.Reader) *textproto.Reader { - if v := textprotoReaderPool.Get(); v != nil { - tr := v.(*textproto.Reader) - tr.R = br - return tr - } - return textproto.NewReader(br) -} - -func putTextprotoReader(r *textproto.Reader) { - r.R = nil - textprotoReaderPool.Put(r) -} - -// ReadRequest reads and parses an incoming request from b. -// -// ReadRequest is a low-level function and should only be used for -// specialized applications; most code should use the Server to read -// requests and handle them via the Handler interface. ReadRequest -// only supports HTTP/1.x requests. For HTTP/2, use golang.org/x/net/http2. -func ReadRequest(b *bufio.Reader) (*Request, error) { - return readRequest(b, deleteHostHeader) -} - -// Constants for readRequest's deleteHostHeader parameter. -const ( - deleteHostHeader = true - keepHostHeader = false -) - -func readRequest(b *bufio.Reader, deleteHostHeader bool) (req *Request, err error) { - tp := newTextprotoReader(b) - req = new(Request) - - // First line: GET /index.html HTTP/1.0 - var s string - if s, err = tp.ReadLine(); err != nil { - return nil, err - } - defer func() { - putTextprotoReader(tp) - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - }() - - var ok bool - req.Method, req.RequestURI, req.Proto, ok = parseRequestLine(s) - if !ok { - return nil, badStringError("malformed HTTP request", s) - } - if !validMethod(req.Method) { - return nil, badStringError("invalid method", req.Method) - } - rawurl := req.RequestURI - if req.ProtoMajor, req.ProtoMinor, ok = ParseHTTPVersion(req.Proto); !ok { - return nil, badStringError("malformed HTTP version", req.Proto) - } - - // CONNECT requests are used two different ways, and neither uses a full URL: - // The standard use is to tunnel HTTPS through an HTTP proxy. - // It looks like "CONNECT www.google.com:443 HTTP/1.1", and the parameter is - // just the authority section of a URL. This information should go in req.URL.Host. - // - // The net/rpc package also uses CONNECT, but there the parameter is a path - // that starts with a slash. It can be parsed with the regular URL parser, - // and the path will end up in req.URL.Path, where it needs to be in order for - // RPC to work. - justAuthority := req.Method == "CONNECT" && !strings.HasPrefix(rawurl, "/") - if justAuthority { - rawurl = "http://" + rawurl - } - - if req.URL, err = url.ParseRequestURI(rawurl); err != nil { - return nil, err - } - - if justAuthority { - // Strip the bogus "http://" back off. - req.URL.Scheme = "" - } - - // Subsequent lines: Key: value. - mimeHeader, err := tp.ReadMIMEHeader() - if err != nil { - return nil, err - } - req.Header = Header(mimeHeader) - - // RFC 7230, section 5.3: Must treat - // GET /index.html HTTP/1.1 - // Host: www.google.com - // and - // GET http://www.google.com/index.html HTTP/1.1 - // Host: doesntmatter - // the same. In the second case, any Host line is ignored. - req.Host = req.URL.Host - if req.Host == "" { - req.Host = req.Header.get("Host") - } - if deleteHostHeader { - delete(req.Header, "Host") - } - - fixPragmaCacheControl(req.Header) - - req.Close = shouldClose(req.ProtoMajor, req.ProtoMinor, req.Header, false) - - err = readTransfer(req, b) - if err != nil { - return nil, err - } - - if req.isH2Upgrade() { - // Because it's neither chunked, nor declared: - req.ContentLength = -1 - - // We want to give handlers a chance to hijack the - // connection, but we need to prevent the Server from - // dealing with the connection further if it's not - // hijacked. Set Close to ensure that: - req.Close = true - } - return req, nil -} - -// MaxBytesReader is similar to io.LimitReader but is intended for -// limiting the size of incoming request bodies. In contrast to -// io.LimitReader, MaxBytesReader's result is a ReadCloser, returns a -// non-EOF error for a Read beyond the limit, and closes the -// underlying reader when its Close method is called. -// -// MaxBytesReader prevents clients from accidentally or maliciously -// sending a large request and wasting server resources. -func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser { - return &maxBytesReader{w: w, r: r, n: n} -} - -type maxBytesReader struct { - w ResponseWriter - r io.ReadCloser // underlying reader - n int64 // max bytes remaining - err error // sticky error -} - -func (l *maxBytesReader) Read(p []byte) (n int, err error) { - if l.err != nil { - return 0, l.err - } - if len(p) == 0 { - return 0, nil - } - // If they asked for a 32KB byte read but only 5 bytes are - // remaining, no need to read 32KB. 6 bytes will answer the - // question of the whether we hit the limit or go past it. - if int64(len(p)) > l.n+1 { - p = p[:l.n+1] - } - n, err = l.r.Read(p) - - if int64(n) <= l.n { - l.n -= int64(n) - l.err = err - return n, err - } - - n = int(l.n) - l.n = 0 - - // The server code and client code both use - // maxBytesReader. This "requestTooLarge" check is - // only used by the server code. To prevent binaries - // which only using the HTTP Client code (such as - // cmd/go) from also linking in the HTTP server, don't - // use a static type assertion to the server - // "*response" type. Check this interface instead: - type requestTooLarger interface { - requestTooLarge() - } - if res, ok := l.w.(requestTooLarger); ok { - res.requestTooLarge() - } - l.err = errors.New("http: request body too large") - return n, l.err -} - -func (l *maxBytesReader) Close() error { - return l.r.Close() -} - -func copyValues(dst, src url.Values) { - for k, vs := range src { - dst[k] = append(dst[k], vs...) - } -} - -func parsePostForm(r *Request) (vs url.Values, err error) { - if r.Body == nil { - err = errors.New("missing form body") - return - } - ct := r.Header.Get("Content-Type") - // RFC 7231, section 3.1.1.5 - empty type - // MAY be treated as application/octet-stream - if ct == "" { - ct = "application/octet-stream" - } - ct, _, err = mime.ParseMediaType(ct) - switch { - case ct == "application/x-www-form-urlencoded": - var reader io.Reader = r.Body - maxFormSize := int64(1<<63 - 1) - if _, ok := r.Body.(*maxBytesReader); !ok { - maxFormSize = int64(10 << 20) // 10 MB is a lot of text. - reader = io.LimitReader(r.Body, maxFormSize+1) - } - b, e := io.ReadAll(reader) - if e != nil { - if err == nil { - err = e - } - break - } - if int64(len(b)) > maxFormSize { - err = errors.New("http: POST too large") - return - } - vs, e = url.ParseQuery(string(b)) - if err == nil { - err = e - } - case ct == "multipart/form-data": - // handled by ParseMultipartForm (which is calling us, or should be) - // TODO(bradfitz): there are too many possible - // orders to call too many functions here. - // Clean this up and write more tests. - // request_test.go contains the start of this, - // in TestParseMultipartFormOrder and others. - } - return -} - -// ParseForm populates r.Form and r.PostForm. -// -// For all requests, ParseForm parses the raw query from the URL and updates -// r.Form. -// -// For POST, PUT, and PATCH requests, it also reads the request body, parses it -// as a form and puts the results into both r.PostForm and r.Form. Request body -// parameters take precedence over URL query string values in r.Form. -// -// If the request Body's size has not already been limited by MaxBytesReader, -// the size is capped at 10MB. -// -// For other HTTP methods, or when the Content-Type is not -// application/x-www-form-urlencoded, the request Body is not read, and -// r.PostForm is initialized to a non-nil, empty value. -// -// ParseMultipartForm calls ParseForm automatically. -// ParseForm is idempotent. -func (r *Request) ParseForm() error { - var err error - if r.PostForm == nil { - if r.Method == "POST" || r.Method == "PUT" || r.Method == "PATCH" { - r.PostForm, err = parsePostForm(r) - } - if r.PostForm == nil { - r.PostForm = make(url.Values) - } - } - if r.Form == nil { - if len(r.PostForm) > 0 { - r.Form = make(url.Values) - copyValues(r.Form, r.PostForm) - } - var newValues url.Values - if r.URL != nil { - var e error - newValues, e = url.ParseQuery(r.URL.RawQuery) - if err == nil { - err = e - } - } - if newValues == nil { - newValues = make(url.Values) - } - if r.Form == nil { - r.Form = newValues - } else { - copyValues(r.Form, newValues) - } - } - return err -} diff --git a/net/http/response.go b/net/http/response.go deleted file mode 100644 index 3015e5270..000000000 --- a/net/http/response.go +++ /dev/null @@ -1,120 +0,0 @@ -package http - -import ( - "crypto/tls" - "io" -) - -// Response represents the response from an HTTP request. -// -// The Client and Transport return Responses from servers once -// the response headers have been received. The response body -// is streamed on demand as the Body field is read. -type Response struct { - Status string // e.g. "200 OK" - StatusCode int // e.g. 200 - Proto string // e.g. "HTTP/1.0" - ProtoMajor int // e.g. 1 - ProtoMinor int // e.g. 0 - - // Header maps header keys to values. If the response had multiple - // headers with the same key, they may be concatenated, with comma - // delimiters. (RFC 7230, section 3.2.2 requires that multiple headers - // be semantically equivalent to a comma-delimited sequence.) When - // Header values are duplicated by other fields in this struct (e.g., - // ContentLength, TransferEncoding, Trailer), the field values are - // authoritative. - // - // Keys in the map are canonicalized (see CanonicalHeaderKey). - Header Header - - // Body represents the response body. - // - // The response body is streamed on demand as the Body field - // is read. If the network connection fails or the server - // terminates the response, Body.Read calls return an error. - // - // The http Client and Transport guarantee that Body is always - // non-nil, even on responses without a body or responses with - // a zero-length body. It is the caller's responsibility to - // close Body. The default HTTP client's Transport may not - // reuse HTTP/1.x "keep-alive" TCP connections if the Body is - // not read to completion and closed. - // - // The Body is automatically dechunked if the server replied - // with a "chunked" Transfer-Encoding. - // - // As of Go 1.12, the Body will also implement io.Writer - // on a successful "101 Switching Protocols" response, - // as used by WebSockets and HTTP/2's "h2c" mode. - Body io.ReadCloser - - // ContentLength records the length of the associated content. The - // value -1 indicates that the length is unknown. Unless Request.Method - // is "HEAD", values >= 0 indicate that the given number of bytes may - // be read from Body. - ContentLength int64 - - // Contains transfer encodings from outer-most to inner-most. Value is - // nil, means that "identity" encoding is used. - TransferEncoding []string - - // Close records whether the header directed that the connection be - // closed after reading Body. The value is advice for clients: neither - // ReadResponse nor Response.Write ever closes a connection. - Close bool - - // Uncompressed reports whether the response was sent compressed but - // was decompressed by the http package. When true, reading from - // Body yields the uncompressed content instead of the compressed - // content actually set from the server, ContentLength is set to -1, - // and the "Content-Length" and "Content-Encoding" fields are deleted - // from the responseHeader. To get the original response from - // the server, set Transport.DisableCompression to true. - Uncompressed bool - - // Trailer maps trailer keys to values in the same - // format as Header. - // - // The Trailer initially contains only nil values, one for - // each key specified in the server's "Trailer" header - // value. Those values are not added to Header. - // - // Trailer must not be accessed concurrently with Read calls - // on the Body. - // - // After Body.Read has returned io.EOF, Trailer will contain - // any trailer values sent by the server. - Trailer Header - - // Request is the request that was sent to obtain this Response. - // Request's Body is nil (having already been consumed). - // This is only populated for Client requests. - Request *Request - - // TLS contains information about the TLS connection on which the - // response was received. It is nil for unencrypted responses. - // The pointer is shared between responses and should not be - // modified. - TLS *tls.ConnectionState -} - -// Cookies parses and returns the cookies set in the Set-Cookie headers. -func (r *Response) Cookies() []*Cookie { - return readSetCookies(r.Header) -} - -// RFC 7234, section 5.4: Should treat -// -// Pragma: no-cache -// -// like -// -// Cache-Control: no-cache -func fixPragmaCacheControl(header Header) { - if hp, ok := header["Pragma"]; ok && len(hp) > 0 && hp[0] == "no-cache" { - if _, presentcc := header["Cache-Control"]; !presentcc { - header["Cache-Control"] = []string{"no-cache"} - } - } -} diff --git a/net/http/server.go b/net/http/server.go deleted file mode 100644 index 383d4f2ac..000000000 --- a/net/http/server.go +++ /dev/null @@ -1,581 +0,0 @@ -package http - -import ( - "fmt" - "net" - "net/url" - urlpkg "net/url" - "path" - "sort" - "strings" - "sync" -) - -// A Handler responds to an HTTP request. -// -// ServeHTTP should write reply headers and data to the ResponseWriter -// and then return. Returning signals that the request is finished; it -// is not valid to use the ResponseWriter or read from the -// Request.Body after or concurrently with the completion of the -// ServeHTTP call. -// -// Depending on the HTTP client software, HTTP protocol version, and -// any intermediaries between the client and the Go server, it may not -// be possible to read from the Request.Body after writing to the -// ResponseWriter. Cautious handlers should read the Request.Body -// first, and then reply. -// -// Except for reading the body, handlers should not modify the -// provided Request. -// -// If ServeHTTP panics, the server (the caller of ServeHTTP) assumes -// that the effect of the panic was isolated to the active request. -// It recovers the panic, logs a stack trace to the server error log, -// and either closes the network connection or sends an HTTP/2 -// RST_STREAM, depending on the HTTP protocol. To abort a handler so -// the client sees an interrupted response but the server doesn't log -// an error, panic with the value ErrAbortHandler. -type Handler interface { - ServeHTTP(ResponseWriter, *Request) -} - -// A ResponseWriter interface is used by an HTTP handler to -// construct an HTTP response. -// -// A ResponseWriter may not be used after the Handler.ServeHTTP method -// has returned. -type ResponseWriter interface { - // Header returns the header map that will be sent by - // WriteHeader. The Header map also is the mechanism with which - // Handlers can set HTTP trailers. - // - // Changing the header map after a call to WriteHeader (or - // Write) has no effect unless the modified headers are - // trailers. - // - // There are two ways to set Trailers. The preferred way is to - // predeclare in the headers which trailers you will later - // send by setting the "Trailer" header to the names of the - // trailer keys which will come later. In this case, those - // keys of the Header map are treated as if they were - // trailers. See the example. The second way, for trailer - // keys not known to the Handler until after the first Write, - // is to prefix the Header map keys with the TrailerPrefix - // constant value. See TrailerPrefix. - // - // To suppress automatic response headers (such as "Date"), set - // their value to nil. - Header() Header - - // Write writes the data to the connection as part of an HTTP reply. - // - // If WriteHeader has not yet been called, Write calls - // WriteHeader(http.StatusOK) before writing the data. If the Header - // does not contain a Content-Type line, Write adds a Content-Type set - // to the result of passing the initial 512 bytes of written data to - // DetectContentType. Additionally, if the total size of all written - // data is under a few KB and there are no Flush calls, the - // Content-Length header is added automatically. - // - // Depending on the HTTP protocol version and the client, calling - // Write or WriteHeader may prevent future reads on the - // Request.Body. For HTTP/1.x requests, handlers should read any - // needed request body data before writing the response. Once the - // headers have been flushed (due to either an explicit Flusher.Flush - // call or writing enough data to trigger a flush), the request body - // may be unavailable. For HTTP/2 requests, the Go HTTP server permits - // handlers to continue to read the request body while concurrently - // writing the response. However, such behavior may not be supported - // by all HTTP/2 clients. Handlers should read before writing if - // possible to maximize compatibility. - Write([]byte) (int, error) - - // WriteHeader sends an HTTP response header with the provided - // status code. - // - // If WriteHeader is not called explicitly, the first call to Write - // will trigger an implicit WriteHeader(http.StatusOK). - // Thus explicit calls to WriteHeader are mainly used to - // send error codes. - // - // The provided code must be a valid HTTP 1xx-5xx status code. - // Only one header may be written. Go does not currently - // support sending user-defined 1xx informational headers, - // with the exception of 100-continue response header that the - // Server sends automatically when the Request.Body is read. - WriteHeader(statusCode int) -} - -// TimeFormat is the time format to use when generating times in HTTP -// headers. It is like time.RFC1123 but hard-codes GMT as the time -// zone. The time being formatted must be in UTC for Format to -// generate the correct format. -// -// For parsing this time format, see ParseTime. -const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT" - -// The HandlerFunc type is an adapter to allow the use of -// ordinary functions as HTTP handlers. If f is a function -// with the appropriate signature, HandlerFunc(f) is a -// Handler that calls f. -type HandlerFunc func(ResponseWriter, *Request) - -// ServeHTTP calls f(w, r). -func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { - f(w, r) -} - -// Helper handlers - -// Error replies to the request with the specified error message and HTTP code. -// It does not otherwise end the request; the caller should ensure no further -// writes are done to w. -// The error message should be plain text. -func Error(w ResponseWriter, error string, code int) { - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - w.Header().Set("X-Content-Type-Options", "nosniff") - w.WriteHeader(code) - fmt.Fprintln(w, error) -} - -// NotFound replies to the request with an HTTP 404 not found error. -func NotFound(w ResponseWriter, r *Request) { Error(w, "404 page not found", StatusNotFound) } - -// NotFoundHandler returns a simple request handler -// that replies to each request with a “404 page not found” reply. -func NotFoundHandler() Handler { return HandlerFunc(NotFound) } - -// StripPrefix returns a handler that serves HTTP requests by removing the -// given prefix from the request URL's Path (and RawPath if set) and invoking -// the handler h. StripPrefix handles a request for a path that doesn't begin -// with prefix by replying with an HTTP 404 not found error. The prefix must -// match exactly: if the prefix in the request contains escaped characters -// the reply is also an HTTP 404 not found error. -func StripPrefix(prefix string, h Handler) Handler { - if prefix == "" { - return h - } - return HandlerFunc(func(w ResponseWriter, r *Request) { - p := strings.TrimPrefix(r.URL.Path, prefix) - rp := strings.TrimPrefix(r.URL.RawPath, prefix) - if len(p) < len(r.URL.Path) && (r.URL.RawPath == "" || len(rp) < len(r.URL.RawPath)) { - r2 := new(Request) - *r2 = *r - r2.URL = new(url.URL) - *r2.URL = *r.URL - r2.URL.Path = p - r2.URL.RawPath = rp - h.ServeHTTP(w, r2) - } else { - NotFound(w, r) - } - }) -} - -// Redirect replies to the request with a redirect to url, -// which may be a path relative to the request path. -// -// The provided code should be in the 3xx range and is usually -// StatusMovedPermanently, StatusFound or StatusSeeOther. -// -// If the Content-Type header has not been set, Redirect sets it -// to "text/html; charset=utf-8" and writes a small HTML body. -// Setting the Content-Type header to any value, including nil, -// disables that behavior. -func Redirect(w ResponseWriter, r *Request, url string, code int) { - if u, err := urlpkg.Parse(url); err == nil { - // If url was relative, make its path absolute by - // combining with request path. - // The client would probably do this for us, - // but doing it ourselves is more reliable. - // See RFC 7231, section 7.1.2 - if u.Scheme == "" && u.Host == "" { - oldpath := r.URL.Path - if oldpath == "" { // should not happen, but avoid a crash if it does - oldpath = "/" - } - - // no leading http://server - if url == "" || url[0] != '/' { - // make relative path absolute - olddir, _ := path.Split(oldpath) - url = olddir + url - } - - var query string - if i := strings.Index(url, "?"); i != -1 { - url, query = url[:i], url[i:] - } - - // clean up but preserve trailing slash - trailing := strings.HasSuffix(url, "/") - url = path.Clean(url) - if trailing && !strings.HasSuffix(url, "/") { - url += "/" - } - url += query - } - } - - h := w.Header() - - // RFC 7231 notes that a short HTML body is usually included in - // the response because older user agents may not understand 301/307. - // Do it only if the request didn't already have a Content-Type header. - _, hadCT := h["Content-Type"] - - h.Set("Location", hexEscapeNonASCII(url)) - if !hadCT && (r.Method == "GET" || r.Method == "HEAD") { - h.Set("Content-Type", "text/html; charset=utf-8") - } - w.WriteHeader(code) - - // Shouldn't send the body for POST or HEAD; that leaves GET. - if !hadCT && r.Method == "GET" { - body := "" + statusText[code] + ".\n" - fmt.Fprintln(w, body) - } -} - -var htmlReplacer = strings.NewReplacer( - "&", "&", - "<", "<", - ">", ">", - // """ is shorter than """. - `"`, """, - // "'" is shorter than "'" and apos was not in HTML until HTML5. - "'", "'", -) - -func htmlEscape(s string) string { - return htmlReplacer.Replace(s) -} - -// Redirect to a fixed URL -type redirectHandler struct { - url string - code int -} - -func (rh *redirectHandler) ServeHTTP(w ResponseWriter, r *Request) { - Redirect(w, r, rh.url, rh.code) -} - -// RedirectHandler returns a request handler that redirects -// each request it receives to the given url using the given -// status code. -// -// The provided code should be in the 3xx range and is usually -// StatusMovedPermanently, StatusFound or StatusSeeOther. -func RedirectHandler(url string, code int) Handler { - return &redirectHandler{url, code} -} - -// ServeMux is an HTTP request multiplexer. -// It matches the URL of each incoming request against a list of registered -// patterns and calls the handler for the pattern that -// most closely matches the URL. -// -// Patterns name fixed, rooted paths, like "/favicon.ico", -// or rooted subtrees, like "/images/" (note the trailing slash). -// Longer patterns take precedence over shorter ones, so that -// if there are handlers registered for both "/images/" -// and "/images/thumbnails/", the latter handler will be -// called for paths beginning "/images/thumbnails/" and the -// former will receive requests for any other paths in the -// "/images/" subtree. -// -// Note that since a pattern ending in a slash names a rooted subtree, -// the pattern "/" matches all paths not matched by other registered -// patterns, not just the URL with Path == "/". -// -// If a subtree has been registered and a request is received naming the -// subtree root without its trailing slash, ServeMux redirects that -// request to the subtree root (adding the trailing slash). This behavior can -// be overridden with a separate registration for the path without -// the trailing slash. For example, registering "/images/" causes ServeMux -// to redirect a request for "/images" to "/images/", unless "/images" has -// been registered separately. -// -// Patterns may optionally begin with a host name, restricting matches to -// URLs on that host only. Host-specific patterns take precedence over -// general patterns, so that a handler might register for the two patterns -// "/codesearch" and "codesearch.google.com/" without also taking over -// requests for "http://www.google.com/". -// -// ServeMux also takes care of sanitizing the URL request path and the Host -// header, stripping the port number and redirecting any request containing . or -// .. elements or repeated slashes to an equivalent, cleaner URL. -type ServeMux struct { - mu sync.RWMutex - m map[string]muxEntry - es []muxEntry // slice of entries sorted from longest to shortest. - hosts bool // whether any patterns contain hostnames -} - -type muxEntry struct { - h Handler - pattern string -} - -// NewServeMux allocates and returns a new ServeMux. -func NewServeMux() *ServeMux { return new(ServeMux) } - -// DefaultServeMux is the default ServeMux used by Serve. -var DefaultServeMux = &defaultServeMux - -var defaultServeMux ServeMux - -// cleanPath returns the canonical path for p, eliminating . and .. elements. -func cleanPath(p string) string { - if p == "" { - return "/" - } - if p[0] != '/' { - p = "/" + p - } - np := path.Clean(p) - // path.Clean removes trailing slash except for root; - // put the trailing slash back if necessary. - if p[len(p)-1] == '/' && np != "/" { - // Fast path for common case of p being the string we want: - if len(p) == len(np)+1 && strings.HasPrefix(p, np) { - np = p - } else { - np += "/" - } - } - return np -} - -// stripHostPort returns h without any trailing ":". -func stripHostPort(h string) string { - // If no port on host, return unchanged - if strings.IndexByte(h, ':') == -1 { - return h - } - host, _, err := net.SplitHostPort(h) - if err != nil { - return h // on error, return unchanged - } - return host -} - -// Find a handler on a handler map given a path string. -// Most-specific (longest) pattern wins. -func (mux *ServeMux) match(path string) (h Handler, pattern string) { - // Check for exact match first. - v, ok := mux.m[path] - if ok { - return v.h, v.pattern - } - - // Check for longest valid match. mux.es contains all patterns - // that end in / sorted from longest to shortest. - for _, e := range mux.es { - if strings.HasPrefix(path, e.pattern) { - return e.h, e.pattern - } - } - return nil, "" -} - -// redirectToPathSlash determines if the given path needs appending "/" to it. -// This occurs when a handler for path + "/" was already registered, but -// not for path itself. If the path needs appending to, it creates a new -// URL, setting the path to u.Path + "/" and returning true to indicate so. -func (mux *ServeMux) redirectToPathSlash(host, path string, u *url.URL) (*url.URL, bool) { - mux.mu.RLock() - shouldRedirect := mux.shouldRedirectRLocked(host, path) - mux.mu.RUnlock() - if !shouldRedirect { - return u, false - } - path = path + "/" - u = &url.URL{Path: path, RawQuery: u.RawQuery} - return u, true -} - -// shouldRedirectRLocked reports whether the given path and host should be redirected to -// path+"/". This should happen if a handler is registered for path+"/" but -// not path -- see comments at ServeMux. -func (mux *ServeMux) shouldRedirectRLocked(host, path string) bool { - p := []string{path, host + path} - - for _, c := range p { - if _, exist := mux.m[c]; exist { - return false - } - } - - n := len(path) - if n == 0 { - return false - } - for _, c := range p { - if _, exist := mux.m[c+"/"]; exist { - return path[n-1] != '/' - } - } - - return false -} - -// Handler returns the handler to use for the given request, -// consulting r.Method, r.Host, and r.URL.Path. It always returns -// a non-nil handler. If the path is not in its canonical form, the -// handler will be an internally-generated handler that redirects -// to the canonical path. If the host contains a port, it is ignored -// when matching handlers. -// -// The path and host are used unchanged for CONNECT requests. -// -// Handler also returns the registered pattern that matches the -// request or, in the case of internally-generated redirects, -// the pattern that will match after following the redirect. -// -// If there is no registered handler that applies to the request, -// Handler returns a “page not found” handler and an empty pattern. -func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) { - - // CONNECT requests are not canonicalized. - if r.Method == "CONNECT" { - // If r.URL.Path is /tree and its handler is not registered, - // the /tree -> /tree/ redirect applies to CONNECT requests - // but the path canonicalization does not. - if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok { - return RedirectHandler(u.String(), StatusMovedPermanently), u.Path - } - - return mux.handler(r.Host, r.URL.Path) - } - - // All other requests have any port stripped and path cleaned - // before passing to mux.handler. - host := stripHostPort(r.Host) - path := cleanPath(r.URL.Path) - - // If the given path is /tree and its handler is not registered, - // redirect for /tree/. - if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok { - return RedirectHandler(u.String(), StatusMovedPermanently), u.Path - } - - if path != r.URL.Path { - _, pattern = mux.handler(host, path) - url := *r.URL - url.Path = path - return RedirectHandler(url.String(), StatusMovedPermanently), pattern - } - - return mux.handler(host, r.URL.Path) -} - -// handler is the main implementation of Handler. -// The path is known to be in canonical form, except for CONNECT methods. -func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) { - mux.mu.RLock() - defer mux.mu.RUnlock() - - // Host-specific pattern takes precedence over generic ones - if mux.hosts { - h, pattern = mux.match(host + path) - } - if h == nil { - h, pattern = mux.match(path) - } - if h == nil { - h, pattern = NotFoundHandler(), "" - } - return -} - -// ServeHTTP dispatches the request to the handler whose -// pattern most closely matches the request URL. -func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { - if r.RequestURI == "*" { - if r.ProtoAtLeast(1, 1) { - w.Header().Set("Connection", "close") - } - w.WriteHeader(StatusBadRequest) - return - } - h, _ := mux.Handler(r) - h.ServeHTTP(w, r) -} - -// Handle registers the handler for the given pattern. -// If a handler already exists for pattern, Handle panics. -func (mux *ServeMux) Handle(pattern string, handler Handler) { - mux.mu.Lock() - defer mux.mu.Unlock() - - if pattern == "" { - panic("http: invalid pattern") - } - if handler == nil { - panic("http: nil handler") - } - if _, exist := mux.m[pattern]; exist { - panic("http: multiple registrations for " + pattern) - } - - if mux.m == nil { - mux.m = make(map[string]muxEntry) - } - e := muxEntry{h: handler, pattern: pattern} - mux.m[pattern] = e - if pattern[len(pattern)-1] == '/' { - mux.es = appendSorted(mux.es, e) - } - - if pattern[0] != '/' { - mux.hosts = true - } -} - -func appendSorted(es []muxEntry, e muxEntry) []muxEntry { - n := len(es) - i := sort.Search(n, func(i int) bool { - return len(es[i].pattern) < len(e.pattern) - }) - if i == n { - return append(es, e) - } - // we now know that i points at where we want to insert - es = append(es, muxEntry{}) // try to grow the slice in place, any entry works. - copy(es[i+1:], es[i:]) // Move shorter entries down - es[i] = e - return es -} - -// HandleFunc registers the handler function for the given pattern. -func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { - if handler == nil { - panic("http: nil handler") - } - mux.Handle(pattern, HandlerFunc(handler)) -} - -// Handle registers the handler for the given pattern -// in the DefaultServeMux. -// The documentation for ServeMux explains how patterns are matched. -func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) } - -// HandleFunc registers the handler function for the given pattern -// in the DefaultServeMux. -// The documentation for ServeMux explains how patterns are matched. -func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { - DefaultServeMux.HandleFunc(pattern, handler) -} - -// ListenAndServe listens on the TCP network address addr and then calls -// Serve with handler to handle requests on incoming connections. -// Accepted connections are configured to enable TCP keep-alives. -// -// The handler is typically nil, in which case the DefaultServeMux is used. -// -// ListenAndServe always returns a non-nil error. -func ListenAndServe(addr string, handler Handler) error { - return ActiveDevice.ListenAndServe(addr, handler) -} diff --git a/net/http/status.go b/net/http/status.go deleted file mode 100644 index 286315f63..000000000 --- a/net/http/status.go +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package http - -// HTTP status codes as registered with IANA. -// See: https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml -const ( - StatusContinue = 100 // RFC 7231, 6.2.1 - StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2 - StatusProcessing = 102 // RFC 2518, 10.1 - StatusEarlyHints = 103 // RFC 8297 - - StatusOK = 200 // RFC 7231, 6.3.1 - StatusCreated = 201 // RFC 7231, 6.3.2 - StatusAccepted = 202 // RFC 7231, 6.3.3 - StatusNonAuthoritativeInfo = 203 // RFC 7231, 6.3.4 - StatusNoContent = 204 // RFC 7231, 6.3.5 - StatusResetContent = 205 // RFC 7231, 6.3.6 - StatusPartialContent = 206 // RFC 7233, 4.1 - StatusMultiStatus = 207 // RFC 4918, 11.1 - StatusAlreadyReported = 208 // RFC 5842, 7.1 - StatusIMUsed = 226 // RFC 3229, 10.4.1 - - StatusMultipleChoices = 300 // RFC 7231, 6.4.1 - StatusMovedPermanently = 301 // RFC 7231, 6.4.2 - StatusFound = 302 // RFC 7231, 6.4.3 - StatusSeeOther = 303 // RFC 7231, 6.4.4 - StatusNotModified = 304 // RFC 7232, 4.1 - StatusUseProxy = 305 // RFC 7231, 6.4.5 - _ = 306 // RFC 7231, 6.4.6 (Unused) - StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7 - StatusPermanentRedirect = 308 // RFC 7538, 3 - - StatusBadRequest = 400 // RFC 7231, 6.5.1 - StatusUnauthorized = 401 // RFC 7235, 3.1 - StatusPaymentRequired = 402 // RFC 7231, 6.5.2 - StatusForbidden = 403 // RFC 7231, 6.5.3 - StatusNotFound = 404 // RFC 7231, 6.5.4 - StatusMethodNotAllowed = 405 // RFC 7231, 6.5.5 - StatusNotAcceptable = 406 // RFC 7231, 6.5.6 - StatusProxyAuthRequired = 407 // RFC 7235, 3.2 - StatusRequestTimeout = 408 // RFC 7231, 6.5.7 - StatusConflict = 409 // RFC 7231, 6.5.8 - StatusGone = 410 // RFC 7231, 6.5.9 - StatusLengthRequired = 411 // RFC 7231, 6.5.10 - StatusPreconditionFailed = 412 // RFC 7232, 4.2 - StatusRequestEntityTooLarge = 413 // RFC 7231, 6.5.11 - StatusRequestURITooLong = 414 // RFC 7231, 6.5.12 - StatusUnsupportedMediaType = 415 // RFC 7231, 6.5.13 - StatusRequestedRangeNotSatisfiable = 416 // RFC 7233, 4.4 - StatusExpectationFailed = 417 // RFC 7231, 6.5.14 - StatusTeapot = 418 // RFC 7168, 2.3.3 - StatusMisdirectedRequest = 421 // RFC 7540, 9.1.2 - StatusUnprocessableEntity = 422 // RFC 4918, 11.2 - StatusLocked = 423 // RFC 4918, 11.3 - StatusFailedDependency = 424 // RFC 4918, 11.4 - StatusTooEarly = 425 // RFC 8470, 5.2. - StatusUpgradeRequired = 426 // RFC 7231, 6.5.15 - StatusPreconditionRequired = 428 // RFC 6585, 3 - StatusTooManyRequests = 429 // RFC 6585, 4 - StatusRequestHeaderFieldsTooLarge = 431 // RFC 6585, 5 - StatusUnavailableForLegalReasons = 451 // RFC 7725, 3 - - StatusInternalServerError = 500 // RFC 7231, 6.6.1 - StatusNotImplemented = 501 // RFC 7231, 6.6.2 - StatusBadGateway = 502 // RFC 7231, 6.6.3 - StatusServiceUnavailable = 503 // RFC 7231, 6.6.4 - StatusGatewayTimeout = 504 // RFC 7231, 6.6.5 - StatusHTTPVersionNotSupported = 505 // RFC 7231, 6.6.6 - StatusVariantAlsoNegotiates = 506 // RFC 2295, 8.1 - StatusInsufficientStorage = 507 // RFC 4918, 11.5 - StatusLoopDetected = 508 // RFC 5842, 7.2 - StatusNotExtended = 510 // RFC 2774, 7 - StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6 -) - -var statusText = map[int]string{ - StatusContinue: "Continue", - StatusSwitchingProtocols: "Switching Protocols", - StatusProcessing: "Processing", - StatusEarlyHints: "Early Hints", - - StatusOK: "OK", - StatusCreated: "Created", - StatusAccepted: "Accepted", - StatusNonAuthoritativeInfo: "Non-Authoritative Information", - StatusNoContent: "No Content", - StatusResetContent: "Reset Content", - StatusPartialContent: "Partial Content", - StatusMultiStatus: "Multi-Status", - StatusAlreadyReported: "Already Reported", - StatusIMUsed: "IM Used", - - StatusMultipleChoices: "Multiple Choices", - StatusMovedPermanently: "Moved Permanently", - StatusFound: "Found", - StatusSeeOther: "See Other", - StatusNotModified: "Not Modified", - StatusUseProxy: "Use Proxy", - StatusTemporaryRedirect: "Temporary Redirect", - StatusPermanentRedirect: "Permanent Redirect", - - StatusBadRequest: "Bad Request", - StatusUnauthorized: "Unauthorized", - StatusPaymentRequired: "Payment Required", - StatusForbidden: "Forbidden", - StatusNotFound: "Not Found", - StatusMethodNotAllowed: "Method Not Allowed", - StatusNotAcceptable: "Not Acceptable", - StatusProxyAuthRequired: "Proxy Authentication Required", - StatusRequestTimeout: "Request Timeout", - StatusConflict: "Conflict", - StatusGone: "Gone", - StatusLengthRequired: "Length Required", - StatusPreconditionFailed: "Precondition Failed", - StatusRequestEntityTooLarge: "Request Entity Too Large", - StatusRequestURITooLong: "Request URI Too Long", - StatusUnsupportedMediaType: "Unsupported Media Type", - StatusRequestedRangeNotSatisfiable: "Requested Range Not Satisfiable", - StatusExpectationFailed: "Expectation Failed", - StatusTeapot: "I'm a teapot", - StatusMisdirectedRequest: "Misdirected Request", - StatusUnprocessableEntity: "Unprocessable Entity", - StatusLocked: "Locked", - StatusFailedDependency: "Failed Dependency", - StatusTooEarly: "Too Early", - StatusUpgradeRequired: "Upgrade Required", - StatusPreconditionRequired: "Precondition Required", - StatusTooManyRequests: "Too Many Requests", - StatusRequestHeaderFieldsTooLarge: "Request Header Fields Too Large", - StatusUnavailableForLegalReasons: "Unavailable For Legal Reasons", - - StatusInternalServerError: "Internal Server Error", - StatusNotImplemented: "Not Implemented", - StatusBadGateway: "Bad Gateway", - StatusServiceUnavailable: "Service Unavailable", - StatusGatewayTimeout: "Gateway Timeout", - StatusHTTPVersionNotSupported: "HTTP Version Not Supported", - StatusVariantAlsoNegotiates: "Variant Also Negotiates", - StatusInsufficientStorage: "Insufficient Storage", - StatusLoopDetected: "Loop Detected", - StatusNotExtended: "Not Extended", - StatusNetworkAuthenticationRequired: "Network Authentication Required", -} - -// StatusText returns a text for the HTTP status code. It returns the empty -// string if the code is unknown. -func StatusText(code int) string { - return statusText[code] -} diff --git a/net/http/tinygo.go b/net/http/tinygo.go deleted file mode 100644 index 6ac34495a..000000000 --- a/net/http/tinygo.go +++ /dev/null @@ -1,308 +0,0 @@ -package http - -import ( - "bufio" - "bytes" - "fmt" - "io" - "strconv" - "strings" - "time" - - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/net/tls" -) - -var buf []byte - -func SetBuf(b []byte) { - buf = b -} - -func (c *Client) Do(req *Request) (*Response, error) { - if c.Jar != nil { - for _, cookie := range c.Jar.Cookies(req.URL) { - req.AddCookie(cookie) - } - } - switch req.URL.Scheme { - case "http": - return c.doHTTP(req) - case "https": - return c.doHTTPS(req) - default: - return nil, fmt.Errorf("invalid schemer : %s", req.URL.Scheme) - } -} - -func (c *Client) doHTTP(req *Request) (*Response, error) { - // make TCP connection - ip := net.ParseIP(req.URL.Hostname()) - port := 80 - if req.URL.Port() != "" { - p, err := strconv.ParseUint(req.URL.Port(), 0, 64) - if err != nil { - return nil, err - } - port = int(p) - } - raddr := &net.TCPAddr{IP: ip, Port: port} - laddr := &net.TCPAddr{Port: 8080} - - conn, err := net.DialTCP("tcp", laddr, raddr) - retry := 0 - for ; err != nil; conn, err = net.DialTCP("tcp", laddr, raddr) { - retry++ - if retry > 10 { - return nil, fmt.Errorf("Connection failed: %s", err.Error()) - } - time.Sleep(1 * time.Second) - } - - p := req.URL.Path - if p == "" { - p = "/" - } - if req.URL.RawQuery != "" { - p += "?" + req.URL.RawQuery - } - fmt.Fprintln(conn, req.Method+" "+p+" HTTP/1.1") - fmt.Fprintln(conn, "Host:", req.URL.Host) - - if req.Header.get(`User-Agent`) == "" { - fmt.Fprintln(conn, "User-Agent: TinyGo") - } - - for k, v := range req.Header { - if v == nil || len(v) == 0 { - return nil, fmt.Errorf("req.Header error: %s", k) - } - fmt.Fprintln(conn, k+": "+v[0]) - } - - if req.Header.get(`Connection`) == "" { - fmt.Fprintln(conn, "Connection: close") - } - - if req.ContentLength > 0 { - fmt.Fprintf(conn, "Content-Length: %d\n", req.ContentLength) - } - - fmt.Fprintln(conn) - - if req.ContentLength > 0 { - b, err := req.GetBody() - if err != nil { - return nil, err - } - - n, err := b.Read(buf) - if err != nil { - return nil, err - } - conn.Write(buf[:n]) - - b.Close() - - } - - return c.doResp(conn, req) -} - -func (c *Client) doHTTPS(req *Request) (*Response, error) { - conn, err := tls.Dial("tcp", req.URL.Host, nil) - retry := 0 - for ; err != nil; conn, err = tls.Dial("tcp", req.URL.Host, nil) { - retry++ - if retry > 10 { - return nil, fmt.Errorf("Connection failed: %s", err.Error()) - } - time.Sleep(1 * time.Second) - } - - p := req.URL.Path - if p == "" { - p = "/" - } - if req.URL.RawQuery != "" { - p += "?" + req.URL.RawQuery - } - fmt.Fprintln(conn, req.Method+" "+p+" HTTP/1.1") - fmt.Fprintln(conn, "Host:", req.URL.Host) - - if req.Header.get(`User-Agent`) == "" { - fmt.Fprintln(conn, "User-Agent: TinyGo") - } - - for k, v := range req.Header { - if v == nil || len(v) == 0 { - return nil, fmt.Errorf("req.Header error: %s", k) - } - fmt.Fprintln(conn, k+": "+v[0]) - } - - if req.Header.get(`Connection`) == "" { - fmt.Fprintln(conn, "Connection: close") - } - - if req.ContentLength > 0 { - fmt.Fprintf(conn, "Content-Length: %d\n", req.ContentLength) - } - - fmt.Fprintln(conn) - - if req.ContentLength > 0 { - b, err := req.GetBody() - if err != nil { - return nil, err - } - - n, err := b.Read(buf) - if err != nil { - return nil, err - } - conn.Write(buf[:n]) - - b.Close() - - } - - return c.doResp(conn, req) -} - -func (c *Client) doResp(conn net.Conn, req *Request) (*Response, error) { - resp := &Response{ - Header: map[string][]string{}, - } - - // Header - var scanner *bufio.Scanner - cont := true - ofs := 0 - remain := int64(0) - for cont { - for n, err := conn.Read(buf[ofs:]); n > 0; n, err = conn.Read(buf[ofs:]) { - if err != nil { - println("Read error: " + err.Error()) - } else { - // Take care of the case where "\r\n\r\n" is on the boundary of a buffer - start := ofs - if start > 3 { - start -= 3 - } - idx := bytes.Index(buf[start:ofs+n], []byte("\r\n\r\n")) - if idx == -1 { - ofs += n - continue - } - idx += start + 4 - - scanner = bufio.NewScanner(bytes.NewReader(buf[0 : ofs+n])) - if resp.Status == "" && scanner.Scan() { - status := strings.SplitN(scanner.Text(), " ", 2) - if len(status) != 2 { - conn.Close() - return nil, fmt.Errorf("invalid status : %q", scanner.Text()) - } - resp.Proto = status[0] - fmt.Sscanf(status[0], "HTTP/%d.%d", &resp.ProtoMajor, &resp.ProtoMinor) - - resp.Status = status[1] - fmt.Sscanf(status[1], "%d", &resp.StatusCode) - } - - for scanner.Scan() { - text := scanner.Text() - if text == "" { - // end of header - if idx < n+ofs { - ofs = ofs + n - idx - for i := 0; i < ofs; i++ { - buf[i] = buf[i+idx] - } - } else { - ofs = 0 - } - break - } else { - header := strings.SplitN(text, ": ", 2) - if len(header) != 2 { - conn.Close() - return nil, fmt.Errorf("invalid header : %q", text) - } - if resp.Header.Get(header[0]) == "" { - resp.Header.Set(header[0], header[1]) - } else { - resp.Header.Add(header[0], header[1]) - } - - if strings.ToLower(header[0]) == "content-length" { - resp.ContentLength, err = strconv.ParseInt(header[1], 10, 64) - if err != nil { - conn.Close() - return nil, err - } - remain = resp.ContentLength - } - } - } - cont = false - break - } - } - } - - // Body - remain -= int64(ofs) - if remain <= 0 { - resp.Body = io.NopCloser(bytes.NewReader(buf[:ofs])) - if c.Jar != nil { - if rc := resp.Cookies(); len(rc) > 0 { - c.Jar.SetCookies(req.URL, rc) - } - } - return resp, conn.Close() - } - - cont = true - lastRequestTime := time.Now() - for cont { - for { - end := ofs + 0x400 - if len(buf) < end { - return nil, fmt.Errorf("slice out of range : use http.SetBuf() to change the allocation to %d bytes or more", end) - } - n, err := conn.Read(buf[ofs : ofs+0x400]) - if err != nil { - return nil, err - } - if n == 0 { - continue - } - if err != nil { - conn.Close() - return nil, err - } else { - ofs += n - remain -= int64(n) - if remain <= 0 { - resp.Body = io.NopCloser(bytes.NewReader(buf[:ofs])) - cont = false - break - } - if time.Now().Sub(lastRequestTime).Milliseconds() >= 1000 { - conn.Close() - return nil, fmt.Errorf("time out") - } - } - } - } - - if c.Jar != nil { - if rc := resp.Cookies(); len(rc) > 0 { - c.Jar.SetCookies(req.URL, rc) - } - } - - return resp, conn.Close() -} diff --git a/net/http/transefer.go b/net/http/transefer.go deleted file mode 100644 index d9a440621..000000000 --- a/net/http/transefer.go +++ /dev/null @@ -1,34 +0,0 @@ -package http - -import ( - "bufio" - - "golang.org/x/net/http/httpguts" -) - -// msg is *Request or *Response. -func readTransfer(msg *Request, r *bufio.Reader) (err error) { - // TODO: - return nil -} - -// Determine whether to hang up after sending a request and body, or -// receiving a response and body -// 'header' is the request headers -func shouldClose(major, minor int, header Header, removeCloseHeader bool) bool { - if major < 1 { - return true - } - - conv := header["Connection"] - hasClose := httpguts.HeaderValuesContainsToken(conv, "close") - if major == 1 && minor == 0 { - return hasClose || !httpguts.HeaderValuesContainsToken(conv, "keep-alive") - } - - if hasClose && removeCloseHeader { - header.Del("Connection") - } - - return hasClose -} diff --git a/net/ipsocki.go b/net/ipsocki.go deleted file mode 100644 index dfc30c9b2..000000000 --- a/net/ipsocki.go +++ /dev/null @@ -1,26 +0,0 @@ -package net - -import "strings" - -// SplitHostPort splits a network address of the form "host:port", -// "host%zone:port", "[host]:port" or "[host%zone]:port" into host or -// host%zone and port. -// -// A literal IPv6 address in hostport must be enclosed in square -// brackets, as in "[::1]:80", "[::1%lo0]:80". -// -// See func Dial for a description of the hostport parameter, and host -// and port results. -func SplitHostPort(hostport string) (host, port string, err error) { - - if strings.Contains(hostport, ":") { - spl := strings.Split(hostport, ":") - host = spl[0] - port = spl[1] - } else { - host = hostport - port = "80" - } - - return host, port, nil -} diff --git a/net/mqtt/mqtt.go b/net/mqtt/mqtt.go deleted file mode 100644 index c18641ba9..000000000 --- a/net/mqtt/mqtt.go +++ /dev/null @@ -1,396 +0,0 @@ -// Package mqtt is intended to provide compatible interfaces with the -// Paho mqtt library. -package mqtt - -import ( - "errors" - "strings" - "sync" - "time" - - "github.com/eclipse/paho.mqtt.golang/packets" - "tinygo.org/x/drivers/net" - "tinygo.org/x/drivers/net/tls" -) - -// NewClient will create an MQTT v3.1.1 client with all of the options specified -// in the provided ClientOptions. The client must have the Connect method called -// on it before it may be used. This is to make sure resources (such as a net -// connection) are created before the application is actually ready. -func NewClient(o *ClientOptions) Client { - c := &mqttclient{opts: o, adaptor: o.Adaptor} - c.msgRouter, c.stopRouter = newRouter() - - c.inboundPacketChan = make(chan packets.ControlPacket, 10) - c.stopInbound = make(chan struct{}) - c.incomingPubChan = make(chan *packets.PublishPacket, 10) - // this launches a goroutine, so only call once per client: - c.msgRouter.matchAndDispatch(c.incomingPubChan, c.opts.Order, c) - return c -} - -type mqttclient struct { - adaptor net.Adapter - conn net.Conn - connected bool - opts *ClientOptions - mid uint16 - inboundPacketChan chan packets.ControlPacket - stopInbound chan struct{} - msgRouter *router - stopRouter chan bool - incomingPubChan chan *packets.PublishPacket - // stats for keepalive - lastReceive time.Time - lastSend time.Time - // keep track of routines and signal a shutdown - workers sync.WaitGroup - shutdown bool -} - -// AddRoute allows you to add a handler for messages on a specific topic -// without making a subscription. For example having a different handler -// for parts of a wildcard subscription -func (c *mqttclient) AddRoute(topic string, callback MessageHandler) { - return -} - -// IsConnected returns a bool signifying whether -// the client is connected or not. -func (c *mqttclient) IsConnected() bool { - return c.connected -} - -// IsConnectionOpen return a bool signifying whether the client has an active -// connection to mqtt broker, i.e not in disconnected or reconnect mode -func (c *mqttclient) IsConnectionOpen() bool { - return c.connected -} - -// Connect will create a connection to the message broker. -func (c *mqttclient) Connect() Token { - if c.IsConnected() { - return &mqtttoken{} - } - var err error - - // make connection - if strings.Contains(c.opts.Servers, "ssl://") { - url := strings.TrimPrefix(c.opts.Servers, "ssl://") - c.conn, err = tls.Dial("tcp", url, nil) - if err != nil { - return &mqtttoken{err: err} - } - } else if strings.Contains(c.opts.Servers, "tcp://") { - url := strings.TrimPrefix(c.opts.Servers, "tcp://") - c.conn, err = net.Dial("tcp", url) - if err != nil { - return &mqtttoken{err: err} - } - } else { - // invalid protocol - return &mqtttoken{err: errors.New("invalid protocol")} - } - - c.mid = 1 - - // send the MQTT connect message - connectPkt := packets.NewControlPacket(packets.Connect).(*packets.ConnectPacket) - connectPkt.Qos = 0 - if c.opts.Username != "" { - connectPkt.Username = c.opts.Username - connectPkt.UsernameFlag = true - } - - if c.opts.Password != "" { - connectPkt.Password = []byte(c.opts.Password) - connectPkt.PasswordFlag = true - } - - connectPkt.ClientIdentifier = c.opts.ClientID - connectPkt.ProtocolVersion = byte(c.opts.ProtocolVersion) - connectPkt.ProtocolName = "MQTT" - connectPkt.Keepalive = uint16(c.opts.KeepAlive) - - connectPkt.WillFlag = c.opts.WillEnabled - connectPkt.WillTopic = c.opts.WillTopic - connectPkt.WillMessage = c.opts.WillPayload - connectPkt.WillQos = c.opts.WillQos - connectPkt.WillRetain = c.opts.WillRetained - - err = connectPkt.Write(c.conn) - if err != nil { - return &mqtttoken{err: err} - } - c.lastSend = time.Now() - - // TODO: handle timeout as ReadPacket blocks until it gets a packet. - // CONNECT response. - packet, err := packets.ReadPacket(c.conn) - if err != nil { - return &mqtttoken{err: err} - } - if packet != nil { - ack, ok := packet.(*packets.ConnackPacket) - if ok { - if ack.ReturnCode != 0 { - return &mqtttoken{err: errors.New(packet.String())} - } - c.connected = true - } - } - - go processInbound(c) - go readMessages(c) - go keepAlive(c) - - return &mqtttoken{} -} - -// Disconnect will end the connection with the server, but not before waiting -// the specified number of milliseconds to wait for existing work to be -// completed. Blocks until disconnected. -func (c *mqttclient) Disconnect(quiesce uint) { - c.shutdownRoutines() - // block until all done - for c.connected { - time.Sleep(time.Millisecond * 10) - } - return -} - -// shutdownRoutines will disconnect and shut down all processes. If you want to trigger a -// disconnect internally, make sure you call this instead of Disconnect() to avoid deadlocks -func (c *mqttclient) shutdownRoutines() { - if c.shutdown { - return - } - c.shutdown = true - c.conn.Close() - c.stopInbound <- struct{}{} -} - -// Publish will publish a message with the specified QoS and content -// to the specified topic. -// Returns a token to track delivery of the message to the broker -func (c *mqttclient) Publish(topic string, qos byte, retained bool, payload interface{}) Token { - if !c.IsConnected() { - return &mqtttoken{err: errors.New("MQTT client not connected")} - } - - pub := packets.NewControlPacket(packets.Publish).(*packets.PublishPacket) - pub.Qos = qos - pub.TopicName = topic - pub.Retain = retained - - switch payload.(type) { - case string: - pub.Payload = []byte(payload.(string)) - case []byte: - pub.Payload = payload.([]byte) - default: - return &mqtttoken{err: errors.New("Unknown payload type")} - } - pub.MessageID = c.mid - c.mid++ - - err := pub.Write(c.conn) - if err != nil { - return &mqtttoken{err: err} - } - // update this for every control message that is sent successfully, for keepalive - c.lastSend = time.Now() - - return &mqtttoken{} -} - -// Subscribe starts a new subscription. Provide a MessageHandler to be executed when -// a message is published on the topic provided. -func (c *mqttclient) Subscribe(topic string, qos byte, callback MessageHandler) Token { - if !c.IsConnected() { - return &mqtttoken{err: errors.New("MQTT client not connected")} - } - - sub := packets.NewControlPacket(packets.Subscribe).(*packets.SubscribePacket) - sub.Topics = append(sub.Topics, topic) - sub.Qoss = append(sub.Qoss, qos) - - if callback != nil { - c.msgRouter.addRoute(topic, callback) - } - - sub.MessageID = c.mid - c.mid++ - - // drop in the channel to send - err := sub.Write(c.conn) - if err != nil { - return &mqtttoken{err: err} - } - c.lastSend = time.Now() - - return &mqtttoken{} -} - -// SubscribeMultiple starts a new subscription for multiple topics. Provide a MessageHandler to -// be executed when a message is published on one of the topics provided. -func (c *mqttclient) SubscribeMultiple(filters map[string]byte, callback MessageHandler) Token { - return &mqtttoken{} -} - -// Unsubscribe will end the subscription from each of the topics provided. -// Messages published to those topics from other clients will no longer be -// received. -func (c *mqttclient) Unsubscribe(topics ...string) Token { - return &mqtttoken{} -} - -// OptionsReader returns a ClientOptionsReader which is a copy of the clientoptions -// in use by the client. -func (c *mqttclient) OptionsReader() ClientOptionsReader { - r := ClientOptionsReader{} - return r -} - -func processInbound(c *mqttclient) { -PROCESS: - for { - select { - case msg := <-c.inboundPacketChan: - switch m := msg.(type) { - case *packets.PingrespPacket: - // println("pong") - case *packets.SubackPacket: - // TODO: handle this - case *packets.UnsubackPacket: - // TODO: handle this - case *packets.PublishPacket: - // TODO: handle Qos - c.incomingPubChan <- m - case *packets.PubackPacket: - // TODO: handle this - case *packets.PubrecPacket: - // TODO: handle this - case *packets.PubrelPacket: - // TODO: handle this - case *packets.PubcompPacket: - // TODO: handle this - } - case <-c.stopInbound: - break PROCESS - } - } - - // as this routine could be the last to finish (if a lot of messages are queued in the - // channel), it is the last to turn out the lights - - c.workers.Wait() - c.connected = false - c.shutdown = false -} - -// readMessages reads incoming messages off the wire. -// incoming messages are then send into inbound buffered channel. -func readMessages(c *mqttclient) { - c.workers.Add(1) - defer c.workers.Done() - - var err error - var cp packets.ControlPacket - - for !c.shutdown { - if cp, err = c.ReadPacket(); err != nil { - c.shutdownRoutines() - return - } - if cp != nil { - c.inboundPacketChan <- cp - // notify keepalive logic that we recently received a packet - c.lastReceive = time.Now() - } - - time.Sleep(100 * time.Millisecond) - } -} - -// keepAlive is a goroutine to handle sending ping requests according to the MQTT spec. If the keepalive time has -// been reached with no messages being sent, we will send a ping request and check back to see if we've -// had any activity by the timeout. If not, disconnect. -func keepAlive(c *mqttclient) { - c.workers.Add(1) - defer c.workers.Done() - - var err error - var ping *packets.PingreqPacket - var timeout, pingsent time.Time - - for !c.shutdown { - // As long as we haven't reached the keepalive value... - if time.Since(c.lastSend) < time.Duration(c.opts.KeepAlive)*time.Second { - // ...sleep and check shutdown status again - time.Sleep(time.Millisecond * 100) - continue - } - - // value has been reached, so send a ping request - ping = packets.NewControlPacket(packets.Pingreq).(*packets.PingreqPacket) - if err = ping.Write(c.conn); err != nil { - // if connection is lost, report disconnect - c.shutdownRoutines() - return - } - // println("ping") - - c.lastSend = time.Now() - pingsent = time.Now() - timeout = pingsent.Add(c.opts.PingTimeout) - - // as long as we are still connected and haven't received anything after the ping... - for !c.shutdown && c.lastReceive.Before(pingsent) { - // if the timeout has passed, disconnect - if time.Now().After(timeout) { - c.shutdownRoutines() - return - } - time.Sleep(time.Millisecond * 100) - } - } -} - -func (c *mqttclient) ackFunc(packet *packets.PublishPacket) func() { - return func() { - switch packet.Qos { - case 2: - // pr := packets.NewControlPacket(packets.Pubrec).(*packets.PubrecPacket) - // pr.MessageID = packet.MessageID - // DEBUG.Println(NET, "putting pubrec msg on obound") - // select { - // case c.oboundP <- &PacketAndToken{p: pr, t: nil}: - // case <-c.stop: - // } - // DEBUG.Println(NET, "done putting pubrec msg on obound") - case 1: - // pa := packets.NewControlPacket(packets.Puback).(*packets.PubackPacket) - // pa.MessageID = packet.MessageID - // DEBUG.Println(NET, "putting puback msg on obound") - // persistOutbound(c.persist, pa) - // select { - // case c.oboundP <- &PacketAndToken{p: pa, t: nil}: - // case <-c.stop: - // } - // DEBUG.Println(NET, "done putting puback msg on obound") - case 0: - // do nothing, since there is no need to send an ack packet back - } - } -} - -// ReadPacket tries to read the next incoming packet from the MQTT broker. -// If there is no data yet but also is no error, it returns nil for both values. -func (c *mqttclient) ReadPacket() (packets.ControlPacket, error) { - // check for data first... - if net.ActiveDevice.IsSocketDataAvailable() { - return packets.ReadPacket(c.conn) - } - return nil, nil -} diff --git a/net/mqtt/paho.go b/net/mqtt/paho.go deleted file mode 100644 index ff9c7af52..000000000 --- a/net/mqtt/paho.go +++ /dev/null @@ -1,299 +0,0 @@ -// The following code is a slightly modified version of code taken from the Paho MQTT library. -// It is here until TinyGo can compile the "net" package from the standard library, at which time -// it can be removed. - -/* - * Copyright (c) 2013 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Seth Hoenig - * Allan Stockdill-Mander - * Mike Robertson - */ - -// Portions copyright © 2018 TIBCO Software Inc. - -package mqtt - -import ( - "strings" - "time" - - "github.com/eclipse/paho.mqtt.golang/packets" - "tinygo.org/x/drivers/net" -) - -const ( - disconnected uint32 = iota - connecting - reconnecting - connected -) - -// Client is the interface definition for a Client as used by this -// library, the interface is primarily to allow mocking tests. -// -// It is an MQTT v3.1.1 client for communicating -// with an MQTT server using non-blocking methods that allow work -// to be done in the background. -// An application may connect to an MQTT server using: -// -// A plain TCP socket -// A secure SSL/TLS socket -// A websocket -// -// To enable ensured message delivery at Quality of Service (QoS) levels -// described in the MQTT spec, a message persistence mechanism must be -// used. This is done by providing a type which implements the Store -// interface. For convenience, FileStore and MemoryStore are provided -// implementations that should be sufficient for most use cases. More -// information can be found in their respective documentation. -// Numerous connection options may be specified by configuring a -// and then supplying a ClientOptions type. -type Client interface { - // IsConnected returns a bool signifying whether - // the client is connected or not. - IsConnected() bool - // IsConnectionOpen return a bool signifying wether the client has an active - // connection to mqtt broker, i.e not in disconnected or reconnect mode - IsConnectionOpen() bool - // Connect will create a connection to the message broker, by default - // it will attempt to connect at v3.1.1 and auto retry at v3.1 if that - // fails - Connect() Token - // Disconnect will end the connection with the server, but not before waiting - // the specified number of milliseconds to wait for existing work to be - // completed. - Disconnect(quiesce uint) - // Publish will publish a message with the specified QoS and content - // to the specified topic. - // Returns a token to track delivery of the message to the broker - Publish(topic string, qos byte, retained bool, payload interface{}) Token - // Subscribe starts a new subscription. Provide a MessageHandler to be executed when - // a message is published on the topic provided, or nil for the default handler - Subscribe(topic string, qos byte, callback MessageHandler) Token - // SubscribeMultiple starts a new subscription for multiple topics. Provide a MessageHandler to - // be executed when a message is published on one of the topics provided, or nil for the - // default handler - SubscribeMultiple(filters map[string]byte, callback MessageHandler) Token - // Unsubscribe will end the subscription from each of the topics provided. - // Messages published to those topics from other clients will no longer be - // received. - Unsubscribe(topics ...string) Token - // AddRoute allows you to add a handler for messages on a specific topic - // without making a subscription. For example having a different handler - // for parts of a wildcard subscription - AddRoute(topic string, callback MessageHandler) - // OptionsReader returns a ClientOptionsReader which is a copy of the clientoptions - // in use by the client. - OptionsReader() ClientOptionsReader -} - -// Token defines the interface for the tokens used to indicate when -// actions have completed. -type Token interface { - Wait() bool - WaitTimeout(time.Duration) bool - Error() error -} - -// MessageHandler is a callback type which can be set to be -// executed upon the arrival of messages published to topics -// to which the client is subscribed. -type MessageHandler func(Client, Message) - -// Message defines the externals that a message implementation must support -// these are received messages that are passed to the callbacks, not internal -// messages -type Message interface { - Duplicate() bool - Qos() byte - Retained() bool - Topic() string - MessageID() uint16 - Payload() []byte - Ack() -} - -type message struct { - duplicate bool - qos byte - retained bool - topic string - messageID uint16 - payload []byte - ack func() -} - -func (m *message) Duplicate() bool { - return m.duplicate -} - -func (m *message) Qos() byte { - return m.qos -} - -func (m *message) Retained() bool { - return m.retained -} - -func (m *message) Topic() string { - return m.topic -} - -func (m *message) MessageID() uint16 { - return m.messageID -} - -func (m *message) Payload() []byte { - return m.payload -} - -func (m *message) Ack() { - return -} - -func messageFromPublish(p *packets.PublishPacket, ack func()) Message { - return &message{ - duplicate: p.Dup, - qos: p.Qos, - retained: p.Retain, - topic: p.TopicName, - messageID: p.MessageID, - payload: p.Payload, - ack: ack, - } -} - -// ClientOptionsReader provides an interface for reading ClientOptions after the client has been initialized. -type ClientOptionsReader struct { - options *ClientOptions -} - -// ClientOptions contains configurable options for an MQTT Client. -type ClientOptions struct { - Adaptor net.Adapter - - //Servers []*url.URL - Servers string - ClientID string - Username string - Password string - //CredentialsProvider CredentialsProvider - CleanSession bool - Order bool - WillEnabled bool - WillTopic string - WillPayload []byte - WillQos byte - WillRetained bool - ProtocolVersion uint - protocolVersionExplicit bool - //TLSConfig *tls.Config - KeepAlive int64 - PingTimeout time.Duration - ConnectTimeout time.Duration - MaxReconnectInterval time.Duration - AutoReconnect bool - //Store Store - //DefaultPublishHandler MessageHandler - //OnConnect OnConnectHandler - //OnConnectionLost ConnectionLostHandler - WriteTimeout time.Duration - MessageChannelDepth uint - ResumeSubs bool - //HTTPHeaders http.Header -} - -// NewClientOptions returns a new ClientOptions struct. -func NewClientOptions() *ClientOptions { - return &ClientOptions{Adaptor: net.ActiveDevice, ProtocolVersion: 4, KeepAlive: 60, PingTimeout: time.Second * 10} -} - -// AddBroker adds a broker URI to the list of brokers to be used. The format should be -// scheme://host:port -// Where "scheme" is one of "tcp", "ssl", or "ws", "host" is the ip-address (or hostname) -// and "port" is the port on which the broker is accepting connections. -// -// Default values for hostname is "127.0.0.1", for schema is "tcp://". -// -// An example broker URI would look like: tcp://foobar.com:1883 -func (o *ClientOptions) AddBroker(server string) *ClientOptions { - if len(server) > 0 && server[0] == ':' { - server = "127.0.0.1" + server - } - if !strings.Contains(server, "://") { - server = "tcp://" + server - } - - o.Servers = server - return o -} - -// SetClientID will set the client id to be used by this client when -// connecting to the MQTT broker. According to the MQTT v3.1 specification, -// a client id mus be no longer than 23 characters. -func (o *ClientOptions) SetClientID(id string) *ClientOptions { - o.ClientID = id - return o -} - -// SetUsername will set the username to be used by this client when connecting -// to the MQTT broker. Note: without the use of SSL/TLS, this information will -// be sent in plaintext accross the wire. -func (o *ClientOptions) SetUsername(u string) *ClientOptions { - o.Username = u - return o -} - -// SetPassword will set the password to be used by this client when connecting -// to the MQTT broker. Note: without the use of SSL/TLS, this information will -// be sent in plaintext accross the wire. -func (o *ClientOptions) SetPassword(p string) *ClientOptions { - o.Password = p - return o -} - -// SetKeepAlive will set the amount of time (in seconds) that the client -// should wait before sending a PING request to the broker. This will -// allow the client to know that a connection has not been lost with the -// server. -func (o *ClientOptions) SetKeepAlive(k time.Duration) *ClientOptions { - o.KeepAlive = int64(k / time.Second) - return o -} - -// SetPingTimeout will set the amount of time (in seconds) that the client -// will wait after sending a PING request to the broker, before deciding -// that the connection has been lost. Default is 10 seconds. -func (o *ClientOptions) SetPingTimeout(k time.Duration) *ClientOptions { - o.PingTimeout = k - return o -} - -// SetWill accepts a string will message to be set. When the client connects, -// it will give this will message to the broker, which will then publish the -// provided payload (the will) to any clients that are subscribed to the provided -// topic. -func (o *ClientOptions) SetWill(topic string, payload string, qos byte, retained bool) *ClientOptions { - o.SetBinaryWill(topic, []byte(payload), qos, retained) - return o -} - -// SetBinaryWill accepts a []byte will message to be set. When the client connects, -// it will give this will message to the broker, which will then publish the -// provided payload (the will) to any clients that are subscribed to the provided -// topic. -func (o *ClientOptions) SetBinaryWill(topic string, payload []byte, qos byte, retained bool) *ClientOptions { - o.WillEnabled = true - o.WillTopic = topic - o.WillPayload = payload - o.WillQos = qos - o.WillRetained = retained - return o -} diff --git a/net/mqtt/router.go b/net/mqtt/router.go deleted file mode 100644 index 7d9b930f1..000000000 --- a/net/mqtt/router.go +++ /dev/null @@ -1,178 +0,0 @@ -// The following code is a slightly modified version of code taken from the Paho MQTT library. -// It is here until TinyGo can compile the "net" package from the standard library, at which time -// it can be removed. - -/* - * Copyright (c) 2013 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Seth Hoenig - * Allan Stockdill-Mander - * Mike Robertson - */ - -package mqtt - -import ( - "container/list" - "strings" - - "github.com/eclipse/paho.mqtt.golang/packets" -) - -// route is a type which associates MQTT Topic strings with a -// callback to be executed upon the arrival of a message associated -// with a subscription to that topic. -type route struct { - topic string - callback MessageHandler -} - -// match takes a slice of strings which represent the route being tested having been split on '/' -// separators, and a slice of strings representing the topic string in the published message, similarly -// split. -// The function determines if the topic string matches the route according to the MQTT topic rules -// and returns a boolean of the outcome -func match(route []string, topic []string) bool { - if len(route) == 0 { - if len(topic) == 0 { - return true - } - return false - } - - if len(topic) == 0 { - if route[0] == "#" { - return true - } - return false - } - - if route[0] == "#" { - return true - } - - if (route[0] == "+") || (route[0] == topic[0]) { - return match(route[1:], topic[1:]) - } - return false -} - -func routeIncludesTopic(route, topic string) bool { - return match(routeSplit(route), strings.Split(topic, "/")) -} - -// removes $share and sharename when splitting the route to allow -// shared subscription routes to correctly match the topic -func routeSplit(route string) []string { - var result []string - if strings.HasPrefix(route, "$share") { - result = strings.Split(route, "/")[2:] - } else { - result = strings.Split(route, "/") - } - return result -} - -// match takes the topic string of the published message and does a basic compare to the -// string of the current Route, if they match it returns true -func (r *route) match(topic string) bool { - return r.topic == topic || routeIncludesTopic(r.topic, topic) -} - -type router struct { - //sync.RWMutex - routes *list.List - defaultHandler MessageHandler - messages chan *packets.PublishPacket - stop chan bool -} - -// newRouter returns a new instance of a Router and channel which can be used to tell the Router -// to stop -func newRouter() (*router, chan bool) { - router := &router{routes: list.New(), messages: make(chan *packets.PublishPacket), stop: make(chan bool)} - stop := router.stop - return router, stop -} - -// addRoute takes a topic string and MessageHandler callback. It looks in the current list of -// routes to see if there is already a matching Route. If there is it replaces the current -// callback with the new one. If not it add a new entry to the list of Routes. -func (r *router) addRoute(topic string, callback MessageHandler) { - for e := r.routes.Front(); e != nil; e = e.Next() { - if e.Value.(*route).match(topic) { - r := e.Value.(*route) - r.callback = callback - return - } - } - r.routes.PushBack(&route{topic: topic, callback: callback}) -} - -// deleteRoute takes a route string, looks for a matching Route in the list of Routes. If -// found it removes the Route from the list. -func (r *router) deleteRoute(topic string) { - for e := r.routes.Front(); e != nil; e = e.Next() { - if e.Value.(*route).match(topic) { - r.routes.Remove(e) - return - } - } -} - -// setDefaultHandler assigns a default callback that will be called if no matching Route -// is found for an incoming Publish. -func (r *router) setDefaultHandler(handler MessageHandler) { - r.defaultHandler = handler -} - -// matchAndDispatch takes a channel of Message pointers as input and starts a go routine that -// takes messages off the channel, matches them against the internal route list and calls the -// associated callback (or the defaultHandler, if one exists and no other route matched). If -// anything is sent down the stop channel the function will end. -func (r *router) matchAndDispatch(messages <-chan *packets.PublishPacket, order bool, client *mqttclient) { - go func() { - for { - select { - case message := <-messages: - sent := false - m := messageFromPublish(message, client.ackFunc(message)) - handlers := []MessageHandler{} - for e := r.routes.Front(); e != nil; e = e.Next() { - if e.Value.(*route).match(message.TopicName) { - if order { - handlers = append(handlers, e.Value.(*route).callback) - } else { - hd := e.Value.(*route).callback - hd(client, m) - //TODO: m.Ack() - } - sent = true - } - } - if !sent && r.defaultHandler != nil { - if order { - handlers = append(handlers, r.defaultHandler) - } else { - r.defaultHandler(client, m) - //TODO: m.Ack() - } - } - for _, handler := range handlers { - func() { - handler(client, m) - //TODO: m.Ack() - }() - } - case <-r.stop: - return - } - } - }() -} diff --git a/net/mqtt/token.go b/net/mqtt/token.go deleted file mode 100644 index f4225ffce..000000000 --- a/net/mqtt/token.go +++ /dev/null @@ -1,19 +0,0 @@ -package mqtt - -import "time" - -type mqtttoken struct { - err error -} - -func (t *mqtttoken) Wait() bool { - return true -} - -func (t *mqtttoken) WaitTimeout(time.Duration) bool { - return true -} - -func (t *mqtttoken) Error() error { - return t.err -} diff --git a/net/net.go b/net/net.go deleted file mode 100644 index f28da84a7..000000000 --- a/net/net.go +++ /dev/null @@ -1,423 +0,0 @@ -// package net is intended to provide compatible interfaces with the -// Go standard library's net package. -package net - -import ( - "errors" - "strconv" - "strings" - "time" -) - -// DialUDP makes a UDP network connection. raadr is the port that the messages will -// be sent to, and laddr is the port that will be listened to in order to -// receive incoming messages. -func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPSerialConn, error) { - addr := raddr.IP.String() - sendport := strconv.Itoa(raddr.Port) - listenport := strconv.Itoa(laddr.Port) - - // disconnect any old socket - ActiveDevice.DisconnectSocket() - - // connect new socket - err := ActiveDevice.ConnectUDPSocket(addr, sendport, listenport) - if err != nil { - return nil, err - } - - return &UDPSerialConn{SerialConn: SerialConn{Adaptor: ActiveDevice}, laddr: laddr, raddr: raddr}, nil -} - -// ListenUDP listens for UDP connections on the port listed in laddr. -func ListenUDP(network string, laddr *UDPAddr) (*UDPSerialConn, error) { - addr := "0" - sendport := "0" - listenport := strconv.Itoa(laddr.Port) - - // disconnect any old socket - ActiveDevice.DisconnectSocket() - - // connect new socket - err := ActiveDevice.ConnectUDPSocket(addr, sendport, listenport) - if err != nil { - return nil, err - } - - return &UDPSerialConn{SerialConn: SerialConn{Adaptor: ActiveDevice}, laddr: laddr}, nil -} - -// DialTCP makes a TCP network connection. raadr is the port that the messages will -// be sent to, and laddr is the port that will be listened to in order to -// receive incoming messages. -func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPSerialConn, error) { - addr := raddr.IP.String() - sendport := strconv.Itoa(raddr.Port) - - // disconnect any old socket? - //ActiveDevice.DisconnectSocket() - - // connect new socket - err := ActiveDevice.ConnectTCPSocket(addr, sendport) - if err != nil { - return nil, err - } - - return &TCPSerialConn{SerialConn: SerialConn{Adaptor: ActiveDevice}, laddr: laddr, raddr: raddr}, nil -} - -// Dial connects to the address on the named network. -// It tries to provide a mostly compatible interface -// to net.Dial(). -func Dial(network, address string) (Conn, error) { - switch network { - case "tcp": - raddr, err := ResolveTCPAddr(network, address) - if err != nil { - return nil, err - } - - c, e := DialTCP(network, &TCPAddr{}, raddr) - return c.opConn(), e - case "udp": - raddr, err := ResolveUDPAddr(network, address) - if err != nil { - return nil, err - } - - c, e := DialUDP(network, &UDPAddr{}, raddr) - return c.opConn(), e - default: - return nil, errors.New("invalid network for dial") - } -} - -// SerialConn is a loosely net.Conn compatible implementation -type SerialConn struct { - Adaptor Adapter -} - -// UDPSerialConn is a loosely net.Conn compatible intended to support -// UDP over serial. -type UDPSerialConn struct { - SerialConn - laddr *UDPAddr - raddr *UDPAddr -} - -// NewUDPSerialConn returns a new UDPSerialConn/ -func NewUDPSerialConn(c SerialConn, laddr, raddr *UDPAddr) *UDPSerialConn { - return &UDPSerialConn{SerialConn: c, raddr: raddr} -} - -// TCPSerialConn is a loosely net.Conn compatible intended to support -// TCP over serial. -type TCPSerialConn struct { - SerialConn - laddr *TCPAddr - raddr *TCPAddr -} - -// NewTCPSerialConn returns a new TCPSerialConn/ -func NewTCPSerialConn(c SerialConn, laddr, raddr *TCPAddr) *TCPSerialConn { - return &TCPSerialConn{SerialConn: c, raddr: raddr} -} - -// Read reads data from the connection. -// TODO: implement the full method functionality: -// Read can be made to time out and return an Error with Timeout() == true -// after a fixed time limit; see SetDeadline and SetReadDeadline. -func (c *SerialConn) Read(b []byte) (n int, err error) { - // read only the data that has been received via "+IPD" socket - return c.Adaptor.ReadSocket(b) -} - -// Write writes data to the connection. -// TODO: implement the full method functionality for timeouts. -// Write can be made to time out and return an Error with Timeout() == true -// after a fixed time limit; see SetDeadline and SetWriteDeadline. -func (c *SerialConn) Write(b []byte) (n int, err error) { - // specify that is a data transfer to the - // currently open socket, not commands to the ESP8266/ESP32. - err = c.Adaptor.StartSocketSend(len(b)) - if err != nil { - return - } - n, err = c.Adaptor.Write(b) - if err != nil { - return n, err - } - /* TODO(bcg): this is kind of specific to espat, should maybe refactor */ - _, err = c.Adaptor.Response(1000) - if err != nil { - return n, err - } - return n, err -} - -// Close closes the connection. -// Currently only supports a single Read or Write operations without blocking. -func (c *SerialConn) Close() error { - c.Adaptor.DisconnectSocket() - return nil -} - -// LocalAddr returns the local network address. -func (c *UDPSerialConn) LocalAddr() Addr { - return c.laddr.opAddr() -} - -// RemoteAddr returns the remote network address. -func (c *UDPSerialConn) RemoteAddr() Addr { - return c.raddr.opAddr() -} - -func (c *UDPSerialConn) opConn() Conn { - if c == nil { - return nil - } - return c -} - -// LocalAddr returns the local network address. -func (c *TCPSerialConn) LocalAddr() Addr { - return c.laddr.opAddr() -} - -// RemoteAddr returns the remote network address. -func (c *TCPSerialConn) RemoteAddr() Addr { - return c.raddr.opAddr() -} - -func (c *TCPSerialConn) opConn() Conn { - if c == nil { - return nil - } - return c -} - -// SetDeadline sets the read and write deadlines associated -// with the connection. It is equivalent to calling both -// SetReadDeadline and SetWriteDeadline. -// -// A deadline is an absolute time after which I/O operations -// fail with a timeout (see type Error) instead of -// blocking. The deadline applies to all future and pending -// I/O, not just the immediately following call to Read or -// Write. After a deadline has been exceeded, the connection -// can be refreshed by setting a deadline in the future. -// -// An idle timeout can be implemented by repeatedly extending -// the deadline after successful Read or Write calls. -// -// A zero value for t means I/O operations will not time out. -func (c *SerialConn) SetDeadline(t time.Time) error { - return nil -} - -// SetReadDeadline sets the deadline for future Read calls -// and any currently-blocked Read call. -// A zero value for t means Read will not time out. -func (c *SerialConn) SetReadDeadline(t time.Time) error { - return nil -} - -// SetWriteDeadline sets the deadline for future Write calls -// and any currently-blocked Write call. -// Even if write times out, it may return n > 0, indicating that -// some of the data was successfully written. -// A zero value for t means Write will not time out. -func (c *SerialConn) SetWriteDeadline(t time.Time) error { - return nil -} - -// ResolveTCPAddr returns an address of TCP end point. -// -// The network must be a TCP network name. -func ResolveTCPAddr(network, address string) (*TCPAddr, error) { - // TODO: make sure network is 'tcp' - // separate domain from port, if any - r := strings.Split(address, ":") - addr, err := ActiveDevice.GetDNS(r[0]) - if err != nil { - return nil, err - } - ip := IP(addr) - if len(r) > 1 { - port, e := strconv.Atoi(r[1]) - if e != nil { - return nil, e - } - return &TCPAddr{IP: ip, Port: port}, nil - } - return &TCPAddr{IP: ip}, nil -} - -// ResolveUDPAddr returns an address of UDP end point. -// -// The network must be a UDP network name. -func ResolveUDPAddr(network, address string) (*UDPAddr, error) { - // TODO: make sure network is 'udp' - // separate domain from port, if any - r := strings.Split(address, ":") - addr, err := ActiveDevice.GetDNS(r[0]) - if err != nil { - return nil, err - } - ip := IP(addr) - if len(r) > 1 { - port, e := strconv.Atoi(r[1]) - if e != nil { - return nil, e - } - return &UDPAddr{IP: ip, Port: port}, nil - } - - return &UDPAddr{IP: ip}, nil -} - -// The following definitions are here to support a Golang standard package -// net-compatible interface for IP until TinyGo can compile the net package. - -// IP is an IP address. Unlike the standard implementation, it is only -// a buffer of bytes that contains the string form of the IP address, not the -// full byte format used by the Go standard . -type IP []byte - -// UDPAddr here to serve as compatible type. until TinyGo can compile the net package. -type UDPAddr struct { - IP IP - Port int - Zone string // IPv6 scoped addressing zone; added in Go 1.1 -} - -// Network returns the address's network name, "udp". -func (a *UDPAddr) Network() string { return "udp" } - -func (a *UDPAddr) String() string { - if a == nil { - return "" - } - if a.Port != 0 { - return a.IP.String() + ":" + strconv.Itoa(a.Port) - } - return a.IP.String() -} - -func (a *UDPAddr) opAddr() Addr { - if a == nil { - return nil - } - return a -} - -// TCPAddr here to serve as compatible type. until TinyGo can compile the net package. -type TCPAddr struct { - IP IP - Port int - Zone string // IPv6 scoped addressing zone -} - -// Network returns the address's network name, "tcp". -func (a *TCPAddr) Network() string { return "tcp" } - -func (a *TCPAddr) String() string { - if a == nil { - return "" - } - if a.Port != 0 { - return a.IP.String() + ":" + strconv.Itoa(a.Port) - } - return a.IP.String() -} - -func (a *TCPAddr) opAddr() Addr { - if a == nil { - return nil - } - return a -} - -// ParseIP parses s as an IP address, returning the result. -func ParseIP(s string) IP { - b := strings.Split(s, ".") - if len(b) == 4 { - ip := make([]byte, 4) - - for i := range ip { - x, _ := strconv.ParseUint(b[i], 10, 8) - ip[i] = byte(x) - } - - return IP(ip) - } - - return IP([]byte(s)) -} - -// String returns the string form of the IP address ip. -func (ip IP) String() string { - if len(ip) < 4 { - return "" - } - return strconv.Itoa(int(ip[0])) + "." + strconv.Itoa(int(ip[1])) + "." + strconv.Itoa(int(ip[2])) + "." + strconv.Itoa(int(ip[3])) -} - -// Conn is a generic stream-oriented network connection. -// This interface is from the Go standard library. -type Conn interface { - // Read reads data from the connection. - // Read can be made to time out and return an Error with Timeout() == true - // after a fixed time limit; see SetDeadline and SetReadDeadline. - Read(b []byte) (n int, err error) - - // Write writes data to the connection. - // Write can be made to time out and return an Error with Timeout() == true - // after a fixed time limit; see SetDeadline and SetWriteDeadline. - Write(b []byte) (n int, err error) - - // Close closes the connection. - // Any blocked Read or Write operations will be unblocked and return errors. - Close() error - - // LocalAddr returns the local network address. - LocalAddr() Addr - - // RemoteAddr returns the remote network address. - RemoteAddr() Addr - - // SetDeadline sets the read and write deadlines associated - // with the connection. It is equivalent to calling both - // SetReadDeadline and SetWriteDeadline. - // - // A deadline is an absolute time after which I/O operations - // fail with a timeout (see type Error) instead of - // blocking. The deadline applies to all future and pending - // I/O, not just the immediately following call to Read or - // Write. After a deadline has been exceeded, the connection - // can be refreshed by setting a deadline in the future. - // - // An idle timeout can be implemented by repeatedly extending - // the deadline after successful Read or Write calls. - // - // A zero value for t means I/O operations will not time out. - SetDeadline(t time.Time) error - - // SetReadDeadline sets the deadline for future Read calls - // and any currently-blocked Read call. - // A zero value for t means Read will not time out. - SetReadDeadline(t time.Time) error - - // SetWriteDeadline sets the deadline for future Write calls - // and any currently-blocked Write call. - // Even if write times out, it may return n > 0, indicating that - // some of the data was successfully written. - // A zero value for t means Write will not time out. - SetWriteDeadline(t time.Time) error -} - -// Addr represents a network end point address. -type Addr interface { - Network() string // name of the network (for example, "tcp", "udp") - String() string // string form of address (for example, "192.0.2.1:25", "[2001:db8::1]:80") -} diff --git a/net/net_test.go b/net/net_test.go deleted file mode 100644 index e9ac6623e..000000000 --- a/net/net_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package net - -import ( - "testing" - - qt "github.com/frankban/quicktest" -) - -func TestIPAddressString(t *testing.T) { - c := qt.New(t) - ipaddr := ParseIP("127.0.0.1") - - c.Assert(ipaddr.String(), qt.Equals, "127.0.0.1") -} diff --git a/net/tls/tls.go b/net/tls/tls.go deleted file mode 100644 index 12ba037ea..000000000 --- a/net/tls/tls.go +++ /dev/null @@ -1,42 +0,0 @@ -// Package tls is intended to provide a minimal set of compatible interfaces with the -// Go standard library's tls package. -package tls - -import ( - "strconv" - "strings" - - "tinygo.org/x/drivers/net" -) - -// Dial makes a TLS network connection. It tries to provide a mostly compatible interface -// to tls.Dial(). -// Dial connects to the given network address. -func Dial(network, address string, config *Config) (*net.TCPSerialConn, error) { - raddr, err := net.ResolveTCPAddr(network, address) - if err != nil { - return nil, err - } - - hostname := strings.Split(address, ":")[0] - sendport := strconv.Itoa(raddr.Port) - if sendport == "0" { - sendport = "443" - } - - // disconnect any old socket - net.ActiveDevice.DisconnectSocket() - - // connect new socket - err = net.ActiveDevice.ConnectSSLSocket(hostname, sendport) - if err != nil { - return nil, err - } - - return net.NewTCPSerialConn(net.SerialConn{Adaptor: net.ActiveDevice}, nil, raddr), nil -} - -// Config is a placeholder for future compatibility with -// tls.Config. -type Config struct { -} diff --git a/netdev/README.md b/netdev/README.md new file mode 100644 index 000000000..980a21d1f --- /dev/null +++ b/netdev/README.md @@ -0,0 +1,184 @@ +# Netdev + +#### Table of Contents + +- [Overview](#overview) +- [Porting Applications from Go "net"](#porting-applications-from-go-net) +- [Writing a New Driver](#writing-a-new-driver) + +## Overview + +Netdev is TinyGo's network device driver model. + +Let's see where netdev fits in the network stack. The diagram below shows the traditional full OS stack vs. different possible embedded stacks for TinyGo. + +![Netdev models](netdev_models.jpg) + +In the traditional full OS stack, the driver that sits above hardware (the "nic") and below TCP/IP is the network driver, the netdev. The netdev provides a raw packet interface to the OS. + +For TinyGo netdev, the netdev includes TCP/IP and provides a socket(2) interface to TinyGo's "net" package. Applications are written to use the net.Conn interfaces. "net" translates net.Conn functions (Dial, Listen, Read, Write) into netdev socket(2) calls. The netdev translates those socket(2) calls into hardware access, ultimately. Let's consider the three use cases: + +#### Firware Offload Model + +Here we are fortunate that hardware includes firmware with a TCP/IP implmentation, and the firmware manages the TCP/IP connection state. The netdev driver translates socket(2) calls to the firmware's TCP/IP calls. Usually, minimal work is required since the firmware is likely to use lwip, which has an socket(2) API. + +The Wifinina (ESP32) and RTL8720dn netdev drivers are examples of the firmware offload model. + +#### Full Stack Model + +Here the netdev includes the TCP/IP stack, maybe some port of lwip/uip to Go? + +#### "Bring-Your-Own-net.Comm" Model + +Here the netdev is the entire stack, accessing hardware on the bottom and serving up net.Conn connections above to applications. + +## Porting Applications from Go "net" + +Ideally, TinyGo's "net" package would just be Go's "net" package and applications using "net" would just work, as-is. Unfortunately, Go's "net" can't fully be ported to TinyGo, so TinyGo's "net" is a subset of Go's. Hopefully, for the embedded space, the subset is sufficient for most needs. + +To view TinyGo's "net" package exports, use ```go doc ./net```, ```go doc ./net/http```, etc. For the most part, Go's "net" documentation applies to TinyGo's "net". There are a few features excluded during the porting process, in particular: + +- No IPv6 support +- No HTTP/2 support +- HTTP client request can't be reused +- No multipart form support +- No TLS support for HTTP servers +- No DualStack support + +Applications using Go's "net" package will need a few (minor) modifications to work with TinyGo's net package. + +### Step 1: Load Netdev + +#### Option 1: + +Import netdev package to load the netdev driver. Import only for side effects using leading underscore. + +```go +import _ "tinygo.org/x/drivers/netdev" +``` + +This will select the netdev driver for the target machine using build tags. For example, when flashing to target Arduino Nano RP2040 Connect, the build tag nano_rp2040 will select the "Wifinina" netdev driver. + +#### Option 2: + +Manually load the netdev driver. Import the driver directly, and then call net.UseNetdev to load the driver. e.g.: + +``` +import "tinygo.org/x/drivers/netdev/wifinina" + +func main() { + net.UseNetdev(wifinina.New("SSID", "PASSPHRASE")) + ... +} +``` + +### Step 2: Connect to the Network + +Call net.Connect() to connect the device to an IP network, via Wifi, cellular, Ethernet, etc. Make this call first, before any net.* or http.* or tls.* calls. + +Here is a simple http server listening on port :8080, before and after porting from Go "net/http": + +#### Before +```go +package main + +import ( + "fmt" + "net/http" +) + +func main() { + http.HandleFunc("/", HelloServer) + http.ListenAndServe(":8080", nil) +} + +func HelloServer(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) +} +``` + +#### After +```go +package main + +import ( + "fmt" + "net" + "net/http" + + _ "tinygo.org/x/drivers/netdev" +) + +func main() { + net.Connect(nil) + http.HandleFunc("/", HelloServer) + http.ListenAndServe(":8080", nil) +} + +func HelloServer(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) +} +``` + +## Writing a New Driver + +:bulb: A reference netdev driver is the Wifinina driver (netdev/wifinina). + +Netdev drivers implement the net.Netdever interface, which includes the net.Socketer interface. The Socketer interface is modeled after BSD socket(2). TinyGo's "net" package translates net.Conn calls into netdev Socketer calls. For example, DialTCP calls netdev.Socket() and netdev.Connect(): + +```go +func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error) { + + fd, _ := netdev.Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) + + addr := NewSockAddr("", uint16(raddr.Port), raddr.IP) + + netdev.Connect(fd, addr) + + return &TCPConn{ + fd: fd, + laddr: laddr, + raddr: raddr, + }, nil +} +``` + +### net.Socketer Interface + +```go +type Socketer interface { + Socket(family AddressFamily, sockType SockType, protocol Protocol) (Sockfd, error) + Bind(sockfd Sockfd, myaddr SockAddr) error + Connect(sockfd Sockfd, servaddr SockAddr) error + Listen(sockfd Sockfd, backlog int) error + Accept(sockfd Sockfd, peer SockAddr) (Sockfd, error) + Send(sockfd Sockfd, buf []byte, flags SockFlags, timeout time.Duration) (int, error) + Recv(sockfd Sockfd, buf []byte, flags SockFlags, timeout time.Duration) (int, error) + Close(sockfd Sockfd) error + SetSockOpt(sockfd Sockfd, level SockOptLevel, opt SockOpt, value any) error +} +``` + +Socketer interface is intended to mimic a subset of BSD socket(2). They've been Go-ified, but should otherwise maintain the semantics of the original socket(2) calls. Send and Recv add a timeout to put a limit on blocking operations. Recv in paricular is blocking and will block until data arrives on the socket or EOF. The timeout is calculated from net.Conn's SetDeadline(), typically. + +#### Locking + +Multiple goroutines may invoke methods on a net.Conn simultaneously, and the "net" package translates net.Conn calls into Socketer calls. It follows that multiple goroutines may invoke Socketer calls, so locking is required to keep Socketer calls from stepping on one another. + +Don't hold a lock while Time.Sleep()'ing waiting for a hardware operation to finish. Unlocking while sleeping let's other goroutines to run. If the sleep period is really small, then you can get away with holding the lock sometimes. + +#### Sockfd + +The Socketer interface uses a socket fd to represent a socket connection end-point. Each net.Conn maps 1:1 to a fd. The number of fds available is a netdev hardware limitation. Wifinina, for example, can hand out 10 socket fds. + +### Packaging + +1. Create a new directory in netdev/foo to hold the driver files. + +2. Add a initialization file netdev/netdev_foo.go to compile and load the driver based on target build tags. + +### Testing + +The netdev driver should minimally pass all of the example/net examples. + +TODO: automate testing to catch regressions. diff --git a/netdev/netdev.go b/netdev/netdev.go new file mode 100644 index 000000000..9e12a785d --- /dev/null +++ b/netdev/netdev.go @@ -0,0 +1,133 @@ +// Netdev is TinyGo's network device driver model. + +package netdev + +import ( + "errors" + "fmt" + "strconv" + "strings" + _ "unsafe" +) + +//go:linkname Use net.useNetdev +func Use(netdev Netdever) + +type HardwareAddr []byte + +const hexDigit = "0123456789abcdef" + +func (a HardwareAddr) String() string { + if len(a) == 0 { + return "" + } + buf := make([]byte, 0, len(a)*3-1) + for i, b := range a { + if i > 0 { + buf = append(buf, ':') + } + buf = append(buf, hexDigit[b>>4]) + buf = append(buf, hexDigit[b&0xF]) + } + return string(buf) +} + +func ParseHardwareAddr(s string) HardwareAddr { + parts := strings.Split(s, ":") + if len(parts) != 6 { + return nil + } + + var mac []byte + for _, part := range parts { + b, err := strconv.ParseInt(part, 16, 64) + if err != nil { + return nil + } + mac = append(mac, byte(b)) + } + + return HardwareAddr(mac) +} + +type Port uint16 // host byte-order + +type IP [4]byte + +func (ip IP) String() string { + return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]) +} + +func ParseIP(s string) IP { + var result IP + octets := strings.Split(s, ".") + if len(octets) != 4 { + return IP{} + } + for i, octet := range octets { + v, err := strconv.Atoi(octet) + if err != nil { + return IP{} + } + result[i] = byte(v) + } + return result +} + +// NetConnect() errors +var ErrConnected = errors.New("Already connected") +var ErrConnectFailed = errors.New("Connect failed") +var ErrConnectTimeout = errors.New("Connect timed out") +var ErrMissingSSID = errors.New("Missing WiFi SSID") +var ErrStartingDHCPClient = errors.New("Error starting DHPC client") + +// GethostByName() errors +var ErrHostUnknown = errors.New("Host unknown") + +// Socketer errors +var ErrFamilyNotSupported = errors.New("Address family not supported") +var ErrProtocolNotSupported = errors.New("Socket protocol/type not supported") +var ErrNoMoreSockets = errors.New("No more sockets") +var ErrClosingSocket = errors.New("Error closing socket") +var ErrNotSupported = errors.New("Not supported") +var ErrRecvTimeout = errors.New("Recv timeout expired") + +type Event int + +// NetNotify network events +const ( + EventNetUp Event = iota + EventNetDown +) + +// Netdev drivers implement the Netdever interface. +// +// A Netdever is passed to the "net" package using netdev.Use(). +// +// Just like a net.Conn, multiple goroutines may invoke methods on a Netdever +// simultaneously. +type Netdever interface { + + // NetConnect device to IP network + NetConnect() error + + // NetDisconnect from IP network + NetDisconnect() + + // NetNotify to register callback for network events + NetNotify(func(Event)) + + // GetHostByName returns the IP address of either a hostname or IPv4 + // address in standard dot notation + GetHostByName(name string) (IP, error) + + // GetHardwareAddr returns device MAC address + GetHardwareAddr() (HardwareAddr, error) + + // GetIPAddr returns IP address assigned to device, either by DHCP or + // statically + GetIPAddr() (IP, error) + + // Socketer is a Berkely Sockets-like interface + Socketer +} diff --git a/netdev/netdev_models.jpg b/netdev/netdev_models.jpg new file mode 100644 index 0000000000000000000000000000000000000000..37e29d7040913eaa633e0568f991f935f2addbae GIT binary patch literal 65672 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 x;4yIE-?7@?bk 0 { - break - } - - _, err = rtl.Rpc_lwip_errno() - if err != nil { - return nil - } - - time.Sleep(100 * time.Millisecond) - } - - buf2 := make([]byte, 4096) - result, err := rtl.Rpc_lwip_recv(socket, &buf2, uint32(len(buf2)), 8, 0) - if err != nil { - return nil - } - if result != -1 && result != 0 { - return fmt.Errorf("Rpc_lwip_recv error") - } - - result, err = rtl.Rpc_lwip_errno() - if err != nil { - return nil - } - if result != 11 { - return fmt.Errorf("Rpc_lwip_errno error") - } - - b := bufio.NewReader(bytes.NewReader(buf)) - req, err := http.ReadRequest(b) - if err != nil { - return err - } - if rtl.debug { - fmt.Printf("%s %s %s\r\n", req.Method, req.RequestURI, req.Proto) - } - - pos := bytes.Index(buf, []byte("\r\n\r\n")) - if pos > 0 { - body := bytes.NewReader(buf[pos+4:]) - req.Body = io.NopCloser(body) - } - - handler, _ := http.DefaultServeMux.Handler(req) - rwx := responseWriter{ - header: http.Header{}, - statusCode: 200, - } - rwx.header.Add(`Content-Type`, `text/html; charset=UTF-8`) - rwx.header.Add(`Connection`, `close`) - handler.ServeHTTP(&rwx, req) - rwx.header.Add(`Content-Length`, fmt.Sprintf("%d", len(rwx.Buf))) - - optval = []byte{0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA5, 0xA5, 0xA5, 0xA5} - _, err = rtl.Rpc_lwip_setsockopt(socket, 0x00000FFF, 0x1006, optval, uint32(len(optval))) - if err != nil { - return nil - } - - optval = []byte{0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA5, 0xA5, 0xA5, 0xA5} - _, err = rtl.Rpc_lwip_setsockopt(socket, 0x00000FFF, 0x1005, optval, uint32(len(optval))) - if err != nil { - return nil - } - - maxfdp1 := int32(2) - writeset := []byte{0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - timeout := []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x42, 0x0F, 0x00, 0x0A, 0x00, 0x00, 0x00} - _, err = rtl.Rpc_lwip_select(maxfdp1, []byte{}, writeset, []byte{}, timeout) - if err != nil { - return nil - } - - msg := rwx.Buf - hb := bytes.Buffer{} - err = rwx.header.Write(&hb) - if err != nil { - return err - } - - data := []byte(fmt.Sprintf("HTTP/1.1 %d OK\n", rwx.statusCode)) - data = append(data, hb.Bytes()...) - data = append(data, byte('\n')) - - _, err = rtl.Rpc_lwip_send(socket, data, 8) - if err != nil { - return nil - } - - if len(msg) > 0 { - timeout = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x42, 0x0F, 0x00, 0x54, 0x00, 0x00, 0x00} - _, err = rtl.Rpc_lwip_select(maxfdp1, []byte{}, writeset, []byte{}, timeout) - if err != nil { - return nil - } - - _, err = rtl.Rpc_lwip_send(socket, msg, 8) - if err != nil { - return nil - } - } - - for i := 0; i < 4; i++ { - buf := make([]byte, 4096) - _, err = rtl.Rpc_lwip_recv(socket, &buf, uint32(len(buf)), 8, 0) - if err != nil { - return nil - } - - _, err = rtl.Rpc_lwip_errno() - if err != nil { - return nil - } - } - - _, err = rtl.Rpc_lwip_close(socket) - if err != nil { - return nil - } - return nil -} - -func (rtl *RTL8720DN) ListenAndServe(addr string, handler http.Handler) error { - err := rtl.setupHTTPServer() - if err != nil { - return err - } - - for { - connected, err := rtl.accept() - if err != nil { - return err - } - - if connected { - err := rtl.handleHTTP() - if err != nil { - return err - } - } - - time.Sleep(100 * time.Millisecond) - } -} - -type responseWriter struct { - Buf []byte - header http.Header - statusCode int -} - -func (r *responseWriter) Header() http.Header { - return r.header -} - -func (r *responseWriter) Write(b []byte) (int, error) { - r.Buf = append(r.Buf, b...) - return len(b), nil -} - -func (r *responseWriter) WriteHeader(statusCode int) { - r.statusCode = statusCode -} diff --git a/rtl8720dn/netdriver.go b/rtl8720dn/netdriver.go deleted file mode 100644 index 73e6cc43b..000000000 --- a/rtl8720dn/netdriver.go +++ /dev/null @@ -1,377 +0,0 @@ -package rtl8720dn - -import ( - "fmt" - "strconv" -) - -// Here is the implementation of tinygo-org/x/drivers/net.DeviceDriver. - -func (d *Driver) GetDNS(domain string) (string, error) { - if d.debug { - fmt.Printf("GetDNS(%q)\r\n", domain) - } - - ipaddr := make([]byte, 4) - _, err := d.Rpc_netconn_gethostbyname(domain, &ipaddr) - if err != nil { - return "", err - } - - ret, err := fmt.Sprintf("%d.%d.%d.%d", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]), nil - if d.debug { - fmt.Printf("-> %s\r\n", ret) - fmt.Printf("-> %02X.%02X.%02X.%02X\r\n", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]) - } - return ret, err -} - -func (d *Driver) ConnectTCPSocket(addr, port string) error { - if d.debug { - fmt.Printf("ConnectTCPSocket(%q, %q)\r\n", addr, port) - } - - ipaddr := make([]byte, 4) - if len(addr) == 4 { - copy(ipaddr, addr) - } else { - _, err := d.Rpc_netconn_gethostbyname(addr, &ipaddr) - if err != nil { - return err - } - } - - portNum, err := strconv.ParseUint(port, 0, 0) - if err != nil { - return err - } - - socket, err := d.Rpc_lwip_socket(0x02, 0x01, 0x00) - if err != nil { - return err - } - d.socket = socket - d.connectionType = ConnectionTypeTCP - - _, err = d.Rpc_lwip_fcntl(socket, 0x00000003, 0x00000000) - if err != nil { - return err - } - - _, err = d.Rpc_lwip_fcntl(socket, 0x00000004, 0x00000001) - if err != nil { - return err - } - - name := []byte{0x00, 0x02, 0x00, 0x50, 0xC0, 0xA8, 0x01, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - name[2] = byte(portNum >> 8) - name[3] = byte(portNum) - name[4] = byte(ipaddr[0]) - name[5] = byte(ipaddr[1]) - name[6] = byte(ipaddr[2]) - name[7] = byte(ipaddr[3]) - - _, err = d.Rpc_lwip_connect(socket, name, uint32(len(name))) - if err != nil { - return err - } - - readset := []byte{} - writeset := []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - exceptset := []byte{} - timeout := []byte{} - _, err = d.Rpc_lwip_select(0x01, readset, writeset, exceptset, timeout) - if err != nil { - return err - } - - optval := make([]byte, 4) - optlen := uint32(len(optval)) - _, err = d.Rpc_lwip_getsockopt(socket, 0x00000FFF, 0x00001007, []byte{0xA5, 0xA5, 0xA5, 0xA5}, &optval, &optlen) - if err != nil { - return err - } - - _, err = d.Rpc_lwip_fcntl(socket, 0x00000003, 0x00000000) - if err != nil { - return err - } - - _, err = d.Rpc_lwip_fcntl(socket, 0x00000004, 0x00000000) - if err != nil { - return err - } - - readset = []byte{} - writeset = []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - exceptset = []byte{} - timeout = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x42, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF} - _, err = d.Rpc_lwip_select(0x01, readset, writeset, exceptset, timeout) - if err != nil { - return err - } - - return nil -} - -func (d *Driver) ConnectSSLSocket(addr, port string) error { - if d.debug { - fmt.Printf("ConnectSSLSocket(%q, %q)\r\n", addr, port) - } - if d.root_ca == nil { - return fmt.Errorf("root_ca is not set") - } - - client, err := d.Rpc_wifi_ssl_client_create() - if err != nil { - return err - } - d.client = client - d.connectionType = ConnectionTypeTLS - - err = d.Rpc_wifi_ssl_init(client) - if err != nil { - return err - } - - err = d.Rpc_wifi_ssl_set_timeout(client, 0x0001D4C0) - if err != nil { - return err - } - - _, err = d.Rpc_wifi_ssl_set_rootCA(client, *d.root_ca) - if err != nil { - return err - } - - // TODO: use port - _, err = d.Rpc_wifi_start_ssl_client(client, addr, 443, 0x0001D4C0) - if err != nil { - return err - } - - _, err = d.Rpc_wifi_ssl_get_socket(client) - if err != nil { - return err - } - return nil -} - -func (d *Driver) ConnectUDPSocket(addr, sendport, listenport string) error { - if d.debug { - fmt.Printf("ConnectUDPSocket(\"%d.%d.%d.%d\", %q, %q)\r\n", byte(addr[0]), byte(addr[1]), byte(addr[2]), byte(addr[3]), sendport, listenport) - } - - socket, err := d.Rpc_lwip_socket(0x02, 0x02, 0x00) - if err != nil { - return err - } - d.socket = socket - d.connectionType = ConnectionTypeUDP - - optval := []byte{0x01, 0x00, 0x00, 0x00} - _, err = d.Rpc_lwip_setsockopt(socket, 0x00000FFF, 0x00000004, optval, uint32(len(optval))) - if err != nil { - return err - } - - port, err := strconv.ParseUint(sendport, 10, 0) - if err != nil { - return err - } - - ip := []byte(addr) - - // remote info - d.udpInfo[0] = byte(port >> 8) - d.udpInfo[1] = byte(port) - d.udpInfo[2] = ip[0] - d.udpInfo[3] = ip[1] - d.udpInfo[4] = ip[2] - d.udpInfo[5] = ip[3] - - port, err = strconv.ParseUint(listenport, 10, 0) - if err != nil { - return err - } - - ip_info := make([]byte, 12) - _, err = d.Rpc_tcpip_adapter_get_ip_info(0, &ip_info) - if err != nil { - return err - } - - name := []byte{0x00, 0x02, 0x0D, 0x05, 0xC0, 0xA8, 0x01, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - name[2] = byte(port >> 8) - name[3] = byte(port) - name[4] = ip_info[0] - name[5] = ip_info[1] - name[6] = ip_info[2] - name[7] = ip_info[3] - - _, err = d.Rpc_lwip_bind(socket, name, uint32(len(name))) - if err != nil { - return err - } - - _, err = d.Rpc_lwip_fcntl(socket, 0x00000004, 0x00000000) - if err != nil { - return err - } - - return nil -} - -func (d *Driver) DisconnectSocket() error { - if d.debug { - fmt.Printf("DisconnectSocket()\r\n") - } - switch d.connectionType { - case ConnectionTypeTCP, ConnectionTypeUDP: - _, err := d.Rpc_lwip_close(d.socket) - if err != nil { - return err - } - case ConnectionTypeTLS: - err := d.Rpc_wifi_stop_ssl_socket(d.client) - if err != nil { - return err - } - - err = d.Rpc_wifi_ssl_client_destroy(d.client) - if err != nil { - return err - } - default: - } - d.connectionType = ConnectionTypeNone - return nil -} - -func (d *Driver) StartSocketSend(size int) error { - if d.debug { - fmt.Printf("StartSocketSend(%d)\r\n", size) - } - // No implementation required - return nil -} - -func (d *Driver) Write(b []byte) (n int, err error) { - if d.debug { - fmt.Printf("Write(%#v)\r\n", b) - } - - switch d.connectionType { - case ConnectionTypeTCP: - sn, err := d.Rpc_lwip_send(d.socket, b, 0x00000008) - if err != nil { - return 0, err - } - n = int(sn) - case ConnectionTypeUDP: - to := []byte{0x00, 0x02, 0x0D, 0x05, 0xC0, 0xA8, 0x01, 0x76, 0xEB, 0x43, 0x00, 0x00, 0xD5, 0x27, 0x01, 0x00} - copy(to[2:], d.udpInfo[:]) - sn, err := d.Rpc_lwip_sendto(d.socket, b, 0x00000000, to, uint32(len(to))) - if err != nil { - return 0, err - } - n = int(sn) - case ConnectionTypeTLS: - sn, err := d.Rpc_wifi_send_ssl_data(d.client, b, uint16(len(b))) - if err != nil { - return 0, err - } - n = int(sn) - default: - return 0, nil - } - return n, nil -} - -func (d *Driver) ReadSocket(b []byte) (n int, err error) { - if d.debug { - //fmt.Printf("ReadSocket(b)\r\n") - } - if d.connectionType == ConnectionTypeNone { - return 0, nil - } - - switch d.connectionType { - case ConnectionTypeTCP: - length := len(b) - if length > maxUartRecvSize-16 { - length = maxUartRecvSize - 16 - } - buf := b[:length] - nn, err := d.Rpc_lwip_recv(d.socket, &buf, uint32(length), 0x00000008, 0x00002800) - if err != nil { - return 0, err - } - - if nn == -1 { - return 0, nil - } else if nn == 0 { - return 0, d.DisconnectSocket() - } - n = int(nn) - case ConnectionTypeUDP: - length := len(b) - if length > maxUartRecvSize-32 { - length = maxUartRecvSize - 32 - } - buf := b[:length] - from := make([]byte, 16) - fromLen := uint32(len(from)) - nn, err := d.Rpc_lwip_recvfrom(d.socket, &buf, uint32(length), 0x00000008, &from, &fromLen, 10000) - if err != nil { - return 0, err - } - - if nn == -1 { - return 0, nil - } - n = int(nn) - case ConnectionTypeTLS: - length := len(b) - if length > maxUartRecvSize-16 { - length = maxUartRecvSize - 16 - } - buf := b[:length] - nn, err := d.Rpc_wifi_get_ssl_receive(d.client, &buf, int32(length)) - if err != nil { - return 0, err - } - if nn < 0 { - return 0, fmt.Errorf("error %d", n) - } else if nn == 0 || nn == -30848 { - return 0, d.DisconnectSocket() - } - n = int(nn) - default: - } - - return n, nil -} - -func (d *Driver) IsSocketDataAvailable() bool { - if d.debug { - fmt.Printf("IsSocketDataAvailable()\r\n") - } - ret, err := d.Rpc_lwip_available(d.socket) - if err != nil { - fmt.Printf("error: %s\r\n", err.Error()) - return false - } - if ret == 1 { - return true - } - return false -} - -func (d *Driver) Response(timeout int) ([]byte, error) { - if d.debug { - fmt.Printf("Response(%d))\r\n", timeout) - } - // No implementation required - return nil, nil -} diff --git a/rtl8720dn/rpc.go b/rtl8720dn/rpc.go index 50498aef2..fc7b2f833 100644 --- a/rtl8720dn/rpc.go +++ b/rtl8720dn/rpc.go @@ -8,21 +8,13 @@ import ( "fmt" ) -func (r *RTL8720DN) Rpc_system_version() (string, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_system_version() string { if r.debug { fmt.Printf("rpc_system_version()\r\n") } msg := startWriteMessage(0x00, 0x01, 0x01, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return "", err - } + r.performRequest(msg) r.read() widx := 8 @@ -32,15 +24,10 @@ func (r *RTL8720DN) Rpc_system_version() (string, error) { result = string(payload[widx : widx+int(result_length)]) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_system_ack(c uint8) (uint8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_system_ack(c uint8) uint8 { if r.debug { fmt.Printf("rpc_system_ack()\r\n") } @@ -49,10 +36,7 @@ func (r *RTL8720DN) Rpc_system_ack(c uint8) (uint8, error) { // c : in uint8 msg = append(msg, byte(c>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -61,24 +45,16 @@ func (r *RTL8720DN) Rpc_system_ack(c uint8) (uint8, error) { result = uint8(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_init() (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_init() bool { if r.debug { fmt.Printf("rpc_ble_init()\r\n") } msg := startWriteMessage(0x00, 0x02, 0x01, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -86,59 +62,38 @@ func (r *RTL8720DN) Rpc_ble_init() (bool, error) { result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_start() error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_start() { if r.debug { fmt.Printf("rpc_ble_start()\r\n") } msg := startWriteMessage(0x00, 0x02, 0x02, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_ble_deinit() error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_deinit() { if r.debug { fmt.Printf("rpc_ble_deinit()\r\n") } msg := startWriteMessage(0x00, 0x02, 0x03, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_gap_set_param(param RPC_T_GAP_PARAM_TYPE, value []byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_set_param(param RPC_T_GAP_PARAM_TYPE, value []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_gap_set_param()\r\n") } @@ -153,10 +108,7 @@ func (r *RTL8720DN) Rpc_gap_set_param(param RPC_T_GAP_PARAM_TYPE, value []byte) msg = append(msg, byte(len(value)), byte(len(value)>>8), byte(len(value)>>16), byte(len(value)>>24)) msg = append(msg, []byte(value)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -165,15 +117,10 @@ func (r *RTL8720DN) Rpc_gap_set_param(param RPC_T_GAP_PARAM_TYPE, value []byte) result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_gap_get_param(param RPC_T_GAP_PARAM_TYPE, value *[]byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_get_param(param RPC_T_GAP_PARAM_TYPE, value []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_gap_get_param()\r\n") } @@ -185,10 +132,7 @@ func (r *RTL8720DN) Rpc_gap_get_param(param RPC_T_GAP_PARAM_TYPE, value *[]byte) msg = append(msg, byte(param>>16)) msg = append(msg, byte(param>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -196,33 +140,24 @@ func (r *RTL8720DN) Rpc_gap_get_param(param RPC_T_GAP_PARAM_TYPE, value *[]byte) value_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if value_length > 0 { - copy(*value, payload[widx:widx+int(value_length)]) + copy(value, payload[widx:widx+int(value_length)]) widx += int(value_length) } - *value = (*value)[:value_length] var result RPC_T_GAP_CAUSE result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_gap_set_pairable_mode() (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_set_pairable_mode() RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_gap_set_pairable_mode()\r\n") } msg := startWriteMessage(0x00, 0x03, 0x03, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -230,15 +165,10 @@ func (r *RTL8720DN) Rpc_gap_set_pairable_mode() (RPC_T_GAP_CAUSE, error) { result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_set_param(param RPC_T_LE_BOND_PARAM_TYPE, value []byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_set_param(param RPC_T_LE_BOND_PARAM_TYPE, value []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_set_param()\r\n") } @@ -253,10 +183,7 @@ func (r *RTL8720DN) Rpc_le_bond_set_param(param RPC_T_LE_BOND_PARAM_TYPE, value msg = append(msg, byte(len(value)), byte(len(value)>>8), byte(len(value)>>16), byte(len(value)>>24)) msg = append(msg, []byte(value)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -265,15 +192,10 @@ func (r *RTL8720DN) Rpc_le_bond_set_param(param RPC_T_LE_BOND_PARAM_TYPE, value result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_get_param(param RPC_T_LE_BOND_PARAM_TYPE, value *[]byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_get_param(param RPC_T_LE_BOND_PARAM_TYPE, value []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_get_param()\r\n") } @@ -285,10 +207,7 @@ func (r *RTL8720DN) Rpc_le_bond_get_param(param RPC_T_LE_BOND_PARAM_TYPE, value msg = append(msg, byte(param>>16)) msg = append(msg, byte(param>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -296,24 +215,18 @@ func (r *RTL8720DN) Rpc_le_bond_get_param(param RPC_T_LE_BOND_PARAM_TYPE, value value_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if value_length > 0 { - copy(*value, payload[widx:widx+int(value_length)]) + copy(value, payload[widx:widx+int(value_length)]) widx += int(value_length) } - *value = (*value)[:value_length] var result RPC_T_GAP_CAUSE result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_pair(conn_id uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_pair(conn_id uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_pair()\r\n") } @@ -322,10 +235,7 @@ func (r *RTL8720DN) Rpc_le_bond_pair(conn_id uint8) (RPC_T_GAP_CAUSE, error) { // conn_id : in uint8 msg = append(msg, byte(conn_id>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -334,15 +244,10 @@ func (r *RTL8720DN) Rpc_le_bond_pair(conn_id uint8) (RPC_T_GAP_CAUSE, error) { result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_get_display_key(conn_id uint8, key *uint32) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_get_display_key(conn_id uint8, key *uint32) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_get_display_key()\r\n") } @@ -351,10 +256,7 @@ func (r *RTL8720DN) Rpc_le_bond_get_display_key(conn_id uint8, key *uint32) (RPC // conn_id : in uint8 msg = append(msg, byte(conn_id>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -366,15 +268,10 @@ func (r *RTL8720DN) Rpc_le_bond_get_display_key(conn_id uint8, key *uint32) (RPC result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_passkey_input_confirm(conn_id uint8, passcode uint32, cause RPC_T_GAP_CFM_CAUSE) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_passkey_input_confirm(conn_id uint8, passcode uint32, cause RPC_T_GAP_CFM_CAUSE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_passkey_input_confirm()\r\n") } @@ -393,10 +290,7 @@ func (r *RTL8720DN) Rpc_le_bond_passkey_input_confirm(conn_id uint8, passcode ui msg = append(msg, byte(cause>>16)) msg = append(msg, byte(cause>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -405,15 +299,10 @@ func (r *RTL8720DN) Rpc_le_bond_passkey_input_confirm(conn_id uint8, passcode ui result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_oob_input_confirm(conn_id uint8, cause RPC_T_GAP_CFM_CAUSE) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_oob_input_confirm(conn_id uint8, cause RPC_T_GAP_CFM_CAUSE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_oob_input_confirm()\r\n") } @@ -427,10 +316,7 @@ func (r *RTL8720DN) Rpc_le_bond_oob_input_confirm(conn_id uint8, cause RPC_T_GAP msg = append(msg, byte(cause>>16)) msg = append(msg, byte(cause>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -439,15 +325,10 @@ func (r *RTL8720DN) Rpc_le_bond_oob_input_confirm(conn_id uint8, cause RPC_T_GAP result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_just_work_confirm(conn_id uint8, cause RPC_T_GAP_CFM_CAUSE) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_just_work_confirm(conn_id uint8, cause RPC_T_GAP_CFM_CAUSE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_just_work_confirm()\r\n") } @@ -461,10 +342,7 @@ func (r *RTL8720DN) Rpc_le_bond_just_work_confirm(conn_id uint8, cause RPC_T_GAP msg = append(msg, byte(cause>>16)) msg = append(msg, byte(cause>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -473,15 +351,10 @@ func (r *RTL8720DN) Rpc_le_bond_just_work_confirm(conn_id uint8, cause RPC_T_GAP result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_passkey_display_confirm(conn_id uint8, cause RPC_T_GAP_CFM_CAUSE) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_passkey_display_confirm(conn_id uint8, cause RPC_T_GAP_CFM_CAUSE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_passkey_display_confirm()\r\n") } @@ -495,10 +368,7 @@ func (r *RTL8720DN) Rpc_le_bond_passkey_display_confirm(conn_id uint8, cause RPC msg = append(msg, byte(cause>>16)) msg = append(msg, byte(cause>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -507,15 +377,10 @@ func (r *RTL8720DN) Rpc_le_bond_passkey_display_confirm(conn_id uint8, cause RPC result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_user_confirm(conn_id uint8, cause RPC_T_GAP_CFM_CAUSE) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_user_confirm(conn_id uint8, cause RPC_T_GAP_CFM_CAUSE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_user_confirm()\r\n") } @@ -529,10 +394,7 @@ func (r *RTL8720DN) Rpc_le_bond_user_confirm(conn_id uint8, cause RPC_T_GAP_CFM_ msg = append(msg, byte(cause>>16)) msg = append(msg, byte(cause>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -541,15 +403,10 @@ func (r *RTL8720DN) Rpc_le_bond_user_confirm(conn_id uint8, cause RPC_T_GAP_CFM_ result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_cfg_local_key_distribute(init_dist uint8, rsp_dist uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_cfg_local_key_distribute(init_dist uint8, rsp_dist uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_cfg_local_key_distribute()\r\n") } @@ -560,10 +417,7 @@ func (r *RTL8720DN) Rpc_le_bond_cfg_local_key_distribute(init_dist uint8, rsp_di // rsp_dist : in uint8 msg = append(msg, byte(rsp_dist>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -572,37 +426,24 @@ func (r *RTL8720DN) Rpc_le_bond_cfg_local_key_distribute(init_dist uint8, rsp_di result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_clear_all_keys() error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_clear_all_keys() { if r.debug { fmt.Printf("rpc_le_bond_clear_all_keys()\r\n") } msg := startWriteMessage(0x00, 0x04, 0x0B, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_le_bond_delete_by_idx(idx uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_delete_by_idx(idx uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_delete_by_idx()\r\n") } @@ -611,10 +452,7 @@ func (r *RTL8720DN) Rpc_le_bond_delete_by_idx(idx uint8) (RPC_T_GAP_CAUSE, error // idx : in uint8 msg = append(msg, byte(idx>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -623,15 +461,10 @@ func (r *RTL8720DN) Rpc_le_bond_delete_by_idx(idx uint8) (RPC_T_GAP_CAUSE, error result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_delete_by_bd(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_delete_by_bd(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_delete_by_bd()\r\n") } @@ -645,10 +478,7 @@ func (r *RTL8720DN) Rpc_le_bond_delete_by_bd(bd_addr uint8, bd_type RPC_T_GAP_RE msg = append(msg, byte(bd_type>>16)) msg = append(msg, byte(bd_type>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -657,15 +487,10 @@ func (r *RTL8720DN) Rpc_le_bond_delete_by_bd(bd_addr uint8, bd_type RPC_T_GAP_RE result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_bond_get_sec_level(conn_id uint8, sec_type RPC_T_GAP_SEC_LEVEL) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_bond_get_sec_level(conn_id uint8, sec_type RPC_T_GAP_SEC_LEVEL) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_get_sec_level()\r\n") } @@ -674,10 +499,7 @@ func (r *RTL8720DN) Rpc_le_bond_get_sec_level(conn_id uint8, sec_type RPC_T_GAP_ // conn_id : in uint8 msg = append(msg, byte(conn_id>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -688,15 +510,10 @@ func (r *RTL8720DN) Rpc_le_bond_get_sec_level(conn_id uint8, sec_type RPC_T_GAP_ result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_gap_init(link_num uint8) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_gap_init(link_num uint8) bool { if r.debug { fmt.Printf("rpc_le_gap_init()\r\n") } @@ -705,10 +522,7 @@ func (r *RTL8720DN) Rpc_le_gap_init(link_num uint8) (bool, error) { // link_num : in uint8 msg = append(msg, byte(link_num>>0)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -717,15 +531,10 @@ func (r *RTL8720DN) Rpc_le_gap_init(link_num uint8) (bool, error) { result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_gap_msg_info_way(use_msg bool) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_gap_msg_info_way(use_msg bool) { if r.debug { fmt.Printf("rpc_le_gap_msg_info_way()\r\n") } @@ -738,32 +547,21 @@ func (r *RTL8720DN) Rpc_le_gap_msg_info_way(use_msg bool) error { msg = append(msg, 0) } - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_le_get_max_link_num() (uint8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_max_link_num() uint8 { if r.debug { fmt.Printf("rpc_le_get_max_link_num()\r\n") } msg := startWriteMessage(0x00, 0x05, 0x03, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -771,15 +569,10 @@ func (r *RTL8720DN) Rpc_le_get_max_link_num() (uint8, error) { result = uint8(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_set_gap_param(param RPC_T_GAP_LE_PARAM_TYPE, value []byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_set_gap_param(param RPC_T_GAP_LE_PARAM_TYPE, value []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_set_gap_param()\r\n") } @@ -794,10 +587,7 @@ func (r *RTL8720DN) Rpc_le_set_gap_param(param RPC_T_GAP_LE_PARAM_TYPE, value [] msg = append(msg, byte(len(value)), byte(len(value)>>8), byte(len(value)>>16), byte(len(value)>>24)) msg = append(msg, []byte(value)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -806,15 +596,10 @@ func (r *RTL8720DN) Rpc_le_set_gap_param(param RPC_T_GAP_LE_PARAM_TYPE, value [] result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_gap_param(param RPC_T_GAP_LE_PARAM_TYPE, value *[]byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_gap_param(param RPC_T_GAP_LE_PARAM_TYPE, value []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_get_gap_param()\r\n") } @@ -826,10 +611,7 @@ func (r *RTL8720DN) Rpc_le_get_gap_param(param RPC_T_GAP_LE_PARAM_TYPE, value *[ msg = append(msg, byte(param>>16)) msg = append(msg, byte(param>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -837,24 +619,18 @@ func (r *RTL8720DN) Rpc_le_get_gap_param(param RPC_T_GAP_LE_PARAM_TYPE, value *[ value_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if value_length > 0 { - copy(*value, payload[widx:widx+int(value_length)]) + copy(value, payload[widx:widx+int(value_length)]) widx += int(value_length) } - *value = (*value)[:value_length] var result RPC_T_GAP_CAUSE result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_modify_white_list(operation RPC_T_GAP_WHITE_LIST_OP, bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_modify_white_list(operation RPC_T_GAP_WHITE_LIST_OP, bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_modify_white_list()\r\n") } @@ -873,10 +649,7 @@ func (r *RTL8720DN) Rpc_le_modify_white_list(operation RPC_T_GAP_WHITE_LIST_OP, msg = append(msg, byte(bd_type>>16)) msg = append(msg, byte(bd_type>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -885,15 +658,10 @@ func (r *RTL8720DN) Rpc_le_modify_white_list(operation RPC_T_GAP_WHITE_LIST_OP, result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_gen_rand_addr(rand_addr_type RPC_T_GAP_RAND_ADDR_TYPE, random_bd *uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_gen_rand_addr(rand_addr_type RPC_T_GAP_RAND_ADDR_TYPE, random_bd *uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_gen_rand_addr()\r\n") } @@ -905,10 +673,7 @@ func (r *RTL8720DN) Rpc_le_gen_rand_addr(rand_addr_type RPC_T_GAP_RAND_ADDR_TYPE msg = append(msg, byte(rand_addr_type>>16)) msg = append(msg, byte(rand_addr_type>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -920,15 +685,10 @@ func (r *RTL8720DN) Rpc_le_gen_rand_addr(rand_addr_type RPC_T_GAP_RAND_ADDR_TYPE result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_set_rand_addr(random_bd uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_set_rand_addr(random_bd uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_set_rand_addr()\r\n") } @@ -937,10 +697,7 @@ func (r *RTL8720DN) Rpc_le_set_rand_addr(random_bd uint8) (RPC_T_GAP_CAUSE, erro // random_bd : in uint8 msg = append(msg, byte(random_bd>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -949,15 +706,10 @@ func (r *RTL8720DN) Rpc_le_set_rand_addr(random_bd uint8) (RPC_T_GAP_CAUSE, erro result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_cfg_local_identity_address(addr uint8, ident_addr_type RPC_T_GAP_IDENT_ADDR_TYPE) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_cfg_local_identity_address(addr uint8, ident_addr_type RPC_T_GAP_IDENT_ADDR_TYPE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_cfg_local_identity_address()\r\n") } @@ -971,10 +723,7 @@ func (r *RTL8720DN) Rpc_le_cfg_local_identity_address(addr uint8, ident_addr_typ msg = append(msg, byte(ident_addr_type>>16)) msg = append(msg, byte(ident_addr_type>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -983,15 +732,10 @@ func (r *RTL8720DN) Rpc_le_cfg_local_identity_address(addr uint8, ident_addr_typ result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_set_host_chann_classif(p_channel_map uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_set_host_chann_classif(p_channel_map uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_set_host_chann_classif()\r\n") } @@ -1000,10 +744,7 @@ func (r *RTL8720DN) Rpc_le_set_host_chann_classif(p_channel_map uint8) (RPC_T_GA // p_channel_map : in uint8 msg = append(msg, byte(p_channel_map>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1012,15 +753,10 @@ func (r *RTL8720DN) Rpc_le_set_host_chann_classif(p_channel_map uint8) (RPC_T_GA result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_write_default_data_len(tx_octets uint16, tx_time uint16) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_write_default_data_len(tx_octets uint16, tx_time uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_write_default_data_len()\r\n") } @@ -1033,10 +769,7 @@ func (r *RTL8720DN) Rpc_le_write_default_data_len(tx_octets uint16, tx_time uint msg = append(msg, byte(tx_time>>0)) msg = append(msg, byte(tx_time>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1045,15 +778,10 @@ func (r *RTL8720DN) Rpc_le_write_default_data_len(tx_octets uint16, tx_time uint result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_gap_config_cccd_not_check(cccd_not_check_flag RPC_T_GAP_CONFIG_GATT_CCCD_NOT_CHECK) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_config_cccd_not_check(cccd_not_check_flag RPC_T_GAP_CONFIG_GATT_CCCD_NOT_CHECK) { if r.debug { fmt.Printf("rpc_gap_config_cccd_not_check()\r\n") } @@ -1065,23 +793,15 @@ func (r *RTL8720DN) Rpc_gap_config_cccd_not_check(cccd_not_check_flag RPC_T_GAP_ msg = append(msg, byte(cccd_not_check_flag>>16)) msg = append(msg, byte(cccd_not_check_flag>>24)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_gap_config_ccc_bits_count(gatt_server_ccc_bits_count uint8, gatt_storage_ccc_bits_count uint8) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_config_ccc_bits_count(gatt_server_ccc_bits_count uint8, gatt_storage_ccc_bits_count uint8) { if r.debug { fmt.Printf("rpc_gap_config_ccc_bits_count()\r\n") } @@ -1092,23 +812,15 @@ func (r *RTL8720DN) Rpc_gap_config_ccc_bits_count(gatt_server_ccc_bits_count uin // gatt_storage_ccc_bits_count : in uint8 msg = append(msg, byte(gatt_storage_ccc_bits_count>>0)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_gap_config_max_attribute_table_count(gatt_max_attribute_table_count uint8) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_config_max_attribute_table_count(gatt_max_attribute_table_count uint8) { if r.debug { fmt.Printf("rpc_gap_config_max_attribute_table_count()\r\n") } @@ -1117,23 +829,15 @@ func (r *RTL8720DN) Rpc_gap_config_max_attribute_table_count(gatt_max_attribute_ // gatt_max_attribute_table_count : in uint8 msg = append(msg, byte(gatt_max_attribute_table_count>>0)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_gap_config_max_mtu_size(att_max_mtu_size uint16) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_config_max_mtu_size(att_max_mtu_size uint16) { if r.debug { fmt.Printf("rpc_gap_config_max_mtu_size()\r\n") } @@ -1143,23 +847,15 @@ func (r *RTL8720DN) Rpc_gap_config_max_mtu_size(att_max_mtu_size uint16) error { msg = append(msg, byte(att_max_mtu_size>>0)) msg = append(msg, byte(att_max_mtu_size>>8)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_gap_config_bte_pool_size(bte_pool_size uint8) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_config_bte_pool_size(bte_pool_size uint8) { if r.debug { fmt.Printf("rpc_gap_config_bte_pool_size()\r\n") } @@ -1168,23 +864,15 @@ func (r *RTL8720DN) Rpc_gap_config_bte_pool_size(bte_pool_size uint8) error { // bte_pool_size : in uint8 msg = append(msg, byte(bte_pool_size>>0)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_gap_config_bt_report_buf_num(bt_report_buf_num uint8) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_config_bt_report_buf_num(bt_report_buf_num uint8) { if r.debug { fmt.Printf("rpc_gap_config_bt_report_buf_num()\r\n") } @@ -1193,23 +881,15 @@ func (r *RTL8720DN) Rpc_gap_config_bt_report_buf_num(bt_report_buf_num uint8) er // bt_report_buf_num : in uint8 msg = append(msg, byte(bt_report_buf_num>>0)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_gap_config_le_key_storage_flag(le_key_storage_flag uint16) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_config_le_key_storage_flag(le_key_storage_flag uint16) { if r.debug { fmt.Printf("rpc_gap_config_le_key_storage_flag()\r\n") } @@ -1219,23 +899,15 @@ func (r *RTL8720DN) Rpc_gap_config_le_key_storage_flag(le_key_storage_flag uint1 msg = append(msg, byte(le_key_storage_flag>>0)) msg = append(msg, byte(le_key_storage_flag>>8)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_gap_config_max_le_paired_device(max_le_paired_device uint8) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_config_max_le_paired_device(max_le_paired_device uint8) { if r.debug { fmt.Printf("rpc_gap_config_max_le_paired_device()\r\n") } @@ -1244,23 +916,15 @@ func (r *RTL8720DN) Rpc_gap_config_max_le_paired_device(max_le_paired_device uin // max_le_paired_device : in uint8 msg = append(msg, byte(max_le_paired_device>>0)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_gap_config_max_le_link_num(le_link_num uint8) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_gap_config_max_le_link_num(le_link_num uint8) { if r.debug { fmt.Printf("rpc_gap_config_max_le_link_num()\r\n") } @@ -1269,23 +933,15 @@ func (r *RTL8720DN) Rpc_gap_config_max_le_link_num(le_link_num uint8) error { // le_link_num : in uint8 msg = append(msg, byte(le_link_num>>0)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_le_adv_set_param(param RPC_T_LE_ADV_PARAM_TYPE, value []byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_adv_set_param(param RPC_T_LE_ADV_PARAM_TYPE, value []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_adv_set_param()\r\n") } @@ -1300,10 +956,7 @@ func (r *RTL8720DN) Rpc_le_adv_set_param(param RPC_T_LE_ADV_PARAM_TYPE, value [] msg = append(msg, byte(len(value)), byte(len(value)>>8), byte(len(value)>>16), byte(len(value)>>24)) msg = append(msg, []byte(value)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1312,15 +965,10 @@ func (r *RTL8720DN) Rpc_le_adv_set_param(param RPC_T_LE_ADV_PARAM_TYPE, value [] result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_adv_get_param(param RPC_T_LE_ADV_PARAM_TYPE, value *[]byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_adv_get_param(param RPC_T_LE_ADV_PARAM_TYPE, value []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_adv_get_param()\r\n") } @@ -1332,10 +980,7 @@ func (r *RTL8720DN) Rpc_le_adv_get_param(param RPC_T_LE_ADV_PARAM_TYPE, value *[ msg = append(msg, byte(param>>16)) msg = append(msg, byte(param>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1343,33 +988,24 @@ func (r *RTL8720DN) Rpc_le_adv_get_param(param RPC_T_LE_ADV_PARAM_TYPE, value *[ value_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if value_length > 0 { - copy(*value, payload[widx:widx+int(value_length)]) + copy(value, payload[widx:widx+int(value_length)]) widx += int(value_length) } - *value = (*value)[:value_length] var result RPC_T_GAP_CAUSE result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_adv_start() (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_adv_start() RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_adv_start()\r\n") } msg := startWriteMessage(0x00, 0x07, 0x03, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1377,24 +1013,16 @@ func (r *RTL8720DN) Rpc_le_adv_start() (RPC_T_GAP_CAUSE, error) { result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_adv_stop() (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_adv_stop() RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_adv_stop()\r\n") } msg := startWriteMessage(0x00, 0x07, 0x04, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1402,24 +1030,16 @@ func (r *RTL8720DN) Rpc_le_adv_stop() (RPC_T_GAP_CAUSE, error) { result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_adv_update_param() (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_adv_update_param() RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_adv_update_param()\r\n") } msg := startWriteMessage(0x00, 0x07, 0x05, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1427,15 +1047,10 @@ func (r *RTL8720DN) Rpc_le_adv_update_param() (RPC_T_GAP_CAUSE, error) { result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_scan_set_param(param RPC_T_LE_SCAN_PARAM_TYPE, value []byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_scan_set_param(param RPC_T_LE_SCAN_PARAM_TYPE, value []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_scan_set_param()\r\n") } @@ -1450,10 +1065,7 @@ func (r *RTL8720DN) Rpc_le_scan_set_param(param RPC_T_LE_SCAN_PARAM_TYPE, value msg = append(msg, byte(len(value)), byte(len(value)>>8), byte(len(value)>>16), byte(len(value)>>24)) msg = append(msg, []byte(value)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1462,15 +1074,10 @@ func (r *RTL8720DN) Rpc_le_scan_set_param(param RPC_T_LE_SCAN_PARAM_TYPE, value result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_scan_get_param(param RPC_T_LE_SCAN_PARAM_TYPE, value *[]byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_scan_get_param(param RPC_T_LE_SCAN_PARAM_TYPE, value []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_scan_get_param()\r\n") } @@ -1482,10 +1089,7 @@ func (r *RTL8720DN) Rpc_le_scan_get_param(param RPC_T_LE_SCAN_PARAM_TYPE, value msg = append(msg, byte(param>>16)) msg = append(msg, byte(param>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1493,33 +1097,24 @@ func (r *RTL8720DN) Rpc_le_scan_get_param(param RPC_T_LE_SCAN_PARAM_TYPE, value value_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if value_length > 0 { - copy(*value, payload[widx:widx+int(value_length)]) + copy(value, payload[widx:widx+int(value_length)]) widx += int(value_length) } - *value = (*value)[:value_length] var result RPC_T_GAP_CAUSE result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_scan_start() (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_scan_start() RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_scan_start()\r\n") } msg := startWriteMessage(0x00, 0x08, 0x03, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1527,15 +1122,10 @@ func (r *RTL8720DN) Rpc_le_scan_start() (RPC_T_GAP_CAUSE, error) { result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_scan_timer_start(tick uint32) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_scan_timer_start(tick uint32) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_scan_timer_start()\r\n") } @@ -1547,10 +1137,7 @@ func (r *RTL8720DN) Rpc_le_scan_timer_start(tick uint32) (RPC_T_GAP_CAUSE, error msg = append(msg, byte(tick>>16)) msg = append(msg, byte(tick>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1559,24 +1146,16 @@ func (r *RTL8720DN) Rpc_le_scan_timer_start(tick uint32) (RPC_T_GAP_CAUSE, error result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_scan_stop() (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_scan_stop() RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_scan_stop()\r\n") } msg := startWriteMessage(0x00, 0x08, 0x05, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1584,15 +1163,10 @@ func (r *RTL8720DN) Rpc_le_scan_stop() (RPC_T_GAP_CAUSE, error) { result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_scan_info_filter(enable bool, offset uint8, length uint8, p_filter uint8) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_scan_info_filter(enable bool, offset uint8, length uint8, p_filter uint8) bool { if r.debug { fmt.Printf("rpc_le_scan_info_filter()\r\n") } @@ -1611,10 +1185,7 @@ func (r *RTL8720DN) Rpc_le_scan_info_filter(enable bool, offset uint8, length ui // p_filter : in uint8 msg = append(msg, byte(p_filter>>0)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1623,15 +1194,10 @@ func (r *RTL8720DN) Rpc_le_scan_info_filter(enable bool, offset uint8, length ui result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_conn_param(param RPC_T_LE_CONN_PARAM_TYPE, value *[]byte, conn_id uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_conn_param(param RPC_T_LE_CONN_PARAM_TYPE, value []byte, conn_id uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_get_conn_param()\r\n") } @@ -1645,10 +1211,7 @@ func (r *RTL8720DN) Rpc_le_get_conn_param(param RPC_T_LE_CONN_PARAM_TYPE, value // conn_id : in uint8 msg = append(msg, byte(conn_id>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1656,24 +1219,18 @@ func (r *RTL8720DN) Rpc_le_get_conn_param(param RPC_T_LE_CONN_PARAM_TYPE, value value_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if value_length > 0 { - copy(*value, payload[widx:widx+int(value_length)]) + copy(value, payload[widx:widx+int(value_length)]) widx += int(value_length) } - *value = (*value)[:value_length] var result RPC_T_GAP_CAUSE result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_conn_info(conn_id uint8, p_conn_info RPC_T_GAP_CONN_INFO) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_conn_info(conn_id uint8, p_conn_info RPC_T_GAP_CONN_INFO) bool { if r.debug { fmt.Printf("rpc_le_get_conn_info()\r\n") } @@ -1682,10 +1239,7 @@ func (r *RTL8720DN) Rpc_le_get_conn_info(conn_id uint8, p_conn_info RPC_T_GAP_CO // conn_id : in uint8 msg = append(msg, byte(conn_id>>0)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1696,15 +1250,10 @@ func (r *RTL8720DN) Rpc_le_get_conn_info(conn_id uint8, p_conn_info RPC_T_GAP_CO result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_conn_addr(conn_id uint8, bd_addr *uint8, bd_type *uint8) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_conn_addr(conn_id uint8, bd_addr *uint8, bd_type *uint8) bool { if r.debug { fmt.Printf("rpc_le_get_conn_addr()\r\n") } @@ -1713,10 +1262,7 @@ func (r *RTL8720DN) Rpc_le_get_conn_addr(conn_id uint8, bd_addr *uint8, bd_type // conn_id : in uint8 msg = append(msg, byte(conn_id>>0)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1731,15 +1277,10 @@ func (r *RTL8720DN) Rpc_le_get_conn_addr(conn_id uint8, bd_addr *uint8, bd_type result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_conn_id(bd_addr uint8, bd_type uint8, p_conn_id *uint8) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_conn_id(bd_addr uint8, bd_type uint8, p_conn_id *uint8) bool { if r.debug { fmt.Printf("rpc_le_get_conn_id()\r\n") } @@ -1750,10 +1291,7 @@ func (r *RTL8720DN) Rpc_le_get_conn_id(bd_addr uint8, bd_type uint8, p_conn_id * // bd_type : in uint8 msg = append(msg, byte(bd_type>>0)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1765,24 +1303,16 @@ func (r *RTL8720DN) Rpc_le_get_conn_id(bd_addr uint8, bd_type uint8, p_conn_id * result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_active_link_num() (uint8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_active_link_num() uint8 { if r.debug { fmt.Printf("rpc_le_get_active_link_num()\r\n") } msg := startWriteMessage(0x00, 0x09, 0x05, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1790,24 +1320,16 @@ func (r *RTL8720DN) Rpc_le_get_active_link_num() (uint8, error) { result = uint8(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_idle_link_num() (uint8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_idle_link_num() uint8 { if r.debug { fmt.Printf("rpc_le_get_idle_link_num()\r\n") } msg := startWriteMessage(0x00, 0x09, 0x06, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1815,15 +1337,10 @@ func (r *RTL8720DN) Rpc_le_get_idle_link_num() (uint8, error) { result = uint8(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_disconnect(conn_id uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_disconnect(conn_id uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_disconnect()\r\n") } @@ -1832,10 +1349,7 @@ func (r *RTL8720DN) Rpc_le_disconnect(conn_id uint8) (RPC_T_GAP_CAUSE, error) { // conn_id : in uint8 msg = append(msg, byte(conn_id>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1844,15 +1358,10 @@ func (r *RTL8720DN) Rpc_le_disconnect(conn_id uint8) (RPC_T_GAP_CAUSE, error) { result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_read_rssi(conn_id uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_read_rssi(conn_id uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_read_rssi()\r\n") } @@ -1861,10 +1370,7 @@ func (r *RTL8720DN) Rpc_le_read_rssi(conn_id uint8) (RPC_T_GAP_CAUSE, error) { // conn_id : in uint8 msg = append(msg, byte(conn_id>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1873,15 +1379,10 @@ func (r *RTL8720DN) Rpc_le_read_rssi(conn_id uint8) (RPC_T_GAP_CAUSE, error) { result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_set_data_len(conn_id uint8, tx_octets uint16, tx_time uint16) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_set_data_len(conn_id uint8, tx_octets uint16, tx_time uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_set_data_len()\r\n") } @@ -1896,10 +1397,7 @@ func (r *RTL8720DN) Rpc_le_set_data_len(conn_id uint8, tx_octets uint16, tx_time msg = append(msg, byte(tx_time>>0)) msg = append(msg, byte(tx_time>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1908,15 +1406,10 @@ func (r *RTL8720DN) Rpc_le_set_data_len(conn_id uint8, tx_octets uint16, tx_time result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_set_phy(conn_id uint8, all_phys uint8, tx_phys uint8, rx_phys uint8, phy_options RPC_T_GAP_PHYS_OPTIONS) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_set_phy(conn_id uint8, all_phys uint8, tx_phys uint8, rx_phys uint8, phy_options RPC_T_GAP_PHYS_OPTIONS) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_set_phy()\r\n") } @@ -1936,10 +1429,7 @@ func (r *RTL8720DN) Rpc_le_set_phy(conn_id uint8, all_phys uint8, tx_phys uint8, msg = append(msg, byte(phy_options>>16)) msg = append(msg, byte(phy_options>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1948,15 +1438,10 @@ func (r *RTL8720DN) Rpc_le_set_phy(conn_id uint8, all_phys uint8, tx_phys uint8, result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_set_conn_param(conn_type RPC_T_GAP_CONN_PARAM_TYPE, p_conn_param RPC_T_GAP_LE_CONN_REQ_PARAM) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_set_conn_param(conn_type RPC_T_GAP_CONN_PARAM_TYPE, p_conn_param RPC_T_GAP_LE_CONN_REQ_PARAM) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_set_conn_param()\r\n") } @@ -1973,10 +1458,7 @@ func (r *RTL8720DN) Rpc_le_set_conn_param(conn_type RPC_T_GAP_CONN_PARAM_TYPE, p msg = append(msg, byte(p_conn_param>>16)) msg = append(msg, byte(p_conn_param>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -1985,15 +1467,10 @@ func (r *RTL8720DN) Rpc_le_set_conn_param(conn_type RPC_T_GAP_CONN_PARAM_TYPE, p result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_connect(init_phys uint8, remote_bd uint8, remote_bd_type RPC_T_GAP_REMOTE_ADDR_TYPE, local_bd_type RPC_T_GAP_LOCAL_ADDR_TYPE, scan_timeout uint16) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_connect(init_phys uint8, remote_bd uint8, remote_bd_type RPC_T_GAP_REMOTE_ADDR_TYPE, local_bd_type RPC_T_GAP_LOCAL_ADDR_TYPE, scan_timeout uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_connect()\r\n") } @@ -2017,10 +1494,7 @@ func (r *RTL8720DN) Rpc_le_connect(init_phys uint8, remote_bd uint8, remote_bd_t msg = append(msg, byte(scan_timeout>>0)) msg = append(msg, byte(scan_timeout>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2029,15 +1503,10 @@ func (r *RTL8720DN) Rpc_le_connect(init_phys uint8, remote_bd uint8, remote_bd_t result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_update_conn_param(conn_id uint8, conn_interval_min uint16, conn_interval_max uint16, conn_latency uint16, supervision_timeout uint16, ce_length_min uint16, ce_length_max uint16) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_update_conn_param(conn_id uint8, conn_interval_min uint16, conn_interval_max uint16, conn_latency uint16, supervision_timeout uint16, ce_length_min uint16, ce_length_max uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_update_conn_param()\r\n") } @@ -2064,10 +1533,7 @@ func (r *RTL8720DN) Rpc_le_update_conn_param(conn_id uint8, conn_interval_min ui msg = append(msg, byte(ce_length_max>>0)) msg = append(msg, byte(ce_length_max>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2076,15 +1542,10 @@ func (r *RTL8720DN) Rpc_le_update_conn_param(conn_id uint8, conn_interval_min ui result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_flash_save_local_name(p_data RPC_T_LOCAL_NAME) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_flash_save_local_name(p_data RPC_T_LOCAL_NAME) uint32 { if r.debug { fmt.Printf("rpc_flash_save_local_name()\r\n") } @@ -2096,10 +1557,7 @@ func (r *RTL8720DN) Rpc_flash_save_local_name(p_data RPC_T_LOCAL_NAME) (uint32, msg = append(msg, byte(p_data>>16)) msg = append(msg, byte(p_data>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2108,24 +1566,16 @@ func (r *RTL8720DN) Rpc_flash_save_local_name(p_data RPC_T_LOCAL_NAME) (uint32, result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_flash_load_local_name(p_data RPC_T_LOCAL_NAME) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_flash_load_local_name(p_data RPC_T_LOCAL_NAME) uint32 { if r.debug { fmt.Printf("rpc_flash_load_local_name()\r\n") } msg := startWriteMessage(0x00, 0x0A, 0x02, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2136,15 +1586,10 @@ func (r *RTL8720DN) Rpc_flash_load_local_name(p_data RPC_T_LOCAL_NAME) (uint32, result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_flash_save_local_appearance(p_data RPC_T_LOCAL_APPEARANCE) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_flash_save_local_appearance(p_data RPC_T_LOCAL_APPEARANCE) uint32 { if r.debug { fmt.Printf("rpc_flash_save_local_appearance()\r\n") } @@ -2156,10 +1601,7 @@ func (r *RTL8720DN) Rpc_flash_save_local_appearance(p_data RPC_T_LOCAL_APPEARANC msg = append(msg, byte(p_data>>16)) msg = append(msg, byte(p_data>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2168,24 +1610,16 @@ func (r *RTL8720DN) Rpc_flash_save_local_appearance(p_data RPC_T_LOCAL_APPEARANC result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_flash_load_local_appearance(p_data RPC_T_LOCAL_APPEARANCE) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_flash_load_local_appearance(p_data RPC_T_LOCAL_APPEARANCE) uint32 { if r.debug { fmt.Printf("rpc_flash_load_local_appearance()\r\n") } msg := startWriteMessage(0x00, 0x0A, 0x04, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2196,15 +1630,10 @@ func (r *RTL8720DN) Rpc_flash_load_local_appearance(p_data RPC_T_LOCAL_APPEARANC result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_find_key_entry(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) (RPC_T_LE_KEY_ENTRY, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_find_key_entry(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) RPC_T_LE_KEY_ENTRY { if r.debug { fmt.Printf("rpc_le_find_key_entry()\r\n") } @@ -2218,10 +1647,7 @@ func (r *RTL8720DN) Rpc_le_find_key_entry(bd_addr uint8, bd_type RPC_T_GAP_REMOT msg = append(msg, byte(bd_type>>16)) msg = append(msg, byte(bd_type>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2230,15 +1656,10 @@ func (r *RTL8720DN) Rpc_le_find_key_entry(bd_addr uint8, bd_type RPC_T_GAP_REMOT result = RPC_T_LE_KEY_ENTRY(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_find_key_entry_by_idx(idx uint8) (RPC_T_LE_KEY_ENTRY, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_find_key_entry_by_idx(idx uint8) RPC_T_LE_KEY_ENTRY { if r.debug { fmt.Printf("rpc_le_find_key_entry_by_idx()\r\n") } @@ -2247,10 +1668,7 @@ func (r *RTL8720DN) Rpc_le_find_key_entry_by_idx(idx uint8) (RPC_T_LE_KEY_ENTRY, // idx : in uint8 msg = append(msg, byte(idx>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2259,24 +1677,16 @@ func (r *RTL8720DN) Rpc_le_find_key_entry_by_idx(idx uint8) (RPC_T_LE_KEY_ENTRY, result = RPC_T_LE_KEY_ENTRY(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_bond_dev_num() (uint8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_bond_dev_num() uint8 { if r.debug { fmt.Printf("rpc_le_get_bond_dev_num()\r\n") } msg := startWriteMessage(0x00, 0x0A, 0x07, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2284,24 +1694,16 @@ func (r *RTL8720DN) Rpc_le_get_bond_dev_num() (uint8, error) { result = uint8(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_low_priority_bond() (RPC_T_LE_KEY_ENTRY, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_low_priority_bond() RPC_T_LE_KEY_ENTRY { if r.debug { fmt.Printf("rpc_le_get_low_priority_bond()\r\n") } msg := startWriteMessage(0x00, 0x0A, 0x08, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2309,24 +1711,16 @@ func (r *RTL8720DN) Rpc_le_get_low_priority_bond() (RPC_T_LE_KEY_ENTRY, error) { result = RPC_T_LE_KEY_ENTRY(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_high_priority_bond() (RPC_T_LE_KEY_ENTRY, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_high_priority_bond() RPC_T_LE_KEY_ENTRY { if r.debug { fmt.Printf("rpc_le_get_high_priority_bond()\r\n") } msg := startWriteMessage(0x00, 0x0A, 0x09, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2334,15 +1728,10 @@ func (r *RTL8720DN) Rpc_le_get_high_priority_bond() (RPC_T_LE_KEY_ENTRY, error) result = RPC_T_LE_KEY_ENTRY(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_set_high_priority_bond(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_set_high_priority_bond(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) bool { if r.debug { fmt.Printf("rpc_le_set_high_priority_bond()\r\n") } @@ -2356,10 +1745,7 @@ func (r *RTL8720DN) Rpc_le_set_high_priority_bond(bd_addr uint8, bd_type RPC_T_G msg = append(msg, byte(bd_type>>16)) msg = append(msg, byte(bd_type>>24)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2368,15 +1754,10 @@ func (r *RTL8720DN) Rpc_le_set_high_priority_bond(bd_addr uint8, bd_type RPC_T_G result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_resolve_random_address(unresolved_addr uint8, resolved_addr *uint8, resolved_addr_type RPC_T_GAP_IDENT_ADDR_TYPE) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_resolve_random_address(unresolved_addr uint8, resolved_addr *uint8, resolved_addr_type RPC_T_GAP_IDENT_ADDR_TYPE) bool { if r.debug { fmt.Printf("rpc_le_resolve_random_address()\r\n") } @@ -2392,10 +1773,7 @@ func (r *RTL8720DN) Rpc_le_resolve_random_address(unresolved_addr uint8, resolve msg = append(msg, byte(resolved_addr_type>>16)) msg = append(msg, byte(resolved_addr_type>>24)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2409,15 +1787,10 @@ func (r *RTL8720DN) Rpc_le_resolve_random_address(unresolved_addr uint8, resolve result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_cccd_data(p_entry RPC_T_LE_KEY_ENTRY, p_data RPC_T_LE_CCCD) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_cccd_data(p_entry RPC_T_LE_KEY_ENTRY, p_data RPC_T_LE_CCCD) bool { if r.debug { fmt.Printf("rpc_le_get_cccd_data()\r\n") } @@ -2429,10 +1802,7 @@ func (r *RTL8720DN) Rpc_le_get_cccd_data(p_entry RPC_T_LE_KEY_ENTRY, p_data RPC_ msg = append(msg, byte(p_entry>>16)) msg = append(msg, byte(p_entry>>24)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2443,15 +1813,10 @@ func (r *RTL8720DN) Rpc_le_get_cccd_data(p_entry RPC_T_LE_KEY_ENTRY, p_data RPC_ result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_gen_bond_dev(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE, local_bd_type RPC_T_GAP_LOCAL_ADDR_TYPE, local_ltk []byte, key_type RPC_T_LE_KEY_TYPE, p_cccd RPC_T_LE_CCCD) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_gen_bond_dev(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE, local_bd_type RPC_T_GAP_LOCAL_ADDR_TYPE, local_ltk []byte, key_type RPC_T_LE_KEY_TYPE, p_cccd RPC_T_LE_CCCD) bool { if r.debug { fmt.Printf("rpc_le_gen_bond_dev()\r\n") } @@ -2483,10 +1848,7 @@ func (r *RTL8720DN) Rpc_le_gen_bond_dev(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ msg = append(msg, byte(p_cccd>>16)) msg = append(msg, byte(p_cccd>>24)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2495,24 +1857,16 @@ func (r *RTL8720DN) Rpc_le_gen_bond_dev(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_dev_bond_info_len() (uint16, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_dev_bond_info_len() uint16 { if r.debug { fmt.Printf("rpc_le_get_dev_bond_info_len()\r\n") } msg := startWriteMessage(0x00, 0x0A, 0x0E, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2520,15 +1874,10 @@ func (r *RTL8720DN) Rpc_le_get_dev_bond_info_len() (uint16, error) { result = uint16(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_set_dev_bond_info(p_data []byte, exist *bool) (RPC_T_LE_KEY_ENTRY, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_set_dev_bond_info(p_data []byte, exist *bool) RPC_T_LE_KEY_ENTRY { if r.debug { fmt.Printf("rpc_le_set_dev_bond_info()\r\n") } @@ -2538,10 +1887,7 @@ func (r *RTL8720DN) Rpc_le_set_dev_bond_info(p_data []byte, exist *bool) (RPC_T_ msg = append(msg, byte(len(p_data)), byte(len(p_data)>>8), byte(len(p_data)>>16), byte(len(p_data)>>24)) msg = append(msg, []byte(p_data)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2553,15 +1899,10 @@ func (r *RTL8720DN) Rpc_le_set_dev_bond_info(p_data []byte, exist *bool) (RPC_T_ result = RPC_T_LE_KEY_ENTRY(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_le_get_dev_bond_info(p_entry RPC_T_LE_KEY_ENTRY, p_data *[]byte) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_le_get_dev_bond_info(p_entry RPC_T_LE_KEY_ENTRY, p_data []byte) bool { if r.debug { fmt.Printf("rpc_le_get_dev_bond_info()\r\n") } @@ -2573,10 +1914,7 @@ func (r *RTL8720DN) Rpc_le_get_dev_bond_info(p_entry RPC_T_LE_KEY_ENTRY, p_data msg = append(msg, byte(p_entry>>16)) msg = append(msg, byte(p_entry>>24)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2584,24 +1922,18 @@ func (r *RTL8720DN) Rpc_le_get_dev_bond_info(p_entry RPC_T_LE_KEY_ENTRY, p_data p_data_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if p_data_length > 0 { - copy(*p_data, payload[widx:widx+int(p_data_length)]) + copy(p_data, payload[widx:widx+int(p_data_length)]) widx += int(p_data_length) } - *p_data = (*p_data)[:p_data_length] var result bool result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_client_init(num uint8) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_client_init(num uint8) bool { if r.debug { fmt.Printf("rpc_ble_client_init()\r\n") } @@ -2610,10 +1942,7 @@ func (r *RTL8720DN) Rpc_ble_client_init(num uint8) (bool, error) { // num : in uint8 msg = append(msg, byte(num>>0)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2622,15 +1951,10 @@ func (r *RTL8720DN) Rpc_ble_client_init(num uint8) (bool, error) { result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_add_client(app_id uint8, link_num uint8) (uint8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_add_client(app_id uint8, link_num uint8) uint8 { if r.debug { fmt.Printf("rpc_ble_add_client()\r\n") } @@ -2641,10 +1965,7 @@ func (r *RTL8720DN) Rpc_ble_add_client(app_id uint8, link_num uint8) (uint8, err // link_num : in uint8 msg = append(msg, byte(link_num>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2653,15 +1974,10 @@ func (r *RTL8720DN) Rpc_ble_add_client(app_id uint8, link_num uint8) (uint8, err result = uint8(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_init(client_num uint8) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_init(client_num uint8) { if r.debug { fmt.Printf("rpc_client_init()\r\n") } @@ -2670,23 +1986,15 @@ func (r *RTL8720DN) Rpc_client_init(client_num uint8) error { // client_num : in uint8 msg = append(msg, byte(client_num>>0)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_client_all_primary_srv_discovery(conn_id uint8, client_id uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_all_primary_srv_discovery(conn_id uint8, client_id uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_all_primary_srv_discovery()\r\n") } @@ -2697,10 +2005,7 @@ func (r *RTL8720DN) Rpc_client_all_primary_srv_discovery(conn_id uint8, client_i // client_id : in uint8 msg = append(msg, byte(client_id>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2709,15 +2014,10 @@ func (r *RTL8720DN) Rpc_client_all_primary_srv_discovery(conn_id uint8, client_i result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_by_uuid_srv_discovery(conn_id uint8, client_id uint8, uuid16 uint16) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_by_uuid_srv_discovery(conn_id uint8, client_id uint8, uuid16 uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_by_uuid_srv_discovery()\r\n") } @@ -2731,10 +2031,7 @@ func (r *RTL8720DN) Rpc_client_by_uuid_srv_discovery(conn_id uint8, client_id ui msg = append(msg, byte(uuid16>>0)) msg = append(msg, byte(uuid16>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2743,15 +2040,10 @@ func (r *RTL8720DN) Rpc_client_by_uuid_srv_discovery(conn_id uint8, client_id ui result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_by_uuid128_srv_discovery(conn_id uint8, client_id uint8, p_uuid128 uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_by_uuid128_srv_discovery(conn_id uint8, client_id uint8, p_uuid128 uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_by_uuid128_srv_discovery()\r\n") } @@ -2764,10 +2056,7 @@ func (r *RTL8720DN) Rpc_client_by_uuid128_srv_discovery(conn_id uint8, client_id // p_uuid128 : in uint8 msg = append(msg, byte(p_uuid128>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2776,15 +2065,10 @@ func (r *RTL8720DN) Rpc_client_by_uuid128_srv_discovery(conn_id uint8, client_id result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_relationship_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_relationship_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_relationship_discovery()\r\n") } @@ -2801,10 +2085,7 @@ func (r *RTL8720DN) Rpc_client_relationship_discovery(conn_id uint8, client_id u msg = append(msg, byte(end_handle>>0)) msg = append(msg, byte(end_handle>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2813,15 +2094,10 @@ func (r *RTL8720DN) Rpc_client_relationship_discovery(conn_id uint8, client_id u result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_all_char_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_all_char_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_all_char_discovery()\r\n") } @@ -2838,10 +2114,7 @@ func (r *RTL8720DN) Rpc_client_all_char_discovery(conn_id uint8, client_id uint8 msg = append(msg, byte(end_handle>>0)) msg = append(msg, byte(end_handle>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2850,15 +2123,10 @@ func (r *RTL8720DN) Rpc_client_all_char_discovery(conn_id uint8, client_id uint8 result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_by_uuid_char_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16, uuid16 uint16) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_by_uuid_char_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16, uuid16 uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_by_uuid_char_discovery()\r\n") } @@ -2878,10 +2146,7 @@ func (r *RTL8720DN) Rpc_client_by_uuid_char_discovery(conn_id uint8, client_id u msg = append(msg, byte(uuid16>>0)) msg = append(msg, byte(uuid16>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2890,15 +2155,10 @@ func (r *RTL8720DN) Rpc_client_by_uuid_char_discovery(conn_id uint8, client_id u result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_by_uuid128_char_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16, p_uuid128 uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_by_uuid128_char_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16, p_uuid128 uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_by_uuid128_char_discovery()\r\n") } @@ -2917,10 +2177,7 @@ func (r *RTL8720DN) Rpc_client_by_uuid128_char_discovery(conn_id uint8, client_i // p_uuid128 : in uint8 msg = append(msg, byte(p_uuid128>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2929,15 +2186,10 @@ func (r *RTL8720DN) Rpc_client_by_uuid128_char_discovery(conn_id uint8, client_i result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_all_char_descriptor_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_all_char_descriptor_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_all_char_descriptor_discovery()\r\n") } @@ -2954,10 +2206,7 @@ func (r *RTL8720DN) Rpc_client_all_char_descriptor_discovery(conn_id uint8, clie msg = append(msg, byte(end_handle>>0)) msg = append(msg, byte(end_handle>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -2966,15 +2215,10 @@ func (r *RTL8720DN) Rpc_client_all_char_descriptor_discovery(conn_id uint8, clie result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_attr_read(conn_id uint8, client_id uint8, handle uint16) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_attr_read(conn_id uint8, client_id uint8, handle uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_attr_read()\r\n") } @@ -2988,10 +2232,7 @@ func (r *RTL8720DN) Rpc_client_attr_read(conn_id uint8, client_id uint8, handle msg = append(msg, byte(handle>>0)) msg = append(msg, byte(handle>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3000,15 +2241,10 @@ func (r *RTL8720DN) Rpc_client_attr_read(conn_id uint8, client_id uint8, handle result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_attr_read_using_uuid(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16, uuid16 uint16, p_uuid128 uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_attr_read_using_uuid(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16, uuid16 uint16, p_uuid128 uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_attr_read_using_uuid()\r\n") } @@ -3030,10 +2266,7 @@ func (r *RTL8720DN) Rpc_client_attr_read_using_uuid(conn_id uint8, client_id uin // p_uuid128 : in uint8 msg = append(msg, byte(p_uuid128>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3042,15 +2275,10 @@ func (r *RTL8720DN) Rpc_client_attr_read_using_uuid(conn_id uint8, client_id uin result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_attr_write(conn_id uint8, client_id uint8, write_type RPC_T_GATT_WRITE_TYPE, handle uint16, data []byte) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_attr_write(conn_id uint8, client_id uint8, write_type RPC_T_GATT_WRITE_TYPE, handle uint16, data []byte) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_attr_write()\r\n") } @@ -3072,10 +2300,7 @@ func (r *RTL8720DN) Rpc_client_attr_write(conn_id uint8, client_id uint8, write_ msg = append(msg, byte(len(data)), byte(len(data)>>8), byte(len(data)>>16), byte(len(data)>>24)) msg = append(msg, []byte(data)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3084,15 +2309,10 @@ func (r *RTL8720DN) Rpc_client_attr_write(conn_id uint8, client_id uint8, write_ result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_client_attr_ind_confirm(conn_id uint8) (RPC_T_GAP_CAUSE, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_client_attr_ind_confirm(conn_id uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_attr_ind_confirm()\r\n") } @@ -3101,10 +2321,7 @@ func (r *RTL8720DN) Rpc_client_attr_ind_confirm(conn_id uint8) (RPC_T_GAP_CAUSE, // conn_id : in uint8 msg = append(msg, byte(conn_id>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3113,15 +2330,10 @@ func (r *RTL8720DN) Rpc_client_attr_ind_confirm(conn_id uint8) (RPC_T_GAP_CAUSE, result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_server_init(num uint8) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_server_init(num uint8) bool { if r.debug { fmt.Printf("rpc_ble_server_init()\r\n") } @@ -3130,10 +2342,7 @@ func (r *RTL8720DN) Rpc_ble_server_init(num uint8) (bool, error) { // num : in uint8 msg = append(msg, byte(num>>0)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3142,15 +2351,10 @@ func (r *RTL8720DN) Rpc_ble_server_init(num uint8) (bool, error) { result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_create_service(uuid uint8, uuid_length uint8, is_primary bool) (uint8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_create_service(uuid uint8, uuid_length uint8, is_primary bool) uint8 { if r.debug { fmt.Printf("rpc_ble_create_service()\r\n") } @@ -3167,10 +2371,7 @@ func (r *RTL8720DN) Rpc_ble_create_service(uuid uint8, uuid_length uint8, is_pri msg = append(msg, 0) } - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3179,15 +2380,10 @@ func (r *RTL8720DN) Rpc_ble_create_service(uuid uint8, uuid_length uint8, is_pri result = uint8(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_delete_service(app_id uint8) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_delete_service(app_id uint8) bool { if r.debug { fmt.Printf("rpc_ble_delete_service()\r\n") } @@ -3196,10 +2392,7 @@ func (r *RTL8720DN) Rpc_ble_delete_service(app_id uint8) (bool, error) { // app_id : in uint8 msg = append(msg, byte(app_id>>0)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3208,15 +2401,10 @@ func (r *RTL8720DN) Rpc_ble_delete_service(app_id uint8) (bool, error) { result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_service_start(app_id uint8) (uint8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_service_start(app_id uint8) uint8 { if r.debug { fmt.Printf("rpc_ble_service_start()\r\n") } @@ -3225,10 +2413,7 @@ func (r *RTL8720DN) Rpc_ble_service_start(app_id uint8) (uint8, error) { // app_id : in uint8 msg = append(msg, byte(app_id>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3237,15 +2422,10 @@ func (r *RTL8720DN) Rpc_ble_service_start(app_id uint8) (uint8, error) { result = uint8(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_get_servie_handle(app_id uint8) (uint8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_get_servie_handle(app_id uint8) uint8 { if r.debug { fmt.Printf("rpc_ble_get_servie_handle()\r\n") } @@ -3254,10 +2434,7 @@ func (r *RTL8720DN) Rpc_ble_get_servie_handle(app_id uint8) (uint8, error) { // app_id : in uint8 msg = append(msg, byte(app_id>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3266,15 +2443,10 @@ func (r *RTL8720DN) Rpc_ble_get_servie_handle(app_id uint8) (uint8, error) { result = uint8(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_create_char(app_id uint8, uuid uint8, uuid_length uint8, properties uint8, permissions uint32) (uint16, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_create_char(app_id uint8, uuid uint8, uuid_length uint8, properties uint8, permissions uint32) uint16 { if r.debug { fmt.Printf("rpc_ble_create_char()\r\n") } @@ -3294,10 +2466,7 @@ func (r *RTL8720DN) Rpc_ble_create_char(app_id uint8, uuid uint8, uuid_length ui msg = append(msg, byte(permissions>>16)) msg = append(msg, byte(permissions>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3306,15 +2475,10 @@ func (r *RTL8720DN) Rpc_ble_create_char(app_id uint8, uuid uint8, uuid_length ui result = uint16(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_create_desc(app_id uint8, char_handle uint16, uuid uint8, uuid_length uint8, flags uint8, permissions uint32, value_length uint16, p_value []byte) (uint16, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_create_desc(app_id uint8, char_handle uint16, uuid uint8, uuid_length uint8, flags uint8, permissions uint32, value_length uint16, p_value []byte) uint16 { if r.debug { fmt.Printf("rpc_ble_create_desc()\r\n") } @@ -3348,10 +2512,7 @@ func (r *RTL8720DN) Rpc_ble_create_desc(app_id uint8, char_handle uint16, uuid u msg = append(msg, []byte(p_value)...) } - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3360,15 +2521,10 @@ func (r *RTL8720DN) Rpc_ble_create_desc(app_id uint8, char_handle uint16, uuid u result = uint16(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_server_send_data(conn_id uint8, service_id uint8, attrib_index uint16, data []byte, pdu_type RPC_T_GATT_PDU_TYPE) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_server_send_data(conn_id uint8, service_id uint8, attrib_index uint16, data []byte, pdu_type RPC_T_GATT_PDU_TYPE) bool { if r.debug { fmt.Printf("rpc_server_send_data()\r\n") } @@ -3390,10 +2546,7 @@ func (r *RTL8720DN) Rpc_server_send_data(conn_id uint8, service_id uint8, attrib msg = append(msg, byte(pdu_type>>16)) msg = append(msg, byte(pdu_type>>24)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3402,15 +2555,10 @@ func (r *RTL8720DN) Rpc_server_send_data(conn_id uint8, service_id uint8, attrib result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_server_get_attr_value(app_id uint8, attr_handle uint16) ([]byte, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_server_get_attr_value(app_id uint8, attr_handle uint16) []byte { if r.debug { fmt.Printf("rpc_ble_server_get_attr_value()\r\n") } @@ -3422,10 +2570,7 @@ func (r *RTL8720DN) Rpc_ble_server_get_attr_value(app_id uint8, attr_handle uint msg = append(msg, byte(attr_handle>>0)) msg = append(msg, byte(attr_handle>>8)) - err := r.performRequest(msg) - if err != nil { - return nil, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3436,15 +2581,10 @@ func (r *RTL8720DN) Rpc_ble_server_get_attr_value(app_id uint8, attr_handle uint copy(result, payload[widx:result_length]) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_server_exec_write_confirm(conn_id uint8, cause uint16, handle uint16) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_server_exec_write_confirm(conn_id uint8, cause uint16, handle uint16) bool { if r.debug { fmt.Printf("rpc_server_exec_write_confirm()\r\n") } @@ -3459,10 +2599,7 @@ func (r *RTL8720DN) Rpc_server_exec_write_confirm(conn_id uint8, cause uint16, h msg = append(msg, byte(handle>>0)) msg = append(msg, byte(handle>>8)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3471,15 +2608,10 @@ func (r *RTL8720DN) Rpc_server_exec_write_confirm(conn_id uint8, cause uint16, h result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_server_attr_write_confirm(conn_id uint8, service_id uint8, attrib_index uint16, cause RPC_T_APP_RESULT) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_server_attr_write_confirm(conn_id uint8, service_id uint8, attrib_index uint16, cause RPC_T_APP_RESULT) bool { if r.debug { fmt.Printf("rpc_server_attr_write_confirm()\r\n") } @@ -3498,10 +2630,7 @@ func (r *RTL8720DN) Rpc_server_attr_write_confirm(conn_id uint8, service_id uint msg = append(msg, byte(cause>>16)) msg = append(msg, byte(cause>>24)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3510,15 +2639,10 @@ func (r *RTL8720DN) Rpc_server_attr_write_confirm(conn_id uint8, service_id uint result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_server_attr_read_confirm(conn_id uint8, service_id uint8, attrib_index uint16, data []byte, cause RPC_T_APP_RESULT) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_server_attr_read_confirm(conn_id uint8, service_id uint8, attrib_index uint16, data []byte, cause RPC_T_APP_RESULT) bool { if r.debug { fmt.Printf("rpc_server_attr_read_confirm()\r\n") } @@ -3540,10 +2664,7 @@ func (r *RTL8720DN) Rpc_server_attr_read_confirm(conn_id uint8, service_id uint8 msg = append(msg, byte(cause>>16)) msg = append(msg, byte(cause>>24)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3552,15 +2673,10 @@ func (r *RTL8720DN) Rpc_server_attr_read_confirm(conn_id uint8, service_id uint8 result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_handle_gap_msg(gap_msg []byte) (RPC_T_APP_RESULT, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_handle_gap_msg(gap_msg []byte) RPC_T_APP_RESULT { if r.debug { fmt.Printf("rpc_ble_handle_gap_msg()\r\n") } @@ -3570,10 +2686,7 @@ func (r *RTL8720DN) Rpc_ble_handle_gap_msg(gap_msg []byte) (RPC_T_APP_RESULT, er msg = append(msg, byte(len(gap_msg)), byte(len(gap_msg)>>8), byte(len(gap_msg)>>16), byte(len(gap_msg)>>24)) msg = append(msg, []byte(gap_msg)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3582,15 +2695,10 @@ func (r *RTL8720DN) Rpc_ble_handle_gap_msg(gap_msg []byte) (RPC_T_APP_RESULT, er result = RPC_T_APP_RESULT(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_gap_callback(cb_type uint8, cb_data []byte) (RPC_T_APP_RESULT, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_gap_callback(cb_type uint8, cb_data []byte) RPC_T_APP_RESULT { if r.debug { fmt.Printf("rpc_ble_gap_callback()\r\n") } @@ -3602,10 +2710,7 @@ func (r *RTL8720DN) Rpc_ble_gap_callback(cb_type uint8, cb_data []byte) (RPC_T_A msg = append(msg, byte(len(cb_data)), byte(len(cb_data)>>8), byte(len(cb_data)>>16), byte(len(cb_data)>>24)) msg = append(msg, []byte(cb_data)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3614,15 +2719,10 @@ func (r *RTL8720DN) Rpc_ble_gap_callback(cb_type uint8, cb_data []byte) (RPC_T_A result = RPC_T_APP_RESULT(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_gattc_callback(gatt_if uint8, conn_id uint8, cb_data []byte, extra_data []byte) (RPC_T_APP_RESULT, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_gattc_callback(gatt_if uint8, conn_id uint8, cb_data []byte, extra_data []byte) RPC_T_APP_RESULT { if r.debug { fmt.Printf("rpc_ble_gattc_callback()\r\n") } @@ -3639,10 +2739,7 @@ func (r *RTL8720DN) Rpc_ble_gattc_callback(gatt_if uint8, conn_id uint8, cb_data msg = append(msg, byte(len(extra_data)), byte(len(extra_data)>>8), byte(len(extra_data)>>16), byte(len(extra_data)>>24)) msg = append(msg, []byte(extra_data)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3651,15 +2748,10 @@ func (r *RTL8720DN) Rpc_ble_gattc_callback(gatt_if uint8, conn_id uint8, cb_data result = RPC_T_APP_RESULT(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ble_gatts_callback(gatt_if uint8, conn_id uint8, attrib_index uint16, event RPC_T_SERVICE_CALLBACK_TYPE, property uint16, read_cb_data *[]byte, write_cb_data []byte, app_cb_data []byte) (RPC_T_APP_RESULT, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ble_gatts_callback(gatt_if uint8, conn_id uint8, attrib_index uint16, event RPC_T_SERVICE_CALLBACK_TYPE, property uint16, read_cb_data []byte, write_cb_data []byte, app_cb_data []byte) RPC_T_APP_RESULT { if r.debug { fmt.Printf("rpc_ble_gatts_callback()\r\n") } @@ -3697,10 +2789,7 @@ func (r *RTL8720DN) Rpc_ble_gatts_callback(gatt_if uint8, conn_id uint8, attrib_ msg = append(msg, []byte(app_cb_data)...) } - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -3712,24 +2801,18 @@ func (r *RTL8720DN) Rpc_ble_gatts_callback(gatt_if uint8, conn_id uint8, attrib_ widx += 4 } if read_cb_data_length > 0 { - copy(*read_cb_data, payload[widx:widx+int(read_cb_data_length)]) + copy(read_cb_data, payload[widx:widx+int(read_cb_data_length)]) widx += int(read_cb_data_length) } - *read_cb_data = (*read_cb_data)[:read_cb_data_length] var result RPC_T_APP_RESULT result = RPC_T_APP_RESULT(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_connect(ssid string, password string, security_type uint32, key_id int32, semaphore uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_connect(ssid string, password string, security_type uint32, key_id int32, semaphore uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_connect()\r\n") } @@ -3762,33 +2845,20 @@ func (r *RTL8720DN) Rpc_wifi_connect(ssid string, password string, security_type msg = append(msg, byte(semaphore>>16)) msg = append(msg, byte(semaphore>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_connect_bssid(bssid []byte, ssid string, password string, security_type uint32, key_id int32, semaphore uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_connect_bssid(bssid []byte, ssid string, password string, security_type uint32, key_id int32, semaphore uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_connect_bssid()\r\n") } @@ -3824,95 +2894,56 @@ func (r *RTL8720DN) Rpc_wifi_connect_bssid(bssid []byte, ssid string, password s msg = append(msg, byte(semaphore>>16)) msg = append(msg, byte(semaphore>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_disconnect() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_disconnect() int32 { if r.debug { fmt.Printf("rpc_wifi_disconnect()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x03, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_is_connected_to_ap() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_is_connected_to_ap() int32 { if r.debug { fmt.Printf("rpc_wifi_is_connected_to_ap()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x04, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_is_up(itf uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_is_up(itf uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_is_up()\r\n") } @@ -3924,33 +2955,20 @@ func (r *RTL8720DN) Rpc_wifi_is_up(itf uint32) (int32, error) { msg = append(msg, byte(itf>>16)) msg = append(msg, byte(itf>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_is_ready_to_transceive(itf uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_is_ready_to_transceive(itf uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_is_ready_to_transceive()\r\n") } @@ -3962,33 +2980,20 @@ func (r *RTL8720DN) Rpc_wifi_is_ready_to_transceive(itf uint32) (int32, error) { msg = append(msg, byte(itf>>16)) msg = append(msg, byte(itf>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_mac_address(mac []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_mac_address(mac []byte) int32 { if r.debug { fmt.Printf("rpc_wifi_set_mac_address()\r\n") } @@ -3998,205 +3003,124 @@ func (r *RTL8720DN) Rpc_wifi_set_mac_address(mac []byte) (int32, error) { msg = append(msg, byte(len(mac)), byte(len(mac)>>8), byte(len(mac)>>16), byte(len(mac)>>24)) msg = append(msg, []byte(mac)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_mac_address(mac *uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_mac_address(mac []uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_get_mac_address()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x08, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 - // mac : out uint8 - *mac = payload[widx] - widx += 1 + // mac : out []uint8 + copy(mac, payload[widx:widx+18]) + widx += 18 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_enable_powersave() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_enable_powersave() int32 { if r.debug { fmt.Printf("rpc_wifi_enable_powersave()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x09, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_resume_powersave() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_resume_powersave() int32 { if r.debug { fmt.Printf("rpc_wifi_resume_powersave()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x0A, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_disable_powersave() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_disable_powersave() int32 { if r.debug { fmt.Printf("rpc_wifi_disable_powersave()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x0B, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_btcoex_set_bt_on() error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_btcoex_set_bt_on() { if r.debug { fmt.Printf("rpc_wifi_btcoex_set_bt_on()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x0C, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_wifi_btcoex_set_bt_off() error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_btcoex_set_bt_off() { if r.debug { fmt.Printf("rpc_wifi_btcoex_set_bt_off()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x0D, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_wifi_get_associated_client_list(client_list_buffer *[]byte, buffer_length uint16) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_associated_client_list(client_list_buffer []byte, buffer_length uint16) int32 { if r.debug { fmt.Printf("rpc_wifi_get_associated_client_list()\r\n") } @@ -4206,10 +3130,7 @@ func (r *RTL8720DN) Rpc_wifi_get_associated_client_list(client_list_buffer *[]by msg = append(msg, byte(buffer_length>>0)) msg = append(msg, byte(buffer_length>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -4217,39 +3138,25 @@ func (r *RTL8720DN) Rpc_wifi_get_associated_client_list(client_list_buffer *[]by client_list_buffer_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if client_list_buffer_length > 0 { - copy(*client_list_buffer, payload[widx:widx+int(client_list_buffer_length)]) + copy(client_list_buffer, payload[widx:widx+int(client_list_buffer_length)]) widx += int(client_list_buffer_length) } - *client_list_buffer = (*client_list_buffer)[:client_list_buffer_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_ap_bssid(bssid *uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_ap_bssid(bssid *uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_get_ap_bssid()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x0F, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -4259,32 +3166,19 @@ func (r *RTL8720DN) Rpc_wifi_get_ap_bssid(bssid *uint8) (int32, error) { var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_ap_info(ap_info *[]byte, security *uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_ap_info(ap_info []byte, security *uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_get_ap_info()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x10, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -4292,33 +3186,22 @@ func (r *RTL8720DN) Rpc_wifi_get_ap_info(ap_info *[]byte, security *uint32) (int ap_info_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if ap_info_length > 0 { - copy(*ap_info, payload[widx:widx+int(ap_info_length)]) + copy(ap_info, payload[widx:widx+int(ap_info_length)]) widx += int(ap_info_length) } - *ap_info = (*ap_info)[:ap_info_length] // security : out uint32 *security = binary.LittleEndian.Uint32(payload[widx:]) widx += 4 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_country(country_code uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_country(country_code uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_set_country()\r\n") } @@ -4330,42 +3213,26 @@ func (r *RTL8720DN) Rpc_wifi_set_country(country_code uint32) (int32, error) { msg = append(msg, byte(country_code>>16)) msg = append(msg, byte(country_code>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_sta_max_data_rate(inidata_rate *uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_sta_max_data_rate(inidata_rate *uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_get_sta_max_data_rate()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x12, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -4375,32 +3242,19 @@ func (r *RTL8720DN) Rpc_wifi_get_sta_max_data_rate(inidata_rate *uint8) (int32, var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_rssi(pRSSI *int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_rssi(pRSSI *int32) int32 { if r.debug { fmt.Printf("rpc_wifi_get_rssi()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x13, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -4410,23 +3264,13 @@ func (r *RTL8720DN) Rpc_wifi_get_rssi(pRSSI *int32) (int32, error) { var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_channel(channel int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_channel(channel int32) int32 { if r.debug { fmt.Printf("rpc_wifi_set_channel()\r\n") } @@ -4438,42 +3282,26 @@ func (r *RTL8720DN) Rpc_wifi_set_channel(channel int32) (int32, error) { msg = append(msg, byte(channel>>16)) msg = append(msg, byte(channel>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_channel(channel *int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_channel(channel *int32) int32 { if r.debug { fmt.Printf("rpc_wifi_get_channel()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x15, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -4483,23 +3311,13 @@ func (r *RTL8720DN) Rpc_wifi_get_channel(channel *int32) (int32, error) { var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_change_channel_plan(channel_plan uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_change_channel_plan(channel_plan uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_change_channel_plan()\r\n") } @@ -4508,33 +3326,20 @@ func (r *RTL8720DN) Rpc_wifi_change_channel_plan(channel_plan uint8) (int32, err // channel_plan : in uint8 msg = append(msg, byte(channel_plan>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_register_multicast_address(mac uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_register_multicast_address(mac uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_register_multicast_address()\r\n") } @@ -4543,33 +3348,20 @@ func (r *RTL8720DN) Rpc_wifi_register_multicast_address(mac uint8) (int32, error // mac : in uint8 msg = append(msg, byte(mac>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_unregister_multicast_address(mac uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_unregister_multicast_address(mac uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_unregister_multicast_address()\r\n") } @@ -4578,95 +3370,56 @@ func (r *RTL8720DN) Rpc_wifi_unregister_multicast_address(mac uint8) (int32, err // mac : in uint8 msg = append(msg, byte(mac>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_rf_on() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_rf_on() int32 { if r.debug { fmt.Printf("rpc_wifi_rf_on()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x19, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_rf_off() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_rf_off() int32 { if r.debug { fmt.Printf("rpc_wifi_rf_off()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x1A, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_on(mode uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_on(mode uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_on()\r\n") } @@ -4678,64 +3431,38 @@ func (r *RTL8720DN) Rpc_wifi_on(mode uint32) (int32, error) { msg = append(msg, byte(mode>>16)) msg = append(msg, byte(mode>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_off() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_off() int32 { if r.debug { fmt.Printf("rpc_wifi_off()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x1C, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_mode(mode uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_mode(mode uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_set_mode()\r\n") } @@ -4747,64 +3474,38 @@ func (r *RTL8720DN) Rpc_wifi_set_mode(mode uint32) (int32, error) { msg = append(msg, byte(mode>>16)) msg = append(msg, byte(mode>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_off_fastly() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_off_fastly() int32 { if r.debug { fmt.Printf("rpc_wifi_off_fastly()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x1E, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_power_mode(ips_mode uint8, lps_mode uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_power_mode(ips_mode uint8, lps_mode uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_power_mode()\r\n") } @@ -4815,33 +3516,20 @@ func (r *RTL8720DN) Rpc_wifi_set_power_mode(ips_mode uint8, lps_mode uint8) (int // lps_mode : in uint8 msg = append(msg, byte(lps_mode>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_tdma_param(slot_period uint8, rfon_period_len_1 uint8, rfon_period_len_2 uint8, rfon_period_len_3 uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_tdma_param(slot_period uint8, rfon_period_len_1 uint8, rfon_period_len_2 uint8, rfon_period_len_3 uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_tdma_param()\r\n") } @@ -4856,33 +3544,20 @@ func (r *RTL8720DN) Rpc_wifi_set_tdma_param(slot_period uint8, rfon_period_len_1 // rfon_period_len_3 : in uint8 msg = append(msg, byte(rfon_period_len_3>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_lps_dtim(dtim uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_lps_dtim(dtim uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_lps_dtim()\r\n") } @@ -4891,42 +3566,26 @@ func (r *RTL8720DN) Rpc_wifi_set_lps_dtim(dtim uint8) (int32, error) { // dtim : in uint8 msg = append(msg, byte(dtim>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_lps_dtim(dtim *uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_lps_dtim(dtim *uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_get_lps_dtim()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x22, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -4936,23 +3595,13 @@ func (r *RTL8720DN) Rpc_wifi_get_lps_dtim(dtim *uint8) (int32, error) { var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_lps_thresh(mode uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_lps_thresh(mode uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_lps_thresh()\r\n") } @@ -4961,33 +3610,20 @@ func (r *RTL8720DN) Rpc_wifi_set_lps_thresh(mode uint8) (int32, error) { // mode : in uint8 msg = append(msg, byte(mode>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_lps_level(lps_level uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_lps_level(lps_level uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_lps_level()\r\n") } @@ -4996,33 +3632,20 @@ func (r *RTL8720DN) Rpc_wifi_set_lps_level(lps_level uint8) (int32, error) { // lps_level : in uint8 msg = append(msg, byte(lps_level>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_mfp_support(value uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_mfp_support(value uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_mfp_support()\r\n") } @@ -5031,33 +3654,20 @@ func (r *RTL8720DN) Rpc_wifi_set_mfp_support(value uint8) (int32, error) { // value : in uint8 msg = append(msg, byte(value>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_start_ap(ssid string, password string, security_type uint32, channel int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_start_ap(ssid string, password string, security_type uint32, channel int32) int32 { if r.debug { fmt.Printf("rpc_wifi_start_ap()\r\n") } @@ -5085,33 +3695,20 @@ func (r *RTL8720DN) Rpc_wifi_start_ap(ssid string, password string, security_typ msg = append(msg, byte(channel>>16)) msg = append(msg, byte(channel>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_start_ap_with_hidden_ssid(ssid string, password string, security_type uint32, channel int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_start_ap_with_hidden_ssid(ssid string, password string, security_type uint32, channel int32) int32 { if r.debug { fmt.Printf("rpc_wifi_start_ap_with_hidden_ssid()\r\n") } @@ -5139,33 +3736,20 @@ func (r *RTL8720DN) Rpc_wifi_start_ap_with_hidden_ssid(ssid string, password str msg = append(msg, byte(channel>>16)) msg = append(msg, byte(channel>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_pscan_chan(channel_list []byte, pscan_config uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_pscan_chan(channel_list []byte, pscan_config uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_pscan_chan()\r\n") } @@ -5177,33 +3761,20 @@ func (r *RTL8720DN) Rpc_wifi_set_pscan_chan(channel_list []byte, pscan_config ui // pscan_config : in uint8 msg = append(msg, byte(pscan_config>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_setting(ifname string, pSetting *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_setting(ifname string, pSetting []byte) int32 { if r.debug { fmt.Printf("rpc_wifi_get_setting()\r\n") } @@ -5213,10 +3784,7 @@ func (r *RTL8720DN) Rpc_wifi_get_setting(ifname string, pSetting *[]byte) (int32 msg = append(msg, byte(len(ifname)), byte(len(ifname)>>8), byte(len(ifname)>>16), byte(len(ifname)>>24)) msg = append(msg, []byte(ifname)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -5224,30 +3792,19 @@ func (r *RTL8720DN) Rpc_wifi_get_setting(ifname string, pSetting *[]byte) (int32 pSetting_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pSetting_length > 0 { - copy(*pSetting, payload[widx:widx+int(pSetting_length)]) + copy(pSetting, payload[widx:widx+int(pSetting_length)]) widx += int(pSetting_length) } - *pSetting = (*pSetting)[:pSetting_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_network_mode(mode uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_network_mode(mode uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_set_network_mode()\r\n") } @@ -5259,42 +3816,26 @@ func (r *RTL8720DN) Rpc_wifi_set_network_mode(mode uint32) (int32, error) { msg = append(msg, byte(mode>>16)) msg = append(msg, byte(mode>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_network_mode(pmode *uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_network_mode(pmode *uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_get_network_mode()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x2B, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -5304,23 +3845,13 @@ func (r *RTL8720DN) Rpc_wifi_get_network_mode(pmode *uint32) (int32, error) { var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_wps_phase(is_trigger_wps uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_wps_phase(is_trigger_wps uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_wps_phase()\r\n") } @@ -5329,33 +3860,20 @@ func (r *RTL8720DN) Rpc_wifi_set_wps_phase(is_trigger_wps uint8) (int32, error) // is_trigger_wps : in uint8 msg = append(msg, byte(is_trigger_wps>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_restart_ap(ssid []byte, password []byte, security_type uint32, channel int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_restart_ap(ssid []byte, password []byte, security_type uint32, channel int32) int32 { if r.debug { fmt.Printf("rpc_wifi_restart_ap()\r\n") } @@ -5378,33 +3896,20 @@ func (r *RTL8720DN) Rpc_wifi_restart_ap(ssid []byte, password []byte, security_t msg = append(msg, byte(channel>>16)) msg = append(msg, byte(channel>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_config_autoreconnect(mode uint8, retry_times uint8, timeout uint16) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_config_autoreconnect(mode uint8, retry_times uint8, timeout uint16) int32 { if r.debug { fmt.Printf("rpc_wifi_config_autoreconnect()\r\n") } @@ -5418,33 +3923,20 @@ func (r *RTL8720DN) Rpc_wifi_config_autoreconnect(mode uint8, retry_times uint8, msg = append(msg, byte(timeout>>0)) msg = append(msg, byte(timeout>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_autoreconnect(mode uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_autoreconnect(mode uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_autoreconnect()\r\n") } @@ -5453,42 +3945,26 @@ func (r *RTL8720DN) Rpc_wifi_set_autoreconnect(mode uint8) (int32, error) { // mode : in uint8 msg = append(msg, byte(mode>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_autoreconnect(mode *uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_autoreconnect(mode *uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_get_autoreconnect()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x30, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -5498,54 +3974,31 @@ func (r *RTL8720DN) Rpc_wifi_get_autoreconnect(mode *uint8) (int32, error) { var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_last_error() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_last_error() int32 { if r.debug { fmt.Printf("rpc_wifi_get_last_error()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x31, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_add_custom_ie(cus_ie []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_add_custom_ie(cus_ie []byte) int32 { if r.debug { fmt.Printf("rpc_wifi_add_custom_ie()\r\n") } @@ -5555,33 +4008,20 @@ func (r *RTL8720DN) Rpc_wifi_add_custom_ie(cus_ie []byte) (int32, error) { msg = append(msg, byte(len(cus_ie)), byte(len(cus_ie)>>8), byte(len(cus_ie)>>16), byte(len(cus_ie)>>24)) msg = append(msg, []byte(cus_ie)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_update_custom_ie(cus_ie []byte, ie_index int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_update_custom_ie(cus_ie []byte, ie_index int32) int32 { if r.debug { fmt.Printf("rpc_wifi_update_custom_ie()\r\n") } @@ -5596,64 +4036,38 @@ func (r *RTL8720DN) Rpc_wifi_update_custom_ie(cus_ie []byte, ie_index int32) (in msg = append(msg, byte(ie_index>>16)) msg = append(msg, byte(ie_index>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_del_custom_ie() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_del_custom_ie() int32 { if r.debug { fmt.Printf("rpc_wifi_del_custom_ie()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x34, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_indicate_mgnt(enable int32) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_indicate_mgnt(enable int32) { if r.debug { fmt.Printf("rpc_wifi_set_indicate_mgnt()\r\n") } @@ -5665,32 +4079,21 @@ func (r *RTL8720DN) Rpc_wifi_set_indicate_mgnt(enable int32) error { msg = append(msg, byte(enable>>16)) msg = append(msg, byte(enable>>24)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_wifi_get_drv_ability(ability *uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_drv_ability(ability *uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_get_drv_ability()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x36, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -5700,23 +4103,13 @@ func (r *RTL8720DN) Rpc_wifi_get_drv_ability(ability *uint32) (int32, error) { var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_channel_plan(channel_plan uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_channel_plan(channel_plan uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_channel_plan()\r\n") } @@ -5725,42 +4118,26 @@ func (r *RTL8720DN) Rpc_wifi_set_channel_plan(channel_plan uint8) (int32, error) // channel_plan : in uint8 msg = append(msg, byte(channel_plan>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_channel_plan(channel_plan *uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_channel_plan(channel_plan *uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_get_channel_plan()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x38, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -5770,85 +4147,49 @@ func (r *RTL8720DN) Rpc_wifi_get_channel_plan(channel_plan *uint8) (int32, error var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_enable_forwarding() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_enable_forwarding() int32 { if r.debug { fmt.Printf("rpc_wifi_enable_forwarding()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x39, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_disable_forwarding() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_disable_forwarding() int32 { if r.debug { fmt.Printf("rpc_wifi_disable_forwarding()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x3A, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_ch_deauth(enable uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_ch_deauth(enable uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_set_ch_deauth()\r\n") } @@ -5857,42 +4198,26 @@ func (r *RTL8720DN) Rpc_wifi_set_ch_deauth(enable uint8) (int32, error) { // enable : in uint8 msg = append(msg, byte(enable>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_band_type() (uint8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_band_type() uint8 { if r.debug { fmt.Printf("rpc_wifi_get_band_type()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x3C, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -5900,15 +4225,10 @@ func (r *RTL8720DN) Rpc_wifi_get_band_type() (uint8, error) { result = uint8(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_set_tx_pause_data(NewState uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_set_tx_pause_data(NewState uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_set_tx_pause_data()\r\n") } @@ -5920,42 +4240,26 @@ func (r *RTL8720DN) Rpc_wifi_set_tx_pause_data(NewState uint32) (int32, error) { msg = append(msg, byte(NewState>>16)) msg = append(msg, byte(NewState>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_reconnect_data(wifi_info *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_reconnect_data(wifi_info []byte) int32 { if r.debug { fmt.Printf("rpc_wifi_get_reconnect_data()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x3E, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -5963,101 +4267,61 @@ func (r *RTL8720DN) Rpc_wifi_get_reconnect_data(wifi_info *[]byte) (int32, error wifi_info_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if wifi_info_length > 0 { - copy(*wifi_info, payload[widx:widx+int(wifi_info_length)]) + copy(wifi_info, payload[widx:widx+int(wifi_info_length)]) widx += int(wifi_info_length) } - *wifi_info = (*wifi_info)[:wifi_info_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_clear_reconnect_data() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_clear_reconnect_data() int32 { if r.debug { fmt.Printf("rpc_wifi_clear_reconnect_data()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x3F, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_scan_start() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_scan_start() int32 { if r.debug { fmt.Printf("rpc_wifi_scan_start()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x40, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_is_scaning() (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_is_scaning() bool { if r.debug { fmt.Printf("rpc_wifi_is_scaning()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x41, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -6065,15 +4329,10 @@ func (r *RTL8720DN) Rpc_wifi_is_scaning() (bool, error) { result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_scan_get_ap_records(number uint16, _scanResult *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_scan_get_ap_records(number uint16, _scanResult []byte) int32 { if r.debug { fmt.Printf("rpc_wifi_scan_get_ap_records()\r\n") } @@ -6083,10 +4342,7 @@ func (r *RTL8720DN) Rpc_wifi_scan_get_ap_records(number uint16, _scanResult *[]b msg = append(msg, byte(number>>0)) msg = append(msg, byte(number>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -6094,39 +4350,25 @@ func (r *RTL8720DN) Rpc_wifi_scan_get_ap_records(number uint16, _scanResult *[]b _scanResult_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if _scanResult_length > 0 { - copy(*_scanResult, payload[widx:widx+int(_scanResult_length)]) + copy(_scanResult, payload[widx:widx+int(_scanResult_length)]) widx += int(_scanResult_length) } - *_scanResult = (*_scanResult)[:_scanResult_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_scan_get_ap_num() (uint16, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_scan_get_ap_num() uint16 { if r.debug { fmt.Printf("rpc_wifi_scan_get_ap_num()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x43, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -6134,46 +4376,28 @@ func (r *RTL8720DN) Rpc_wifi_scan_get_ap_num() (uint16, error) { result = uint16(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_init() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_init() int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_init()\r\n") } msg := startWriteMessage(0x00, 0x0F, 0x01, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_sta_start(mac []byte, ip_info []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_sta_start(mac []byte, ip_info []byte) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_sta_start()\r\n") } @@ -6186,33 +4410,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_sta_start(mac []byte, ip_info []byte) (int msg = append(msg, byte(len(ip_info)), byte(len(ip_info)>>8), byte(len(ip_info)>>16), byte(len(ip_info)>>24)) msg = append(msg, []byte(ip_info)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_ap_start(mac []byte, ip_info []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_ap_start(mac []byte, ip_info []byte) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_ap_start()\r\n") } @@ -6225,33 +4436,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_ap_start(mac []byte, ip_info []byte) (int3 msg = append(msg, byte(len(ip_info)), byte(len(ip_info)>>8), byte(len(ip_info)>>16), byte(len(ip_info)>>24)) msg = append(msg, []byte(ip_info)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_stop(tcpip_if uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_stop(tcpip_if uint32) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_stop()\r\n") } @@ -6263,33 +4461,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_stop(tcpip_if uint32) (int32, error) { msg = append(msg, byte(tcpip_if>>16)) msg = append(msg, byte(tcpip_if>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_up(tcpip_if uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_up(tcpip_if uint32) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_up()\r\n") } @@ -6301,33 +4486,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_up(tcpip_if uint32) (int32, error) { msg = append(msg, byte(tcpip_if>>16)) msg = append(msg, byte(tcpip_if>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_down(tcpip_if uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_down(tcpip_if uint32) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_down()\r\n") } @@ -6339,33 +4511,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_down(tcpip_if uint32) (int32, error) { msg = append(msg, byte(tcpip_if>>16)) msg = append(msg, byte(tcpip_if>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_get_ip_info(tcpip_if uint32, ip_info *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_get_ip_info(tcpip_if uint32, ip_info []byte) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_get_ip_info()\r\n") } @@ -6377,10 +4536,7 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_get_ip_info(tcpip_if uint32, ip_info *[]by msg = append(msg, byte(tcpip_if>>16)) msg = append(msg, byte(tcpip_if>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -6388,30 +4544,19 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_get_ip_info(tcpip_if uint32, ip_info *[]by ip_info_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if ip_info_length > 0 { - copy(*ip_info, payload[widx:widx+int(ip_info_length)]) + copy(ip_info, payload[widx:widx+int(ip_info_length)]) widx += int(ip_info_length) } - *ip_info = (*ip_info)[:ip_info_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_set_ip_info(tcpip_if uint32, ip_info []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_set_ip_info(tcpip_if uint32, ip_info []byte) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_set_ip_info()\r\n") } @@ -6426,33 +4571,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_set_ip_info(tcpip_if uint32, ip_info []byt msg = append(msg, byte(len(ip_info)), byte(len(ip_info)>>8), byte(len(ip_info)>>16), byte(len(ip_info)>>24)) msg = append(msg, []byte(ip_info)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_set_dns_info(tcpip_if uint32, dns_type uint32, dns []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_set_dns_info(tcpip_if uint32, dns_type uint32, dns []byte) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_set_dns_info()\r\n") } @@ -6472,33 +4604,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_set_dns_info(tcpip_if uint32, dns_type uin msg = append(msg, byte(len(dns)), byte(len(dns)>>8), byte(len(dns)>>16), byte(len(dns)>>24)) msg = append(msg, []byte(dns)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_get_dns_info(tcpip_if uint32, dns_type uint32, dns *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_get_dns_info(tcpip_if uint32, dns_type uint32, dns []byte) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_get_dns_info()\r\n") } @@ -6515,10 +4634,7 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_get_dns_info(tcpip_if uint32, dns_type uin msg = append(msg, byte(dns_type>>16)) msg = append(msg, byte(dns_type>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -6526,30 +4642,19 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_get_dns_info(tcpip_if uint32, dns_type uin dns_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if dns_length > 0 { - copy(*dns, payload[widx:widx+int(dns_length)]) + copy(dns, payload[widx:widx+int(dns_length)]) widx += int(dns_length) } - *dns = (*dns)[:dns_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_dhcps_start(tcpip_if uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_dhcps_start(tcpip_if uint32) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_dhcps_start()\r\n") } @@ -6561,33 +4666,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_dhcps_start(tcpip_if uint32) (int32, error msg = append(msg, byte(tcpip_if>>16)) msg = append(msg, byte(tcpip_if>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_dhcps_stop(tcpip_if uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_dhcps_stop(tcpip_if uint32) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_dhcps_stop()\r\n") } @@ -6599,33 +4691,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_dhcps_stop(tcpip_if uint32) (int32, error) msg = append(msg, byte(tcpip_if>>16)) msg = append(msg, byte(tcpip_if>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_dhcpc_start(tcpip_if uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_dhcpc_start(tcpip_if uint32) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_dhcpc_start()\r\n") } @@ -6637,33 +4716,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_dhcpc_start(tcpip_if uint32) (int32, error msg = append(msg, byte(tcpip_if>>16)) msg = append(msg, byte(tcpip_if>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_dhcpc_stop(tcpip_if uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_dhcpc_stop(tcpip_if uint32) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_dhcpc_stop()\r\n") } @@ -6675,33 +4741,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_dhcpc_stop(tcpip_if uint32) (int32, error) msg = append(msg, byte(tcpip_if>>16)) msg = append(msg, byte(tcpip_if>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_set_hostname(tcpip_if uint32, hostname string) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_set_hostname(tcpip_if uint32, hostname string) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_set_hostname()\r\n") } @@ -6716,33 +4769,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_set_hostname(tcpip_if uint32, hostname str msg = append(msg, byte(len(hostname)), byte(len(hostname)>>8), byte(len(hostname)>>16), byte(len(hostname)>>24)) msg = append(msg, []byte(hostname)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_get_hostname(tcpip_if uint32, hostname string) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_get_hostname(tcpip_if uint32, hostname string) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_get_hostname()\r\n") } @@ -6754,10 +4794,7 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_get_hostname(tcpip_if uint32, hostname str msg = append(msg, byte(tcpip_if>>16)) msg = append(msg, byte(tcpip_if>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -6768,27 +4805,16 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_get_hostname(tcpip_if uint32, hostname str hostname = string(payload[widx : widx+int(hostname_length)]) widx += int(hostname_length) } - hostname = (hostname)[:hostname_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_get_mac(tcpip_if uint32, mac *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_get_mac(tcpip_if uint32, mac []byte) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_get_mac()\r\n") } @@ -6800,10 +4826,7 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_get_mac(tcpip_if uint32, mac *[]byte) (int msg = append(msg, byte(tcpip_if>>16)) msg = append(msg, byte(tcpip_if>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -6811,30 +4834,19 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_get_mac(tcpip_if uint32, mac *[]byte) (int mac_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if mac_length > 0 { - copy(*mac, payload[widx:widx+int(mac_length)]) + copy(mac, payload[widx:widx+int(mac_length)]) widx += int(mac_length) } - *mac = (*mac)[:mac_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_adapter_set_mac(tcpip_if uint32, mac []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_adapter_set_mac(tcpip_if uint32, mac []byte) int32 { if r.debug { fmt.Printf("rpc_tcpip_adapter_set_mac()\r\n") } @@ -6849,33 +4861,20 @@ func (r *RTL8720DN) Rpc_tcpip_adapter_set_mac(tcpip_if uint32, mac []byte) (int3 msg = append(msg, byte(len(mac)), byte(len(mac)>>8), byte(len(mac)>>16), byte(len(mac)>>24)) msg = append(msg, []byte(mac)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcpip_api_call(fn []byte, call []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_api_call(fn []byte, call []byte) int32 { if r.debug { fmt.Printf("rpc_tcpip_api_call()\r\n") } @@ -6888,33 +4887,20 @@ func (r *RTL8720DN) Rpc_tcpip_api_call(fn []byte, call []byte) (int32, error) { msg = append(msg, byte(len(call)), byte(len(call)>>8), byte(len(call)>>16), byte(len(call)>>24)) msg = append(msg, []byte(call)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_connect(pcb_in []byte, pcb_out *[]byte, ipaddr []byte, port uint16, connected []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_connect(pcb_in []byte, pcb_out []byte, ipaddr []byte, port uint16, connected []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_connect()\r\n") } @@ -6933,10 +4919,7 @@ func (r *RTL8720DN) Rpc_tcp_connect(pcb_in []byte, pcb_out *[]byte, ipaddr []byt msg = append(msg, byte(len(connected)), byte(len(connected)>>8), byte(len(connected)>>16), byte(len(connected)>>24)) msg = append(msg, []byte(connected)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -6944,30 +4927,19 @@ func (r *RTL8720DN) Rpc_tcp_connect(pcb_in []byte, pcb_out *[]byte, ipaddr []byt pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_recved(pcb_in []byte, pcb_out *[]byte, length uint16) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_recved(pcb_in []byte, pcb_out []byte, length uint16) int32 { if r.debug { fmt.Printf("rpc_tcp_recved()\r\n") } @@ -6980,10 +4952,7 @@ func (r *RTL8720DN) Rpc_tcp_recved(pcb_in []byte, pcb_out *[]byte, length uint16 msg = append(msg, byte(length>>0)) msg = append(msg, byte(length>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -6991,30 +4960,19 @@ func (r *RTL8720DN) Rpc_tcp_recved(pcb_in []byte, pcb_out *[]byte, length uint16 pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_abort(pcb_in []byte, pcb_out *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_abort(pcb_in []byte, pcb_out []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_abort()\r\n") } @@ -7024,10 +4982,7 @@ func (r *RTL8720DN) Rpc_tcp_abort(pcb_in []byte, pcb_out *[]byte) (int32, error) msg = append(msg, byte(len(pcb_in)), byte(len(pcb_in)>>8), byte(len(pcb_in)>>16), byte(len(pcb_in)>>24)) msg = append(msg, []byte(pcb_in)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7035,30 +4990,19 @@ func (r *RTL8720DN) Rpc_tcp_abort(pcb_in []byte, pcb_out *[]byte) (int32, error) pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_write(pcb_in []byte, pcb_out *[]byte, data []byte, apiflags uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_write(pcb_in []byte, pcb_out []byte, data []byte, apiflags uint8) int32 { if r.debug { fmt.Printf("rpc_tcp_write()\r\n") } @@ -7073,10 +5017,7 @@ func (r *RTL8720DN) Rpc_tcp_write(pcb_in []byte, pcb_out *[]byte, data []byte, a // apiflags : in uint8 msg = append(msg, byte(apiflags>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7084,30 +5025,19 @@ func (r *RTL8720DN) Rpc_tcp_write(pcb_in []byte, pcb_out *[]byte, data []byte, a pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_output(pcb_in []byte, pcb_out *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_output(pcb_in []byte, pcb_out []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_output()\r\n") } @@ -7117,10 +5047,7 @@ func (r *RTL8720DN) Rpc_tcp_output(pcb_in []byte, pcb_out *[]byte) (int32, error msg = append(msg, byte(len(pcb_in)), byte(len(pcb_in)>>8), byte(len(pcb_in)>>16), byte(len(pcb_in)>>24)) msg = append(msg, []byte(pcb_in)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7128,30 +5055,19 @@ func (r *RTL8720DN) Rpc_tcp_output(pcb_in []byte, pcb_out *[]byte) (int32, error pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_close(pcb_in []byte, pcb_out *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_close(pcb_in []byte, pcb_out []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_close()\r\n") } @@ -7161,10 +5077,7 @@ func (r *RTL8720DN) Rpc_tcp_close(pcb_in []byte, pcb_out *[]byte) (int32, error) msg = append(msg, byte(len(pcb_in)), byte(len(pcb_in)>>8), byte(len(pcb_in)>>16), byte(len(pcb_in)>>24)) msg = append(msg, []byte(pcb_in)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7172,30 +5085,19 @@ func (r *RTL8720DN) Rpc_tcp_close(pcb_in []byte, pcb_out *[]byte) (int32, error) pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_bind(pcb_in []byte, pcb_out *[]byte, ipaddr []byte, port uint16) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_bind(pcb_in []byte, pcb_out []byte, ipaddr []byte, port uint16) int32 { if r.debug { fmt.Printf("rpc_tcp_bind()\r\n") } @@ -7211,10 +5113,7 @@ func (r *RTL8720DN) Rpc_tcp_bind(pcb_in []byte, pcb_out *[]byte, ipaddr []byte, msg = append(msg, byte(port>>0)) msg = append(msg, byte(port>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7222,30 +5121,19 @@ func (r *RTL8720DN) Rpc_tcp_bind(pcb_in []byte, pcb_out *[]byte, ipaddr []byte, pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_new_ip_type(ip_type uint8, pcb_out *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_new_ip_type(ip_type uint8, pcb_out []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_new_ip_type()\r\n") } @@ -7254,10 +5142,7 @@ func (r *RTL8720DN) Rpc_tcp_new_ip_type(ip_type uint8, pcb_out *[]byte) (int32, // ip_type : in uint8 msg = append(msg, byte(ip_type>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7265,30 +5150,19 @@ func (r *RTL8720DN) Rpc_tcp_new_ip_type(ip_type uint8, pcb_out *[]byte) (int32, pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_arg(pcb_in []byte, pcb_out *[]byte, func_arg []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_arg(pcb_in []byte, pcb_out []byte, func_arg []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_arg()\r\n") } @@ -7301,10 +5175,7 @@ func (r *RTL8720DN) Rpc_tcp_arg(pcb_in []byte, pcb_out *[]byte, func_arg []byte) msg = append(msg, byte(len(func_arg)), byte(len(func_arg)>>8), byte(len(func_arg)>>16), byte(len(func_arg)>>24)) msg = append(msg, []byte(func_arg)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7312,30 +5183,19 @@ func (r *RTL8720DN) Rpc_tcp_arg(pcb_in []byte, pcb_out *[]byte, func_arg []byte) pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_err(pcb_in []byte, pcb_out *[]byte, func_err []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_err(pcb_in []byte, pcb_out []byte, func_err []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_err()\r\n") } @@ -7348,10 +5208,7 @@ func (r *RTL8720DN) Rpc_tcp_err(pcb_in []byte, pcb_out *[]byte, func_err []byte) msg = append(msg, byte(len(func_err)), byte(len(func_err)>>8), byte(len(func_err)>>16), byte(len(func_err)>>24)) msg = append(msg, []byte(func_err)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7359,30 +5216,19 @@ func (r *RTL8720DN) Rpc_tcp_err(pcb_in []byte, pcb_out *[]byte, func_err []byte) pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_recv(pcb_in []byte, pcb_out *[]byte, func_recv []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_recv(pcb_in []byte, pcb_out []byte, func_recv []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_recv()\r\n") } @@ -7395,10 +5241,7 @@ func (r *RTL8720DN) Rpc_tcp_recv(pcb_in []byte, pcb_out *[]byte, func_recv []byt msg = append(msg, byte(len(func_recv)), byte(len(func_recv)>>8), byte(len(func_recv)>>16), byte(len(func_recv)>>24)) msg = append(msg, []byte(func_recv)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7406,30 +5249,19 @@ func (r *RTL8720DN) Rpc_tcp_recv(pcb_in []byte, pcb_out *[]byte, func_recv []byt pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_sent(pcb_in []byte, pcb_out *[]byte, func_sent []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_sent(pcb_in []byte, pcb_out []byte, func_sent []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_sent()\r\n") } @@ -7442,10 +5274,7 @@ func (r *RTL8720DN) Rpc_tcp_sent(pcb_in []byte, pcb_out *[]byte, func_sent []byt msg = append(msg, byte(len(func_sent)), byte(len(func_sent)>>8), byte(len(func_sent)>>16), byte(len(func_sent)>>24)) msg = append(msg, []byte(func_sent)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7453,30 +5282,19 @@ func (r *RTL8720DN) Rpc_tcp_sent(pcb_in []byte, pcb_out *[]byte, func_sent []byt pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_accept(pcb_in []byte, pcb_out *[]byte, func_accept []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_accept(pcb_in []byte, pcb_out []byte, func_accept []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_accept()\r\n") } @@ -7489,10 +5307,7 @@ func (r *RTL8720DN) Rpc_tcp_accept(pcb_in []byte, pcb_out *[]byte, func_accept [ msg = append(msg, byte(len(func_accept)), byte(len(func_accept)>>8), byte(len(func_accept)>>16), byte(len(func_accept)>>24)) msg = append(msg, []byte(func_accept)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7500,30 +5315,19 @@ func (r *RTL8720DN) Rpc_tcp_accept(pcb_in []byte, pcb_out *[]byte, func_accept [ pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_poll(pcb_in []byte, pcb_out *[]byte, func_poll []byte, interval uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_poll(pcb_in []byte, pcb_out []byte, func_poll []byte, interval uint8) int32 { if r.debug { fmt.Printf("rpc_tcp_poll()\r\n") } @@ -7538,10 +5342,7 @@ func (r *RTL8720DN) Rpc_tcp_poll(pcb_in []byte, pcb_out *[]byte, func_poll []byt // interval : in uint8 msg = append(msg, byte(interval>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7549,30 +5350,19 @@ func (r *RTL8720DN) Rpc_tcp_poll(pcb_in []byte, pcb_out *[]byte, func_poll []byt pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_listen_with_backlog(pcb_in []byte, pcb_out *[]byte, backlog uint8) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_listen_with_backlog(pcb_in []byte, pcb_out []byte, backlog uint8) int32 { if r.debug { fmt.Printf("rpc_tcp_listen_with_backlog()\r\n") } @@ -7584,10 +5374,7 @@ func (r *RTL8720DN) Rpc_tcp_listen_with_backlog(pcb_in []byte, pcb_out *[]byte, // backlog : in uint8 msg = append(msg, byte(backlog>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7595,30 +5382,19 @@ func (r *RTL8720DN) Rpc_tcp_listen_with_backlog(pcb_in []byte, pcb_out *[]byte, pcb_out_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if pcb_out_length > 0 { - copy(*pcb_out, payload[widx:widx+int(pcb_out_length)]) + copy(pcb_out, payload[widx:widx+int(pcb_out_length)]) widx += int(pcb_out_length) } - *pcb_out = (*pcb_out)[:pcb_out_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_pbuf_free(p []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_pbuf_free(p []byte) int32 { if r.debug { fmt.Printf("rpc_pbuf_free()\r\n") } @@ -7628,33 +5404,20 @@ func (r *RTL8720DN) Rpc_pbuf_free(p []byte) (int32, error) { msg = append(msg, byte(len(p)), byte(len(p)>>8), byte(len(p)>>16), byte(len(p)>>24)) msg = append(msg, []byte(p)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_ip4addr_ntoa(ip4_addr_in []byte) (string, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_ip4addr_ntoa(ip4_addr_in []byte) string { if r.debug { fmt.Printf("rpc_ip4addr_ntoa()\r\n") } @@ -7664,10 +5427,7 @@ func (r *RTL8720DN) Rpc_ip4addr_ntoa(ip4_addr_in []byte) (string, error) { msg = append(msg, byte(len(ip4_addr_in)), byte(len(ip4_addr_in)>>8), byte(len(ip4_addr_in)>>16), byte(len(ip4_addr_in)>>24)) msg = append(msg, []byte(ip4_addr_in)...) - err := r.performRequest(msg) - if err != nil { - return "", err - } + r.performRequest(msg) r.read() widx := 8 @@ -7678,15 +5438,10 @@ func (r *RTL8720DN) Rpc_ip4addr_ntoa(ip4_addr_in []byte) (string, error) { result = string(payload[widx : widx+int(result_length)]) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_inet_chksum(dataptr_in []byte) (uint16, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_inet_chksum(dataptr_in []byte) uint16 { if r.debug { fmt.Printf("rpc_inet_chksum()\r\n") } @@ -7696,10 +5451,7 @@ func (r *RTL8720DN) Rpc_inet_chksum(dataptr_in []byte) (uint16, error) { msg = append(msg, byte(len(dataptr_in)), byte(len(dataptr_in)>>8), byte(len(dataptr_in)>>16), byte(len(dataptr_in)>>24)) msg = append(msg, []byte(dataptr_in)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7708,15 +5460,10 @@ func (r *RTL8720DN) Rpc_inet_chksum(dataptr_in []byte) (uint16, error) { result = uint16(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_accept(s int32, addr []byte, addrlen *uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_accept(s int32, addr []byte, addrlen *uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_accept()\r\n") } @@ -7736,10 +5483,7 @@ func (r *RTL8720DN) Rpc_lwip_accept(s int32, addr []byte, addrlen *uint32) (int3 msg = append(msg, byte(*addrlen>>16)) msg = append(msg, byte(*addrlen>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7749,23 +5493,13 @@ func (r *RTL8720DN) Rpc_lwip_accept(s int32, addr []byte, addrlen *uint32) (int3 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_bind(s int32, name []byte, namelen uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_bind(s int32, name []byte, namelen uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_bind()\r\n") } @@ -7785,33 +5519,20 @@ func (r *RTL8720DN) Rpc_lwip_bind(s int32, name []byte, namelen uint32) (int32, msg = append(msg, byte(namelen>>16)) msg = append(msg, byte(namelen>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_shutdown(s int32, how int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_shutdown(s int32, how int32) int32 { if r.debug { fmt.Printf("rpc_lwip_shutdown()\r\n") } @@ -7828,33 +5549,20 @@ func (r *RTL8720DN) Rpc_lwip_shutdown(s int32, how int32) (int32, error) { msg = append(msg, byte(how>>16)) msg = append(msg, byte(how>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_getpeername(s int32, name *[]byte, namelen *uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_getpeername(s int32, name []byte, namelen *uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_getpeername()\r\n") } @@ -7871,10 +5579,7 @@ func (r *RTL8720DN) Rpc_lwip_getpeername(s int32, name *[]byte, namelen *uint32) msg = append(msg, byte(*namelen>>16)) msg = append(msg, byte(*namelen>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7882,33 +5587,22 @@ func (r *RTL8720DN) Rpc_lwip_getpeername(s int32, name *[]byte, namelen *uint32) name_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if name_length > 0 { - copy(*name, payload[widx:widx+int(name_length)]) + copy(name, payload[widx:widx+int(name_length)]) widx += int(name_length) } - *name = (*name)[:name_length] // namelen : inout uint32 *namelen = binary.LittleEndian.Uint32(payload[widx:]) widx += 4 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_getsockname(s int32, name *[]byte, namelen *uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_getsockname(s int32, name []byte, namelen *uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_getsockname()\r\n") } @@ -7925,10 +5619,7 @@ func (r *RTL8720DN) Rpc_lwip_getsockname(s int32, name *[]byte, namelen *uint32) msg = append(msg, byte(*namelen>>16)) msg = append(msg, byte(*namelen>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -7936,33 +5627,22 @@ func (r *RTL8720DN) Rpc_lwip_getsockname(s int32, name *[]byte, namelen *uint32) name_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if name_length > 0 { - copy(*name, payload[widx:widx+int(name_length)]) + copy(name, payload[widx:widx+int(name_length)]) widx += int(name_length) } - *name = (*name)[:name_length] // namelen : inout uint32 *namelen = binary.LittleEndian.Uint32(payload[widx:]) widx += 4 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_getsockopt(s int32, level int32, optname int32, in_optval []byte, out_optval *[]byte, optlen *uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_getsockopt(s int32, level int32, optname int32, in_optval []byte, out_optval []byte, optlen *uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_getsockopt()\r\n") } @@ -7992,10 +5672,7 @@ func (r *RTL8720DN) Rpc_lwip_getsockopt(s int32, level int32, optname int32, in_ msg = append(msg, byte(*optlen>>16)) msg = append(msg, byte(*optlen>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -8003,33 +5680,22 @@ func (r *RTL8720DN) Rpc_lwip_getsockopt(s int32, level int32, optname int32, in_ out_optval_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if out_optval_length > 0 { - copy(*out_optval, payload[widx:widx+int(out_optval_length)]) + copy(out_optval, payload[widx:widx+int(out_optval_length)]) widx += int(out_optval_length) } - *out_optval = (*out_optval)[:out_optval_length] // optlen : inout uint32 *optlen = binary.LittleEndian.Uint32(payload[widx:]) widx += 4 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_setsockopt(s int32, level int32, optname int32, optval []byte, optlen uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_setsockopt(s int32, level int32, optname int32, optval []byte, optlen uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_setsockopt()\r\n") } @@ -8059,33 +5725,20 @@ func (r *RTL8720DN) Rpc_lwip_setsockopt(s int32, level int32, optname int32, opt msg = append(msg, byte(optlen>>16)) msg = append(msg, byte(optlen>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_close(s int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_close(s int32) int32 { if r.debug { fmt.Printf("rpc_lwip_close()\r\n") } @@ -8097,33 +5750,20 @@ func (r *RTL8720DN) Rpc_lwip_close(s int32) (int32, error) { msg = append(msg, byte(s>>16)) msg = append(msg, byte(s>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_connect(s int32, name []byte, namelen uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_connect(s int32, name []byte, namelen uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_connect()\r\n") } @@ -8143,33 +5783,20 @@ func (r *RTL8720DN) Rpc_lwip_connect(s int32, name []byte, namelen uint32) (int3 msg = append(msg, byte(namelen>>16)) msg = append(msg, byte(namelen>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_listen(s int32, backlog int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_listen(s int32, backlog int32) int32 { if r.debug { fmt.Printf("rpc_lwip_listen()\r\n") } @@ -8186,33 +5813,20 @@ func (r *RTL8720DN) Rpc_lwip_listen(s int32, backlog int32) (int32, error) { msg = append(msg, byte(backlog>>16)) msg = append(msg, byte(backlog>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_available(s int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_available(s int32) int32 { if r.debug { fmt.Printf("rpc_lwip_available()\r\n") } @@ -8224,33 +5838,20 @@ func (r *RTL8720DN) Rpc_lwip_available(s int32) (int32, error) { msg = append(msg, byte(s>>16)) msg = append(msg, byte(s>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_recv(s int32, mem *[]byte, length uint32, flags int32, timeout uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_recv(s int32, mem []byte, length uint32, flags int32, timeout uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_recv()\r\n") } @@ -8277,10 +5878,7 @@ func (r *RTL8720DN) Rpc_lwip_recv(s int32, mem *[]byte, length uint32, flags int msg = append(msg, byte(timeout>>16)) msg = append(msg, byte(timeout>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -8288,30 +5886,19 @@ func (r *RTL8720DN) Rpc_lwip_recv(s int32, mem *[]byte, length uint32, flags int mem_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if mem_length > 0 { - copy(*mem, payload[widx:widx+int(mem_length)]) + copy(mem, payload[widx:widx+int(mem_length)]) widx += int(mem_length) } - *mem = (*mem)[:mem_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_read(s int32, mem *[]byte, length uint32, timeout uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_read(s int32, mem []byte, length uint32, timeout uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_read()\r\n") } @@ -8333,10 +5920,7 @@ func (r *RTL8720DN) Rpc_lwip_read(s int32, mem *[]byte, length uint32, timeout u msg = append(msg, byte(timeout>>16)) msg = append(msg, byte(timeout>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -8344,30 +5928,19 @@ func (r *RTL8720DN) Rpc_lwip_read(s int32, mem *[]byte, length uint32, timeout u mem_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if mem_length > 0 { - copy(*mem, payload[widx:widx+int(mem_length)]) + copy(mem, payload[widx:widx+int(mem_length)]) widx += int(mem_length) } - *mem = (*mem)[:mem_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_recvfrom(s int32, mem *[]byte, length uint32, flags int32, from *[]byte, fromlen *uint32, timeout uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_recvfrom(s int32, mem []byte, length uint32, flags int32, from []byte, fromlen *uint32, timeout uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_recvfrom()\r\n") } @@ -8399,10 +5972,7 @@ func (r *RTL8720DN) Rpc_lwip_recvfrom(s int32, mem *[]byte, length uint32, flags msg = append(msg, byte(timeout>>16)) msg = append(msg, byte(timeout>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -8410,41 +5980,29 @@ func (r *RTL8720DN) Rpc_lwip_recvfrom(s int32, mem *[]byte, length uint32, flags mem_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if mem_length > 0 { - copy(*mem, payload[widx:widx+int(mem_length)]) + copy(mem, payload[widx:widx+int(mem_length)]) widx += int(mem_length) } - *mem = (*mem)[:mem_length] // from : out []byte from_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if from_length > 0 { - copy(*from, payload[widx:widx+int(from_length)]) + copy(from, payload[widx:widx+int(from_length)]) widx += int(from_length) } - *from = (*from)[:from_length] // fromlen : inout uint32 *fromlen = binary.LittleEndian.Uint32(payload[widx:]) widx += 4 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_send(s int32, dataptr []byte, flags int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_send(s int32, dataptr []byte, flags int32) int32 { if r.debug { fmt.Printf("rpc_lwip_send()\r\n") } @@ -8464,33 +6022,20 @@ func (r *RTL8720DN) Rpc_lwip_send(s int32, dataptr []byte, flags int32) (int32, msg = append(msg, byte(flags>>16)) msg = append(msg, byte(flags>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_sendmsg(s int32, msg_name []byte, msg_iov []byte, msg_control []byte, msg_flags int32, flags int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_sendmsg(s int32, msg_name []byte, msg_iov []byte, msg_control []byte, msg_flags int32, flags int32) int32 { if r.debug { fmt.Printf("rpc_lwip_sendmsg()\r\n") } @@ -8521,33 +6066,20 @@ func (r *RTL8720DN) Rpc_lwip_sendmsg(s int32, msg_name []byte, msg_iov []byte, m msg = append(msg, byte(flags>>16)) msg = append(msg, byte(flags>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_sendto(s int32, dataptr []byte, flags int32, to []byte, tolen uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_sendto(s int32, dataptr []byte, flags int32, to []byte, tolen uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_sendto()\r\n") } @@ -8575,33 +6107,20 @@ func (r *RTL8720DN) Rpc_lwip_sendto(s int32, dataptr []byte, flags int32, to []b msg = append(msg, byte(tolen>>16)) msg = append(msg, byte(tolen>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_socket(domain int32, l_type int32, protocol int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_socket(domain int32, l_type int32, protocol int32) int32 { if r.debug { fmt.Printf("rpc_lwip_socket()\r\n") } @@ -8623,33 +6142,20 @@ func (r *RTL8720DN) Rpc_lwip_socket(domain int32, l_type int32, protocol int32) msg = append(msg, byte(protocol>>16)) msg = append(msg, byte(protocol>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_write(s int32, dataptr []byte, size uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_write(s int32, dataptr []byte, size uint32) int32 { if r.debug { fmt.Printf("rpc_lwip_write()\r\n") } @@ -8669,33 +6175,20 @@ func (r *RTL8720DN) Rpc_lwip_write(s int32, dataptr []byte, size uint32) (int32, msg = append(msg, byte(size>>16)) msg = append(msg, byte(size>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_writev(s int32, iov []byte, iovcnt int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_writev(s int32, iov []byte, iovcnt int32) int32 { if r.debug { fmt.Printf("rpc_lwip_writev()\r\n") } @@ -8715,33 +6208,20 @@ func (r *RTL8720DN) Rpc_lwip_writev(s int32, iov []byte, iovcnt int32) (int32, e msg = append(msg, byte(iovcnt>>16)) msg = append(msg, byte(iovcnt>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_select(maxfdp1 int32, readset []byte, writeset []byte, exceptset []byte, timeout []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_select(maxfdp1 int32, readset []byte, writeset []byte, exceptset []byte, timeout []byte) int32 { if r.debug { fmt.Printf("rpc_lwip_select()\r\n") } @@ -8785,33 +6265,20 @@ func (r *RTL8720DN) Rpc_lwip_select(maxfdp1 int32, readset []byte, writeset []by msg = append(msg, []byte(timeout)...) } - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_ioctl(s int32, cmd uint32, in_argp []byte, out_argp *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_ioctl(s int32, cmd uint32, in_argp []byte, out_argp []byte) int32 { if r.debug { fmt.Printf("rpc_lwip_ioctl()\r\n") } @@ -8831,10 +6298,7 @@ func (r *RTL8720DN) Rpc_lwip_ioctl(s int32, cmd uint32, in_argp []byte, out_argp msg = append(msg, byte(len(in_argp)), byte(len(in_argp)>>8), byte(len(in_argp)>>16), byte(len(in_argp)>>24)) msg = append(msg, []byte(in_argp)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -8842,30 +6306,19 @@ func (r *RTL8720DN) Rpc_lwip_ioctl(s int32, cmd uint32, in_argp []byte, out_argp out_argp_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if out_argp_length > 0 { - copy(*out_argp, payload[widx:widx+int(out_argp_length)]) + copy(out_argp, payload[widx:widx+int(out_argp_length)]) widx += int(out_argp_length) } - *out_argp = (*out_argp)[:out_argp_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_fcntl(s int32, cmd int32, val int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_fcntl(s int32, cmd int32, val int32) int32 { if r.debug { fmt.Printf("rpc_lwip_fcntl()\r\n") } @@ -8887,64 +6340,38 @@ func (r *RTL8720DN) Rpc_lwip_fcntl(s int32, cmd int32, val int32) (int32, error) msg = append(msg, byte(val>>16)) msg = append(msg, byte(val>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_lwip_errno() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_lwip_errno() int32 { if r.debug { fmt.Printf("rpc_lwip_errno()\r\n") } msg := startWriteMessage(0x00, 0x10, 0x18, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_netconn_gethostbyname(name string, addr *[]byte) (int8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_netconn_gethostbyname(name string, addr []byte) int8 { if r.debug { fmt.Printf("rpc_netconn_gethostbyname()\r\n") } @@ -8954,10 +6381,7 @@ func (r *RTL8720DN) Rpc_netconn_gethostbyname(name string, addr *[]byte) (int8, msg = append(msg, byte(len(name)), byte(len(name)>>8), byte(len(name)>>16), byte(len(name)>>24)) msg = append(msg, []byte(name)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -8965,30 +6389,19 @@ func (r *RTL8720DN) Rpc_netconn_gethostbyname(name string, addr *[]byte) (int8, addr_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if addr_length > 0 { - copy(*addr, payload[widx:widx+int(addr_length)]) + copy(addr, payload[widx:widx+int(addr_length)]) widx += int(addr_length) } - *addr = (*addr)[:addr_length] var result int8 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80 { - result = int8(int(x) * -1) - } else { - result = int8(int(x)) - } result = int8(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_dns_gethostbyname_addrtype(hostname string, addr *[]byte, found uint32, callback_arg []byte, dns_addrtype uint8) (int8, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_dns_gethostbyname_addrtype(hostname string, addr []byte, found uint32, callback_arg []byte, dns_addrtype uint8) int8 { if r.debug { fmt.Printf("rpc_dns_gethostbyname_addrtype()\r\n") } @@ -9013,10 +6426,7 @@ func (r *RTL8720DN) Rpc_dns_gethostbyname_addrtype(hostname string, addr *[]byte // dns_addrtype : in uint8 msg = append(msg, byte(dns_addrtype>>0)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9024,39 +6434,25 @@ func (r *RTL8720DN) Rpc_dns_gethostbyname_addrtype(hostname string, addr *[]byte addr_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if addr_length > 0 { - copy(*addr, payload[widx:widx+int(addr_length)]) + copy(addr, payload[widx:widx+int(addr_length)]) widx += int(addr_length) } - *addr = (*addr)[:addr_length] var result int8 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80 { - result = int8(int(x) * -1) - } else { - result = int8(int(x)) - } result = int8(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_client_create() (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_client_create() uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_client_create()\r\n") } msg := startWriteMessage(0x00, 0x11, 0x01, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9064,15 +6460,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_client_create() (uint32, error) { result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_client_destroy(ssl_client uint32) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_client_destroy(ssl_client uint32) { if r.debug { fmt.Printf("rpc_wifi_ssl_client_destroy()\r\n") } @@ -9084,23 +6475,15 @@ func (r *RTL8720DN) Rpc_wifi_ssl_client_destroy(ssl_client uint32) error { msg = append(msg, byte(ssl_client>>16)) msg = append(msg, byte(ssl_client>>24)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_wifi_ssl_init(ssl_client uint32) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_init(ssl_client uint32) { if r.debug { fmt.Printf("rpc_wifi_ssl_init()\r\n") } @@ -9112,23 +6495,15 @@ func (r *RTL8720DN) Rpc_wifi_ssl_init(ssl_client uint32) error { msg = append(msg, byte(ssl_client>>16)) msg = append(msg, byte(ssl_client>>24)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_wifi_ssl_set_socket(ssl_client uint32, socket int32) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_set_socket(ssl_client uint32, socket int32) { if r.debug { fmt.Printf("rpc_wifi_ssl_set_socket()\r\n") } @@ -9145,23 +6520,15 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_socket(ssl_client uint32, socket int32) err msg = append(msg, byte(socket>>16)) msg = append(msg, byte(socket>>24)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_wifi_ssl_set_timeout(ssl_client uint32, timeout uint32) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_set_timeout(ssl_client uint32, timeout uint32) { if r.debug { fmt.Printf("rpc_wifi_ssl_set_timeout()\r\n") } @@ -9178,23 +6545,15 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_timeout(ssl_client uint32, timeout uint32) msg = append(msg, byte(timeout>>16)) msg = append(msg, byte(timeout>>24)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_wifi_ssl_get_socket(ssl_client uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_get_socket(ssl_client uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_ssl_get_socket()\r\n") } @@ -9206,33 +6565,20 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_socket(ssl_client uint32) (int32, error) { msg = append(msg, byte(ssl_client>>16)) msg = append(msg, byte(ssl_client>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_get_timeout(ssl_client uint32) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_get_timeout(ssl_client uint32) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_get_timeout()\r\n") } @@ -9244,10 +6590,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_timeout(ssl_client uint32) (uint32, error) msg = append(msg, byte(ssl_client>>16)) msg = append(msg, byte(ssl_client>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9256,15 +6599,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_timeout(ssl_client uint32) (uint32, error) result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_set_rootCA(ssl_client uint32, rootCABuff string) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_set_rootCA(ssl_client uint32, rootCABuff string) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_set_rootCA()\r\n") } @@ -9279,10 +6617,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_rootCA(ssl_client uint32, rootCABuff string msg = append(msg, byte(len(rootCABuff)), byte(len(rootCABuff)>>8), byte(len(rootCABuff)>>16), byte(len(rootCABuff)>>24)) msg = append(msg, []byte(rootCABuff)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9291,15 +6626,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_rootCA(ssl_client uint32, rootCABuff string result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_get_rootCA(ssl_client uint32, rootCABuff string) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_get_rootCA(ssl_client uint32, rootCABuff string) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_get_rootCA()\r\n") } @@ -9311,10 +6641,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_rootCA(ssl_client uint32, rootCABuff string msg = append(msg, byte(ssl_client>>16)) msg = append(msg, byte(ssl_client>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9329,21 +6656,15 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_rootCA(ssl_client uint32, rootCABuff string rootCABuff = string(payload[widx : widx+int(rootCABuff_length)]) widx += int(rootCABuff_length) } - rootCABuff = (rootCABuff)[:rootCABuff_length] var result uint32 result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_set_cliCert(ssl_client uint32, cli_cert string) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_set_cliCert(ssl_client uint32, cli_cert string) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_set_cliCert()\r\n") } @@ -9358,10 +6679,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_cliCert(ssl_client uint32, cli_cert string) msg = append(msg, byte(len(cli_cert)), byte(len(cli_cert)>>8), byte(len(cli_cert)>>16), byte(len(cli_cert)>>24)) msg = append(msg, []byte(cli_cert)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9370,15 +6688,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_cliCert(ssl_client uint32, cli_cert string) result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_get_cliCert(ssl_client uint32, cli_cert string) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_get_cliCert(ssl_client uint32, cli_cert string) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_get_cliCert()\r\n") } @@ -9398,10 +6711,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_cliCert(ssl_client uint32, cli_cert string) msg = append(msg, []byte(cli_cert)...) } - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9410,15 +6720,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_cliCert(ssl_client uint32, cli_cert string) result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_set_cliKey(ssl_client uint32, cli_key string) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_set_cliKey(ssl_client uint32, cli_key string) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_set_cliKey()\r\n") } @@ -9433,10 +6738,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_cliKey(ssl_client uint32, cli_key string) ( msg = append(msg, byte(len(cli_key)), byte(len(cli_key)>>8), byte(len(cli_key)>>16), byte(len(cli_key)>>24)) msg = append(msg, []byte(cli_key)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9445,15 +6747,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_cliKey(ssl_client uint32, cli_key string) ( result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_get_cliKey(ssl_client uint32, cli_key string) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_get_cliKey(ssl_client uint32, cli_key string) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_get_cliKey()\r\n") } @@ -9473,10 +6770,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_cliKey(ssl_client uint32, cli_key string) ( msg = append(msg, []byte(cli_key)...) } - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9485,15 +6779,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_cliKey(ssl_client uint32, cli_key string) ( result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_set_pskIdent(ssl_client uint32, pskIdent string) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_set_pskIdent(ssl_client uint32, pskIdent string) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_set_pskIdent()\r\n") } @@ -9508,10 +6797,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_pskIdent(ssl_client uint32, pskIdent string msg = append(msg, byte(len(pskIdent)), byte(len(pskIdent)>>8), byte(len(pskIdent)>>16), byte(len(pskIdent)>>24)) msg = append(msg, []byte(pskIdent)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9520,15 +6806,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_pskIdent(ssl_client uint32, pskIdent string result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_get_pskIdent(ssl_client uint32, pskIdent string) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_get_pskIdent(ssl_client uint32, pskIdent string) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_get_pskIdent()\r\n") } @@ -9548,10 +6829,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_pskIdent(ssl_client uint32, pskIdent string msg = append(msg, []byte(pskIdent)...) } - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9560,15 +6838,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_pskIdent(ssl_client uint32, pskIdent string result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_set_psKey(ssl_client uint32, psKey string) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_set_psKey(ssl_client uint32, psKey string) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_set_psKey()\r\n") } @@ -9583,10 +6856,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_psKey(ssl_client uint32, psKey string) (uin msg = append(msg, byte(len(psKey)), byte(len(psKey)>>8), byte(len(psKey)>>16), byte(len(psKey)>>24)) msg = append(msg, []byte(psKey)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9595,15 +6865,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_set_psKey(ssl_client uint32, psKey string) (uin result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_get_psKey(ssl_client uint32, psKey string) (uint32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_get_psKey(ssl_client uint32, psKey string) uint32 { if r.debug { fmt.Printf("rpc_wifi_ssl_get_psKey()\r\n") } @@ -9623,10 +6888,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_psKey(ssl_client uint32, psKey string) (uin msg = append(msg, []byte(psKey)...) } - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9635,15 +6897,10 @@ func (r *RTL8720DN) Rpc_wifi_ssl_get_psKey(ssl_client uint32, psKey string) (uin result = uint32(binary.LittleEndian.Uint32(payload[widx:])) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_start_ssl_client(ssl_client uint32, host string, port uint32, timeout int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_start_ssl_client(ssl_client uint32, host string, port uint32, timeout int32) int32 { if r.debug { fmt.Printf("rpc_wifi_start_ssl_client()\r\n") } @@ -9673,33 +6930,20 @@ func (r *RTL8720DN) Rpc_wifi_start_ssl_client(ssl_client uint32, host string, po msg = append(msg, byte(timeout>>16)) msg = append(msg, byte(timeout>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_stop_ssl_socket(ssl_client uint32) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_stop_ssl_socket(ssl_client uint32) { if r.debug { fmt.Printf("rpc_wifi_stop_ssl_socket()\r\n") } @@ -9711,23 +6955,15 @@ func (r *RTL8720DN) Rpc_wifi_stop_ssl_socket(ssl_client uint32) error { msg = append(msg, byte(ssl_client>>16)) msg = append(msg, byte(ssl_client>>24)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_wifi_data_to_read(ssl_client uint32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_data_to_read(ssl_client uint32) int32 { if r.debug { fmt.Printf("rpc_wifi_data_to_read()\r\n") } @@ -9739,33 +6975,20 @@ func (r *RTL8720DN) Rpc_wifi_data_to_read(ssl_client uint32) (int32, error) { msg = append(msg, byte(ssl_client>>16)) msg = append(msg, byte(ssl_client>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_send_ssl_data(ssl_client uint32, data []byte, length uint16) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_send_ssl_data(ssl_client uint32, data []byte, length uint16) int32 { if r.debug { fmt.Printf("rpc_wifi_send_ssl_data()\r\n") } @@ -9783,33 +7006,20 @@ func (r *RTL8720DN) Rpc_wifi_send_ssl_data(ssl_client uint32, data []byte, lengt msg = append(msg, byte(length>>0)) msg = append(msg, byte(length>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_get_ssl_receive(ssl_client uint32, data *[]byte, length int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_get_ssl_receive(ssl_client uint32, data []byte, length int32) int32 { if r.debug { fmt.Printf("rpc_wifi_get_ssl_receive()\r\n") } @@ -9826,10 +7036,7 @@ func (r *RTL8720DN) Rpc_wifi_get_ssl_receive(ssl_client uint32, data *[]byte, le msg = append(msg, byte(length>>16)) msg = append(msg, byte(length>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9837,30 +7044,19 @@ func (r *RTL8720DN) Rpc_wifi_get_ssl_receive(ssl_client uint32, data *[]byte, le data_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if data_length > 0 { - copy(*data, payload[widx:widx+int(data_length)]) + copy(data, payload[widx:widx+int(data_length)]) widx += int(data_length) } - *data = (*data)[:data_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_verify_ssl_fingerprint(ssl_client uint32, fp string, domain_name string) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_verify_ssl_fingerprint(ssl_client uint32, fp string, domain_name string) bool { if r.debug { fmt.Printf("rpc_wifi_verify_ssl_fingerprint()\r\n") } @@ -9878,10 +7074,7 @@ func (r *RTL8720DN) Rpc_wifi_verify_ssl_fingerprint(ssl_client uint32, fp string msg = append(msg, byte(len(domain_name)), byte(len(domain_name)>>8), byte(len(domain_name)>>16), byte(len(domain_name)>>24)) msg = append(msg, []byte(domain_name)...) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9890,15 +7083,10 @@ func (r *RTL8720DN) Rpc_wifi_verify_ssl_fingerprint(ssl_client uint32, fp string result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_verify_ssl_dn(ssl_client uint32, domain_name string) (bool, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_verify_ssl_dn(ssl_client uint32, domain_name string) bool { if r.debug { fmt.Printf("rpc_wifi_verify_ssl_dn()\r\n") } @@ -9913,10 +7101,7 @@ func (r *RTL8720DN) Rpc_wifi_verify_ssl_dn(ssl_client uint32, domain_name string msg = append(msg, byte(len(domain_name)), byte(len(domain_name)>>8), byte(len(domain_name)>>16), byte(len(domain_name)>>24)) msg = append(msg, []byte(domain_name)...) - err := r.performRequest(msg) - if err != nil { - return false, err - } + r.performRequest(msg) r.read() widx := 8 @@ -9925,15 +7110,10 @@ func (r *RTL8720DN) Rpc_wifi_verify_ssl_dn(ssl_client uint32, domain_name string result = binary.LittleEndian.Uint32(payload[widx:]) == 1 r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_ssl_strerror(errnum int32, buffer *[]byte, buflen uint32) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_ssl_strerror(errnum int32, buffer []byte, buflen uint32) { if r.debug { fmt.Printf("rpc_wifi_ssl_strerror()\r\n") } @@ -9950,10 +7130,7 @@ func (r *RTL8720DN) Rpc_wifi_ssl_strerror(errnum int32, buffer *[]byte, buflen u msg = append(msg, byte(buflen>>16)) msg = append(msg, byte(buflen>>24)) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() widx := 8 @@ -9961,83 +7138,51 @@ func (r *RTL8720DN) Rpc_wifi_ssl_strerror(errnum int32, buffer *[]byte, buflen u buffer_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if buffer_length > 0 { - copy(*buffer, payload[widx:widx+int(buffer_length)]) + copy(buffer, payload[widx:widx+int(buffer_length)]) widx += int(buffer_length) } - *buffer = (*buffer)[:buffer_length] r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_mdns_init() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_init() int32 { if r.debug { fmt.Printf("rpc_mdns_init()\r\n") } msg := startWriteMessage(0x00, 0x12, 0x01, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_free() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_free() int32 { if r.debug { fmt.Printf("rpc_mdns_free()\r\n") } msg := startWriteMessage(0x00, 0x12, 0x02, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_service_add(instance_name string, service_type string, proto string, port uint16) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_service_add(instance_name string, service_type string, proto string, port uint16) int32 { if r.debug { fmt.Printf("rpc_mdns_service_add()\r\n") } @@ -10056,33 +7201,20 @@ func (r *RTL8720DN) Rpc_mdns_service_add(instance_name string, service_type stri msg = append(msg, byte(port>>0)) msg = append(msg, byte(port>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_service_remove(service_type string, proto string) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_service_remove(service_type string, proto string) int32 { if r.debug { fmt.Printf("rpc_mdns_service_remove()\r\n") } @@ -10095,33 +7227,20 @@ func (r *RTL8720DN) Rpc_mdns_service_remove(service_type string, proto string) ( msg = append(msg, byte(len(proto)), byte(len(proto)>>8), byte(len(proto)>>16), byte(len(proto)>>24)) msg = append(msg, []byte(proto)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_service_txt_item_set(service_type string, proto string, key string, value string) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_service_txt_item_set(service_type string, proto string, key string, value string) int32 { if r.debug { fmt.Printf("rpc_mdns_service_txt_item_set()\r\n") } @@ -10140,33 +7259,20 @@ func (r *RTL8720DN) Rpc_mdns_service_txt_item_set(service_type string, proto str msg = append(msg, byte(len(value)), byte(len(value)>>8), byte(len(value)>>16), byte(len(value)>>24)) msg = append(msg, []byte(value)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_service_instance_name_set(service string, proto string, instance string) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_service_instance_name_set(service string, proto string, instance string) int32 { if r.debug { fmt.Printf("rpc_mdns_service_instance_name_set()\r\n") } @@ -10182,33 +7288,20 @@ func (r *RTL8720DN) Rpc_mdns_service_instance_name_set(service string, proto str msg = append(msg, byte(len(instance)), byte(len(instance)>>8), byte(len(instance)>>16), byte(len(instance)>>24)) msg = append(msg, []byte(instance)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_instance_name_set(instance_name string) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_instance_name_set(instance_name string) int32 { if r.debug { fmt.Printf("rpc_mdns_instance_name_set()\r\n") } @@ -10218,33 +7311,20 @@ func (r *RTL8720DN) Rpc_mdns_instance_name_set(instance_name string) (int32, err msg = append(msg, byte(len(instance_name)), byte(len(instance_name)>>8), byte(len(instance_name)>>16), byte(len(instance_name)>>24)) msg = append(msg, []byte(instance_name)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_hostname_set(hostname string) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_hostname_set(hostname string) int32 { if r.debug { fmt.Printf("rpc_mdns_hostname_set()\r\n") } @@ -10254,33 +7334,20 @@ func (r *RTL8720DN) Rpc_mdns_hostname_set(hostname string) (int32, error) { msg = append(msg, byte(len(hostname)), byte(len(hostname)>>8), byte(len(hostname)>>16), byte(len(hostname)>>24)) msg = append(msg, []byte(hostname)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_query_a(host_name string, timeout uint32, addr *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_query_a(host_name string, timeout uint32, addr []byte) int32 { if r.debug { fmt.Printf("rpc_mdns_query_a()\r\n") } @@ -10295,10 +7362,7 @@ func (r *RTL8720DN) Rpc_mdns_query_a(host_name string, timeout uint32, addr *[]b msg = append(msg, byte(timeout>>16)) msg = append(msg, byte(timeout>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -10306,30 +7370,19 @@ func (r *RTL8720DN) Rpc_mdns_query_a(host_name string, timeout uint32, addr *[]b addr_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if addr_length > 0 { - copy(*addr, payload[widx:widx+int(addr_length)]) + copy(addr, payload[widx:widx+int(addr_length)]) widx += int(addr_length) } - *addr = (*addr)[:addr_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_query_ptr(service_type string, proto string, timeout uint32, max_results int32, result_total *int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_query_ptr(service_type string, proto string, timeout uint32, max_results int32, result_total *int32) int32 { if r.debug { fmt.Printf("rpc_mdns_query_ptr()\r\n") } @@ -10352,10 +7405,7 @@ func (r *RTL8720DN) Rpc_mdns_query_ptr(service_type string, proto string, timeou msg = append(msg, byte(max_results>>16)) msg = append(msg, byte(max_results>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -10365,23 +7415,13 @@ func (r *RTL8720DN) Rpc_mdns_query_ptr(service_type string, proto string, timeou var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_query_ptr_result_basic(result_target int32, scan_result *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_query_ptr_result_basic(result_target int32, scan_result []byte) int32 { if r.debug { fmt.Printf("rpc_mdns_query_ptr_result_basic()\r\n") } @@ -10393,10 +7433,7 @@ func (r *RTL8720DN) Rpc_mdns_query_ptr_result_basic(result_target int32, scan_re msg = append(msg, byte(result_target>>16)) msg = append(msg, byte(result_target>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -10404,30 +7441,19 @@ func (r *RTL8720DN) Rpc_mdns_query_ptr_result_basic(result_target int32, scan_re scan_result_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if scan_result_length > 0 { - copy(*scan_result, payload[widx:widx+int(scan_result_length)]) + copy(scan_result, payload[widx:widx+int(scan_result_length)]) widx += int(scan_result_length) } - *scan_result = (*scan_result)[:scan_result_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_query_ptr_result_txt(result_target int32, txt_target int32, txt *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_query_ptr_result_txt(result_target int32, txt_target int32, txt []byte) int32 { if r.debug { fmt.Printf("rpc_mdns_query_ptr_result_txt()\r\n") } @@ -10444,10 +7470,7 @@ func (r *RTL8720DN) Rpc_mdns_query_ptr_result_txt(result_target int32, txt_targe msg = append(msg, byte(txt_target>>16)) msg = append(msg, byte(txt_target>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -10455,30 +7478,19 @@ func (r *RTL8720DN) Rpc_mdns_query_ptr_result_txt(result_target int32, txt_targe txt_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if txt_length > 0 { - copy(*txt, payload[widx:widx+int(txt_length)]) + copy(txt, payload[widx:widx+int(txt_length)]) widx += int(txt_length) } - *txt = (*txt)[:txt_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_query_ptr_result_addr(result_target int32, addr_target int32, addr *[]byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_query_ptr_result_addr(result_target int32, addr_target int32, addr []byte) int32 { if r.debug { fmt.Printf("rpc_mdns_query_ptr_result_addr()\r\n") } @@ -10495,10 +7507,7 @@ func (r *RTL8720DN) Rpc_mdns_query_ptr_result_addr(result_target int32, addr_tar msg = append(msg, byte(addr_target>>16)) msg = append(msg, byte(addr_target>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 @@ -10506,61 +7515,37 @@ func (r *RTL8720DN) Rpc_mdns_query_ptr_result_addr(result_target int32, addr_tar addr_length := binary.LittleEndian.Uint32(payload[widx:]) widx += 4 if addr_length > 0 { - copy(*addr, payload[widx:widx+int(addr_length)]) + copy(addr, payload[widx:widx+int(addr_length)]) widx += int(addr_length) } - *addr = (*addr)[:addr_length] var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_mdns_query_results_free() (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_mdns_query_results_free() int32 { if r.debug { fmt.Printf("rpc_mdns_query_results_free()\r\n") } msg := startWriteMessage(0x00, 0x12, 0x0E, uint32(r.seq)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_wifi_event_callback(event []byte) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_event_callback(event []byte) { if r.debug { fmt.Printf("rpc_wifi_event_callback()\r\n") } @@ -10570,23 +7555,15 @@ func (r *RTL8720DN) Rpc_wifi_event_callback(event []byte) error { msg = append(msg, byte(len(event)), byte(len(event)>>8), byte(len(event)>>16), byte(len(event)>>24)) msg = append(msg, []byte(event)...) - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_wifi_dns_found(hostname string, ipaddr []byte, arg []byte) error { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_wifi_dns_found(hostname string, ipaddr []byte, arg []byte) { if r.debug { fmt.Printf("rpc_wifi_dns_found()\r\n") } @@ -10607,23 +7584,15 @@ func (r *RTL8720DN) Rpc_wifi_dns_found(hostname string, ipaddr []byte, arg []byt msg = append(msg, []byte(arg)...) } - err := r.performRequest(msg) - if err != nil { - return err - } + r.performRequest(msg) r.read() r.seq++ - return err + return } -func (r *RTL8720DN) Rpc_tcpip_api_call_fn(fn uint32, call []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcpip_api_call_fn(fn uint32, call []byte) int32 { if r.debug { fmt.Printf("rpc_tcpip_api_call_fn()\r\n") } @@ -10638,33 +7607,20 @@ func (r *RTL8720DN) Rpc_tcpip_api_call_fn(fn uint32, call []byte) (int32, error) msg = append(msg, byte(len(call)), byte(len(call)>>8), byte(len(call)>>16), byte(len(call)>>24)) msg = append(msg, []byte(call)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_connected_fn(fn uint32, arg []byte, tpcb []byte, err_val int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_connected_fn(fn uint32, arg []byte, tpcb []byte, err_val int32) int32 { if r.debug { fmt.Printf("rpc_tcp_connected_fn()\r\n") } @@ -10687,33 +7643,20 @@ func (r *RTL8720DN) Rpc_tcp_connected_fn(fn uint32, arg []byte, tpcb []byte, err msg = append(msg, byte(err_val>>16)) msg = append(msg, byte(err_val>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_recv_fn(fn uint32, arg []byte, tpcb []byte, p_data []byte, p_addr []byte, err_val int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_recv_fn(fn uint32, arg []byte, tpcb []byte, p_data []byte, p_addr []byte, err_val int32) int32 { if r.debug { fmt.Printf("rpc_tcp_recv_fn()\r\n") } @@ -10742,33 +7685,20 @@ func (r *RTL8720DN) Rpc_tcp_recv_fn(fn uint32, arg []byte, tpcb []byte, p_data [ msg = append(msg, byte(err_val>>16)) msg = append(msg, byte(err_val>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_accept_fn(fn uint32, arg []byte, newpcb []byte, err_val int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_accept_fn(fn uint32, arg []byte, newpcb []byte, err_val int32) int32 { if r.debug { fmt.Printf("rpc_tcp_accept_fn()\r\n") } @@ -10791,33 +7721,20 @@ func (r *RTL8720DN) Rpc_tcp_accept_fn(fn uint32, arg []byte, newpcb []byte, err_ msg = append(msg, byte(err_val>>16)) msg = append(msg, byte(err_val>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_err_fn(fn uint32, arg []byte, err_val int32) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_err_fn(fn uint32, arg []byte, err_val int32) int32 { if r.debug { fmt.Printf("rpc_tcp_err_fn()\r\n") } @@ -10837,33 +7754,20 @@ func (r *RTL8720DN) Rpc_tcp_err_fn(fn uint32, arg []byte, err_val int32) (int32, msg = append(msg, byte(err_val>>16)) msg = append(msg, byte(err_val>>24)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_sent_fn(fn uint32, arg []byte, tpcb []byte, length uint16) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_sent_fn(fn uint32, arg []byte, tpcb []byte, length uint16) int32 { if r.debug { fmt.Printf("rpc_tcp_sent_fn()\r\n") } @@ -10884,33 +7788,20 @@ func (r *RTL8720DN) Rpc_tcp_sent_fn(fn uint32, arg []byte, tpcb []byte, length u msg = append(msg, byte(length>>0)) msg = append(msg, byte(length>>8)) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } -func (r *RTL8720DN) Rpc_tcp_poll_fn(fn uint32, arg []byte, tpcb []byte) (int32, error) { - r.sema <- true - defer func() { - <-r.sema - }() - +func (r *rtl8720dn) rpc_tcp_poll_fn(fn uint32, arg []byte, tpcb []byte) int32 { if r.debug { fmt.Printf("rpc_tcp_poll_fn()\r\n") } @@ -10928,23 +7819,15 @@ func (r *RTL8720DN) Rpc_tcp_poll_fn(fn uint32, arg []byte, tpcb []byte) (int32, msg = append(msg, byte(len(tpcb)), byte(len(tpcb)>>8), byte(len(tpcb)>>16), byte(len(tpcb)>>24)) msg = append(msg, []byte(tpcb)...) - err := r.performRequest(msg) - if err != nil { - return 0, err - } + r.performRequest(msg) r.read() widx := 8 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) - if x >= 0x80000000 { - result = int32(int(x) * -1) - } else { - result = int32(int(x)) - } result = int32(x) r.seq++ - return result, err + return result } diff --git a/rtl8720dn/rpc_util.go b/rtl8720dn/rpc_util.go index ee894224a..68efebbe0 100644 --- a/rtl8720dn/rpc_util.go +++ b/rtl8720dn/rpc_util.go @@ -9,7 +9,7 @@ var ( headerBuf [4]byte readBuf [4]byte startWriteMessageBuf [1024]byte - payload [1024 + 256]byte + payload [2048]byte ) const ( @@ -30,7 +30,7 @@ func startWriteMessage(msgType, service, requestNumber, sequence uint32) []byte return startWriteMessageBuf[:8] } -func (r *RTL8720DN) performRequest(msg []byte) error { +func (r *rtl8720dn) performRequest(msg []byte) { crc := computeCRC16(msg) headerBuf[0] = byte(len(msg)) headerBuf[1] = byte(len(msg) >> 8) @@ -43,16 +43,14 @@ func (r *RTL8720DN) performRequest(msg []byte) error { fmt.Printf("\r\n") } - r.port.Write(headerBuf[:]) + r.uart.Write(headerBuf[:]) if r.debug { fmt.Printf("tx : %2d : ", len(msg)) dumpHex(msg) fmt.Printf("\r\n") } - r.port.Write(msg) - - return nil + r.uart.Write(msg) } func dumpHex(b []byte) { @@ -65,9 +63,9 @@ func dumpHex(b []byte) { } } -func (r *RTL8720DN) read() { +func (r *rtl8720dn) read() { for { - n, _ := io.ReadFull(r.port, readBuf[:4]) + n, _ := io.ReadFull(r.uart, readBuf[:4]) if n == 0 { continue } @@ -81,7 +79,7 @@ func (r *RTL8720DN) read() { length := uint16(readBuf[0]) + uint16(readBuf[1])<<8 crc := uint16(readBuf[2]) + uint16(readBuf[3])<<8 - n, _ = io.ReadFull(r.port, payload[:length]) + n, _ = io.ReadFull(r.uart, payload[:length]) if r.debug { fmt.Printf("rx : %2d : ", length) dumpHex(payload[0:n]) diff --git a/rtl8720dn/rtl8720dn.go b/rtl8720dn/rtl8720dn.go index 8a21bde97..35bfff496 100644 --- a/rtl8720dn/rtl8720dn.go +++ b/rtl8720dn/rtl8720dn.go @@ -1,69 +1,783 @@ -package rtl8720dn +// Package rtl8720dn implements TCP wireless communication over UART +// talking to a RealTek rtl8720dn module. +// +// 01/2023 sfeldma@gmail.com Heavily modified to use netdev interface -import ( - "machine" +package rtl8720dn // import "tinygo.org/x/drivers/rtl8720dn" +import ( + "encoding/hex" + "errors" + "fmt" "io" + "machine" + "strings" + "sync" "time" + + "tinygo.org/x/drivers/netdev" ) -const maxUartRecvSize = 128 +var _debug debug = debugBasic + +//var _debug debug = debugBasic | debugNetdev +//var _debug debug = debugBasic | debugNetdev | debugRpc + +var ( + version = "0.0.1" + driverName = "Realtek rtl8720dn Wifi network device driver (rtl8720dn)" +) + +const ( + F_SETFL = 4 + O_NONBLOCK = 1 + RTW_MODE_STA = 0x00000001 +) + +type macAddress netdev.HardwareAddr +type sock int32 + +type socket struct { + protocol netdev.Protocol + inuse bool +} + +type Config struct { + // AP creditials + Ssid string + Passphrase string + + // Enable + En machine.Pin + + // UART config + Uart *machine.UART + Tx machine.Pin + Rx machine.Pin + Baudrate uint32 + + // Retries is how many attempts to connect before returning with a + // "Connect failed" error. Zero means infinite retries. + Retries int + + // Watchdog ticker duration. On tick, the watchdog will check for + // downed connection and try to recover the connection. Default is + // 0secs, which means no watchdog. Set to non-zero to enable + // watchodog. + WatchdogTimeo time.Duration +} + +type rtl8720dn struct { + cfg *Config + notifyCb func(netdev.Event) + mu sync.Mutex + + uart *machine.UART + seq uint64 -type RTL8720DN struct { - port io.ReadWriter - seq uint64 - sema chan bool debug bool - connectionType ConnectionType - socket int32 - client uint32 - length int - root_ca *string - udpInfo [6]byte // Port: [2]byte + IP: [4]byte + netConnected bool + driverShown bool + deviceShown bool + + killWatchdog chan bool + + // keyed by sock as returned by rpc_lwip_socket() + sockets map[sock]*socket } -type ConnectionType int +func newSocket(protocol netdev.Protocol) *socket { + return &socket{protocol: protocol, inuse: true} +} -const ( - ConnectionTypeNone ConnectionType = iota - ConnectionTypeTCP - ConnectionTypeUDP - ConnectionTypeTLS -) +func New(cfg *Config) *rtl8720dn { + return &rtl8720dn{ + debug: (_debug & debugRpc) != 0, + cfg: cfg, + sockets: make(map[sock]*socket), + killWatchdog: make(chan bool), + } +} + +func (r *rtl8720dn) startDhcpc() error { + if result := r.rpc_tcpip_adapter_dhcpc_start(0); result == -1 { + return netdev.ErrStartingDHCPClient + } + return nil +} + +func (r *rtl8720dn) connectToAP() error { + + if len(r.cfg.Ssid) == 0 { + return netdev.ErrMissingSSID + } + + if debugging(debugBasic) { + fmt.Printf("Connecting to Wifi SSID '%s'...", r.cfg.Ssid) + } -func (d *Driver) SetSeq(s uint64) { - d.seq = s + // Start the connection process + securityType := uint32(0x00400004) + result := r.rpc_wifi_connect(r.cfg.Ssid, r.cfg.Passphrase, securityType, -1, 0) + if result == -1 { + if debugging(debugBasic) { + fmt.Printf("FAILED\r\n") + } + return netdev.ErrConnectFailed + } + + if debugging(debugBasic) { + fmt.Printf("CONNECTED\r\n") + } + + if r.notifyCb != nil { + r.notifyCb(netdev.EventNetUp) + } + + return r.startDhcpc() } -func (d *Driver) Debug(b bool) { - d.debug = b +func (r *rtl8720dn) showDriver() { + if r.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) + } + r.driverShown = true } -func (d *Driver) SetRootCA(s *string) { - d.root_ca = s +func (r *rtl8720dn) initWifi() error { + if result := r.rpc_tcpip_adapter_init(); result == -1 { + return fmt.Errorf("TCP/IP adapter init failed") + } + if result := r.rpc_wifi_off(); result == -1 { + return errors.New("Error turning off WiFi") + } + if result := r.rpc_wifi_on(RTW_MODE_STA); result == -1 { + return errors.New("Error turning on WiFi") + } + if result := r.rpc_wifi_disconnect(); result == -1 { + return errors.New("Error disconnecting WiFi") + } + return nil } -func (d *Driver) Version() (string, error) { - return d.Rpc_system_version() +func (r *rtl8720dn) setupUART() { + r.uart = r.cfg.Uart + r.uart.Configure(machine.UARTConfig{TX: r.cfg.Tx, + RX: r.cfg.Rx, BaudRate: r.cfg.Baudrate}) } -func enable(en machine.Pin) { +func (r *rtl8720dn) start() error { + en := r.cfg.En + if en == 0 { + return fmt.Errorf("Must set Config.En") + } en.Configure(machine.PinConfig{Mode: machine.PinOutput}) en.Low() time.Sleep(100 * time.Millisecond) en.High() time.Sleep(1000 * time.Millisecond) + r.setupUART() + return r.initWifi() +} + +func (r *rtl8720dn) stop() { + r.rpc_tcpip_adapter_stop(0) + r.cfg.En.Low() +} + +func (r *rtl8720dn) showDevice() { + if r.deviceShown { + return + } + if debugging(debugBasic) { + fmt.Printf("RTL8720 firmware version : %s\r\n", r.getFwVersion()) + fmt.Printf("MAC address : %s\r\n", r.getMACAddr()) + fmt.Printf("\r\n") + } + r.deviceShown = true +} + +func (r *rtl8720dn) showIP() { + if debugging(debugBasic) { + ip, subnet, gateway, _ := r.getIP() + fmt.Printf("\r\n") + fmt.Printf("DHCP-assigned IP : %s\r\n", netdev.IP(ip).String()) + fmt.Printf("DHCP-assigned subnet : %s\r\n", netdev.IP(subnet).String()) + fmt.Printf("DHCP-assigned gateway : %s\r\n", netdev.IP(gateway).String()) + fmt.Printf("\r\n") + } +} + +func (r *rtl8720dn) networkDown() bool { + result := r.rpc_wifi_is_connected_to_ap() + return result != 0 +} + +func (r *rtl8720dn) watchdog() { + ticker := time.NewTicker(r.cfg.WatchdogTimeo) + for { + select { + case <-r.killWatchdog: + return + case <-ticker.C: + r.mu.Lock() + if r.networkDown() { + if debugging(debugBasic) { + fmt.Printf("Watchdog: Wifi NOT CONNECTED, trying again...\r\n") + } + if r.notifyCb != nil { + r.notifyCb(netdev.EventNetDown) + } + r.netConnect(false) + } + r.mu.Unlock() + } + } +} + +func (r *rtl8720dn) netConnect(reset bool) error { + if reset { + if err := r.start(); err != nil { + return err + } + } + r.showDevice() + + for i := 0; r.cfg.Retries == 0 || i < r.cfg.Retries; i++ { + if err := r.connectToAP(); err != nil { + if err == netdev.ErrConnectFailed { + continue + } + return err + } + break + } + + if r.networkDown() { + return netdev.ErrConnectFailed + } + + r.showIP() + return nil +} + +func (r *rtl8720dn) NetConnect() error { + + r.mu.Lock() + defer r.mu.Unlock() + + if r.netConnected { + return netdev.ErrConnected + } + + r.showDriver() + + if err := r.netConnect(true); err != nil { + return err + } + + r.netConnected = true + + if r.cfg.WatchdogTimeo != 0 { + go r.watchdog() + } + + return nil +} + +func (r *rtl8720dn) netDisconnect() { + r.disconnect() +} + +func (r *rtl8720dn) NetDisconnect() { + + r.mu.Lock() + defer r.mu.Unlock() + + if !r.netConnected { + return + } + + if r.cfg.WatchdogTimeo != 0 { + r.killWatchdog <- true + } + r.netDisconnect() + r.stop() + + r.netConnected = false + + if debugging(debugBasic) { + fmt.Printf("\r\nDisconnected from Wifi SSID '%s'\r\n\r\n", r.cfg.Ssid) + } + + if r.notifyCb != nil { + r.notifyCb(netdev.EventNetDown) + } +} + +func (r *rtl8720dn) NetNotify(cb func(netdev.Event)) { + r.notifyCb = cb +} + +func (r *rtl8720dn) GetHostByName(name string) (netdev.IP, error) { + + if debugging(debugNetdev) { + fmt.Printf("[GetHostByName] name: %s\r\n", name) + } + + r.mu.Lock() + defer r.mu.Unlock() + + var addr [4]byte + result := r.rpc_netconn_gethostbyname(name, addr[:]) + if result == -1 { + return netdev.IP{}, fmt.Errorf("Get IP of host '%s' failed", name) + } + + var ip netdev.IP + copy(ip[:], addr[:]) + + return ip, nil +} + +func (r *rtl8720dn) GetHardwareAddr() (netdev.HardwareAddr, error) { + + if debugging(debugNetdev) { + fmt.Printf("[GetHardwareAddr]\r\n") + } + + r.mu.Lock() + defer r.mu.Unlock() + + mac := strings.ReplaceAll(r.getMACAddr(), ":", "") + addr, err := hex.DecodeString(mac) + + return netdev.HardwareAddr(addr), err +} + +func (r *rtl8720dn) GetIPAddr() (netdev.IP, error) { + + if debugging(debugNetdev) { + fmt.Printf("[GetIPAddr]\r\n") + } + + r.mu.Lock() + defer r.mu.Unlock() + + ip, _, _, err := r.getIP() + + return netdev.IP(ip), err +} + +func (r *rtl8720dn) clientTLS() uint32 { + client := r.rpc_wifi_ssl_client_create() + r.rpc_wifi_ssl_init(client) + r.rpc_wifi_ssl_set_timeout(client, 120*1000 /* usec? */) + return client +} + +// 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 (r *rtl8720dn) Socket(family netdev.AddressFamily, sockType netdev.SockType, + protocol netdev.Protocol) (netdev.Sockfd, error) { + + if debugging(debugNetdev) { + fmt.Printf("[Socket] family: %s, sockType: %s, protocol: %s\r\n", + family, sockType, protocol) + } + + switch family { + case netdev.AF_INET: + default: + return -1, netdev.ErrFamilyNotSupported + } + + var newSock int32 + + r.mu.Lock() + defer r.mu.Unlock() + + switch { + case protocol == netdev.IPPROTO_TCP && sockType == netdev.SOCK_STREAM: + newSock = r.rpc_lwip_socket(netdev.AF_INET, netdev.SOCK_STREAM, + netdev.IPPROTO_TCP) + case protocol == netdev.IPPROTO_TLS && sockType == netdev.SOCK_STREAM: + // TODO Investigate: using client number as socket number; + // TODO this may cause a problem if mixing TLS and non-TLS sockets? + newSock = int32(r.clientTLS()) + case protocol == netdev.IPPROTO_UDP && sockType == netdev.SOCK_DGRAM: + newSock = r.rpc_lwip_socket(netdev.AF_INET, netdev.SOCK_DGRAM, + netdev.IPPROTO_UDP) + default: + return -1, netdev.ErrProtocolNotSupported + } + + if newSock == -1 { + return -1, netdev.ErrNoMoreSockets + } + + socket := newSocket(protocol) + r.sockets[sock(newSock)] = socket + + return netdev.Sockfd(newSock), nil +} + +func addrToName(addr netdev.SockAddr) []byte { + port := addr.Port() + ip := addr.IpBytes() + + name := make([]byte, 16) + name[0] = 0x00 + name[1] = netdev.AF_INET + name[2] = byte(port >> 8) + name[3] = byte(port) + name[4] = byte(ip[0]) + name[5] = byte(ip[1]) + name[6] = byte(ip[2]) + name[7] = byte(ip[3]) + + return name +} + +func (r *rtl8720dn) Bind(sockfd netdev.Sockfd, addr netdev.SockAddr) error { + + if debugging(debugNetdev) { + fmt.Printf("[Bind] sockfd: %d, addr: %s\r\n", sockfd, addr) + } + + r.mu.Lock() + defer r.mu.Unlock() + + var sock = sock(sockfd) + var socket = r.sockets[sock] + var name = addrToName(addr) + + switch socket.protocol { + case netdev.IPPROTO_TCP, netdev.IPPROTO_UDP: + result := r.rpc_lwip_bind(int32(sock), name, uint32(len(name))) + if result == -1 { + return fmt.Errorf("Bind to %s failed", addr.String()) + } + default: + return netdev.ErrProtocolNotSupported + } + + return nil +} + +func (r *rtl8720dn) Connect(sockfd netdev.Sockfd, servaddr netdev.SockAddr) error { + + if debugging(debugNetdev) { + fmt.Printf("[Connect] sockfd: %d, servaddr: %s\r\n", sockfd, servaddr) + } + + r.mu.Lock() + defer r.mu.Unlock() + + var sock = sock(sockfd) + var socket = r.sockets[sock] + var name = addrToName(servaddr) + + // Start the connection + switch socket.protocol { + case netdev.IPPROTO_TCP, netdev.IPPROTO_UDP: + result := r.rpc_lwip_connect(int32(sock), name, uint32(len(name))) + if result == -1 { + return fmt.Errorf("Connect to %s failed", servaddr.String()) + } + case netdev.IPPROTO_TLS: + result := r.rpc_wifi_start_ssl_client(uint32(sock), + servaddr.Host(), uint32(servaddr.Port()), 0) + if result == -1 { + return fmt.Errorf("Connect to %s failed", servaddr.String()) + } + } + + return nil +} + +func (r *rtl8720dn) Listen(sockfd netdev.Sockfd, backlog int) error { + + if debugging(debugNetdev) { + fmt.Printf("[Listen] sockfd: %d\r\n", sockfd) + } + + r.mu.Lock() + defer r.mu.Unlock() + + var sock = sock(sockfd) + var socket = r.sockets[sock] + + switch socket.protocol { + case netdev.IPPROTO_TCP: + result := r.rpc_lwip_listen(int32(sock), int32(backlog)) + if result == -1 { + return fmt.Errorf("Listen failed") + } + result = r.rpc_lwip_fcntl(int32(sock), F_SETFL, O_NONBLOCK) + if result == -1 { + return fmt.Errorf("Fcntl failed") + } + case netdev.IPPROTO_UDP: + result := r.rpc_lwip_listen(int32(sock), int32(backlog)) + if result == -1 { + return fmt.Errorf("Listen failed") + } + default: + return netdev.ErrProtocolNotSupported + } + + return nil +} + +func (r *rtl8720dn) Accept(sockfd netdev.Sockfd, peer netdev.SockAddr) (netdev.Sockfd, error) { + + if debugging(debugNetdev) { + fmt.Printf("[Accept] sockfd: %d, peer: %s\r\n", sockfd, peer) + } + + r.mu.Lock() + defer r.mu.Unlock() + + var newSock int32 + var lsock = sock(sockfd) + var socket = r.sockets[lsock] + var addr = addrToName(peer) + + switch socket.protocol { + case netdev.IPPROTO_TCP: + default: + return -1, netdev.ErrProtocolNotSupported + } + + for { + // Accept() will be sleeping most of the time, checking for a + // new clients every 1/10 sec. + r.mu.Unlock() + time.Sleep(100 * time.Millisecond) + r.mu.Lock() + + // Check if a client connected. O_NONBLOCK is set on lsock. + addrlen := uint32(len(addr)) + newSock = r.rpc_lwip_accept(int32(lsock), addr, &addrlen) + if newSock == -1 { + // No new client + time.Sleep(100 * time.Millisecond) + continue + } + + // If we've already seen this socket, we can re-use + // the socket and return it. But, only if the socket + // is closed. If it's not closed, we'll just come back + // later to reuse it. + + clientSocket, ok := r.sockets[sock(newSock)] + if ok { + // Wait for client to Close + if clientSocket.inuse { + continue + } + // Reuse client socket + return netdev.Sockfd(newSock), nil + } + + // Create new socket for client and return fd + r.sockets[sock(newSock)] = newSocket(socket.protocol) + return netdev.Sockfd(newSock), nil + } +} + +func (r *rtl8720dn) sendChunk(sockfd netdev.Sockfd, buf []byte) (int, error) { + var sock = sock(sockfd) + var socket = r.sockets[sock] + + switch socket.protocol { + case netdev.IPPROTO_TCP, netdev.IPPROTO_UDP: + result := r.rpc_lwip_send(int32(sock), buf, 0x00000008) + if result == -1 { + return -1, fmt.Errorf("Send error") + } + return int(result), nil + case netdev.IPPROTO_TLS: + result := r.rpc_wifi_send_ssl_data(uint32(sock), buf, uint16(len(buf))) + if result == -1 { + return -1, fmt.Errorf("TLS Send error") + } + return int(result), nil + } + + return -1, netdev.ErrProtocolNotSupported +} + +func (r *rtl8720dn) Send(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, + timeout time.Duration) (int, error) { + + if debugging(debugNetdev) { + fmt.Printf("[Send] sockfd: %d, len(buf): %d, flags: %d\r\n", + sockfd, len(buf), flags) + } + + r.mu.Lock() + defer r.mu.Unlock() + + // Break large bufs into chunks + + // TODO handle timeout + + chunkSize := 1436 + for i := 0; i < len(buf); i += chunkSize { + end := i + chunkSize + if end > len(buf) { + end = len(buf) + } + _, err := r.sendChunk(sockfd, buf[i:end]) + if err != nil { + return -1, err + } + } + + return len(buf), nil +} + +func (r *rtl8720dn) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, + timeout time.Duration) (int, error) { + + if debugging(debugNetdev) { + fmt.Printf("[Recv] sockfd: %d, len(buf): %d, flags: %d\r\n", + sockfd, len(buf), flags) + } + + r.mu.Lock() + defer r.mu.Unlock() + + var sock = sock(sockfd) + var socket = r.sockets[sock] + var length = len(buf) + var expire = time.Now().Add(timeout) + var n int32 + + // Limit length read size to chunk large read requests + if length > 1436 { + length = 1436 + } + + for { + // Check if we've timed out + if timeout > 0 { + if time.Now().Before(expire) { + return -1, netdev.ErrRecvTimeout + } + } + + switch socket.protocol { + case netdev.IPPROTO_TCP, netdev.IPPROTO_UDP: + n = r.rpc_lwip_recv(int32(sock), buf[:length], + uint32(length), 0x00000008, 0) + case netdev.IPPROTO_TLS: + n = r.rpc_wifi_get_ssl_receive(uint32(sock), + buf[:length], int32(length)) + } + + if n < 0 { + r.mu.Unlock() + time.Sleep(100 * time.Millisecond) + r.mu.Lock() + continue + } else if n == 0 { + if debugging(debugNetdev) { + fmt.Printf("[<--Recv] sockfd: %d, n: %d EOF\r\n", + sock, n) + } + return -1, io.EOF + } + + if debugging(debugNetdev) { + fmt.Printf("[<--Recv] sockfd: %d, n: %d\r\n", + sock, n) + } + + return int(n), nil + } +} + +func (r *rtl8720dn) Close(sockfd netdev.Sockfd) error { + + if debugging(debugNetdev) { + fmt.Printf("[Close] sockfd: %d\r\n", sockfd) + } + + r.mu.Lock() + defer r.mu.Unlock() + + var sock = sock(sockfd) + var socket = r.sockets[sock] + var result int32 + + if !socket.inuse { + return nil + } + + switch socket.protocol { + case netdev.IPPROTO_TCP, netdev.IPPROTO_UDP: + result = r.rpc_lwip_close(int32(sock)) + case netdev.IPPROTO_TLS: + r.rpc_wifi_stop_ssl_socket(uint32(sock)) + r.rpc_wifi_ssl_client_destroy(uint32(sock)) + } + + if result == -1 { + return netdev.ErrClosingSocket + } + + socket.inuse = false + + return nil +} + +func (r *rtl8720dn) SetSockOpt(sockfd netdev.Sockfd, level netdev.SockOptLevel, + opt netdev.SockOpt, value any) error { + + if debugging(debugNetdev) { + fmt.Printf("[SetSockOpt] sockfd: %d\r\n", sockfd) + } + + return netdev.ErrNotSupported +} + +func (r *rtl8720dn) disconnect() error { + result := r.rpc_wifi_disconnect() + if result == -1 { + return fmt.Errorf("Error disconnecting Wifi") + } + return nil +} + +func (r *rtl8720dn) getFwVersion() string { + return r.rpc_system_version() } -type UARTx struct { - *machine.UART +func (r *rtl8720dn) getMACAddr() string { + var mac [18]uint8 + r.rpc_wifi_get_mac_address(mac[:]) + return string(mac[:]) } -func (u *UARTx) Read(p []byte) (n int, err error) { - if u.Buffered() == 0 { - time.Sleep(1 * time.Millisecond) - return 0, nil +func (r *rtl8720dn) getIP() (ip, subnet, gateway netdev.IP, err error) { + var ip_info [12]byte + result := r.rpc_tcpip_adapter_get_ip_info(0, ip_info[:]) + if result == -1 { + err = fmt.Errorf("Get IP info failed") + return } - return u.UART.Read(p) + copy(ip[:], ip_info[0:4]) + copy(subnet[:], ip_info[4:8]) + copy(gateway[:], ip_info[8:12]) + return } diff --git a/rtl8720dn/util.go b/rtl8720dn/util.go deleted file mode 100644 index 588106c5c..000000000 --- a/rtl8720dn/util.go +++ /dev/null @@ -1,51 +0,0 @@ -package rtl8720dn - -import ( - "bytes" - "encoding/binary" - "fmt" - "strconv" - "strings" -) - -type httpHeader []byte - -func (h httpHeader) ContentLength() int { - contentLength := -1 - idx := bytes.Index(h, []byte("Content-Length: ")) - if 0 <= idx { - _, err := fmt.Sscanf(string(h[idx+16:]), "%d", &contentLength) - if err != nil { - return -1 - } - } - return contentLength -} - -// TODO: IPAddress implementation should be moved under drivers/net -// The same implementation exists in wifinina. -type IPAddress []byte - -func (addr IPAddress) String() string { - if len(addr) < 4 { - return "" - } - return strconv.Itoa(int(addr[0])) + "." + strconv.Itoa(int(addr[1])) + "." + strconv.Itoa(int(addr[2])) + "." + strconv.Itoa(int(addr[3])) -} - -func ParseIPv4(s string) (IPAddress, error) { - v := strings.Split(s, ".") - v0, _ := strconv.Atoi(v[0]) - v1, _ := strconv.Atoi(v[1]) - v2, _ := strconv.Atoi(v[2]) - v3, _ := strconv.Atoi(v[3]) - return IPAddress([]byte{byte(v0), byte(v1), byte(v2), byte(v3)}), nil -} - -func (addr IPAddress) AsUint32() uint32 { - if len(addr) < 4 { - return 0 - } - b := []byte(string(addr)) - return binary.BigEndian.Uint32(b[0:4]) -} diff --git a/rtl8720dn/wifi.go b/rtl8720dn/wifi.go deleted file mode 100644 index 08764210c..000000000 --- a/rtl8720dn/wifi.go +++ /dev/null @@ -1,72 +0,0 @@ -package rtl8720dn - -import ( - "fmt" - "time" -) - -func (d *Driver) ConnectToAP(ssid string, password string) error { - if len(ssid) == 0 || len(password) == 0 { - return fmt.Errorf("connection failed: either ssid or password not set") - } - - _, err := d.Rpc_wifi_off() - if err != nil { - return err - } - _, err = d.Rpc_wifi_on(0x00000001) - if err != nil { - return err - } - - _, err = d.Rpc_wifi_disconnect() - if err != nil { - return err - } - - numTry := 5 - securityType := uint32(0x00400004) - for i := 0; i < numTry; i++ { - ret, err := d.Rpc_wifi_connect(ssid, password, securityType, -1, 0) - if err != nil { - return err - } - if ret != 0 { - if i == numTry-1 { - return fmt.Errorf("connection failed: rpc_wifi_connect failed") - } - time.Sleep(100 * time.Millisecond) - } else { - break - } - } - - _, err = d.Rpc_tcpip_adapter_dhcpc_start(0) - if err != nil { - return err - } - - for i := 0; i < 3; i++ { - _, err = d.Rpc_wifi_is_connected_to_ap() - if err != nil { - return err - } - time.Sleep(1 * time.Second) - } - - return nil -} - -func (d *Driver) GetIP() (ip, subnet, gateway IPAddress, err error) { - ip_info := make([]byte, 12) - _, err = d.Rpc_tcpip_adapter_get_ip_info(0, &ip_info) - if err != nil { - return nil, nil, nil, err - } - - ip = IPAddress(ip_info[0:4]) - subnet = IPAddress(ip_info[4:8]) - gateway = IPAddress(ip_info[8:12]) - - return ip, subnet, gateway, nil -} diff --git a/wifinina/README.md b/wifinina/README.md index b851fd72e..3be01be1a 100644 --- a/wifinina/README.md +++ b/wifinina/README.md @@ -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 diff --git a/wifinina/adapter.go b/wifinina/adapter.go deleted file mode 100644 index 063ca0771..000000000 --- a/wifinina/adapter.go +++ /dev/null @@ -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 -} diff --git a/wifinina/debug.go b/wifinina/debug.go new file mode 100644 index 000000000..0def23f57 --- /dev/null +++ b/wifinina/debug.go @@ -0,0 +1,17 @@ +package wifinina + +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 +} diff --git a/wifinina/http.go b/wifinina/http.go deleted file mode 100644 index e1358a0f3..000000000 --- a/wifinina/http.go +++ /dev/null @@ -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() != TCPStateClosed; i++ { - time.Sleep(100 * time.Millisecond) - } - - if c.status() != 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 -} diff --git a/wifinina/pins.go b/wifinina/pins.go index a1afbcbde..5d7ca2b4f 100644 --- a/wifinina/pins.go +++ b/wifinina/pins.go @@ -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 { diff --git a/wifinina/protocol/readme.md b/wifinina/protocol/readme.md deleted file mode 100644 index 0f2a5e395..000000000 --- a/wifinina/protocol/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -WiFiNINA protocol -================= - diff --git a/wifinina/tcp.go b/wifinina/tcp.go deleted file mode 100644 index ba5f41a5d..000000000 --- a/wifinina/tcp.go +++ /dev/null @@ -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 == TCPStateListen || s == TCPStateClosed || - s == TCPStateFinWait1 || s == TCPStateFinWait2 || s == TCPStateTimeWait || - s == TCPStateSynSent || s == TCPStateSynRcvd || s == 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 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 == TCPStateClosed { - break - } - time.Sleep(1 * time.Millisecond) - } - d.sock = NoSocketAvail - return nil -} diff --git a/wifinina/wifinina.go b/wifinina/wifinina.go index c493035e0..ea6f008df 100644 --- a/wifinina/wifinina.go +++ b/wifinina/wifinina.go @@ -1,1247 +1,1893 @@ -// Package wifinina implements TCP wireless communication over SPI -// with an attached separate ESP32 board using the Arduino WiFiNINA protocol. +// 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 +// 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" // used only in debug printouts and is optimized out when debugging is disabled - "strconv" - "strings" + "fmt" + "io" + "machine" + "math/bits" "sync" "time" - "machine" - "tinygo.org/x/drivers" - "tinygo.org/x/drivers/net" + "tinygo.org/x/drivers/netdev" ) -const _debug = false +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 ( - MaxSockets = 4 - MaxNetworks = 10 - MaxAttempts = 10 - - MaxLengthSSID = 32 - MaxLengthWPAKey = 63 - MaxLengthWEPKey = 13 - - LengthMacAddress = 6 - LengthIPV4 = 4 - - WlFailure = -1 - WlSuccess = 1 - - 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 - /* - // Default state value for Wifi state field - #define NA_STATE -1 - */ - - FlagCmd = 0 - FlagReply = 1 << 7 - FlagData = 0x40 - - NinaCmdPos = 1 - NinaParamLenPos = 2 - - CmdStart = 0xE0 - CmdEnd = 0xEE - CmdErr = 0xEF + 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 - // TEST_CMD = 0x13 - - 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 - // GET_IDX_SSID_CMD = 0x31, - // GET_TEST_CMD = 0x38 - - // All command with DATA_FLAG 0x40 send a 16bit Len - CmdSendDataTCP = 0x44 - CmdGetDatabufTCP = 0x45 - CmdInsertDataBuf = 0x46 - - // regular format commands - CmdSetPinMode = 0x50 - CmdSetDigitalWrite = 0x51 - CmdSetAnalogWrite = 0x52 - - ErrTimeoutChipReady Error = 0x01 - ErrTimeoutChipSelect Error = 0x02 - ErrCheckStartCmd Error = 0x03 - ErrWaitRsp Error = 0x04 - ErrUnexpectedLength Error = 0xE0 - ErrNoParamsReturned Error = 0xE1 - ErrIncorrectSentinel Error = 0xE2 - ErrCmdErrorReceived Error = 0xEF - ErrNotImplemented Error = 0xF0 - ErrUnknownHost Error = 0xF1 - ErrSocketAlreadySet Error = 0xF2 - ErrConnectionTimeout Error = 0xF3 - ErrNoData Error = 0xF4 - ErrDataNotWritten Error = 0xF5 - ErrCheckDataError Error = 0xF6 - ErrBufferTooSmall Error = 0xF7 - ErrNoSocketAvail Error = 0xFF - - NoSocketAvail uint8 = 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 + protoModeTCP = iota + protoModeUDP + protoModeTLS + protoModeMul ) -type ConnectionStatus uint8 - -func (c ConnectionStatus) String() string { - switch c { - case StatusIdle: - return "Idle" - case StatusNoSSIDAvail: - return "No SSID Available" - case StatusScanCompleted: - return "Scan Completed" - case StatusConnected: - return "Connected" - case StatusConnectFailed: - return "Connect Failed" - case StatusConnectionLost: - return "Connection Lost" - case StatusDisconnected: - return "Disconnected" - case StatusNoShield: - return "No Shield" - default: - return "Unknown" +type connectionStatus uint8 +type encryptionType uint8 +type macAddress netdev.HardwareAddr +type sock uint8 +type hwerr uint8 + +type socket struct { + protocol netdev.Protocol + laddr netdev.SockAddr + inuse bool +} + +type Config struct { + // AP creditials + Ssid string + Passphrase string + + // 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 + + // Retries is how many attempts to connect before returning with a + // "Connect failed" error. Zero means infinite retries. + Retries int + + // Timeout duration for each connection attempt. Default is 10sec. + ConnectTimeo time.Duration + + // Watchdog ticker duration. On tick, the watchdog will check for + // downed connection or hardware fault and try to recover the + // connection. Default is 0secs, which means no watchdog. Set to + // non-zero to enable watchodog. + WatchdogTimeo time.Duration +} + +type wifinina struct { + cfg *Config + notifyCb func(netdev.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 + + netConnected bool + driverShown bool + deviceShown bool + spiSetup bool + + killWatchdog chan bool + fault error + + sockets map[sock]*socket // keyed by sock as returned by getSocket() +} + +func newSocket(protocol netdev.Protocol) *socket { + return &socket{protocol: protocol, inuse: true} +} + +func New(cfg *Config) *wifinina { + w := wifinina{ + cfg: cfg, + sockets: make(map[sock]*socket), + killWatchdog: make(chan bool), + cs: cfg.Cs, + ack: cfg.Ack, + gpio0: cfg.Gpio0, + resetn: cfg.Resetn, } + + if w.cfg.ConnectTimeo == 0 { + w.cfg.ConnectTimeo = 10 * time.Second + } + + return &w } -type EncryptionType uint8 +func (err hwerr) Error() string { + return "[wifinina] error: 0x" + hex.EncodeToString([]byte{uint8(err)}) +} -func (e EncryptionType) String() string { - switch e { - case EncTypeTKIP: - return "TKIP" - case EncTypeCCMP: - return "WPA2" - case EncTypeWEP: - return "WEP" - case EncTypeNone: - return "None" - case EncTypeAuto: - return "Auto" - default: - return "Unknown" +func (w *wifinina) connectToAP(timeout time.Duration) error { + + if len(w.cfg.Ssid) == 0 { + return netdev.ErrMissingSSID + } + + if debugging(debugBasic) { + fmt.Printf("Connecting to Wifi SSID '%s'...", w.cfg.Ssid) + } + + start := time.Now() + + // Start the connection process + w.setPassphrase(w.cfg.Ssid, w.cfg.Passphrase) + + // Check if we connected + for time.Since(start) < timeout { + status := w.getConnectionStatus() + if status == statusConnected { + if debugging(debugBasic) { + fmt.Printf("CONNECTED\r\n") + } + if w.notifyCb != nil { + w.notifyCb(netdev.EventNetUp) + } + return nil + } + time.Sleep(1 * time.Second) } + + if debugging(debugBasic) { + fmt.Printf("FAILED\r\n") + } + + return netdev.ErrConnectTimeout } -type IPAddress string // TODO: does WiFiNINA support ipv6??? +func (w *wifinina) netDisconnect() { + w.disconnect() +} -func (addr IPAddress) String() string { - if len(addr) < 4 { - return "" +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) } - return strconv.Itoa(int(addr[0])) + "." + strconv.Itoa(int(addr[1])) + "." + strconv.Itoa(int(addr[2])) + "." + strconv.Itoa(int(addr[3])) + w.driverShown = true } -func ParseIPv4(s string) (IPAddress, error) { - v := strings.Split(s, ".") - v0, _ := strconv.Atoi(v[0]) - v1, _ := strconv.Atoi(v[1]) - v2, _ := strconv.Atoi(v[2]) - v3, _ := strconv.Atoi(v[3]) - return IPAddress([]byte{byte(v0), byte(v1), byte(v2), byte(v3)}), nil +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 (addr IPAddress) AsUint32() uint32 { - if len(addr) < 4 { - return 0 +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.Low() + time.Sleep(10 * time.Millisecond) + w.resetn.High() + 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) { + mac := netdev.HardwareAddr(w.getMACAddr()) + fmt.Printf("ESP32 firmware version : %s\r\n", w.getFwVersion()) + 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", netdev.IP(ip).String()) + fmt.Printf("DHCP-assigned subnet : %s\r\n", netdev.IP(subnet).String()) + fmt.Printf("DHCP-assigned gateway : %s\r\n", netdev.IP(gateway).String()) + fmt.Printf("\r\n") + } +} + +func (w *wifinina) networkDown() bool { + return w.getConnectionStatus() != statusConnected +} + +func (w *wifinina) watchdog() { + ticker := time.NewTicker(w.cfg.WatchdogTimeo) + 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(netdev.EventNetDown) + } + w.netConnect(false) + } + w.mu.Unlock() + } } - b := []byte(string(addr)) - return binary.BigEndian.Uint32(b[0:4]) } -type MACAddress uint64 +func (w *wifinina) netConnect(reset bool) error { + if reset { + w.start() + } + w.showDevice() -func (addr MACAddress) String() string { - b := make([]byte, 8) - binary.BigEndian.PutUint64(b, uint64(addr)) - encoded := hex.EncodeToString(b) - result := "" - for i := 2; i < 8; i++ { - result += encoded[i*2 : i*2+2] - if i < 7 { - result += ":" + for i := 0; w.cfg.Retries == 0 || i < w.cfg.Retries; i++ { + if err := w.connectToAP(w.cfg.ConnectTimeo); err != nil { + if err == netdev.ErrConnectTimeout { + continue + } + return err } + break } - return result + + if w.networkDown() { + return netdev.ErrConnectFailed + } + + w.showIP() + return nil } -type Error uint8 +func (w *wifinina) NetConnect() error { + + w.mu.Lock() + defer w.mu.Unlock() + + if w.netConnected { + return netdev.ErrConnected + } + + w.showDriver() + w.setupSPI() + + if err := w.netConnect(true); err != nil { + return err + } + + w.netConnected = true + + if w.cfg.WatchdogTimeo != 0 { + go w.watchdog() + } -func (err Error) Error() string { - return "wifinina error: 0x" + hex.EncodeToString([]byte{uint8(err)}) + return nil } -// Cmd Struct Message */ -// ._______________________________________________________________________. -// | START CMD | C/R | CMD | N.PARAM | PARAM LEN | PARAM | .. | END CMD | -// |___________|______|______|_________|___________|________|____|_________| -// | 8 bit | 1bit | 7bit | 8bit | 8bit | nbytes | .. | 8bit | -// |___________|______|______|_________|___________|________|____|_________| -type command struct { - cmd uint8 - reply bool - params []int - paramData []byte +func (w *wifinina) NetDisconnect() { + + w.mu.Lock() + defer w.mu.Unlock() + + if !w.netConnected { + return + } + + if w.cfg.WatchdogTimeo != 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.cfg.Ssid) + } + + if w.notifyCb != nil { + w.notifyCb(netdev.EventNetDown) + } +} + +func (w *wifinina) NetNotify(cb func(netdev.Event)) { + w.notifyCb = cb } -type Device struct { - SPI drivers.SPI - CS machine.Pin - ACK machine.Pin - GPIO0 machine.Pin - RESET machine.Pin +func (w *wifinina) GetHostByName(name string) (netdev.IP, error) { - buf [64]byte - ssids [10]string + if debugging(debugNetdev) { + fmt.Printf("[GetHostByName] name: %s\r\n", name) + } + + w.mu.Lock() + defer w.mu.Unlock() + + ip := w.getHostByName(name) + if ip == "" { + return netdev.IP{}, netdev.ErrHostUnknown + } - sock uint8 - readBuf readBuffer + var hostIp netdev.IP + copy(hostIp[:], []byte(ip)) - proto uint8 - ip uint32 - port uint16 - mu sync.Mutex + return hostIp, nil } -// New returns a new Wifinina device. -func New(bus drivers.SPI, csPin, ackPin, gpio0Pin, resetPin machine.Pin) *Device { - return &Device{ - SPI: bus, - CS: csPin, - ACK: ackPin, - GPIO0: gpio0Pin, - RESET: resetPin, +func (w *wifinina) GetHardwareAddr() (netdev.HardwareAddr, error) { + + if debugging(debugNetdev) { + fmt.Printf("[GetHardwareAddr]\r\n") } + + w.mu.Lock() + defer w.mu.Unlock() + + return netdev.HardwareAddr(w.getMACAddr()), nil } -func (d *Device) Configure() { - net.UseDriver(d) - pinUseDevice(d) +func (w *wifinina) GetIPAddr() (netdev.IP, error) { - d.CS.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.ACK.Configure(machine.PinConfig{Mode: machine.PinInput}) - d.RESET.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.GPIO0.Configure(machine.PinConfig{Mode: machine.PinOutput}) + if debugging(debugNetdev) { + fmt.Printf("[GetIPAddr]\r\n") + } - d.GPIO0.High() - d.CS.High() - d.RESET.Low() - time.Sleep(1 * time.Millisecond) - d.RESET.High() - time.Sleep(1 * time.Millisecond) + w.mu.Lock() + defer w.mu.Unlock() - d.GPIO0.Low() - d.GPIO0.Configure(machine.PinConfig{Mode: machine.PinInput}) + ip, _, _ := w.getIP() + return netdev.IP(ip), nil } -// ----------- client methods (should this be a separate struct?) ------------ +// 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(family netdev.AddressFamily, sockType netdev.SockType, + protocol netdev.Protocol) (netdev.Sockfd, error) { -func (d *Device) StartClient(hostname string, addr uint32, port uint16, sock uint8, mode uint8) error { - if _debug { - fmt.Printf("[StartClient] hostname: %s addr: % 02X, port: %d, sock: %d\r\n", hostname, addr, port, sock) + if debugging(debugNetdev) { + fmt.Printf("[Socket] family: %s, sockType: %s, protocol: %s\r\n", + family, sockType, protocol) } - if err := d.waitForChipSelect(); err != nil { - d.spiChipDeselect() - return err + + switch family { + case netdev.AF_INET: + default: + return -1, netdev.ErrFamilyNotSupported } - if len(hostname) > 0 { - d.sendCmd(CmdStartClientTCP, 5) - d.sendParamStr(hostname, false) - } else { - d.sendCmd(CmdStartClientTCP, 4) + switch { + case protocol == netdev.IPPROTO_TCP && sockType == netdev.SOCK_STREAM: + case protocol == netdev.IPPROTO_TLS && sockType == netdev.SOCK_STREAM: + case protocol == netdev.IPPROTO_UDP && sockType == netdev.SOCK_DGRAM: + default: + return -1, netdev.ErrProtocolNotSupported } - d.sendParam32(addr, false) - d.sendParam16(port, false) - d.sendParam8(sock, false) - d.sendParam8(mode, true) - if len(hostname) > 0 { - d.padTo4(17 + len(hostname)) + w.mu.Lock() + defer w.mu.Unlock() + + sock := w.getSocket() + if sock == noSocketAvail { + return -1, netdev.ErrNoMoreSockets } - d.spiChipDeselect() + socket := newSocket(protocol) + w.sockets[sock] = socket - _, err := d.waitRspCmd1(CmdStartClientTCP) - return err + return netdev.Sockfd(sock), nil } -func (d *Device) GetSocket() (uint8, error) { - return d.getUint8(d.req0(CmdGetSocket)) +func (w *wifinina) Bind(sockfd netdev.Sockfd, addr netdev.SockAddr) error { + + if debugging(debugNetdev) { + fmt.Printf("[Bind] sockfd: %d, addr: %s\r\n", sockfd, addr) + } + + w.mu.Lock() + defer w.mu.Unlock() + + var sock = sock(sockfd) + var socket = w.sockets[sock] + + switch socket.protocol { + case netdev.IPPROTO_TCP: + case netdev.IPPROTO_TLS: + case netdev.IPPROTO_UDP: + w.startServer(sock, addr.Port(), protoModeUDP) + } + + socket.laddr = addr + + return nil } -func (d *Device) GetClientState(sock uint8) (uint8, error) { - return d.getUint8(d.reqUint8(CmdGetClientStateTCP, sock)) +func (w *wifinina) Connect(sockfd netdev.Sockfd, servaddr netdev.SockAddr) error { + + if debugging(debugNetdev) { + fmt.Printf("[Connect] sockfd: %d, servaddr: %s\r\n", sockfd, servaddr) + } + + w.mu.Lock() + defer w.mu.Unlock() + + var sock = sock(sockfd) + var socket = w.sockets[sock] + + // Start the connection + switch socket.protocol { + case netdev.IPPROTO_TCP: + w.startClient(sock, "", servaddr.IpUint32(), servaddr.Port(), protoModeTCP) + case netdev.IPPROTO_TLS: + w.startClient(sock, servaddr.Host(), 0, servaddr.Port(), protoModeTLS) + case netdev.IPPROTO_UDP: + w.startClient(sock, "", servaddr.IpUint32(), servaddr.Port(), protoModeUDP) + return nil + } + + // Wait for up to 10s to connect... + expire := time.Now().Add(10 * time.Second) + + for time.Now().Before(expire) { + if w.isConnected(sock) { + return nil + } + + // Check if we've faulted + if w.fault != nil { + w.stopClient(sock) + return w.fault + } + + w.mu.Unlock() + time.Sleep(100 * time.Millisecond) + w.mu.Lock() + } + + w.stopClient(sock) + + return fmt.Errorf("Connect to %s timed out", servaddr.String()) } -func (d *Device) SendData(buf []byte, sock uint8) (uint16, error) { - d.mu.Lock() - defer d.mu.Unlock() +func (w *wifinina) Listen(sockfd netdev.Sockfd, backlog int) error { + + if debugging(debugNetdev) { + fmt.Printf("[Listen] sockfd: %d\r\n", sockfd) + } + + w.mu.Lock() + defer w.mu.Unlock() - if err := d.waitForChipSelect(); err != nil { - d.spiChipDeselect() - return 0, err + var sock = sock(sockfd) + var socket = w.sockets[sock] + + switch socket.protocol { + case netdev.IPPROTO_TCP: + w.startServer(sock, socket.laddr.Port(), protoModeTCP) + case netdev.IPPROTO_UDP: + default: + return netdev.ErrProtocolNotSupported } - l := d.sendCmd(CmdSendDataTCP, 2) - l += d.sendParamBuf([]byte{sock}, false) - l += d.sendParamBuf(buf, true) - d.addPadding(l) - d.spiChipDeselect() - return d.getUint16(d.waitRspCmd1(CmdSendDataTCP)) + + return nil } -func (d *Device) CheckDataSent(sock uint8) (bool, error) { - d.mu.Lock() - defer d.mu.Unlock() +func (w *wifinina) Accept(sockfd netdev.Sockfd, peer netdev.SockAddr) (netdev.Sockfd, error) { - var lastErr error - for timeout := 0; timeout < 10; timeout++ { - sent, err := d.getUint8(d.reqUint8(CmdDataSentTCP, sock)) - if err != nil { - lastErr = err + if debugging(debugNetdev) { + fmt.Printf("[Accept] sockfd: %d, peer: %s\r\n", sockfd, peer) + } + + w.mu.Lock() + defer w.mu.Unlock() + + var client sock + var sock = sock(sockfd) + var socket = w.sockets[sock] + + switch socket.protocol { + case netdev.IPPROTO_TCP: + default: + return -1, netdev.ErrProtocolNotSupported + } + + for { + // Accept() will be sleeping most of the time, checking for a + // 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, 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 + client = w.accept(sock) + if client == noSocketAvail { + // None ready + continue } - if sent > 0 { - return true, nil + + // If we've already seen this socket, we can reuse + // the socket and return it. But, only if the socket + // is closed. If it's not closed, we'll just come back + // later to reuse it. + + clientSocket, ok := w.sockets[client] + if ok { + // Wait for client to Close + if clientSocket.inuse { + continue + } + // Reuse client socket + return netdev.Sockfd(client), nil } - time.Sleep(100 * time.Microsecond) + + // Create new socket for client and return fd + w.sockets[client] = newSocket(socket.protocol) + return netdev.Sockfd(client), nil + } +} + +func (w *wifinina) sockDown(sock sock) bool { + state := w.getClientState(sock) + if state == tcpStateEstablished { + return false } - return false, lastErr + return true } -func (d *Device) GetDataBuf(sock uint8, buf []byte) (int, error) { - d.mu.Lock() - defer d.mu.Unlock() +func (w *wifinina) sendTCP(sock sock, buf []byte, timeout time.Duration) (int, error) { - if err := d.waitForChipSelect(); err != nil { - d.spiChipDeselect() - return 0, err + var timeoutDataSent = 25 + var expire = time.Now().Add(timeout) + + // Send it + n := int(w.sendData(sock, buf)) + if n == 0 { + return -1, io.EOF } - p := uint16(len(buf)) - l := d.sendCmd(CmdGetDatabufTCP, 2) - l += d.sendParamBuf([]byte{sock}, false) - l += d.sendParamBuf([]byte{uint8(p & 0x00FF), uint8((p) >> 8)}, true) - d.addPadding(l) - d.spiChipDeselect() - if err := d.waitForChipSelect(); err != nil { - d.spiChipDeselect() - return 0, err + + // 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 timeout > 0 { + if time.Now().Before(expire) { + return -1, fmt.Errorf("Send timeout expired") + } + } + + // Check if socket went down + if w.sockDown(sock) { + 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() } - n, err := d.waitRspBuf16(CmdGetDatabufTCP, buf) - d.spiChipDeselect() - return int(n), err + + return -1, fmt.Errorf("Send timed out") } -func (d *Device) StopClient(sock uint8) error { - d.mu.Lock() - defer d.mu.Unlock() +func (w *wifinina) sendUDP(sock sock, buf []byte, timeout time.Duration) (int, error) { + + // Queue it + ok := w.insertDataBuf(sock, buf) + if !ok { + return -1, fmt.Errorf("Insert UDP data failed, len(buf)=%d", len(buf)) + } - if _debug { - println("[StopClient] called StopClient()\r") + // Send it + ok = w.sendUDPData(sock) + if !ok { + return -1, fmt.Errorf("Send UDP data failed, len(buf)=%d", len(buf)) } - _, err := d.getUint8(d.reqUint8(CmdStopClientTCP, sock)) - return err + + return len(buf), nil } -func (d *Device) StartServer(port uint16, sock uint8, mode uint8) error { - d.mu.Lock() - defer d.mu.Unlock() +func (w *wifinina) sendChunk(sockfd netdev.Sockfd, buf []byte, timeout time.Duration) (int, error) { + var sock = sock(sockfd) + var socket = w.sockets[sock] - if err := d.waitForChipSelect(); err != nil { - d.spiChipDeselect() - return err + switch socket.protocol { + case netdev.IPPROTO_TCP, netdev.IPPROTO_TLS: + return w.sendTCP(sock, buf, timeout) + case netdev.IPPROTO_UDP: + return w.sendUDP(sock, buf, timeout) } - l := d.sendCmd(CmdStartServerTCP, 3) - l += d.sendParam16(port, false) - l += d.sendParam8(sock, false) - l += d.sendParam8(mode, true) - d.addPadding(l) - d.spiChipDeselect() - _, err := d.waitRspCmd1(CmdStartClientTCP) - return err + + return -1, netdev.ErrProtocolNotSupported } -// InsertDataBuf adds data to the buffer used for sending UDP data -func (d *Device) InsertDataBuf(buf []byte, sock uint8) (bool, error) { - d.mu.Lock() - defer d.mu.Unlock() +func (w *wifinina) Send(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, + timeout time.Duration) (int, error) { - if err := d.waitForChipSelect(); err != nil { - d.spiChipDeselect() - return false, err + if debugging(debugNetdev) { + fmt.Printf("[Send] sockfd: %d, len(buf): %d, flags: %d\r\n", + sockfd, len(buf), flags) } - l := d.sendCmd(CmdInsertDataBuf, 2) - l += d.sendParamBuf([]byte{sock}, false) - l += d.sendParamBuf(buf, true) - d.addPadding(l) - d.spiChipDeselect() - n, err := d.getUint8(d.waitRspCmd1(CmdInsertDataBuf)) - return n == 1, err -} -// SendUDPData sends the data previously added to the UDP buffer -func (d *Device) SendUDPData(sock uint8) (bool, error) { - d.mu.Lock() - defer d.mu.Unlock() + w.mu.Lock() + defer w.mu.Unlock() - if err := d.waitForChipSelect(); err != nil { - d.spiChipDeselect() - return false, err + // 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], timeout) + if err != nil { + return -1, err + } } - l := d.sendCmd(CmdSendDataUDP, 1) - l += d.sendParam8(sock, true) - d.addPadding(l) - d.spiChipDeselect() - n, err := d.getUint8(d.waitRspCmd1(CmdSendDataUDP)) - return n == 1, err + + return len(buf), nil } -// ---------- /client methods (should this be a separate struct?) ------------ +func (w *wifinina) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, + timeout time.Duration) (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() -/* - static bool startServer(uint16_t port, uint8_t sock); - static uint8_t getServerState(uint8_t sock); - static bool getData(uint8_t connId, uint8_t *data, bool peek, bool* connClose); - static int getDataBuf(uint8_t connId, uint8_t *buf, uint16_t bufSize); - static bool sendData(uint8_t sock, const uint8_t *data, uint16_t len); - static bool sendDataUdp(uint8_t sock, const char* host, uint16_t port, const uint8_t *data, uint16_t len); - static uint16_t availData(uint8_t connId); + var sock = sock(sockfd) + var socket = w.sockets[sock] + var max = len(buf) + // Limit max read size to chunk large read requests + if max > 1436 { + max = 1436 + } - static bool ping(const char *host); - static void reset(); + expire := time.Now().Add(timeout) - static void getRemoteIpAddress(IPAddress& ip); - static uint16_t getRemotePort(); -*/ + for { + // Check if we've timed out + if timeout > 0 { + if time.Now().Before(expire) { + return -1, netdev.ErrRecvTimeout + } + } -func (d *Device) Disconnect() error { - _, err := d.req1(CmdDisconnect) - return err + // 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(sock, buf[:max])) + if n > 0 { + if debugging(debugNetdev) { + fmt.Printf("[<--Recv] sockfd: %d, n: %d\r\n", + sock, n) + } + return n, nil + } + + // Check if socket went down + if socket.protocol != netdev.IPPROTO_UDP && w.sockDown(sock) { + // Get any last bytes + n = int(w.getDataBuf(sock, buf[:max])) + if debugging(debugNetdev) { + fmt.Printf("[<--Recv] sockfd: %d, n: %d, EOF\r\n", + sock, 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 (d *Device) GetFwVersion() (string, error) { - return d.getString(d.req0(CmdGetFwVersion)) +func (w *wifinina) Close(sockfd netdev.Sockfd) error { + + if debugging(debugNetdev) { + fmt.Printf("[Close] sockfd: %d\r\n", sockfd) + } + + w.mu.Lock() + defer w.mu.Unlock() + + var sock = sock(sockfd) + var socket = w.sockets[sock] + + if !socket.inuse { + return nil + } + + w.stopClient(sock) + + if socket.protocol == netdev.IPPROTO_UDP { + socket.inuse = false + return nil + } + + start := time.Now() + for time.Since(start) < 5*time.Second { + + state := w.getClientState(sock) + if state == tcpStateClosed { + socket.inuse = false + return nil + } + + w.mu.Unlock() + time.Sleep(100 * time.Millisecond) + w.mu.Lock() + } + + return netdev.ErrClosingSocket } -func (d *Device) GetConnectionStatus() (ConnectionStatus, error) { - status, err := d.getUint8(d.req0(CmdGetConnStatus)) - return ConnectionStatus(status), err +func (w *wifinina) SetSockOpt(sockfd netdev.Sockfd, level netdev.SockOptLevel, + opt netdev.SockOpt, value any) error { + + if debugging(debugNetdev) { + fmt.Printf("[SetSockOpt] sockfd: %d\r\n", sockfd) + } + + return netdev.ErrNotSupported } -func (d *Device) GetCurrentEncryptionType() (EncryptionType, error) { - enctype, err := d.getUint8(d.req1(CmdGetCurrEncrType)) - return EncryptionType(enctype), err +// Is TCP/TLS socket connected? +func (w *wifinina) isConnected(sock sock) bool { + s := w.getClientState(sock) + + connected := !(s == tcpStateListen || s == tcpStateClosed || + s == tcpStateFinWait1 || s == tcpStateFinWait2 || s == tcpStateTimeWait || + s == tcpStateSynSent || s == tcpStateSynRcvd || s == tcpStateCloseWait) + + return connected } -func (d *Device) GetCurrentBSSID() (MACAddress, error) { - return d.getMACAddress(d.req1(CmdGetCurrBSSID)) +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 (d *Device) GetCurrentRSSI() (int32, error) { - return d.getInt32(d.req1(CmdGetCurrRSSI)) +func (w *wifinina) getSocket() sock { + if debugging(debugCmd) { + fmt.Printf(" [cmdGetSocket]\r\n") + } + return sock(w.getUint8(w.req0(cmdGetSocket))) } -func (d *Device) GetCurrentSSID() (string, error) { - return d.getString(d.req1(CmdGetCurrSSID)) +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 (d *Device) GetMACAddress() (MACAddress, error) { - return d.getMACAddress(d.req1(CmdGetMACAddr)) +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 (d *Device) GetIP() (ip, subnet, gateway IPAddress, err error) { - sl := make([]string, 3) - if l, err := d.reqRspStr1(CmdGetIPAddr, dummyData, sl); err != nil { - return "", "", "", err - } else if l != 3 { - return "", "", "", ErrUnexpectedLength +func (w *wifinina) checkDataSent(sock sock) bool { + if debugging(debugCmd) { + fmt.Printf(" [cmdDataSentTCP] sock: %d\r\n", sock) } - return IPAddress(sl[0]), IPAddress(sl[1]), IPAddress(sl[2]), err + sent := w.getUint8(w.reqUint8(cmdDataSentTCP, uint8(sock))) + return sent > 0 } -func (d *Device) GetHostByName(hostname string) (IPAddress, error) { - ok, err := d.getUint8(d.reqStr(CmdReqHostByName, hostname)) - if err != nil { - return "", err +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)) } - if ok != 1 { - return "", ErrUnknownHost + + 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) + } } - ip, err := d.getString(d.req0(CmdGetHostByName)) - return IPAddress(ip), err + + return n } -func (d *Device) GetNetworkBSSID(idx int) (MACAddress, error) { - if idx < 0 || idx >= MaxNetworks { - return 0, nil +func (w *wifinina) stopClient(sock sock) { + if debugging(debugCmd) { + fmt.Printf(" [cmdStopClientTCP] sock: %d\r\n", sock) } - return d.getMACAddress(d.reqUint8(CmdGetIdxBSSID, uint8(idx))) + w.getUint8(w.reqUint8(cmdStopClientTCP, uint8(sock))) } -func (d *Device) GetNetworkChannel(idx int) (uint8, error) { - if idx < 0 || idx >= MaxNetworks { - return 0, nil +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) } - return d.getUint8(d.reqUint8(CmdGetIdxChannel, uint8(idx))) + + 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 (d *Device) GetNetworkEncrType(idx int) (EncryptionType, error) { - if idx < 0 || idx >= MaxNetworks { - return 0, nil +func (w *wifinina) accept(s sock) sock { + + if debugging(debugCmd) { + fmt.Printf(" [cmdAvailDataTCP] sock: %d\r\n", s) } - enctype, err := d.getUint8(d.reqUint8(CmdGetIdxEncrType, uint8(idx))) - return EncryptionType(enctype), err + + 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 (d *Device) GetNetworkRSSI(idx int) (int32, error) { - if idx < 0 || idx >= MaxNetworks { - return 0, nil +// 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)) } - return d.getInt32(d.reqUint8(CmdGetIdxRSSI, uint8(idx))) + + 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 } -func (d *Device) GetNetworkSSID(idx int) string { - if idx < 0 || idx >= MaxNetworks { - return "" +// 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) } - return d.ssids[idx] + + 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 (d *Device) GetReasonCode() (uint8, error) { - return d.getUint8(d.req0(CmdGetReasonCode)) +func (w *wifinina) disconnect() { + if debugging(debugCmd) { + fmt.Printf(" [cmdDisconnect]\r\n") + } + w.req1(cmdDisconnect) } -// GetTime is the time as a Unix timestamp -func (d *Device) GetTime() (uint32, error) { - return d.getUint32(d.req0(CmdGetTime)) +func (w *wifinina) getFwVersion() string { + if debugging(debugCmd) { + fmt.Printf(" [cmdGetFwVersion]\r\n") + } + return w.getString(w.req0(cmdGetFwVersion)) } -func (d *Device) GetTemperature() (float32, error) { - return d.getFloat32(d.req0(CmdGetTemperature)) +func (w *wifinina) getConnectionStatus() connectionStatus { + if debugging(debugCmd) { + fmt.Printf(" [cmdGetConnStatus]\r\n") + } + status := w.getUint8(w.req0(cmdGetConnStatus)) + return connectionStatus(status) } -func (d *Device) Ping(ip IPAddress, ttl uint8) int16 { - return 0 +func (w *wifinina) getCurrentencryptionType() encryptionType { + enctype := w.getUint8(w.req1(cmdGetCurrEncrType)) + return encryptionType(enctype) } -func (d *Device) SetDebug(on bool) error { - var v uint8 - if on { - v = 1 - } - _, err := d.reqUint8(CmdSetDebug, v) - return err +func (w *wifinina) getCurrentBSSID() macAddress { + return w.getMACAddress(w.req1(cmdGetCurrBSSID)) } -func (d *Device) SetNetwork(ssid string) error { - _, err := d.reqStr(CmdSetNet, ssid) - return err +func (w *wifinina) getCurrentRSSI() int32 { + return w.getInt32(w.req1(cmdGetCurrRSSI)) } -func (d *Device) SetPassphrase(ssid string, passphrase string) error { - _, err := d.reqStr2(CmdSetPassphrase, ssid, passphrase) - return err +func (w *wifinina) getCurrentSSID() string { + return w.getString(w.req1(cmdGetCurrSSID)) } -func (d *Device) SetKey(ssid string, index uint8, key string) error { - defer d.spiChipDeselect() - if err := d.waitForChipSelect(); err != nil { - return err +func (w *wifinina) getMACAddr() macAddress { + if debugging(debugCmd) { + fmt.Printf(" [cmdGetMACAddr]\r\n") } + return w.getMACAddress(w.req1(cmdGetMACAddr)) +} - d.sendCmd(CmdSetKey, 3) - d.sendParamStr(ssid, false) - d.sendParam8(index, false) - d.sendParamStr(key, true) +func (w *wifinina) faultf(f string, args ...any) { + // Only record the first fault + if w.fault == nil { + w.fault = fmt.Errorf(f, args...) + } +} - d.padTo4(8 + len(ssid) + len(key)) +func (w *wifinina) getIP() (ip, subnet, gateway netdev.IP) { + 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 + } + copy(ip[:], sl[0]) + copy(subnet[:], sl[1]) + copy(gateway[:], sl[2]) + return +} - _, err := d.waitRspCmd1(CmdSetKey) - if err != nil { - return err +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)) +} - return nil +func (w *wifinina) getNetworkBSSID(idx int) macAddress { + if idx < 0 || idx >= maxNetworks { + return macAddress{} + } + return w.getMACAddress(w.reqUint8(cmdGetIdxBSSID, uint8(idx))) } -func (d *Device) SetNetworkForAP(ssid string) error { - _, err := d.reqStr(CmdSetAPNet, ssid) - return err +func (w *wifinina) getNetworkChannel(idx int) uint8 { + if idx < 0 || idx >= maxNetworks { + return 0 + } + return w.getUint8(w.reqUint8(cmdGetIdxChannel, uint8(idx))) } -func (d *Device) SetPassphraseForAP(ssid string, passphrase string) error { - _, err := d.reqStr2(CmdSetAPPassphrase, ssid, passphrase) - return err +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 (d *Device) SetIP(which uint8, ip uint32, gw uint32, subnet uint32) error { - return ErrNotImplemented +func (w *wifinina) getNetworkRSSI(idx int) int32 { + if idx < 0 || idx >= maxNetworks { + return 0 + } + return w.getInt32(w.reqUint8(cmdGetIdxRSSI, uint8(idx))) } -func (d *Device) SetDNS(which uint8, dns1 uint32, dns2 uint32) error { - defer d.spiChipDeselect() - if err := d.waitForChipSelect(); err != nil { - return err +func (w *wifinina) getNetworkSSID(idx int) string { + if idx < 0 || idx >= maxNetworks { + return "" } + return w.ssids[idx] +} - d.sendCmd(CmdSetDNSConfig, 3) - d.sendParam8(which, false) - d.sendParam32(dns1, false) - d.sendParam32(dns2, true) +func (w *wifinina) getReasonCode() uint8 { + return w.getUint8(w.req0(cmdGetReasonCode)) +} - _, err := d.waitRspCmd1(CmdSetDNSConfig) - if err != nil { - return err - } +// getTime is the time as a Unix timestamp +func (w *wifinina) getTime() uint32 { + return w.getUint32(w.req0(cmdGetTime)) +} - return nil +func (w *wifinina) getTemperature() float32 { + return w.getFloat32(w.req0(cmdGetTemperature)) } -func (d *Device) SetHostname(hostname string) error { - defer d.spiChipDeselect() - if err := d.waitForChipSelect(); err != nil { - return err +func (w *wifinina) setDebug(on bool) { + var v uint8 + if on { + v = 1 } + w.reqUint8(cmdSetDebug, v) +} - d.sendCmd(CmdSetHostname, 3) - d.sendParamStr(hostname, true) +func (w *wifinina) setNetwork(ssid string) { + w.reqStr(cmdSetNet, ssid) +} - d.padTo4(5 + len(hostname)) +func (w *wifinina) setPassphrase(ssid string, passphrase string) { - _, err := d.waitRspCmd1(CmdSetHostname) - if err != nil { - return err + if debugging(debugCmd) { + fmt.Printf(" [cmdSetPassphrase] ssid: %s, passphrase: ******\r\n", + ssid) } - return nil + // Dont' show passphrase in debug output + saveDebug := _debug + _debug = _debug & ^debugDetail + w.reqStr2(cmdSetPassphrase, ssid, passphrase) + _debug = saveDebug } -func (d *Device) SetPowerMode(mode uint8) error { - _, err := d.reqUint8(CmdSetPowerMode, mode) - return err +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 (d *Device) ScanNetworks() (uint8, error) { - return d.reqRspStr0(CmdScanNetworks, d.ssids[:]) +func (w *wifinina) setNetworkForAP(ssid string) { + w.reqStr(cmdSetAPNet, ssid) } -func (d *Device) StartScanNetworks() (uint8, error) { - return d.getUint8(d.req0(CmdStartScanNetworks)) +func (w *wifinina) setPassphraseForAP(ssid string, passphrase string) { + w.reqStr2(cmdSetAPPassphrase, ssid, passphrase) } -func (d *Device) PinMode(pin uint8, mode uint8) error { - _, err := d.req2Uint8(CmdSetPinMode, pin, mode) - return err +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 (d *Device) DigitalWrite(pin uint8, value uint8) error { - _, err := d.req2Uint8(CmdSetDigitalWrite, pin, value) - return err +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 (d *Device) AnalogWrite(pin uint8, value uint8) error { - _, err := d.req2Uint8(CmdSetAnalogWrite, pin, value) - return err +func (w *wifinina) setPowerMode(mode uint8) { + w.reqUint8(cmdSetPowerMode, mode) } -// ------------- End of public device interface ---------------------------- +func (w *wifinina) scanNetworks() uint8 { + return w.reqRspStr0(cmdScanNetworks, w.ssids[:]) +} -func (d *Device) getString(l uint8, err error) (string, error) { - if err != nil { - return "", err - } - return string(d.buf[0:l]), err +func (w *wifinina) startScanNetworks() uint8 { + return w.getUint8(w.req0(cmdStartScanNetworks)) } -func (d *Device) getUint8(l uint8, err error) (uint8, error) { - if err != nil { - return 0, err - } - if l != 1 { - if _debug { - println("expected length 1, was actually", l, "\r") - } - return 0, ErrUnexpectedLength +func (w *wifinina) PinMode(pin uint8, mode uint8) { + if debugging(debugCmd) { + fmt.Printf(" [cmdSetPinMode] pin: %d, mode: %d\r\n", pin, mode) } - return d.buf[0], err + w.req2Uint8(cmdSetPinMode, pin, mode) } -func (d *Device) getUint16(l uint8, err error) (uint16, error) { - if err != nil { - return 0, err +func (w *wifinina) DigitalWrite(pin uint8, value uint8) { + if debugging(debugCmd) { + fmt.Printf(" [cmdSetDigitialWrite] pin: %d, value: %d\r\n", pin, value) } - if l != 2 { - if _debug { - println("expected length 2, was actually", l, "\r") - } - return 0, ErrUnexpectedLength + 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] } - return binary.BigEndian.Uint16(d.buf[0:2]), err + w.faultf("expected length 1, was actually %d", l) + return 0 } -func (d *Device) getUint32(l uint8, err error) (uint32, error) { - if err != nil { - return 0, err +func (w *wifinina) getUint16(l uint8) uint16 { + if l == 2 { + return binary.BigEndian.Uint16(w.buf[0:2]) } - if l != 4 { - return 0, ErrUnexpectedLength + 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]) } - return binary.LittleEndian.Uint32(d.buf[0:4]), err + w.faultf("expected length 4, was actually %d", l) + return 0 } -func (d *Device) getInt32(l uint8, err error) (int32, error) { - i, err := d.getUint32(l, err) - return int32(i), err +func (w *wifinina) getInt32(l uint8) int32 { + return int32(w.getUint32(l)) } -func (d *Device) getFloat32(l uint8, err error) (float32, error) { - i, err := d.getUint32(l, err) - return float32(i), err +func (w *wifinina) getFloat32(l uint8) float32 { + return float32(w.getUint32(l)) } -func (d *Device) getMACAddress(l uint8, err error) (MACAddress, error) { - if err != nil { - return 0, err +func (w *wifinina) getMACAddress(l uint8) macAddress { + 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 } - if l != 6 { - return 0, ErrUnexpectedLength + w.faultf("expected length 6, was actually %d", l) + return macAddress{} +} + +func (w *wifinina) transfer(b byte) byte { + v, err := w.spi.Transfer(b) + if err != nil { + w.faultf("SPI.Transfer") + return 0 } - return MACAddress(binary.LittleEndian.Uint64(d.buf[0:8]) & 0xFFFFFFFFFFFF), err + 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 (d *Device) req0(cmd uint8) (l uint8, err error) { - if err := d.sendCmd0(cmd); err != nil { - return 0, err - } - return d.waitRspCmd1(cmd) +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 (d *Device) req1(cmd uint8) (l uint8, err error) { - return d.reqUint8(cmd, dummyData) +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 (d *Device) reqUint8(cmd uint8, data uint8) (l uint8, err error) { - if err := d.sendCmdPadded1(cmd, data); err != nil { - return 0, err - } - return d.waitRspCmd1(cmd) +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 (d *Device) req2Uint8(cmd, p1, p2 uint8) (l uint8, err error) { - if err := d.sendCmdPadded2(cmd, p1, p2); err != nil { - return 0, err - } - return d.waitRspCmd1(cmd) +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 (d *Device) reqStr(cmd uint8, p1 string) (uint8, error) { - if err := d.sendCmdStr(cmd, p1); err != nil { - return 0, err - } - return d.waitRspCmd1(cmd) +func (w *wifinina) reqStr(cmd uint8, p1 string) uint8 { + w.sendCmdStr(cmd, p1) + return w.waitRspCmd1(cmd) } -// reqStr sends a command to the device with 2 string parameters -func (d *Device) reqStr2(cmd uint8, p1 string, p2 string) (uint8, error) { - if err := d.sendCmdStr2(cmd, p1, p2); err != nil { - return 0, err - } - return d.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 (d *Device) reqRspStr0(cmd uint8, sl []string) (l uint8, err error) { - if err := d.sendCmd0(cmd); err != nil { - return 0, err - } - defer d.spiChipDeselect() - if err = d.waitForChipSelect(); err != nil { - return - } - return d.waitRspStr(cmd, sl) +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 (d *Device) reqRspStr1(cmd uint8, data uint8, sl []string) (uint8, error) { - if err := d.sendCmdPadded1(cmd, data); err != nil { - return 0, err - } - defer d.spiChipDeselect() - if err := d.waitForChipSelect(); err != nil { - return 0, err - } - return d.waitRspStr(cmd, sl) -} - -func (d *Device) sendCmd0(cmd uint8) error { - defer d.spiChipDeselect() - if err := d.waitForChipSelect(); err != nil { - return err - } - d.sendCmd(cmd, 0) - return nil +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 (d *Device) sendCmdPadded1(cmd uint8, data uint8) error { - defer d.spiChipDeselect() - if err := d.waitForChipSelect(); err != nil { - return err - } - d.sendCmd(cmd, 1) - d.sendParam8(data, true) - d.SPI.Transfer(dummyData) - d.SPI.Transfer(dummyData) - return nil +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 (d *Device) sendCmdPadded2(cmd, data1, data2 uint8) error { - defer d.spiChipDeselect() - if err := d.waitForChipSelect(); err != nil { - return err - } - l := d.sendCmd(cmd, 1) - l += d.sendParam8(data1, false) - l += d.sendParam8(data2, true) - d.SPI.Transfer(dummyData) - return nil +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 (d *Device) sendCmdStr(cmd uint8, p1 string) (err error) { - defer d.spiChipDeselect() - if err := d.waitForChipSelect(); err != nil { - return err - } - l := d.sendCmd(cmd, 1) - l += d.sendParamStr(p1, true) - d.padTo4(5 + len(p1)) - return nil +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 (d *Device) sendCmdStr2(cmd uint8, p1 string, p2 string) (err error) { - defer d.spiChipDeselect() - if err := d.waitForChipSelect(); err != nil { - return err - } - d.sendCmd(cmd, 2) - d.sendParamStr(p1, false) - d.sendParamStr(p2, true) - d.padTo4(6 + len(p1) + len(p2)) - return nil +func (w *wifinina) waitRspCmd1(cmd uint8) uint8 { + w.waitForChipReady() + w.spiChipSelect() + l := w.waitRspCmd(cmd, 1) + w.spiChipDeselect() + return l } -func (d *Device) waitRspCmd1(cmd uint8) (l uint8, err error) { - defer d.spiChipDeselect() - if err = d.waitForChipSelect(); err != nil { - return +func (w *wifinina) sendCmd(cmd uint8, numParam uint8) (l int) { + if debugging(debugDetail) { + fmt.Printf(" sendCmd: %02X %02X %02X", + cmdStart, cmd & ^(uint8(flagReply)), numParam) } - return d.waitRspCmd(cmd, 1) -} -func (d *Device) sendCmd(cmd uint8, numParam uint8) (l int) { - if _debug { - fmt.Printf( - "sendCmd: %02X %02X %02X", - CmdStart, cmd & ^(uint8(FlagReply)), numParam) - } l = 3 - d.SPI.Transfer(CmdStart) - d.SPI.Transfer(cmd & ^(uint8(FlagReply))) - d.SPI.Transfer(numParam) + w.transfer(cmdStart) + w.transfer(cmd & ^(uint8(flagReply))) + w.transfer(numParam) if numParam == 0 { - d.SPI.Transfer(CmdEnd) + w.transfer(cmdEnd) l += 1 - if _debug { - fmt.Printf(" %02X", CmdEnd) + if debugging(debugDetail) { + fmt.Printf(" %02X", cmdEnd) } } - if _debug { + + if debugging(debugDetail) { fmt.Printf(" (%d)\r\n", l) } return } -func (d *Device) sendParamLen16(p uint16) (l int) { - d.SPI.Transfer(uint8(p >> 8)) - d.SPI.Transfer(uint8(p & 0xFF)) - if _debug { - fmt.Printf(" %02X %02X", uint8(p>>8), uint8(p&0xFF)) +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 (d *Device) sendParamBuf(p []byte, isLastParam bool) (l int) { - if _debug { - println("sendParamBuf:") +func (w *wifinina) sendParamBuf(p []byte, isLastParam bool) (l int) { + if debugging(debugDetail) { + fmt.Printf(" sendParamBuf:") } - l += d.sendParamLen16(uint16(len(p))) + l += w.sendParamLen16(uint16(len(p))) for _, b := range p { - if _debug { + if debugging(debugDetail) { fmt.Printf(" %02X", b) } - d.SPI.Transfer(b) + w.transfer(b) l += 1 } if isLastParam { - if _debug { - fmt.Printf(" %02X", CmdEnd) + if debugging(debugDetail) { + fmt.Printf(" %02X", cmdEnd) } - d.SPI.Transfer(CmdEnd) + w.transfer(cmdEnd) l += 1 } - if _debug { + if debugging(debugDetail) { fmt.Printf(" (%d) \r\n", l) } return } -func (d *Device) sendParamStr(p string, isLastParam bool) (l int) { +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) - d.SPI.Transfer(uint8(l)) + w.transfer(uint8(l)) if l > 0 { - d.SPI.Tx([]byte(p), nil) + w.spi.Tx([]byte(p), nil) } if isLastParam { - d.SPI.Transfer(CmdEnd) + w.transfer(cmdEnd) l += 1 } return } -func (d *Device) sendParam8(p uint8, isLastParam bool) (l int) { - if _debug { - println("sendParam8:", p, "lastParam:", isLastParam, "\r") +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 - d.SPI.Transfer(1) - d.SPI.Transfer(p) + w.transfer(1) + w.transfer(p) if isLastParam { - d.SPI.Transfer(CmdEnd) + w.transfer(cmdEnd) l += 1 } return } -func (d *Device) sendParam16(p uint16, isLastParam bool) (l int) { +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 - d.SPI.Transfer(2) - d.SPI.Transfer(uint8(p >> 8)) - d.SPI.Transfer(uint8(p & 0xFF)) + w.transfer(2) + w.transfer(uint8(p >> 8)) + w.transfer(uint8(p & 0xFF)) if isLastParam { - d.SPI.Transfer(CmdEnd) + w.transfer(cmdEnd) l += 1 } return } -func (d *Device) sendParam32(p uint32, isLastParam bool) (l int) { +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 - d.SPI.Transfer(4) - d.SPI.Transfer(uint8(p >> 24)) - d.SPI.Transfer(uint8(p >> 16)) - d.SPI.Transfer(uint8(p >> 8)) - d.SPI.Transfer(uint8(p & 0xFF)) + 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 { - d.SPI.Transfer(CmdEnd) + w.transfer(cmdEnd) l += 1 } return } -func (d *Device) checkStartCmd() (bool, error) { - check, err := d.waitSpiChar(CmdStart) - if err != nil { - return false, err - } - if !check { - return false, ErrCheckStartCmd +func (w *wifinina) waitForChipReady() { + if debugging(debugDetail) { + fmt.Printf(" waitForChipReady\r\n") } - return true, nil -} -func (d *Device) waitForChipSelect() (err error) { - if err = d.waitForChipReady(); err == nil { - err = d.spiChipSelect() - } - return -} - -func (d *Device) waitForChipReady() error { - if _debug { - println("waitForChipReady()\r") - } - start := time.Now() - for time.Since(start) < 10*time.Second { - if !d.ACK.Get() { - return nil - } + for i := 0; w.ack.Get(); i++ { time.Sleep(1 * time.Millisecond) + if i == 10000 { + w.faultf("hung in waitForChipReady") + return + } } - return ErrTimeoutChipReady } -func (d *Device) spiChipSelect() error { - if _debug { - println("spiChipSelect()\r") +func (w *wifinina) spiChipSelect() { + if debugging(debugDetail) { + fmt.Printf(" spiChipSelect\r\n") } - d.CS.Low() + w.cs.Low() start := time.Now() for time.Since(start) < 5*time.Millisecond { - if d.ACK.Get() { - return nil + if w.ack.Get() { + return } time.Sleep(100 * time.Microsecond) } - return ErrTimeoutChipSelect + w.faultf("hung in spiChipSelect") } -func (d *Device) spiChipDeselect() { - if _debug { - println("spiChipDeselect\r") +func (w *wifinina) spiChipDeselect() { + if debugging(debugDetail) { + fmt.Printf(" spiChipDeselect\r\n") } - d.CS.High() + w.cs.High() } -func (d *Device) waitSpiChar(wait byte) (bool, error) { - var timeout = 1000 +func (w *wifinina) waitSpiChar(wait byte) { + + if debugging(debugDetail) { + fmt.Printf(" waitSpiChar: wait: %02X\r\n", wait) + } + var read byte - for first := true; first || (timeout > 0 && read != wait); timeout-- { - first = false - d.readParam(&read) - if read == CmdErr { - return false, ErrCmdErrorReceived + + for timeout := 1000; read != wait && timeout > 0; timeout-- { + w.readParam(&read) + if read == cmdErr { + w.faultf("cmdErr received, waiting for %d", wait) + return } } - if _debug && read != wait { - fmt.Printf("read: %02X, wait: %02X\r\n", read, wait) + + if read != wait { + w.faultf("timeout waiting for SPI char %02X\r\n", wait) } - return read == wait, nil } -func (d *Device) waitRspCmd(cmd uint8, np uint8) (l uint8, err error) { - if _debug { - println("waitRspCmd") +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 check bool + var data byte - if check, err = d.checkStartCmd(); !check { - return - } - if check = d.readAndCheckByte(cmd|FlagReply, &data); !check { + + w.waitSpiChar(cmdStart) + + if !w.readAndCheckByte(cmd|flagReply, &data) { + w.faultf("expected cmd %02X, read %02X", cmd, data) return } - if check = d.readAndCheckByte(np, &data); check { - d.readParam(&l) + + if w.readAndCheckByte(np, &data) { + + w.readParam(&l) for i := uint8(0); i < l; i++ { - d.readParam(&d.buf[i]) + w.readParam(&w.buf[i]) + } + + if !w.readAndCheckByte(cmdEnd, &data) { + w.faultf("expected cmdEnd, read %02X", data) } } - if !d.readAndCheckByte(CmdEnd, &data) { - err = ErrIncorrectSentinel - } + return } -func (d *Device) waitRspBuf16(cmd uint8, buf []byte) (l uint16, err error) { - if _debug { - println("waitRspBuf16") +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 check bool + var data byte - if check, err = d.checkStartCmd(); !check { - return - } - if check = d.readAndCheckByte(cmd|FlagReply, &data); !check { + + w.waitSpiChar(cmdStart) + + if !w.readAndCheckByte(cmd|flagReply, &data) { + w.faultf("expected cmd %02X, read %02X", cmd, data) return } - if check = d.readAndCheckByte(1, &data); check { - l, _ = d.readParamLen16() + + if w.readAndCheckByte(1, &data) { + l = w.readParamLen16() for i := uint16(0); i < l; i++ { - d.readParam(&buf[i]) + w.readParam(&buf[i]) + } + if !w.readAndCheckByte(cmdEnd, &data) { + w.faultf("expected cmdEnd, read %02X", data) } } - if !d.readAndCheckByte(CmdEnd, &data) { - err = ErrIncorrectSentinel - } + return } -func (d *Device) waitRspStr(cmd uint8, sl []string) (numRead uint8, err error) { - if _debug { - println("waitRspStr") +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 check bool + var data byte - if check, err = d.checkStartCmd(); !check { - return - } - if check = d.readAndCheckByte(cmd|FlagReply, &data); !check { + + w.waitSpiChar(cmdStart) + + if !w.readAndCheckByte(cmd|flagReply, &data) { + w.faultf("expected cmd %02X, read %02X", cmd, data) return } - numRead, _ = d.SPI.Transfer(dummyData) + + numRead = w.transfer(dummyData) if numRead == 0 { - return 0, ErrNoParamsReturned + w.faultf("waitRspStr numRead == 0") + return } + maxNumRead := uint8(len(sl)) for j, l := uint8(0), uint8(0); j < numRead; j++ { - d.readParam(&l) + w.readParam(&l) for i := uint8(0); i < l; i++ { - d.readParam(&d.buf[i]) + w.readParam(&w.buf[i]) } if j < maxNumRead { - sl[j] = string(d.buf[0:l]) - if _debug { - fmt.Printf("str %d (%d) - %08X\r\n", j, l, []byte(sl[j])) + 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 _debug { - println("str", j, "\"\"\r") + if debugging(debugDetail) { + fmt.Printf(" str: ", j, "\"\"\r") } sl[j] = "" } - if !d.readAndCheckByte(CmdEnd, &data) { - err = ErrIncorrectSentinel + + if !w.readAndCheckByte(cmdEnd, &data) { + w.faultf("expected cmdEnd, read %02X", data) + return } + if numRead > maxNumRead { numRead = maxNumRead } return } -func (d *Device) readAndCheckByte(check byte, read *byte) bool { - d.readParam(read) - return (*read == check) +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 (d *Device) readParamLen16() (v uint16, err error) { - if b, err := d.SPI.Transfer(0xFF); err == nil { - v |= uint16(b) << 8 - if b, err = d.SPI.Transfer(0xFF); err == nil { - v |= uint16(b) - } - } +func (w *wifinina) readParamLen16() (v uint16) { + b := w.transfer(0xFF) + v = uint16(b) << 8 + b = w.transfer(0xFF) + v |= uint16(b) return } -func (d *Device) readParam(b *byte) (err error) { - *b, err = d.SPI.Transfer(0xFF) - return +func (w *wifinina) readParam(b *byte) { + *b = w.transfer(0xFF) } -func (d *Device) addPadding(l int) { - if _debug { - println("addPadding", l, "\r") +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 _debug { - println("padding\r") + if debugging(debugDetail) { + fmt.Printf(" padding\r\n") } - d.SPI.Transfer(dummyData) + w.transfer(dummyData) } } -func (d *Device) padTo4(l int) { - if _debug { - println("padTo4", l, "\r") +func (w *wifinina) padTo4(l int) { + if debugging(debugDetail) { + fmt.Printf(" padTo4: l: %d\r\n", l) } for l%4 != 0 { - d.SPI.Transfer(dummyData) + if debugging(debugDetail) { + fmt.Printf(" padding\r\n") + } + w.transfer(dummyData) l++ } } From c7effd97c9c35d925dbbb6c02ea688f2c4e0e359 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Fri, 10 Feb 2023 00:37:24 -0800 Subject: [PATCH 28/47] websocket/dial: remove work-around-code for no-RNG on rp2040 --- examples/net/websocket/dial/main.go | 30 ----------------------------- 1 file changed, 30 deletions(-) diff --git a/examples/net/websocket/dial/main.go b/examples/net/websocket/dial/main.go index 72be95749..c8cf2f00a 100644 --- a/examples/net/websocket/dial/main.go +++ b/examples/net/websocket/dial/main.go @@ -9,7 +9,6 @@ package main import ( - // "crypto/rand" "fmt" "log" "machine" @@ -31,35 +30,6 @@ func waitSerial() { } } -/* -func init() { - rand.Reader = &reader{} -} - -type reader struct {} - -func (r *reader) Read(b []byte) (n int, err error) { - if len(b) == 0 { - return - } - - var randomByte uint32 - for i := range b { - if i%4 == 0 { - randomByte, err = machine.GetRNG() - if err != nil { - return n, err - } - } else { - randomByte >>= 8 - } - b[i] = byte(randomByte) - } - - return len(b), nil -} -*/ - func main() { waitSerial() From a5b8f66faf07cd1759a2f5a60d9311c5ba22afa3 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Fri, 10 Feb 2023 13:17:34 -0800 Subject: [PATCH 29/47] Documentation cleanup --- netdev/netdev.go | 8 ++++++-- netdev/socket.go | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/netdev/netdev.go b/netdev/netdev.go index 9e12a785d..3c22c835b 100644 --- a/netdev/netdev.go +++ b/netdev/netdev.go @@ -1,4 +1,6 @@ -// Netdev is TinyGo's network device driver model. +// Netdev is TinyGo's network device driver model. TinyGo's "net" package +// interfaces to the netdev driver directly to provide TCPConn, UDPConn, +// and TLSConn socket connections. package netdev @@ -96,7 +98,9 @@ type Event int // NetNotify network events const ( + // The device's network connection is now UP EventNetUp Event = iota + // The device's network connection is now DOWN EventNetDown ) @@ -111,7 +115,7 @@ type Netdever interface { // NetConnect device to IP network NetConnect() error - // NetDisconnect from IP network + // NetDisconnect device from IP network NetDisconnect() // NetNotify to register callback for network events diff --git a/netdev/socket.go b/netdev/socket.go index 3b3d2dd9f..2f3e112a4 100644 --- a/netdev/socket.go +++ b/netdev/socket.go @@ -1,3 +1,5 @@ +// Netdev socket interface + package netdev import ( From 1fe160125ac40c48f723898a43a8f3533af4ed0b Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Fri, 10 Feb 2023 14:21:13 -0800 Subject: [PATCH 30/47] add raw socket example --- examples/net/rawsocket/main.go | 101 ++++++++++++++++++++++++++++ examples/net/rawsocket/rtl8720dn.go | 37 ++++++++++ examples/net/rawsocket/wifinina.go | 41 +++++++++++ 3 files changed, 179 insertions(+) create mode 100644 examples/net/rawsocket/main.go create mode 100644 examples/net/rawsocket/rtl8720dn.go create mode 100644 examples/net/rawsocket/wifinina.go diff --git a/examples/net/rawsocket/main.go b/examples/net/rawsocket/main.go new file mode 100644 index 000000000..92f6e04f4 --- /dev/null +++ b/examples/net/rawsocket/main.go @@ -0,0 +1,101 @@ +// This example opens a TCP connection and sends some data, for the purpose of +// testing speed and connectivity. +// +// You can open a server to accept connections from this program using: +// +// nc -lk 8080 + +package main + +import ( + "bytes" + "fmt" + "log" + "machine" + "strconv" + "strings" + "time" + + "tinygo.org/x/drivers/netdev" +) + +var ( + ssid string + pass string + addr string = "10.0.0.100:8080" +) + +var buf = &bytes.Buffer{} + +func main() { + + waitSerial() + + if err := NetConnect(); err != nil { + log.Fatal(err) + } + + for { + sendBatch() + time.Sleep(500 * time.Millisecond) + } +} + +func sendBatch() { + + parts := strings.Split(addr, ":") + ip := netdev.ParseIP(parts[0]) + port, _ := strconv.Atoi(parts[1]) + sockAddr := netdev.NewSockAddr("", netdev.Port(port), ip) + + // make TCP connection + message("---------------\r\nDialing TCP connection") + sock, _ := dev.Socket(netdev.AF_INET, netdev.SOCK_STREAM, netdev.IPPROTO_TCP) + err := dev.Connect(sock, sockAddr) + for ; err != nil; err = dev.Connect(sock, sockAddr) { + message(err.Error()) + time.Sleep(5 * time.Second) + } + + n := 0 + w := 0 + start := time.Now() + + // send data + message("Sending data") + + for i := 0; i < 1000; i++ { + buf.Reset() + fmt.Fprint(buf, + "\r---------------------------- i == ", i, " ----------------------------"+ + "\r---------------------------- i == ", i, " ----------------------------") + if w, err = dev.Send(sock, buf.Bytes(), 0, 0); err != nil { + println("error:", err.Error(), "\r") + break + } + n += w + } + + buf.Reset() + ms := time.Now().Sub(start).Milliseconds() + fmt.Fprint(buf, "\nWrote ", n, " bytes in ", ms, " ms\r\n") + message(buf.String()) + + if _, err := dev.Send(sock, buf.Bytes(), 0, 0); err != nil { + println("error:", err.Error(), "\r") + } + + println("Disconnecting TCP...") + dev.Close(sock) +} + +func message(msg string) { + println(msg, "\r") +} + +// Wait for user to open serial console +func waitSerial() { + for !machine.Serial.DTR() { + time.Sleep(100 * time.Millisecond) + } +} diff --git a/examples/net/rawsocket/rtl8720dn.go b/examples/net/rawsocket/rtl8720dn.go new file mode 100644 index 000000000..a6dcb77fa --- /dev/null +++ b/examples/net/rawsocket/rtl8720dn.go @@ -0,0 +1,37 @@ +//go:build wioterminal + +// +build: wioterminal + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/rtl8720dn" +) + +var cfg = rtl8720dn.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Device + En: machine.RTL8720D_CHIP_PU, + // UART + Uart: machine.UART3, + Tx: machine.PB24, + Rx: machine.PC24, + Baudrate: 614400, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = rtl8720dn.New(&cfg) + +func NetConnect() error { + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} diff --git a/examples/net/rawsocket/wifinina.go b/examples/net/rawsocket/wifinina.go new file mode 100644 index 000000000..1011d75cf --- /dev/null +++ b/examples/net/rawsocket/wifinina.go @@ -0,0 +1,41 @@ +//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 + +// +build: pyportal arduino_nano33 nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/wifinina" +) + +var cfg = wifinina.Config{ + // WiFi AP credentials + Ssid: ssid, + Passphrase: pass, + // Configure SPI for 8Mhz, Mode 0, MSB First + Spi: machine.NINA_SPI, + Freq: 8 * 1e6, + Sdo: machine.NINA_SDO, + Sdi: machine.NINA_SDI, + Sck: machine.NINA_SCK, + // Device pins + Cs: machine.NINA_CS, + Ack: machine.NINA_ACK, + Gpio0: machine.NINA_GPIO0, + Resetn: machine.NINA_RESETN, + // Watchdog (set to 0 to disable) + WatchdogTimeo: time.Duration(20 * time.Second), +} + +var dev = wifinina.New(&cfg) + +func NetConnect() error { + return dev.NetConnect() +} + +func NetDisconnect() { + dev.NetDisconnect() +} From 1030a1d96afed77b44ba1fea841010945dcafea1 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Mon, 13 Feb 2023 13:28:36 -0800 Subject: [PATCH 31/47] fix header comment on raw socket test --- examples/net/rawsocket/main.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/net/rawsocket/main.go b/examples/net/rawsocket/main.go index 92f6e04f4..d502a9499 100644 --- a/examples/net/rawsocket/main.go +++ b/examples/net/rawsocket/main.go @@ -1,5 +1,4 @@ -// This example opens a TCP connection and sends some data, for the purpose of -// testing speed and connectivity. +// This example opens a TCP connection and sends some data using raw netdev sockets. // // You can open a server to accept connections from this program using: // From 6304deef4a4f9940002c9ef4d11b8ada3fed92d3 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Mon, 13 Feb 2023 13:39:58 -0800 Subject: [PATCH 32/47] gofmt -l -w -s --- espat/espat.go | 12 ++++++------ netdev/netdev.go | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/espat/espat.go b/espat/espat.go index c24731f76..e949db96e 100644 --- a/espat/espat.go +++ b/espat/espat.go @@ -23,8 +23,8 @@ package espat // import "tinygo.org/x/drivers/espat" import ( "errors" "fmt" - "strconv" "machine" + "strconv" "strings" "time" @@ -48,21 +48,21 @@ type Config struct { } type Device struct { - cfg *Config + cfg *Config uart *machine.UART // command responses that come back from the ESP8266/ESP32 response []byte // data received from a TCP/UDP connection forwarded by the ESP8266/ESP32 - socketdata []byte - socketInUse bool + socketdata []byte + socketInUse bool socketProtocol netdev.Protocol socketLaddr netdev.SockAddr } func New(cfg *Config) *Device { d := Device{ - cfg: cfg, - response: make([]byte, 512), + cfg: cfg, + response: make([]byte, 512), socketdata: make([]byte, 0, 1024), } return &d diff --git a/netdev/netdev.go b/netdev/netdev.go index 3c22c835b..4527c62f9 100644 --- a/netdev/netdev.go +++ b/netdev/netdev.go @@ -99,7 +99,7 @@ type Event int // NetNotify network events const ( // The device's network connection is now UP - EventNetUp Event = iota + EventNetUp Event = iota // The device's network connection is now DOWN EventNetDown ) From 521251ac9afb52bfebd886182977205eb9852ead Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Mon, 13 Feb 2023 22:14:49 -0800 Subject: [PATCH 33/47] rtl8720dn: pull in latest rpc.go from sago35/go-erpcgen --- rtl8720dn/rpc.go | 150 +++++++++++++++++++++++------------------------ 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/rtl8720dn/rpc.go b/rtl8720dn/rpc.go index fc7b2f833..b554e8b0f 100644 --- a/rtl8720dn/rpc.go +++ b/rtl8720dn/rpc.go @@ -464,14 +464,14 @@ func (r *rtl8720dn) rpc_le_bond_delete_by_idx(idx uint8) RPC_T_GAP_CAUSE { return result } -func (r *rtl8720dn) rpc_le_bond_delete_by_bd(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) RPC_T_GAP_CAUSE { +func (r *rtl8720dn) rpc_le_bond_delete_by_bd(bd_addr []uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_bond_delete_by_bd()\r\n") } msg := startWriteMessage(0x00, 0x04, 0x0D, uint32(r.seq)) - // bd_addr : in uint8 - msg = append(msg, byte(bd_addr>>0)) + // bd_addr : in []uint8 (6) + msg = append(msg, bd_addr...) // bd_type : in RPC_T_GAP_REMOTE_ADDR_TYPE msg = append(msg, byte(bd_type>>0)) msg = append(msg, byte(bd_type>>8)) @@ -630,7 +630,7 @@ func (r *rtl8720dn) rpc_le_get_gap_param(param RPC_T_GAP_LE_PARAM_TYPE, value [] return result } -func (r *rtl8720dn) rpc_le_modify_white_list(operation RPC_T_GAP_WHITE_LIST_OP, bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) RPC_T_GAP_CAUSE { +func (r *rtl8720dn) rpc_le_modify_white_list(operation RPC_T_GAP_WHITE_LIST_OP, bd_addr []uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_modify_white_list()\r\n") } @@ -641,8 +641,8 @@ func (r *rtl8720dn) rpc_le_modify_white_list(operation RPC_T_GAP_WHITE_LIST_OP, msg = append(msg, byte(operation>>8)) msg = append(msg, byte(operation>>16)) msg = append(msg, byte(operation>>24)) - // bd_addr : in uint8 - msg = append(msg, byte(bd_addr>>0)) + // bd_addr : in []uint8 (6) + msg = append(msg, bd_addr...) // bd_type : in RPC_T_GAP_REMOTE_ADDR_TYPE msg = append(msg, byte(bd_type>>0)) msg = append(msg, byte(bd_type>>8)) @@ -661,7 +661,7 @@ func (r *rtl8720dn) rpc_le_modify_white_list(operation RPC_T_GAP_WHITE_LIST_OP, return result } -func (r *rtl8720dn) rpc_le_gen_rand_addr(rand_addr_type RPC_T_GAP_RAND_ADDR_TYPE, random_bd *uint8) RPC_T_GAP_CAUSE { +func (r *rtl8720dn) rpc_le_gen_rand_addr(rand_addr_type RPC_T_GAP_RAND_ADDR_TYPE, random_bd []uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_gen_rand_addr()\r\n") } @@ -677,9 +677,9 @@ func (r *rtl8720dn) rpc_le_gen_rand_addr(rand_addr_type RPC_T_GAP_RAND_ADDR_TYPE r.read() widx := 8 - // random_bd : out uint8 - *random_bd = payload[widx] - widx += 1 + // random_bd : out []uint8 (6) + copy(random_bd, payload[widx:widx+6]) + widx += 6 var result RPC_T_GAP_CAUSE result = RPC_T_GAP_CAUSE(binary.LittleEndian.Uint32(payload[widx:])) @@ -688,14 +688,14 @@ func (r *rtl8720dn) rpc_le_gen_rand_addr(rand_addr_type RPC_T_GAP_RAND_ADDR_TYPE return result } -func (r *rtl8720dn) rpc_le_set_rand_addr(random_bd uint8) RPC_T_GAP_CAUSE { +func (r *rtl8720dn) rpc_le_set_rand_addr(random_bd []uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_set_rand_addr()\r\n") } msg := startWriteMessage(0x00, 0x05, 0x08, uint32(r.seq)) - // random_bd : in uint8 - msg = append(msg, byte(random_bd>>0)) + // random_bd : in []uint8 (6) + msg = append(msg, random_bd...) r.performRequest(msg) @@ -709,14 +709,14 @@ func (r *rtl8720dn) rpc_le_set_rand_addr(random_bd uint8) RPC_T_GAP_CAUSE { return result } -func (r *rtl8720dn) rpc_le_cfg_local_identity_address(addr uint8, ident_addr_type RPC_T_GAP_IDENT_ADDR_TYPE) RPC_T_GAP_CAUSE { +func (r *rtl8720dn) rpc_le_cfg_local_identity_address(addr []uint8, ident_addr_type RPC_T_GAP_IDENT_ADDR_TYPE) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_cfg_local_identity_address()\r\n") } msg := startWriteMessage(0x00, 0x05, 0x09, uint32(r.seq)) - // addr : in uint8 - msg = append(msg, byte(addr>>0)) + // addr : in []uint8 (6) + msg = append(msg, addr...) // ident_addr_type : in RPC_T_GAP_IDENT_ADDR_TYPE msg = append(msg, byte(ident_addr_type>>0)) msg = append(msg, byte(ident_addr_type>>8)) @@ -1166,7 +1166,7 @@ func (r *rtl8720dn) rpc_le_scan_stop() RPC_T_GAP_CAUSE { return result } -func (r *rtl8720dn) rpc_le_scan_info_filter(enable bool, offset uint8, length uint8, p_filter uint8) bool { +func (r *rtl8720dn) rpc_le_scan_info_filter(enable bool, offset uint8, length uint8, p_filter []uint8) bool { if r.debug { fmt.Printf("rpc_le_scan_info_filter()\r\n") } @@ -1182,8 +1182,8 @@ func (r *rtl8720dn) rpc_le_scan_info_filter(enable bool, offset uint8, length ui msg = append(msg, byte(offset>>0)) // length : in uint8 msg = append(msg, byte(length>>0)) - // p_filter : in uint8 - msg = append(msg, byte(p_filter>>0)) + // p_filter : in []uint8 (31) + msg = append(msg, p_filter...) r.performRequest(msg) @@ -1253,7 +1253,7 @@ func (r *rtl8720dn) rpc_le_get_conn_info(conn_id uint8, p_conn_info RPC_T_GAP_CO return result } -func (r *rtl8720dn) rpc_le_get_conn_addr(conn_id uint8, bd_addr *uint8, bd_type *uint8) bool { +func (r *rtl8720dn) rpc_le_get_conn_addr(conn_id uint8, bd_addr []uint8, bd_type *uint8) bool { if r.debug { fmt.Printf("rpc_le_get_conn_addr()\r\n") } @@ -1266,9 +1266,9 @@ func (r *rtl8720dn) rpc_le_get_conn_addr(conn_id uint8, bd_addr *uint8, bd_type r.read() widx := 8 - // bd_addr : out uint8 - *bd_addr = payload[widx] - widx += 1 + // bd_addr : out []uint8 (6) + copy(bd_addr, payload[widx:widx+6]) + widx += 6 // bd_type : out uint8 *bd_type = payload[widx] widx += 1 @@ -1280,14 +1280,14 @@ func (r *rtl8720dn) rpc_le_get_conn_addr(conn_id uint8, bd_addr *uint8, bd_type return result } -func (r *rtl8720dn) rpc_le_get_conn_id(bd_addr uint8, bd_type uint8, p_conn_id *uint8) bool { +func (r *rtl8720dn) rpc_le_get_conn_id(bd_addr []uint8, bd_type uint8, p_conn_id *uint8) bool { if r.debug { fmt.Printf("rpc_le_get_conn_id()\r\n") } msg := startWriteMessage(0x00, 0x09, 0x04, uint32(r.seq)) - // bd_addr : in uint8 - msg = append(msg, byte(bd_addr>>0)) + // bd_addr : in []uint8 (6) + msg = append(msg, bd_addr...) // bd_type : in uint8 msg = append(msg, byte(bd_type>>0)) @@ -1470,7 +1470,7 @@ func (r *rtl8720dn) rpc_le_set_conn_param(conn_type RPC_T_GAP_CONN_PARAM_TYPE, p return result } -func (r *rtl8720dn) rpc_le_connect(init_phys uint8, remote_bd uint8, remote_bd_type RPC_T_GAP_REMOTE_ADDR_TYPE, local_bd_type RPC_T_GAP_LOCAL_ADDR_TYPE, scan_timeout uint16) RPC_T_GAP_CAUSE { +func (r *rtl8720dn) rpc_le_connect(init_phys uint8, remote_bd []uint8, remote_bd_type RPC_T_GAP_REMOTE_ADDR_TYPE, local_bd_type RPC_T_GAP_LOCAL_ADDR_TYPE, scan_timeout uint16) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_le_connect()\r\n") } @@ -1478,8 +1478,8 @@ func (r *rtl8720dn) rpc_le_connect(init_phys uint8, remote_bd uint8, remote_bd_t // init_phys : in uint8 msg = append(msg, byte(init_phys>>0)) - // remote_bd : in uint8 - msg = append(msg, byte(remote_bd>>0)) + // remote_bd : in []uint8 (6) + msg = append(msg, remote_bd...) // remote_bd_type : in RPC_T_GAP_REMOTE_ADDR_TYPE msg = append(msg, byte(remote_bd_type>>0)) msg = append(msg, byte(remote_bd_type>>8)) @@ -1633,14 +1633,14 @@ func (r *rtl8720dn) rpc_flash_load_local_appearance(p_data RPC_T_LOCAL_APPEARANC return result } -func (r *rtl8720dn) rpc_le_find_key_entry(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) RPC_T_LE_KEY_ENTRY { +func (r *rtl8720dn) rpc_le_find_key_entry(bd_addr []uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) RPC_T_LE_KEY_ENTRY { if r.debug { fmt.Printf("rpc_le_find_key_entry()\r\n") } msg := startWriteMessage(0x00, 0x0A, 0x05, uint32(r.seq)) - // bd_addr : in uint8 - msg = append(msg, byte(bd_addr>>0)) + // bd_addr : in []uint8 (6) + msg = append(msg, bd_addr...) // bd_type : in RPC_T_GAP_REMOTE_ADDR_TYPE msg = append(msg, byte(bd_type>>0)) msg = append(msg, byte(bd_type>>8)) @@ -1731,14 +1731,14 @@ func (r *rtl8720dn) rpc_le_get_high_priority_bond() RPC_T_LE_KEY_ENTRY { return result } -func (r *rtl8720dn) rpc_le_set_high_priority_bond(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) bool { +func (r *rtl8720dn) rpc_le_set_high_priority_bond(bd_addr []uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE) bool { if r.debug { fmt.Printf("rpc_le_set_high_priority_bond()\r\n") } msg := startWriteMessage(0x00, 0x0A, 0x0A, uint32(r.seq)) - // bd_addr : in uint8 - msg = append(msg, byte(bd_addr>>0)) + // bd_addr : in []uint8 (6) + msg = append(msg, bd_addr...) // bd_type : in RPC_T_GAP_REMOTE_ADDR_TYPE msg = append(msg, byte(bd_type>>0)) msg = append(msg, byte(bd_type>>8)) @@ -1757,16 +1757,16 @@ func (r *rtl8720dn) rpc_le_set_high_priority_bond(bd_addr uint8, bd_type RPC_T_G return result } -func (r *rtl8720dn) rpc_le_resolve_random_address(unresolved_addr uint8, resolved_addr *uint8, resolved_addr_type RPC_T_GAP_IDENT_ADDR_TYPE) bool { +func (r *rtl8720dn) rpc_le_resolve_random_address(unresolved_addr []uint8, resolved_addr []uint8, resolved_addr_type RPC_T_GAP_IDENT_ADDR_TYPE) bool { if r.debug { fmt.Printf("rpc_le_resolve_random_address()\r\n") } msg := startWriteMessage(0x00, 0x0A, 0x0B, uint32(r.seq)) - // unresolved_addr : in uint8 - msg = append(msg, byte(unresolved_addr>>0)) - // resolved_addr : inout uint8 - msg = append(msg, byte(*resolved_addr>>0)) + // unresolved_addr : in []uint8 (6) + msg = append(msg, unresolved_addr...) + // resolved_addr : inout []uint8 (6) + msg = append(msg, resolved_addr...) // resolved_addr_type : inout RPC_T_GAP_IDENT_ADDR_TYPE msg = append(msg, byte(resolved_addr_type>>0)) msg = append(msg, byte(resolved_addr_type>>8)) @@ -1777,9 +1777,9 @@ func (r *rtl8720dn) rpc_le_resolve_random_address(unresolved_addr uint8, resolve r.read() widx := 8 - // resolved_addr : inout uint8 - *resolved_addr = payload[widx] - widx += 1 + // resolved_addr : inout []uint8 (6) + copy(resolved_addr, payload[widx:widx+6]) + widx += 6 // resolved_addr_type : inout RPC_T_GAP_IDENT_ADDR_TYPE // not impl (a.Size() > 0) @@ -1816,14 +1816,14 @@ func (r *rtl8720dn) rpc_le_get_cccd_data(p_entry RPC_T_LE_KEY_ENTRY, p_data RPC_ return result } -func (r *rtl8720dn) rpc_le_gen_bond_dev(bd_addr uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE, local_bd_type RPC_T_GAP_LOCAL_ADDR_TYPE, local_ltk []byte, key_type RPC_T_LE_KEY_TYPE, p_cccd RPC_T_LE_CCCD) bool { +func (r *rtl8720dn) rpc_le_gen_bond_dev(bd_addr []uint8, bd_type RPC_T_GAP_REMOTE_ADDR_TYPE, local_bd_type RPC_T_GAP_LOCAL_ADDR_TYPE, local_ltk []byte, key_type RPC_T_LE_KEY_TYPE, p_cccd RPC_T_LE_CCCD) bool { if r.debug { fmt.Printf("rpc_le_gen_bond_dev()\r\n") } msg := startWriteMessage(0x00, 0x0A, 0x0D, uint32(r.seq)) - // bd_addr : in uint8 - msg = append(msg, byte(bd_addr>>0)) + // bd_addr : in []uint8 (6) + msg = append(msg, bd_addr...) // bd_type : in RPC_T_GAP_REMOTE_ADDR_TYPE msg = append(msg, byte(bd_type>>0)) msg = append(msg, byte(bd_type>>8)) @@ -2043,7 +2043,7 @@ func (r *rtl8720dn) rpc_client_by_uuid_srv_discovery(conn_id uint8, client_id ui return result } -func (r *rtl8720dn) rpc_client_by_uuid128_srv_discovery(conn_id uint8, client_id uint8, p_uuid128 uint8) RPC_T_GAP_CAUSE { +func (r *rtl8720dn) rpc_client_by_uuid128_srv_discovery(conn_id uint8, client_id uint8, p_uuid128 []uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_by_uuid128_srv_discovery()\r\n") } @@ -2053,8 +2053,8 @@ func (r *rtl8720dn) rpc_client_by_uuid128_srv_discovery(conn_id uint8, client_id msg = append(msg, byte(conn_id>>0)) // client_id : in uint8 msg = append(msg, byte(client_id>>0)) - // p_uuid128 : in uint8 - msg = append(msg, byte(p_uuid128>>0)) + // p_uuid128 : in []uint8 (16) + msg = append(msg, p_uuid128...) r.performRequest(msg) @@ -2158,7 +2158,7 @@ func (r *rtl8720dn) rpc_client_by_uuid_char_discovery(conn_id uint8, client_id u return result } -func (r *rtl8720dn) rpc_client_by_uuid128_char_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16, p_uuid128 uint8) RPC_T_GAP_CAUSE { +func (r *rtl8720dn) rpc_client_by_uuid128_char_discovery(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16, p_uuid128 []uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_by_uuid128_char_discovery()\r\n") } @@ -2174,8 +2174,8 @@ func (r *rtl8720dn) rpc_client_by_uuid128_char_discovery(conn_id uint8, client_i // end_handle : in uint16 msg = append(msg, byte(end_handle>>0)) msg = append(msg, byte(end_handle>>8)) - // p_uuid128 : in uint8 - msg = append(msg, byte(p_uuid128>>0)) + // p_uuid128 : in []uint8 (16) + msg = append(msg, p_uuid128...) r.performRequest(msg) @@ -2244,7 +2244,7 @@ func (r *rtl8720dn) rpc_client_attr_read(conn_id uint8, client_id uint8, handle return result } -func (r *rtl8720dn) rpc_client_attr_read_using_uuid(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16, uuid16 uint16, p_uuid128 uint8) RPC_T_GAP_CAUSE { +func (r *rtl8720dn) rpc_client_attr_read_using_uuid(conn_id uint8, client_id uint8, start_handle uint16, end_handle uint16, uuid16 uint16, p_uuid128 []uint8) RPC_T_GAP_CAUSE { if r.debug { fmt.Printf("rpc_client_attr_read_using_uuid()\r\n") } @@ -2263,8 +2263,8 @@ func (r *rtl8720dn) rpc_client_attr_read_using_uuid(conn_id uint8, client_id uin // uuid16 : in uint16 msg = append(msg, byte(uuid16>>0)) msg = append(msg, byte(uuid16>>8)) - // p_uuid128 : in uint8 - msg = append(msg, byte(p_uuid128>>0)) + // p_uuid128 : in []uint8 (16) + msg = append(msg, p_uuid128...) r.performRequest(msg) @@ -2354,14 +2354,14 @@ func (r *rtl8720dn) rpc_ble_server_init(num uint8) bool { return result } -func (r *rtl8720dn) rpc_ble_create_service(uuid uint8, uuid_length uint8, is_primary bool) uint8 { +func (r *rtl8720dn) rpc_ble_create_service(uuid []uint8, uuid_length uint8, is_primary bool) uint8 { if r.debug { fmt.Printf("rpc_ble_create_service()\r\n") } msg := startWriteMessage(0x00, 0x0C, 0x02, uint32(r.seq)) - // uuid : in uint8 - msg = append(msg, byte(uuid>>0)) + // uuid : in []uint8 (16) + msg = append(msg, uuid...) // uuid_length : in uint8 msg = append(msg, byte(uuid_length>>0)) // is_primary : in bool @@ -2446,7 +2446,7 @@ func (r *rtl8720dn) rpc_ble_get_servie_handle(app_id uint8) uint8 { return result } -func (r *rtl8720dn) rpc_ble_create_char(app_id uint8, uuid uint8, uuid_length uint8, properties uint8, permissions uint32) uint16 { +func (r *rtl8720dn) rpc_ble_create_char(app_id uint8, uuid []uint8, uuid_length uint8, properties uint8, permissions uint32) uint16 { if r.debug { fmt.Printf("rpc_ble_create_char()\r\n") } @@ -2454,8 +2454,8 @@ func (r *rtl8720dn) rpc_ble_create_char(app_id uint8, uuid uint8, uuid_length ui // app_id : in uint8 msg = append(msg, byte(app_id>>0)) - // uuid : in uint8 - msg = append(msg, byte(uuid>>0)) + // uuid : in []uint8 (16) + msg = append(msg, uuid...) // uuid_length : in uint8 msg = append(msg, byte(uuid_length>>0)) // properties : in uint8 @@ -2478,7 +2478,7 @@ func (r *rtl8720dn) rpc_ble_create_char(app_id uint8, uuid uint8, uuid_length ui return result } -func (r *rtl8720dn) rpc_ble_create_desc(app_id uint8, char_handle uint16, uuid uint8, uuid_length uint8, flags uint8, permissions uint32, value_length uint16, p_value []byte) uint16 { +func (r *rtl8720dn) rpc_ble_create_desc(app_id uint8, char_handle uint16, uuid []uint8, uuid_length uint8, flags uint8, permissions uint32, value_length uint16, p_value []byte) uint16 { if r.debug { fmt.Printf("rpc_ble_create_desc()\r\n") } @@ -2489,8 +2489,8 @@ func (r *rtl8720dn) rpc_ble_create_desc(app_id uint8, char_handle uint16, uuid u // char_handle : in uint16 msg = append(msg, byte(char_handle>>0)) msg = append(msg, byte(char_handle>>8)) - // uuid : in uint8 - msg = append(msg, byte(uuid>>0)) + // uuid : in []uint8 (16) + msg = append(msg, uuid...) // uuid_length : in uint8 msg = append(msg, byte(uuid_length>>0)) // flags : in uint8 @@ -3026,7 +3026,7 @@ func (r *rtl8720dn) rpc_wifi_get_mac_address(mac []uint8) int32 { r.read() widx := 8 - // mac : out []uint8 + // mac : out []uint8 (18) copy(mac, payload[widx:widx+18]) widx += 18 @@ -3150,7 +3150,7 @@ func (r *rtl8720dn) rpc_wifi_get_associated_client_list(client_list_buffer []byt return result } -func (r *rtl8720dn) rpc_wifi_get_ap_bssid(bssid *uint8) int32 { +func (r *rtl8720dn) rpc_wifi_get_ap_bssid(bssid []uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_get_ap_bssid()\r\n") } @@ -3160,9 +3160,9 @@ func (r *rtl8720dn) rpc_wifi_get_ap_bssid(bssid *uint8) int32 { r.read() widx := 8 - // bssid : out uint8 - *bssid = payload[widx] - widx += 1 + // bssid : out []uint8 (6) + copy(bssid, payload[widx:widx+6]) + widx += 6 var result int32 x := binary.LittleEndian.Uint32(payload[widx:]) @@ -3339,14 +3339,14 @@ func (r *rtl8720dn) rpc_wifi_change_channel_plan(channel_plan uint8) int32 { return result } -func (r *rtl8720dn) rpc_wifi_register_multicast_address(mac uint8) int32 { +func (r *rtl8720dn) rpc_wifi_register_multicast_address(mac []uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_register_multicast_address()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x17, uint32(r.seq)) - // mac : in uint8 - msg = append(msg, byte(mac>>0)) + // mac : in []uint8 (6) + msg = append(msg, mac...) r.performRequest(msg) @@ -3361,14 +3361,14 @@ func (r *rtl8720dn) rpc_wifi_register_multicast_address(mac uint8) int32 { return result } -func (r *rtl8720dn) rpc_wifi_unregister_multicast_address(mac uint8) int32 { +func (r *rtl8720dn) rpc_wifi_unregister_multicast_address(mac []uint8) int32 { if r.debug { fmt.Printf("rpc_wifi_unregister_multicast_address()\r\n") } msg := startWriteMessage(0x00, 0x0E, 0x18, uint32(r.seq)) - // mac : in uint8 - msg = append(msg, byte(mac>>0)) + // mac : in []uint8 (6) + msg = append(msg, mac...) r.performRequest(msg) From fbed859a5828da1870502ce8147639fe3c4433ec Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Mon, 13 Feb 2023 14:55:42 -0800 Subject: [PATCH 34/47] README updates: porting applications from Go's "net" package --- netdev/README.md | 93 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 29 deletions(-) diff --git a/netdev/README.md b/netdev/README.md index 980a21d1f..b97a7166e 100644 --- a/netdev/README.md +++ b/netdev/README.md @@ -34,47 +34,78 @@ Here the netdev is the entire stack, accessing hardware on the bottom and servin ## Porting Applications from Go "net" -Ideally, TinyGo's "net" package would just be Go's "net" package and applications using "net" would just work, as-is. Unfortunately, Go's "net" can't fully be ported to TinyGo, so TinyGo's "net" is a subset of Go's. Hopefully, for the embedded space, the subset is sufficient for most needs. +Ideally, TinyGo's "net" package would just be Go's "net" package and applications using "net" would just work, as-is. Unfortunately, Go's "net" can't fully be ported to TinyGo, so TinyGo's "net" is a subset of Go's. To view TinyGo's "net" package exports, use ```go doc ./net```, ```go doc ./net/http```, etc. For the most part, Go's "net" documentation applies to TinyGo's "net". There are a few features excluded during the porting process, in particular: - No IPv6 support - No HTTP/2 support - HTTP client request can't be reused -- No multipart form support -- No TLS support for HTTP servers +- No TLS support for HTTP servers (no https servers) - No DualStack support -Applications using Go's "net" package will need a few (minor) modifications to work with TinyGo's net package. +Applications using Go's "net" package will need a few setup modifications to work with TinyGo's "net" package. -### Step 1: Load Netdev +### Step 1: Create the netdev for your target device. -#### Option 1: +The available netdev are: -Import netdev package to load the netdev driver. Import only for side effects using leading underscore. +- wifinina: SPI to ESP32 WiFi co-controller running Arduino WiFiNINA firmware +- rtl8720dn: UART to RealTek WiFi rtl8720dn co-controller +- espat: UART to ESP32/ESP8266 WiFi co-controller running Espressif AT firmware + +This example configures and creates a wifinina netdev using New(). -```go -import _ "tinygo.org/x/drivers/netdev" ``` +import "tinygo.org/x/drivers/wifinina" -This will select the netdev driver for the target machine using build tags. For example, when flashing to target Arduino Nano RP2040 Connect, the build tag nano_rp2040 will select the "Wifinina" netdev driver. +func main() { + cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} + dev := wifinina.New(&cfg) + ... +} +``` + +The Config structure is netdev-specific; consult the netdev package for Config details. In this case, the WiFi credentials are passed. -#### Option 2: +### Step 2: Hook the netdev into the "net" package -Manually load the netdev driver. Import the driver directly, and then call net.UseNetdev to load the driver. e.g.: +Tell the "net" package to use the netdev. Continuing with the wifinina example: ``` -import "tinygo.org/x/drivers/netdev/wifinina" +import "tinygo.org/x/drivers/netdev" +import "tinygo.org/x/drivers/wifinina" func main() { - net.UseNetdev(wifinina.New("SSID", "PASSPHRASE")) + cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} + dev := wifinina.New(&cfg) + netdev.Use(dev) ... } ``` -### Step 2: Connect to the Network +### Step 3: Connect to an IP Network -Call net.Connect() to connect the device to an IP network, via Wifi, cellular, Ethernet, etc. Make this call first, before any net.* or http.* or tls.* calls. +Before the "net" package is fully functional, connect the netdev to an underlying IP network. For example, a WiFi netdev would connect to a WiFi access point or become a WiFi access point; either way, once connected, the netdev has a station IP address and is connected on the IP network. + +Call dev.NetConnect() to connect the device to an IP network. Call dev.NetDisconnect() to disconnect. Continuing example: + +``` +import "tinygo.org/x/drivers/netdev" +import "tinygo.org/x/drivers/wifinina" + +func main() { + cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} + dev := wifinina.New(&cfg) + netdev.Use(dev) + + dev.NetConnect() + + // "net" package calls here + + dev.NetDisconnect() +} +``` Here is a simple http server listening on port :8080, before and after porting from Go "net/http": @@ -83,17 +114,17 @@ Here is a simple http server listening on port :8080, before and after porting f package main import ( - "fmt" - "net/http" + "fmt" + "net/http" ) func main() { - http.HandleFunc("/", HelloServer) - http.ListenAndServe(":8080", nil) + http.HandleFunc("/", HelloServer) + http.ListenAndServe(":8080", nil) } func HelloServer(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) + fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) } ``` @@ -102,21 +133,25 @@ func HelloServer(w http.ResponseWriter, r *http.Request) { package main import ( - "fmt" - "net" - "net/http" + "fmt" + "net/http" - _ "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" ) func main() { - net.Connect(nil) - http.HandleFunc("/", HelloServer) - http.ListenAndServe(":8080", nil) + cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} + dev := wifinina.New(&cfg) + netdev.Use(dev) + dev.NetConnect() + + http.HandleFunc("/", HelloServer) + http.ListenAndServe(":8080", nil) } func HelloServer(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) + fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) } ``` From c57ee308d7b1dbe8f37a79c7825dc492c072d68d Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Mon, 13 Feb 2023 15:08:37 -0800 Subject: [PATCH 35/47] README: add raw socket section and example --- netdev/README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/netdev/README.md b/netdev/README.md index b97a7166e..57624053b 100644 --- a/netdev/README.md +++ b/netdev/README.md @@ -4,6 +4,7 @@ - [Overview](#overview) - [Porting Applications from Go "net"](#porting-applications-from-go-net) +- [Using Raw Sockets](#using-raw-sockets) - [Writing a New Driver](#writing-a-new-driver) ## Overview @@ -155,6 +156,37 @@ func HelloServer(w http.ResponseWriter, r *http.Request) { } ``` +## Using Raw Sockets + +A netdev implements the Socketer interface so an application can make raw socket calls, bypassing the "net" package. + +Here is a simple TCP application using raw sockets: + +```go +package main + +import ( + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/wifinina" +) + +func main() { + cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} + dev := wifinina.New(&cfg) + netdev.Use(dev) + dev.NetConnect() + + sock, _ := dev.Socket(netdev.AF_INET, netdev.SOCK_STREAM, netdev.IPPROTO_TCP) + + sockAddr := netdev.NewSockAddr("", netdev.Port(8080), netdev.ParseIP("10.0.0.100") + dev.Connect(sock, sockAddr) + + dev.Send(sock, []bytes("hello"), 0, 0) + + dev.Close(sock) +} +``` + ## Writing a New Driver :bulb: A reference netdev driver is the Wifinina driver (netdev/wifinina). From d91bafc6cfafe0a4dd717ac011551f4786e70f6d Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Tue, 14 Feb 2023 01:28:42 -0800 Subject: [PATCH 36/47] README: update netdev models jpg --- netdev/netdev_models.jpg | Bin 65672 -> 67370 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/netdev/netdev_models.jpg b/netdev/netdev_models.jpg index 37e29d7040913eaa633e0568f991f935f2addbae..063f3afe7790cffad27f74f393d6fdf32070bb62 100644 GIT binary patch delta 40481 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%0AMIAs>Xqbp4j`$2v8} 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 Date: Mon, 13 Feb 2023 23:02:10 -0800 Subject: [PATCH 37/47] README: updates and cleanups --- netdev/README.md | 57 +++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/netdev/README.md b/netdev/README.md index 57624053b..e77693ff8 100644 --- a/netdev/README.md +++ b/netdev/README.md @@ -35,25 +35,33 @@ Here the netdev is the entire stack, accessing hardware on the bottom and servin ## Porting Applications from Go "net" -Ideally, TinyGo's "net" package would just be Go's "net" package and applications using "net" would just work, as-is. Unfortunately, Go's "net" can't fully be ported to TinyGo, so TinyGo's "net" is a subset of Go's. +Ideally, TinyGo's "net" package would just be Go's "net" package and applications using "net" would just work, as-is. Unfortunately, Go's net package can't fully be ported to TinyGo, so TinyGo's net package is a subset. To view TinyGo's "net" package exports, use ```go doc ./net```, ```go doc ./net/http```, etc. For the most part, Go's "net" documentation applies to TinyGo's "net". There are a few features excluded during the porting process, in particular: - No IPv6 support - No HTTP/2 support -- HTTP client request can't be reused - No TLS support for HTTP servers (no https servers) - No DualStack support +- HTTP client request can't be reused -Applications using Go's "net" package will need a few setup modifications to work with TinyGo's "net" package. +Applications using Go's net package will need a few setup modifications to work with TinyGo's net package. ### Step 1: Create the netdev for your target device. The available netdev are: -- wifinina: SPI to ESP32 WiFi co-controller running Arduino WiFiNINA firmware -- rtl8720dn: UART to RealTek WiFi rtl8720dn co-controller -- espat: UART to ESP32/ESP8266 WiFi co-controller running Espressif AT firmware +- [wifinina]: SPI to ESP32 WiFi co-controller running Arduino WiFiNINA firmware + + targets: pyportal arduino_nano33 nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 + +- [rtl8720dn]: UART to RealTek WiFi rtl8720dn co-controller + + targets: wioterminal + +- [espat]: UART to ESP32/ESP8266 WiFi co-controller running Espressif AT firmware + + targets: TBD This example configures and creates a wifinina netdev using New(). @@ -69,9 +77,9 @@ func main() { The Config structure is netdev-specific; consult the netdev package for Config details. In this case, the WiFi credentials are passed. -### Step 2: Hook the netdev into the "net" package +### Step 2: Hook the netdev into the net package -Tell the "net" package to use the netdev. Continuing with the wifinina example: +Tell the net package to use the netdev by calling netdev.Use(). Continuing with the wifinina example: ``` import "tinygo.org/x/drivers/netdev" @@ -85,9 +93,13 @@ func main() { } ``` +Now, the net package is linked to the netdev so any net I/O will go through the netdev. Calls to net.Dial(), net.Listen() etc will translate to netdev socket calls. + +The last step is to connect the netdev to an IP network. + ### Step 3: Connect to an IP Network -Before the "net" package is fully functional, connect the netdev to an underlying IP network. For example, a WiFi netdev would connect to a WiFi access point or become a WiFi access point; either way, once connected, the netdev has a station IP address and is connected on the IP network. +Before the net package is fully functional, connect the netdev to an underlying IP network. For example, a WiFi netdev would connect to a WiFi access point or become a WiFi access point; either way, once connected, the netdev has a station IP address and is connected on the IP network. Call dev.NetConnect() to connect the device to an IP network. Call dev.NetDisconnect() to disconnect. Continuing example: @@ -102,7 +114,7 @@ func main() { dev.NetConnect() - // "net" package calls here + // net package calls here dev.NetDisconnect() } @@ -158,7 +170,7 @@ func HelloServer(w http.ResponseWriter, r *http.Request) { ## Using Raw Sockets -A netdev implements the Socketer interface so an application can make raw socket calls, bypassing the "net" package. +A netdev implements the Socketer interface so an application can make raw socket calls, bypassing the net package. Here is a simple TCP application using raw sockets: @@ -174,6 +186,9 @@ func main() { cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} dev := wifinina.New(&cfg) netdev.Use(dev) + + // ignoring error handling + dev.NetConnect() sock, _ := dev.Socket(netdev.AF_INET, netdev.SOCK_STREAM, netdev.IPPROTO_TCP) @@ -189,9 +204,7 @@ func main() { ## Writing a New Driver -:bulb: A reference netdev driver is the Wifinina driver (netdev/wifinina). - -Netdev drivers implement the net.Netdever interface, which includes the net.Socketer interface. The Socketer interface is modeled after BSD socket(2). TinyGo's "net" package translates net.Conn calls into netdev Socketer calls. For example, DialTCP calls netdev.Socket() and netdev.Connect(): +Netdev drivers implement the netdev.Netdever interface, which includes the netdev.Socketer interface. The Socketer interface is modeled after BSD socket(2). TinyGo's net package translates net.Conn calls into netdev Socketer calls. For example, DialTCP calls netdev.Socket() and netdev.Connect(): ```go func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error) { @@ -226,26 +239,20 @@ type Socketer interface { } ``` -Socketer interface is intended to mimic a subset of BSD socket(2). They've been Go-ified, but should otherwise maintain the semantics of the original socket(2) calls. Send and Recv add a timeout to put a limit on blocking operations. Recv in paricular is blocking and will block until data arrives on the socket or EOF. The timeout is calculated from net.Conn's SetDeadline(), typically. +Socketer interface is intended to mimic a subset of BSD socket(2). They've been Go-ified, but should otherwise maintain the semantics of the original socket(2) calls. Send and Recv add a timeout to put a limit on blocking operations. Recv in paricular is blocking and will block until data arrives on the socket or EOF. The timeout value is calculated from net.Conn's SetDeadline(), typically. #### Locking -Multiple goroutines may invoke methods on a net.Conn simultaneously, and the "net" package translates net.Conn calls into Socketer calls. It follows that multiple goroutines may invoke Socketer calls, so locking is required to keep Socketer calls from stepping on one another. +Multiple goroutines may invoke methods on a net.Conn simultaneously, and since the net package translates net.Conn calls into Socketer calls, it follows that multiple goroutines may invoke Socketer calls, so locking is required to keep Socketer calls from stepping on one another. -Don't hold a lock while Time.Sleep()'ing waiting for a hardware operation to finish. Unlocking while sleeping let's other goroutines to run. If the sleep period is really small, then you can get away with holding the lock sometimes. +Don't hold a lock while Time.Sleep()ing waiting for a hardware operation to finish. Unlocking while sleeping let's other goroutines make progress. If the sleep period is really small, then you can get away with holding the lock. #### Sockfd -The Socketer interface uses a socket fd to represent a socket connection end-point. Each net.Conn maps 1:1 to a fd. The number of fds available is a netdev hardware limitation. Wifinina, for example, can hand out 10 socket fds. - -### Packaging - -1. Create a new directory in netdev/foo to hold the driver files. - -2. Add a initialization file netdev/netdev_foo.go to compile and load the driver based on target build tags. +The Socketer interface uses a socket fd to represent a socket connection (end-point). Each net.Conn maps 1:1 to a fd. The number of fds available is a netdev hardware limitation. Wifinina, for example, can hand out 10 socket fds. ### Testing -The netdev driver should minimally pass all of the example/net examples. +The netdev driver should minimally run all of the example/net examples. TODO: automate testing to catch regressions. From c862c8e8e94e8c0b43b0f341375dc5072b297ad2 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Tue, 14 Feb 2023 00:01:26 -0800 Subject: [PATCH 38/47] README: add http and tls documentation --- netdev/README.md | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/netdev/README.md b/netdev/README.md index e77693ff8..89dd99bdd 100644 --- a/netdev/README.md +++ b/netdev/README.md @@ -3,7 +3,9 @@ #### Table of Contents - [Overview](#overview) -- [Porting Applications from Go "net"](#porting-applications-from-go-net) +- [Using "net" Package](#using-net-package) +- [Using "net/http" Package](#using-nethttp-package) +- [Using "crypto/tls" Package](#using-cryptotls-package) - [Using Raw Sockets](#using-raw-sockets) - [Writing a New Driver](#writing-a-new-driver) @@ -33,11 +35,9 @@ Here the netdev includes the TCP/IP stack, maybe some port of lwip/uip to Go? Here the netdev is the entire stack, accessing hardware on the bottom and serving up net.Conn connections above to applications. -## Porting Applications from Go "net" +## Using "net" Package -Ideally, TinyGo's "net" package would just be Go's "net" package and applications using "net" would just work, as-is. Unfortunately, Go's net package can't fully be ported to TinyGo, so TinyGo's net package is a subset. - -To view TinyGo's "net" package exports, use ```go doc ./net```, ```go doc ./net/http```, etc. For the most part, Go's "net" documentation applies to TinyGo's "net". There are a few features excluded during the porting process, in particular: +Ideally, TinyGo's "net" package would just be Go's "net" package and applications using "net" would just work, as-is. TinyGo's net package is a partial port from Go's net package, replacing OS socket syscalls with netdev socket calls. TinyGo's net package is a subset of Go's net package. There are a few features excluded during the porting process, in particular: - No IPv6 support - No HTTP/2 support @@ -45,7 +45,9 @@ To view TinyGo's "net" package exports, use ```go doc ./net```, ```go doc ./net/ - No DualStack support - HTTP client request can't be reused -Applications using Go's net package will need a few setup modifications to work with TinyGo's net package. +Run ```go doc -all ./src/net``` on tinygo directory to see full listing. + +Applications using Go's net package will need a few setup steps to work with TinyGo's net package. ### Step 1: Create the netdev for your target device. @@ -168,6 +170,26 @@ func HelloServer(w http.ResponseWriter, r *http.Request) { } ``` +## Using "net/http" Package + +TinyGo's net/http package is a partial port of Go's net/http package, providing a subset of the full net/http package. + +HTTP client methods (http.Get, http.Head, http.Post, and http.PostForm) are functional. Dial clients support both HTTP and HTTPS transactions. + +HTTP server methods and objects are mostly ported, but for HTTP only. HTTPS servers are not supported. + +HTTP request and response handling code is 100% ported, so all the intricacy of parsing and writing headers is handled as in the full net/http package. + +Run ```go doc -all ./src/net/http``` on tinygo directory to see full listing. + +## Using "crypto/tls" Package + +TinyGo's TLS support (crypto/tls) relies on hardware offload of the TLS protocol. This is different from Go's crypto/tls package which handles the TLS protocol in software. + +TinyGo's TLS support is only available for client applications. You can http.Get() to an http:// or https:// address, but you cannot http.ListenAndServe() an https server. + +The offloading hardware has pre-defined TLS certificates built-in, so software does not need to supply certificates. + ## Using Raw Sockets A netdev implements the Socketer interface so an application can make raw socket calls, bypassing the net package. From 13fce41493f40d693252ea8facb18fbf0887695f Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Tue, 14 Feb 2023 01:10:13 -0800 Subject: [PATCH 39/47] README cleanups --- netdev/README.md | 67 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/netdev/README.md b/netdev/README.md index 89dd99bdd..0fb8a442f 100644 --- a/netdev/README.md +++ b/netdev/README.md @@ -67,7 +67,7 @@ The available netdev are: This example configures and creates a wifinina netdev using New(). -``` +```go import "tinygo.org/x/drivers/wifinina" func main() { @@ -83,7 +83,7 @@ The Config structure is netdev-specific; consult the netdev package for Config d Tell the net package to use the netdev by calling netdev.Use(). Continuing with the wifinina example: -``` +```go import "tinygo.org/x/drivers/netdev" import "tinygo.org/x/drivers/wifinina" @@ -105,7 +105,7 @@ Before the net package is fully functional, connect the netdev to an underlying Call dev.NetConnect() to connect the device to an IP network. Call dev.NetDisconnect() to disconnect. Continuing example: -``` +```go import "tinygo.org/x/drivers/netdev" import "tinygo.org/x/drivers/wifinina" @@ -122,6 +122,18 @@ func main() { } ``` +Get notified of IP network connects and disconnects: + +```go + dev.Notify(func(e netdev.Event) { + switch e { + case netdev.EventNetUp: + println("Network UP") + case netdev.EventNetDown: + println("Network DOWN") + }) +``` + Here is a simple http server listening on port :8080, before and after porting from Go "net/http": #### Before @@ -174,11 +186,11 @@ func HelloServer(w http.ResponseWriter, r *http.Request) { TinyGo's net/http package is a partial port of Go's net/http package, providing a subset of the full net/http package. -HTTP client methods (http.Get, http.Head, http.Post, and http.PostForm) are functional. Dial clients support both HTTP and HTTPS transactions. +HTTP client methods (http.Get, http.Head, http.Post, and http.PostForm) are functional. Dial clients support both HTTP and HTTPS URLs. -HTTP server methods and objects are mostly ported, but for HTTP only. HTTPS servers are not supported. +HTTP server methods and objects are mostly ported, but for HTTP only; HTTPS servers are not supported. -HTTP request and response handling code is 100% ported, so all the intricacy of parsing and writing headers is handled as in the full net/http package. +HTTP request and response handling code is mostly ported, so most the intricacy of parsing and writing headers is handled as in the full net/http package. Run ```go doc -all ./src/net/http``` on tinygo directory to see full listing. @@ -245,9 +257,50 @@ func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error) { } ``` -### net.Socketer Interface +### Netdever Interface + +A netdev driver implements the Netdever interface: + +```go +// Netdev drivers implement the Netdever interface. +// +// A Netdever is passed to the "net" package using netdev.Use(). +// +// Just like a net.Conn, multiple goroutines may invoke methods on a Netdever +// simultaneously. +type Netdever interface { + + // NetConnect device to IP network + NetConnect() error + + // NetDisconnect device from IP network + NetDisconnect() + + // NetNotify to register callback for network events + NetNotify(func(Event)) + + // GetHostByName returns the IP address of either a hostname or IPv4 + // address in standard dot notation + GetHostByName(name string) (IP, error) + + // GetHardwareAddr returns device MAC address + GetHardwareAddr() (HardwareAddr, error) + + // GetIPAddr returns IP address assigned to device, either by DHCP or + // statically + GetIPAddr() (IP, error) + + // Socketer is a Berkely Sockets-like interface + Socketer +} +``` + +### Socketer Interface ```go +// Berkely Sockets-like interface. See man page for socket(2), etc. +// +// Multiple goroutines may invoke methods on a Socketer simultaneously. type Socketer interface { Socket(family AddressFamily, sockType SockType, protocol Protocol) (Sockfd, error) Bind(sockfd Sockfd, myaddr SockAddr) error From c890554636b1b0eb576c8e62b0c29b735211c90b Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Tue, 14 Feb 2023 01:57:16 -0800 Subject: [PATCH 40/47] README more updates --- netdev/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/netdev/README.md b/netdev/README.md index 0fb8a442f..1a827f9f8 100644 --- a/netdev/README.md +++ b/netdev/README.md @@ -13,31 +13,31 @@ Netdev is TinyGo's network device driver model. -Let's see where netdev fits in the network stack. The diagram below shows the traditional full OS stack vs. different possible embedded stacks for TinyGo. +Let's see where netdev fits in the network stack. The diagram below shows the traditional full OS stack vs. different possible embedded stacks for TinyGo. The application is written to the same net.Conn interface in each case. ![Netdev models](netdev_models.jpg) -In the traditional full OS stack, the driver that sits above hardware (the "nic") and below TCP/IP is the network driver, the netdev. The netdev provides a raw packet interface to the OS. +In the (1) Go full OS stack, the network driver, aka netdev, sits above hardware (the "nic") and below TCP/IP. The netdev provides a raw packet interface to TCP/IP. -For TinyGo netdev, the netdev includes TCP/IP and provides a socket(2) interface to TinyGo's "net" package. Applications are written to use the net.Conn interfaces. "net" translates net.Conn functions (Dial, Listen, Read, Write) into netdev socket(2) calls. The netdev translates those socket(2) calls into hardware access, ultimately. Let's consider the three use cases: +For TinyGo, the netdev includes TCP/IP and provides a socket(2) interface to the "net" package. Applications are written to the net.Conn interface: TCPConn, UDPConn, and TLSConn. net.Conn functions calls translate to netdev socket(2) calls, which in turn call into firmware/hardware. Let's consider the three use cases: -#### Firware Offload Model +#### (2) Firware Offload Model Here we are fortunate that hardware includes firmware with a TCP/IP implmentation, and the firmware manages the TCP/IP connection state. The netdev driver translates socket(2) calls to the firmware's TCP/IP calls. Usually, minimal work is required since the firmware is likely to use lwip, which has an socket(2) API. The Wifinina (ESP32) and RTL8720dn netdev drivers are examples of the firmware offload model. -#### Full Stack Model +#### (3) Full Stack Model Here the netdev includes the TCP/IP stack, maybe some port of lwip/uip to Go? -#### "Bring-Your-Own-net.Comm" Model +#### (4) "Bring-Your-Own-net.Comm" Model Here the netdev is the entire stack, accessing hardware on the bottom and serving up net.Conn connections above to applications. ## Using "net" Package -Ideally, TinyGo's "net" package would just be Go's "net" package and applications using "net" would just work, as-is. TinyGo's net package is a partial port from Go's net package, replacing OS socket syscalls with netdev socket calls. TinyGo's net package is a subset of Go's net package. There are a few features excluded during the porting process, in particular: +Ideally, TinyGo's "net" package would be Go's "net" package and applications using "net" would just work, as-is. TinyGo's net package is a partial port from Go's net package, replacing OS socket syscalls with netdev socket calls. TinyGo's net package is a subset of Go's net package. There are a few features excluded during the porting process, in particular: - No IPv6 support - No HTTP/2 support From 64b166ae28cd776692809369d389d805dcaec416 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Wed, 15 Feb 2023 19:01:45 -0800 Subject: [PATCH 41/47] increase the delay waitin for serial --- examples/net/webserver/main.go | 2 +- examples/net/webstatic/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/net/webserver/main.go b/examples/net/webserver/main.go index 9a32cf444..c24af38ef 100644 --- a/examples/net/webserver/main.go +++ b/examples/net/webserver/main.go @@ -28,7 +28,7 @@ var led = machine.LED func main() { // wait a bit for serial - time.Sleep(time.Second) + time.Sleep(2 * time.Second) led.Configure(machine.PinConfig{Mode: machine.PinOutput}) diff --git a/examples/net/webstatic/main.go b/examples/net/webstatic/main.go index d2c522bc6..5bebb0a61 100644 --- a/examples/net/webstatic/main.go +++ b/examples/net/webstatic/main.go @@ -23,7 +23,7 @@ var fs embed.FS func main() { // wait a bit for console - time.Sleep(time.Second) + time.Sleep(2 * time.Second) if err := NetConnect(); err != nil { log.Fatal(err) From 5c3af520ad917a974826d691d93c40dfd75a0803 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Wed, 15 Feb 2023 19:29:41 -0800 Subject: [PATCH 42/47] Update README.md --- netdev/README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/netdev/README.md b/netdev/README.md index 1a827f9f8..b4969e5ef 100644 --- a/netdev/README.md +++ b/netdev/README.md @@ -13,27 +13,29 @@ Netdev is TinyGo's network device driver model. -Let's see where netdev fits in the network stack. The diagram below shows the traditional full OS stack vs. different possible embedded stacks for TinyGo. The application is written to the same net.Conn interface in each case. +Let's see where netdev fits in the network stack. The diagram below shows the traditional full OS stack vs. different possible embedded stacks for TinyGo. Notice the application is written to the same net.Conn interface for all cases. ![Netdev models](netdev_models.jpg) In the (1) Go full OS stack, the network driver, aka netdev, sits above hardware (the "nic") and below TCP/IP. The netdev provides a raw packet interface to TCP/IP. -For TinyGo, the netdev includes TCP/IP and provides a socket(2) interface to the "net" package. Applications are written to the net.Conn interface: TCPConn, UDPConn, and TLSConn. net.Conn functions calls translate to netdev socket(2) calls, which in turn call into firmware/hardware. Let's consider the three use cases: +For TinyGo, the netdev includes TCP/IP and provides a socket(2) interface to the "net" package. Applications are written to the net.Conn interfaces: TCPConn, UDPConn, and TLSConn. net.Conn functions call netdev socket(2) calls, which in turn call into firmware/hardware. Let's consider the three use cases: #### (2) Firware Offload Model -Here we are fortunate that hardware includes firmware with a TCP/IP implmentation, and the firmware manages the TCP/IP connection state. The netdev driver translates socket(2) calls to the firmware's TCP/IP calls. Usually, minimal work is required since the firmware is likely to use lwip, which has an socket(2) API. +Here the networking device is a co-controller installed with firmware running a full TCP/IP stack. Firmware manages the TCP/IP connection state with the network. -The Wifinina (ESP32) and RTL8720dn netdev drivers are examples of the firmware offload model. +The netdev driver runs on the main controller and talks to the co-controller's firmware interface using UART/SPI/etc. The netdev driver translates socket(2) calls to the firmware's TCP/IP calls. + +The wifinina (ESP32), espat (ESP32-AT), and rtl8720dn netdev drivers are examples of the firmware offload model. #### (3) Full Stack Model -Here the netdev includes the TCP/IP stack, maybe some port of lwip/uip to Go? +Here the netdev includes the TCP/IP stack. There is no co-controller. #### (4) "Bring-Your-Own-net.Comm" Model -Here the netdev is the entire stack, accessing hardware on the bottom and serving up net.Conn connections above to applications. +Here the netdev is the entire stack, accessing hardware on the bottom and serving up net.Conn connections to applications. ## Using "net" Package @@ -122,7 +124,7 @@ func main() { } ``` -Get notified of IP network connects and disconnects: +Optionally, get notified of IP network connects and disconnects: ```go dev.Notify(func(e netdev.Event) { @@ -198,9 +200,9 @@ Run ```go doc -all ./src/net/http``` on tinygo directory to see full listing. TinyGo's TLS support (crypto/tls) relies on hardware offload of the TLS protocol. This is different from Go's crypto/tls package which handles the TLS protocol in software. -TinyGo's TLS support is only available for client applications. You can http.Get() to an http:// or https:// address, but you cannot http.ListenAndServe() an https server. +TinyGo's TLS support is only available for client applications. You can http.Get() to an http:// or https:// address, but you cannot http.ListenAndServeTLS() an https server. -The offloading hardware has pre-defined TLS certificates built-in, so software does not need to supply certificates. +The offloading hardware has pre-defined TLS certificates built-in. ## Using Raw Sockets From 80eee5649e53adee8be4a9f4a662063f3c6039da Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Wed, 15 Feb 2023 19:30:48 -0800 Subject: [PATCH 43/47] update netdev model diagram --- netdev/netdev_models.jpg | Bin 67370 -> 65432 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/netdev/netdev_models.jpg b/netdev/netdev_models.jpg index 063f3afe7790cffad27f74f393d6fdf32070bb62..04abc822d6fa0880abbdfa641094ed8b4aa889fc 100644 GIT binary patch delta 26407 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 Nt?d`BFIbD>_%}{AuSNg> delta 28790 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 Date: Wed, 15 Feb 2023 20:20:27 -0800 Subject: [PATCH 44/47] gofmt --- examples/net/espat/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/net/espat/main.go b/examples/net/espat/main.go index a1891fe0e..539049a9a 100644 --- a/examples/net/espat/main.go +++ b/examples/net/espat/main.go @@ -8,8 +8,8 @@ import ( "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/espat" + "tinygo.org/x/drivers/netdev" ) var ( From d13ae5a14d6d6652c1d3c601eb17bff4628a8f21 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Wed, 15 Feb 2023 21:47:10 -0800 Subject: [PATCH 45/47] fix make test --- Makefile | 52 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index 9b874f5b8..022de7cb4 100644 --- a/Makefile +++ b/Makefile @@ -43,12 +43,6 @@ smoke-test: @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=microbit ./examples/easystepper/main.go @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/espat/espconsole/main.go - @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/espat/esphub/main.go - @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/espat/espstation/main.go - @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/flash/console/spi @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/flash/console/qspi @@ -155,14 +149,6 @@ smoke-test: @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd4in2/main.go @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/ntpclient/main.go - @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/udpstation/main.go - @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/tcpclient/main.go - @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/webclient/main.go - @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/ws2812 @md5sum ./build/test.hex tinygo build -size short -o ./build/test.bin -target=m5stamp-c3 ./examples/ws2812 @@ -221,12 +207,6 @@ endif @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/sdcard/tinyfs/ @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/rtl8720dn/webclient/ - @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/rtl8720dn/webserver/ - @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/rtl8720dn/mqttsub/ - @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/i2csoft/adt7410/ @md5sum ./build/test.hex tinygo build -size short -o ./build/test.elf -target=wioterminal ./examples/axp192/m5stack-core2-blinky/ @@ -251,6 +231,38 @@ endif @md5sum ./build/test.uf2 tinygo build -size short -o ./build/test.hex -target=nucleo-wl55jc ./examples/lora/lorawan/atcmd/ @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/net/http-get + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/net/http-head + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=nano-rp2040 ./examples/net/http-post + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=metro-m4-airlift ./examples/net/http-postform + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=matrixportal-m4 ./examples/net/mqttclient + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/net/ntpclient + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/net/rawsocket + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/net/tcpclient + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=nano-rp2040 ./examples/net/tcpecho + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=metro-m4-airlift ./examples/net/tlsclient + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=matrixportal-m4 ./examples/net/webclient + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/net/webclient-tinyterm + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/net/webserver + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=nano-rp2040 ./examples/net/websocket/dial + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=metro-m4-airlift ./examples/net/websocket/handler + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/net/webstatic + @md5sum ./build/test.hex # rwildcard is a recursive version of $(wildcard) From 0b35e03dbf3a95618f39531ee0dee5ec7f8047c2 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Tue, 21 Feb 2023 12:19:14 -0800 Subject: [PATCH 46/47] move netdev to tinygo/src/net --- examples/net/http-get/rtl8720dn.go | 4 +- examples/net/http-get/wifinina.go | 4 +- examples/net/http-head/rtl8720dn.go | 4 +- examples/net/http-head/wifinina.go | 4 +- examples/net/http-post/rtl8720dn.go | 4 +- examples/net/http-post/wifinina.go | 4 +- examples/net/http-postform/rtl8720dn.go | 4 +- examples/net/http-postform/wifinina.go | 4 +- examples/net/mqttclient/rtl8720dn.go | 4 +- examples/net/mqttclient/wifinina.go | 4 +- examples/net/ntpclient/rtl8720dn.go | 4 +- examples/net/ntpclient/wifinina.go | 4 +- examples/net/rawsocket/main.go | 3 +- examples/net/tcpclient/rtl8720dn.go | 4 +- examples/net/tcpclient/wifinina.go | 4 +- examples/net/tcpecho/rtl8720dn.go | 4 +- examples/net/tcpecho/wifinina.go | 4 +- examples/net/tlsclient/rtl8720dn.go | 4 +- examples/net/tlsclient/wifinina.go | 4 +- examples/net/webclient-tinyterm/main.go | 5 +- examples/net/webclient/rtl8720dn.go | 4 +- examples/net/webclient/wifinina.go | 4 +- examples/net/webserver/rtl8720dn.go | 4 +- examples/net/webserver/wifinina.go | 4 +- examples/net/websocket/dial/rtl8720dn.go | 4 +- examples/net/websocket/dial/wifinina.go | 4 +- examples/net/websocket/handler/rtl8720dn.go | 4 +- examples/net/websocket/handler/wifinina.go | 4 +- examples/net/webstatic/rtl8720dn.go | 4 +- examples/net/webstatic/wifinina.go | 4 +- netdev/README.md | 335 -------------------- netdev/netdev.go | 137 -------- netdev/netdev_models.jpg | Bin 65432 -> 0 bytes netdev/socket.go | 143 --------- rtl8720dn/rtl8720dn.go | 3 +- wifinina/wifinina.go | 2 +- 36 files changed, 62 insertions(+), 678 deletions(-) delete mode 100644 netdev/README.md delete mode 100644 netdev/netdev.go delete mode 100644 netdev/netdev_models.jpg delete mode 100644 netdev/socket.go diff --git a/examples/net/http-get/rtl8720dn.go b/examples/net/http-get/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/http-get/rtl8720dn.go +++ b/examples/net/http-get/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/http-get/wifinina.go b/examples/net/http-get/wifinina.go index 658492e71..c3f8e9199 100644 --- a/examples/net/http-get/wifinina.go +++ b/examples/net/http-get/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/http-head/rtl8720dn.go b/examples/net/http-head/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/http-head/rtl8720dn.go +++ b/examples/net/http-head/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/http-head/wifinina.go b/examples/net/http-head/wifinina.go index 658492e71..c3f8e9199 100644 --- a/examples/net/http-head/wifinina.go +++ b/examples/net/http-head/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/http-post/rtl8720dn.go b/examples/net/http-post/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/http-post/rtl8720dn.go +++ b/examples/net/http-post/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/http-post/wifinina.go b/examples/net/http-post/wifinina.go index 658492e71..c3f8e9199 100644 --- a/examples/net/http-post/wifinina.go +++ b/examples/net/http-post/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/http-postform/rtl8720dn.go b/examples/net/http-postform/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/http-postform/rtl8720dn.go +++ b/examples/net/http-postform/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/http-postform/wifinina.go b/examples/net/http-postform/wifinina.go index 658492e71..c3f8e9199 100644 --- a/examples/net/http-postform/wifinina.go +++ b/examples/net/http-postform/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/mqttclient/rtl8720dn.go b/examples/net/mqttclient/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/mqttclient/rtl8720dn.go +++ b/examples/net/mqttclient/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/mqttclient/wifinina.go b/examples/net/mqttclient/wifinina.go index 658492e71..c3f8e9199 100644 --- a/examples/net/mqttclient/wifinina.go +++ b/examples/net/mqttclient/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/ntpclient/rtl8720dn.go b/examples/net/ntpclient/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/ntpclient/rtl8720dn.go +++ b/examples/net/ntpclient/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/ntpclient/wifinina.go b/examples/net/ntpclient/wifinina.go index 07859b1a0..e9151a53c 100644 --- a/examples/net/ntpclient/wifinina.go +++ b/examples/net/ntpclient/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/rawsocket/main.go b/examples/net/rawsocket/main.go index d502a9499..8194f2a26 100644 --- a/examples/net/rawsocket/main.go +++ b/examples/net/rawsocket/main.go @@ -11,11 +11,10 @@ import ( "fmt" "log" "machine" + "net/netdev" "strconv" "strings" "time" - - "tinygo.org/x/drivers/netdev" ) var ( diff --git a/examples/net/tcpclient/rtl8720dn.go b/examples/net/tcpclient/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/tcpclient/rtl8720dn.go +++ b/examples/net/tcpclient/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/tcpclient/wifinina.go b/examples/net/tcpclient/wifinina.go index 07859b1a0..e9151a53c 100644 --- a/examples/net/tcpclient/wifinina.go +++ b/examples/net/tcpclient/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/tcpecho/rtl8720dn.go b/examples/net/tcpecho/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/tcpecho/rtl8720dn.go +++ b/examples/net/tcpecho/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/tcpecho/wifinina.go b/examples/net/tcpecho/wifinina.go index 07859b1a0..e9151a53c 100644 --- a/examples/net/tcpecho/wifinina.go +++ b/examples/net/tcpecho/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/tlsclient/rtl8720dn.go b/examples/net/tlsclient/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/tlsclient/rtl8720dn.go +++ b/examples/net/tlsclient/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/tlsclient/wifinina.go b/examples/net/tlsclient/wifinina.go index 07859b1a0..e9151a53c 100644 --- a/examples/net/tlsclient/wifinina.go +++ b/examples/net/tlsclient/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/webclient-tinyterm/main.go b/examples/net/webclient-tinyterm/main.go index 697101282..507cd820c 100644 --- a/examples/net/webclient-tinyterm/main.go +++ b/examples/net/webclient-tinyterm/main.go @@ -16,13 +16,14 @@ import ( "io" "log" "machine" + "net" "net/http" + "net/netdev" "net/url" "strings" "time" "tinygo.org/x/drivers/ili9341" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" "tinygo.org/x/tinyfont/proggy" "tinygo.org/x/tinyterm" @@ -101,7 +102,7 @@ func main() { fmt.Fprintf(terminal, "Connecting to %s...\r\n", ssid) dev.NetNotify(notify) - netdev.Use(dev) + net.UseNetdev(dev) if err := dev.NetConnect(); err != nil { log.Fatal(err) diff --git a/examples/net/webclient/rtl8720dn.go b/examples/net/webclient/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/webclient/rtl8720dn.go +++ b/examples/net/webclient/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/webclient/wifinina.go b/examples/net/webclient/wifinina.go index 07859b1a0..e9151a53c 100644 --- a/examples/net/webclient/wifinina.go +++ b/examples/net/webclient/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/webserver/rtl8720dn.go b/examples/net/webserver/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/webserver/rtl8720dn.go +++ b/examples/net/webserver/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/webserver/wifinina.go b/examples/net/webserver/wifinina.go index 658492e71..c3f8e9199 100644 --- a/examples/net/webserver/wifinina.go +++ b/examples/net/webserver/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/websocket/dial/rtl8720dn.go b/examples/net/websocket/dial/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/websocket/dial/rtl8720dn.go +++ b/examples/net/websocket/dial/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/websocket/dial/wifinina.go b/examples/net/websocket/dial/wifinina.go index 658492e71..c3f8e9199 100644 --- a/examples/net/websocket/dial/wifinina.go +++ b/examples/net/websocket/dial/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/websocket/handler/rtl8720dn.go b/examples/net/websocket/handler/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/websocket/handler/rtl8720dn.go +++ b/examples/net/websocket/handler/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/websocket/handler/wifinina.go b/examples/net/websocket/handler/wifinina.go index 658492e71..c3f8e9199 100644 --- a/examples/net/websocket/handler/wifinina.go +++ b/examples/net/websocket/handler/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/webstatic/rtl8720dn.go b/examples/net/webstatic/rtl8720dn.go index 73da6a867..871a88acd 100644 --- a/examples/net/webstatic/rtl8720dn.go +++ b/examples/net/webstatic/rtl8720dn.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/rtl8720dn" ) @@ -30,7 +30,7 @@ var cfg = rtl8720dn.Config{ var dev = rtl8720dn.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/examples/net/webstatic/wifinina.go b/examples/net/webstatic/wifinina.go index 658492e71..c3f8e9199 100644 --- a/examples/net/webstatic/wifinina.go +++ b/examples/net/webstatic/wifinina.go @@ -6,9 +6,9 @@ package main import ( "machine" + "net" "time" - "tinygo.org/x/drivers/netdev" "tinygo.org/x/drivers/wifinina" ) @@ -34,7 +34,7 @@ var cfg = wifinina.Config{ var dev = wifinina.New(&cfg) func NetConnect() error { - netdev.Use(dev) + net.UseNetdev(dev) return dev.NetConnect() } diff --git a/netdev/README.md b/netdev/README.md deleted file mode 100644 index b4969e5ef..000000000 --- a/netdev/README.md +++ /dev/null @@ -1,335 +0,0 @@ -# Netdev - -#### Table of Contents - -- [Overview](#overview) -- [Using "net" Package](#using-net-package) -- [Using "net/http" Package](#using-nethttp-package) -- [Using "crypto/tls" Package](#using-cryptotls-package) -- [Using Raw Sockets](#using-raw-sockets) -- [Writing a New Driver](#writing-a-new-driver) - -## Overview - -Netdev is TinyGo's network device driver model. - -Let's see where netdev fits in the network stack. The diagram below shows the traditional full OS stack vs. different possible embedded stacks for TinyGo. Notice the application is written to the same net.Conn interface for all cases. - -![Netdev models](netdev_models.jpg) - -In the (1) Go full OS stack, the network driver, aka netdev, sits above hardware (the "nic") and below TCP/IP. The netdev provides a raw packet interface to TCP/IP. - -For TinyGo, the netdev includes TCP/IP and provides a socket(2) interface to the "net" package. Applications are written to the net.Conn interfaces: TCPConn, UDPConn, and TLSConn. net.Conn functions call netdev socket(2) calls, which in turn call into firmware/hardware. Let's consider the three use cases: - -#### (2) Firware Offload Model - -Here the networking device is a co-controller installed with firmware running a full TCP/IP stack. Firmware manages the TCP/IP connection state with the network. - -The netdev driver runs on the main controller and talks to the co-controller's firmware interface using UART/SPI/etc. The netdev driver translates socket(2) calls to the firmware's TCP/IP calls. - -The wifinina (ESP32), espat (ESP32-AT), and rtl8720dn netdev drivers are examples of the firmware offload model. - -#### (3) Full Stack Model - -Here the netdev includes the TCP/IP stack. There is no co-controller. - -#### (4) "Bring-Your-Own-net.Comm" Model - -Here the netdev is the entire stack, accessing hardware on the bottom and serving up net.Conn connections to applications. - -## Using "net" Package - -Ideally, TinyGo's "net" package would be Go's "net" package and applications using "net" would just work, as-is. TinyGo's net package is a partial port from Go's net package, replacing OS socket syscalls with netdev socket calls. TinyGo's net package is a subset of Go's net package. There are a few features excluded during the porting process, in particular: - -- No IPv6 support -- No HTTP/2 support -- No TLS support for HTTP servers (no https servers) -- No DualStack support -- HTTP client request can't be reused - -Run ```go doc -all ./src/net``` on tinygo directory to see full listing. - -Applications using Go's net package will need a few setup steps to work with TinyGo's net package. - -### Step 1: Create the netdev for your target device. - -The available netdev are: - -- [wifinina]: SPI to ESP32 WiFi co-controller running Arduino WiFiNINA firmware - - targets: pyportal arduino_nano33 nano_rp2040 metro_m4_airlift arduino_mkrwifi1010 matrixportal_m4 - -- [rtl8720dn]: UART to RealTek WiFi rtl8720dn co-controller - - targets: wioterminal - -- [espat]: UART to ESP32/ESP8266 WiFi co-controller running Espressif AT firmware - - targets: TBD - -This example configures and creates a wifinina netdev using New(). - -```go -import "tinygo.org/x/drivers/wifinina" - -func main() { - cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} - dev := wifinina.New(&cfg) - ... -} -``` - -The Config structure is netdev-specific; consult the netdev package for Config details. In this case, the WiFi credentials are passed. - -### Step 2: Hook the netdev into the net package - -Tell the net package to use the netdev by calling netdev.Use(). Continuing with the wifinina example: - -```go -import "tinygo.org/x/drivers/netdev" -import "tinygo.org/x/drivers/wifinina" - -func main() { - cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} - dev := wifinina.New(&cfg) - netdev.Use(dev) - ... -} -``` - -Now, the net package is linked to the netdev so any net I/O will go through the netdev. Calls to net.Dial(), net.Listen() etc will translate to netdev socket calls. - -The last step is to connect the netdev to an IP network. - -### Step 3: Connect to an IP Network - -Before the net package is fully functional, connect the netdev to an underlying IP network. For example, a WiFi netdev would connect to a WiFi access point or become a WiFi access point; either way, once connected, the netdev has a station IP address and is connected on the IP network. - -Call dev.NetConnect() to connect the device to an IP network. Call dev.NetDisconnect() to disconnect. Continuing example: - -```go -import "tinygo.org/x/drivers/netdev" -import "tinygo.org/x/drivers/wifinina" - -func main() { - cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} - dev := wifinina.New(&cfg) - netdev.Use(dev) - - dev.NetConnect() - - // net package calls here - - dev.NetDisconnect() -} -``` - -Optionally, get notified of IP network connects and disconnects: - -```go - dev.Notify(func(e netdev.Event) { - switch e { - case netdev.EventNetUp: - println("Network UP") - case netdev.EventNetDown: - println("Network DOWN") - }) -``` - -Here is a simple http server listening on port :8080, before and after porting from Go "net/http": - -#### Before -```go -package main - -import ( - "fmt" - "net/http" -) - -func main() { - http.HandleFunc("/", HelloServer) - http.ListenAndServe(":8080", nil) -} - -func HelloServer(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) -} -``` - -#### After -```go -package main - -import ( - "fmt" - "net/http" - - "tinygo.org/x/drivers/netdev" - "tinygo.org/x/drivers/wifinina" -) - -func main() { - cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} - dev := wifinina.New(&cfg) - netdev.Use(dev) - dev.NetConnect() - - http.HandleFunc("/", HelloServer) - http.ListenAndServe(":8080", nil) -} - -func HelloServer(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) -} -``` - -## Using "net/http" Package - -TinyGo's net/http package is a partial port of Go's net/http package, providing a subset of the full net/http package. - -HTTP client methods (http.Get, http.Head, http.Post, and http.PostForm) are functional. Dial clients support both HTTP and HTTPS URLs. - -HTTP server methods and objects are mostly ported, but for HTTP only; HTTPS servers are not supported. - -HTTP request and response handling code is mostly ported, so most the intricacy of parsing and writing headers is handled as in the full net/http package. - -Run ```go doc -all ./src/net/http``` on tinygo directory to see full listing. - -## Using "crypto/tls" Package - -TinyGo's TLS support (crypto/tls) relies on hardware offload of the TLS protocol. This is different from Go's crypto/tls package which handles the TLS protocol in software. - -TinyGo's TLS support is only available for client applications. You can http.Get() to an http:// or https:// address, but you cannot http.ListenAndServeTLS() an https server. - -The offloading hardware has pre-defined TLS certificates built-in. - -## Using Raw Sockets - -A netdev implements the Socketer interface so an application can make raw socket calls, bypassing the net package. - -Here is a simple TCP application using raw sockets: - -```go -package main - -import ( - "tinygo.org/x/drivers/netdev" - "tinygo.org/x/drivers/wifinina" -) - -func main() { - cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} - dev := wifinina.New(&cfg) - netdev.Use(dev) - - // ignoring error handling - - dev.NetConnect() - - sock, _ := dev.Socket(netdev.AF_INET, netdev.SOCK_STREAM, netdev.IPPROTO_TCP) - - sockAddr := netdev.NewSockAddr("", netdev.Port(8080), netdev.ParseIP("10.0.0.100") - dev.Connect(sock, sockAddr) - - dev.Send(sock, []bytes("hello"), 0, 0) - - dev.Close(sock) -} -``` - -## Writing a New Driver - -Netdev drivers implement the netdev.Netdever interface, which includes the netdev.Socketer interface. The Socketer interface is modeled after BSD socket(2). TinyGo's net package translates net.Conn calls into netdev Socketer calls. For example, DialTCP calls netdev.Socket() and netdev.Connect(): - -```go -func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error) { - - fd, _ := netdev.Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) - - addr := NewSockAddr("", uint16(raddr.Port), raddr.IP) - - netdev.Connect(fd, addr) - - return &TCPConn{ - fd: fd, - laddr: laddr, - raddr: raddr, - }, nil -} -``` - -### Netdever Interface - -A netdev driver implements the Netdever interface: - -```go -// Netdev drivers implement the Netdever interface. -// -// A Netdever is passed to the "net" package using netdev.Use(). -// -// Just like a net.Conn, multiple goroutines may invoke methods on a Netdever -// simultaneously. -type Netdever interface { - - // NetConnect device to IP network - NetConnect() error - - // NetDisconnect device from IP network - NetDisconnect() - - // NetNotify to register callback for network events - NetNotify(func(Event)) - - // GetHostByName returns the IP address of either a hostname or IPv4 - // address in standard dot notation - GetHostByName(name string) (IP, error) - - // GetHardwareAddr returns device MAC address - GetHardwareAddr() (HardwareAddr, error) - - // GetIPAddr returns IP address assigned to device, either by DHCP or - // statically - GetIPAddr() (IP, error) - - // Socketer is a Berkely Sockets-like interface - Socketer -} -``` - -### Socketer Interface - -```go -// Berkely Sockets-like interface. See man page for socket(2), etc. -// -// Multiple goroutines may invoke methods on a Socketer simultaneously. -type Socketer interface { - Socket(family AddressFamily, sockType SockType, protocol Protocol) (Sockfd, error) - Bind(sockfd Sockfd, myaddr SockAddr) error - Connect(sockfd Sockfd, servaddr SockAddr) error - Listen(sockfd Sockfd, backlog int) error - Accept(sockfd Sockfd, peer SockAddr) (Sockfd, error) - Send(sockfd Sockfd, buf []byte, flags SockFlags, timeout time.Duration) (int, error) - Recv(sockfd Sockfd, buf []byte, flags SockFlags, timeout time.Duration) (int, error) - Close(sockfd Sockfd) error - SetSockOpt(sockfd Sockfd, level SockOptLevel, opt SockOpt, value any) error -} -``` - -Socketer interface is intended to mimic a subset of BSD socket(2). They've been Go-ified, but should otherwise maintain the semantics of the original socket(2) calls. Send and Recv add a timeout to put a limit on blocking operations. Recv in paricular is blocking and will block until data arrives on the socket or EOF. The timeout value is calculated from net.Conn's SetDeadline(), typically. - -#### Locking - -Multiple goroutines may invoke methods on a net.Conn simultaneously, and since the net package translates net.Conn calls into Socketer calls, it follows that multiple goroutines may invoke Socketer calls, so locking is required to keep Socketer calls from stepping on one another. - -Don't hold a lock while Time.Sleep()ing waiting for a hardware operation to finish. Unlocking while sleeping let's other goroutines make progress. If the sleep period is really small, then you can get away with holding the lock. - -#### Sockfd - -The Socketer interface uses a socket fd to represent a socket connection (end-point). Each net.Conn maps 1:1 to a fd. The number of fds available is a netdev hardware limitation. Wifinina, for example, can hand out 10 socket fds. - -### Testing - -The netdev driver should minimally run all of the example/net examples. - -TODO: automate testing to catch regressions. diff --git a/netdev/netdev.go b/netdev/netdev.go deleted file mode 100644 index 4527c62f9..000000000 --- a/netdev/netdev.go +++ /dev/null @@ -1,137 +0,0 @@ -// Netdev is TinyGo's network device driver model. TinyGo's "net" package -// interfaces to the netdev driver directly to provide TCPConn, UDPConn, -// and TLSConn socket connections. - -package netdev - -import ( - "errors" - "fmt" - "strconv" - "strings" - _ "unsafe" -) - -//go:linkname Use net.useNetdev -func Use(netdev Netdever) - -type HardwareAddr []byte - -const hexDigit = "0123456789abcdef" - -func (a HardwareAddr) String() string { - if len(a) == 0 { - return "" - } - buf := make([]byte, 0, len(a)*3-1) - for i, b := range a { - if i > 0 { - buf = append(buf, ':') - } - buf = append(buf, hexDigit[b>>4]) - buf = append(buf, hexDigit[b&0xF]) - } - return string(buf) -} - -func ParseHardwareAddr(s string) HardwareAddr { - parts := strings.Split(s, ":") - if len(parts) != 6 { - return nil - } - - var mac []byte - for _, part := range parts { - b, err := strconv.ParseInt(part, 16, 64) - if err != nil { - return nil - } - mac = append(mac, byte(b)) - } - - return HardwareAddr(mac) -} - -type Port uint16 // host byte-order - -type IP [4]byte - -func (ip IP) String() string { - return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]) -} - -func ParseIP(s string) IP { - var result IP - octets := strings.Split(s, ".") - if len(octets) != 4 { - return IP{} - } - for i, octet := range octets { - v, err := strconv.Atoi(octet) - if err != nil { - return IP{} - } - result[i] = byte(v) - } - return result -} - -// NetConnect() errors -var ErrConnected = errors.New("Already connected") -var ErrConnectFailed = errors.New("Connect failed") -var ErrConnectTimeout = errors.New("Connect timed out") -var ErrMissingSSID = errors.New("Missing WiFi SSID") -var ErrStartingDHCPClient = errors.New("Error starting DHPC client") - -// GethostByName() errors -var ErrHostUnknown = errors.New("Host unknown") - -// Socketer errors -var ErrFamilyNotSupported = errors.New("Address family not supported") -var ErrProtocolNotSupported = errors.New("Socket protocol/type not supported") -var ErrNoMoreSockets = errors.New("No more sockets") -var ErrClosingSocket = errors.New("Error closing socket") -var ErrNotSupported = errors.New("Not supported") -var ErrRecvTimeout = errors.New("Recv timeout expired") - -type Event int - -// NetNotify network events -const ( - // The device's network connection is now UP - EventNetUp Event = iota - // The device's network connection is now DOWN - EventNetDown -) - -// Netdev drivers implement the Netdever interface. -// -// A Netdever is passed to the "net" package using netdev.Use(). -// -// Just like a net.Conn, multiple goroutines may invoke methods on a Netdever -// simultaneously. -type Netdever interface { - - // NetConnect device to IP network - NetConnect() error - - // NetDisconnect device from IP network - NetDisconnect() - - // NetNotify to register callback for network events - NetNotify(func(Event)) - - // GetHostByName returns the IP address of either a hostname or IPv4 - // address in standard dot notation - GetHostByName(name string) (IP, error) - - // GetHardwareAddr returns device MAC address - GetHardwareAddr() (HardwareAddr, error) - - // GetIPAddr returns IP address assigned to device, either by DHCP or - // statically - GetIPAddr() (IP, error) - - // Socketer is a Berkely Sockets-like interface - Socketer -} diff --git a/netdev/netdev_models.jpg b/netdev/netdev_models.jpg deleted file mode 100644 index 04abc822d6fa0880abbdfa641094ed8b4aa889fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65432 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 diff --git a/netdev/socket.go b/netdev/socket.go deleted file mode 100644 index 2f3e112a4..000000000 --- a/netdev/socket.go +++ /dev/null @@ -1,143 +0,0 @@ -// Netdev socket interface - -package netdev - -import ( - "fmt" - "time" -) - -type AddressFamily int - -const ( - AF_UNSPEC = 0 - AF_INET = 2 - // Currently not supporting AF_INET6 (IPv6) -) - -func (f AddressFamily) String() string { - switch f { - case AF_INET: - return "INET" - } - return "Unknown" -} - -type SockType int - -const ( - SOCK_STREAM = 1 - SOCK_DGRAM = 2 -) - -func (t SockType) String() string { - switch t { - case SOCK_STREAM: - return "STREAM" - case SOCK_DGRAM: - return "DATAGRAM" - } - return "Unknown" -} - -type Protocol int - -const ( - IPPROTO_TCP = 0x06 - IPPROTO_UDP = 0x11 - // Made up, not a real IP protocol number. This is used to create - // a TLS socket on the device, assuming the device supports mbed TLS. - IPPROTO_TLS = 0xFE -) - -func (p Protocol) String() string { - switch p { - case IPPROTO_TCP: - return "TCP" - case IPPROTO_UDP: - return "UDP" - case IPPROTO_TLS: - return "TLS" - } - return "Unknown" -} - -// sockaddr_in -type SockAddr struct { - host string - port Port - ip IP -} - -func NewSockAddr(host string, port Port, ip IP) SockAddr { - return SockAddr{host: host, port: port, ip: ip} -} - -func (a SockAddr) String() string { - if a.host == "" { - return fmt.Sprintf("%s:%d", a.ip.String(), a.port) - } - return fmt.Sprintf("%s:%d", a.host, a.port) -} - -func (a SockAddr) Port() uint16 { - return uint16(a.port) -} - -func (a SockAddr) IpUint32() uint32 { - return uint32(a.ip[0])<<24 | - uint32(a.ip[1])<<16 | - uint32(a.ip[2])<<8 | - uint32(a.ip[3]) -} - -func (a SockAddr) IpBytes() []byte { - return a.ip[:] -} - -func (a SockAddr) Host() string { - return a.host -} - -func (a SockAddr) Ip() IP { - return a.ip -} - -type SockFlags int - -const ( - MSG_OOB SockFlags = 1 - MSG_PEEK = 2 - MSG_DONTROUTE = 4 -) - -type SockOpt int - -const ( - SO_KEEPALIVE SockOpt = 9 // Value: bool - TCP_KEEPINTVL = 5 // Interval between keepalives, value (1/2 sec units) -) - -type SockOptLevel int - -const ( - SOL_SOCKET SockOptLevel = 1 - SOL_TCP = 6 -) - -type Sockfd int - -// Berkely Sockets-like interface. See man page for socket(2), etc. -// -// Multiple goroutines may invoke methods on a Socketer simultaneously. -type Socketer interface { - Socket(family AddressFamily, sockType SockType, protocol Protocol) (Sockfd, error) - Bind(sockfd Sockfd, myaddr SockAddr) error - Connect(sockfd Sockfd, servaddr SockAddr) error - Listen(sockfd Sockfd, backlog int) error - Accept(sockfd Sockfd, peer SockAddr) (Sockfd, error) - Send(sockfd Sockfd, buf []byte, flags SockFlags, timeout time.Duration) (int, error) - Recv(sockfd Sockfd, buf []byte, flags SockFlags, timeout time.Duration) (int, error) - Close(sockfd Sockfd) error - SetSockOpt(sockfd Sockfd, level SockOptLevel, opt SockOpt, value any) error -} diff --git a/rtl8720dn/rtl8720dn.go b/rtl8720dn/rtl8720dn.go index 35bfff496..cc1c1e71a 100644 --- a/rtl8720dn/rtl8720dn.go +++ b/rtl8720dn/rtl8720dn.go @@ -11,11 +11,10 @@ import ( "fmt" "io" "machine" + "net/netdev" "strings" "sync" "time" - - "tinygo.org/x/drivers/netdev" ) var _debug debug = debugBasic diff --git a/wifinina/wifinina.go b/wifinina/wifinina.go index ea6f008df..98c77005d 100644 --- a/wifinina/wifinina.go +++ b/wifinina/wifinina.go @@ -16,11 +16,11 @@ import ( "io" "machine" "math/bits" + "net/netdev" "sync" "time" "tinygo.org/x/drivers" - "tinygo.org/x/drivers/netdev" ) var _debug debug = debugBasic From 98d16d6c1d54a48f018125d01a8e599d7227dd8e Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Sat, 4 Mar 2023 16:06:16 -0800 Subject: [PATCH 47/47] Use mirrored netdever interface Changes based on feedback from Ben and Patricio: 0) Move netdever interface to "drivers" package 1) Mirror netdever interface with same in drivers repo 2) Use go:linkname to link to net.useNetdev() 3) Use native Go types in netdever interface 4) Add Netlinker interface to "drivers" package 5) Update documentation, add new README-net.md file --- README-net.md | 289 ++++++++++++++++++++ espat/espat.go | 124 +++++---- espat/wifi.go | 3 +- examples/net/espat/main.go | 9 +- examples/net/http-get/main.go | 2 +- examples/net/http-get/rtl8720dn.go | 12 +- examples/net/http-get/wifinina.go | 12 +- examples/net/http-head/main.go | 4 +- examples/net/http-head/rtl8720dn.go | 12 +- examples/net/http-head/wifinina.go | 12 +- examples/net/http-post/main.go | 4 +- examples/net/http-post/rtl8720dn.go | 12 +- examples/net/http-post/wifinina.go | 12 +- examples/net/http-postform/main.go | 2 +- examples/net/http-postform/rtl8720dn.go | 12 +- examples/net/http-postform/wifinina.go | 12 +- examples/net/mqttclient/main.go | 2 +- examples/net/mqttclient/rtl8720dn.go | 12 +- examples/net/mqttclient/wifinina.go | 12 +- examples/net/ntpclient/main.go | 4 +- examples/net/ntpclient/rtl8720dn.go | 12 +- examples/net/ntpclient/wifinina.go | 12 +- examples/net/rawsocket/main.go | 25 +- examples/net/rawsocket/rtl8720dn.go | 10 +- examples/net/rawsocket/wifinina.go | 10 +- examples/net/tcpclient/main.go | 2 +- examples/net/tcpclient/rtl8720dn.go | 12 +- examples/net/tcpclient/wifinina.go | 12 +- examples/net/tcpecho/main.go | 2 +- examples/net/tcpecho/rtl8720dn.go | 12 +- examples/net/tcpecho/wifinina.go | 12 +- examples/net/tlsclient/main.go | 2 +- examples/net/tlsclient/rtl8720dn.go | 12 +- examples/net/tlsclient/wifinina.go | 12 +- examples/net/webclient-tinyterm/main.go | 16 +- examples/net/webclient/main.go | 2 +- examples/net/webclient/rtl8720dn.go | 12 +- examples/net/webclient/wifinina.go | 12 +- examples/net/webserver/main.go | 2 +- examples/net/webserver/rtl8720dn.go | 12 +- examples/net/webserver/wifinina.go | 12 +- examples/net/websocket/dial/main.go | 2 +- examples/net/websocket/dial/rtl8720dn.go | 12 +- examples/net/websocket/dial/wifinina.go | 12 +- examples/net/websocket/handler/main.go | 2 +- examples/net/websocket/handler/rtl8720dn.go | 12 +- examples/net/websocket/handler/wifinina.go | 12 +- examples/net/webstatic/main.go | 2 +- examples/net/webstatic/rtl8720dn.go | 12 +- examples/net/webstatic/wifinina.go | 12 +- netdev.go | 59 ++++ netlink.go | 49 ++++ rtl8720dn/rtl8720dn.go | 213 ++++++++------- wifinina/wifinina.go | 198 ++++++++------ 54 files changed, 750 insertions(+), 625 deletions(-) create mode 100644 README-net.md create mode 100644 netdev.go create mode 100644 netlink.go diff --git a/README-net.md b/README-net.md new file mode 100644 index 000000000..2a126282b --- /dev/null +++ b/README-net.md @@ -0,0 +1,289 @@ +#### Table of Contents + +- ["net" Package](#net-package) +- [Using "net" Package](#using-net-package) +- [Using "net/http" Package](#using-nethttp-package) +- [Using "crypto/tls" Package](#using-cryptotls-package) +- [Using Raw Sockets](#using-raw-sockets) +- [Netdev and Netlink](#netdev-and-netlink) +- [Writing a New Netdev Driver](#writing-a-new-netdev-driver) + +## "net" Package + +TinyGo's "net" package is ported from Go. The port offers a subset of Go's +"net" package. The subset maintains Go 1 compatiblity guarantee. A Go +application that uses "net" will most-likey just work on TinyGo if the usage is +within the subset offered. (There may be external constraints such as limited +SRAM on embedded environment that may limit full functionality). + +Continue below for details on using "net" and "net/http" packages. + +See src/net/READMD.md in the TinyGo repo for more details on maintaining +TinyGo's "net" package. + +## Using "net" Package + +Ideally, TinyGo's "net" package would be Go's "net" package and applications +using "net" would just work, as-is. TinyGo's net package is a partial port +from Go's net package, replacing OS socket syscalls with netdev socket calls. + +Netdev is TinyGo's network device driver model; read more about +[Netdev](#netdev-and-netlink). + +There are a few features excluded during the porting process, in particular: + +- No IPv6 support +- No DualStack support + +Run ```go doc -all ./src/net``` in TinyGo repo to see full listing. + +Applications using Go's net package will need a few setup steps to work with +TinyGo's net package. + +### Step 1: Create the netdev for your target device. + +The available netdev are: + +- [wifinina]: ESP32 WiFi co-controller running Arduino WiFiNINA firmware + + targets: pyportal arduino_nano33 nano_rp2040 metro_m4_airlift + arduino_mkrwifi1010 matrixportal_m4 + +- [rtl8720dn]: RealTek WiFi rtl8720dn co-controller + + targets: wioterminal + +- [espat]: ESP32/ESP8266 WiFi co-controller running Espressif AT firmware + + targets: TBD + +This example configures and creates a wifinina netdev using New(). + +```go +import "tinygo.org/x/drivers/wifinina" + +func main() { + cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} + netdev := wifinina.New(&cfg) + ... +} +``` + +New() registers the netdev with the "net" package using net.useNetdev(). + +The Config structure is netdev-specific; consult the specific netdev package +for Config details. In this case, the WiFi credentials are passed, but other +settings are typically passed such as device configuration. + +### Step 2: Connect to an IP Network + +Before the net package is fully functional, connect the netdev to an underlying +IP network. For example, a WiFi netdev would connect to a WiFi access point or +become a WiFi access point; either way, once connected, the netdev has a +station IP address and is connected on the IP network. Similarly, a LTE netdev +would connect to a LTE provider, giving the device an IP address on the LTE +network. + +Using the Netlinker interface, Call netdev.NetConnect() to connect the device +to an IP network. Call netdev.NetDisconnect() to disconnect. Continuing example: + +```go +import ( + "tinygo.org/x/drivers/wifinina" +) + +func main() { + cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} + netdev := wifinina.New(&cfg) + + netdev.NetConnect() + + // "net" package calls here + + netdev.NetDisconnect() +} +``` + +Optionally, get notified of IP network connects and disconnects: + +```go + netdev.Notify(func(e drivers.NetlinkEvent) { + switch e { + case drivers.NetlinkEventNetUp: + println("Network UP") + case drivers.NetlinkEventNetDown: + println("Network DOWN") + }) +``` + +Here is a simple example of an http server listening on port :8080, before and +after: + +#### Before +```go +package main + +import ( + "fmt" + "net/http" +) + +func main() { + http.HandleFunc("/", HelloServer) + http.ListenAndServe(":8080", nil) +} + +func HelloServer(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) +} +``` + +#### After +```go +package main + +import ( + "fmt" + "net/http" + + "tinygo.org/x/drivers/wifinina" +) + +func main() { + cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} + netdev := wifinina.New(&cfg) + netdev.NetConnect() + + http.HandleFunc("/", HelloServer) + http.ListenAndServe(":8080", nil) +} + +func HelloServer(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) +} +``` + +## Using "net/http" Package + +TinyGo's net/http package is a partial port of Go's net/http package, providing +a subset of the full net/http package. There are a few features excluded +during the porting process, in particular: + +- No HTTP/2 support +- No TLS support for HTTP servers (no https servers) +- HTTP client request can't be reused + +HTTP client methods (http.Get, http.Head, http.Post, and http.PostForm) are +functional. Dial clients support both HTTP and HTTPS URLs. + +HTTP server methods and objects are mostly ported, but for HTTP only; HTTPS +servers are not supported. + +HTTP request and response handling code is mostly ported, so most the intricacy +of parsing and writing headers is handled as in the full net/http package. + +Run ```go doc -all ./src/net/http``` in TinyGo repo to see full listing. + +## Using "crypto/tls" Package + +TinyGo's TLS support (crypto/tls) relies on hardware offload of the TLS +protocol. This is different from Go's crypto/tls package which handles the TLS +protocol in software. + +TinyGo's TLS support is only available for client applications. You can +http.Get() to an http:// or https:// address, but you cannot +http.ListenAndServeTLS() an https server. + +The offloading hardware has pre-defined TLS certificates built-in. + +## Using Raw Sockets + +A netdev implements a BSD socket-like interface so an application can make raw +socket calls, bypassing the net package. + +Here is a simple TCP application using raw sockets: + +```go +package main + +import ( + "net" // only need to parse IP address + "syscall" + + "tinygo.org/x/drivers/wifinina" +) + +func main() { + cfg := wifinina.Config{Ssid: "foo", Passphrase: "bar"} + netdev := wifinina.New(&cfg) + + // ignoring error handling + + netdev.NetConnect() + + sock, _ := netdev.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + + netdev.Connect(sock, "", net.ParseIP("10.0.0.100"), 8080) + netdev.Send(sock, []bytes("hello"), 0, 0) + + netdev.Close(sock) +} +``` + +## Netdev and Netlink + +Netdev is TinyGo's network device driver model. Network drivers implement the +netdever interface, providing a common network I/O interface to TinyGo's "net" +package. The interface is modeled after the BSD socket interface. net.Conn +implementations (TCPConn, UDPConn, and TLSConn) use the netdev interface for +device I/O access. For example, net.DialTCP, which returns a net.TCPConn, +calls netdev.Socket() and netdev.Connect(): + +```go +func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error) { + + fd, _ := netdev.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + + netdev.Connect(fd, "", raddr.IP, raddr.Port) + + return &TCPConn{ + fd: fd, + laddr: laddr, + raddr: raddr, + }, nil +} +``` + +Network drivers also (optionally) implement the Netlinker interface. This +interface is not used by TinyGo's "net" package, but rather provides the TinyGo +application direct access to the network device for common settings and control +that fall outside of netdev's socket interface. + +## Writing a New Netdev Driver + +A new netdev driver will implement the netdever and optionally the Netlinker +interfaces. See the wifinina or rtl8720dn drivers for examples. + +#### Locking + +Multiple goroutines may invoke methods on a net.Conn simultaneously, and since +the net package translates net.Conn calls into netdev socket calls, it follows +that multiple goroutines may invoke socket calls, so locking is required to +keep socket calls from stepping on one another. + +Don't hold a lock while Time.Sleep()ing waiting for a hardware operation to +finish. Unlocking while sleeping let's other goroutines make progress. If the +sleep period is really small, then you can get away with holding the lock. + +#### Sockfd + +The netdev socket interface uses a socket fd (int) to represent a socket +connection (end-point). Each net.Conn maps 1:1 to a fd. The number of fds +available is a hardware limitation. Wifinina, for example, can hand out 10 +fds. + +### Testing + +The netdev driver should minimally run all of the example/net examples. + +TODO: automate testing to catch regressions. diff --git a/espat/espat.go b/espat/espat.go index e949db96e..f1ca0b5bd 100644 --- a/espat/espat.go +++ b/espat/espat.go @@ -24,8 +24,10 @@ import ( "errors" "fmt" "machine" + "net" "strconv" "strings" + "syscall" "time" "tinygo.org/x/drivers/netdev" @@ -47,24 +49,31 @@ type Config struct { Rx machine.Pin } +type socket struct { + inUse bool + protocol int + lip net.IP + lport int +} + type Device struct { cfg *Config uart *machine.UART // command responses that come back from the ESP8266/ESP32 response []byte // data received from a TCP/UDP connection forwarded by the ESP8266/ESP32 - socketdata []byte - socketInUse bool - socketProtocol netdev.Protocol - socketLaddr netdev.SockAddr + data []byte + socket socket } func New(cfg *Config) *Device { d := Device{ - cfg: cfg, - response: make([]byte, 512), - socketdata: make([]byte, 0, 1024), + cfg: cfg, + response: make([]byte, 512), + data: make([]byte, 0, 1024), } + netdev.Use(&d) + var _ netdev.Ifacer = (*Device)(nil) return &d } @@ -136,84 +145,84 @@ func (d *Device) NetNotify(cb func(netdev.Event)) { // Not supported } -func (d *Device) GetHostByName(name string) (netdev.IP, error) { +func (d *Device) GetHostByName(name string) (net.IP, error) { ip, err := d.GetDNS(name) - return netdev.ParseIP(ip), err + return net.ParseIP(ip), err } -func (d *Device) GetHardwareAddr() (netdev.HardwareAddr, error) { - return netdev.ParseHardwareAddr(d.GetMacAddress()), nil +func (d *Device) GetHardwareAddr() (net.HardwareAddr, error) { + return net.ParseMAC(d.GetMacAddress()) } -func (d *Device) GetIPAddr() (netdev.IP, error) { +func (d *Device) GetIPAddr() (net.IP, error) { ip, err := d.GetClientIP() - return netdev.ParseIP(ip), err + return net.ParseIP(ip), err } -func (d *Device) Socket(family netdev.AddressFamily, sockType netdev.SockType, - protocol netdev.Protocol) (netdev.Sockfd, error) { +func (d *Device) Socket(domain int, stype int, protocol int) (int, error) { - switch family { - case netdev.AF_INET: + switch domain { + case syscall.AF_INET: default: return -1, netdev.ErrFamilyNotSupported } switch { - case protocol == netdev.IPPROTO_TCP && sockType == netdev.SOCK_STREAM: - case protocol == netdev.IPPROTO_TLS && sockType == netdev.SOCK_STREAM: - case protocol == netdev.IPPROTO_UDP && sockType == netdev.SOCK_DGRAM: + case protocol == syscall.IPPROTO_TCP && stype == syscall.SOCK_STREAM: + case protocol == syscall.IPPROTO_TLS && stype == syscall.SOCK_STREAM: + case protocol == syscall.IPPROTO_UDP && stype == syscall.SOCK_DGRAM: default: return -1, netdev.ErrProtocolNotSupported } // Only supporting single connection mode, so only one socket at a time - if d.socketInUse { + if d.socket.inUse { return -1, netdev.ErrNoMoreSockets } - d.socketInUse = true - d.socketProtocol = protocol + d.socket.inUse = true + d.socket.protocol = protocol - return netdev.Sockfd(0), nil + return 0, nil } -func (d *Device) Bind(sockfd netdev.Sockfd, addr netdev.SockAddr) error { - d.socketLaddr = addr +func (d *Device) Bind(sockfd int, ip net.IP, port int) error { + d.socket.lip = ip + d.socket.lport = port return nil } -func (d *Device) Connect(sockfd netdev.Sockfd, servaddr netdev.SockAddr) error { +func (d *Device) Connect(sockfd int, host string, ip net.IP, port int) error { var err error - var addr = servaddr.Ip().String() - var port = fmt.Sprintf("%d", servaddr.Port()) - var lport = fmt.Sprintf("%d", d.socketLaddr.Port()) - - switch d.socketProtocol { - case netdev.IPPROTO_TCP: - err = d.ConnectTCPSocket(addr, port) - case netdev.IPPROTO_UDP: - err = d.ConnectUDPSocket(addr, port, lport) - case netdev.IPPROTO_TLS: - err = d.ConnectSSLSocket(addr, port) + var addr = ip.String() + var sport = strconv.Itoa(port) + var lport = strconv.Itoa(d.socket.lport) + + switch d.socket.protocol { + case syscall.IPPROTO_TCP: + err = d.ConnectTCPSocket(addr, sport) + case syscall.IPPROTO_UDP: + err = d.ConnectUDPSocket(addr, sport, lport) + case syscall.IPPROTO_TLS: + err = d.ConnectSSLSocket(addr, sport) } return err } -func (d *Device) Listen(sockfd netdev.Sockfd, backlog int) error { - switch d.socketProtocol { - case netdev.IPPROTO_UDP: +func (d *Device) Listen(sockfd int, backlog int) error { + switch d.socket.protocol { + case syscall.IPPROTO_UDP: default: return netdev.ErrProtocolNotSupported } return nil } -func (d *Device) Accept(sockfd netdev.Sockfd, peer netdev.SockAddr) (netdev.Sockfd, error) { +func (d *Device) Accept(sockfd int, ip net.IP, port int) (int, error) { return -1, netdev.ErrNotSupported } -func (d *Device) sendChunk(sockfd netdev.Sockfd, buf []byte, timeout time.Duration) (int, error) { +func (d *Device) sendChunk(sockfd int, buf []byte, timeout time.Duration) (int, error) { err := d.StartSocketSend(len(buf)) if err != nil { return -1, err @@ -229,8 +238,7 @@ func (d *Device) sendChunk(sockfd netdev.Sockfd, buf []byte, timeout time.Durati return n, err } -func (d *Device) Send(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, - timeout time.Duration) (int, error) { +func (d *Device) Send(sockfd int, buf []byte, flags int, timeout time.Duration) (int, error) { // Break large bufs into chunks so we don't overrun the hw queue @@ -249,8 +257,7 @@ func (d *Device) Send(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, return len(buf), nil } -func (d *Device) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, - timeout time.Duration) (int, error) { +func (d *Device) Recv(sockfd int, buf []byte, flags int, timeout time.Duration) (int, error) { var length = len(buf) var expire = time.Now().Add(timeout) @@ -281,12 +288,11 @@ func (d *Device) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, } } -func (d *Device) Close(sockfd netdev.Sockfd) error { +func (d *Device) Close(sockfd int) error { return d.DisconnectSocket() } -func (d *Device) SetSockOpt(sockfd netdev.Sockfd, level netdev.SockOptLevel, - opt netdev.SockOpt, value any) error { +func (d *Device) SetSockOpt(sockfd int, level int, opt int, value interface{}) error { return netdev.ErrNotSupported } @@ -370,16 +376,16 @@ func (d *Device) ReadSocket(b []byte) (n int, err error) { d.Response(300) count := len(b) - if len(b) >= len(d.socketdata) { + if len(b) >= len(d.data) { // copy it all, then clear socket data - count = len(d.socketdata) - copy(b, d.socketdata[:count]) - d.socketdata = d.socketdata[:0] + count = len(d.data) + copy(b, d.data[:count]) + d.data = d.data[:0] } else { // copy all we can, then keep the remaining socket data around - copy(b, d.socketdata[:count]) - copy(d.socketdata, d.socketdata[count:]) - d.socketdata = d.socketdata[:len(d.socketdata)-count] + copy(b, d.data[:count]) + copy(d.data, d.data[count:]) + d.data = d.data[:len(d.data)-count] } return count, nil @@ -449,11 +455,11 @@ func (d *Device) parseIPD(end int) error { } // load up the socket data - d.socketdata = append(d.socketdata, d.response[e+1:end]...) + d.data = append(d.data, d.response[e+1:end]...) return nil } // IsSocketDataAvailable returns of there is socket data available func (d *Device) IsSocketDataAvailable() bool { - return len(d.socketdata) > 0 || d.uart.Buffered() > 0 + return len(d.data) > 0 || d.uart.Buffered() > 0 } diff --git a/espat/wifi.go b/espat/wifi.go index ff752234f..203dd9390 100644 --- a/espat/wifi.go +++ b/espat/wifi.go @@ -54,8 +54,7 @@ func (d *Device) DisconnectFromAP() error { return err } -// GetClientIP returns the ESP8266/ESP32 current station MAC addess when -// connected to an Access Point. +// GetMacAddress returns the ESP8266/ESP32 current station MAC addess func (d *Device) GetMacAddress() string { d.Query(SetStationMACAddress) r, err := d.Response(1000) diff --git a/examples/net/espat/main.go b/examples/net/espat/main.go index 539049a9a..2c9660756 100644 --- a/examples/net/espat/main.go +++ b/examples/net/espat/main.go @@ -9,7 +9,6 @@ import ( "time" "tinygo.org/x/drivers/espat" - "tinygo.org/x/drivers/netdev" ) var ( @@ -19,15 +18,14 @@ var ( ) var ( - netcfg = espat.Config{ + cfg = espat.Config{ Ssid: ssid, Passphrase: pass, Uart: machine.UART2, Tx: machine.TX1, Rx: machine.RX0, } - - dev = espat.New(&netcfg) + netdev = espat.New(&cfg) ) var buf = &bytes.Buffer{} @@ -36,8 +34,7 @@ func main() { waitSerial() - netdev.Use(dev) - if err := dev.NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/http-get/main.go b/examples/net/http-get/main.go index 9028da578..210c48f91 100644 --- a/examples/net/http-get/main.go +++ b/examples/net/http-get/main.go @@ -31,7 +31,7 @@ func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/http-get/rtl8720dn.go b/examples/net/http-get/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/http-get/rtl8720dn.go +++ b/examples/net/http-get/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/http-get/wifinina.go b/examples/net/http-get/wifinina.go index c3f8e9199..fd8b29129 100644 --- a/examples/net/http-get/wifinina.go +++ b/examples/net/http-get/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/http-head/main.go b/examples/net/http-head/main.go index 641f7c227..1b0dcae3e 100644 --- a/examples/net/http-head/main.go +++ b/examples/net/http-head/main.go @@ -30,7 +30,7 @@ func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } @@ -46,7 +46,7 @@ func main() { } fmt.Println(string(buf.Bytes())) - NetDisconnect() + netdev.NetDisconnect() } // Wait for user to open serial console diff --git a/examples/net/http-head/rtl8720dn.go b/examples/net/http-head/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/http-head/rtl8720dn.go +++ b/examples/net/http-head/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/http-head/wifinina.go b/examples/net/http-head/wifinina.go index c3f8e9199..fd8b29129 100644 --- a/examples/net/http-head/wifinina.go +++ b/examples/net/http-head/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/http-post/main.go b/examples/net/http-post/main.go index 79d2134d7..e1506a338 100644 --- a/examples/net/http-post/main.go +++ b/examples/net/http-post/main.go @@ -30,7 +30,7 @@ func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } @@ -50,7 +50,7 @@ func main() { fmt.Println(string(body)) - NetDisconnect() + netdev.NetDisconnect() } // Wait for user to open serial console diff --git a/examples/net/http-post/rtl8720dn.go b/examples/net/http-post/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/http-post/rtl8720dn.go +++ b/examples/net/http-post/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/http-post/wifinina.go b/examples/net/http-post/wifinina.go index c3f8e9199..fd8b29129 100644 --- a/examples/net/http-post/wifinina.go +++ b/examples/net/http-post/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/http-postform/main.go b/examples/net/http-postform/main.go index a88d560dd..4c7ac59be 100644 --- a/examples/net/http-postform/main.go +++ b/examples/net/http-postform/main.go @@ -30,7 +30,7 @@ func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/http-postform/rtl8720dn.go b/examples/net/http-postform/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/http-postform/rtl8720dn.go +++ b/examples/net/http-postform/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/http-postform/wifinina.go b/examples/net/http-postform/wifinina.go index c3f8e9199..fd8b29129 100644 --- a/examples/net/http-postform/wifinina.go +++ b/examples/net/http-postform/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/mqttclient/main.go b/examples/net/mqttclient/main.go index cd7a0059f..c2b2ad44a 100644 --- a/examples/net/mqttclient/main.go +++ b/examples/net/mqttclient/main.go @@ -36,7 +36,7 @@ var connectionLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/mqttclient/rtl8720dn.go b/examples/net/mqttclient/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/mqttclient/rtl8720dn.go +++ b/examples/net/mqttclient/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/mqttclient/wifinina.go b/examples/net/mqttclient/wifinina.go index c3f8e9199..fd8b29129 100644 --- a/examples/net/mqttclient/wifinina.go +++ b/examples/net/mqttclient/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/ntpclient/main.go b/examples/net/ntpclient/main.go index 1d74feb00..e6f9d6b45 100644 --- a/examples/net/ntpclient/main.go +++ b/examples/net/ntpclient/main.go @@ -30,7 +30,7 @@ func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } @@ -49,7 +49,7 @@ func main() { } conn.Close() - NetDisconnect() + netdev.NetDisconnect() runtime.AdjustTimeOffset(-1 * int64(time.Since(t))) diff --git a/examples/net/ntpclient/rtl8720dn.go b/examples/net/ntpclient/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/ntpclient/rtl8720dn.go +++ b/examples/net/ntpclient/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/ntpclient/wifinina.go b/examples/net/ntpclient/wifinina.go index e9151a53c..bcb524a9c 100644 --- a/examples/net/ntpclient/wifinina.go +++ b/examples/net/ntpclient/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/rawsocket/main.go b/examples/net/rawsocket/main.go index 8194f2a26..fa3619d69 100644 --- a/examples/net/rawsocket/main.go +++ b/examples/net/rawsocket/main.go @@ -11,9 +11,9 @@ import ( "fmt" "log" "machine" - "net/netdev" + "net" "strconv" - "strings" + "syscall" "time" ) @@ -29,7 +29,7 @@ func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } @@ -41,16 +41,15 @@ func main() { func sendBatch() { - parts := strings.Split(addr, ":") - ip := netdev.ParseIP(parts[0]) - port, _ := strconv.Atoi(parts[1]) - sockAddr := netdev.NewSockAddr("", netdev.Port(port), ip) + host, sport, _ := net.SplitHostPort(addr) + ip := net.ParseIP(host).To4() + port, _ := strconv.Atoi(sport) // make TCP connection message("---------------\r\nDialing TCP connection") - sock, _ := dev.Socket(netdev.AF_INET, netdev.SOCK_STREAM, netdev.IPPROTO_TCP) - err := dev.Connect(sock, sockAddr) - for ; err != nil; err = dev.Connect(sock, sockAddr) { + fd, _ := netdev.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + err := netdev.Connect(fd, "", ip, port) + for ; err != nil; err = netdev.Connect(fd, "", ip, port) { message(err.Error()) time.Sleep(5 * time.Second) } @@ -67,7 +66,7 @@ func sendBatch() { fmt.Fprint(buf, "\r---------------------------- i == ", i, " ----------------------------"+ "\r---------------------------- i == ", i, " ----------------------------") - if w, err = dev.Send(sock, buf.Bytes(), 0, 0); err != nil { + if w, err = netdev.Send(fd, buf.Bytes(), 0, 0); err != nil { println("error:", err.Error(), "\r") break } @@ -79,12 +78,12 @@ func sendBatch() { fmt.Fprint(buf, "\nWrote ", n, " bytes in ", ms, " ms\r\n") message(buf.String()) - if _, err := dev.Send(sock, buf.Bytes(), 0, 0); err != nil { + if _, err := netdev.Send(fd, buf.Bytes(), 0, 0); err != nil { println("error:", err.Error(), "\r") } println("Disconnecting TCP...") - dev.Close(sock) + netdev.Close(fd) } func message(msg string) { diff --git a/examples/net/rawsocket/rtl8720dn.go b/examples/net/rawsocket/rtl8720dn.go index a6dcb77fa..df94a8e4f 100644 --- a/examples/net/rawsocket/rtl8720dn.go +++ b/examples/net/rawsocket/rtl8720dn.go @@ -26,12 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/rawsocket/wifinina.go b/examples/net/rawsocket/wifinina.go index 1011d75cf..bcb524a9c 100644 --- a/examples/net/rawsocket/wifinina.go +++ b/examples/net/rawsocket/wifinina.go @@ -30,12 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/tcpclient/main.go b/examples/net/tcpclient/main.go index 2e603d879..76fd56261 100644 --- a/examples/net/tcpclient/main.go +++ b/examples/net/tcpclient/main.go @@ -28,7 +28,7 @@ func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/tcpclient/rtl8720dn.go b/examples/net/tcpclient/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/tcpclient/rtl8720dn.go +++ b/examples/net/tcpclient/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/tcpclient/wifinina.go b/examples/net/tcpclient/wifinina.go index e9151a53c..bcb524a9c 100644 --- a/examples/net/tcpclient/wifinina.go +++ b/examples/net/tcpclient/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/tcpecho/main.go b/examples/net/tcpecho/main.go index 6ffe81d57..f91c6df90 100644 --- a/examples/net/tcpecho/main.go +++ b/examples/net/tcpecho/main.go @@ -36,7 +36,7 @@ func main() { time.Sleep(time.Second) - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/tcpecho/rtl8720dn.go b/examples/net/tcpecho/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/tcpecho/rtl8720dn.go +++ b/examples/net/tcpecho/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/tcpecho/wifinina.go b/examples/net/tcpecho/wifinina.go index e9151a53c..bcb524a9c 100644 --- a/examples/net/tcpecho/wifinina.go +++ b/examples/net/tcpecho/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/tlsclient/main.go b/examples/net/tlsclient/main.go index ec8ae0630..57caad4ef 100644 --- a/examples/net/tlsclient/main.go +++ b/examples/net/tlsclient/main.go @@ -80,7 +80,7 @@ func makeRequest() { func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/tlsclient/rtl8720dn.go b/examples/net/tlsclient/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/tlsclient/rtl8720dn.go +++ b/examples/net/tlsclient/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/tlsclient/wifinina.go b/examples/net/tlsclient/wifinina.go index e9151a53c..bcb524a9c 100644 --- a/examples/net/tlsclient/wifinina.go +++ b/examples/net/tlsclient/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/webclient-tinyterm/main.go b/examples/net/webclient-tinyterm/main.go index 507cd820c..47dfa48a7 100644 --- a/examples/net/webclient-tinyterm/main.go +++ b/examples/net/webclient-tinyterm/main.go @@ -16,13 +16,12 @@ import ( "io" "log" "machine" - "net" "net/http" - "net/netdev" "net/url" "strings" "time" + "tinygo.org/x/drivers" "tinygo.org/x/drivers/ili9341" "tinygo.org/x/drivers/rtl8720dn" "tinygo.org/x/tinyfont/proggy" @@ -45,7 +44,7 @@ var ( Baudrate: 614400, } - dev = rtl8720dn.New(&netcfg) + netdev = rtl8720dn.New(&netcfg) display = ili9341.NewSPI( machine.SPI3, @@ -67,12 +66,12 @@ var ( font = &proggy.TinySZ8pt7b ) -func notify(e netdev.Event) { +func notify(e drivers.NetlinkEvent) { switch e { - case netdev.EventNetUp: + case drivers.NetlinkEventNetUp: fmt.Println("Wifi connection UP") fmt.Fprintf(terminal, "Wifi connection UP") - case netdev.EventNetDown: + case drivers.NetlinkEventNetDown: fmt.Println("Wifi connection DOWN") fmt.Fprintf(terminal, "Wifi connection DOWN") } @@ -101,10 +100,9 @@ func main() { fmt.Fprintf(terminal, "Connecting to %s...\r\n", ssid) - dev.NetNotify(notify) - net.UseNetdev(dev) + netdev.NetNotify(notify) - if err := dev.NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/webclient/main.go b/examples/net/webclient/main.go index 05b6a63dc..15b1d2c91 100644 --- a/examples/net/webclient/main.go +++ b/examples/net/webclient/main.go @@ -91,7 +91,7 @@ func closeConnection() { func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/webclient/rtl8720dn.go b/examples/net/webclient/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/webclient/rtl8720dn.go +++ b/examples/net/webclient/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/webclient/wifinina.go b/examples/net/webclient/wifinina.go index e9151a53c..bcb524a9c 100644 --- a/examples/net/webclient/wifinina.go +++ b/examples/net/webclient/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/webserver/main.go b/examples/net/webserver/main.go index c24af38ef..1aa587f58 100644 --- a/examples/net/webserver/main.go +++ b/examples/net/webserver/main.go @@ -32,7 +32,7 @@ func main() { led.Configure(machine.PinConfig{Mode: machine.PinOutput}) - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/webserver/rtl8720dn.go b/examples/net/webserver/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/webserver/rtl8720dn.go +++ b/examples/net/webserver/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/webserver/wifinina.go b/examples/net/webserver/wifinina.go index c3f8e9199..fd8b29129 100644 --- a/examples/net/webserver/wifinina.go +++ b/examples/net/webserver/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/websocket/dial/main.go b/examples/net/websocket/dial/main.go index c8cf2f00a..d53d6e89d 100644 --- a/examples/net/websocket/dial/main.go +++ b/examples/net/websocket/dial/main.go @@ -33,7 +33,7 @@ func waitSerial() { func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/websocket/dial/rtl8720dn.go b/examples/net/websocket/dial/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/websocket/dial/rtl8720dn.go +++ b/examples/net/websocket/dial/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/websocket/dial/wifinina.go b/examples/net/websocket/dial/wifinina.go index c3f8e9199..fd8b29129 100644 --- a/examples/net/websocket/dial/wifinina.go +++ b/examples/net/websocket/dial/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/websocket/handler/main.go b/examples/net/websocket/handler/main.go index 5a50e9b6d..4ff4fc85f 100644 --- a/examples/net/websocket/handler/main.go +++ b/examples/net/websocket/handler/main.go @@ -40,7 +40,7 @@ func waitSerial() { func main() { waitSerial() - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/websocket/handler/rtl8720dn.go b/examples/net/websocket/handler/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/websocket/handler/rtl8720dn.go +++ b/examples/net/websocket/handler/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/websocket/handler/wifinina.go b/examples/net/websocket/handler/wifinina.go index c3f8e9199..fd8b29129 100644 --- a/examples/net/websocket/handler/wifinina.go +++ b/examples/net/websocket/handler/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/examples/net/webstatic/main.go b/examples/net/webstatic/main.go index 5bebb0a61..cab727955 100644 --- a/examples/net/webstatic/main.go +++ b/examples/net/webstatic/main.go @@ -25,7 +25,7 @@ func main() { // wait a bit for console time.Sleep(2 * time.Second) - if err := NetConnect(); err != nil { + if err := netdev.NetConnect(); err != nil { log.Fatal(err) } diff --git a/examples/net/webstatic/rtl8720dn.go b/examples/net/webstatic/rtl8720dn.go index 871a88acd..df94a8e4f 100644 --- a/examples/net/webstatic/rtl8720dn.go +++ b/examples/net/webstatic/rtl8720dn.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/rtl8720dn" @@ -27,13 +26,4 @@ var cfg = rtl8720dn.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = rtl8720dn.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = rtl8720dn.New(&cfg) diff --git a/examples/net/webstatic/wifinina.go b/examples/net/webstatic/wifinina.go index c3f8e9199..fd8b29129 100644 --- a/examples/net/webstatic/wifinina.go +++ b/examples/net/webstatic/wifinina.go @@ -6,7 +6,6 @@ package main import ( "machine" - "net" "time" "tinygo.org/x/drivers/wifinina" @@ -31,13 +30,4 @@ var cfg = wifinina.Config{ WatchdogTimeo: time.Duration(20 * time.Second), } -var dev = wifinina.New(&cfg) - -func NetConnect() error { - net.UseNetdev(dev) - return dev.NetConnect() -} - -func NetDisconnect() { - dev.NetDisconnect() -} +var netdev = wifinina.New(&cfg) diff --git a/netdev.go b/netdev.go new file mode 100644 index 000000000..f41362f76 --- /dev/null +++ b/netdev.go @@ -0,0 +1,59 @@ +package drivers + +import ( + "errors" + "net" + "time" + _ "unsafe" // to use go:linkname +) + +// GethostByName() errors +var ( + ErrHostUnknown = errors.New("Host unknown") +) + +// Socket errors +var ( + ErrFamilyNotSupported = errors.New("Address family not supported") + ErrProtocolNotSupported = errors.New("Socket protocol/type not supported") + ErrNoMoreSockets = errors.New("No more sockets") + ErrClosingSocket = errors.New("Error closing socket") + ErrNotSupported = errors.New("Not supported") + ErrRecvTimeout = errors.New("Recv timeout expired") +) + +//go:linkname UseNetdev net.useNetdev +func UseNetdev(dev netdever) + +// Netdev is TinyGo's network device driver model. Network drivers implement +// the netdever interface, providing a common network I/O interface to TinyGo's +// "net" package. The interface is modeled after the BSD socket interface. +// net.Conn implementations (TCPConn, UDPConn, and TLSConn) use the netdev +// interface for device I/O access. +// +// A netdever is passed to the "net" package using UseNetdev(). +// +// Just like a net.Conn, multiple goroutines may invoke methods on a netdever +// simultaneously. +// +// NOTE: The netdever interface is mirrored in tinygo/src/net/netdev.go. +// NOTE: If making changes to this interface, mirror the changes in +// NOTE: tinygo/src/net/netdev.go, and visa-versa. + +type netdever interface { + + // GetHostByName returns the IP address of either a hostname or IPv4 + // address in standard dot notation + GetHostByName(name string) (net.IP, error) + + // Berkely Sockets-like interface, Go-ified. See man page for socket(2), etc. + Socket(domain int, stype int, protocol int) (int, error) + Bind(sockfd int, ip net.IP, port int) error + Connect(sockfd int, host string, ip net.IP, port int) error + Listen(sockfd int, backlog int) error + Accept(sockfd int, ip net.IP, port int) (int, error) + Send(sockfd int, buf []byte, flags int, timeout time.Duration) (int, error) + Recv(sockfd int, buf []byte, flags int, timeout time.Duration) (int, error) + Close(sockfd int) error + SetSockOpt(sockfd int, level int, opt int, value interface{}) error +} diff --git a/netlink.go b/netlink.go new file mode 100644 index 000000000..955289fa5 --- /dev/null +++ b/netlink.go @@ -0,0 +1,49 @@ +package drivers + +import ( + "errors" + "net" +) + +// NetConnect() errors +var ( + ErrConnected = errors.New("Already connected") + ErrConnectFailed = errors.New("Connect failed") + ErrConnectTimeout = errors.New("Connect timed out") + ErrMissingSSID = errors.New("Missing WiFi SSID") + ErrStartingDHCPClient = errors.New("Error starting DHPC client") +) + +type NetlinkEvent int + +// Netlink network events +const ( + // The device's network connection is now UP + NetlinkEventNetUp NetlinkEvent = iota + // The device's network connection is now DOWN + NetlinkEventNetDown +) + +// Network drivers (optionally) implement the Netlinker interface. This +// interface is not used by TinyGo's "net" package, but rather provides the +// TinyGo application direct access to the network device for common settings +// and control that fall outside of netdev's socket interface. + +type Netlinker interface { + + // NetConnect device to IP network + NetConnect() error + + // NetDisconnect device from IP network + NetDisconnect() + + // NetNotify to register callback for network events + NetNotify(func(NetlinkEvent)) + + // GetHardwareAddr returns device MAC address + GetHardwareAddr() (net.HardwareAddr, error) + + // GetIPAddr returns IP address assigned to device, either by DHCP or + // statically + GetIPAddr() (net.IP, error) +} diff --git a/rtl8720dn/rtl8720dn.go b/rtl8720dn/rtl8720dn.go index cc1c1e71a..1295368fe 100644 --- a/rtl8720dn/rtl8720dn.go +++ b/rtl8720dn/rtl8720dn.go @@ -1,7 +1,7 @@ // Package rtl8720dn implements TCP wireless communication over UART // talking to a RealTek rtl8720dn module. // -// 01/2023 sfeldma@gmail.com Heavily modified to use netdev interface +// 01/2023 sfeldma@gmail.com Heavily modified to use netdevinterface package rtl8720dn // import "tinygo.org/x/drivers/rtl8720dn" @@ -11,10 +11,13 @@ import ( "fmt" "io" "machine" - "net/netdev" + "net" "strings" "sync" + "syscall" "time" + + "tinygo.org/x/drivers" ) var _debug debug = debugBasic @@ -28,16 +31,14 @@ var ( ) const ( - F_SETFL = 4 - O_NONBLOCK = 1 + O_NONBLOCK = 1 // note: different value than syscall.O_NONBLOCK (0x800) RTW_MODE_STA = 0x00000001 ) -type macAddress netdev.HardwareAddr type sock int32 type socket struct { - protocol netdev.Protocol + protocol int inuse bool } @@ -68,7 +69,7 @@ type Config struct { type rtl8720dn struct { cfg *Config - notifyCb func(netdev.Event) + notifyCb func(drivers.NetlinkEvent) mu sync.Mutex uart *machine.UART @@ -86,22 +87,29 @@ type rtl8720dn struct { sockets map[sock]*socket } -func newSocket(protocol netdev.Protocol) *socket { +func newSocket(protocol int) *socket { return &socket{protocol: protocol, inuse: true} } func New(cfg *Config) *rtl8720dn { - return &rtl8720dn{ + r := rtl8720dn{ debug: (_debug & debugRpc) != 0, cfg: cfg, sockets: make(map[sock]*socket), killWatchdog: make(chan bool), } + + drivers.UseNetdev(&r) + + // assert that rtl8720dn implements Netlinker + var _ drivers.Netlinker = (*rtl8720dn)(nil) + + return &r } func (r *rtl8720dn) startDhcpc() error { if result := r.rpc_tcpip_adapter_dhcpc_start(0); result == -1 { - return netdev.ErrStartingDHCPClient + return drivers.ErrStartingDHCPClient } return nil } @@ -109,7 +117,7 @@ func (r *rtl8720dn) startDhcpc() error { func (r *rtl8720dn) connectToAP() error { if len(r.cfg.Ssid) == 0 { - return netdev.ErrMissingSSID + return drivers.ErrMissingSSID } if debugging(debugBasic) { @@ -123,7 +131,7 @@ func (r *rtl8720dn) connectToAP() error { if debugging(debugBasic) { fmt.Printf("FAILED\r\n") } - return netdev.ErrConnectFailed + return drivers.ErrConnectFailed } if debugging(debugBasic) { @@ -131,7 +139,7 @@ func (r *rtl8720dn) connectToAP() error { } if r.notifyCb != nil { - r.notifyCb(netdev.EventNetUp) + r.notifyCb(drivers.NetlinkEventNetUp) } return r.startDhcpc() @@ -206,9 +214,9 @@ func (r *rtl8720dn) showIP() { if debugging(debugBasic) { ip, subnet, gateway, _ := r.getIP() fmt.Printf("\r\n") - fmt.Printf("DHCP-assigned IP : %s\r\n", netdev.IP(ip).String()) - fmt.Printf("DHCP-assigned subnet : %s\r\n", netdev.IP(subnet).String()) - fmt.Printf("DHCP-assigned gateway : %s\r\n", netdev.IP(gateway).String()) + fmt.Printf("DHCP-assigned IP : %s\r\n", ip.String()) + fmt.Printf("DHCP-assigned subnet : %s\r\n", subnet.String()) + fmt.Printf("DHCP-assigned gateway : %s\r\n", gateway.String()) fmt.Printf("\r\n") } } @@ -231,7 +239,7 @@ func (r *rtl8720dn) watchdog() { fmt.Printf("Watchdog: Wifi NOT CONNECTED, trying again...\r\n") } if r.notifyCb != nil { - r.notifyCb(netdev.EventNetDown) + r.notifyCb(drivers.NetlinkEventNetDown) } r.netConnect(false) } @@ -250,7 +258,7 @@ func (r *rtl8720dn) netConnect(reset bool) error { for i := 0; r.cfg.Retries == 0 || i < r.cfg.Retries; i++ { if err := r.connectToAP(); err != nil { - if err == netdev.ErrConnectFailed { + if err == drivers.ErrConnectFailed { continue } return err @@ -259,7 +267,7 @@ func (r *rtl8720dn) netConnect(reset bool) error { } if r.networkDown() { - return netdev.ErrConnectFailed + return drivers.ErrConnectFailed } r.showIP() @@ -272,7 +280,7 @@ func (r *rtl8720dn) NetConnect() error { defer r.mu.Unlock() if r.netConnected { - return netdev.ErrConnected + return drivers.ErrConnected } r.showDriver() @@ -316,15 +324,15 @@ func (r *rtl8720dn) NetDisconnect() { } if r.notifyCb != nil { - r.notifyCb(netdev.EventNetDown) + r.notifyCb(drivers.NetlinkEventNetDown) } } -func (r *rtl8720dn) NetNotify(cb func(netdev.Event)) { +func (r *rtl8720dn) NetNotify(cb func(drivers.NetlinkEvent)) { r.notifyCb = cb } -func (r *rtl8720dn) GetHostByName(name string) (netdev.IP, error) { +func (r *rtl8720dn) GetHostByName(name string) (net.IP, error) { if debugging(debugNetdev) { fmt.Printf("[GetHostByName] name: %s\r\n", name) @@ -333,19 +341,16 @@ func (r *rtl8720dn) GetHostByName(name string) (netdev.IP, error) { r.mu.Lock() defer r.mu.Unlock() - var addr [4]byte - result := r.rpc_netconn_gethostbyname(name, addr[:]) + var ip [4]byte + result := r.rpc_netconn_gethostbyname(name, ip[:]) if result == -1 { - return netdev.IP{}, fmt.Errorf("Get IP of host '%s' failed", name) + return net.IP{}, drivers.ErrHostUnknown } - var ip netdev.IP - copy(ip[:], addr[:]) - - return ip, nil + return net.IP(ip[:]), nil } -func (r *rtl8720dn) GetHardwareAddr() (netdev.HardwareAddr, error) { +func (r *rtl8720dn) GetHardwareAddr() (net.HardwareAddr, error) { if debugging(debugNetdev) { fmt.Printf("[GetHardwareAddr]\r\n") @@ -357,10 +362,10 @@ func (r *rtl8720dn) GetHardwareAddr() (netdev.HardwareAddr, error) { mac := strings.ReplaceAll(r.getMACAddr(), ":", "") addr, err := hex.DecodeString(mac) - return netdev.HardwareAddr(addr), err + return net.HardwareAddr(addr), err } -func (r *rtl8720dn) GetIPAddr() (netdev.IP, error) { +func (r *rtl8720dn) GetIPAddr() (net.IP, error) { if debugging(debugNetdev) { fmt.Printf("[GetIPAddr]\r\n") @@ -371,7 +376,7 @@ func (r *rtl8720dn) GetIPAddr() (netdev.IP, error) { ip, _, _, err := r.getIP() - return netdev.IP(ip), err + return net.IP(ip), err } func (r *rtl8720dn) clientTLS() uint32 { @@ -384,18 +389,17 @@ func (r *rtl8720dn) clientTLS() uint32 { // 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 (r *rtl8720dn) Socket(family netdev.AddressFamily, sockType netdev.SockType, - protocol netdev.Protocol) (netdev.Sockfd, error) { +func (r *rtl8720dn) Socket(domain int, stype int, protocol int) (int, error) { if debugging(debugNetdev) { - fmt.Printf("[Socket] family: %s, sockType: %s, protocol: %s\r\n", - family, sockType, protocol) + fmt.Printf("[Socket] domain: %d, type: %d, protocol: %d\r\n", + domain, stype, protocol) } - switch family { - case netdev.AF_INET: + switch domain { + case syscall.AF_INET: default: - return -1, netdev.ErrFamilyNotSupported + return -1, drivers.ErrFamilyNotSupported } var newSock int32 @@ -404,51 +408,50 @@ func (r *rtl8720dn) Socket(family netdev.AddressFamily, sockType netdev.SockType defer r.mu.Unlock() switch { - case protocol == netdev.IPPROTO_TCP && sockType == netdev.SOCK_STREAM: - newSock = r.rpc_lwip_socket(netdev.AF_INET, netdev.SOCK_STREAM, - netdev.IPPROTO_TCP) - case protocol == netdev.IPPROTO_TLS && sockType == netdev.SOCK_STREAM: + case protocol == syscall.IPPROTO_TCP && stype == syscall.SOCK_STREAM: + newSock = r.rpc_lwip_socket(syscall.AF_INET, syscall.SOCK_STREAM, + syscall.IPPROTO_TCP) + case protocol == syscall.IPPROTO_TLS && stype == syscall.SOCK_STREAM: // TODO Investigate: using client number as socket number; // TODO this may cause a problem if mixing TLS and non-TLS sockets? newSock = int32(r.clientTLS()) - case protocol == netdev.IPPROTO_UDP && sockType == netdev.SOCK_DGRAM: - newSock = r.rpc_lwip_socket(netdev.AF_INET, netdev.SOCK_DGRAM, - netdev.IPPROTO_UDP) + case protocol == syscall.IPPROTO_UDP && stype == syscall.SOCK_DGRAM: + newSock = r.rpc_lwip_socket(syscall.AF_INET, syscall.SOCK_DGRAM, + syscall.IPPROTO_UDP) default: - return -1, netdev.ErrProtocolNotSupported + return -1, drivers.ErrProtocolNotSupported } if newSock == -1 { - return -1, netdev.ErrNoMoreSockets + return -1, drivers.ErrNoMoreSockets } socket := newSocket(protocol) r.sockets[sock(newSock)] = socket - return netdev.Sockfd(newSock), nil + return int(newSock), nil } -func addrToName(addr netdev.SockAddr) []byte { - port := addr.Port() - ip := addr.IpBytes() - +func addrToName(ip net.IP, port int) []byte { name := make([]byte, 16) name[0] = 0x00 - name[1] = netdev.AF_INET + name[1] = syscall.AF_INET name[2] = byte(port >> 8) name[3] = byte(port) - name[4] = byte(ip[0]) - name[5] = byte(ip[1]) - name[6] = byte(ip[2]) - name[7] = byte(ip[3]) + if len(ip) == 4 { + name[4] = byte(ip[0]) + name[5] = byte(ip[1]) + name[6] = byte(ip[2]) + name[7] = byte(ip[3]) + } return name } -func (r *rtl8720dn) Bind(sockfd netdev.Sockfd, addr netdev.SockAddr) error { +func (r *rtl8720dn) Bind(sockfd int, ip net.IP, port int) error { if debugging(debugNetdev) { - fmt.Printf("[Bind] sockfd: %d, addr: %s\r\n", sockfd, addr) + fmt.Printf("[Bind] sockfd: %d, addr: %s:%d\r\n", sockfd, ip, port) } r.mu.Lock() @@ -456,25 +459,29 @@ func (r *rtl8720dn) Bind(sockfd netdev.Sockfd, addr netdev.SockAddr) error { var sock = sock(sockfd) var socket = r.sockets[sock] - var name = addrToName(addr) + var name = addrToName(ip, port) switch socket.protocol { - case netdev.IPPROTO_TCP, netdev.IPPROTO_UDP: + case syscall.IPPROTO_TCP, syscall.IPPROTO_UDP: result := r.rpc_lwip_bind(int32(sock), name, uint32(len(name))) if result == -1 { - return fmt.Errorf("Bind to %s failed", addr.String()) + return fmt.Errorf("Bind to %s:%d failed", ip, port) } default: - return netdev.ErrProtocolNotSupported + return drivers.ErrProtocolNotSupported } return nil } -func (r *rtl8720dn) Connect(sockfd netdev.Sockfd, servaddr netdev.SockAddr) error { +func (r *rtl8720dn) Connect(sockfd int, host string, ip net.IP, port int) error { if debugging(debugNetdev) { - fmt.Printf("[Connect] sockfd: %d, servaddr: %s\r\n", sockfd, servaddr) + if host == "" { + fmt.Printf("[Connect] sockfd: %d, addr: %s:%d\r\n", sockfd, ip, port) + } else { + fmt.Printf("[Connect] sockfd: %d, host: %s:%d\r\n", sockfd, host, port) + } } r.mu.Lock() @@ -482,27 +489,27 @@ func (r *rtl8720dn) Connect(sockfd netdev.Sockfd, servaddr netdev.SockAddr) erro var sock = sock(sockfd) var socket = r.sockets[sock] - var name = addrToName(servaddr) + var name = addrToName(ip, port) // Start the connection switch socket.protocol { - case netdev.IPPROTO_TCP, netdev.IPPROTO_UDP: + case syscall.IPPROTO_TCP, syscall.IPPROTO_UDP: result := r.rpc_lwip_connect(int32(sock), name, uint32(len(name))) if result == -1 { - return fmt.Errorf("Connect to %s failed", servaddr.String()) + return fmt.Errorf("Connect to %s:%d failed", ip, port) } - case netdev.IPPROTO_TLS: + case syscall.IPPROTO_TLS: result := r.rpc_wifi_start_ssl_client(uint32(sock), - servaddr.Host(), uint32(servaddr.Port()), 0) + host, uint32(port), 0) if result == -1 { - return fmt.Errorf("Connect to %s failed", servaddr.String()) + return fmt.Errorf("Connect to %s:%d failed", host, port) } } return nil } -func (r *rtl8720dn) Listen(sockfd netdev.Sockfd, backlog int) error { +func (r *rtl8720dn) Listen(sockfd int, backlog int) error { if debugging(debugNetdev) { fmt.Printf("[Listen] sockfd: %d\r\n", sockfd) @@ -515,31 +522,31 @@ func (r *rtl8720dn) Listen(sockfd netdev.Sockfd, backlog int) error { var socket = r.sockets[sock] switch socket.protocol { - case netdev.IPPROTO_TCP: + case syscall.IPPROTO_TCP: result := r.rpc_lwip_listen(int32(sock), int32(backlog)) if result == -1 { return fmt.Errorf("Listen failed") } - result = r.rpc_lwip_fcntl(int32(sock), F_SETFL, O_NONBLOCK) + result = r.rpc_lwip_fcntl(int32(sock), syscall.F_SETFL, O_NONBLOCK) if result == -1 { return fmt.Errorf("Fcntl failed") } - case netdev.IPPROTO_UDP: + case syscall.IPPROTO_UDP: result := r.rpc_lwip_listen(int32(sock), int32(backlog)) if result == -1 { return fmt.Errorf("Listen failed") } default: - return netdev.ErrProtocolNotSupported + return drivers.ErrProtocolNotSupported } return nil } -func (r *rtl8720dn) Accept(sockfd netdev.Sockfd, peer netdev.SockAddr) (netdev.Sockfd, error) { +func (r *rtl8720dn) Accept(sockfd int, ip net.IP, port int) (int, error) { if debugging(debugNetdev) { - fmt.Printf("[Accept] sockfd: %d, peer: %s\r\n", sockfd, peer) + fmt.Printf("[Accept] sockfd: %d, peer: %s:%d\r\n", sockfd, ip, port) } r.mu.Lock() @@ -548,12 +555,12 @@ func (r *rtl8720dn) Accept(sockfd netdev.Sockfd, peer netdev.SockAddr) (netdev.S var newSock int32 var lsock = sock(sockfd) var socket = r.sockets[lsock] - var addr = addrToName(peer) + var addr = addrToName(ip, port) switch socket.protocol { - case netdev.IPPROTO_TCP: + case syscall.IPPROTO_TCP: default: - return -1, netdev.ErrProtocolNotSupported + return -1, drivers.ErrProtocolNotSupported } for { @@ -584,27 +591,27 @@ func (r *rtl8720dn) Accept(sockfd netdev.Sockfd, peer netdev.SockAddr) (netdev.S continue } // Reuse client socket - return netdev.Sockfd(newSock), nil + return int(newSock), nil } // Create new socket for client and return fd r.sockets[sock(newSock)] = newSocket(socket.protocol) - return netdev.Sockfd(newSock), nil + return int(newSock), nil } } -func (r *rtl8720dn) sendChunk(sockfd netdev.Sockfd, buf []byte) (int, error) { +func (r *rtl8720dn) sendChunk(sockfd int, buf []byte) (int, error) { var sock = sock(sockfd) var socket = r.sockets[sock] switch socket.protocol { - case netdev.IPPROTO_TCP, netdev.IPPROTO_UDP: + case syscall.IPPROTO_TCP, syscall.IPPROTO_UDP: result := r.rpc_lwip_send(int32(sock), buf, 0x00000008) if result == -1 { return -1, fmt.Errorf("Send error") } return int(result), nil - case netdev.IPPROTO_TLS: + case syscall.IPPROTO_TLS: result := r.rpc_wifi_send_ssl_data(uint32(sock), buf, uint16(len(buf))) if result == -1 { return -1, fmt.Errorf("TLS Send error") @@ -612,10 +619,10 @@ func (r *rtl8720dn) sendChunk(sockfd netdev.Sockfd, buf []byte) (int, error) { return int(result), nil } - return -1, netdev.ErrProtocolNotSupported + return -1, drivers.ErrProtocolNotSupported } -func (r *rtl8720dn) Send(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, +func (r *rtl8720dn) Send(sockfd int, buf []byte, flags int, timeout time.Duration) (int, error) { if debugging(debugNetdev) { @@ -645,7 +652,7 @@ func (r *rtl8720dn) Send(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlag return len(buf), nil } -func (r *rtl8720dn) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, +func (r *rtl8720dn) Recv(sockfd int, buf []byte, flags int, timeout time.Duration) (int, error) { if debugging(debugNetdev) { @@ -671,15 +678,15 @@ func (r *rtl8720dn) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlag // Check if we've timed out if timeout > 0 { if time.Now().Before(expire) { - return -1, netdev.ErrRecvTimeout + return -1, drivers.ErrRecvTimeout } } switch socket.protocol { - case netdev.IPPROTO_TCP, netdev.IPPROTO_UDP: + case syscall.IPPROTO_TCP, syscall.IPPROTO_UDP: n = r.rpc_lwip_recv(int32(sock), buf[:length], uint32(length), 0x00000008, 0) - case netdev.IPPROTO_TLS: + case syscall.IPPROTO_TLS: n = r.rpc_wifi_get_ssl_receive(uint32(sock), buf[:length], int32(length)) } @@ -706,7 +713,7 @@ func (r *rtl8720dn) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlag } } -func (r *rtl8720dn) Close(sockfd netdev.Sockfd) error { +func (r *rtl8720dn) Close(sockfd int) error { if debugging(debugNetdev) { fmt.Printf("[Close] sockfd: %d\r\n", sockfd) @@ -724,15 +731,15 @@ func (r *rtl8720dn) Close(sockfd netdev.Sockfd) error { } switch socket.protocol { - case netdev.IPPROTO_TCP, netdev.IPPROTO_UDP: + case syscall.IPPROTO_TCP, syscall.IPPROTO_UDP: result = r.rpc_lwip_close(int32(sock)) - case netdev.IPPROTO_TLS: + case syscall.IPPROTO_TLS: r.rpc_wifi_stop_ssl_socket(uint32(sock)) r.rpc_wifi_ssl_client_destroy(uint32(sock)) } if result == -1 { - return netdev.ErrClosingSocket + return drivers.ErrClosingSocket } socket.inuse = false @@ -740,14 +747,13 @@ func (r *rtl8720dn) Close(sockfd netdev.Sockfd) error { return nil } -func (r *rtl8720dn) SetSockOpt(sockfd netdev.Sockfd, level netdev.SockOptLevel, - opt netdev.SockOpt, value any) error { +func (r *rtl8720dn) SetSockOpt(sockfd int, level int, opt int, value interface{}) error { if debugging(debugNetdev) { fmt.Printf("[SetSockOpt] sockfd: %d\r\n", sockfd) } - return netdev.ErrNotSupported + return drivers.ErrNotSupported } func (r *rtl8720dn) disconnect() error { @@ -768,13 +774,14 @@ func (r *rtl8720dn) getMACAddr() string { return string(mac[:]) } -func (r *rtl8720dn) getIP() (ip, subnet, gateway netdev.IP, err error) { +func (r *rtl8720dn) getIP() (ip, subnet, gateway net.IP, err error) { var ip_info [12]byte result := r.rpc_tcpip_adapter_get_ip_info(0, ip_info[:]) if result == -1 { err = fmt.Errorf("Get IP info failed") return } + ip, subnet, gateway = make([]byte, 4), make([]byte, 4), make([]byte, 4) copy(ip[:], ip_info[0:4]) copy(subnet[:], ip_info[4:8]) copy(gateway[:], ip_info[8:12]) diff --git a/wifinina/wifinina.go b/wifinina/wifinina.go index 98c77005d..2a306feee 100644 --- a/wifinina/wifinina.go +++ b/wifinina/wifinina.go @@ -16,8 +16,9 @@ import ( "io" "machine" "math/bits" - "net/netdev" + "net" "sync" + "syscall" "time" "tinygo.org/x/drivers" @@ -157,13 +158,13 @@ const ( type connectionStatus uint8 type encryptionType uint8 -type macAddress netdev.HardwareAddr type sock uint8 type hwerr uint8 type socket struct { - protocol netdev.Protocol - laddr netdev.SockAddr + protocol int + ip net.IP + port int inuse bool } @@ -201,7 +202,7 @@ type Config struct { type wifinina struct { cfg *Config - notifyCb func(netdev.Event) + notifyCb func(drivers.NetlinkEvent) mu sync.Mutex spi drivers.SPI @@ -224,7 +225,7 @@ type wifinina struct { sockets map[sock]*socket // keyed by sock as returned by getSocket() } -func newSocket(protocol netdev.Protocol) *socket { +func newSocket(protocol int) *socket { return &socket{protocol: protocol, inuse: true} } @@ -243,6 +244,11 @@ func New(cfg *Config) *wifinina { w.cfg.ConnectTimeo = 10 * time.Second } + drivers.UseNetdev(&w) + + // assert that wifinina implements Netlinker + var _ drivers.Netlinker = (*wifinina)(nil) + return &w } @@ -253,7 +259,7 @@ func (err hwerr) Error() string { func (w *wifinina) connectToAP(timeout time.Duration) error { if len(w.cfg.Ssid) == 0 { - return netdev.ErrMissingSSID + return drivers.ErrMissingSSID } if debugging(debugBasic) { @@ -273,7 +279,7 @@ func (w *wifinina) connectToAP(timeout time.Duration) error { fmt.Printf("CONNECTED\r\n") } if w.notifyCb != nil { - w.notifyCb(netdev.EventNetUp) + w.notifyCb(drivers.NetlinkEventNetUp) } return nil } @@ -284,7 +290,7 @@ func (w *wifinina) connectToAP(timeout time.Duration) error { fmt.Printf("FAILED\r\n") } - return netdev.ErrConnectTimeout + return drivers.ErrConnectTimeout } func (w *wifinina) netDisconnect() { @@ -348,7 +354,7 @@ func (w *wifinina) showDevice() { return } if debugging(debugBasic) { - mac := netdev.HardwareAddr(w.getMACAddr()) + mac := w.getMACAddr() fmt.Printf("ESP32 firmware version : %s\r\n", w.getFwVersion()) fmt.Printf("MAC address : %s\r\n", mac.String()) fmt.Printf("\r\n") @@ -360,9 +366,9 @@ func (w *wifinina) showIP() { if debugging(debugBasic) { ip, subnet, gateway := w.getIP() fmt.Printf("\r\n") - fmt.Printf("DHCP-assigned IP : %s\r\n", netdev.IP(ip).String()) - fmt.Printf("DHCP-assigned subnet : %s\r\n", netdev.IP(subnet).String()) - fmt.Printf("DHCP-assigned gateway : %s\r\n", netdev.IP(gateway).String()) + fmt.Printf("DHCP-assigned IP : %s\r\n", ip.String()) + fmt.Printf("DHCP-assigned subnet : %s\r\n", subnet.String()) + fmt.Printf("DHCP-assigned gateway : %s\r\n", gateway.String()) fmt.Printf("\r\n") } } @@ -391,7 +397,7 @@ func (w *wifinina) watchdog() { fmt.Printf("Watchdog: Wifi NOT CONNECTED, trying again...\r\n") } if w.notifyCb != nil { - w.notifyCb(netdev.EventNetDown) + w.notifyCb(drivers.NetlinkEventNetDown) } w.netConnect(false) } @@ -408,7 +414,7 @@ func (w *wifinina) netConnect(reset bool) error { for i := 0; w.cfg.Retries == 0 || i < w.cfg.Retries; i++ { if err := w.connectToAP(w.cfg.ConnectTimeo); err != nil { - if err == netdev.ErrConnectTimeout { + if err == drivers.ErrConnectTimeout { continue } return err @@ -417,7 +423,7 @@ func (w *wifinina) netConnect(reset bool) error { } if w.networkDown() { - return netdev.ErrConnectFailed + return drivers.ErrConnectFailed } w.showIP() @@ -430,7 +436,7 @@ func (w *wifinina) NetConnect() error { defer w.mu.Unlock() if w.netConnected { - return netdev.ErrConnected + return drivers.ErrConnected } w.showDriver() @@ -472,15 +478,15 @@ func (w *wifinina) NetDisconnect() { } if w.notifyCb != nil { - w.notifyCb(netdev.EventNetDown) + w.notifyCb(drivers.NetlinkEventNetDown) } } -func (w *wifinina) NetNotify(cb func(netdev.Event)) { +func (w *wifinina) NetNotify(cb func(drivers.NetlinkEvent)) { w.notifyCb = cb } -func (w *wifinina) GetHostByName(name string) (netdev.IP, error) { +func (w *wifinina) GetHostByName(name string) (net.IP, error) { if debugging(debugNetdev) { fmt.Printf("[GetHostByName] name: %s\r\n", name) @@ -491,16 +497,13 @@ func (w *wifinina) GetHostByName(name string) (netdev.IP, error) { ip := w.getHostByName(name) if ip == "" { - return netdev.IP{}, netdev.ErrHostUnknown + return net.IP{}, drivers.ErrHostUnknown } - var hostIp netdev.IP - copy(hostIp[:], []byte(ip)) - - return hostIp, nil + return net.IP([]byte(ip)), nil } -func (w *wifinina) GetHardwareAddr() (netdev.HardwareAddr, error) { +func (w *wifinina) GetHardwareAddr() (net.HardwareAddr, error) { if debugging(debugNetdev) { fmt.Printf("[GetHardwareAddr]\r\n") @@ -509,10 +512,10 @@ func (w *wifinina) GetHardwareAddr() (netdev.HardwareAddr, error) { w.mu.Lock() defer w.mu.Unlock() - return netdev.HardwareAddr(w.getMACAddr()), nil + return w.getMACAddr(), nil } -func (w *wifinina) GetIPAddr() (netdev.IP, error) { +func (w *wifinina) GetIPAddr() (net.IP, error) { if debugging(debugNetdev) { fmt.Printf("[GetIPAddr]\r\n") @@ -523,32 +526,31 @@ func (w *wifinina) GetIPAddr() (netdev.IP, error) { ip, _, _ := w.getIP() - return netdev.IP(ip), nil + return net.IP(ip), nil } // 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(family netdev.AddressFamily, sockType netdev.SockType, - protocol netdev.Protocol) (netdev.Sockfd, error) { +func (w *wifinina) Socket(domain int, stype int, protocol int) (int, error) { if debugging(debugNetdev) { - fmt.Printf("[Socket] family: %s, sockType: %s, protocol: %s\r\n", - family, sockType, protocol) + fmt.Printf("[Socket] domain: %d, type: %d, protocol: %d\r\n", + domain, stype, protocol) } - switch family { - case netdev.AF_INET: + switch domain { + case syscall.AF_INET: default: - return -1, netdev.ErrFamilyNotSupported + return -1, drivers.ErrFamilyNotSupported } switch { - case protocol == netdev.IPPROTO_TCP && sockType == netdev.SOCK_STREAM: - case protocol == netdev.IPPROTO_TLS && sockType == netdev.SOCK_STREAM: - case protocol == netdev.IPPROTO_UDP && sockType == netdev.SOCK_DGRAM: + case protocol == syscall.IPPROTO_TCP && stype == syscall.SOCK_STREAM: + case protocol == syscall.IPPROTO_TLS && stype == syscall.SOCK_STREAM: + case protocol == syscall.IPPROTO_UDP && stype == syscall.SOCK_DGRAM: default: - return -1, netdev.ErrProtocolNotSupported + return -1, drivers.ErrProtocolNotSupported } w.mu.Lock() @@ -556,19 +558,19 @@ func (w *wifinina) Socket(family netdev.AddressFamily, sockType netdev.SockType, sock := w.getSocket() if sock == noSocketAvail { - return -1, netdev.ErrNoMoreSockets + return -1, drivers.ErrNoMoreSockets } socket := newSocket(protocol) w.sockets[sock] = socket - return netdev.Sockfd(sock), nil + return int(sock), nil } -func (w *wifinina) Bind(sockfd netdev.Sockfd, addr netdev.SockAddr) error { +func (w *wifinina) Bind(sockfd int, ip net.IP, port int) error { if debugging(debugNetdev) { - fmt.Printf("[Bind] sockfd: %d, addr: %s\r\n", sockfd, addr) + fmt.Printf("[Bind] sockfd: %d, addr: %s:%d\r\n", sockfd, ip, port) } w.mu.Lock() @@ -578,21 +580,33 @@ func (w *wifinina) Bind(sockfd netdev.Sockfd, addr netdev.SockAddr) error { var socket = w.sockets[sock] switch socket.protocol { - case netdev.IPPROTO_TCP: - case netdev.IPPROTO_TLS: - case netdev.IPPROTO_UDP: - w.startServer(sock, addr.Port(), protoModeUDP) + case syscall.IPPROTO_TCP: + case syscall.IPPROTO_TLS: + case syscall.IPPROTO_UDP: + w.startServer(sock, uint16(port), protoModeUDP) } - socket.laddr = addr + socket.ip, socket.port = ip, port return nil } -func (w *wifinina) Connect(sockfd netdev.Sockfd, servaddr netdev.SockAddr) error { +func toUint32(ip net.IP) uint32 { + ip = ip.To4() + 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 net.IP, port int) error { if debugging(debugNetdev) { - fmt.Printf("[Connect] sockfd: %d, servaddr: %s\r\n", sockfd, servaddr) + if host == "" { + fmt.Printf("[Connect] sockfd: %d, addr: %s:%d\r\n", sockfd, ip, port) + } else { + fmt.Printf("[Connect] sockfd: %d, host: %s:%d\r\n", sockfd, host, port) + } } w.mu.Lock() @@ -603,12 +617,12 @@ func (w *wifinina) Connect(sockfd netdev.Sockfd, servaddr netdev.SockAddr) error // Start the connection switch socket.protocol { - case netdev.IPPROTO_TCP: - w.startClient(sock, "", servaddr.IpUint32(), servaddr.Port(), protoModeTCP) - case netdev.IPPROTO_TLS: - w.startClient(sock, servaddr.Host(), 0, servaddr.Port(), protoModeTLS) - case netdev.IPPROTO_UDP: - w.startClient(sock, "", servaddr.IpUint32(), servaddr.Port(), protoModeUDP) + case syscall.IPPROTO_TCP: + w.startClient(sock, "", toUint32(ip), uint16(port), protoModeTCP) + case syscall.IPPROTO_TLS: + w.startClient(sock, host, 0, uint16(port), protoModeTLS) + case syscall.IPPROTO_UDP: + w.startClient(sock, "", toUint32(ip), uint16(port), protoModeUDP) return nil } @@ -633,10 +647,14 @@ func (w *wifinina) Connect(sockfd netdev.Sockfd, servaddr netdev.SockAddr) error w.stopClient(sock) - return fmt.Errorf("Connect to %s timed out", servaddr.String()) + if host == "" { + return fmt.Errorf("Connect to %s:%d timed out", ip, port) + } else { + return fmt.Errorf("Connect to %s:%d timed out", host, port) + } } -func (w *wifinina) Listen(sockfd netdev.Sockfd, backlog int) error { +func (w *wifinina) Listen(sockfd int, backlog int) error { if debugging(debugNetdev) { fmt.Printf("[Listen] sockfd: %d\r\n", sockfd) @@ -649,20 +667,20 @@ func (w *wifinina) Listen(sockfd netdev.Sockfd, backlog int) error { var socket = w.sockets[sock] switch socket.protocol { - case netdev.IPPROTO_TCP: - w.startServer(sock, socket.laddr.Port(), protoModeTCP) - case netdev.IPPROTO_UDP: + case syscall.IPPROTO_TCP: + w.startServer(sock, uint16(socket.port), protoModeTCP) + case syscall.IPPROTO_UDP: default: - return netdev.ErrProtocolNotSupported + return drivers.ErrProtocolNotSupported } return nil } -func (w *wifinina) Accept(sockfd netdev.Sockfd, peer netdev.SockAddr) (netdev.Sockfd, error) { +func (w *wifinina) Accept(sockfd int, ip net.IP, port int) (int, error) { if debugging(debugNetdev) { - fmt.Printf("[Accept] sockfd: %d, peer: %s\r\n", sockfd, peer) + fmt.Printf("[Accept] sockfd: %d, peer: %s:%d\r\n", sockfd, ip, port) } w.mu.Lock() @@ -673,9 +691,9 @@ func (w *wifinina) Accept(sockfd netdev.Sockfd, peer netdev.SockAddr) (netdev.So var socket = w.sockets[sock] switch socket.protocol { - case netdev.IPPROTO_TCP: + case syscall.IPPROTO_TCP: default: - return -1, netdev.ErrProtocolNotSupported + return -1, drivers.ErrProtocolNotSupported } for { @@ -715,12 +733,12 @@ func (w *wifinina) Accept(sockfd netdev.Sockfd, peer netdev.SockAddr) (netdev.So continue } // Reuse client socket - return netdev.Sockfd(client), nil + return int(client), nil } // Create new socket for client and return fd w.sockets[client] = newSocket(socket.protocol) - return netdev.Sockfd(client), nil + return int(client), nil } } @@ -793,21 +811,21 @@ func (w *wifinina) sendUDP(sock sock, buf []byte, timeout time.Duration) (int, e return len(buf), nil } -func (w *wifinina) sendChunk(sockfd netdev.Sockfd, buf []byte, timeout time.Duration) (int, error) { +func (w *wifinina) sendChunk(sockfd int, buf []byte, timeout time.Duration) (int, error) { var sock = sock(sockfd) var socket = w.sockets[sock] switch socket.protocol { - case netdev.IPPROTO_TCP, netdev.IPPROTO_TLS: + case syscall.IPPROTO_TCP, syscall.IPPROTO_TLS: return w.sendTCP(sock, buf, timeout) - case netdev.IPPROTO_UDP: + case syscall.IPPROTO_UDP: return w.sendUDP(sock, buf, timeout) } - return -1, netdev.ErrProtocolNotSupported + return -1, drivers.ErrProtocolNotSupported } -func (w *wifinina) Send(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, +func (w *wifinina) Send(sockfd int, buf []byte, flags int, timeout time.Duration) (int, error) { if debugging(debugNetdev) { @@ -835,7 +853,7 @@ func (w *wifinina) Send(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags return len(buf), nil } -func (w *wifinina) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags, +func (w *wifinina) Recv(sockfd int, buf []byte, flags int, timeout time.Duration) (int, error) { if debugging(debugNetdev) { @@ -861,7 +879,7 @@ func (w *wifinina) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags // Check if we've timed out if timeout > 0 { if time.Now().Before(expire) { - return -1, netdev.ErrRecvTimeout + return -1, drivers.ErrRecvTimeout } } @@ -880,7 +898,7 @@ func (w *wifinina) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags } // Check if socket went down - if socket.protocol != netdev.IPPROTO_UDP && w.sockDown(sock) { + if socket.protocol != syscall.IPPROTO_UDP && w.sockDown(sock) { // Get any last bytes n = int(w.getDataBuf(sock, buf[:max])) if debugging(debugNetdev) { @@ -905,7 +923,7 @@ func (w *wifinina) Recv(sockfd netdev.Sockfd, buf []byte, flags netdev.SockFlags } } -func (w *wifinina) Close(sockfd netdev.Sockfd) error { +func (w *wifinina) Close(sockfd int) error { if debugging(debugNetdev) { fmt.Printf("[Close] sockfd: %d\r\n", sockfd) @@ -923,7 +941,7 @@ func (w *wifinina) Close(sockfd netdev.Sockfd) error { w.stopClient(sock) - if socket.protocol == netdev.IPPROTO_UDP { + if socket.protocol == syscall.IPPROTO_UDP { socket.inuse = false return nil } @@ -942,17 +960,16 @@ func (w *wifinina) Close(sockfd netdev.Sockfd) error { w.mu.Lock() } - return netdev.ErrClosingSocket + return drivers.ErrClosingSocket } -func (w *wifinina) SetSockOpt(sockfd netdev.Sockfd, level netdev.SockOptLevel, - opt netdev.SockOpt, value any) error { +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 + return drivers.ErrNotSupported } // Is TCP/TLS socket connected? @@ -1181,7 +1198,7 @@ func (w *wifinina) getCurrentencryptionType() encryptionType { return encryptionType(enctype) } -func (w *wifinina) getCurrentBSSID() macAddress { +func (w *wifinina) getCurrentBSSID() net.HardwareAddr { return w.getMACAddress(w.req1(cmdGetCurrBSSID)) } @@ -1193,7 +1210,7 @@ func (w *wifinina) getCurrentSSID() string { return w.getString(w.req1(cmdGetCurrSSID)) } -func (w *wifinina) getMACAddr() macAddress { +func (w *wifinina) getMACAddr() net.HardwareAddr { if debugging(debugCmd) { fmt.Printf(" [cmdGetMACAddr]\r\n") } @@ -1207,7 +1224,7 @@ func (w *wifinina) faultf(f string, args ...any) { } } -func (w *wifinina) getIP() (ip, subnet, gateway netdev.IP) { +func (w *wifinina) getIP() (ip, subnet, gateway net.IP) { if debugging(debugCmd) { fmt.Printf(" [cmdGetIPAddr]\r\n") } @@ -1217,6 +1234,7 @@ func (w *wifinina) getIP() (ip, subnet, gateway netdev.IP) { w.faultf("getIP wanted l=3, got l=%d", l) return } + ip, subnet, gateway = make([]byte, 4), make([]byte, 4), make([]byte, 4) copy(ip[:], sl[0]) copy(subnet[:], sl[1]) copy(gateway[:], sl[2]) @@ -1234,9 +1252,9 @@ func (w *wifinina) getHostByName(name string) string { return w.getString(w.req0(cmdGetHostByName)) } -func (w *wifinina) getNetworkBSSID(idx int) macAddress { +func (w *wifinina) getNetworkBSSID(idx int) net.HardwareAddr { if idx < 0 || idx >= maxNetworks { - return macAddress{} + return net.HardwareAddr{} } return w.getMACAddress(w.reqUint8(cmdGetIdxBSSID, uint8(idx))) } @@ -1421,7 +1439,7 @@ func (w *wifinina) getFloat32(l uint8) float32 { return float32(w.getUint32(l)) } -func (w *wifinina) getMACAddress(l uint8) macAddress { +func (w *wifinina) getMACAddress(l uint8) net.HardwareAddr { if l == 6 { mac := w.buf[0:6] // Reverse the bytes @@ -1431,7 +1449,7 @@ func (w *wifinina) getMACAddress(l uint8) macAddress { return mac } w.faultf("expected length 6, was actually %d", l) - return macAddress{} + return net.HardwareAddr{} } func (w *wifinina) transfer(b byte) byte {