diff options
| -rw-r--r-- | drivers/net/hyperv/hyperv_net.h | 1 | ||||
| -rw-r--r-- | drivers/net/hyperv/netvsc_drv.c | 30 | ||||
| -rw-r--r-- | drivers/net/hyperv/rndis_filter.c | 79 |
3 files changed, 109 insertions, 1 deletions
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 2857ab078aac..95ceb3593043 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h | |||
| @@ -131,6 +131,7 @@ int rndis_filter_send(struct hv_device *dev, | |||
| 131 | struct hv_netvsc_packet *pkt); | 131 | struct hv_netvsc_packet *pkt); |
| 132 | 132 | ||
| 133 | int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter); | 133 | int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter); |
| 134 | int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac); | ||
| 134 | 135 | ||
| 135 | 136 | ||
| 136 | #define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF) | 137 | #define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF) |
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 8f8ed3320425..8e23c084c4a7 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c | |||
| @@ -341,6 +341,34 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) | |||
| 341 | return 0; | 341 | return 0; |
| 342 | } | 342 | } |
| 343 | 343 | ||
| 344 | |||
| 345 | static int netvsc_set_mac_addr(struct net_device *ndev, void *p) | ||
| 346 | { | ||
| 347 | struct net_device_context *ndevctx = netdev_priv(ndev); | ||
| 348 | struct hv_device *hdev = ndevctx->device_ctx; | ||
| 349 | struct sockaddr *addr = p; | ||
| 350 | char save_adr[14]; | ||
| 351 | unsigned char save_aatype; | ||
| 352 | int err; | ||
| 353 | |||
| 354 | memcpy(save_adr, ndev->dev_addr, ETH_ALEN); | ||
| 355 | save_aatype = ndev->addr_assign_type; | ||
| 356 | |||
| 357 | err = eth_mac_addr(ndev, p); | ||
| 358 | if (err != 0) | ||
| 359 | return err; | ||
| 360 | |||
| 361 | err = rndis_filter_set_device_mac(hdev, addr->sa_data); | ||
| 362 | if (err != 0) { | ||
| 363 | /* roll back to saved MAC */ | ||
| 364 | memcpy(ndev->dev_addr, save_adr, ETH_ALEN); | ||
| 365 | ndev->addr_assign_type = save_aatype; | ||
| 366 | } | ||
| 367 | |||
| 368 | return err; | ||
| 369 | } | ||
| 370 | |||
| 371 | |||
| 344 | static const struct ethtool_ops ethtool_ops = { | 372 | static const struct ethtool_ops ethtool_ops = { |
| 345 | .get_drvinfo = netvsc_get_drvinfo, | 373 | .get_drvinfo = netvsc_get_drvinfo, |
| 346 | .get_link = ethtool_op_get_link, | 374 | .get_link = ethtool_op_get_link, |
| @@ -353,7 +381,7 @@ static const struct net_device_ops device_ops = { | |||
| 353 | .ndo_set_rx_mode = netvsc_set_multicast_list, | 381 | .ndo_set_rx_mode = netvsc_set_multicast_list, |
| 354 | .ndo_change_mtu = netvsc_change_mtu, | 382 | .ndo_change_mtu = netvsc_change_mtu, |
| 355 | .ndo_validate_addr = eth_validate_addr, | 383 | .ndo_validate_addr = eth_validate_addr, |
| 356 | .ndo_set_mac_address = eth_mac_addr, | 384 | .ndo_set_mac_address = netvsc_set_mac_addr, |
| 357 | }; | 385 | }; |
| 358 | 386 | ||
| 359 | /* | 387 | /* |
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 981ebb115637..fbf539468205 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c | |||
| @@ -27,6 +27,7 @@ | |||
| 27 | #include <linux/if_ether.h> | 27 | #include <linux/if_ether.h> |
| 28 | #include <linux/netdevice.h> | 28 | #include <linux/netdevice.h> |
| 29 | #include <linux/if_vlan.h> | 29 | #include <linux/if_vlan.h> |
| 30 | #include <linux/nls.h> | ||
| 30 | 31 | ||
| 31 | #include "hyperv_net.h" | 32 | #include "hyperv_net.h" |
| 32 | 33 | ||
| @@ -47,6 +48,7 @@ struct rndis_request { | |||
| 47 | struct hv_page_buffer buf; | 48 | struct hv_page_buffer buf; |
| 48 | /* FIXME: We assumed a fixed size request here. */ | 49 | /* FIXME: We assumed a fixed size request here. */ |
| 49 | struct rndis_message request_msg; | 50 | struct rndis_message request_msg; |
| 51 | u8 ext[100]; | ||
| 50 | }; | 52 | }; |
| 51 | 53 | ||
| 52 | static void rndis_filter_send_completion(void *ctx); | 54 | static void rndis_filter_send_completion(void *ctx); |
| @@ -511,6 +513,83 @@ static int rndis_filter_query_device_mac(struct rndis_device *dev) | |||
| 511 | dev->hw_mac_adr, &size); | 513 | dev->hw_mac_adr, &size); |
| 512 | } | 514 | } |
| 513 | 515 | ||
| 516 | #define NWADR_STR "NetworkAddress" | ||
| 517 | #define NWADR_STRLEN 14 | ||
| 518 | |||
| 519 | int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac) | ||
| 520 | { | ||
| 521 | struct netvsc_device *nvdev = hv_get_drvdata(hdev); | ||
| 522 | struct rndis_device *rdev = nvdev->extension; | ||
| 523 | struct net_device *ndev = nvdev->ndev; | ||
| 524 | struct rndis_request *request; | ||
| 525 | struct rndis_set_request *set; | ||
| 526 | struct rndis_config_parameter_info *cpi; | ||
| 527 | wchar_t *cfg_nwadr, *cfg_mac; | ||
| 528 | struct rndis_set_complete *set_complete; | ||
| 529 | char macstr[2*ETH_ALEN+1]; | ||
| 530 | u32 extlen = sizeof(struct rndis_config_parameter_info) + | ||
| 531 | 2*NWADR_STRLEN + 4*ETH_ALEN; | ||
| 532 | int ret, t; | ||
| 533 | |||
| 534 | request = get_rndis_request(rdev, RNDIS_MSG_SET, | ||
| 535 | RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen); | ||
| 536 | if (!request) | ||
| 537 | return -ENOMEM; | ||
| 538 | |||
| 539 | set = &request->request_msg.msg.set_req; | ||
| 540 | set->oid = RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER; | ||
| 541 | set->info_buflen = extlen; | ||
| 542 | set->info_buf_offset = sizeof(struct rndis_set_request); | ||
| 543 | set->dev_vc_handle = 0; | ||
| 544 | |||
| 545 | cpi = (struct rndis_config_parameter_info *)((ulong)set + | ||
| 546 | set->info_buf_offset); | ||
| 547 | cpi->parameter_name_offset = | ||
| 548 | sizeof(struct rndis_config_parameter_info); | ||
| 549 | /* Multiply by 2 because host needs 2 bytes (utf16) for each char */ | ||
| 550 | cpi->parameter_name_length = 2*NWADR_STRLEN; | ||
| 551 | cpi->parameter_type = RNDIS_CONFIG_PARAM_TYPE_STRING; | ||
| 552 | cpi->parameter_value_offset = | ||
| 553 | cpi->parameter_name_offset + cpi->parameter_name_length; | ||
| 554 | /* Multiply by 4 because each MAC byte displayed as 2 utf16 chars */ | ||
| 555 | cpi->parameter_value_length = 4*ETH_ALEN; | ||
| 556 | |||
| 557 | cfg_nwadr = (wchar_t *)((ulong)cpi + cpi->parameter_name_offset); | ||
| 558 | cfg_mac = (wchar_t *)((ulong)cpi + cpi->parameter_value_offset); | ||
| 559 | ret = utf8s_to_utf16s(NWADR_STR, NWADR_STRLEN, UTF16_HOST_ENDIAN, | ||
| 560 | cfg_nwadr, NWADR_STRLEN); | ||
| 561 | if (ret < 0) | ||
| 562 | goto cleanup; | ||
| 563 | snprintf(macstr, 2*ETH_ALEN+1, "%pm", mac); | ||
| 564 | ret = utf8s_to_utf16s(macstr, 2*ETH_ALEN, UTF16_HOST_ENDIAN, | ||
| 565 | cfg_mac, 2*ETH_ALEN); | ||
| 566 | if (ret < 0) | ||
| 567 | goto cleanup; | ||
| 568 | |||
| 569 | ret = rndis_filter_send_request(rdev, request); | ||
| 570 | if (ret != 0) | ||
| 571 | goto cleanup; | ||
| 572 | |||
| 573 | t = wait_for_completion_timeout(&request->wait_event, 5*HZ); | ||
| 574 | if (t == 0) { | ||
| 575 | netdev_err(ndev, "timeout before we got a set response...\n"); | ||
| 576 | /* | ||
| 577 | * can't put_rndis_request, since we may still receive a | ||
| 578 | * send-completion. | ||
| 579 | */ | ||
| 580 | return -EBUSY; | ||
| 581 | } else { | ||
| 582 | set_complete = &request->response_msg.msg.set_complete; | ||
| 583 | if (set_complete->status != RNDIS_STATUS_SUCCESS) | ||
| 584 | ret = -EINVAL; | ||
| 585 | } | ||
| 586 | |||
| 587 | cleanup: | ||
| 588 | put_rndis_request(rdev, request); | ||
| 589 | return ret; | ||
| 590 | } | ||
| 591 | |||
| 592 | |||
| 514 | static int rndis_filter_query_device_link_status(struct rndis_device *dev) | 593 | static int rndis_filter_query_device_link_status(struct rndis_device *dev) |
| 515 | { | 594 | { |
| 516 | u32 size = sizeof(u32); | 595 | u32 size = sizeof(u32); |
