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
init_device(device_t device,driver_t * driver)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
new_device(driver_t * driver)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
find_own_image()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
resolve_method(driver_t * driver,const char * name)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
driver_printf(const char * format,...)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
driver_vprintf_etc(const char * extra,const char * format,va_list vl)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
driver_vprintf(const char * format,va_list vl)132 driver_vprintf(const char *format, va_list vl)
133 {
134 return driver_vprintf_etc(NULL, format, vl);
135 }
136
137
138 int
device_printf(device_t dev,const char * format,...)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
device_set_desc(device_t dev,const char * desc)151 device_set_desc(device_t dev, const char *desc)
152 {
153 dev->description = desc;
154 }
155
156
157 void
device_set_desc_copy(device_t dev,const char * desc)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 *
device_get_desc(device_t dev)166 device_get_desc(device_t dev)
167 {
168 return dev->description;
169 }
170
171
172 device_t
device_get_parent(device_t dev)173 device_get_parent(device_t dev)
174 {
175 return dev->parent;
176 }
177
178
179 devclass_t
device_get_devclass(device_t dev)180 device_get_devclass(device_t dev)
181 {
182 // TODO find out what to do
183 return 0;
184 }
185
186
187 int
device_get_children(device_t dev,device_t ** devlistp,int * devcountp)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
device_set_ivars(device_t dev,void * ivars)223 device_set_ivars(device_t dev, void *ivars)
224 {
225 dev->ivars = ivars;
226 }
227
228
229 void *
device_get_ivars(device_t dev)230 device_get_ivars(device_t dev)
231 {
232 return dev->ivars;
233 }
234
235
236 const char *
device_get_name(device_t dev)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
device_get_unit(device_t dev)247 device_get_unit(device_t dev)
248 {
249 return dev->unit;
250 }
251
252
253 const char *
device_get_nameunit(device_t dev)254 device_get_nameunit(device_t dev)
255 {
256 return dev->nameunit;
257 }
258
259
260 void *
device_get_softc(device_t dev)261 device_get_softc(device_t dev)
262 {
263 return dev->softc;
264 }
265
266
267 void
device_set_softc(device_t dev,void * softc)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
device_get_flags(device_t dev)287 device_get_flags(device_t dev)
288 {
289 return dev->flags;
290 }
291
292
293 int
device_set_driver(device_t dev,driver_t * driver)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
device_is_alive(device_t device)354 device_is_alive(device_t device)
355 {
356 return (device->flags & DEVICE_ATTACHED) != 0;
357 }
358
359
360 device_t
device_add_child_driver(device_t parent,const char * name,driver_t * _driver,int unit)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
device_add_child(device_t parent,const char * name,int unit)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
device_delete_child(device_t parent,device_t child)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
device_is_attached(device_t device)469 device_is_attached(device_t device)
470 {
471 return (device->flags & DEVICE_ATTACHED) != 0;
472 }
473
474
475 int
device_attach(device_t device)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
device_detach(device_t device)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
bus_generic_attach(device_t dev)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
bus_generic_detach(device_t device)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
find_root_device(int unit)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 *
__haiku_probe_miibus(device_t dev,driver_t * drivers[])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
printf(const char * format,...)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
resource_int_value(const char * name,int unit,const char * resname,int * result)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
resource_disabled(const char * name,int unit)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