⛏️ index : haiku.git

/*
 * 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>


//#define TRACE_DRIVER
#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;
}


//	#pragma mark - Haiku Driver API


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;

		// We've found a driver; now try to reserve the device and store it
		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;

	// Always hold the giant lock during attach.
	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);
		}

		// some drivers expect probe() to be called before attach()
		// (i.e. they set driver softc in probe(), etc.)
		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;
}