From 91fb434bdaf7a8a60972764a5c8227d6f8ba5bc2 Mon Sep 17 00:00:00 2001 From: Piotr Jaroszyński Date: Mon, 31 May 2010 18:50:11 +0200 Subject: [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 Signed-off-by: Michael Brown --- src/drivers/linux/linux.c | 152 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 src/drivers/linux/linux.c (limited to 'src/drivers') diff --git a/src/drivers/linux/linux.c b/src/drivers/linux/linux.c new file mode 100644 index 000000000..1f26f5663 --- /dev/null +++ b/src/drivers/linux/linux.c @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2010 Piotr Jaroszyński + * + * 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 +#include +#include +#include +#include +#include + +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); + } + } +} -- cgit v1.2.3-55-g7522