diff options
Diffstat (limited to 'drivers/staging/hv/vmbus_drv.c')
-rw-r--r-- | drivers/staging/hv/vmbus_drv.c | 1228 |
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 | ||
49 | struct 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 | // | ||
67 | static int vmbus_match(struct device *device, struct device_driver *driver); | ||
68 | static int vmbus_probe(struct device *device); | ||
69 | static int vmbus_remove(struct device *device); | ||
70 | static 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) | ||
73 | static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env); | ||
74 | #else | ||
75 | static int vmbus_uevent(struct device *device, char **envp, int num_envp, char *buffer, int buffer_size); | ||
76 | #endif | ||
77 | static void vmbus_msg_dpc(unsigned long data); | ||
78 | static void vmbus_event_dpc(unsigned long data); | ||
79 | |||
80 | #ifdef KERNEL_2_6_27 | ||
81 | static irqreturn_t vmbus_isr(int irq, void* dev_id); | ||
82 | #else | ||
83 | static int vmbus_isr(int irq, void* dev_id, struct pt_regs *regs); | ||
84 | #endif | ||
85 | |||
86 | static void vmbus_device_release(struct device *device); | ||
87 | static void vmbus_bus_release(struct device *device); | ||
88 | |||
89 | static DEVICE_OBJECT* vmbus_child_device_create(GUID type, GUID instance, void* context); | ||
90 | static void vmbus_child_device_destroy(DEVICE_OBJECT* device_obj); | ||
91 | static int vmbus_child_device_register(DEVICE_OBJECT* root_device_obj, DEVICE_OBJECT* child_device_obj); | ||
92 | static void vmbus_child_device_unregister(DEVICE_OBJECT* child_device_obj); | ||
93 | static 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 | |||
98 | static 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); | ||
108 | unsigned int vmbus_loglevel= (ALL_MODULES << 16 | INFO_LVL); | ||
109 | EXPORT_SYMBOL(vmbus_loglevel); | ||
110 | |||
111 | static 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 | ||
115 | static struct ctl_table_header *vmbus_ctl_table_hdr; | ||
116 | |||
117 | static 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 | |||
127 | static 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 | |||
135 | static 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 | // | ||
148 | static 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 | ||
179 | static 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 | |||
199 | Name: vmbus_show_device_attr() | ||
200 | |||
201 | Desc: Show the device attribute in sysfs. This is invoked when user does a "cat /sys/bus/vmbus/devices/<bus device>/<attr name>" | ||
202 | |||
203 | --*/ | ||
204 | static 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 | |||
314 | Name: vmbus_show_class_id() | ||
315 | |||
316 | Desc: 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 | |||
331 | Name: vmbus_show_device_id() | ||
332 | |||
333 | Desc: 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 | |||
348 | Name: vmbus_bus_init() | ||
349 | |||
350 | Desc: 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 | --*/ | ||
359 | int 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 | |||
470 | cleanup: | ||
471 | DPRINT_EXIT(VMBUS_DRV); | ||
472 | |||
473 | return ret; | ||
474 | } | ||
475 | |||
476 | |||
477 | /*++ | ||
478 | |||
479 | Name: vmbus_bus_exit() | ||
480 | |||
481 | Desc: Terminate the vmbus driver. This routine is opposite of vmbus_bus_init() | ||
482 | |||
483 | --*/ | ||
484 | void 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 | |||
517 | Name: vmbus_child_driver_register() | ||
518 | |||
519 | Desc: Register a vmbus's child driver | ||
520 | |||
521 | --*/ | ||
522 | void 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 | |||
540 | EXPORT_SYMBOL(vmbus_child_driver_register); | ||
541 | |||
542 | /*++ | ||
543 | |||
544 | Name: vmbus_child_driver_unregister() | ||
545 | |||
546 | Desc: Unregister a vmbus's child driver | ||
547 | |||
548 | --*/ | ||
549 | void 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 | |||
562 | EXPORT_SYMBOL(vmbus_child_driver_unregister); | ||
563 | |||
564 | /*++ | ||
565 | |||
566 | Name: vmbus_get_interface() | ||
567 | |||
568 | Desc: Get the vmbus channel interface. This is invoked by child/client driver that sits | ||
569 | above vmbus | ||
570 | --*/ | ||
571 | void 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 | |||
578 | EXPORT_SYMBOL(vmbus_get_interface); | ||
579 | |||
580 | |||
581 | /*++ | ||
582 | |||
583 | Name: vmbus_child_device_get_info() | ||
584 | |||
585 | Desc: Get the vmbus child device info. This is invoked to display various device attributes in sysfs. | ||
586 | --*/ | ||
587 | static 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 | |||
597 | Name: vmbus_child_device_create() | ||
598 | |||
599 | Desc: Creates and registers a new child device on the vmbus. | ||
600 | |||
601 | --*/ | ||
602 | static 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 | |||
641 | Name: vmbus_child_device_register() | ||
642 | |||
643 | Desc: Register the child device on the specified bus | ||
644 | |||
645 | --*/ | ||
646 | static 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 | |||
687 | Cleanup: | ||
688 | DPRINT_EXIT(VMBUS_DRV); | ||
689 | |||
690 | return ret; | ||
691 | } | ||
692 | |||
693 | /*++ | ||
694 | |||
695 | Name: vmbus_child_device_unregister() | ||
696 | |||
697 | Desc: Remove the specified child device from the vmbus. | ||
698 | |||
699 | --*/ | ||
700 | static 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 | |||
720 | Name: vmbus_child_device_destroy() | ||
721 | |||
722 | Desc: Destroy the specified child device on the vmbus. | ||
723 | |||
724 | --*/ | ||
725 | static 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 | |||
734 | Name: vmbus_uevent() | ||
735 | |||
736 | Desc: 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) | ||
742 | static 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 | ||
794 | static 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 | |||
846 | Name: vmbus_match() | ||
847 | |||
848 | Desc: Attempt to match the specified device to the specified driver | ||
849 | |||
850 | --*/ | ||
851 | static 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 | |||
879 | Name: vmbus_probe_failed_cb() | ||
880 | |||
881 | Desc: 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 | ||
887 | static void vmbus_probe_failed_cb(struct work_struct *context) | ||
888 | #else | ||
889 | static 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 | |||
908 | Name: vmbus_probe() | ||
909 | |||
910 | Desc: Add the new vmbus's child device | ||
911 | |||
912 | --*/ | ||
913 | static 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 | |||
950 | Name: vmbus_remove() | ||
951 | |||
952 | Desc: Remove a vmbus device | ||
953 | |||
954 | --*/ | ||
955 | static 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 | |||
997 | Name: vmbus_shutdown() | ||
998 | |||
999 | Desc: Shutdown a vmbus device | ||
1000 | |||
1001 | --*/ | ||
1002 | static 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 | |||
1038 | Name: vmbus_bus_release() | ||
1039 | |||
1040 | Desc: Final callback release of the vmbus root device | ||
1041 | |||
1042 | --*/ | ||
1043 | static void vmbus_bus_release(struct device *device) | ||
1044 | { | ||
1045 | DPRINT_ENTER(VMBUS_DRV); | ||
1046 | DPRINT_EXIT(VMBUS_DRV); | ||
1047 | } | ||
1048 | |||
1049 | /*++ | ||
1050 | |||
1051 | Name: vmbus_device_release() | ||
1052 | |||
1053 | Desc: Final callback release of the vmbus child device | ||
1054 | |||
1055 | --*/ | ||
1056 | static 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 | |||
1074 | Name: vmbus_msg_dpc() | ||
1075 | |||
1076 | Desc: Tasklet routine to handle hypervisor messages | ||
1077 | |||
1078 | --*/ | ||
1079 | static 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 | |||
1095 | Name: vmbus_msg_dpc() | ||
1096 | |||
1097 | Desc: Tasklet routine to handle hypervisor events | ||
1098 | |||
1099 | --*/ | ||
1100 | static 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 | |||
1116 | Name: vmbus_msg_dpc() | ||
1117 | |||
1118 | Desc: ISR routine | ||
1119 | |||
1120 | --*/ | ||
1121 | #ifdef KERNEL_2_6_27 | ||
1122 | static irqreturn_t vmbus_isr(int irq, void* dev_id) | ||
1123 | #else | ||
1124 | static 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 | |||
1160 | MODULE_LICENSE("GPL"); | ||
1161 | |||
1162 | |||
1163 | /*++ | ||
1164 | |||
1165 | Name: vmbus_init() | ||
1166 | |||
1167 | Desc: Main vmbus driver entry routine | ||
1168 | |||
1169 | --*/ | ||
1170 | static 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 | |||
1200 | Name: vmbus_init() | ||
1201 | |||
1202 | Desc: Main vmbus driver exit routine | ||
1203 | |||
1204 | --*/ | ||
1205 | static 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 | ||
1222 | module_param(vmbus_irq, int, S_IRUGO); | ||
1223 | module_param(vmbus_loglevel, int, S_IRUGO); | ||
1224 | #endif | ||
1225 | |||
1226 | module_init(vmbus_init); | ||
1227 | module_exit(vmbus_exit); | ||
1228 | // eof | ||