aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/hv/vmbus_drv.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/hv/vmbus_drv.c')
-rw-r--r--drivers/staging/hv/vmbus_drv.c1228
1 files changed, 1228 insertions, 0 deletions
diff --git a/drivers/staging/hv/vmbus_drv.c b/drivers/staging/hv/vmbus_drv.c
new file mode 100644
index 00000000000..0acf42c3566
--- /dev/null
+++ b/drivers/staging/hv/vmbus_drv.c
@@ -0,0 +1,1228 @@
1/*
2 *
3 * Copyright (c) 2009, Microsoft Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16 * Place - Suite 330, Boston, MA 02111-1307 USA.
17 *
18 * Authors:
19 * Haiyang Zhang <haiyangz@microsoft.com>
20 * Hank Janssen <hjanssen@microsoft.com>
21 *
22 */
23
24
25#include <linux/init.h>
26#include <linux/module.h>
27#include <linux/device.h>
28#include <linux/irq.h>
29#include <linux/interrupt.h>
30#include <linux/sysctl.h>
31
32#include "logging.h"
33#include "vmbus.h"
34
35//
36// Defines
37//
38
39// FIXME! We need to do this dynamically for PIC and APIC system
40#define VMBUS_IRQ 0x5
41#ifdef KERNEL_2_6_27
42#define VMBUS_IRQ_VECTOR IRQ5_VECTOR
43#endif
44//
45// Data types
46//
47
48// Main vmbus driver data structure
49struct vmbus_driver_context {
50 // !! These must be the first 2 fields !!
51 // The driver field is not used in here. Instead, the bus field is
52 // used to represent the driver
53 struct driver_context drv_ctx;
54 VMBUS_DRIVER_OBJECT drv_obj;
55
56 struct bus_type bus;
57 struct tasklet_struct msg_dpc;
58 struct tasklet_struct event_dpc;
59
60 // The bus root device
61 struct device_context device_ctx;
62};
63
64//
65// Static decl
66//
67static int vmbus_match(struct device *device, struct device_driver *driver);
68static int vmbus_probe(struct device *device);
69static int vmbus_remove(struct device *device);
70static void vmbus_shutdown(struct device *device);
71#if defined(KERNEL_2_6_5) || defined(KERNEL_2_6_9)
72#elif defined(KERNEL_2_6_27)
73static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env);
74#else
75static int vmbus_uevent(struct device *device, char **envp, int num_envp, char *buffer, int buffer_size);
76#endif
77static void vmbus_msg_dpc(unsigned long data);
78static void vmbus_event_dpc(unsigned long data);
79
80#ifdef KERNEL_2_6_27
81static irqreturn_t vmbus_isr(int irq, void* dev_id);
82#else
83static int vmbus_isr(int irq, void* dev_id, struct pt_regs *regs);
84#endif
85
86static void vmbus_device_release(struct device *device);
87static void vmbus_bus_release(struct device *device);
88
89static DEVICE_OBJECT* vmbus_child_device_create(GUID type, GUID instance, void* context);
90static void vmbus_child_device_destroy(DEVICE_OBJECT* device_obj);
91static int vmbus_child_device_register(DEVICE_OBJECT* root_device_obj, DEVICE_OBJECT* child_device_obj);
92static void vmbus_child_device_unregister(DEVICE_OBJECT* child_device_obj);
93static void vmbus_child_device_get_info(DEVICE_OBJECT *device_obj, DEVICE_INFO *device_info);
94
95//static ssize_t vmbus_show_class_id(struct device *dev, struct device_attribute *attr, char *buf);
96//static ssize_t vmbus_show_device_id(struct device *dev, struct device_attribute *attr, char *buf);
97
98static ssize_t vmbus_show_device_attr(struct device *dev, struct device_attribute *dev_attr, char *buf);
99
100//
101// Global
102//
103
104// Global logging setting
105
106//unsigned int vmbus_loglevel= (((VMBUS | VMBUS_DRV)<<16) | DEBUG_LVL_ENTEREXIT);
107//unsigned int vmbus_loglevel= (ALL_MODULES << 16 | DEBUG_LVL_ENTEREXIT);
108unsigned int vmbus_loglevel= (ALL_MODULES << 16 | INFO_LVL);
109EXPORT_SYMBOL(vmbus_loglevel);
110
111static int vmbus_irq = VMBUS_IRQ;
112
113// Setup /proc/sys/bus/vmbus/vmbus_loglevel
114// Allow usage of sysctl cmd to set the logging level
115static struct ctl_table_header *vmbus_ctl_table_hdr;
116
117static ctl_table vmbus_dev_ctl_table[] = {
118 { .ctl_name = 8461,
119 .procname = "vmbus_loglevel",
120 .data = &vmbus_loglevel,
121 .maxlen = sizeof(vmbus_loglevel),
122 .mode = 0644,
123 .proc_handler = &proc_dointvec },
124 { }
125};
126
127static ctl_table vmbus_ctl_table[] = {
128 { .ctl_name = CTL_DEV,
129 .procname = "vmbus",
130 .mode = 0555,
131 .child = vmbus_dev_ctl_table },
132 { }
133};
134
135static ctl_table vmus_root_ctl_table[] = {
136 { .ctl_name = CTL_BUS,
137 .procname = "bus",
138 .mode = 0555,
139 .child = vmbus_ctl_table },
140 { }
141};
142
143#if defined(KERNEL_2_6_5) || defined(KERNEL_2_6_9)
144#else
145//
146// Set up per device attributes in /sys/bus/vmbus/devices/<bus device>
147//
148static struct device_attribute vmbus_device_attrs[] = {
149 __ATTR(id, S_IRUGO, vmbus_show_device_attr, NULL),
150 __ATTR(state, S_IRUGO, vmbus_show_device_attr, NULL),
151 __ATTR(class_id, S_IRUGO, vmbus_show_device_attr, NULL),
152 __ATTR(device_id, S_IRUGO, vmbus_show_device_attr, NULL),
153 __ATTR(monitor_id, S_IRUGO, vmbus_show_device_attr, NULL),
154
155 __ATTR(server_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL),
156 __ATTR(server_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL),
157 __ATTR(server_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL),
158
159 __ATTR(client_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL),
160 __ATTR(client_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL),
161 __ATTR(client_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL),
162
163 __ATTR(out_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL),
164 __ATTR(out_read_index, S_IRUGO, vmbus_show_device_attr, NULL),
165 __ATTR(out_write_index, S_IRUGO, vmbus_show_device_attr, NULL),
166 __ATTR(out_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
167 __ATTR(out_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
168
169 __ATTR(in_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL),
170 __ATTR(in_read_index, S_IRUGO, vmbus_show_device_attr, NULL),
171 __ATTR(in_write_index, S_IRUGO, vmbus_show_device_attr, NULL),
172 __ATTR(in_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
173 __ATTR(in_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
174 __ATTR_NULL
175};
176#endif
177
178// The one and only one
179static struct vmbus_driver_context g_vmbus_drv={
180 .bus.name = "vmbus",
181 .bus.match = vmbus_match,
182#if defined(KERNEL_2_6_5) || defined(KERNEL_2_6_9)
183#else
184 .bus.shutdown = vmbus_shutdown,
185 .bus.remove = vmbus_remove,
186 .bus.probe = vmbus_probe,
187 .bus.uevent = vmbus_uevent,
188 .bus.dev_attrs = vmbus_device_attrs,
189#endif
190};
191
192//
193// Routines
194//
195
196
197/*++
198
199Name: vmbus_show_device_attr()
200
201Desc: Show the device attribute in sysfs. This is invoked when user does a "cat /sys/bus/vmbus/devices/<bus device>/<attr name>"
202
203--*/
204static ssize_t vmbus_show_device_attr(struct device *dev, struct device_attribute *dev_attr, char *buf)
205{
206 struct device_context *device_ctx = device_to_device_context(dev);
207 DEVICE_INFO device_info;
208
209 memset(&device_info, 0, sizeof(DEVICE_INFO));
210
211 vmbus_child_device_get_info(&device_ctx->device_obj, &device_info);
212
213 if (!strcmp(dev_attr->attr.name, "class_id"))
214 {
215 return sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}\n",
216 device_info.ChannelType.Data[3], device_info.ChannelType.Data[2], device_info.ChannelType.Data[1], device_info.ChannelType.Data[0],
217 device_info.ChannelType.Data[5], device_info.ChannelType.Data[4],
218 device_info.ChannelType.Data[7], device_info.ChannelType.Data[6],
219 device_info.ChannelType.Data[8], device_info.ChannelType.Data[9], device_info.ChannelType.Data[10], device_info.ChannelType.Data[11], device_info.ChannelType.Data[12], device_info.ChannelType.Data[13], device_info.ChannelType.Data[14], device_info.ChannelType.Data[15]);
220
221 }
222 else if (!strcmp(dev_attr->attr.name, "device_id"))
223 {
224 return sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}\n",
225 device_info.ChannelInstance.Data[3], device_info.ChannelInstance.Data[2], device_info.ChannelInstance.Data[1], device_info.ChannelInstance.Data[0],
226 device_info.ChannelInstance.Data[5], device_info.ChannelInstance.Data[4],
227 device_info.ChannelInstance.Data[7], device_info.ChannelInstance.Data[6],
228 device_info.ChannelInstance.Data[8], device_info.ChannelInstance.Data[9], device_info.ChannelInstance.Data[10], device_info.ChannelInstance.Data[11], device_info.ChannelInstance.Data[12], device_info.ChannelInstance.Data[13], device_info.ChannelInstance.Data[14], device_info.ChannelInstance.Data[15]);
229 }
230 else if (!strcmp(dev_attr->attr.name, "state"))
231 {
232 return sprintf(buf, "%d\n", device_info.ChannelState);
233 }
234 else if (!strcmp(dev_attr->attr.name, "id"))
235 {
236 return sprintf(buf, "%d\n", device_info.ChannelId);
237 }
238 else if (!strcmp(dev_attr->attr.name, "out_intr_mask"))
239 {
240 return sprintf(buf, "%d\n", device_info.Outbound.InterruptMask);
241 }
242 else if (!strcmp(dev_attr->attr.name, "out_read_index"))
243 {
244 return sprintf(buf, "%d\n", device_info.Outbound.ReadIndex);
245 }
246 else if (!strcmp(dev_attr->attr.name, "out_write_index"))
247 {
248 return sprintf(buf, "%d\n", device_info.Outbound.WriteIndex);
249 }
250 else if (!strcmp(dev_attr->attr.name, "out_read_bytes_avail"))
251 {
252 return sprintf(buf, "%d\n", device_info.Outbound.BytesAvailToRead);
253 }
254 else if (!strcmp(dev_attr->attr.name, "out_write_bytes_avail"))
255 {
256 return sprintf(buf, "%d\n", device_info.Outbound.BytesAvailToWrite);
257 }
258 else if (!strcmp(dev_attr->attr.name, "in_intr_mask"))
259 {
260 return sprintf(buf, "%d\n", device_info.Inbound.InterruptMask);
261 }
262 else if (!strcmp(dev_attr->attr.name, "in_read_index"))
263 {
264 return sprintf(buf, "%d\n", device_info.Inbound.ReadIndex);
265 }
266 else if (!strcmp(dev_attr->attr.name, "in_write_index"))
267 {
268 return sprintf(buf, "%d\n", device_info.Inbound.WriteIndex);
269 }
270 else if (!strcmp(dev_attr->attr.name, "in_read_bytes_avail"))
271 {
272 return sprintf(buf, "%d\n", device_info.Inbound.BytesAvailToRead);
273 }
274 else if (!strcmp(dev_attr->attr.name, "in_write_bytes_avail"))
275 {
276 return sprintf(buf, "%d\n", device_info.Inbound.BytesAvailToWrite);
277 }
278 else if (!strcmp(dev_attr->attr.name, "monitor_id"))
279 {
280 return sprintf(buf, "%d\n", device_info.MonitorId);
281 }
282 else if (!strcmp(dev_attr->attr.name, "server_monitor_pending"))
283 {
284 return sprintf(buf, "%d\n", device_info.ServerMonitorPending);
285 }
286 else if (!strcmp(dev_attr->attr.name, "server_monitor_latency"))
287 {
288 return sprintf(buf, "%d\n", device_info.ServerMonitorLatency);
289 }
290 else if (!strcmp(dev_attr->attr.name, "server_monitor_conn_id"))
291 {
292 return sprintf(buf, "%d\n", device_info.ServerMonitorConnectionId);
293 }
294 else if (!strcmp(dev_attr->attr.name, "client_monitor_pending"))
295 {
296 return sprintf(buf, "%d\n", device_info.ClientMonitorPending);
297 }
298 else if (!strcmp(dev_attr->attr.name, "client_monitor_latency"))
299 {
300 return sprintf(buf, "%d\n", device_info.ClientMonitorLatency);
301 }
302 else if (!strcmp(dev_attr->attr.name, "client_monitor_conn_id"))
303 {
304 return sprintf(buf, "%d\n", device_info.ClientMonitorConnectionId);
305 }
306 else
307 {
308 return 0;
309 }
310}
311
312/*++
313
314Name: vmbus_show_class_id()
315
316Desc: Show the device class id in sysfs
317
318--*/
319//static ssize_t vmbus_show_class_id(struct device *dev, struct device_attribute *attr, char *buf)
320//{
321// struct device_context *device_ctx = device_to_device_context(dev);
322// return sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}\n",
323// device_ctx->class_id[3], device_ctx->class_id[2], device_ctx->class_id[1], device_ctx->class_id[0],
324// device_ctx->class_id[5], device_ctx->class_id[4],
325// device_ctx->class_id[7], device_ctx->class_id[6],
326// device_ctx->class_id[8], device_ctx->class_id[9], device_ctx->class_id[10], device_ctx->class_id[11], device_ctx->class_id[12], device_ctx->class_id[13], device_ctx->class_id[14], device_ctx->class_id[15]);
327//}
328
329/*++
330
331Name: vmbus_show_device_id()
332
333Desc: Show the device instance id in sysfs
334
335--*/
336//static ssize_t vmbus_show_device_id(struct device *dev, struct device_attribute *attr, char *buf)
337//{
338// struct device_context *device_ctx = device_to_device_context(dev);
339// return sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}\n",
340// device_ctx->device_id[3], device_ctx->device_id[2], device_ctx->device_id[1], device_ctx->device_id[0],
341// device_ctx->device_id[5], device_ctx->device_id[4],
342// device_ctx->device_id[7], device_ctx->device_id[6],
343// device_ctx->device_id[8], device_ctx->device_id[9], device_ctx->device_id[10], device_ctx->device_id[11], device_ctx->device_id[12], device_ctx->device_id[13], device_ctx->device_id[14], device_ctx->device_id[15]);
344//}
345
346/*++
347
348Name: vmbus_bus_init()
349
350Desc: Main vmbus driver initialization routine. Here, we
351 - initialize the vmbus driver context
352 - setup various driver entry points
353 - invoke the vmbus hv main init routine
354 - get the irq resource
355 - invoke the vmbus to add the vmbus root device
356 - setup the vmbus root device
357 - retrieve the channel offers
358--*/
359int vmbus_bus_init(PFN_DRIVERINITIALIZE pfn_drv_init)
360{
361 int ret=0;
362 unsigned int vector=0;
363
364 struct vmbus_driver_context *vmbus_drv_ctx=&g_vmbus_drv;
365 VMBUS_DRIVER_OBJECT *vmbus_drv_obj=&g_vmbus_drv.drv_obj;
366
367 struct device_context *dev_ctx=&g_vmbus_drv.device_ctx;
368
369 DPRINT_ENTER(VMBUS_DRV);
370
371 // Set this up to allow lower layer to callback to add/remove child devices on the bus
372 vmbus_drv_obj->OnChildDeviceCreate = vmbus_child_device_create;
373 vmbus_drv_obj->OnChildDeviceDestroy = vmbus_child_device_destroy;
374 vmbus_drv_obj->OnChildDeviceAdd = vmbus_child_device_register;
375 vmbus_drv_obj->OnChildDeviceRemove = vmbus_child_device_unregister;
376
377 // Call to bus driver to initialize
378 ret = pfn_drv_init(&vmbus_drv_obj->Base);
379 if (ret != 0)
380 {
381 DPRINT_ERR(VMBUS_DRV, "Unable to initialize vmbus (%d)", ret);
382 goto cleanup;
383 }
384
385 // Sanity checks
386 if (!vmbus_drv_obj->Base.OnDeviceAdd)
387 {
388 DPRINT_ERR(VMBUS_DRV, "OnDeviceAdd() routine not set");
389 ret = -1;
390 goto cleanup;
391 }
392
393 vmbus_drv_ctx->bus.name = vmbus_drv_obj->Base.name;
394
395 // Initialize the bus context
396 tasklet_init(&vmbus_drv_ctx->msg_dpc, vmbus_msg_dpc, (unsigned long)vmbus_drv_obj);
397 tasklet_init(&vmbus_drv_ctx->event_dpc, vmbus_event_dpc, (unsigned long)vmbus_drv_obj);
398
399 // Now, register the bus driver with LDM
400 bus_register(&vmbus_drv_ctx->bus);
401
402 // Get the interrupt resource
403#ifdef KERNEL_2_6_27
404 ret = request_irq(vmbus_irq,
405 vmbus_isr,
406 IRQF_SAMPLE_RANDOM,
407 vmbus_drv_obj->Base.name,
408 NULL);
409#else
410 ret = request_irq(vmbus_irq,
411 vmbus_isr,
412 SA_SAMPLE_RANDOM,
413 vmbus_drv_obj->Base.name,
414 NULL);
415#endif
416
417 if (ret != 0)
418 {
419 DPRINT_ERR(VMBUS_DRV, "ERROR - Unable to request IRQ %d", vmbus_irq);
420
421 bus_unregister(&vmbus_drv_ctx->bus);
422
423 ret = -1;
424 goto cleanup;
425 }
426#ifdef KERNEL_2_6_27
427 vector = VMBUS_IRQ_VECTOR;
428#else
429#if X2V_LINUX
430 vector = vmbus_irq + FIRST_DEVICE_VECTOR - 2;
431#else
432 vector = vmbus_irq + FIRST_EXTERNAL_VECTOR;
433#endif
434#endif
435
436 DPRINT_INFO(VMBUS_DRV, "irq 0x%x vector 0x%x", vmbus_irq, vector);
437
438 // Call to bus driver to add the root device
439 memset(dev_ctx, 0, sizeof(struct device_context));
440
441 ret = vmbus_drv_obj->Base.OnDeviceAdd(&dev_ctx->device_obj, &vector);
442 if (ret != 0)
443 {
444 DPRINT_ERR(VMBUS_DRV, "ERROR - Unable to add vmbus root device");
445
446 free_irq(vmbus_irq, NULL);
447
448 bus_unregister(&vmbus_drv_ctx->bus);
449
450 ret = -1;
451 goto cleanup;
452 }
453 //strcpy(dev_ctx->device.bus_id, dev_ctx->device_obj.name);
454 sprintf(dev_ctx->device.bus_id, "vmbus_0_0");
455 memcpy(&dev_ctx->class_id, &dev_ctx->device_obj.deviceType, sizeof(GUID));
456 memcpy(&dev_ctx->device_id, &dev_ctx->device_obj.deviceInstance, sizeof(GUID));
457
458 // No need to bind a driver to the root device.
459 dev_ctx->device.parent = NULL;
460 dev_ctx->device.bus = &vmbus_drv_ctx->bus; //NULL; // vmbus_remove() does not get invoked
461
462 // Setup the device dispatch table
463 dev_ctx->device.release = vmbus_bus_release;
464
465 // Setup the bus as root device
466 device_register(&dev_ctx->device);
467
468 vmbus_drv_obj->GetChannelOffers();
469
470cleanup:
471 DPRINT_EXIT(VMBUS_DRV);
472
473 return ret;
474}
475
476
477/*++
478
479Name: vmbus_bus_exit()
480
481Desc: Terminate the vmbus driver. This routine is opposite of vmbus_bus_init()
482
483--*/
484void vmbus_bus_exit(void)
485{
486 VMBUS_DRIVER_OBJECT *vmbus_drv_obj=&g_vmbus_drv.drv_obj;
487 struct vmbus_driver_context *vmbus_drv_ctx=&g_vmbus_drv;
488
489 struct device_context *dev_ctx=&g_vmbus_drv.device_ctx;
490
491 DPRINT_ENTER(VMBUS_DRV);
492
493 // Remove the root device
494 if (vmbus_drv_obj->Base.OnDeviceRemove)
495 vmbus_drv_obj->Base.OnDeviceRemove(&dev_ctx->device_obj);
496
497 if (vmbus_drv_obj->Base.OnCleanup)
498 vmbus_drv_obj->Base.OnCleanup(&vmbus_drv_obj->Base);
499
500 // Unregister the root bus device
501 device_unregister(&dev_ctx->device);
502
503 bus_unregister(&vmbus_drv_ctx->bus);
504
505 free_irq(vmbus_irq, NULL);
506
507 tasklet_kill(&vmbus_drv_ctx->msg_dpc);
508 tasklet_kill(&vmbus_drv_ctx->event_dpc);
509
510 DPRINT_EXIT(VMBUS_DRV);
511
512 return;
513}
514
515/*++
516
517Name: vmbus_child_driver_register()
518
519Desc: Register a vmbus's child driver
520
521--*/
522void vmbus_child_driver_register(struct driver_context* driver_ctx)
523{
524 VMBUS_DRIVER_OBJECT *vmbus_drv_obj=&g_vmbus_drv.drv_obj;
525
526 DPRINT_ENTER(VMBUS_DRV);
527
528 DPRINT_INFO(VMBUS_DRV, "child driver (%p) registering - name %s", driver_ctx, driver_ctx->driver.name);
529
530 // The child driver on this vmbus
531 driver_ctx->driver.bus = &g_vmbus_drv.bus;
532
533 driver_register(&driver_ctx->driver);
534
535 vmbus_drv_obj->GetChannelOffers();
536
537 DPRINT_EXIT(VMBUS_DRV);
538}
539
540EXPORT_SYMBOL(vmbus_child_driver_register);
541
542/*++
543
544Name: vmbus_child_driver_unregister()
545
546Desc: Unregister a vmbus's child driver
547
548--*/
549void vmbus_child_driver_unregister(struct driver_context* driver_ctx)
550{
551 DPRINT_ENTER(VMBUS_DRV);
552
553 DPRINT_INFO(VMBUS_DRV, "child driver (%p) unregistering - name %s", driver_ctx, driver_ctx->driver.name);
554
555 driver_unregister(&driver_ctx->driver);
556
557 driver_ctx->driver.bus = NULL;
558
559 DPRINT_EXIT(VMBUS_DRV);
560}
561
562EXPORT_SYMBOL(vmbus_child_driver_unregister);
563
564/*++
565
566Name: vmbus_get_interface()
567
568Desc: Get the vmbus channel interface. This is invoked by child/client driver that sits
569 above vmbus
570--*/
571void vmbus_get_interface(VMBUS_CHANNEL_INTERFACE *interface)
572{
573 VMBUS_DRIVER_OBJECT *vmbus_drv_obj=&g_vmbus_drv.drv_obj;
574
575 vmbus_drv_obj->GetChannelInterface(interface);
576}
577
578EXPORT_SYMBOL(vmbus_get_interface);
579
580
581/*++
582
583Name: vmbus_child_device_get_info()
584
585Desc: Get the vmbus child device info. This is invoked to display various device attributes in sysfs.
586--*/
587static void vmbus_child_device_get_info(DEVICE_OBJECT *device_obj, DEVICE_INFO *device_info)
588{
589 VMBUS_DRIVER_OBJECT *vmbus_drv_obj=&g_vmbus_drv.drv_obj;
590
591 vmbus_drv_obj->GetChannelInfo(device_obj, device_info);
592}
593
594
595/*++
596
597Name: vmbus_child_device_create()
598
599Desc: Creates and registers a new child device on the vmbus.
600
601--*/
602static DEVICE_OBJECT* vmbus_child_device_create(GUID type, GUID instance, void* context)
603{
604 struct device_context *child_device_ctx;
605 DEVICE_OBJECT* child_device_obj;
606
607 DPRINT_ENTER(VMBUS_DRV);
608
609 // Allocate the new child device
610 child_device_ctx = kzalloc(sizeof(struct device_context), GFP_KERNEL);
611 if (!child_device_ctx)
612 {
613 DPRINT_ERR(VMBUS_DRV, "unable to allocate device_context for child device");
614 DPRINT_EXIT(VMBUS_DRV);
615
616 return NULL;
617 }
618
619 DPRINT_DBG(VMBUS_DRV, "child device (%p) allocated - "
620 "type {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x},"
621 "id {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}",
622 &child_device_ctx->device,
623 type.Data[3], type.Data[2], type.Data[1], type.Data[0], type.Data[5], type.Data[4], type.Data[7], type.Data[6], type.Data[8], type.Data[9], type.Data[10], type.Data[11], type.Data[12], type.Data[13], type.Data[14], type.Data[15],
624 instance.Data[3], instance.Data[2], instance.Data[1], instance.Data[0], instance.Data[5], instance.Data[4], instance.Data[7], instance.Data[6], instance.Data[8], instance.Data[9], instance.Data[10], instance.Data[11], instance.Data[12], instance.Data[13], instance.Data[14], instance.Data[15]);
625
626 child_device_obj = &child_device_ctx->device_obj;
627 child_device_obj->context = context;
628 memcpy(&child_device_obj->deviceType, &type, sizeof(GUID));
629 memcpy(&child_device_obj->deviceInstance, &instance, sizeof(GUID));
630
631 memcpy(&child_device_ctx->class_id, &type, sizeof(GUID));
632 memcpy(&child_device_ctx->device_id, &instance, sizeof(GUID));
633
634 DPRINT_EXIT(VMBUS_DRV);
635
636 return child_device_obj;
637}
638
639/*++
640
641Name: vmbus_child_device_register()
642
643Desc: Register the child device on the specified bus
644
645--*/
646static int vmbus_child_device_register(DEVICE_OBJECT* root_device_obj, DEVICE_OBJECT* child_device_obj)
647{
648 int ret=0;
649 struct device_context *root_device_ctx = to_device_context(root_device_obj);
650 struct device_context *child_device_ctx = to_device_context(child_device_obj);
651 static int device_num=0;
652
653 DPRINT_ENTER(VMBUS_DRV);
654
655 DPRINT_DBG(VMBUS_DRV, "child device (%p) registering", child_device_ctx);
656 //
657 // Make sure we are not registered already
658 //
659 if (child_device_ctx->device.bus_id[0] != '\0')
660 {
661 DPRINT_ERR(VMBUS_DRV, "child device (%p) already registered - busid %s", child_device_ctx, child_device_ctx->device.bus_id);
662
663 ret = -1;
664 goto Cleanup;
665 }
666
667 // Set the device bus id. Otherwise, device_register()will fail.
668 sprintf(child_device_ctx->device.bus_id, "vmbus_0_%d", InterlockedIncrement(&device_num));
669
670 // The new device belongs to this bus
671 child_device_ctx->device.bus = &g_vmbus_drv.bus; //device->dev.bus;
672 child_device_ctx->device.parent = &root_device_ctx->device;
673 child_device_ctx->device.release = vmbus_device_release;
674
675 // Register with the LDM. This will kick off the driver/device binding...which will
676 // eventually call vmbus_match() and vmbus_probe()
677 ret = device_register(&child_device_ctx->device);
678
679 // vmbus_probe() error does not get propergate to device_register().
680 ret = child_device_ctx->probe_error;
681
682 if (ret)
683 DPRINT_ERR(VMBUS_DRV, "unable to register child device (%p) (%d)", &child_device_ctx->device);
684 else
685 DPRINT_INFO(VMBUS_DRV, "child device (%p) registered", &child_device_ctx->device);
686
687Cleanup:
688 DPRINT_EXIT(VMBUS_DRV);
689
690 return ret;
691}
692
693/*++
694
695Name: vmbus_child_device_unregister()
696
697Desc: Remove the specified child device from the vmbus.
698
699--*/
700static void vmbus_child_device_unregister(DEVICE_OBJECT* device_obj)
701{
702 struct device_context *device_ctx = to_device_context(device_obj);
703
704 DPRINT_ENTER(VMBUS_DRV);
705
706 DPRINT_INFO(VMBUS_DRV, "unregistering child device (%p)", &device_ctx->device);
707
708 // Kick off the process of unregistering the device.
709 // This will call vmbus_remove() and eventually vmbus_device_release()
710 device_unregister(&device_ctx->device);
711
712 DPRINT_INFO(VMBUS_DRV, "child device (%p) unregistered", &device_ctx->device);
713
714 DPRINT_EXIT(VMBUS_DRV);
715}
716
717
718/*++
719
720Name: vmbus_child_device_destroy()
721
722Desc: Destroy the specified child device on the vmbus.
723
724--*/
725static void vmbus_child_device_destroy(DEVICE_OBJECT* device_obj)
726{
727 DPRINT_ENTER(VMBUS_DRV);
728
729 DPRINT_EXIT(VMBUS_DRV);
730}
731
732/*++
733
734Name: vmbus_uevent()
735
736Desc: This routine is invoked when a device is added or removed on the vmbus to generate a uevent to udev in the
737 userspace. The udev will then look at its rule and the uevent generated here to load the appropriate driver
738
739--*/
740#if defined(KERNEL_2_6_5) || defined(KERNEL_2_6_9)
741#elif defined(KERNEL_2_6_27)
742static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env)
743{
744 struct device_context *device_ctx = device_to_device_context(device);
745 int i=0;
746 int len=0;
747 int ret;
748
749 DPRINT_ENTER(VMBUS_DRV);
750
751 DPRINT_INFO(VMBUS_DRV, "generating uevent - VMBUS_DEVICE_CLASS_GUID={%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}",
752 device_ctx->class_id.Data[3], device_ctx->class_id.Data[2], device_ctx->class_id.Data[1], device_ctx->class_id.Data[0],
753 device_ctx->class_id.Data[5], device_ctx->class_id.Data[4],
754 device_ctx->class_id.Data[7], device_ctx->class_id.Data[6],
755 device_ctx->class_id.Data[8], device_ctx->class_id.Data[9], device_ctx->class_id.Data[10], device_ctx->class_id.Data[11],
756 device_ctx->class_id.Data[12], device_ctx->class_id.Data[13], device_ctx->class_id.Data[14], device_ctx->class_id.Data[15]);
757
758 env->envp_idx = i;
759 env->buflen = len;
760 ret = add_uevent_var(env,
761 "VMBUS_DEVICE_CLASS_GUID={%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}",
762 device_ctx->class_id.Data[3], device_ctx->class_id.Data[2], device_ctx->class_id.Data[1], device_ctx->class_id.Data[0],
763 device_ctx->class_id.Data[5], device_ctx->class_id.Data[4],
764 device_ctx->class_id.Data[7], device_ctx->class_id.Data[6],
765 device_ctx->class_id.Data[8], device_ctx->class_id.Data[9], device_ctx->class_id.Data[10], device_ctx->class_id.Data[11],
766 device_ctx->class_id.Data[12], device_ctx->class_id.Data[13], device_ctx->class_id.Data[14], device_ctx->class_id.Data[15]);
767
768 if (ret)
769 {
770 return ret;
771 }
772
773 ret = add_uevent_var(env,
774 "VMBUS_DEVICE_DEVICE_GUID={%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}",
775 device_ctx->device_id.Data[3], device_ctx->device_id.Data[2], device_ctx->device_id.Data[1], device_ctx->device_id.Data[0],
776 device_ctx->device_id.Data[5], device_ctx->device_id.Data[4],
777 device_ctx->device_id.Data[7], device_ctx->device_id.Data[6],
778 device_ctx->device_id.Data[8], device_ctx->device_id.Data[9], device_ctx->device_id.Data[10], device_ctx->device_id.Data[11],
779 device_ctx->device_id.Data[12], device_ctx->device_id.Data[13], device_ctx->device_id.Data[14], device_ctx->device_id.Data[15]);
780
781 if (ret)
782 {
783 return ret;
784 }
785
786 env->envp[env->envp_idx] = NULL;
787
788 DPRINT_EXIT(VMBUS_DRV);
789
790 return 0;
791}
792
793#else
794static int vmbus_uevent(struct device *device, char **envp, int num_envp, char *buffer, int buffer_size)
795{
796 struct device_context *device_ctx = device_to_device_context(device);
797 int i=0;
798 int len=0;
799 int ret;
800
801 DPRINT_ENTER(VMBUS_DRV);
802
803 DPRINT_INFO(VMBUS_DRV, "generating uevent - VMBUS_DEVICE_CLASS_GUID={%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}",
804 device_ctx->class_id.Data[3], device_ctx->class_id.Data[2], device_ctx->class_id.Data[1], device_ctx->class_id.Data[0],
805 device_ctx->class_id.Data[5], device_ctx->class_id.Data[4],
806 device_ctx->class_id.Data[7], device_ctx->class_id.Data[6],
807 device_ctx->class_id.Data[8], device_ctx->class_id.Data[9], device_ctx->class_id.Data[10], device_ctx->class_id.Data[11],
808 device_ctx->class_id.Data[12], device_ctx->class_id.Data[13], device_ctx->class_id.Data[14], device_ctx->class_id.Data[15]);
809
810 ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
811 "VMBUS_DEVICE_CLASS_GUID={%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}",
812 device_ctx->class_id.Data[3], device_ctx->class_id.Data[2], device_ctx->class_id.Data[1], device_ctx->class_id.Data[0],
813 device_ctx->class_id.Data[5], device_ctx->class_id.Data[4],
814 device_ctx->class_id.Data[7], device_ctx->class_id.Data[6],
815 device_ctx->class_id.Data[8], device_ctx->class_id.Data[9], device_ctx->class_id.Data[10], device_ctx->class_id.Data[11],
816 device_ctx->class_id.Data[12], device_ctx->class_id.Data[13], device_ctx->class_id.Data[14], device_ctx->class_id.Data[15]);
817
818 if (ret)
819 {
820 return ret;
821 }
822
823 ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
824 "VMBUS_DEVICE_DEVICE_GUID={%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}",
825 device_ctx->device_id.Data[3], device_ctx->device_id.Data[2], device_ctx->device_id.Data[1], device_ctx->device_id.Data[0],
826 device_ctx->device_id.Data[5], device_ctx->device_id.Data[4],
827 device_ctx->device_id.Data[7], device_ctx->device_id.Data[6],
828 device_ctx->device_id.Data[8], device_ctx->device_id.Data[9], device_ctx->device_id.Data[10], device_ctx->device_id.Data[11],
829 device_ctx->device_id.Data[12], device_ctx->device_id.Data[13], device_ctx->device_id.Data[14], device_ctx->device_id.Data[15]);
830
831 if (ret)
832 {
833 return ret;
834 }
835
836 envp[i] = NULL;
837
838 DPRINT_EXIT(VMBUS_DRV);
839
840 return 0;
841}
842#endif
843
844/*++
845
846Name: vmbus_match()
847
848Desc: Attempt to match the specified device to the specified driver
849
850--*/
851static int vmbus_match(struct device *device, struct device_driver *driver)
852{
853 int match=0;
854 struct driver_context *driver_ctx = driver_to_driver_context(driver);
855 struct device_context *device_ctx = device_to_device_context(device);
856
857 DPRINT_ENTER(VMBUS_DRV);
858
859 // We found our driver ?
860 if (memcmp(&device_ctx->class_id, &driver_ctx->class_id, sizeof(GUID)) == 0)
861 {
862 // !! NOTE: The driver_ctx is not a vmbus_drv_ctx. We typecast it here to access the
863 // DRIVER_OBJECT field
864 struct vmbus_driver_context *vmbus_drv_ctx = (struct vmbus_driver_context*)driver_ctx;
865 device_ctx->device_obj.Driver = &vmbus_drv_ctx->drv_obj.Base;
866 DPRINT_INFO(VMBUS_DRV, "device object (%p) set to driver object (%p)", &device_ctx->device_obj, device_ctx->device_obj.Driver);
867
868 match = 1;
869 }
870
871 DPRINT_EXIT(VMBUS_DRV);
872
873 return match;
874}
875
876
877/*++
878
879Name: vmbus_probe_failed_cb()
880
881Desc: Callback when a driver probe failed in vmbus_probe(). We need a callback because
882 we cannot invoked device_unregister() inside vmbus_probe() since vmbus_probe() may be
883 invoked inside device_register() i.e. we cannot call device_unregister() inside
884 device_register()
885--*/
886#ifdef KERNEL_2_6_27
887static void vmbus_probe_failed_cb(struct work_struct *context)
888#else
889static void vmbus_probe_failed_cb(void* context)
890#endif
891{
892 struct device_context *device_ctx = (struct device_context*)context;
893
894
895 DPRINT_ENTER(VMBUS_DRV);
896
897 // Kick off the process of unregistering the device.
898 // This will call vmbus_remove() and eventually vmbus_device_release()
899 device_unregister(&device_ctx->device);
900
901 //put_device(&device_ctx->device);
902 DPRINT_EXIT(VMBUS_DRV);
903}
904
905
906/*++
907
908Name: vmbus_probe()
909
910Desc: Add the new vmbus's child device
911
912--*/
913static int vmbus_probe(struct device *child_device)
914{
915 int ret=0;
916 struct driver_context *driver_ctx = driver_to_driver_context(child_device->driver);
917 struct device_context *device_ctx = device_to_device_context(child_device);
918
919 DPRINT_ENTER(VMBUS_DRV);
920
921 // Let the specific open-source driver handles the probe if it can
922 if (driver_ctx->probe)
923 {
924 ret = device_ctx->probe_error = driver_ctx->probe(child_device);
925 if (ret != 0)
926 {
927 DPRINT_ERR(VMBUS_DRV, "probe() failed for device %s (%p) on driver %s (%d)...", child_device->bus_id, child_device, child_device->driver->name, ret);
928
929#ifdef KERNEL_2_6_27
930 INIT_WORK(&device_ctx->probe_failed_work_item, vmbus_probe_failed_cb);
931#else
932 INIT_WORK(&device_ctx->probe_failed_work_item, vmbus_probe_failed_cb, device_ctx);
933#endif
934 schedule_work(&device_ctx->probe_failed_work_item);
935 }
936 }
937 else
938 {
939 DPRINT_ERR(VMBUS_DRV, "probe() method not set for driver - %s", child_device->driver->name);
940 ret = -1;
941 }
942
943 DPRINT_EXIT(VMBUS_DRV);
944 return ret;
945}
946
947
948/*++
949
950Name: vmbus_remove()
951
952Desc: Remove a vmbus device
953
954--*/
955static int vmbus_remove(struct device *child_device)
956{
957 int ret=0;
958 struct driver_context *driver_ctx;
959
960 DPRINT_ENTER(VMBUS_DRV);
961
962 // Special case root bus device
963 if (child_device->parent == NULL)
964 {
965 // No-op since it is statically defined and handle in vmbus_bus_exit()
966 DPRINT_EXIT(VMBUS_DRV);
967 return 0;
968 }
969
970 if (child_device->driver)
971 {
972 driver_ctx = driver_to_driver_context(child_device->driver);
973
974 // Let the specific open-source driver handles the removal if it can
975 if (driver_ctx->remove)
976 {
977 ret = driver_ctx->remove(child_device);
978 }
979 else
980 {
981 DPRINT_ERR(VMBUS_DRV, "remove() method not set for driver - %s", child_device->driver->name);
982 ret = -1;
983 }
984 }
985 else
986 {
987
988 }
989
990 DPRINT_EXIT(VMBUS_DRV);
991
992 return 0;
993}
994
995/*++
996
997Name: vmbus_shutdown()
998
999Desc: Shutdown a vmbus device
1000
1001--*/
1002static void vmbus_shutdown(struct device *child_device)
1003{
1004 struct driver_context *driver_ctx;
1005
1006 DPRINT_ENTER(VMBUS_DRV);
1007
1008 // Special case root bus device
1009 if (child_device->parent == NULL)
1010 {
1011 // No-op since it is statically defined and handle in vmbus_bus_exit()
1012 DPRINT_EXIT(VMBUS_DRV);
1013 return;
1014 }
1015
1016 // The device may not be attached yet
1017 if (!child_device->driver)
1018 {
1019 DPRINT_EXIT(VMBUS_DRV);
1020 return;
1021 }
1022
1023 driver_ctx = driver_to_driver_context(child_device->driver);
1024
1025 // Let the specific open-source driver handles the removal if it can
1026 if (driver_ctx->shutdown)
1027 {
1028 driver_ctx->shutdown(child_device);
1029 }
1030
1031 DPRINT_EXIT(VMBUS_DRV);
1032
1033 return;
1034}
1035
1036/*++
1037
1038Name: vmbus_bus_release()
1039
1040Desc: Final callback release of the vmbus root device
1041
1042--*/
1043static void vmbus_bus_release(struct device *device)
1044{
1045 DPRINT_ENTER(VMBUS_DRV);
1046 DPRINT_EXIT(VMBUS_DRV);
1047}
1048
1049/*++
1050
1051Name: vmbus_device_release()
1052
1053Desc: Final callback release of the vmbus child device
1054
1055--*/
1056static void vmbus_device_release(struct device *device)
1057{
1058 struct device_context *device_ctx = device_to_device_context(device);
1059
1060 DPRINT_ENTER(VMBUS_DRV);
1061
1062 //vmbus_child_device_destroy(&device_ctx->device_obj);
1063 kfree(device_ctx);
1064
1065 // !!DO NOT REFERENCE device_ctx anymore at this point!!
1066
1067 DPRINT_EXIT(VMBUS_DRV);
1068
1069 return;
1070}
1071
1072/*++
1073
1074Name: vmbus_msg_dpc()
1075
1076Desc: Tasklet routine to handle hypervisor messages
1077
1078--*/
1079static void vmbus_msg_dpc(unsigned long data)
1080{
1081 VMBUS_DRIVER_OBJECT* vmbus_drv_obj = (VMBUS_DRIVER_OBJECT*)data;
1082
1083 DPRINT_ENTER(VMBUS_DRV);
1084
1085 ASSERT(vmbus_drv_obj->OnMsgDpc != NULL);
1086
1087 // Call to bus driver to handle interrupt
1088 vmbus_drv_obj->OnMsgDpc(&vmbus_drv_obj->Base);
1089
1090 DPRINT_EXIT(VMBUS_DRV);
1091}
1092
1093/*++
1094
1095Name: vmbus_msg_dpc()
1096
1097Desc: Tasklet routine to handle hypervisor events
1098
1099--*/
1100static void vmbus_event_dpc(unsigned long data)
1101{
1102 VMBUS_DRIVER_OBJECT* vmbus_drv_obj = (VMBUS_DRIVER_OBJECT*)data;
1103
1104 DPRINT_ENTER(VMBUS_DRV);
1105
1106 ASSERT(vmbus_drv_obj->OnEventDpc != NULL);
1107
1108 // Call to bus driver to handle interrupt
1109 vmbus_drv_obj->OnEventDpc(&vmbus_drv_obj->Base);
1110
1111 DPRINT_EXIT(VMBUS_DRV);
1112}
1113
1114/*++
1115
1116Name: vmbus_msg_dpc()
1117
1118Desc: ISR routine
1119
1120--*/
1121#ifdef KERNEL_2_6_27
1122static irqreturn_t vmbus_isr(int irq, void* dev_id)
1123#else
1124static int vmbus_isr(int irq, void* dev_id, struct pt_regs *regs)
1125#endif
1126{
1127 int ret=0;
1128 VMBUS_DRIVER_OBJECT* vmbus_driver_obj = &g_vmbus_drv.drv_obj;
1129
1130 DPRINT_ENTER(VMBUS_DRV);
1131
1132 ASSERT(vmbus_driver_obj->OnIsr != NULL);
1133
1134 // Call to bus driver to handle interrupt
1135 ret = vmbus_driver_obj->OnIsr(&vmbus_driver_obj->Base);
1136
1137 // Schedules a dpc if necessary
1138 if (ret > 0)
1139 {
1140 if (test_bit(0, (unsigned long*)&ret))
1141 {
1142 tasklet_schedule(&g_vmbus_drv.msg_dpc);
1143 }
1144
1145 if (test_bit(1, (unsigned long*)&ret))
1146 {
1147 tasklet_schedule(&g_vmbus_drv.event_dpc);
1148 }
1149
1150 DPRINT_EXIT(VMBUS_DRV);
1151 return IRQ_HANDLED;
1152 }
1153 else
1154 {
1155 DPRINT_EXIT(VMBUS_DRV);
1156 return IRQ_NONE;
1157 }
1158}
1159
1160MODULE_LICENSE("GPL");
1161
1162
1163/*++
1164
1165Name: vmbus_init()
1166
1167Desc: Main vmbus driver entry routine
1168
1169--*/
1170static int __init vmbus_init(void)
1171{
1172 int ret=0;
1173
1174 DPRINT_ENTER(VMBUS_DRV);
1175
1176 DPRINT_INFO(VMBUS_DRV,
1177 "Vmbus initializing.... current log level 0x%x (%x,%x)",
1178 vmbus_loglevel, HIWORD(vmbus_loglevel), LOWORD(vmbus_loglevel));
1179#ifdef KERNEL_2_6_27
1180//Todo: it is used for loglevel, to be ported to new kernel.
1181#else
1182 vmbus_ctl_table_hdr = register_sysctl_table(vmus_root_ctl_table, 0);
1183 if (!vmbus_ctl_table_hdr)
1184 {
1185 DPRINT_EXIT(VMBUS_DRV);
1186 return -ENOMEM;
1187 }
1188#endif
1189
1190 ret = vmbus_bus_init(VmbusInitialize);
1191
1192 DPRINT_EXIT(VMBUS_DRV);
1193 return ret;
1194}
1195
1196
1197
1198/*++
1199
1200Name: vmbus_init()
1201
1202Desc: Main vmbus driver exit routine
1203
1204--*/
1205static void __exit vmbus_exit(void)
1206{
1207 DPRINT_ENTER(VMBUS_DRV);
1208
1209 vmbus_bus_exit();
1210#ifdef KERNEL_2_6_27
1211//Todo: it is used for loglevel, to be ported to new kernel.
1212#else
1213 unregister_sysctl_table(vmbus_ctl_table_hdr);
1214#endif
1215 DPRINT_EXIT(VMBUS_DRV);
1216
1217 return;
1218}
1219
1220#if defined(KERNEL_2_6_5)
1221#else
1222module_param(vmbus_irq, int, S_IRUGO);
1223module_param(vmbus_loglevel, int, S_IRUGO);
1224#endif
1225
1226module_init(vmbus_init);
1227module_exit(vmbus_exit);
1228// eof