Skip to content

Commit

Permalink
Add network device driver model, netdev
Browse files Browse the repository at this point in the history
  • Loading branch information
scottfeldman authored and deadprogram committed Dec 6, 2023
1 parent 00b1b4f commit 276feec
Show file tree
Hide file tree
Showing 128 changed files with 6,214 additions and 15,364 deletions.
289 changes: 289 additions & 0 deletions README-net.md
Original file line number Diff line number Diff line change
@@ -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 Sockets](#using-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 Sockets
A netdev implements a BSD socket-like interface so an application can make direct
socket calls, bypassing the net package.
Here is a simple TCP application using direct sockets:
```go
package main

import (
"net" // only need to parse IP address

"tinygo.org/x/drivers"
"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(drivers.AF_INET, drivers.SOCK_STREAM, drivers.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.
20 changes: 0 additions & 20 deletions espat/adapter.go

This file was deleted.

Loading

0 comments on commit 276feec

Please sign in to comment.