Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/hyperv/netvsc/hv_rndis_filter.c
Show First 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | |||||
#define HN_RNDIS_RID_COMPAT_MASK 0xffff | #define HN_RNDIS_RID_COMPAT_MASK 0xffff | ||||
#define HN_RNDIS_RID_COMPAT_MAX HN_RNDIS_RID_COMPAT_MASK | #define HN_RNDIS_RID_COMPAT_MAX HN_RNDIS_RID_COMPAT_MASK | ||||
#define HN_RNDIS_XFER_SIZE 2048 | #define HN_RNDIS_XFER_SIZE 2048 | ||||
/* | /* | ||||
* Forward declarations | * Forward declarations | ||||
*/ | */ | ||||
static void hv_rf_receive_response(rndis_device *device, | static void hv_rf_receive_indicate_status(struct hn_softc *sc, | ||||
const rndis_msg *response); | const rndis_msg *response); | ||||
static void hv_rf_receive_indicate_status(rndis_device *device, | |||||
const rndis_msg *response); | |||||
static void hv_rf_receive_data(struct hn_rx_ring *rxr, | static void hv_rf_receive_data(struct hn_rx_ring *rxr, | ||||
const void *data, int dlen); | const void *data, int dlen); | ||||
static inline int hv_rf_query_device_mac(rndis_device *device); | static int hv_rf_query_device_mac(struct hn_softc *sc, uint8_t *eaddr); | ||||
static inline int hv_rf_query_device_link_status(rndis_device *device); | static int hv_rf_query_device_link_status(struct hn_softc *sc, | ||||
static int hv_rf_init_device(rndis_device *device); | uint32_t *link_status); | ||||
static int hv_rf_init_device(struct hn_softc *sc); | |||||
static int hn_rndis_query(struct hn_softc *sc, uint32_t oid, | static int hn_rndis_query(struct hn_softc *sc, uint32_t oid, | ||||
const void *idata, size_t idlen, void *odata, size_t *odlen0); | const void *idata, size_t idlen, void *odata, size_t *odlen0); | ||||
static int hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data, | static int hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data, | ||||
size_t dlen); | size_t dlen); | ||||
static int hn_rndis_conf_offload(struct hn_softc *sc); | static int hn_rndis_conf_offload(struct hn_softc *sc); | ||||
static int hn_rndis_get_rsscaps(struct hn_softc *sc, int *rxr_cnt); | static int hn_rndis_get_rsscaps(struct hn_softc *sc, int *rxr_cnt); | ||||
static int hn_rndis_conf_rss(struct hn_softc *sc, int nchan); | static int hn_rndis_conf_rss(struct hn_softc *sc, int nchan); | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | while (len > 0) { | ||||
len -= ppi->size; | len -= ppi->size; | ||||
ppi = (rndis_per_packet_info *)((unsigned long)ppi + ppi->size); | ppi = (rndis_per_packet_info *)((unsigned long)ppi + ppi->size); | ||||
} | } | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
/* | /* | ||||
* Allow module_param to work and override to switch to promiscuous mode. | |||||
*/ | |||||
static inline rndis_device * | |||||
hv_get_rndis_device(void) | |||||
{ | |||||
rndis_device *device; | |||||
device = malloc(sizeof(rndis_device), M_NETVSC, M_WAITOK | M_ZERO); | |||||
mtx_init(&device->req_lock, "HV-FRL", NULL, MTX_DEF); | |||||
/* Same effect as STAILQ_HEAD_INITIALIZER() static initializer */ | |||||
STAILQ_INIT(&device->myrequest_list); | |||||
device->state = RNDIS_DEV_UNINITIALIZED; | |||||
return (device); | |||||
} | |||||
/* | |||||
* | |||||
*/ | |||||
static inline void | |||||
hv_put_rndis_device(rndis_device *device) | |||||
{ | |||||
mtx_destroy(&device->req_lock); | |||||
free(device, M_NETVSC); | |||||
} | |||||
/* | |||||
* RNDIS filter receive response | |||||
*/ | |||||
static void | |||||
hv_rf_receive_response(rndis_device *device, const rndis_msg *response) | |||||
{ | |||||
rndis_request *request = NULL; | |||||
rndis_request *next_request; | |||||
boolean_t found = FALSE; | |||||
mtx_lock(&device->req_lock); | |||||
request = STAILQ_FIRST(&device->myrequest_list); | |||||
while (request != NULL) { | |||||
/* | |||||
* All request/response message contains request_id as the | |||||
* first field | |||||
*/ | |||||
if (request->request_msg.msg.init_request.request_id == | |||||
response->msg.init_complete.request_id) { | |||||
found = TRUE; | |||||
break; | |||||
} | |||||
next_request = STAILQ_NEXT(request, mylist_entry); | |||||
request = next_request; | |||||
} | |||||
mtx_unlock(&device->req_lock); | |||||
if (found) { | |||||
if (response->msg_len <= sizeof(rndis_msg)) { | |||||
memcpy(&request->response_msg, response, | |||||
response->msg_len); | |||||
} else { | |||||
request->response_msg.msg.init_complete.status = | |||||
RNDIS_STATUS_BUFFER_OVERFLOW; | |||||
} | |||||
sema_post(&request->wait_sema); | |||||
} | |||||
} | |||||
/* | |||||
* RNDIS filter receive indicate status | * RNDIS filter receive indicate status | ||||
*/ | */ | ||||
static void | static void | ||||
hv_rf_receive_indicate_status(rndis_device *device, const rndis_msg *response) | hv_rf_receive_indicate_status(struct hn_softc *sc, const rndis_msg *response) | ||||
{ | { | ||||
const rndis_indicate_status *indicate = &response->msg.indicate_status; | const rndis_indicate_status *indicate = &response->msg.indicate_status; | ||||
switch(indicate->status) { | switch(indicate->status) { | ||||
case RNDIS_STATUS_MEDIA_CONNECT: | case RNDIS_STATUS_MEDIA_CONNECT: | ||||
netvsc_linkstatus_callback(device->sc, 1); | netvsc_linkstatus_callback(sc, 1); | ||||
break; | break; | ||||
case RNDIS_STATUS_MEDIA_DISCONNECT: | case RNDIS_STATUS_MEDIA_DISCONNECT: | ||||
netvsc_linkstatus_callback(device->sc, 0); | netvsc_linkstatus_callback(sc, 0); | ||||
break; | break; | ||||
default: | default: | ||||
/* TODO: */ | /* TODO: */ | ||||
device_printf(device->sc->hn_dev, | if_printf(sc->hn_ifp, | ||||
"unknown status %d received\n", indicate->status); | "unknown status %d received\n", indicate->status); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
static int | static int | ||||
hv_rf_find_recvinfo(const rndis_packet *rpkt, struct hn_recvinfo *info) | hv_rf_find_recvinfo(const rndis_packet *rpkt, struct hn_recvinfo *info) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 113 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* RNDIS filter on receive | * RNDIS filter on receive | ||||
*/ | */ | ||||
int | int | ||||
hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr, | hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr, | ||||
const void *data, int dlen) | const void *data, int dlen) | ||||
{ | { | ||||
rndis_device *rndis_dev; | |||||
const rndis_msg *rndis_hdr; | const rndis_msg *rndis_hdr; | ||||
const struct rndis_comp_hdr *comp; | const struct rndis_comp_hdr *comp; | ||||
rndis_dev = sc->rndis_dev; | |||||
if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED) | |||||
return (EINVAL); | |||||
rndis_hdr = data; | rndis_hdr = data; | ||||
switch (rndis_hdr->ndis_msg_type) { | switch (rndis_hdr->ndis_msg_type) { | ||||
/* data message */ | /* data message */ | ||||
case REMOTE_NDIS_PACKET_MSG: | case REMOTE_NDIS_PACKET_MSG: | ||||
hv_rf_receive_data(rxr, data, dlen); | hv_rf_receive_data(rxr, data, dlen); | ||||
break; | break; | ||||
/* completion messages */ | /* completion messages */ | ||||
case REMOTE_NDIS_INITIALIZE_CMPLT: | case REMOTE_NDIS_INITIALIZE_CMPLT: | ||||
case REMOTE_NDIS_QUERY_CMPLT: | case REMOTE_NDIS_QUERY_CMPLT: | ||||
case REMOTE_NDIS_SET_CMPLT: | case REMOTE_NDIS_SET_CMPLT: | ||||
case REMOTE_NDIS_KEEPALIVE_CMPLT: | case REMOTE_NDIS_KEEPALIVE_CMPLT: | ||||
comp = data; | comp = data; | ||||
if (comp->rm_rid <= HN_RNDIS_RID_COMPAT_MAX) { | KASSERT(comp->rm_rid > HN_RNDIS_RID_COMPAT_MAX, | ||||
/* Transition time compat code */ | ("invalid rid 0x%08x\n", comp->rm_rid)); | ||||
hv_rf_receive_response(rndis_dev, rndis_hdr); | vmbus_xact_ctx_wakeup(sc->hn_xact, comp, dlen); | ||||
} else { | |||||
vmbus_xact_ctx_wakeup(sc->hn_xact, data, dlen); | |||||
} | |||||
break; | break; | ||||
/* notification message */ | /* notification message */ | ||||
case REMOTE_NDIS_INDICATE_STATUS_MSG: | case REMOTE_NDIS_INDICATE_STATUS_MSG: | ||||
hv_rf_receive_indicate_status(rndis_dev, rndis_hdr); | hv_rf_receive_indicate_status(sc, rndis_hdr); | ||||
break; | break; | ||||
case REMOTE_NDIS_RESET_CMPLT: | case REMOTE_NDIS_RESET_CMPLT: | ||||
/* | /* | ||||
* Reset completed, no rid. | * Reset completed, no rid. | ||||
* | * | ||||
* NOTE: | * NOTE: | ||||
* RESET is not issued by hn(4), so this message should | * RESET is not issued by hn(4), so this message should | ||||
Show All 9 Lines | hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr, | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* RNDIS filter query device MAC address | * RNDIS filter query device MAC address | ||||
*/ | */ | ||||
static int | static int | ||||
hv_rf_query_device_mac(rndis_device *device) | hv_rf_query_device_mac(struct hn_softc *sc, uint8_t *eaddr) | ||||
{ | { | ||||
struct hn_softc *sc = device->sc; | size_t eaddr_len; | ||||
size_t hwaddr_len; | |||||
int error; | int error; | ||||
hwaddr_len = ETHER_ADDR_LEN; | eaddr_len = ETHER_ADDR_LEN; | ||||
error = hn_rndis_query(sc, OID_802_3_PERMANENT_ADDRESS, NULL, 0, | error = hn_rndis_query(sc, OID_802_3_PERMANENT_ADDRESS, NULL, 0, | ||||
device->hw_mac_addr, &hwaddr_len); | eaddr, &eaddr_len); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
if (hwaddr_len != ETHER_ADDR_LEN) { | if (eaddr_len != ETHER_ADDR_LEN) { | ||||
if_printf(sc->hn_ifp, "invalid hwaddr len %zu\n", hwaddr_len); | if_printf(sc->hn_ifp, "invalid eaddr len %zu\n", eaddr_len); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* RNDIS filter query device link status | * RNDIS filter query device link status | ||||
*/ | */ | ||||
static inline int | static int | ||||
hv_rf_query_device_link_status(rndis_device *device) | hv_rf_query_device_link_status(struct hn_softc *sc, uint32_t *link_status) | ||||
{ | { | ||||
struct hn_softc *sc = device->sc; | |||||
size_t size; | size_t size; | ||||
int error; | int error; | ||||
size = sizeof(uint32_t); | size = sizeof(*link_status); | ||||
error = hn_rndis_query(sc, OID_GEN_MEDIA_CONNECT_STATUS, NULL, 0, | error = hn_rndis_query(sc, OID_GEN_MEDIA_CONNECT_STATUS, NULL, 0, | ||||
&device->link_status, &size); | link_status, &size); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
if (size != sizeof(uint32_t)) { | if (size != sizeof(uint32_t)) { | ||||
if_printf(sc->hn_ifp, "invalid link status len %zu\n", size); | if_printf(sc->hn_ifp, "invalid link status len %zu\n", size); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 397 Lines • ▼ Show 20 Lines | hn_rndis_set_rxfilter(struct hn_softc *sc, uint32_t filter) | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* RNDIS filter init device | * RNDIS filter init device | ||||
*/ | */ | ||||
static int | static int | ||||
hv_rf_init_device(rndis_device *device) | hv_rf_init_device(struct hn_softc *sc) | ||||
{ | { | ||||
struct hn_softc *sc = device->sc; | |||||
struct rndis_init_req *req; | struct rndis_init_req *req; | ||||
const struct rndis_init_comp *comp; | const struct rndis_init_comp *comp; | ||||
struct vmbus_xact *xact; | struct vmbus_xact *xact; | ||||
size_t comp_len; | size_t comp_len; | ||||
uint32_t rid; | uint32_t rid; | ||||
int error; | int error; | ||||
/* XXX */ | |||||
device->state = RNDIS_DEV_INITIALIZED; | |||||
xact = vmbus_xact_get(sc->hn_xact, sizeof(*req)); | xact = vmbus_xact_get(sc->hn_xact, sizeof(*req)); | ||||
if (xact == NULL) { | if (xact == NULL) { | ||||
if_printf(sc->hn_ifp, "no xact for RNDIS init\n"); | if_printf(sc->hn_ifp, "no xact for RNDIS init\n"); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
rid = hn_rndis_rid(sc); | rid = hn_rndis_rid(sc); | ||||
req = vmbus_xact_req_data(xact); | req = vmbus_xact_req_data(xact); | ||||
req->rm_type = REMOTE_NDIS_INITIALIZE_MSG; | req->rm_type = REMOTE_NDIS_INITIALIZE_MSG; | ||||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* RNDIS filter on device add | * RNDIS filter on device add | ||||
*/ | */ | ||||
int | int | ||||
hv_rf_on_device_add(struct hn_softc *sc, void *additl_info, | hv_rf_on_device_add(struct hn_softc *sc, void *additl_info, | ||||
int *nchan0, struct hn_rx_ring *rxr) | int *nchan0, struct hn_rx_ring *rxr) | ||||
{ | { | ||||
int ret; | int ret; | ||||
rndis_device *rndis_dev; | |||||
netvsc_device_info *dev_info = (netvsc_device_info *)additl_info; | netvsc_device_info *dev_info = (netvsc_device_info *)additl_info; | ||||
device_t dev = sc->hn_dev; | device_t dev = sc->hn_dev; | ||||
struct hn_nvs_subch_req *req; | struct hn_nvs_subch_req *req; | ||||
const struct hn_nvs_subch_resp *resp; | const struct hn_nvs_subch_resp *resp; | ||||
size_t resp_len; | size_t resp_len; | ||||
struct vmbus_xact *xact = NULL; | struct vmbus_xact *xact = NULL; | ||||
uint32_t status, nsubch; | uint32_t status, nsubch; | ||||
int nchan = *nchan0; | int nchan = *nchan0; | ||||
int rxr_cnt; | int rxr_cnt; | ||||
rndis_dev = hv_get_rndis_device(); | |||||
if (rndis_dev == NULL) { | |||||
return (ENOMEM); | |||||
} | |||||
sc->rndis_dev = rndis_dev; | |||||
rndis_dev->sc = sc; | |||||
/* | /* | ||||
* Let the inner driver handle this first to create the netvsc channel | * Let the inner driver handle this first to create the netvsc channel | ||||
* NOTE! Once the channel is created, we may get a receive callback | * NOTE! Once the channel is created, we may get a receive callback | ||||
* (hv_rf_on_receive()) before this call is completed. | * (hv_rf_on_receive()) before this call is completed. | ||||
* Note: Earlier code used a function pointer here. | * Note: Earlier code used a function pointer here. | ||||
*/ | */ | ||||
ret = hv_nv_on_device_add(sc, rxr); | ret = hv_nv_on_device_add(sc, rxr); | ||||
if (ret != 0) { | if (ret != 0) | ||||
hv_put_rndis_device(rndis_dev); | |||||
return (ret); | return (ret); | ||||
} | |||||
/* | /* | ||||
* Initialize the rndis device | * Initialize the rndis device | ||||
*/ | */ | ||||
/* Send the rndis initialization message */ | /* Send the rndis initialization message */ | ||||
ret = hv_rf_init_device(rndis_dev); | ret = hv_rf_init_device(sc); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
/* | /* | ||||
* TODO: If rndis init failed, we will need to shut down | * TODO: If rndis init failed, we will need to shut down | ||||
* the channel | * the channel | ||||
*/ | */ | ||||
} | } | ||||
/* Get the mac address */ | /* Get the mac address */ | ||||
ret = hv_rf_query_device_mac(rndis_dev); | ret = hv_rf_query_device_mac(sc, dev_info->mac_addr); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
/* TODO: shut down rndis device and the channel */ | /* TODO: shut down rndis device and the channel */ | ||||
} | } | ||||
/* Configure NDIS offload settings */ | /* Configure NDIS offload settings */ | ||||
hn_rndis_conf_offload(sc); | hn_rndis_conf_offload(sc); | ||||
memcpy(dev_info->mac_addr, rndis_dev->hw_mac_addr, ETHER_ADDR_LEN); | hv_rf_query_device_link_status(sc, &dev_info->link_state); | ||||
hv_rf_query_device_link_status(rndis_dev); | |||||
dev_info->link_state = rndis_dev->link_status; | |||||
if (sc->hn_ndis_ver < NDIS_VERSION_6_30 || nchan == 1) { | if (sc->hn_ndis_ver < NDIS_VERSION_6_30 || nchan == 1) { | ||||
/* | /* | ||||
* Either RSS is not supported, or multiple RX/TX rings | * Either RSS is not supported, or multiple RX/TX rings | ||||
* are not requested. | * are not requested. | ||||
*/ | */ | ||||
*nchan0 = 1; | *nchan0 = 1; | ||||
return (0); | return (0); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | if (xact != NULL) | ||||
vmbus_xact_put(xact); | vmbus_xact_put(xact); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
/* | /* | ||||
* RNDIS filter on device remove | * RNDIS filter on device remove | ||||
*/ | */ | ||||
int | int | ||||
hv_rf_on_device_remove(struct hn_softc *sc, boolean_t destroy_channel) | hv_rf_on_device_remove(struct hn_softc *sc) | ||||
{ | { | ||||
rndis_device *rndis_dev = sc->rndis_dev; | |||||
int ret; | int ret; | ||||
/* Halt and release the rndis device */ | /* Halt and release the rndis device */ | ||||
ret = hv_rf_halt_device(sc); | ret = hv_rf_halt_device(sc); | ||||
sc->rndis_dev = NULL; | |||||
hv_put_rndis_device(rndis_dev); | |||||
/* Pass control to inner driver to remove the device */ | /* Pass control to inner driver to remove the device */ | ||||
ret |= hv_nv_on_device_remove(sc, destroy_channel); | ret |= hv_nv_on_device_remove(sc); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
/* | /* | ||||
* RNDIS filter on open | * RNDIS filter on open | ||||
*/ | */ | ||||
int | int | ||||
Show All 31 Lines |