summaryrefslogtreecommitdiff
path: root/platform/nuttx/usbh_net.c
blob: c7af90f99ca26a2b84c0af61accf9a9f3558db85 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/*
 * Copyright (c) 2024, sakumisu
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#include <nuttx/net/ip.h>
#include <nuttx/net/netdev.h>

#include "usbh_core.h"

#if CONFIG_NET_ETH_PKTSIZE < 1514
#error "CONFIG_NET_ETH_PKTSIZE must be at least 1514"
#endif

#if CONFIG_IOB_BUFSIZE < 1514
#error "CONFIG_IOB_BUFSIZE must be at least 1514"
#endif

#ifndef CONFIG_NETDEV_LATEINIT
#error "CONFIG_NETDEV_LATEINIT must be enabled"
#endif

#ifndef CONFIG_NETUTILS_DHCPC
#error "CONFIG_NETUTILS_DHCPC must be enabled"
#endif

#ifndef CONFIG_NETINIT_DHCPC
#error "CONFIG_NETINIT_DHCPC must be enabled"
#endif

// #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, uint8_t* (*eth_input)(void), 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, eth_input());
            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, eth_input());
            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, eth_input());
            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.netdev, 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_get_eth_txbuf, 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);

    netinit_bringup();
}

void usbh_rndis_stop(struct usbh_rndis *rndis_class)
{
}
#endif