david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[linux] Add device and driver model

Add the base to build linux drivers and the linux UI code on.  UI
fills device requests, which are later walked over by the linux
root_driver and delegated to specific linux drivers.

Signed-off-by: Piotr Jaroszyński <p.jaroszynski@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Piotr Jaroszyński 2010-05-31 18:50:11 +02:00 committed by Michael Brown
parent aacd1d62fb
commit 91fb434bda
4 changed files with 298 additions and 0 deletions

View File

@ -73,6 +73,7 @@ SRCDIRS += drivers/block
SRCDIRS += drivers/nvs
SRCDIRS += drivers/bitbash
SRCDIRS += drivers/infiniband
SRCDIRS += drivers/linux
SRCDIRS += interface/pxe interface/efi interface/smbios interface/linux
SRCDIRS += tests
SRCDIRS += crypto crypto/axtls crypto/matrixssl

152
src/drivers/linux/linux.c Normal file
View File

@ -0,0 +1,152 @@
/*
* Copyright (C) 2010 Piotr Jaroszyński <p.jaroszynski@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*/
FILE_LICENCE(GPL2_OR_LATER);
/** @file
*
* Linux root_device and root_driver.
*/
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <ipxe/linux.h>
#include <ipxe/malloc.h>
#include <ipxe/settings.h>
LIST_HEAD(linux_device_requests);
LIST_HEAD(linux_global_settings);
/** Go over the device requests looking for a matching linux driver to handle them. */
static int linux_probe(struct root_device *rootdev)
{
struct linux_device_request *request;
struct linux_driver *driver;
struct linux_device *device = NULL;
int rc;
/* Apply global settings */
linux_apply_settings(&linux_global_settings, NULL);
list_for_each_entry(request, &linux_device_requests, list) {
if (! device)
device = zalloc(sizeof(*device));
if (! device)
return -ENOMEM;
rc = 1;
for_each_table_entry(driver, LINUX_DRIVERS) {
if ((rc = strcmp(driver->name, request->driver)) == 0)
break;
}
if (rc != 0) {
printf("Linux driver '%s' not found\n", request->driver);
continue;
}
if (! driver->can_probe) {
printf("Driver '%s' cannot handle any more devices\n", driver->name);
continue;
}
/* We found a matching driver so add the device to the hierarchy */
list_add(&device->dev.siblings, &rootdev->dev.children);
device->dev.parent = &rootdev->dev;
INIT_LIST_HEAD(&device->dev.children);
if (driver->probe(device, request) == 0) {
device->driver = driver;
/* Driver handled the device so release ownership */
device = NULL;
} else {
/* Driver failed to handle the device so remove it from the hierarchy
* and reuse the object */
list_del(&device->dev.siblings);
}
};
free(device);
return 0;
}
/** Remove all the linux devices registered in probe() */
static void linux_remove(struct root_device *rootdev)
{
struct linux_device *device;
struct linux_device *tmp;
list_for_each_entry_safe(device, tmp, &rootdev->dev.children, dev.siblings) {
list_del(&device->dev.siblings);
device->driver->remove(device);
free(device);
}
}
/** Linux root driver */
static struct root_driver linux_root_driver = {
.probe = linux_probe,
.remove = linux_remove,
};
/** Linux root device */
struct root_device linux_root_device __root_device = {
.dev = { .name = "linux" },
.driver = &linux_root_driver,
};
struct linux_setting *linux_find_setting(char *name, struct list_head *settings)
{
struct linux_setting *setting;
struct linux_setting *result = NULL;
/* Find the last occurrence of a setting with the specified name */
list_for_each_entry(setting, settings, list) {
if (strcmp(setting->name, name) == 0) {
result = setting;
}
}
return result;
}
void linux_apply_settings(struct list_head *new_settings, struct settings *settings_block)
{
struct linux_setting *setting;
int rc;
list_for_each_entry(setting, new_settings, list) {
/* Skip already applied settings */
if (setting->applied)
continue;
struct setting *s = find_setting(setting->name);
if (s) {
rc = storef_setting(settings_block, find_setting(setting->name), setting->value);
if (rc != 0)
DBG("linux storing setting '%s' = '%s' failed\n", setting->name, setting->value);
setting->applied = 1;
} else {
DBG("linux unknown setting '%s'\n", setting->name);
}
}
}

