blob: a0c4c07a907f74b085672c5dca49da17421a86a1 [file] [log] [blame]
Stephen Hemminger95096f22016-12-03 12:34:40 -08001/*
2 * uio_hv_generic - generic UIO driver for VMBus
3 *
4 * Copyright (c) 2013-2016 Brocade Communications Systems, Inc.
5 * Copyright (c) 2016, Microsoft Corporation.
6 *
7 *
8 * This work is licensed under the terms of the GNU GPL, version 2.
9 *
10 * Since the driver does not declare any device ids, you must allocate
11 * id and bind the device to the driver yourself. For example:
12 *
Stephen Hemminger42896962018-01-04 14:13:27 -080013 * Associate Network GUID with UIO device
Stephen Hemminger95096f22016-12-03 12:34:40 -080014 * # echo "f8615163-df3e-46c5-913f-f2d2f965ed0e" \
Stephen Hemminger42896962018-01-04 14:13:27 -080015 * > /sys/bus/vmbus/drivers/uio_hv_generic/new_id
16 * Then rebind
17 * # echo -n "ed963694-e847-4b2a-85af-bc9cfc11d6f3" \
Stephen Hemminger95096f22016-12-03 12:34:40 -080018 * > /sys/bus/vmbus/drivers/hv_netvsc/unbind
Stephen Hemminger42896962018-01-04 14:13:27 -080019 * # echo -n "ed963694-e847-4b2a-85af-bc9cfc11d6f3" \
Stephen Hemminger95096f22016-12-03 12:34:40 -080020 * > /sys/bus/vmbus/drivers/uio_hv_generic/bind
21 */
22
23#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
24
25#include <linux/device.h>
26#include <linux/kernel.h>
27#include <linux/module.h>
28#include <linux/uio_driver.h>
29#include <linux/netdevice.h>
30#include <linux/if_ether.h>
31#include <linux/skbuff.h>
32#include <linux/hyperv.h>
33#include <linux/vmalloc.h>
34#include <linux/slab.h>
35
36#include "../hv/hyperv_vmbus.h"
37
38#define DRIVER_VERSION "0.02.0"
39#define DRIVER_AUTHOR "Stephen Hemminger <sthemmin at microsoft.com>"
40#define DRIVER_DESC "Generic UIO driver for VMBus devices"
41
42/*
43 * List of resources to be mapped to user space
44 * can be extended up to MAX_UIO_MAPS(5) items
45 */
46enum hv_uio_map {
47 TXRX_RING_MAP = 0,
48 INT_PAGE_MAP,
49 MON_PAGE_MAP,
50};
51
52#define HV_RING_SIZE 512
53
54struct hv_uio_private_data {
55 struct uio_info info;
56 struct hv_device *device;
57};
58
Stephen Hemminger95096f22016-12-03 12:34:40 -080059/*
60 * This is the irqcontrol callback to be registered to uio_info.
61 * It can be used to disable/enable interrupt from user space processes.
62 *
63 * @param info
64 * pointer to uio_info.
65 * @param irq_state
66 * state value. 1 to enable interrupt, 0 to disable interrupt.
67 */
68static int
69hv_uio_irqcontrol(struct uio_info *info, s32 irq_state)
70{
71 struct hv_uio_private_data *pdata = info->priv;
72 struct hv_device *dev = pdata->device;
73
74 dev->channel->inbound.ring_buffer->interrupt_mask = !irq_state;
75 virt_mb();
76
77 return 0;
78}
79
80/*
81 * Callback from vmbus_event when something is in inbound ring.
82 */
83static void hv_uio_channel_cb(void *context)
84{
85 struct hv_uio_private_data *pdata = context;
86 struct hv_device *dev = pdata->device;
87
88 dev->channel->inbound.ring_buffer->interrupt_mask = 1;
89 virt_mb();
90
91 uio_event_notify(&pdata->info);
92}
93
94static int
95hv_uio_probe(struct hv_device *dev,
96 const struct hv_vmbus_device_id *dev_id)
97{
98 struct hv_uio_private_data *pdata;
99 int ret;
100
101 pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
102 if (!pdata)
103 return -ENOMEM;
104
105 ret = vmbus_open(dev->channel, HV_RING_SIZE * PAGE_SIZE,
106 HV_RING_SIZE * PAGE_SIZE, NULL, 0,
107 hv_uio_channel_cb, pdata);
108 if (ret)
109 goto fail;
110
111 dev->channel->inbound.ring_buffer->interrupt_mask = 1;
Stephen Hemminger2141a842018-01-04 14:13:31 -0800112 set_channel_read_mode(dev->channel, HV_CALL_ISR);
Stephen Hemminger95096f22016-12-03 12:34:40 -0800113
114 /* Fill general uio info */
115 pdata->info.name = "uio_hv_generic";
116 pdata->info.version = DRIVER_VERSION;
117 pdata->info.irqcontrol = hv_uio_irqcontrol;
Stephen Hemminger95096f22016-12-03 12:34:40 -0800118 pdata->info.irq = UIO_IRQ_CUSTOM;
119
120 /* mem resources */
121 pdata->info.mem[TXRX_RING_MAP].name = "txrx_rings";
122 pdata->info.mem[TXRX_RING_MAP].addr
Stephen Hemminger9c405462018-01-04 14:13:28 -0800123 = (phys_addr_t)dev->channel->ringbuffer_pages;
Stephen Hemminger95096f22016-12-03 12:34:40 -0800124 pdata->info.mem[TXRX_RING_MAP].size
Stephen Hemminger9c405462018-01-04 14:13:28 -0800125 = dev->channel->ringbuffer_pagecount << PAGE_SHIFT;
Stephen Hemminger95096f22016-12-03 12:34:40 -0800126 pdata->info.mem[TXRX_RING_MAP].memtype = UIO_MEM_LOGICAL;
127
128 pdata->info.mem[INT_PAGE_MAP].name = "int_page";
Stephen Hemminger9c405462018-01-04 14:13:28 -0800129 pdata->info.mem[INT_PAGE_MAP].addr
130 = (phys_addr_t)vmbus_connection.int_page;
Stephen Hemminger95096f22016-12-03 12:34:40 -0800131 pdata->info.mem[INT_PAGE_MAP].size = PAGE_SIZE;
132 pdata->info.mem[INT_PAGE_MAP].memtype = UIO_MEM_LOGICAL;
133
Stephen Hemminger9c405462018-01-04 14:13:28 -0800134 pdata->info.mem[MON_PAGE_MAP].name = "monitor_page";
135 pdata->info.mem[MON_PAGE_MAP].addr
136 = (phys_addr_t)vmbus_connection.monitor_pages[1];
Stephen Hemminger95096f22016-12-03 12:34:40 -0800137 pdata->info.mem[MON_PAGE_MAP].size = PAGE_SIZE;
138 pdata->info.mem[MON_PAGE_MAP].memtype = UIO_MEM_LOGICAL;
139
140 pdata->info.priv = pdata;
141 pdata->device = dev;
142
143 ret = uio_register_device(&dev->device, &pdata->info);
144 if (ret) {
145 dev_err(&dev->device, "hv_uio register failed\n");
146 goto fail_close;
147 }
148
149 hv_set_drvdata(dev, pdata);
150
151 return 0;
152
153fail_close:
154 vmbus_close(dev->channel);
155fail:
156 kfree(pdata);
157
158 return ret;
159}
160
161static int
162hv_uio_remove(struct hv_device *dev)
163{
164 struct hv_uio_private_data *pdata = hv_get_drvdata(dev);
165
166 if (!pdata)
167 return 0;
168
169 uio_unregister_device(&pdata->info);
170 hv_set_drvdata(dev, NULL);
171 vmbus_close(dev->channel);
172 kfree(pdata);
173 return 0;
174}
175
176static struct hv_driver hv_uio_drv = {
177 .name = "uio_hv_generic",
178 .id_table = NULL, /* only dynamic id's */
179 .probe = hv_uio_probe,
180 .remove = hv_uio_remove,
181};
182
183static int __init
184hyperv_module_init(void)
185{
186 return vmbus_driver_register(&hv_uio_drv);
187}
188
189static void __exit
190hyperv_module_exit(void)
191{
192 vmbus_driver_unregister(&hv_uio_drv);
193}
194
195module_init(hyperv_module_init);
196module_exit(hyperv_module_exit);
197
198MODULE_VERSION(DRIVER_VERSION);
199MODULE_LICENSE("GPL v2");
200MODULE_AUTHOR(DRIVER_AUTHOR);
201MODULE_DESCRIPTION(DRIVER_DESC);