mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
a74770b2e6
Signed-off-by: deadprogram <ron@hybridgroup.com>
865 lines
18 KiB
Go
865 lines
18 KiB
Go
// 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
|
|
|
|
package rtl8720dn // import "tinygo.org/x/drivers/rtl8720dn"
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"machine"
|
|
"net"
|
|
"net/netip"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"tinygo.org/x/drivers"
|
|
"tinygo.org/x/drivers/netdev"
|
|
"tinygo.org/x/drivers/netlink"
|
|
)
|
|
|
|
var _debug debug = debugBasic
|
|
|
|
//var _debug debug = debugBasic | debugNetdev
|
|
//var _debug debug = debugBasic | debugNetdev | debugRpc
|
|
|
|
var (
|
|
driverName = "Realtek rtl8720dn Wifi network device driver (rtl8720dn)"
|
|
)
|
|
|
|
const (
|
|
O_NONBLOCK = 1 // note: different value than syscall.O_NONBLOCK (0x800)
|
|
RTW_MODE_STA = 0x00000001
|
|
defaultChannel = 6
|
|
)
|
|
|
|
type sock int32
|
|
|
|
type socket struct {
|
|
protocol int
|
|
inuse bool
|
|
}
|
|
|
|
type Config struct {
|
|
// Enable
|
|
En machine.Pin
|
|
|
|
// UART config
|
|
Uart *machine.UART
|
|
Tx machine.Pin
|
|
Rx machine.Pin
|
|
Baudrate uint32
|
|
}
|
|
|
|
type rtl8720dn struct {
|
|
cfg *Config
|
|
notifyCb func(netlink.Event)
|
|
mu sync.Mutex
|
|
|
|
uart *machine.UART
|
|
seq uint64
|
|
|
|
debug bool
|
|
|
|
params *netlink.ConnectParams
|
|
|
|
netConnected bool
|
|
driverShown bool
|
|
deviceShown bool
|
|
|
|
killWatchdog chan bool
|
|
|
|
// keyed by sock as returned by rpc_lwip_socket()
|
|
sockets map[sock]*socket
|
|
}
|
|
|
|
func newSocket(protocol int) *socket {
|
|
return &socket{protocol: protocol, inuse: true}
|
|
}
|
|
|
|
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.params.Ssid) == 0 {
|
|
return netlink.ErrMissingSSID
|
|
}
|
|
|
|
if len(r.params.Passphrase) != 0 && len(r.params.Passphrase) < 8 {
|
|
return netlink.ErrShortPassphrase
|
|
}
|
|
|
|
if debugging(debugBasic) {
|
|
fmt.Printf("Connecting to Wifi SSID '%s'...", r.params.Ssid)
|
|
}
|
|
|
|
// Start the connection process
|
|
securityType := uint32(0) // RTW_SECURITY_OPEN
|
|
if len(r.params.Passphrase) != 0 {
|
|
securityType = 0x00400004 // RTW_SECURITY_WPA2_AES_PSK
|
|
}
|
|
|
|
result := r.rpc_wifi_connect(r.params.Ssid, r.params.Passphrase, securityType, -1, 0)
|
|
if result != 0 {
|
|
if debugging(debugBasic) {
|
|
fmt.Printf("FAILED\r\n")
|
|
}
|
|
return netlink.ErrConnectFailed
|
|
}
|
|
|
|
if debugging(debugBasic) {
|
|
fmt.Printf("CONNECTED\r\n")
|
|
}
|
|
|
|
if r.notifyCb != nil {
|
|
r.notifyCb(netlink.EventNetUp)
|
|
}
|
|
|
|
return r.startDhcpc()
|
|
}
|
|
|
|
func (r *rtl8720dn) startDhcps() error {
|
|
if result := r.rpc_tcpip_adapter_dhcps_start(0); result == -1 {
|
|
return netdev.ErrStartingDHCPServer
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *rtl8720dn) startAP() error {
|
|
if len(r.params.Ssid) == 0 {
|
|
return netlink.ErrMissingSSID
|
|
}
|
|
|
|
if len(r.params.Passphrase) != 0 && len(r.params.Passphrase) < 8 {
|
|
return netlink.ErrShortPassphrase
|
|
}
|
|
|
|
if debugging(debugBasic) {
|
|
fmt.Printf("Starting Wifi AP as SSID '%s'...", r.params.Ssid)
|
|
}
|
|
|
|
// Start the connection process
|
|
securityType := uint32(0) // RTW_SECURITY_OPEN
|
|
if len(r.params.Passphrase) != 0 {
|
|
securityType = 0x00400004 // RTW_SECURITY_WPA2_AES_PSK
|
|
}
|
|
|
|
result := r.rpc_wifi_start_ap(r.params.Ssid, r.params.Passphrase, securityType, defaultChannel)
|
|
if result != 0 {
|
|
if debugging(debugBasic) {
|
|
fmt.Printf("FAILED\r\n")
|
|
}
|
|
return netlink.ErrConnectFailed
|
|
}
|
|
|
|
if debugging(debugBasic) {
|
|
fmt.Printf("LISTENING\r\n")
|
|
}
|
|
|
|
if r.notifyCb != nil {
|
|
r.notifyCb(netlink.EventNetUp)
|
|
}
|
|
|
|
return r.startDhcps()
|
|
}
|
|
|
|
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", drivers.Version)
|
|
}
|
|
r.driverShown = true
|
|
}
|
|
|
|
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 (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 (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", ip)
|
|
fmt.Printf("DHCP-assigned subnet : %s\r\n", subnet)
|
|
fmt.Printf("DHCP-assigned gateway : %s\r\n", gateway)
|
|
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.params.WatchdogTimeout)
|
|
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(netlink.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()
|
|
|
|
retry:
|
|
for i := 0; r.params.Retries == 0 || i < r.params.Retries; i++ {
|
|
switch r.params.ConnectMode {
|
|
case netlink.ConnectModeAP:
|
|
if err := r.startAP(); err != nil {
|
|
if err == netlink.ErrConnectFailed {
|
|
continue
|
|
}
|
|
return err
|
|
}
|
|
break retry
|
|
|
|
default:
|
|
if err := r.connectToAP(); err != nil {
|
|
if err == netlink.ErrConnectFailed {
|
|
continue
|
|
}
|
|
return err
|
|
}
|
|
break retry
|
|
}
|
|
}
|
|
|
|
if r.networkDown() {
|
|
return netlink.ErrConnectFailed
|
|
}
|
|
|
|
r.showIP()
|
|
return nil
|
|
}
|
|
|
|
func (r *rtl8720dn) NetConnect(params *netlink.ConnectParams) error {
|
|
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
if r.netConnected {
|
|
return netlink.ErrConnected
|
|
}
|
|
|
|
r.params = params
|
|
|
|
r.showDriver()
|
|
|
|
if err := r.netConnect(true); err != nil {
|
|
return err
|
|
}
|
|
|
|
r.netConnected = true
|
|
|
|
if r.params.WatchdogTimeout != 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.params.WatchdogTimeout != 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.params.Ssid)
|
|
}
|
|
|
|
if r.notifyCb != nil {
|
|
r.notifyCb(netlink.EventNetDown)
|
|
}
|
|
}
|
|
|
|
func (r *rtl8720dn) NetNotify(cb func(netlink.Event)) {
|
|
r.notifyCb = cb
|
|
}
|
|
|
|
func (r *rtl8720dn) GetHostByName(name string) (netip.Addr, error) {
|
|
|
|
if debugging(debugNetdev) {
|
|
fmt.Printf("[GetHostByName] name: %s\r\n", name)
|
|
}
|
|
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
var ip [4]byte
|
|
result := r.rpc_netconn_gethostbyname(name, ip[:])
|
|
if result == -1 {
|
|
return netip.Addr{}, netdev.ErrHostUnknown
|
|
}
|
|
|
|
addr, ok := netip.AddrFromSlice(ip[:])
|
|
if !ok {
|
|
return netip.Addr{}, netdev.ErrMalAddr
|
|
}
|
|
|
|
return addr, nil
|
|
}
|
|
|
|
func (r *rtl8720dn) GetHardwareAddr() (net.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 net.HardwareAddr(addr), err
|
|
}
|
|
|
|
func (r *rtl8720dn) Addr() (netip.Addr, error) {
|
|
|
|
if debugging(debugNetdev) {
|
|
fmt.Printf("[GetIPAddr]\r\n")
|
|
}
|
|
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
ip, _, _, err := r.getIP()
|
|
|
|
return 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(domain int, stype int, protocol int) (int, error) {
|
|
|
|
if debugging(debugNetdev) {
|
|
fmt.Printf("[Socket] domain: %d, type: %d, protocol: %d\r\n",
|
|
domain, stype, protocol)
|
|
}
|
|
|
|
switch domain {
|
|
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 && stype == netdev.SOCK_STREAM:
|
|
newSock = r.rpc_lwip_socket(netdev.AF_INET, netdev.SOCK_STREAM,
|
|
netdev.IPPROTO_TCP)
|
|
case protocol == netdev.IPPROTO_TLS && stype == 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 && stype == 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 int(newSock), nil
|
|
}
|
|
|
|
func ipToName(ip netip.AddrPort) []byte {
|
|
name := make([]byte, 16)
|
|
name[0] = 0x00
|
|
name[1] = netdev.AF_INET
|
|
name[2] = byte(ip.Port() >> 8)
|
|
name[3] = byte(ip.Port())
|
|
if ip.Addr().Is4() {
|
|
addr := ip.Addr().As4()
|
|
name[4] = byte(addr[0])
|
|
name[5] = byte(addr[1])
|
|
name[6] = byte(addr[2])
|
|
name[7] = byte(addr[3])
|
|
}
|
|
return name
|
|
}
|
|
|
|
func nameToIp(name []byte) netip.AddrPort {
|
|
port := uint16(name[2])<<8 | uint16(name[3])
|
|
addr, _ := netip.AddrFromSlice(name[4:8])
|
|
return netip.AddrPortFrom(addr, port)
|
|
}
|
|
|
|
func (r *rtl8720dn) Bind(sockfd int, ip netip.AddrPort) error {
|
|
|
|
if debugging(debugNetdev) {
|
|
fmt.Printf("[Bind] sockfd: %d, addr: %s\r\n", sockfd, ip)
|
|
}
|
|
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
var sock = sock(sockfd)
|
|
var socket = r.sockets[sock]
|
|
var name = ipToName(ip)
|
|
|
|
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", ip)
|
|
}
|
|
default:
|
|
return netdev.ErrProtocolNotSupported
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *rtl8720dn) Connect(sockfd int, host string, ip netip.AddrPort) error {
|
|
|
|
port := ip.Port()
|
|
|
|
if debugging(debugNetdev) {
|
|
if host == "" {
|
|
fmt.Printf("[Connect] sockfd: %d, addr: %s\r\n", sockfd, ip)
|
|
} else {
|
|
fmt.Printf("[Connect] sockfd: %d, host: %s:%d\r\n", sockfd, host, port)
|
|
}
|
|
}
|
|
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
var sock = sock(sockfd)
|
|
var socket = r.sockets[sock]
|
|
var name = ipToName(ip)
|
|
|
|
// 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", ip)
|
|
}
|
|
case netdev.IPPROTO_TLS:
|
|
result := r.rpc_wifi_start_ssl_client(uint32(sock),
|
|
host, uint32(port), 0)
|
|
if result == -1 {
|
|
return fmt.Errorf("Connect to %s:%d failed", host, port)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *rtl8720dn) Listen(sockfd int, 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), netdev.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 int) (int, netip.AddrPort, error) {
|
|
|
|
if debugging(debugNetdev) {
|
|
fmt.Printf("[Accept] sockfd: %d\r\n", sockfd)
|
|
}
|
|
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
var newSock int32
|
|
var lsock = sock(sockfd)
|
|
var socket = r.sockets[lsock]
|
|
var name = ipToName(netip.AddrPort{})
|
|
|
|
switch socket.protocol {
|
|
case netdev.IPPROTO_TCP:
|
|
default:
|
|
return -1, netip.AddrPort{}, 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.
|
|
namelen := uint32(len(name))
|
|
newSock = r.rpc_lwip_accept(int32(lsock), name, &namelen)
|
|
if newSock == -1 {
|
|
// No new client
|
|
time.Sleep(100 * time.Millisecond)
|
|
continue
|
|
}
|
|
|
|
// Get remote peer ip:port
|
|
namelen = uint32(len(name))
|
|
result := r.rpc_lwip_getpeername(int32(newSock), name, &namelen)
|
|
if result == -1 {
|
|
return -1, netip.AddrPort{}, fmt.Errorf("Getpeername failed")
|
|
}
|
|
raddr := nameToIp(name)
|
|
|
|
// If we've already seen this socket, we can re-use
|
|
// 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 int(newSock), raddr, nil
|
|
}
|
|
|
|
// Create new socket for client and return fd
|
|
r.sockets[sock(newSock)] = newSocket(socket.protocol)
|
|
return int(newSock), raddr, nil
|
|
}
|
|
}
|
|
|
|
func (r *rtl8720dn) sendChunk(sockfd int, buf []byte, deadline time.Time) (int, error) {
|
|
var sock = sock(sockfd)
|
|
var socket = r.sockets[sock]
|
|
|
|
// Check if we've timed out
|
|
if !deadline.IsZero() {
|
|
if time.Now().After(deadline) {
|
|
return -1, netdev.ErrTimeout
|
|
}
|
|
}
|
|
|
|
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 int, buf []byte, flags int,
|
|
deadline time.Time) (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
|
|
|
|
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], deadline)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
}
|
|
|
|
return len(buf), nil
|
|
}
|
|
|
|
func (r *rtl8720dn) Recv(sockfd int, buf []byte, flags int,
|
|
deadline time.Time) (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 n int32
|
|
|
|
// Limit length read size to chunk large read requests
|
|
if length > 1436 {
|
|
length = 1436
|
|
}
|
|
|
|
for {
|
|
// Check if we've timed out
|
|
if !deadline.IsZero() {
|
|
if time.Now().After(deadline) {
|
|
return -1, netdev.ErrTimeout
|
|
}
|
|
}
|
|
|
|
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 int) 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 int, level int, opt int, value interface{}) 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()
|
|
}
|
|
|
|
func (r *rtl8720dn) getMACAddr() string {
|
|
var mac [18]uint8
|
|
r.rpc_wifi_get_mac_address(mac[:])
|
|
return string(mac[:])
|
|
}
|
|
|
|
func (r *rtl8720dn) getIP() (ip, subnet, gateway netip.Addr, 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, _ = netip.AddrFromSlice(ip_info[0:4])
|
|
subnet, _ = netip.AddrFromSlice(ip_info[4:8])
|
|
gateway, _ = netip.AddrFromSlice(ip_info[8:12])
|
|
return
|
|
}
|