mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-09-10 08:39:27 +00:00
w5500: initial version the driver (#788)
Thank you, @nrwiersma for working on this and @soypat for review. Now merging.
This commit is contained in:
+248
@@ -0,0 +1,248 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user