2014-09-08 09:29:07 +00:00
|
|
|
/*
|
|
|
|
* 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>
|
2014-11-24 11:06:45 +00:00
|
|
|
#include <libfile.h>
|
2014-09-08 09:29:07 +00:00
|
|
|
#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;
|
|
|
|
}
|