| /* |
| * driver.c - barebox driver model |
| * |
| * Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix |
| * |
| * See file CREDITS for list of people who contributed to this |
| * project. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 |
| * as published by the Free Software Foundation. |
| * |
| * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| /** |
| * @file |
| * @brief barebox's driver model, and devinfo command |
| */ |
| |
| #include <common.h> |
| #include <command.h> |
| #include <driver.h> |
| #include <malloc.h> |
| #include <linux/ctype.h> |
| #include <errno.h> |
| #include <fs.h> |
| #include <linux/list.h> |
| |
| LIST_HEAD(device_list); |
| EXPORT_SYMBOL(device_list); |
| |
| LIST_HEAD(driver_list); |
| EXPORT_SYMBOL(driver_list); |
| |
| static LIST_HEAD(active); |
| |
| struct device_d *get_device_by_name(const char *name) |
| { |
| struct device_d *dev; |
| |
| for_each_device(dev) { |
| if(!strcmp(dev_name(dev), name)) |
| return dev; |
| } |
| |
| return NULL; |
| } |
| |
| static struct device_d *get_device_by_name_id(const char *name, int id) |
| { |
| struct device_d *dev; |
| |
| for_each_device(dev) { |
| if(!strcmp(dev->name, name) && id == dev->id) |
| return dev; |
| } |
| |
| return NULL; |
| } |
| |
| int get_free_deviceid(const char *name_template) |
| { |
| int i = 0; |
| |
| while (1) { |
| if (!get_device_by_name_id(name_template, i)) |
| return i; |
| i++; |
| }; |
| } |
| |
| static int match(struct driver_d *drv, struct device_d *dev) |
| { |
| if (dev->driver) |
| return -1; |
| |
| dev->driver = drv; |
| |
| if (dev->bus != drv->bus) |
| goto err_out; |
| if (dev->bus->match(dev, drv)) |
| goto err_out; |
| if (dev->bus->probe(dev)) |
| goto err_out; |
| |
| list_add(&dev->active, &active); |
| |
| return 0; |
| err_out: |
| dev->driver = NULL; |
| return -1; |
| } |
| |
| int register_device(struct device_d *new_device) |
| { |
| struct driver_d *drv; |
| |
| if (new_device->id < 0) { |
| new_device->id = get_free_deviceid(new_device->name); |
| } else { |
| if (get_device_by_name_id(new_device->name, new_device->id)) { |
| eprintf("register_device: already registered %s\n", |
| dev_name(new_device)); |
| return -EINVAL; |
| } |
| } |
| |
| debug ("register_device: %s\n", dev_name(new_device)); |
| |
| if (!new_device->bus) { |
| // dev_err(new_device, "no bus type associated. Needs fixup\n"); |
| new_device->bus = &platform_bus; |
| } |
| |
| list_add_tail(&new_device->list, &device_list); |
| INIT_LIST_HEAD(&new_device->children); |
| INIT_LIST_HEAD(&new_device->cdevs); |
| INIT_LIST_HEAD(&new_device->parameters); |
| |
| for_each_driver(drv) { |
| if (!match(drv, new_device)) |
| break; |
| } |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(register_device); |
| |
| int unregister_device(struct device_d *old_dev) |
| { |
| debug("unregister_device: %s\n", dev_name(old_dev)); |
| |
| if (!list_empty(&old_dev->children)) { |
| errno = -EBUSY; |
| return errno; |
| } |
| |
| if (old_dev->driver) |
| old_dev->bus->remove(old_dev); |
| |
| list_del(&old_dev->list); |
| list_del(&old_dev->active); |
| |
| /* remove device from parents child list */ |
| if (old_dev->parent) |
| list_del(&old_dev->sibling); |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(unregister_device); |
| |
| int dev_add_child(struct device_d *dev, struct device_d *child) |
| { |
| child->parent = dev; |
| |
| list_add_tail(&child->sibling, &dev->children); |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(dev_add_child); |
| |
| struct driver_d *get_driver_by_name(const char *name) |
| { |
| struct driver_d *drv; |
| |
| for_each_driver(drv) { |
| if(!strcmp(name, drv->name)) |
| return drv; |
| } |
| |
| return NULL; |
| } |
| |
| static void noinfo(struct device_d *dev) |
| { |
| printf("no info available for %s\n", dev_name(dev)); |
| } |
| |
| static void noshortinfo(struct device_d *dev) |
| { |
| } |
| |
| int register_driver(struct driver_d *drv) |
| { |
| struct device_d *dev = NULL; |
| |
| debug("register_driver: %s\n", drv->name); |
| |
| if (!drv->bus) { |
| // pr_err("driver %s has no bus type associated. Needs fixup\n", drv->name); |
| drv->bus = &platform_bus; |
| } |
| |
| list_add_tail(&drv->list, &driver_list); |
| |
| if (!drv->info) |
| drv->info = noinfo; |
| if (!drv->shortinfo) |
| drv->shortinfo = noshortinfo; |
| |
| for_each_device(dev) |
| match(drv, dev); |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(register_driver); |
| |
| int dev_protect(struct device_d *dev, size_t count, unsigned long offset, int prot) |
| { |
| printf("%s: currently broken\n", __func__); |
| return -EINVAL; |
| } |
| |
| int generic_memmap_ro(struct cdev *cdev, void **map, int flags) |
| { |
| if (!cdev->dev) |
| return -EINVAL; |
| |
| if (flags & PROT_WRITE) |
| return -EACCES; |
| *map = (void *)cdev->dev->map_base; |
| return 0; |
| } |
| |
| int generic_memmap_rw(struct cdev *cdev, void **map, int flags) |
| { |
| if (!cdev->dev) |
| return -EINVAL; |
| |
| *map = (void *)cdev->dev->map_base; |
| return 0; |
| } |
| |
| int dummy_probe(struct device_d *dev) |
| { |
| return 0; |
| } |
| EXPORT_SYMBOL(dummy_probe); |
| |
| const char *dev_id(const struct device_d *dev) |
| { |
| static char buf[sizeof(unsigned long) * 2]; |
| |
| sprintf(buf, FORMAT_DRIVER_MANE_ID, dev->name, dev->id); |
| |
| return buf; |
| } |
| |
| void devices_shutdown(void) |
| { |
| struct device_d *dev; |
| |
| list_for_each_entry(dev, &active, active) { |
| if (dev->driver->remove) |
| dev->driver->remove(dev); |
| } |
| } |
| |
| #ifdef CONFIG_CMD_DEVINFO |
| static int do_devinfo_subtree(struct device_d *dev, int depth, char edge) |
| { |
| struct device_d *child; |
| struct cdev *cdev; |
| int i; |
| |
| for (i = 0; i < depth; i++) |
| printf("| "); |
| |
| printf("%c----%s", edge, dev_name(dev)); |
| if (!list_empty(&dev->cdevs)) { |
| printf(" ("); |
| list_for_each_entry(cdev, &dev->cdevs, devices_list) { |
| printf("%s", cdev->name); |
| if (!list_is_last(&cdev->devices_list, &dev->cdevs)) |
| printf(", "); |
| } |
| printf(")"); |
| } |
| printf("\n"); |
| |
| if (!list_empty(&dev->children)) { |
| device_for_each_child(dev, child) { |
| do_devinfo_subtree(child, depth + 1, |
| list_is_last(&child->sibling, |
| &dev->children) ? '`' : '|'); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int do_devinfo(struct command *cmdtp, int argc, char *argv[]) |
| { |
| struct device_d *dev; |
| struct driver_d *drv; |
| struct param_d *param; |
| |
| if (argc == 1) { |
| printf("devices:\n"); |
| |
| for_each_device(dev) { |
| if (!dev->parent) |
| do_devinfo_subtree(dev, 0, '|'); |
| } |
| |
| printf("\ndrivers:\n"); |
| for_each_driver(drv) |
| printf("%10s\n",drv->name); |
| } else { |
| dev = get_device_by_name(argv[1]); |
| |
| if (!dev) { |
| printf("no such device: %s\n",argv[1]); |
| return -1; |
| } |
| |
| printf("base : 0x%zx\nsize : 0x%zx\ndriver: %s\n\n", |
| dev->map_base, dev->size, |
| dev->driver ? |
| dev->driver->name : "none"); |
| |
| if (dev->driver) |
| dev->driver->info(dev); |
| |
| printf("%s\n", list_empty(&dev->parameters) ? |
| "no parameters available" : "Parameters:"); |
| |
| list_for_each_entry(param, &dev->parameters, list) |
| printf("%16s = %s\n", param->name, param->value); |
| } |
| |
| return 0; |
| } |
| |
| BAREBOX_CMD_HELP_START(devinfo) |
| BAREBOX_CMD_HELP_USAGE("devinfo [DEVICE]\n") |
| BAREBOX_CMD_HELP_SHORT("Output device information.\n") |
| BAREBOX_CMD_HELP_END |
| |
| /** |
| * @page devinfo_command |
| |
| If called without arguments, devinfo shows a summary of the known |
| devices and drivers. |
| |
| If called with a device path being the argument, devinfo shows more |
| default information about this device and its parameters. |
| |
| Example from an MPC5200 based system: |
| |
| @verbatim |
| barebox:/ devinfo /dev/eth0 |
| base : 0x1002b000 |
| size : 0x00000000 |
| driver: fec_mpc5xxx |
| |
| no info available for eth0 |
| Parameters: |
| ipaddr = 192.168.23.197 |
| ethaddr = 80:81:82:83:84:86 |
| gateway = 192.168.23.1 |
| netmask = 255.255.255.0 |
| serverip = 192.168.23.2 |
| @endverbatim |
| */ |
| |
| BAREBOX_CMD_START(devinfo) |
| .cmd = do_devinfo, |
| .usage = "Show information about devices and drivers.", |
| BAREBOX_CMD_HELP(cmd_devinfo_help) |
| BAREBOX_CMD_END |
| #endif |
| |