174 lines
4.1 KiB
C
174 lines
4.1 KiB
C
/*
|
|
* Copyright (c) 2024, sakumisu
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
#include <nuttx/config.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <semaphore.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <debug.h>
|
|
|
|
#include <nuttx/net/ip.h>
|
|
#include <nuttx/net/netdev.h>
|
|
|
|
#include "usbh_core.h"
|
|
|
|
// #define CONFIG_USBHOST_PLATFORM_CDC_ECM
|
|
#define CONFIG_USBHOST_PLATFORM_CDC_RNDIS
|
|
// #define CONFIG_USBHOST_PLATFORM_CDC_NCM
|
|
// #define CONFIG_USBHOST_PLATFORM_ASIX
|
|
// #define CONFIG_USBHOST_PLATFORM_RTL8152
|
|
|
|
struct usbh_net {
|
|
struct net_driver_s netdev;
|
|
struct work_s txpollwork;
|
|
bool linkup;
|
|
};
|
|
|
|
void usbh_net_eth_output_common(struct net_driver_s *dev, uint8_t *buf)
|
|
{
|
|
usb_memcpy(buf, dev->d_buf, dev->d_len);
|
|
}
|
|
|
|
void usbh_net_eth_input_common(struct net_driver_s *dev, uint8_t *buf, size_t len, int (*eth_output)(uint32_t buflen))
|
|
{
|
|
FAR struct eth_hdr_s *hdr;
|
|
|
|
net_lock();
|
|
|
|
NETDEV_RXPACKETS(dev);
|
|
|
|
/* Any ACK or other response packet generated by the network stack
|
|
* will always be shorter than the received packet, therefore it is
|
|
* safe to pass the received frame buffer directly.
|
|
*/
|
|
|
|
dev->d_buf = buf;
|
|
dev->d_len = len;
|
|
|
|
hdr = (FAR struct eth_hdr_s *)dev->d_buf;
|
|
#ifdef CONFIG_NET_IPv4
|
|
if (hdr->type == HTONS(ETHTYPE_IP)) {
|
|
NETDEV_RXIPV4(dev);
|
|
|
|
/* Receive an IPv4 packet from the network device */
|
|
|
|
ipv4_input(dev);
|
|
if (dev->d_len > 0) {
|
|
/* And send the packet */
|
|
usbh_net_eth_output_common(dev, usbh_rndis_get_eth_txbuf());
|
|
eth_output(dev->d_len);
|
|
}
|
|
} else
|
|
#endif
|
|
#ifdef CONFIG_NET_IPv6
|
|
if (hdr->type == HTONS(ETHTYPE_IP6)) {
|
|
NETDEV_RXIPV6(dev);
|
|
|
|
/* Give the IPv6 packet to the network layer */
|
|
|
|
ipv6_input(dev);
|
|
|
|
if (dev->d_len > 0) {
|
|
/* And send the packet */
|
|
usbh_net_eth_output_common(dev, usbh_rndis_get_eth_txbuf());
|
|
eth_output(dev->d_len);
|
|
}
|
|
} else
|
|
#endif
|
|
#ifdef CONFIG_NET_ARP
|
|
if (hdr->type == HTONS(ETHTYPE_ARP)) {
|
|
NETDEV_RXARP(dev);
|
|
|
|
arp_input(dev);
|
|
if (dev->d_len > 0) {
|
|
usbh_net_eth_output_common(dev, usbh_rndis_get_eth_txbuf());
|
|
eth_output(dev->d_len);
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
NETDEV_RXDROPPED(dev);
|
|
}
|
|
|
|
net_unlock();
|
|
}
|
|
|
|
#ifdef CONFIG_USBHOST_PLATFORM_CDC_RNDIS
|
|
#include "usbh_rndis.h"
|
|
|
|
struct usbh_net g_rndis_dev;
|
|
|
|
static int rndis_ifup(struct net_driver_s *dev)
|
|
{
|
|
printf("rndis if up\r\n");
|
|
g_rndis_dev.linkup = true;
|
|
usb_osal_thread_create("usbh_rndis_rx", 2048, CONFIG_USBHOST_PSC_PRIO + 1, usbh_rndis_rx_thread, NULL);
|
|
return OK;
|
|
}
|
|
|
|
static int rndis_ifdown(struct net_driver_s *dev)
|
|
{
|
|
printf("rndis if down\r\n");
|
|
g_rndis_dev.linkup = false;
|
|
return OK;
|
|
}
|
|
|
|
static int rndis_txpoll(struct net_driver_s *dev)
|
|
{
|
|
usbh_net_eth_output_common(&g_rndis_dev, usbh_rndis_get_eth_txbuf());
|
|
return usbh_rndis_eth_output(g_rndis_dev.netdev.d_len);
|
|
}
|
|
|
|
static void rndis_txavail_work(void *arg)
|
|
{
|
|
net_lock();
|
|
|
|
if (g_rndis_dev.linkup) {
|
|
devif_poll(&g_rndis_dev.netdev, rndis_txpoll);
|
|
} else {
|
|
}
|
|
|
|
net_unlock();
|
|
}
|
|
|
|
static int rndis_txavail(struct net_driver_s *dev)
|
|
{
|
|
if (work_available(&g_rndis_dev.txpollwork)) {
|
|
work_queue(LPWORK, &g_rndis_dev.txpollwork, rndis_txavail_work, NULL, 0);
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
void usbh_rndis_eth_input(uint8_t *buf, uint32_t buflen)
|
|
{
|
|
usbh_net_eth_input_common(&g_rndis_dev.netdev, buf, buflen, usbh_rndis_eth_output);
|
|
}
|
|
|
|
void usbh_rndis_run(struct usbh_rndis *rndis_class)
|
|
{
|
|
memset(&g_rndis_dev.netdev, 0, sizeof(struct net_driver_s));
|
|
|
|
g_rndis_dev.netdev.d_ifup = rndis_ifup;
|
|
g_rndis_dev.netdev.d_ifdown = rndis_ifdown;
|
|
g_rndis_dev.netdev.d_txavail = rndis_txavail;
|
|
g_rndis_dev.netdev.d_private = rndis_class;
|
|
|
|
for (uint8_t j = 0; j < 6; j++) {
|
|
g_rndis_dev.netdev.d_mac.ether.ether_addr_octet[j] = rndis_class->mac[j];
|
|
}
|
|
netdev_register(&g_rndis_dev.netdev, NET_LL_ETHERNET);
|
|
}
|
|
|
|
void usbh_rndis_stop(struct usbh_rndis *rndis_class)
|
|
{
|
|
}
|
|
#endif |