* Copyright 2007-2010, Axel Dörfler. All rights reserved.
* Copyright 2009-2022, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include "device.h"
#include "sysinit.h"
#include <stdlib.h>
#include <sys/sockio.h>
#include <Drivers.h>
#include <ether_driver.h>
#include <compat/sys/haiku-module.h>
#include <compat/sys/bus.h>
#include <compat/sys/mbuf.h>
#include <compat/net/ethernet.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usb_device.h>
#ifdef TRACE_DRIVER
# define TRACE(x) dprintf x
#else
# define TRACE(x)
#endif
static struct {
driver_t* driver;
int bus;
struct pci_info pci_info;
void* compat_device;
void (*prepare_attach)(void*, device_t);
void (*free_compat_device)(void*);
} sProbedDevices[MAX_DEVICES];
const char* gDeviceNameList[MAX_DEVICES + 1];
struct ifnet* gDevices[MAX_DEVICES];
int32 gDeviceCount;
status_t
init_root_device(device_t *_root, int bus_type)
{
static driver_t sRootDriverPCI = {
"pci",
NULL,
sizeof(struct root_device_softc),
};
static driver_t sRootDriverUSB = {
"uhub",
NULL,
sizeof(struct root_device_softc),
};
device_t root = device_add_child(NULL, NULL, 0);
if (root == NULL)
return B_NO_MEMORY;
root->softc = malloc(sizeof(struct root_device_softc));
if (root->softc == NULL) {
device_delete_child(NULL, root);
return B_NO_MEMORY;
}
bzero(root->softc, sizeof(struct root_device_softc));
if (bus_type == BUS_pci)
root->driver = &sRootDriverPCI;
else if (bus_type == BUS_uhub)
root->driver = &sRootDriverUSB;
else
panic("unknown bus type");
((struct root_device_softc*)root->softc)->bus = bus_type;
root->root = root;
if (_root != NULL)
*_root = root;
return B_OK;
}
static status_t
add_child_device(driver_t* driver, device_t root, device_t* _child)
{
device_t child = device_add_child(root, NULL, 0);
if (child == NULL)
return B_ERROR;
strlcpy(child->device_name, driver->name, sizeof(child->device_name));
if (device_set_driver(child, driver) != 0)
return B_ERROR;
if (_child != NULL)
*_child = child;
return B_OK;
}
static void
uninit_probed_devices()
{
for (int p = 0; sProbedDevices[p].bus != BUS_INVALID; p++) {
if (sProbedDevices[p].bus == BUS_pci) {
gPci->unreserve_device(sProbedDevices[p].pci_info.bus,
sProbedDevices[p].pci_info.device, sProbedDevices[p].pci_info.function,
gDriverName, NULL);
} else {
sProbedDevices[p].free_compat_device(sProbedDevices[p].compat_device);
}
}
}
void
report_probed_device(int bus, void* compat_device, driver_t* driver,
void (*prepare_attach)(void*, device_t), void (*free_compat_device)(void*))
{
int p = 0;
while (sProbedDevices[p].bus != BUS_INVALID && p < MAX_DEVICES)
p++;
if (p == MAX_DEVICES) {
free_compat_device(compat_device);
return;
}
sProbedDevices[p].bus = bus;
sProbedDevices[p].driver = driver;
sProbedDevices[p].compat_device = compat_device;
sProbedDevices[p].prepare_attach = prepare_attach;
sProbedDevices[p].free_compat_device = free_compat_device;
if ((p + 1) < MAX_DEVICES)
sProbedDevices[p + 1].bus = BUS_INVALID;
}
status_t
_fbsd_init_hardware_pci(driver_t* drivers[])
{
status_t status;
int i = 0;
pci_info* info;
device_t root;
int p = 0;
while (sProbedDevices[p].bus != BUS_INVALID)
p++;
status = init_pci();
if (status != B_OK)
return status;
status = init_root_device(&root, BUS_pci);
if (status != B_OK)
return status;
bool found = false;
for (info = get_device_pci_info(root); gPci->get_nth_pci_info(i, info) == B_OK; i++) {
int best = 0;
driver_t* driver = NULL;
struct device device = {};
device.parent = root;
device.root = root;
driver = __haiku_probe_drivers(&device, drivers);
if (driver == NULL)
continue;
if (gPci->reserve_device(info->bus, info->device, info->function,
gDriverName, NULL) != B_OK) {
dprintf("%s: Failed to reserve PCI:%d:%d:%d\n",
gDriverName, info->bus, info->device, info->function);
continue;
}
sProbedDevices[p].bus = BUS_pci;
sProbedDevices[p].driver = driver;
sProbedDevices[p].pci_info = *info;
found = true;
p++;
}
sProbedDevices[p].bus = BUS_INVALID;
device_delete_child(NULL, root);
if (found)
return B_OK;
uninit_pci();
return B_NOT_SUPPORTED;
}
status_t
_fbsd_init_hardware()
{
sProbedDevices[0].bus = BUS_INVALID;
__haiku_init_hardware();
return (sProbedDevices[0].bus != BUS_INVALID) ? B_OK : B_NOT_SUPPORTED;
}
status_t
_fbsd_init_drivers()
{
status_t status = init_mutexes();
if (status < B_OK)
goto err2;
status = init_mbufs();
if (status < B_OK)
goto err3;
status = init_callout();
if (status < B_OK)
goto err4;
if (HAIKU_DRIVER_REQUIRES(FBSD_TASKQUEUES)) {
status = init_taskqueues();
if (status < B_OK)
goto err5;
}
init_sysinit();
status = init_wlan_stack();
if (status < B_OK)
goto err6;
mtx_lock(&Giant);
for (int p = 0; sProbedDevices[p].bus != BUS_INVALID; p++) {
device_t root, device = NULL;
status = init_root_device(&root, sProbedDevices[p].bus);
if (status != B_OK)
break;
status = add_child_device(sProbedDevices[p].driver, root, &device);
if (status != B_OK)
break;
if (sProbedDevices[p].bus == BUS_pci) {
pci_info* info = get_device_pci_info(root);
*info = sProbedDevices[p].pci_info;
} else {
sProbedDevices[p].prepare_attach(sProbedDevices[p].compat_device, device);
}
if (device->methods.device_probe(device) >= 0
&& device_attach(device) == 0) {
dprintf("%s: init_driver(%p)\n", gDriverName,
sProbedDevices[p].driver);
} else
device_delete_child(NULL, root);
}
mtx_unlock(&Giant);
if (gDeviceCount > 0)
return B_OK;
if (status == B_OK)
status = B_ERROR;
err7:
uninit_wlan_stack();
err6:
uninit_sysinit();
if (HAIKU_DRIVER_REQUIRES(FBSD_TASKQUEUES))
uninit_taskqueues();
err5:
uninit_callout();
err4:
uninit_mbufs();
err3:
uninit_mutexes();
err2:
uninit_probed_devices();
if (uninit_usb != NULL)
uninit_usb();
uninit_pci();
return status;
}
status_t
_fbsd_uninit_drivers()
{
for (int i = 0; i < gDeviceCount; i++)
device_delete_child(NULL, gDevices[i]->root_device);
uninit_wlan_stack();
uninit_sysinit();
if (HAIKU_DRIVER_REQUIRES(FBSD_TASKQUEUES))
uninit_taskqueues();
uninit_callout();
uninit_mbufs();
uninit_mutexes();
uninit_probed_devices();
if (uninit_usb != NULL)
uninit_usb();
uninit_pci();
return B_OK;
}