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
26spinlock __haiku_intr_spinlock;
27
28struct net_stack_module_info *gStack;
29
30static struct list sRootDevices;
31static int sNextUnit;
32
33//	#pragma mark - private functions
34
35
36static device_t
37init_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
49static device_t
50new_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
67static image_id
68find_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
85static device_method_signature_t
86resolve_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
106void
107driver_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
116static int
117driver_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
131int
132driver_vprintf(const char *format, va_list vl)
133{
134	return driver_vprintf_etc(NULL, format, vl);
135}
136
137
138int
139device_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
150void
151device_set_desc(device_t dev, const char *desc)
152{
153	dev->description = desc;
154}
155
156
157void
158device_set_desc_copy(device_t dev, const char *desc)
159{
160	dev->description = strdup(desc);
161	dev->flags |= DEVICE_DESC_ALLOCED;
162}
163
164
165const char *
166device_get_desc(device_t dev)
167{
168	return dev->description;
169}
170
171
172device_t
173device_get_parent(device_t dev)
174{
175	return dev->parent;
176}
177
178
179devclass_t
180device_get_devclass(device_t dev)
181{
182	// TODO find out what to do
183	return 0;
184}
185
186
187int
188device_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
222void
223device_set_ivars(device_t dev, void *ivars)
224{
225	dev->ivars = ivars;
226}
227
228
229void *
230device_get_ivars(device_t dev)
231{
232	return dev->ivars;
233}
234
235
236const char *
237device_get_name(device_t dev)
238{
239	if (dev == NULL)
240		return NULL;
241
242	return dev->device_name;
243}
244
245
246int
247device_get_unit(device_t dev)
248{
249	return dev->unit;
250}
251
252
253const char *
254device_get_nameunit(device_t dev)
255{
256	return dev->nameunit;
257}
258
259
260void *
261device_get_softc(device_t dev)
262{
263	return dev->softc;
264}
265
266
267void
268device_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
286u_int32_t
287device_get_flags(device_t dev)
288{
289	return dev->flags;
290}
291
292
293int
294device_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
353int
354device_is_alive(device_t device)
355{
356	return (device->flags & DEVICE_ATTACHED) != 0;
357}
358
359
360device_t
361device_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
408device_t
409device_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*/
417int
418device_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
468int
469device_is_attached(device_t device)
470{
471	return (device->flags & DEVICE_ATTACHED) != 0;
472}
473
474
475int
476device_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
496int
497device_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
523int
524bus_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
552int
553bus_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
575device_t
576find_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
589driver_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
617int
618printf(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
631int
632resource_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
640int
641resource_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