18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * uio_hv_generic - generic UIO driver for VMBus 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2013-2016 Brocade Communications Systems, Inc. 68c2ecf20Sopenharmony_ci * Copyright (c) 2016, Microsoft Corporation. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Since the driver does not declare any device ids, you must allocate 98c2ecf20Sopenharmony_ci * id and bind the device to the driver yourself. For example: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Associate Network GUID with UIO device 128c2ecf20Sopenharmony_ci * # echo "f8615163-df3e-46c5-913f-f2d2f965ed0e" \ 138c2ecf20Sopenharmony_ci * > /sys/bus/vmbus/drivers/uio_hv_generic/new_id 148c2ecf20Sopenharmony_ci * Then rebind 158c2ecf20Sopenharmony_ci * # echo -n "ed963694-e847-4b2a-85af-bc9cfc11d6f3" \ 168c2ecf20Sopenharmony_ci * > /sys/bus/vmbus/drivers/hv_netvsc/unbind 178c2ecf20Sopenharmony_ci * # echo -n "ed963694-e847-4b2a-85af-bc9cfc11d6f3" \ 188c2ecf20Sopenharmony_ci * > /sys/bus/vmbus/drivers/uio_hv_generic/bind 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/device.h> 238c2ecf20Sopenharmony_ci#include <linux/kernel.h> 248c2ecf20Sopenharmony_ci#include <linux/module.h> 258c2ecf20Sopenharmony_ci#include <linux/uio_driver.h> 268c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 278c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 288c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 298c2ecf20Sopenharmony_ci#include <linux/hyperv.h> 308c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 318c2ecf20Sopenharmony_ci#include <linux/slab.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include "../hv/hyperv_vmbus.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define DRIVER_VERSION "0.02.1" 368c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Stephen Hemminger <sthemmin at microsoft.com>" 378c2ecf20Sopenharmony_ci#define DRIVER_DESC "Generic UIO driver for VMBus devices" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define HV_RING_SIZE 512 /* pages */ 408c2ecf20Sopenharmony_ci#define SEND_BUFFER_SIZE (16 * 1024 * 1024) 418c2ecf20Sopenharmony_ci#define RECV_BUFFER_SIZE (31 * 1024 * 1024) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* 448c2ecf20Sopenharmony_ci * List of resources to be mapped to user space 458c2ecf20Sopenharmony_ci * can be extended up to MAX_UIO_MAPS(5) items 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_cienum hv_uio_map { 488c2ecf20Sopenharmony_ci TXRX_RING_MAP = 0, 498c2ecf20Sopenharmony_ci INT_PAGE_MAP, 508c2ecf20Sopenharmony_ci MON_PAGE_MAP, 518c2ecf20Sopenharmony_ci RECV_BUF_MAP, 528c2ecf20Sopenharmony_ci SEND_BUF_MAP 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistruct hv_uio_private_data { 568c2ecf20Sopenharmony_ci struct uio_info info; 578c2ecf20Sopenharmony_ci struct hv_device *device; 588c2ecf20Sopenharmony_ci atomic_t refcnt; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci void *recv_buf; 618c2ecf20Sopenharmony_ci u32 recv_gpadl; 628c2ecf20Sopenharmony_ci char recv_name[32]; /* "recv_4294967295" */ 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci void *send_buf; 658c2ecf20Sopenharmony_ci u32 send_gpadl; 668c2ecf20Sopenharmony_ci char send_name[32]; 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* 708c2ecf20Sopenharmony_ci * This is the irqcontrol callback to be registered to uio_info. 718c2ecf20Sopenharmony_ci * It can be used to disable/enable interrupt from user space processes. 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * @param info 748c2ecf20Sopenharmony_ci * pointer to uio_info. 758c2ecf20Sopenharmony_ci * @param irq_state 768c2ecf20Sopenharmony_ci * state value. 1 to enable interrupt, 0 to disable interrupt. 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_cistatic int 798c2ecf20Sopenharmony_cihv_uio_irqcontrol(struct uio_info *info, s32 irq_state) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct hv_uio_private_data *pdata = info->priv; 828c2ecf20Sopenharmony_ci struct hv_device *dev = pdata->device; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci dev->channel->inbound.ring_buffer->interrupt_mask = !irq_state; 858c2ecf20Sopenharmony_ci virt_mb(); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return 0; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* 918c2ecf20Sopenharmony_ci * Callback from vmbus_event when something is in inbound ring. 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_cistatic void hv_uio_channel_cb(void *context) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct vmbus_channel *chan = context; 968c2ecf20Sopenharmony_ci struct hv_device *hv_dev = chan->device_obj; 978c2ecf20Sopenharmony_ci struct hv_uio_private_data *pdata = hv_get_drvdata(hv_dev); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci chan->inbound.ring_buffer->interrupt_mask = 1; 1008c2ecf20Sopenharmony_ci virt_mb(); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci uio_event_notify(&pdata->info); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* 1068c2ecf20Sopenharmony_ci * Callback from vmbus_event when channel is rescinded. 1078c2ecf20Sopenharmony_ci * It is meant for rescind of primary channels only. 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_cistatic void hv_uio_rescind(struct vmbus_channel *channel) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct hv_device *hv_dev = channel->device_obj; 1128c2ecf20Sopenharmony_ci struct hv_uio_private_data *pdata = hv_get_drvdata(hv_dev); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* 1158c2ecf20Sopenharmony_ci * Turn off the interrupt file handle 1168c2ecf20Sopenharmony_ci * Next read for event will return -EIO 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_ci pdata->info.irq = 0; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* Wake up reader */ 1218c2ecf20Sopenharmony_ci uio_event_notify(&pdata->info); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* Sysfs API to allow mmap of the ring buffers 1258c2ecf20Sopenharmony_ci * The ring buffer is allocated as contiguous memory by vmbus_open 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_cistatic int hv_uio_ring_mmap(struct file *filp, struct kobject *kobj, 1288c2ecf20Sopenharmony_ci struct bin_attribute *attr, 1298c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct vmbus_channel *channel 1328c2ecf20Sopenharmony_ci = container_of(kobj, struct vmbus_channel, kobj); 1338c2ecf20Sopenharmony_ci void *ring_buffer = page_address(channel->ringbuffer_page); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (channel->state != CHANNEL_OPENED_STATE) 1368c2ecf20Sopenharmony_ci return -ENODEV; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return vm_iomap_memory(vma, virt_to_phys(ring_buffer), 1398c2ecf20Sopenharmony_ci channel->ringbuffer_pagecount << PAGE_SHIFT); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic const struct bin_attribute ring_buffer_bin_attr = { 1438c2ecf20Sopenharmony_ci .attr = { 1448c2ecf20Sopenharmony_ci .name = "ring", 1458c2ecf20Sopenharmony_ci .mode = 0600, 1468c2ecf20Sopenharmony_ci }, 1478c2ecf20Sopenharmony_ci .size = 2 * HV_RING_SIZE * PAGE_SIZE, 1488c2ecf20Sopenharmony_ci .mmap = hv_uio_ring_mmap, 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* Callback from VMBUS subsystem when new channel created. */ 1528c2ecf20Sopenharmony_cistatic void 1538c2ecf20Sopenharmony_cihv_uio_new_channel(struct vmbus_channel *new_sc) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct hv_device *hv_dev = new_sc->primary_channel->device_obj; 1568c2ecf20Sopenharmony_ci struct device *device = &hv_dev->device; 1578c2ecf20Sopenharmony_ci const size_t ring_bytes = HV_RING_SIZE * PAGE_SIZE; 1588c2ecf20Sopenharmony_ci int ret; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* Create host communication ring */ 1618c2ecf20Sopenharmony_ci ret = vmbus_open(new_sc, ring_bytes, ring_bytes, NULL, 0, 1628c2ecf20Sopenharmony_ci hv_uio_channel_cb, new_sc); 1638c2ecf20Sopenharmony_ci if (ret) { 1648c2ecf20Sopenharmony_ci dev_err(device, "vmbus_open subchannel failed: %d\n", ret); 1658c2ecf20Sopenharmony_ci return; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* Disable interrupts on sub channel */ 1698c2ecf20Sopenharmony_ci new_sc->inbound.ring_buffer->interrupt_mask = 1; 1708c2ecf20Sopenharmony_ci set_channel_read_mode(new_sc, HV_CALL_ISR); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci ret = sysfs_create_bin_file(&new_sc->kobj, &ring_buffer_bin_attr); 1738c2ecf20Sopenharmony_ci if (ret) { 1748c2ecf20Sopenharmony_ci dev_err(device, "sysfs create ring bin file failed; %d\n", ret); 1758c2ecf20Sopenharmony_ci vmbus_close(new_sc); 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* free the reserved buffers for send and receive */ 1808c2ecf20Sopenharmony_cistatic void 1818c2ecf20Sopenharmony_cihv_uio_cleanup(struct hv_device *dev, struct hv_uio_private_data *pdata) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci if (pdata->send_gpadl) { 1848c2ecf20Sopenharmony_ci vmbus_teardown_gpadl(dev->channel, pdata->send_gpadl); 1858c2ecf20Sopenharmony_ci pdata->send_gpadl = 0; 1868c2ecf20Sopenharmony_ci vfree(pdata->send_buf); 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (pdata->recv_gpadl) { 1908c2ecf20Sopenharmony_ci vmbus_teardown_gpadl(dev->channel, pdata->recv_gpadl); 1918c2ecf20Sopenharmony_ci pdata->recv_gpadl = 0; 1928c2ecf20Sopenharmony_ci vfree(pdata->recv_buf); 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* VMBus primary channel is opened on first use */ 1978c2ecf20Sopenharmony_cistatic int 1988c2ecf20Sopenharmony_cihv_uio_open(struct uio_info *info, struct inode *inode) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct hv_uio_private_data *pdata 2018c2ecf20Sopenharmony_ci = container_of(info, struct hv_uio_private_data, info); 2028c2ecf20Sopenharmony_ci struct hv_device *dev = pdata->device; 2038c2ecf20Sopenharmony_ci int ret; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (atomic_inc_return(&pdata->refcnt) != 1) 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci vmbus_set_chn_rescind_callback(dev->channel, hv_uio_rescind); 2098c2ecf20Sopenharmony_ci vmbus_set_sc_create_callback(dev->channel, hv_uio_new_channel); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci ret = vmbus_connect_ring(dev->channel, 2128c2ecf20Sopenharmony_ci hv_uio_channel_cb, dev->channel); 2138c2ecf20Sopenharmony_ci if (ret == 0) 2148c2ecf20Sopenharmony_ci dev->channel->inbound.ring_buffer->interrupt_mask = 1; 2158c2ecf20Sopenharmony_ci else 2168c2ecf20Sopenharmony_ci atomic_dec(&pdata->refcnt); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return ret; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/* VMBus primary channel is closed on last close */ 2228c2ecf20Sopenharmony_cistatic int 2238c2ecf20Sopenharmony_cihv_uio_release(struct uio_info *info, struct inode *inode) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct hv_uio_private_data *pdata 2268c2ecf20Sopenharmony_ci = container_of(info, struct hv_uio_private_data, info); 2278c2ecf20Sopenharmony_ci struct hv_device *dev = pdata->device; 2288c2ecf20Sopenharmony_ci int ret = 0; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&pdata->refcnt)) 2318c2ecf20Sopenharmony_ci ret = vmbus_disconnect_ring(dev->channel); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return ret; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int 2378c2ecf20Sopenharmony_cihv_uio_probe(struct hv_device *dev, 2388c2ecf20Sopenharmony_ci const struct hv_vmbus_device_id *dev_id) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct vmbus_channel *channel = dev->channel; 2418c2ecf20Sopenharmony_ci struct hv_uio_private_data *pdata; 2428c2ecf20Sopenharmony_ci void *ring_buffer; 2438c2ecf20Sopenharmony_ci int ret; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* Communicating with host has to be via shared memory not hypercall */ 2468c2ecf20Sopenharmony_ci if (!channel->offermsg.monitor_allocated) { 2478c2ecf20Sopenharmony_ci dev_err(&dev->device, "vmbus channel requires hypercall\n"); 2488c2ecf20Sopenharmony_ci return -ENOTSUPP; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); 2528c2ecf20Sopenharmony_ci if (!pdata) 2538c2ecf20Sopenharmony_ci return -ENOMEM; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci ret = vmbus_alloc_ring(channel, HV_RING_SIZE * PAGE_SIZE, 2568c2ecf20Sopenharmony_ci HV_RING_SIZE * PAGE_SIZE); 2578c2ecf20Sopenharmony_ci if (ret) 2588c2ecf20Sopenharmony_ci goto fail; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci set_channel_read_mode(channel, HV_CALL_ISR); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* Fill general uio info */ 2638c2ecf20Sopenharmony_ci pdata->info.name = "uio_hv_generic"; 2648c2ecf20Sopenharmony_ci pdata->info.version = DRIVER_VERSION; 2658c2ecf20Sopenharmony_ci pdata->info.irqcontrol = hv_uio_irqcontrol; 2668c2ecf20Sopenharmony_ci pdata->info.open = hv_uio_open; 2678c2ecf20Sopenharmony_ci pdata->info.release = hv_uio_release; 2688c2ecf20Sopenharmony_ci pdata->info.irq = UIO_IRQ_CUSTOM; 2698c2ecf20Sopenharmony_ci atomic_set(&pdata->refcnt, 0); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* mem resources */ 2728c2ecf20Sopenharmony_ci pdata->info.mem[TXRX_RING_MAP].name = "txrx_rings"; 2738c2ecf20Sopenharmony_ci ring_buffer = page_address(channel->ringbuffer_page); 2748c2ecf20Sopenharmony_ci pdata->info.mem[TXRX_RING_MAP].addr 2758c2ecf20Sopenharmony_ci = (uintptr_t)virt_to_phys(ring_buffer); 2768c2ecf20Sopenharmony_ci pdata->info.mem[TXRX_RING_MAP].size 2778c2ecf20Sopenharmony_ci = channel->ringbuffer_pagecount << PAGE_SHIFT; 2788c2ecf20Sopenharmony_ci pdata->info.mem[TXRX_RING_MAP].memtype = UIO_MEM_IOVA; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci pdata->info.mem[INT_PAGE_MAP].name = "int_page"; 2818c2ecf20Sopenharmony_ci pdata->info.mem[INT_PAGE_MAP].addr 2828c2ecf20Sopenharmony_ci = (uintptr_t)vmbus_connection.int_page; 2838c2ecf20Sopenharmony_ci pdata->info.mem[INT_PAGE_MAP].size = PAGE_SIZE; 2848c2ecf20Sopenharmony_ci pdata->info.mem[INT_PAGE_MAP].memtype = UIO_MEM_LOGICAL; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci pdata->info.mem[MON_PAGE_MAP].name = "monitor_page"; 2878c2ecf20Sopenharmony_ci pdata->info.mem[MON_PAGE_MAP].addr 2888c2ecf20Sopenharmony_ci = (uintptr_t)vmbus_connection.monitor_pages[1]; 2898c2ecf20Sopenharmony_ci pdata->info.mem[MON_PAGE_MAP].size = PAGE_SIZE; 2908c2ecf20Sopenharmony_ci pdata->info.mem[MON_PAGE_MAP].memtype = UIO_MEM_LOGICAL; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci pdata->recv_buf = vzalloc(RECV_BUFFER_SIZE); 2938c2ecf20Sopenharmony_ci if (pdata->recv_buf == NULL) { 2948c2ecf20Sopenharmony_ci ret = -ENOMEM; 2958c2ecf20Sopenharmony_ci goto fail_close; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci ret = vmbus_establish_gpadl(channel, pdata->recv_buf, 2998c2ecf20Sopenharmony_ci RECV_BUFFER_SIZE, &pdata->recv_gpadl); 3008c2ecf20Sopenharmony_ci if (ret) { 3018c2ecf20Sopenharmony_ci vfree(pdata->recv_buf); 3028c2ecf20Sopenharmony_ci goto fail_close; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* put Global Physical Address Label in name */ 3068c2ecf20Sopenharmony_ci snprintf(pdata->recv_name, sizeof(pdata->recv_name), 3078c2ecf20Sopenharmony_ci "recv:%u", pdata->recv_gpadl); 3088c2ecf20Sopenharmony_ci pdata->info.mem[RECV_BUF_MAP].name = pdata->recv_name; 3098c2ecf20Sopenharmony_ci pdata->info.mem[RECV_BUF_MAP].addr 3108c2ecf20Sopenharmony_ci = (uintptr_t)pdata->recv_buf; 3118c2ecf20Sopenharmony_ci pdata->info.mem[RECV_BUF_MAP].size = RECV_BUFFER_SIZE; 3128c2ecf20Sopenharmony_ci pdata->info.mem[RECV_BUF_MAP].memtype = UIO_MEM_VIRTUAL; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci pdata->send_buf = vzalloc(SEND_BUFFER_SIZE); 3158c2ecf20Sopenharmony_ci if (pdata->send_buf == NULL) { 3168c2ecf20Sopenharmony_ci ret = -ENOMEM; 3178c2ecf20Sopenharmony_ci goto fail_close; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci ret = vmbus_establish_gpadl(channel, pdata->send_buf, 3218c2ecf20Sopenharmony_ci SEND_BUFFER_SIZE, &pdata->send_gpadl); 3228c2ecf20Sopenharmony_ci if (ret) { 3238c2ecf20Sopenharmony_ci vfree(pdata->send_buf); 3248c2ecf20Sopenharmony_ci goto fail_close; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci snprintf(pdata->send_name, sizeof(pdata->send_name), 3288c2ecf20Sopenharmony_ci "send:%u", pdata->send_gpadl); 3298c2ecf20Sopenharmony_ci pdata->info.mem[SEND_BUF_MAP].name = pdata->send_name; 3308c2ecf20Sopenharmony_ci pdata->info.mem[SEND_BUF_MAP].addr 3318c2ecf20Sopenharmony_ci = (uintptr_t)pdata->send_buf; 3328c2ecf20Sopenharmony_ci pdata->info.mem[SEND_BUF_MAP].size = SEND_BUFFER_SIZE; 3338c2ecf20Sopenharmony_ci pdata->info.mem[SEND_BUF_MAP].memtype = UIO_MEM_VIRTUAL; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci pdata->info.priv = pdata; 3368c2ecf20Sopenharmony_ci pdata->device = dev; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci ret = uio_register_device(&dev->device, &pdata->info); 3398c2ecf20Sopenharmony_ci if (ret) { 3408c2ecf20Sopenharmony_ci dev_err(&dev->device, "hv_uio register failed\n"); 3418c2ecf20Sopenharmony_ci goto fail_close; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci ret = sysfs_create_bin_file(&channel->kobj, &ring_buffer_bin_attr); 3458c2ecf20Sopenharmony_ci if (ret) 3468c2ecf20Sopenharmony_ci dev_notice(&dev->device, 3478c2ecf20Sopenharmony_ci "sysfs create ring bin file failed; %d\n", ret); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci hv_set_drvdata(dev, pdata); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return 0; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cifail_close: 3548c2ecf20Sopenharmony_ci hv_uio_cleanup(dev, pdata); 3558c2ecf20Sopenharmony_cifail: 3568c2ecf20Sopenharmony_ci kfree(pdata); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return ret; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int 3628c2ecf20Sopenharmony_cihv_uio_remove(struct hv_device *dev) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct hv_uio_private_data *pdata = hv_get_drvdata(dev); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (!pdata) 3678c2ecf20Sopenharmony_ci return 0; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci sysfs_remove_bin_file(&dev->channel->kobj, &ring_buffer_bin_attr); 3708c2ecf20Sopenharmony_ci uio_unregister_device(&pdata->info); 3718c2ecf20Sopenharmony_ci hv_uio_cleanup(dev, pdata); 3728c2ecf20Sopenharmony_ci hv_set_drvdata(dev, NULL); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci vmbus_free_ring(dev->channel); 3758c2ecf20Sopenharmony_ci kfree(pdata); 3768c2ecf20Sopenharmony_ci return 0; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic struct hv_driver hv_uio_drv = { 3808c2ecf20Sopenharmony_ci .name = "uio_hv_generic", 3818c2ecf20Sopenharmony_ci .id_table = NULL, /* only dynamic id's */ 3828c2ecf20Sopenharmony_ci .probe = hv_uio_probe, 3838c2ecf20Sopenharmony_ci .remove = hv_uio_remove, 3848c2ecf20Sopenharmony_ci}; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic int __init 3878c2ecf20Sopenharmony_cihyperv_module_init(void) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci return vmbus_driver_register(&hv_uio_drv); 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic void __exit 3938c2ecf20Sopenharmony_cihyperv_module_exit(void) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci vmbus_driver_unregister(&hv_uio_drv); 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cimodule_init(hyperv_module_init); 3998c2ecf20Sopenharmony_cimodule_exit(hyperv_module_exit); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION); 4028c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 4038c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 4048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 405