mirror of https://gitee.com/openkylin/linux.git
greybus: firmware: convert to bundle driver
Convert the legacy firmware protocol driver to a bundle driver. This also fixes a potential crash should a (malicious) module have sent an early request before the private data had been initialised. Note that the firmware protocol needs to support the version request indefinitely since it has been burnt into ROM. In order to avoid having to update current module-loading scripts, keep this driver internal to greybus core at least until modalias support is added. Note that there is no MODULE_DEVICE_TABLE defined for firmware as we cannot have two greybus tables in one module on ancient 3.10 kernels and that the legacy driver is currently also internal to core. This needs be added once the driver can be built as a module. Testing Done: Tested on DB3. Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
parent
c760442e1c
commit
8ec589b979
|
@ -10,6 +10,7 @@
|
|||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "firmware.h"
|
||||
#include "greybus.h"
|
||||
#include "greybus_trace.h"
|
||||
#include "legacy.h"
|
||||
|
@ -243,9 +244,9 @@ static int __init gb_init(void)
|
|||
goto error_operation;
|
||||
}
|
||||
|
||||
retval = gb_firmware_protocol_init();
|
||||
retval = gb_firmware_init();
|
||||
if (retval) {
|
||||
pr_err("gb_firmware_protocol_init failed\n");
|
||||
pr_err("gb_firmware_init failed\n");
|
||||
goto error_firmware;
|
||||
}
|
||||
|
||||
|
@ -258,7 +259,7 @@ static int __init gb_init(void)
|
|||
return 0; /* Success */
|
||||
|
||||
error_legacy:
|
||||
gb_firmware_protocol_exit();
|
||||
gb_firmware_exit();
|
||||
error_firmware:
|
||||
gb_operation_exit();
|
||||
error_operation:
|
||||
|
@ -275,7 +276,7 @@ module_init(gb_init);
|
|||
static void __exit gb_exit(void)
|
||||
{
|
||||
gb_legacy_exit();
|
||||
gb_firmware_protocol_exit();
|
||||
gb_firmware_exit();
|
||||
gb_operation_exit();
|
||||
gb_hd_exit();
|
||||
bus_unregister(&greybus_bus_type);
|
||||
|
|
|
@ -9,11 +9,15 @@
|
|||
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include "firmware.h"
|
||||
#include "greybus.h"
|
||||
|
||||
|
||||
struct gb_firmware {
|
||||
struct gb_connection *connection;
|
||||
const struct firmware *fw;
|
||||
u8 protocol_major;
|
||||
u8 protocol_minor;
|
||||
};
|
||||
|
||||
static void free_firmware(struct gb_firmware *firmware)
|
||||
|
@ -223,8 +227,10 @@ static int gb_firmware_ready_to_boot(struct gb_operation *op)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int gb_firmware_request_recv(u8 type, struct gb_operation *op)
|
||||
static int gb_firmware_request_handler(struct gb_operation *op)
|
||||
{
|
||||
u8 type = op->type;
|
||||
|
||||
switch (type) {
|
||||
case GB_FIRMWARE_TYPE_FIRMWARE_SIZE:
|
||||
return gb_firmware_size_request(op);
|
||||
|
@ -239,60 +245,147 @@ static int gb_firmware_request_recv(u8 type, struct gb_operation *op)
|
|||
}
|
||||
}
|
||||
|
||||
static int gb_firmware_connection_init(struct gb_connection *connection)
|
||||
static int gb_firmware_get_version(struct gb_firmware *firmware)
|
||||
{
|
||||
struct gb_bundle *bundle = firmware->connection->bundle;
|
||||
struct gb_firmware_version_request request;
|
||||
struct gb_firmware_version_response response;
|
||||
int ret;
|
||||
|
||||
request.major = GB_FIRMWARE_VERSION_MAJOR;
|
||||
request.minor = GB_FIRMWARE_VERSION_MINOR;
|
||||
|
||||
ret = gb_operation_sync(firmware->connection,
|
||||
GB_FIRMWARE_TYPE_VERSION,
|
||||
&request, sizeof(request), &response,
|
||||
sizeof(response));
|
||||
if (ret) {
|
||||
dev_err(&bundle->dev,
|
||||
"failed to get protocol version: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (response.major > request.major) {
|
||||
dev_err(&bundle->dev,
|
||||
"unsupported major protocol version (%u > %u)\n",
|
||||
response.major, request.major);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
firmware->protocol_major = response.major;
|
||||
firmware->protocol_minor = response.minor;
|
||||
|
||||
dev_dbg(&bundle->dev, "%s - %u.%u\n", __func__, response.major,
|
||||
response.minor);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gb_firmware_probe(struct gb_bundle *bundle,
|
||||
const struct greybus_bundle_id *id)
|
||||
{
|
||||
struct greybus_descriptor_cport *cport_desc;
|
||||
struct gb_connection *connection;
|
||||
struct gb_firmware *firmware;
|
||||
int ret;
|
||||
|
||||
if (bundle->num_cports != 1)
|
||||
return -ENODEV;
|
||||
|
||||
cport_desc = &bundle->cport_desc[0];
|
||||
if (cport_desc->protocol_id != GREYBUS_PROTOCOL_FIRMWARE)
|
||||
return -ENODEV;
|
||||
|
||||
firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
|
||||
if (!firmware)
|
||||
return -ENOMEM;
|
||||
|
||||
firmware->connection = connection;
|
||||
connection = gb_connection_create(bundle,
|
||||
le16_to_cpu(cport_desc->id),
|
||||
gb_firmware_request_handler);
|
||||
if (IS_ERR(connection)) {
|
||||
ret = PTR_ERR(connection);
|
||||
goto err_free_firmware;
|
||||
}
|
||||
|
||||
connection->private = firmware;
|
||||
|
||||
firmware->connection = connection;
|
||||
|
||||
greybus_set_drvdata(bundle, firmware);
|
||||
|
||||
ret = gb_connection_enable_tx(connection);
|
||||
if (ret)
|
||||
goto err_connection_destroy;
|
||||
|
||||
ret = gb_firmware_get_version(firmware);
|
||||
if (ret)
|
||||
goto err_connection_disable;
|
||||
|
||||
firmware_es2_fixup_vid_pid(firmware);
|
||||
|
||||
ret = gb_connection_enable(connection);
|
||||
if (ret)
|
||||
goto err_connection_disable;
|
||||
|
||||
/* Tell bootrom we're ready. */
|
||||
ret = gb_operation_sync(connection, GB_FIRMWARE_TYPE_AP_READY, NULL, 0,
|
||||
NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(&connection->bundle->dev,
|
||||
"failed to send AP READY: %d\n", ret);
|
||||
goto err_free_firmware;
|
||||
goto err_connection_disable;
|
||||
}
|
||||
|
||||
dev_dbg(&connection->bundle->dev, "%s: AP_READY sent\n", __func__);
|
||||
dev_dbg(&bundle->dev, "AP_READY sent\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_connection_disable:
|
||||
gb_connection_disable(connection);
|
||||
err_connection_destroy:
|
||||
gb_connection_destroy(connection);
|
||||
err_free_firmware:
|
||||
kfree(firmware);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gb_firmware_connection_exit(struct gb_connection *connection)
|
||||
static void gb_firmware_disconnect(struct gb_bundle *bundle)
|
||||
{
|
||||
struct gb_firmware *firmware = connection->private;
|
||||
struct gb_firmware *firmware = greybus_get_drvdata(bundle);
|
||||
|
||||
dev_dbg(&bundle->dev, "%s\n", __func__);
|
||||
|
||||
gb_connection_disable(firmware->connection);
|
||||
|
||||
/* Release firmware */
|
||||
if (firmware->fw)
|
||||
free_firmware(firmware);
|
||||
|
||||
connection->private = NULL;
|
||||
gb_connection_destroy(firmware->connection);
|
||||
kfree(firmware);
|
||||
|
||||
dev_dbg(&connection->bundle->dev, "%s\n", __func__);
|
||||
}
|
||||
|
||||
static struct gb_protocol firmware_protocol = {
|
||||
.name = "firmware",
|
||||
.id = GREYBUS_PROTOCOL_FIRMWARE,
|
||||
.major = GB_FIRMWARE_VERSION_MAJOR,
|
||||
.minor = GB_FIRMWARE_VERSION_MINOR,
|
||||
.connection_init = gb_firmware_connection_init,
|
||||
.connection_exit = gb_firmware_connection_exit,
|
||||
.request_recv = gb_firmware_request_recv,
|
||||
static const struct greybus_bundle_id gb_firmware_id_table[] = {
|
||||
{ GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_FIRMWARE) },
|
||||
{ }
|
||||
};
|
||||
gb_builtin_protocol_driver(firmware_protocol);
|
||||
|
||||
static struct greybus_driver gb_firmware_driver = {
|
||||
.name = "firmware",
|
||||
.probe = gb_firmware_probe,
|
||||
.disconnect = gb_firmware_disconnect,
|
||||
.id_table = gb_firmware_id_table,
|
||||
};
|
||||
|
||||
int gb_firmware_init(void)
|
||||
{
|
||||
return greybus_register(&gb_firmware_driver);
|
||||
}
|
||||
|
||||
void gb_firmware_exit(void)
|
||||
{
|
||||
greybus_deregister(&gb_firmware_driver);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#ifndef __FIRMWARE_H
|
||||
#define __FIRMWARE_H
|
||||
|
||||
int gb_firmware_protocol_init(void);
|
||||
void gb_firmware_protocol_exit(void);
|
||||
int gb_firmware_init(void);
|
||||
void gb_firmware_exit(void);
|
||||
|
||||
#endif /* __FIRMWARE_H */
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include "manifest.h"
|
||||
#include "hd.h"
|
||||
#include "svc.h"
|
||||
#include "firmware.h"
|
||||
#include "control.h"
|
||||
#include "interface.h"
|
||||
#include "bundle.h"
|
||||
|
|
|
@ -203,6 +203,7 @@ struct gb_control_interface_version_response {
|
|||
#define GB_FIRMWARE_VERSION_MINOR 0x01
|
||||
|
||||
/* Greybus firmware request types */
|
||||
#define GB_FIRMWARE_TYPE_VERSION 0x01
|
||||
#define GB_FIRMWARE_TYPE_FIRMWARE_SIZE 0x02
|
||||
#define GB_FIRMWARE_TYPE_GET_FIRMWARE 0x03
|
||||
#define GB_FIRMWARE_TYPE_READY_TO_BOOT 0x04
|
||||
|
@ -226,6 +227,16 @@ struct gb_control_interface_version_response {
|
|||
/* Max firmware data fetch size in bytes */
|
||||
#define GB_FIRMWARE_FETCH_MAX 2000
|
||||
|
||||
struct gb_firmware_version_request {
|
||||
__u8 major;
|
||||
__u8 minor;
|
||||
} __packed;
|
||||
|
||||
struct gb_firmware_version_response {
|
||||
__u8 major;
|
||||
__u8 minor;
|
||||
} __packed;
|
||||
|
||||
/* Firmware protocol firmware size request/response */
|
||||
struct gb_firmware_size_request {
|
||||
__u8 stage;
|
||||
|
|
|
@ -247,7 +247,6 @@ static const struct greybus_bundle_id legacy_id_table[] = {
|
|||
{ GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) },
|
||||
{ GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LIGHTS) },
|
||||
{ GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LOOPBACK) },
|
||||
{ GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_FIRMWARE) },
|
||||
{ GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_RAW) },
|
||||
{ }
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue