xref: /haiku/src/libs/compat/freebsd_network/device.c (revision 909af08f4328301fbdef1ffb41f566c3b5bec0c7)
1 /*
2  * Copyright 2007, Hugo Santos, hugosantos@gmail.com. All Rights Reserved.
3  * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All Rights Reserved.
4  * Copyright 2004, Marcus Overhagen. All Rights Reserved.
5  *
6  * Distributed under the terms of the MIT License.
7  */
8 
9 
10 #include "device.h"
11 
12 #include <stdio.h>
13 
14 #include <KernelExport.h>
15 #include <image.h>
16 #include <kernel/heap.h>
17 
18 #include <compat/machine/resource.h>
19 #include <compat/dev/mii/mii.h>
20 #include <compat/sys/bus.h>
21 #include <compat/net/if_media.h>
22 
23 #include <compat/dev/mii/miivar.h>
24 
25 
26 spinlock __haiku_intr_spinlock;
27 
28 struct net_buffer_module_info *gBufferModule;
29 
30 static struct list sRootDevices;
31 static int sNextUnit;
32 
33 //	#pragma mark - private functions
34 
35 
36 static device_t
37 init_device(device_t device, driver_t *driver)
38 {
39 	list_init_etc(&device->children, offsetof(struct device, link));
40 	device->unit = sNextUnit++;
41 
42 	if (driver != NULL && device_set_driver(device, driver) < 0)
43 		return NULL;
44 
45 	return device;
46 }
47 
48 
49 static device_t
50 new_device(driver_t *driver)
51 {
52 	device_t dev = malloc(sizeof(struct device));
53 	if (dev == NULL)
54 		return NULL;
55 
56 	memset(dev, 0, sizeof(struct device));
57 
58 	if (init_device(dev, driver) == NULL) {
59 		free(dev);
60 		return NULL;
61 	}
62 
63 	return dev;
64 }
65 
66 
67 static image_id
68 find_own_image()
69 {
70 	int32 cookie = 0;
71 	image_info info;
72 	while (get_next_image_info(B_SYSTEM_TEAM, &cookie, &info) == B_OK) {
73 		if (((addr_t)info.text <= (addr_t)find_own_image
74 			&& (addr_t)info.text + (addr_t)info.text_size
75 				> (addr_t)find_own_image)) {
76 			// found our own image
77 			return info.id;
78 		}
79 	}
80 
81 	return B_ENTRY_NOT_FOUND;
82 }
83 
84 
85 static device_method_signature_t
86 resolve_method(driver_t *driver, const char *name)
87 {
88 	device_method_signature_t method = NULL;
89 	int i;
90 
91 	for (i = 0; method == NULL && driver->methods[i].name != NULL; i++) {
92 		if (strcmp(driver->methods[i].name, name) == 0)
93 			method = driver->methods[i].method;
94 	}
95 
96 	if (method == NULL)
97 		panic("resolve_method: method%s not found\n", name);
98 
99 	return method;
100 }
101 
102 
103 //	#pragma mark - Device
104 
105 
106 void
107 driver_printf(const char *format, ...)
108 {
109 	va_list vl;
110 	va_start(vl, format);
111 	driver_vprintf(format, vl);
112 	va_end(vl);
113 }
114 
115 
116 static int
117 driver_vprintf_etc(const char *extra, const char *format, va_list vl)
118 {
119 	char buf[256];
120 	int ret = vsnprintf(buf, sizeof(buf), format, vl);
121 
122 	if (extra)
123 		dprintf("[%s] (%s) %s", gDriverName, extra, buf);
124 	else
125 		dprintf("[%s] %s", gDriverName, buf);
126 
127 	return ret;
128 }
129 
130 
131 int
132 driver_vprintf(const char *format, va_list vl)
133 {
134 	return driver_vprintf_etc(NULL, format, vl);
135 }
136 
137 
138 int
139 device_printf(device_t dev, const char *format, ...)
140 {
141 	va_list vl;
142 
143 	va_start(vl, format);
144 	driver_vprintf_etc(dev->device_name, format, vl);
145 	va_end(vl);
146 	return 0;
147 }
148 
149 
150 void
151 device_set_desc(device_t dev, const char *desc)
152 {
153 	dev->description = desc;
154 }
155 
156 
157 void
158 device_set_desc_copy(device_t dev, const char *desc)
159 {
160 	dev->description = strdup(desc);
161 	dev->flags |= DEVICE_DESC_ALLOCED;
162 }
163 
164 
165 const char *
166 device_get_desc(device_t dev)
167 {
168 	return dev->description;
169 }
170 
171 
172 device_t
173 device_get_parent(device_t dev)
174 {
175 	return dev->parent;
176 }
177 
178 
179 devclass_t
180 device_get_devclass(device_t dev)
181 {
182 	// TODO find out what to do
183 	return 0;
184 }
185 
186 
187 int
188 device_get_children(device_t dev, device_t **devlistp, int *devcountp)
189 {
190 	int count;
191 	device_t child = NULL;
192 	device_t *list;
193 
194 	count = 0;
195 	while ((child = list_get_next_item(&dev->children, child)) != NULL) {
196 		count++;
197 	}
198 
199 	if (count == 0) {
200 		*devlistp = NULL;
201 		*devcountp = 0;
202 		return (0);
203 	}
204 
205 	list = malloc(count * sizeof(device_t));
206 	if (!list)
207 		return (ENOMEM);
208 
209 	count = 0;
210 	while ((child = list_get_next_item(&dev->children, child)) != NULL) {
211 		list[count] = child;
212 		count++;
213 	}
214 
215 	*devlistp = list;
216 	*devcountp = count;
217 
218 	return (0);
219 }
220 
221 
222 void
223 device_set_ivars(device_t dev, void *ivars)
224 {
225 	dev->ivars = ivars;
226 }
227 
228 
229 void *
230 device_get_ivars(device_t dev)
231 {
232 	return dev->ivars;
233 }
234 
235 
236 const char *
237 device_get_name(device_t dev)
238 {
239 	if (dev == NULL)
240 		return NULL;
241 
242 	return dev->device_name;
243 }
244 
245 
246 int
247 device_get_unit(device_t dev)
248 {
249 	return dev->unit;
250 }
251 
252 
253 const char *
254 device_get_nameunit(device_t dev)
255 {
256 	return dev->nameunit;
257 }
258 
259 
260 void *
261 device_get_softc(device_t dev)
262 {
263 	return dev->softc;
264 }
265 
266 
267 void
268 device_set_softc(device_t dev, void *softc)
269 {
270 	if (dev->softc == softc)
271 		return;
272 
273 	if ((dev->flags & DEVICE_SOFTC_SET) == 0) {
274 		// Not externally allocated. We own it so we must clean it up.
275 		free(dev->softc);
276 	}
277 
278 	dev->softc = softc;
279 	if (dev->softc != NULL)
280 		dev->flags |= DEVICE_SOFTC_SET;
281 	else
282 		dev->flags &= ~DEVICE_SOFTC_SET;
283 }
284 
285 
286 u_int32_t
287 device_get_flags(device_t dev)
288 {
289 	return dev->flags;
290 }
291 
292 
293 int
294 device_set_driver(device_t dev, driver_t *driver)
295 {
296 	int i;
297 
298 	dev->softc = malloc(driver->size);
299 	if (dev->softc == NULL)
300 		return -1;
301 
302 	memset(dev->softc, 0, driver->size);
303 	dev->driver = driver;
304 
305 	for (i = 0; driver->methods[i].name != NULL; i++) {
306 		device_method_t *mth = &driver->methods[i];
307 
308 		if (strcmp(mth->name, "device_register") == 0)
309 			dev->methods.device_register = (void *)mth->method;
310 		else if (strcmp(mth->name, "device_probe") == 0)
311 			dev->methods.probe = (void *)mth->method;
312 		else if (strcmp(mth->name, "device_attach") == 0)
313 			dev->methods.attach = (void *)mth->method;
314 		else if (strcmp(mth->name, "device_detach") == 0)
315 			dev->methods.detach = (void *)mth->method;
316 		else if (strcmp(mth->name, "device_suspend") == 0)
317 			dev->methods.suspend = (void *)mth->method;
318 		else if (strcmp(mth->name, "device_resume") == 0)
319 			dev->methods.resume = (void *)mth->method;
320 		else if (strcmp(mth->name, "device_shutdown") == 0)
321 			dev->methods.shutdown = (void *)mth->method;
322 		else if (strcmp(mth->name, "miibus_readreg") == 0)
323 			dev->methods.miibus_readreg = (void *)mth->method;
324 		else if (strcmp(mth->name, "miibus_writereg") == 0)
325 			dev->methods.miibus_writereg = (void *)mth->method;
326 		else if (strcmp(mth->name, "miibus_statchg") == 0)
327 			dev->methods.miibus_statchg = (void *)mth->method;
328 		else if (!strcmp(mth->name, "miibus_linkchg"))
329 			dev->methods.miibus_linkchg = (void *)mth->method;
330 		else if (!strcmp(mth->name, "miibus_mediainit"))
331 			dev->methods.miibus_mediainit = (void *)mth->method;
332 		else if (!strcmp(mth->name, "bus_child_location_str"))
333 			dev->methods.bus_child_location_str = (void *)mth->method;
334 		else if (!strcmp(mth->name, "bus_child_pnpinfo_str"))
335 			dev->methods.bus_child_pnpinfo_str = (void *)mth->method;
336 		else if (!strcmp(mth->name, "bus_hinted_child"))
337 			dev->methods.bus_hinted_child = (void *)mth->method;
338 		else if (!strcmp(mth->name, "bus_print_child"))
339 			dev->methods.bus_print_child = (void *)mth->method;
340 		else if (!strcmp(mth->name, "bus_read_ivar"))
341 			dev->methods.bus_read_ivar = (void *)mth->method;
342 		else if (!strcmp(mth->name, "bus_get_dma_tag"))
343 			dev->methods.bus_get_dma_tag = (void *)mth->method;
344 		else
345 			panic("device_set_driver: method %s not found\n", mth->name);
346 
347 	}
348 
349 	return 0;
350 }
351 
352 
353 int
354 device_is_alive(device_t device)
355 {
356 	return (device->flags & DEVICE_ATTACHED) != 0;
357 }
358 
359 
360 device_t
361 device_add_child_driver(device_t parent, const char* name, driver_t* _driver,
362 	int unit)
363 {
364 	device_t child = NULL;
365 
366 	if (_driver == NULL && name != NULL) {
367 		if (strcmp(name, "miibus") == 0)
368 			child = new_device(&miibus_driver);
369 		else {
370 			// find matching driver structure
371 			driver_t** driver;
372 			char symbol[128];
373 
374 			snprintf(symbol, sizeof(symbol), "__fbsd_%s_%s", name,
375 				parent->driver->name);
376 			if (get_image_symbol(find_own_image(), symbol, B_SYMBOL_TYPE_DATA,
377 					(void**)&driver) == B_OK) {
378 				child = new_device(*driver);
379 			} else
380 				device_printf(parent, "couldn't find symbol %s\n", symbol);
381 		}
382 	} else if (_driver != NULL) {
383 		child = new_device(_driver);
384 	} else
385 		child = new_device(NULL);
386 
387 	if (child == NULL)
388 		return NULL;
389 
390 	if (name != NULL)
391 		strlcpy(child->device_name, name, sizeof(child->device_name));
392 
393 	child->parent = parent;
394 
395 	if (parent != NULL) {
396 		list_add_item(&parent->children, child);
397 		child->root = parent->root;
398 	} else {
399 		if (sRootDevices.link.next == NULL)
400 			list_init_etc(&sRootDevices, offsetof(struct device, link));
401 		list_add_item(&sRootDevices, child);
402 	}
403 
404 	return child;
405 }
406 
407 
408 device_t
409 device_add_child(device_t parent, const char* name, int unit)
410 {
411 	return device_add_child_driver(parent, name, NULL, unit);
412 }
413 
414 
415 /*!	Delete the child and all of its children. Detach as necessary.
416 */
417 int
418 device_delete_child(device_t parent, device_t child)
419 {
420 	int status;
421 
422 	if (child == NULL)
423 		return 0;
424 
425 	if (parent != NULL)
426 		list_remove_item(&parent->children, child);
427 	else
428 		list_remove_item(&sRootDevices, child);
429 
430 	// We differentiate from the FreeBSD logic here - it will first delete
431 	// the children, and will then detach the device.
432 	// This has the problem that you cannot safely call device_delete_child()
433 	// as you don't know if one of the children deletes its own children this
434 	// way when it is detached.
435 	// Therefore, we'll detach first, and then delete whatever is left.
436 
437 	parent = child;
438 	child = NULL;
439 
440 	// detach children
441 	while ((child = list_get_next_item(&parent->children, child)) != NULL) {
442 		device_detach(child);
443 	}
444 
445 	// detach device
446 	status = device_detach(parent);
447 	if (status != 0)
448 		return status;
449 
450 	// delete children
451 	while ((child = list_get_first_item(&parent->children)) != NULL) {
452 		device_delete_child(parent, child);
453 	}
454 
455 	// delete device
456 	if (parent->flags & DEVICE_DESC_ALLOCED)
457 		free((char *)parent->description);
458 
459 	// Delete softc if we were the ones to allocate it.
460 	if ((parent->flags & DEVICE_SOFTC_SET) == 0)
461 		free(parent->softc);
462 
463 	free(parent);
464 	return 0;
465 }
466 
467 
468 int
469 device_is_attached(device_t device)
470 {
471 	return (device->flags & DEVICE_ATTACHED) != 0;
472 }
473 
474 
475 int
476 device_attach(device_t device)
477 {
478 	int result;
479 
480 	if (device->driver == NULL
481 		|| device->methods.attach == NULL)
482 		return B_ERROR;
483 
484 	result = device->methods.attach(device);
485 
486 	if (result == 0)
487 		atomic_or(&device->flags, DEVICE_ATTACHED);
488 
489 	if (result == 0 && HAIKU_DRIVER_REQUIRES(FBSD_WLAN_FEATURE))
490 		result = start_wlan(device);
491 
492 	return result;
493 }
494 
495 
496 int
497 device_detach(device_t device)
498 {
499 	if (device->driver == NULL)
500 		return B_ERROR;
501 
502 	if ((atomic_and(&device->flags, ~DEVICE_ATTACHED) & DEVICE_ATTACHED) != 0
503 			&& device->methods.detach != NULL) {
504 		int result = 0;
505 		if (HAIKU_DRIVER_REQUIRES(FBSD_WLAN_FEATURE))
506 			result = stop_wlan(device);
507 		if (result != 0 && result != B_BAD_VALUE) {
508 			atomic_or(&device->flags, DEVICE_ATTACHED);
509 			return result;
510 		}
511 
512 		result = device->methods.detach(device);
513 		if (result != 0) {
514 			atomic_or(&device->flags, DEVICE_ATTACHED);
515 			return result;
516 		}
517 	}
518 
519 	return 0;
520 }
521 
522 
523 int
524 bus_generic_attach(device_t dev)
525 {
526 	device_t child = NULL;
527 
528 	while ((child = list_get_next_item(&dev->children, child)) != NULL) {
529 		if (child->driver == NULL) {
530 			driver_t *driver = __haiku_select_miibus_driver(child);
531 			if (driver == NULL) {
532 				struct mii_attach_args *ma = device_get_ivars(child);
533 
534 				device_printf(dev, "No PHY module found (%x/%x)!\n",
535 					MII_OUI(ma->mii_id1, ma->mii_id2), MII_MODEL(ma->mii_id2));
536 			} else
537 				device_set_driver(child, driver);
538 		} else
539 			child->methods.probe(child);
540 
541 		if (child->driver != NULL) {
542 			int result = device_attach(child);
543 			if (result != 0)
544 				return result;
545 		}
546 	}
547 
548 	return 0;
549 }
550 
551 
552 int
553 bus_generic_detach(device_t device)
554 {
555 	device_t child = NULL;
556 
557 	if ((device->flags & DEVICE_ATTACHED) == 0)
558 		return B_ERROR;
559 
560 	while (true) {
561 		child = list_get_next_item(&device->children, child);
562 		if (child == NULL)
563 			break;
564 
565 		device_detach(child);
566 	}
567 
568 	return 0;
569 }
570 
571 
572 //	#pragma mark - Misc, Malloc
573 
574 
575 device_t
576 find_root_device(int unit)
577 {
578 	device_t device = NULL;
579 
580 	while ((device = list_get_next_item(&sRootDevices, device)) != NULL) {
581 		if (device->unit <= unit)
582 			return device;
583 	}
584 
585 	return NULL;
586 }
587 
588 
589 driver_t *
590 __haiku_probe_miibus(device_t dev, driver_t *drivers[])
591 {
592 	driver_t *selected = NULL;
593 	int i, selectedResult = 0;
594 
595 	if (drivers == NULL)
596 		return NULL;
597 
598 	for (i = 0; drivers[i]; i++) {
599 		device_probe_t *probe = (device_probe_t *)
600 			resolve_method(drivers[i], "device_probe");
601 		if (probe) {
602 			int result = probe(dev);
603 			if (result >= 0) {
604 				if (selected == NULL || result < selectedResult) {
605 					selected = drivers[i];
606 					selectedResult = result;
607 					device_printf(dev, "Found MII: %s\n", selected->name);
608 				}
609 			}
610 		}
611 	}
612 
613 	return selected;
614 }
615 
616 
617 int
618 printf(const char *format, ...)
619 {
620 	char buf[256];
621 	va_list vl;
622 	va_start(vl, format);
623 	vsnprintf(buf, sizeof(buf), format, vl);
624 	va_end(vl);
625 	dprintf("%s", buf);
626 
627 	return 0;
628 }
629 
630 
631 int
632 resource_int_value(const char *name, int unit, const char *resname,
633 	int *result)
634 {
635 	/* no support for hints */
636 	return -1;
637 }
638 
639 
640 int
641 resource_disabled(const char *name, int unit)
642 {
643 	int error, value;
644 
645 	error = resource_int_value(name, unit, "disabled", &value);
646 	if (error)
647 	       return (0);
648 	return (value);
649 }
650