Add a Firmware programming framework
This framework handles a list of registered Firmware programming handlers to unify a firmware programming interface by hiding the details how to program a specific Firmware in its handler. This is created with FPGAs in mind but should be usable for other devices aswell. A user has two possibilities to load a firmware. A device file is create under /dev/ which can be used to copy a firmware to. Additionally a firmwareload command is introduced which can list the registered firmware handlers and also to upload a firmware. Signed-off-by: Juergen Beisert <jbe@pengutronix.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
This commit is contained in:
parent
5a5ba5ad30
commit
a6982e2e26
|
@ -1905,6 +1905,15 @@ config CMD_BAREBOX_UPDATE
|
|||
-y autom. use 'yes' when asking confirmations
|
||||
-f LEVEL set force level
|
||||
|
||||
config CMD_FIRMWARELOAD
|
||||
bool
|
||||
select FIRMWARE
|
||||
prompt "firmwareload"
|
||||
help
|
||||
Provides the "firmwareload" command which deals with devices which need
|
||||
firmware to work. It is also used to upload firmware to FPGA devices.
|
||||
|
||||
|
||||
config CMD_LINUX_EXEC
|
||||
bool "linux exec"
|
||||
depends on LINUX
|
||||
|
|
|
@ -103,3 +103,4 @@ obj-$(CONFIG_CMD_LSPCI) += lspci.o
|
|||
obj-$(CONFIG_CMD_IMD) += imd.o
|
||||
obj-$(CONFIG_CMD_HWCLOCK) += hwclock.o
|
||||
obj-$(CONFIG_CMD_USBGADGET) += usbgadget.o
|
||||
obj-$(CONFIG_CMD_FIRMWARELOAD) += firmwareload.o
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Juergen Beisert <kernel@pengutronix.de>, Pengutronix
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <getopt.h>
|
||||
#include <firmware.h>
|
||||
|
||||
static int do_firmwareload(int argc, char *argv[])
|
||||
{
|
||||
int ret, opt;
|
||||
const char *name = NULL, *firmware;
|
||||
struct firmware_mgr *mgr;
|
||||
|
||||
while ((opt = getopt(argc, argv, "t:l")) > 0) {
|
||||
switch (opt) {
|
||||
case 't':
|
||||
name = optarg;
|
||||
break;
|
||||
case 'l':
|
||||
firmwaremgr_list_handlers();
|
||||
return 0;
|
||||
default:
|
||||
return COMMAND_ERROR_USAGE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(argc - optind))
|
||||
return COMMAND_ERROR_USAGE;
|
||||
|
||||
firmware = argv[optind];
|
||||
|
||||
mgr = firmwaremgr_find(name);
|
||||
|
||||
if (!mgr) {
|
||||
printf("No such programming handler found: %s\n",
|
||||
name ? name : "default");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = firmwaremgr_load_file(mgr, firmware);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
BAREBOX_CMD_HELP_START(firmwareload)
|
||||
BAREBOX_CMD_HELP_TEXT("Options:")
|
||||
BAREBOX_CMD_HELP_OPT("-t <target>", "define the firmware handler by name\n")
|
||||
BAREBOX_CMD_HELP_OPT("-l\t", "list devices capable of firmware loading\n")
|
||||
BAREBOX_CMD_HELP_END
|
||||
|
||||
BAREBOX_CMD_START(firmwareload)
|
||||
.cmd = do_firmwareload,
|
||||
BAREBOX_CMD_DESC("Program a firmware file into a device")
|
||||
BAREBOX_CMD_HELP(cmd_firmwareload_help)
|
||||
BAREBOX_CMD_END
|
|
@ -328,6 +328,9 @@ config CBSIZE
|
|||
prompt "Buffer size for input from the Console"
|
||||
default 1024
|
||||
|
||||
config FIRMWARE
|
||||
bool
|
||||
|
||||
choice
|
||||
prompt "Select your shell"
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ obj-$(CONFIG_EFI_DEVICEPATH) += efi-devicepath.o
|
|||
lwl-$(CONFIG_IMD) += imd-barebox.o
|
||||
obj-$(CONFIG_IMD) += imd.o
|
||||
obj-$(CONFIG_FILE_LIST) += file-list.o
|
||||
obj-$(CONFIG_FIRMWARE) += firmware.o
|
||||
|
||||
quiet_cmd_pwd_h = PWDH $@
|
||||
ifdef CONFIG_PASSWORD
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Juergen Beisert <kernel@pengutronix.de>, Pengutronix
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <firmware.h>
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <xfuncs.h>
|
||||
#include <fcntl.h>
|
||||
#include <libbb.h>
|
||||
#include <fs.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#define BUFSIZ 4096
|
||||
|
||||
struct firmware_mgr {
|
||||
struct list_head list;
|
||||
struct firmware_handler *handler; /* the program handler */
|
||||
struct cdev cdev;
|
||||
u8 buf[BUFSIZ];
|
||||
int ofs;
|
||||
};
|
||||
|
||||
static LIST_HEAD(firmwaremgr_list);
|
||||
|
||||
/*
|
||||
* firmwaremgr_find - find a firmware device handler
|
||||
*
|
||||
* Find a firmware device handler based on the unique id. If @id is
|
||||
* NULL this returns the single firmware device handler if only one
|
||||
* is registered. If multiple handlers are registered @id is mandatory
|
||||
*
|
||||
*/
|
||||
struct firmware_mgr *firmwaremgr_find(const char *id)
|
||||
{
|
||||
struct firmware_mgr *mgr;
|
||||
|
||||
if (!id) {
|
||||
if (list_is_singular(&firmwaremgr_list))
|
||||
return list_first_entry(&firmwaremgr_list,
|
||||
struct firmware_mgr, list);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_for_each_entry(mgr, &firmwaremgr_list, list)
|
||||
if (!strcmp(mgr->handler->id, id))
|
||||
return mgr;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* firmwaremgr_list_handlers - list registered firmware device handlers
|
||||
* in pretty format
|
||||
*/
|
||||
void firmwaremgr_list_handlers(void)
|
||||
{
|
||||
struct firmware_mgr *mgr;
|
||||
|
||||
printf("firmware programming handlers:\n\n");
|
||||
|
||||
if (list_empty(&firmwaremgr_list)) {
|
||||
printf("(none)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("%-11s%-11s\n", "name:", "model:");
|
||||
|
||||
list_for_each_entry(mgr, &firmwaremgr_list, list) {
|
||||
printf("%-11s", mgr->handler->id);
|
||||
if (mgr->handler->model)
|
||||
printf(" -> %-11s", mgr->handler->model);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int firmware_open(struct cdev *cdev, unsigned long flags)
|
||||
{
|
||||
struct firmware_mgr *mgr = cdev->priv;
|
||||
int ret;
|
||||
|
||||
mgr->ofs = 0;
|
||||
|
||||
ret = mgr->handler->open(mgr->handler);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t firmware_write(struct cdev *cdev, const void *buf, size_t insize,
|
||||
loff_t offset, ulong flags)
|
||||
{
|
||||
struct firmware_mgr *mgr = cdev->priv;
|
||||
int ret;
|
||||
size_t count = insize;
|
||||
|
||||
/*
|
||||
* We guarantee the write handler of the firmware device that only the
|
||||
* last write is a short write. All others are 4k in size.
|
||||
*/
|
||||
|
||||
while (count) {
|
||||
size_t space = BUFSIZ - mgr->ofs;
|
||||
size_t now = min(count, space);
|
||||
|
||||
memcpy(mgr->buf + mgr->ofs, buf, now);
|
||||
|
||||
buf += now;
|
||||
mgr->ofs += now;
|
||||
count -= now;
|
||||
|
||||
if (mgr->ofs == BUFSIZ) {
|
||||
ret = mgr->handler->write(mgr->handler, mgr->buf, BUFSIZ);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mgr->ofs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return insize;
|
||||
}
|
||||
|
||||
static int firmware_close(struct cdev *cdev)
|
||||
{
|
||||
struct firmware_mgr *mgr = cdev->priv;
|
||||
int ret;
|
||||
|
||||
if (mgr->ofs) {
|
||||
ret = mgr->handler->write(mgr->handler, mgr->buf, mgr->ofs);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mgr->handler->close(mgr->handler);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct file_operations firmware_ops = {
|
||||
.open = firmware_open,
|
||||
.write = firmware_write,
|
||||
.close = firmware_close,
|
||||
};
|
||||
|
||||
/*
|
||||
* firmwaremgr_register - register a device which needs firmware
|
||||
*/
|
||||
int firmwaremgr_register(struct firmware_handler *fh)
|
||||
{
|
||||
struct firmware_mgr *mgr;
|
||||
int ret;
|
||||
struct cdev *cdev;
|
||||
|
||||
if (firmwaremgr_find(fh->id))
|
||||
return -EBUSY;
|
||||
|
||||
mgr = xzalloc(sizeof(struct firmware_mgr));
|
||||
mgr->handler = fh;
|
||||
|
||||
cdev = &mgr->cdev;
|
||||
|
||||
cdev->name = xstrdup(fh->id);
|
||||
cdev->size = FILE_SIZE_STREAM;
|
||||
cdev->ops = &firmware_ops;
|
||||
cdev->priv = mgr;
|
||||
cdev->dev = fh->dev;
|
||||
|
||||
ret = devfs_create(cdev);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
list_add_tail(&mgr->list, &firmwaremgr_list);
|
||||
|
||||
return 0;
|
||||
out:
|
||||
free(cdev->name);
|
||||
free(mgr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* firmware_load_file - load a firmware to a device
|
||||
*/
|
||||
int firmwaremgr_load_file(struct firmware_mgr *mgr, const char *firmware)
|
||||
{
|
||||
int ret;
|
||||
char *name = asprintf("/dev/%s", mgr->handler->id);
|
||||
|
||||
ret = copy_file(firmware, name, 0);
|
||||
|
||||
free(name);
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Juergen Beisert <kernel@pengutronix.de>, Pengutronix
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FIRMWARE_H
|
||||
#define FIRMWARE_H
|
||||
|
||||
#include <types.h>
|
||||
#include <driver.h>
|
||||
|
||||
struct firmware_handler {
|
||||
char *id; /* unique identifier for this firmware device */
|
||||
char *model; /* description for this device */
|
||||
struct device_d *dev;
|
||||
/* called once to prepare the firmware's programming cycle */
|
||||
int (*open)(struct firmware_handler*);
|
||||
/* called multiple times to program the firmware with the given data */
|
||||
int (*write)(struct firmware_handler*, const void*, size_t);
|
||||
/* called once to finish programming cycle */
|
||||
int (*close)(struct firmware_handler*);
|
||||
};
|
||||
|
||||
struct firmware_mgr;
|
||||
|
||||
int firmwaremgr_register(struct firmware_handler *);
|
||||
|
||||
struct firmware_mgr *firmwaremgr_find(const char *);
|
||||
|
||||
void firmwaremgr_list_handlers(void);
|
||||
|
||||
int firmwaremgr_load_file(struct firmware_mgr *, const char *path);
|
||||
|
||||
#endif /* FIRMWARE_H */
|
Loading…
Reference in New Issue