mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
4b831af52b
Thank you, @nrwiersma for working on this and @soypat for review. Now merging.
249 lines
5.3 KiB
Go
249 lines
5.3 KiB
Go
// Package w5500 implements a driver for the W5500 Ethernet controller.
|
|
//
|
|
// The driver supports basic network functionality including TCP and UDP sockets.
|
|
// It currently does not use the IRQ or RST pins.
|
|
//
|
|
// Datasheet: https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf
|
|
// Product Page: https://wiznet.io/products/ethernet-chips/w5500
|
|
package w5500
|
|
|
|
import (
|
|
"errors"
|
|
"net"
|
|
"net/netip"
|
|
"sync"
|
|
|
|
"tinygo.org/x/drivers"
|
|
"tinygo.org/x/drivers/internal/pin"
|
|
"tinygo.org/x/drivers/netdev"
|
|
)
|
|
|
|
var _ netdev.Netdever = &Device{}
|
|
|
|
// Resolver is a function that resolves a hostname to an IP address.
|
|
type Resolver func(host string) (netip.Addr, error)
|
|
|
|
// Device is a driver for the W5500 Ethernet controller.
|
|
type Device struct {
|
|
maxSockets int
|
|
maxSockSize int
|
|
|
|
mu sync.Mutex
|
|
bus drivers.SPI
|
|
cs pin.OutputFunc
|
|
dns Resolver
|
|
|
|
sockets []*socket
|
|
laddr netip.Addr
|
|
|
|
cmdBuf [3]byte
|
|
}
|
|
|
|
// New returns a new w5500 driver.
|
|
func New(bus drivers.SPI, csPin pin.Output) *Device {
|
|
return &Device{
|
|
bus: bus,
|
|
cs: csPin.Set,
|
|
}
|
|
}
|
|
|
|
// Config is the configuration for the device.
|
|
//
|
|
// The SPI bus must be fully configured.
|
|
type Config struct {
|
|
DNS Resolver
|
|
|
|
MAC net.HardwareAddr
|
|
IP netip.Addr
|
|
SubnetMask netip.Addr
|
|
Gateway netip.Addr
|
|
|
|
// Optional, default is 8.
|
|
MaxSockets int
|
|
}
|
|
|
|
// Configure sets up the device.
|
|
//
|
|
// MAC address must be provided. The other fields are optional.
|
|
func (d *Device) Configure(cfg Config) error {
|
|
d.cs(true)
|
|
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
|
|
d.dns = cfg.DNS
|
|
|
|
d.reset()
|
|
|
|
if err := d.setupSockets(cfg.MaxSockets); err != nil {
|
|
return errors.New("could not setup sockets: " + err.Error())
|
|
}
|
|
|
|
// Set the MAC address and IP configuration.
|
|
d.write(regMAC, 0, cfg.MAC)
|
|
d.write(regIPAddr, 0, cfg.IP.AsSlice())
|
|
d.write(regSubnetMask, 0, cfg.SubnetMask.AsSlice())
|
|
d.write(regGatewayAddr, 0, cfg.Gateway.AsSlice())
|
|
d.laddr = cfg.IP
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *Device) setupSockets(maxSockets int) error {
|
|
if maxSockets == 0 {
|
|
maxSockets = 8 // Default to 8 sockets if not specified.
|
|
}
|
|
switch maxSockets {
|
|
case 1, 2, 4, 8:
|
|
// Valid socket counts.
|
|
default:
|
|
return errors.New("invalid number of sockets, must be one of 1, 2, 4, or 8")
|
|
}
|
|
|
|
socks := make([]*socket, maxSockets)
|
|
for i := range socks {
|
|
socks[i] = &socket{
|
|
sockn: uint8(i),
|
|
}
|
|
}
|
|
|
|
d.maxSockets = maxSockets
|
|
d.maxSockSize = 16 * 1024 / maxSockets
|
|
d.sockets = socks
|
|
|
|
// Set the RX and TX buffer sizes for each socket.
|
|
for i := 0; i < 8; i++ {
|
|
size := byte(d.maxSockSize >> 10)
|
|
if i >= maxSockets {
|
|
size = 0
|
|
}
|
|
|
|
d.writeByte(sockRXBUFSize, sockAddr(uint8(i)), size)
|
|
d.writeByte(sockTXBUFSize, sockAddr(uint8(i)), size)
|
|
}
|
|
|
|
mask := byte(0b11111111)
|
|
switch maxSockets {
|
|
case 1:
|
|
mask = 0b00000001
|
|
case 2:
|
|
mask = 0b00000011
|
|
case 4:
|
|
mask = 0b00001111
|
|
}
|
|
d.writeByte(regSockIntMask, 0, mask)
|
|
d.writeByte(regIntMask, 0, 0)
|
|
return nil
|
|
}
|
|
|
|
// Reset performs a soft reset.
|
|
func (d *Device) Reset() {
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
|
|
d.reset()
|
|
}
|
|
|
|
func (d *Device) reset() {
|
|
// RST is bit 7 of regMode.
|
|
d.writeByte(regMode, 0, 0x80)
|
|
}
|
|
|
|
// GetHardwareAddr returns the hardware address of the device.
|
|
func (d *Device) GetHardwareAddr() (net.HardwareAddr, error) {
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
|
|
mac := make([]byte, 6)
|
|
d.read(regMAC, 0, mac)
|
|
return mac, nil
|
|
}
|
|
|
|
// Addr returns the IP address of the device.
|
|
func (d *Device) Addr() (netip.Addr, error) {
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
|
|
var ip [4]byte
|
|
d.read(regIPAddr, 0, ip[:])
|
|
return netip.AddrFrom4(ip), nil
|
|
}
|
|
|
|
// SetAddr sets the IP address of the device.
|
|
//
|
|
// The IP address must be a valid IPv4 address.
|
|
func (d *Device) SetAddr(ip netip.Addr) error {
|
|
if err := d.setAddress(regIPAddr, ip); err != nil {
|
|
return errors.New("could not set IP address: " + err.Error())
|
|
}
|
|
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
|
|
d.laddr = ip
|
|
return nil
|
|
}
|
|
|
|
// SetSubnetMask sets the subnet mask of the device.
|
|
//
|
|
// The subnet mask must be a valid IPv4 address.
|
|
// It is not checked if the subnet mask is valid for the device's IP address.
|
|
func (d *Device) SetSubnetMask(mask netip.Addr) error {
|
|
return d.setAddress(regSubnetMask, mask)
|
|
}
|
|
|
|
// SetGateway sets the gateway address of the device.
|
|
//
|
|
// The gateway must be a valid IPv4 address.
|
|
// It is not checked if the gateway is in the same subnet as the device.
|
|
func (d *Device) SetGateway(gateway netip.Addr) error {
|
|
return d.setAddress(regGatewayAddr, gateway)
|
|
}
|
|
|
|
func (d *Device) setAddress(addr uint16, ip netip.Addr) error {
|
|
if !ip.IsValid() || !ip.Is4() {
|
|
return errors.New("invalid IP address: " + ip.String())
|
|
}
|
|
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
|
|
d.write(addr, 0, ip.AsSlice())
|
|
return nil
|
|
}
|
|
|
|
// LinkStatus is the link status of the device.
|
|
type LinkStatus = uint8
|
|
|
|
// LinkStatus values.
|
|
const (
|
|
LinkStatusDown LinkStatus = iota
|
|
LinkStatusUp
|
|
)
|
|
|
|
// LinkStatus returns the current link status of the device.
|
|
func (d *Device) LinkStatus() LinkStatus {
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
|
|
return d.readByte(regPHYCfg, 0) & 0b00000001
|
|
}
|
|
|
|
// LinkInfo returns the current link information of the device.
|
|
func (d *Device) LinkInfo() string {
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
|
|
linkInfo := d.readByte(regPHYCfg, 0) & 0b00000110
|
|
|
|
speed := "10Mbps"
|
|
if linkInfo&0b00000010 != 0 {
|
|
speed = "100Mbps"
|
|
}
|
|
duplex := "Half Duplex"
|
|
if linkInfo&0b00000100 != 0 {
|
|
duplex = "Full Duplex"
|
|
}
|
|
return speed + " " + duplex
|
|
}
|