hyperv/hn: Export VF list and VF-HN mapping
The VF-HN map will be used later on to implement "transparent VF". MFC after: 3 days Sponsored by: Microsoft Differential Revision: https://reviews.freebsd.org/D11618
This commit is contained in:
parent
8819ad852f
commit
499c3e1739
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=321407
@ -69,6 +69,8 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/module.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/rmlock.h>
|
||||
#include <sys/sbuf.h>
|
||||
#include <sys/smp.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
@ -119,6 +121,8 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#define HN_RING_CNT_DEF_MAX 8
|
||||
|
||||
#define HN_VFMAP_SIZE_DEF 8
|
||||
|
||||
/* YYY should get it from the underlying channel */
|
||||
#define HN_TX_DESC_CNT 512
|
||||
|
||||
@ -255,6 +259,11 @@ static int hn_ifmedia_upd(struct ifnet *);
|
||||
static void hn_ifmedia_sts(struct ifnet *,
|
||||
struct ifmediareq *);
|
||||
|
||||
static void hn_ifnet_event(void *, struct ifnet *, int);
|
||||
static void hn_ifaddr_event(void *, struct ifnet *);
|
||||
static void hn_ifnet_attevent(void *, struct ifnet *);
|
||||
static void hn_ifnet_detevent(void *, struct ifnet *);
|
||||
|
||||
static int hn_rndis_rxinfo(const void *, int,
|
||||
struct hn_rxinfo *);
|
||||
static void hn_rndis_rx_data(struct hn_rx_ring *,
|
||||
@ -303,6 +312,9 @@ static int hn_txagg_pktmax_sysctl(SYSCTL_HANDLER_ARGS);
|
||||
static int hn_txagg_align_sysctl(SYSCTL_HANDLER_ARGS);
|
||||
static int hn_polling_sysctl(SYSCTL_HANDLER_ARGS);
|
||||
static int hn_vf_sysctl(SYSCTL_HANDLER_ARGS);
|
||||
static int hn_rxvf_sysctl(SYSCTL_HANDLER_ARGS);
|
||||
static int hn_vflist_sysctl(SYSCTL_HANDLER_ARGS);
|
||||
static int hn_vfmap_sysctl(SYSCTL_HANDLER_ARGS);
|
||||
|
||||
static void hn_stop(struct hn_softc *, bool);
|
||||
static void hn_init_locked(struct hn_softc *);
|
||||
@ -502,9 +514,21 @@ static int hn_tx_agg_pkts = -1;
|
||||
SYSCTL_INT(_hw_hn, OID_AUTO, tx_agg_pkts, CTLFLAG_RDTUN,
|
||||
&hn_tx_agg_pkts, 0, "Packet transmission aggregation packet limit");
|
||||
|
||||
/* VF list */
|
||||
SYSCTL_PROC(_hw_hn, OID_AUTO, vflist, CTLFLAG_RD | CTLTYPE_STRING,
|
||||
0, 0, hn_vflist_sysctl, "A", "VF list");
|
||||
|
||||
/* VF mapping */
|
||||
SYSCTL_PROC(_hw_hn, OID_AUTO, vfmap, CTLFLAG_RD | CTLTYPE_STRING,
|
||||
0, 0, hn_vfmap_sysctl, "A", "VF mapping");
|
||||
|
||||
static u_int hn_cpu_index; /* next CPU for channel */
|
||||
static struct taskqueue **hn_tx_taskque;/* shared TX taskqueues */
|
||||
|
||||
static struct rmlock hn_vfmap_lock;
|
||||
static int hn_vfmap_size;
|
||||
static struct ifnet **hn_vfmap;
|
||||
|
||||
#ifndef RSS
|
||||
static const uint8_t
|
||||
hn_rss_key_default[NDIS_HASH_KEYSIZE_TOEPLITZ] = {
|
||||
@ -971,7 +995,7 @@ hn_update_vf_task(void *arg, int pending __unused)
|
||||
{
|
||||
struct hn_update_vf *uv = arg;
|
||||
|
||||
uv->rxr->hn_vf = uv->vf;
|
||||
uv->rxr->hn_rxvf_ifp = uv->vf;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -994,11 +1018,35 @@ hn_update_vf(struct hn_softc *sc, struct ifnet *vf)
|
||||
uv.vf = vf;
|
||||
vmbus_chan_run_task(rxr->hn_chan, &task);
|
||||
} else {
|
||||
rxr->hn_vf = vf;
|
||||
rxr->hn_rxvf_ifp = vf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static __inline bool
|
||||
hn_ismyvf(const struct hn_softc *sc, const struct ifnet *ifp)
|
||||
{
|
||||
const struct ifnet *hn_ifp;
|
||||
|
||||
hn_ifp = sc->hn_ifp;
|
||||
|
||||
if (ifp == hn_ifp)
|
||||
return (false);
|
||||
|
||||
if (ifp->if_alloctype != IFT_ETHER)
|
||||
return (false);
|
||||
|
||||
/* Ignore lagg/vlan interfaces */
|
||||
if (strcmp(ifp->if_dname, "lagg") == 0 ||
|
||||
strcmp(ifp->if_dname, "vlan") == 0)
|
||||
return (false);
|
||||
|
||||
if (bcmp(IF_LLADDR(ifp), IF_LLADDR(hn_ifp), ETHER_ADDR_LEN) != 0)
|
||||
return (false);
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
static void
|
||||
hn_set_vf(struct hn_softc *sc, struct ifnet *ifp, bool vf)
|
||||
{
|
||||
@ -1009,22 +1057,11 @@ hn_set_vf(struct hn_softc *sc, struct ifnet *ifp, bool vf)
|
||||
if (!(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
|
||||
goto out;
|
||||
|
||||
if (!hn_ismyvf(sc, ifp))
|
||||
goto out;
|
||||
|
||||
hn_ifp = sc->hn_ifp;
|
||||
|
||||
if (ifp == hn_ifp)
|
||||
goto out;
|
||||
|
||||
if (ifp->if_alloctype != IFT_ETHER)
|
||||
goto out;
|
||||
|
||||
/* Ignore lagg/vlan interfaces */
|
||||
if (strcmp(ifp->if_dname, "lagg") == 0 ||
|
||||
strcmp(ifp->if_dname, "vlan") == 0)
|
||||
goto out;
|
||||
|
||||
if (bcmp(IF_LLADDR(ifp), IF_LLADDR(hn_ifp), ETHER_ADDR_LEN) != 0)
|
||||
goto out;
|
||||
|
||||
/* Now we're sure 'ifp' is a real VF device. */
|
||||
if (vf) {
|
||||
if (sc->hn_flags & HN_FLAG_VF)
|
||||
@ -1037,7 +1074,7 @@ hn_set_vf(struct hn_softc *sc, struct ifnet *ifp, bool vf)
|
||||
goto out;
|
||||
|
||||
sc->hn_flags &= ~HN_FLAG_VF;
|
||||
if (sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING)
|
||||
if (hn_ifp->if_drv_flags & IFF_DRV_RUNNING)
|
||||
hn_rxfilter_config(sc);
|
||||
else
|
||||
hn_set_rxfilter(sc, NDIS_PACKET_TYPE_NONE);
|
||||
@ -1052,7 +1089,7 @@ hn_set_vf(struct hn_softc *sc, struct ifnet *ifp, bool vf)
|
||||
hn_suspend_mgmt(sc);
|
||||
sc->hn_link_flags &=
|
||||
~(HN_LINK_FLAG_LINKUP | HN_LINK_FLAG_NETCHG);
|
||||
if_link_state_change(sc->hn_ifp, LINK_STATE_DOWN);
|
||||
if_link_state_change(hn_ifp, LINK_STATE_DOWN);
|
||||
} else {
|
||||
hn_resume_mgmt(sc);
|
||||
}
|
||||
@ -1082,6 +1119,85 @@ hn_ifaddr_event(void *arg, struct ifnet *ifp)
|
||||
hn_set_vf(arg, ifp, ifp->if_flags & IFF_UP);
|
||||
}
|
||||
|
||||
static void
|
||||
hn_ifnet_attevent(void *xsc, struct ifnet *ifp)
|
||||
{
|
||||
struct hn_softc *sc = xsc;
|
||||
|
||||
HN_LOCK(sc);
|
||||
|
||||
if (!(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
|
||||
goto done;
|
||||
|
||||
if (!hn_ismyvf(sc, ifp))
|
||||
goto done;
|
||||
|
||||
if (sc->hn_vf_ifp != NULL) {
|
||||
if_printf(sc->hn_ifp, "%s was attached as VF\n",
|
||||
sc->hn_vf_ifp->if_xname);
|
||||
goto done;
|
||||
}
|
||||
|
||||
rm_wlock(&hn_vfmap_lock);
|
||||
|
||||
if (ifp->if_index >= hn_vfmap_size) {
|
||||
struct ifnet **newmap;
|
||||
int newsize;
|
||||
|
||||
newsize = ifp->if_index + HN_VFMAP_SIZE_DEF;
|
||||
newmap = malloc(sizeof(struct ifnet *) * newsize, M_DEVBUF,
|
||||
M_WAITOK | M_ZERO);
|
||||
|
||||
memcpy(newmap, hn_vfmap,
|
||||
sizeof(struct ifnet *) * hn_vfmap_size);
|
||||
free(hn_vfmap, M_DEVBUF);
|
||||
hn_vfmap = newmap;
|
||||
hn_vfmap_size = newsize;
|
||||
}
|
||||
KASSERT(hn_vfmap[ifp->if_index] == NULL,
|
||||
("%s: ifindex %d was mapped to %s",
|
||||
ifp->if_xname, ifp->if_index, hn_vfmap[ifp->if_index]->if_xname));
|
||||
hn_vfmap[ifp->if_index] = sc->hn_ifp;
|
||||
|
||||
rm_wunlock(&hn_vfmap_lock);
|
||||
|
||||
sc->hn_vf_ifp = ifp;
|
||||
done:
|
||||
HN_UNLOCK(sc);
|
||||
}
|
||||
|
||||
static void
|
||||
hn_ifnet_detevent(void *xsc, struct ifnet *ifp)
|
||||
{
|
||||
struct hn_softc *sc = xsc;
|
||||
|
||||
HN_LOCK(sc);
|
||||
|
||||
if (sc->hn_vf_ifp == NULL)
|
||||
goto done;
|
||||
|
||||
if (!hn_ismyvf(sc, ifp))
|
||||
goto done;
|
||||
|
||||
sc->hn_vf_ifp = NULL;
|
||||
|
||||
rm_wlock(&hn_vfmap_lock);
|
||||
|
||||
KASSERT(ifp->if_index < hn_vfmap_size,
|
||||
("ifindex %d, vfmapsize %d", ifp->if_index, hn_vfmap_size));
|
||||
if (hn_vfmap[ifp->if_index] != NULL) {
|
||||
KASSERT(hn_vfmap[ifp->if_index] == sc->hn_ifp,
|
||||
("%s: ifindex %d was mapped to %s",
|
||||
ifp->if_xname, ifp->if_index,
|
||||
hn_vfmap[ifp->if_index]->if_xname));
|
||||
hn_vfmap[ifp->if_index] = NULL;
|
||||
}
|
||||
|
||||
rm_wunlock(&hn_vfmap_lock);
|
||||
done:
|
||||
HN_UNLOCK(sc);
|
||||
}
|
||||
|
||||
/* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */
|
||||
static const struct hyperv_guid g_net_vsc_device_type = {
|
||||
.hv_guid = {0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46,
|
||||
@ -1322,6 +1438,9 @@ hn_attach(device_t dev)
|
||||
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "vf",
|
||||
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
|
||||
hn_vf_sysctl, "A", "Virtual Function's name");
|
||||
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxvf",
|
||||
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
|
||||
hn_rxvf_sysctl, "A", "activated Virtual Function's name");
|
||||
|
||||
/*
|
||||
* Setup the ifmedia, which has been initialized earlier.
|
||||
@ -1412,10 +1531,14 @@ hn_attach(device_t dev)
|
||||
|
||||
sc->hn_ifnet_evthand = EVENTHANDLER_REGISTER(ifnet_event,
|
||||
hn_ifnet_event, sc, EVENTHANDLER_PRI_ANY);
|
||||
|
||||
sc->hn_ifaddr_evthand = EVENTHANDLER_REGISTER(ifaddr_event,
|
||||
hn_ifaddr_event, sc, EVENTHANDLER_PRI_ANY);
|
||||
|
||||
sc->hn_ifnet_atthand = EVENTHANDLER_REGISTER(ether_ifattach_event,
|
||||
hn_ifnet_attevent, sc, EVENTHANDLER_PRI_ANY);
|
||||
sc->hn_ifnet_dethand = EVENTHANDLER_REGISTER(ifnet_departure_event,
|
||||
hn_ifnet_detevent, sc, EVENTHANDLER_PRI_ANY);
|
||||
|
||||
return (0);
|
||||
failed:
|
||||
if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED)
|
||||
@ -1428,12 +1551,25 @@ static int
|
||||
hn_detach(device_t dev)
|
||||
{
|
||||
struct hn_softc *sc = device_get_softc(dev);
|
||||
struct ifnet *ifp = sc->hn_ifp;
|
||||
struct ifnet *ifp = sc->hn_ifp, *vf_ifp;
|
||||
|
||||
if (sc->hn_ifaddr_evthand != NULL)
|
||||
EVENTHANDLER_DEREGISTER(ifaddr_event, sc->hn_ifaddr_evthand);
|
||||
if (sc->hn_ifnet_evthand != NULL)
|
||||
EVENTHANDLER_DEREGISTER(ifnet_event, sc->hn_ifnet_evthand);
|
||||
if (sc->hn_ifnet_atthand != NULL) {
|
||||
EVENTHANDLER_DEREGISTER(ether_ifattach_event,
|
||||
sc->hn_ifnet_atthand);
|
||||
}
|
||||
if (sc->hn_ifnet_dethand != NULL) {
|
||||
EVENTHANDLER_DEREGISTER(ifnet_departure_event,
|
||||
sc->hn_ifnet_dethand);
|
||||
}
|
||||
|
||||
vf_ifp = sc->hn_vf_ifp;
|
||||
__compiler_membar();
|
||||
if (vf_ifp != NULL)
|
||||
hn_ifnet_detevent(sc, vf_ifp);
|
||||
|
||||
if (sc->hn_xact != NULL && vmbus_chan_is_revoked(sc->hn_prichan)) {
|
||||
/*
|
||||
@ -2326,7 +2462,7 @@ hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen,
|
||||
int hash_type;
|
||||
|
||||
/* If the VF is active, inject the packet through the VF */
|
||||
ifp = rxr->hn_vf ? rxr->hn_vf : rxr->hn_ifp;
|
||||
ifp = rxr->hn_rxvf_ifp ? rxr->hn_rxvf_ifp : rxr->hn_ifp;
|
||||
|
||||
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
|
||||
/*
|
||||
@ -3301,18 +3437,122 @@ static int
|
||||
hn_vf_sysctl(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct hn_softc *sc = arg1;
|
||||
char vf_name[128];
|
||||
char vf_name[IFNAMSIZ + 1];
|
||||
struct ifnet *vf;
|
||||
|
||||
HN_LOCK(sc);
|
||||
vf_name[0] = '\0';
|
||||
vf = sc->hn_rx_ring[0].hn_vf;
|
||||
vf = sc->hn_vf_ifp;
|
||||
if (vf != NULL)
|
||||
snprintf(vf_name, sizeof(vf_name), "%s", if_name(vf));
|
||||
HN_UNLOCK(sc);
|
||||
return sysctl_handle_string(oidp, vf_name, sizeof(vf_name), req);
|
||||
}
|
||||
|
||||
static int
|
||||
hn_rxvf_sysctl(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct hn_softc *sc = arg1;
|
||||
char vf_name[IFNAMSIZ + 1];
|
||||
struct ifnet *vf;
|
||||
|
||||
HN_LOCK(sc);
|
||||
vf_name[0] = '\0';
|
||||
vf = sc->hn_rx_ring[0].hn_rxvf_ifp;
|
||||
if (vf != NULL)
|
||||
snprintf(vf_name, sizeof(vf_name), "%s", if_name(vf));
|
||||
HN_UNLOCK(sc);
|
||||
return sysctl_handle_string(oidp, vf_name, sizeof(vf_name), req);
|
||||
}
|
||||
|
||||
static int
|
||||
hn_vflist_sysctl(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct rm_priotracker pt;
|
||||
struct sbuf *sb;
|
||||
int error, i;
|
||||
bool first;
|
||||
|
||||
error = sysctl_wire_old_buffer(req, 0);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
sb = sbuf_new_for_sysctl(NULL, NULL, 128, req);
|
||||
if (sb == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
rm_rlock(&hn_vfmap_lock, &pt);
|
||||
|
||||
first = true;
|
||||
for (i = 0; i < hn_vfmap_size; ++i) {
|
||||
struct ifnet *ifp;
|
||||
|
||||
if (hn_vfmap[i] == NULL)
|
||||
continue;
|
||||
|
||||
ifp = ifnet_byindex(i);
|
||||
if (ifp != NULL) {
|
||||
if (first)
|
||||
sbuf_printf(sb, "%s", ifp->if_xname);
|
||||
else
|
||||
sbuf_printf(sb, " %s", ifp->if_xname);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
rm_runlock(&hn_vfmap_lock, &pt);
|
||||
|
||||
error = sbuf_finish(sb);
|
||||
sbuf_delete(sb);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
hn_vfmap_sysctl(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct rm_priotracker pt;
|
||||
struct sbuf *sb;
|
||||
int error, i;
|
||||
bool first;
|
||||
|
||||
error = sysctl_wire_old_buffer(req, 0);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
sb = sbuf_new_for_sysctl(NULL, NULL, 128, req);
|
||||
if (sb == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
rm_rlock(&hn_vfmap_lock, &pt);
|
||||
|
||||
first = true;
|
||||
for (i = 0; i < hn_vfmap_size; ++i) {
|
||||
struct ifnet *ifp, *hn_ifp;
|
||||
|
||||
hn_ifp = hn_vfmap[i];
|
||||
if (hn_ifp == NULL)
|
||||
continue;
|
||||
|
||||
ifp = ifnet_byindex(i);
|
||||
if (ifp != NULL) {
|
||||
if (first) {
|
||||
sbuf_printf(sb, "%s:%s", ifp->if_xname,
|
||||
hn_ifp->if_xname);
|
||||
} else {
|
||||
sbuf_printf(sb, " %s:%s", ifp->if_xname,
|
||||
hn_ifp->if_xname);
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
rm_runlock(&hn_vfmap_lock, &pt);
|
||||
|
||||
error = sbuf_finish(sb);
|
||||
sbuf_delete(sb);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
hn_check_iplen(const struct mbuf *m, int hoff)
|
||||
{
|
||||
@ -5829,10 +6069,18 @@ hn_chan_callback(struct vmbus_channel *chan, void *xrxr)
|
||||
}
|
||||
|
||||
static void
|
||||
hn_tx_taskq_create(void *arg __unused)
|
||||
hn_sysinit(void *arg __unused)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Initialize VF map.
|
||||
*/
|
||||
rm_init_flags(&hn_vfmap_lock, "hn_vfmap", RM_SLEEPABLE);
|
||||
hn_vfmap_size = HN_VFMAP_SIZE_DEF;
|
||||
hn_vfmap = malloc(sizeof(struct ifnet *) * hn_vfmap_size, M_DEVBUF,
|
||||
M_WAITOK | M_ZERO);
|
||||
|
||||
/*
|
||||
* Fix the # of TX taskqueues.
|
||||
*/
|
||||
@ -5869,11 +6117,10 @@ hn_tx_taskq_create(void *arg __unused)
|
||||
"hn tx%d", i);
|
||||
}
|
||||
}
|
||||
SYSINIT(hn_txtq_create, SI_SUB_DRIVERS, SI_ORDER_SECOND,
|
||||
hn_tx_taskq_create, NULL);
|
||||
SYSINIT(hn_sysinit, SI_SUB_DRIVERS, SI_ORDER_SECOND, hn_sysinit, NULL);
|
||||
|
||||
static void
|
||||
hn_tx_taskq_destroy(void *arg __unused)
|
||||
hn_sysuninit(void *arg __unused)
|
||||
{
|
||||
|
||||
if (hn_tx_taskque != NULL) {
|
||||
@ -5883,6 +6130,9 @@ hn_tx_taskq_destroy(void *arg __unused)
|
||||
taskqueue_free(hn_tx_taskque[i]);
|
||||
free(hn_tx_taskque, M_DEVBUF);
|
||||
}
|
||||
|
||||
if (hn_vfmap != NULL)
|
||||
free(hn_vfmap, M_DEVBUF);
|
||||
rm_destroy(&hn_vfmap_lock);
|
||||
}
|
||||
SYSUNINIT(hn_txtq_destroy, SI_SUB_DRIVERS, SI_ORDER_SECOND,
|
||||
hn_tx_taskq_destroy, NULL);
|
||||
SYSUNINIT(hn_sysuninit, SI_SUB_DRIVERS, SI_ORDER_SECOND, hn_sysuninit, NULL);
|
||||
|
@ -59,7 +59,7 @@ struct hn_tx_ring;
|
||||
|
||||
struct hn_rx_ring {
|
||||
struct ifnet *hn_ifp;
|
||||
struct ifnet *hn_vf; /* SR-IOV VF */
|
||||
struct ifnet *hn_rxvf_ifp; /* SR-IOV VF for RX */
|
||||
struct hn_tx_ring *hn_txr;
|
||||
void *hn_pktbuf;
|
||||
int hn_pktbuf_len;
|
||||
@ -174,6 +174,7 @@ struct hn_tx_ring {
|
||||
*/
|
||||
struct hn_softc {
|
||||
struct ifnet *hn_ifp;
|
||||
struct ifnet *hn_vf_ifp; /* SR-IOV VF */
|
||||
struct ifmedia hn_media;
|
||||
device_t hn_dev;
|
||||
int hn_if_flags;
|
||||
@ -238,6 +239,8 @@ struct hn_softc {
|
||||
|
||||
eventhandler_tag hn_ifaddr_evthand;
|
||||
eventhandler_tag hn_ifnet_evthand;
|
||||
eventhandler_tag hn_ifnet_atthand;
|
||||
eventhandler_tag hn_ifnet_dethand;
|
||||
};
|
||||
|
||||
#define HN_FLAG_RXBUF_CONNECTED 0x0001
|
||||
|
Loading…
Reference in New Issue
Block a user