View File

@ -61,6 +61,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define ERRFILE_isapnp ( ERRFILE_DRIVER | 0x00020000 )
#define ERRFILE_mca ( ERRFILE_DRIVER | 0x00030000 )
#define ERRFILE_pci ( ERRFILE_DRIVER | 0x00040000 )
#define ERRFILE_linux ( ERRFILE_DRIVER | 0x00050000 )
#define ERRFILE_nvs ( ERRFILE_DRIVER | 0x00100000 )
#define ERRFILE_spi ( ERRFILE_DRIVER | 0x00110000 )

144
src/include/ipxe/linux.h Normal file
View File

@ -0,0 +1,144 @@
/*
* Copyright (C) 2010 Piotr Jaroszyński <p.jaroszynski@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _IPXE_LINUX_H
#define _IPXE_LINUX_H
FILE_LICENCE(GPL2_OR_LATER);
/** @file
*
* Linux devices, drivers and device requests.
*/
#include <ipxe/list.h>
#include <ipxe/device.h>
#include <ipxe/settings.h>
/** A linux device */
struct linux_device {
/** Generic device */
struct device dev;
/** Driver that's handling the device */
struct linux_driver *driver;
/** Private data used by drivers */
void *priv;
};
struct linux_device_request;
/** A linux driver */
struct linux_driver {
/** Name */
char *name;
/** Probe function */
int (*probe)(struct linux_device *device, struct linux_device_request *request);
/** Remove function */
void (*remove)(struct linux_device *device);
/** Can the driver probe any more devices? */
int can_probe;
};
/** Linux driver table */
#define LINUX_DRIVERS __table(struct linux_driver, "linux_drivers")
/** Declare a Linux driver */
#define __linux_driver __table_entry(LINUX_DRIVERS, 01)
/**
* Set linux device driver-private data
*
* @v device Linux device
* @v priv Private data
*/
static inline void linux_set_drvdata(struct linux_device * device, void *priv)
{
device->priv = priv;
}
/**
* Get linux device driver-private data
*
* @v device Linux device
* @ret priv Private data
*/
static inline void *linux_get_drvdata(struct linux_device *device)
{
return device->priv;
}
/**
* A device request.
*
* To be created and filled by the UI code.
*/
struct linux_device_request {
/** Driver name. Compared to the linux drivers' names */
char *driver;
/** List node */
struct list_head list;
/** List of settings */
struct list_head settings;
};
/** A device request setting */
struct linux_setting {
/** Name */
char *name;
/** Value */
char *value;
/** Was the setting already applied? */
int applied;
/** List node */
struct list_head list;
};
/**
* List of requested devices.
*
* Filled by the UI code. Linux root_driver walks over this list looking for an
* appropriate driver to handle each request by matching the driver's name.
*/
extern struct list_head linux_device_requests;
/**
* List of global settings to apply.
*
* Filled by the UI code. Linux root_driver applies these settings.
*/
extern struct list_head linux_global_settings;
/**
* Look for the last occurrence of a setting with the specified name
*
* @v name Name of the setting to look for
* @v settings List of the settings to look through
*/
struct linux_setting *linux_find_setting(char *name, struct list_head *settings);
/**
* Apply a list of linux settings to a settings block
*
* @v new_settings List of linux_setting's to apply
* @v settings_block Settings block to apply the settings to
* @ret rc 0 on success
*/
extern void linux_apply_settings(struct list_head *new_settings, struct settings *settings_block);
#endif /* _IPXE_LINUX_H */