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