aboutsummaryrefslogtreecommitdiffstats
path: root/greybus_firmware.patch
diff options
Diffstat (limited to 'greybus_firmware.patch')
-rw-r--r--greybus_firmware.patch2429
1 files changed, 2429 insertions, 0 deletions
diff --git a/greybus_firmware.patch b/greybus_firmware.patch
new file mode 100644
index 00000000000000..aa4c4f041c4464
--- /dev/null
+++ b/greybus_firmware.patch
@@ -0,0 +1,2429 @@
+---
+ drivers/greybus/Documentation/firmware/authenticate.c | 139 ++
+ drivers/greybus/Documentation/firmware/firmware-management | 333 ++++++
+ drivers/greybus/Documentation/firmware/firmware.c | 262 ++++
+ drivers/greybus/firmware.h | 42
+ drivers/greybus/fw-core.c | 312 +++++
+ drivers/greybus/fw-download.c | 465 ++++++++
+ drivers/greybus/fw-management.c | 721 +++++++++++++
+ drivers/greybus/greybus_firmware.h | 120 ++
+ 8 files changed, 2394 insertions(+)
+
+--- /dev/null
++++ b/drivers/greybus/Documentation/firmware/authenticate.c
+@@ -0,0 +1,139 @@
++/*
++ * Sample code to test CAP protocol
++ *
++ * This file is provided under a dual BSD/GPLv2 license. When using or
++ * redistributing this file, you may do so under either license.
++ *
++ * GPL LICENSE SUMMARY
++ *
++ * Copyright(c) 2016 Google Inc. All rights reserved.
++ * Copyright(c) 2016 Linaro Ltd. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License 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 version 2 for more details.
++ *
++ * BSD LICENSE
++ *
++ * Copyright(c) 2016 Google Inc. All rights reserved.
++ * Copyright(c) 2016 Linaro Ltd. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * * Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * * Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ * * Neither the name of Google Inc. or Linaro Ltd. nor the names of
++ * its contributors may be used to endorse or promote products
++ * derived from this software without specific prior written
++ * permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR
++ * LINARO LTD. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++#include <sys/ioctl.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++
++#include "../../greybus_authentication.h"
++
++struct cap_ioc_get_endpoint_uid uid;
++struct cap_ioc_get_ims_certificate cert = {
++ .certificate_class = 0,
++ .certificate_id = 0,
++};
++
++struct cap_ioc_authenticate authenticate = {
++ .auth_type = 0,
++ .challenge = {0},
++};
++
++int main(int argc, char *argv[])
++{
++ unsigned int timeout = 10000;
++ char *capdev;
++ int fd, ret;
++
++ /* Make sure arguments are correct */
++ if (argc != 2) {
++ printf("\nUsage: ./firmware <Path of the gb-cap-X dev>\n");
++ return 0;
++ }
++
++ capdev = argv[1];
++
++ printf("Opening %s authentication device\n", capdev);
++
++ fd = open(capdev, O_RDWR);
++ if (fd < 0) {
++ printf("Failed to open: %s\n", capdev);
++ return -1;
++ }
++
++ /* Get UID */
++ printf("Get UID\n");
++
++ ret = ioctl(fd, CAP_IOC_GET_ENDPOINT_UID, &uid);
++ if (ret < 0) {
++ printf("Failed to get UID: %s (%d)\n", capdev, ret);
++ ret = -1;
++ goto close_fd;
++ }
++
++ printf("UID received: 0x%llx\n", *(long long unsigned int *)(uid.uid));
++
++ /* Get certificate */
++ printf("Get IMS certificate\n");
++
++ ret = ioctl(fd, CAP_IOC_GET_IMS_CERTIFICATE, &cert);
++ if (ret < 0) {
++ printf("Failed to get IMS certificate: %s (%d)\n", capdev, ret);
++ ret = -1;
++ goto close_fd;
++ }
++
++ printf("IMS Certificate size: %d\n", cert.cert_size);
++
++ /* Authenticate */
++ printf("Authenticate module\n");
++
++ memcpy(authenticate.uid, uid.uid, 8);
++
++ ret = ioctl(fd, CAP_IOC_AUTHENTICATE, &authenticate);
++ if (ret < 0) {
++ printf("Failed to authenticate module: %s (%d)\n", capdev, ret);
++ ret = -1;
++ goto close_fd;
++ }
++
++ printf("Authenticated, result (%02x), sig-size (%02x)\n",
++ authenticate.result_code, authenticate.signature_size);
++
++close_fd:
++ close(fd);
++
++ return ret;
++}
+--- /dev/null
++++ b/drivers/greybus/Documentation/firmware/firmware-management
+@@ -0,0 +1,333 @@
++
++Firmware Management
++-------------------
++ Copyright 2016 Google Inc.
++ Copyright 2016 Linaro Ltd.
++
++Interface-Manifest
++------------------
++
++All firmware packages on the Modules or Interfaces are managed by a special
++Firmware Management Protocol. To support Firmware Management by the AP, the
++Interface Manifest shall at least contain the Firmware Management Bundle and a
++Firmware Management Protocol CPort within it.
++
++The bundle may contain additional CPorts based on the extra functionality
++required to manage firmware packages.
++
++For example, this is how the Firmware Management part of the Interface Manifest
++may look like:
++
++ ; Firmware Management Bundle (Bundle 1):
++ [bundle-descriptor 1]
++ class = 0x16
++
++ ; (Mandatory) Firmware Management Protocol on CPort 1
++ [cport-descriptor 2]
++ bundle = 1
++ protocol = 0x18
++
++ ; (Optional) Firmware Download Protocol on CPort 2
++ [cport-descriptor 1]
++ bundle = 1
++ protocol = 0x17
++
++ ; (Optional) SPI protocol on CPort 3
++ [cport-descriptor 3]
++ bundle = 1
++ protocol = 0x0b
++
++ ; (Optional) Component Authentication Protocol (CAP) on CPort 4
++ [cport-descriptor 4]
++ bundle = 1
++ protocol = 0x19
++
++
++Sysfs Interfaces - Firmware Management
++--------------------------------------
++
++The Firmware Management Protocol interacts with Userspace using the character
++device interface. The character device will be present in /dev/ directory
++and will be named gb-fw-mgmt-<N>. The number <N> is assigned at runtime.
++
++Identifying the Character Device
++================================
++
++There can be multiple devices present in /dev/ directory with name gb-fw-mgmt-N
++and user first needs to identify the character device used for
++firmware-management for a particular interface.
++
++The Firmware Management core creates a device of class 'gb_fw_mgmt', which shall
++be used by the user to identify the right character device for it. The class
++device is created within the Bundle directory for a particular Interface.
++
++For example this is how the class-device can be present:
++
++/sys/bus/greybus/devices/1-1/1-1.1/1-1.1.1/gb_fw_mgmt/gb-fw-mgmt-0
++
++The last name in this path: gb-fw-mgmt-0 is precisely the name of the char
++device and so the device in this case will be:
++
++/dev/gb-fw-mgmt-0.
++
++Operations on the Char device
++=============================
++
++The Character device (gb-fw-mgmt-0 in example) can be opened by the userspace
++application and it can perform various 'ioctl' operations on the device. The
++device doesn't support any read/write operations.
++
++Following are the IOCTLs and their data structures available to the user:
++
++/* IOCTL support */
++#define GB_FW_LOAD_METHOD_UNIPRO 0x01
++#define GB_FW_LOAD_METHOD_INTERNAL 0x02
++
++#define GB_FW_LOAD_STATUS_FAILED 0x00
++#define GB_FW_LOAD_STATUS_UNVALIDATED 0x01
++#define GB_FW_LOAD_STATUS_VALIDATED 0x02
++#define GB_FW_LOAD_STATUS_VALIDATION_FAILED 0x03
++
++#define GB_FW_BACKEND_FW_STATUS_SUCCESS 0x01
++#define GB_FW_BACKEND_FW_STATUS_FAIL_FIND 0x02
++#define GB_FW_BACKEND_FW_STATUS_FAIL_FETCH 0x03
++#define GB_FW_BACKEND_FW_STATUS_FAIL_WRITE 0x04
++#define GB_FW_BACKEND_FW_STATUS_INT 0x05
++#define GB_FW_BACKEND_FW_STATUS_RETRY 0x06
++#define GB_FW_BACKEND_FW_STATUS_NOT_SUPPORTED 0x07
++
++#define GB_FW_BACKEND_VERSION_STATUS_SUCCESS 0x01
++#define GB_FW_BACKEND_VERSION_STATUS_NOT_AVAILABLE 0x02
++#define GB_FW_BACKEND_VERSION_STATUS_NOT_SUPPORTED 0x03
++#define GB_FW_BACKEND_VERSION_STATUS_RETRY 0x04
++#define GB_FW_BACKEND_VERSION_STATUS_FAIL_INT 0x05
++
++
++struct fw_mgmt_ioc_get_intf_version {
++ __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_SIZE];
++ __u16 major;
++ __u16 minor;
++} __attribute__ ((__packed__));
++
++struct fw_mgmt_ioc_get_backend_version {
++ __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_SIZE];
++ __u16 major;
++ __u16 minor;
++ __u8 status;
++} __attribute__ ((__packed__));
++
++struct fw_mgmt_ioc_intf_load_and_validate {
++ __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE];
++ __u8 load_method;
++ __u8 status;
++ __u16 major;
++ __u16 minor;
++} __packed;
++
++struct fw_mgmt_ioc_backend_fw_update {
++ __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE];
++ __u8 status;
++} __packed;
++
++#define FW_MGMT_IOCTL_BASE 'S'
++#define FW_MGMT_IOC_GET_INTF_FW _IOR(FW_MGMT_IOCTL_BASE, 0, struct fw_mgmt_ioc_get_intf_version)
++#define FW_MGMT_IOC_GET_BACKEND_FW _IOWR(FW_MGMT_IOCTL_BASE, 1, struct fw_mgmt_ioc_get_backend_version)
++#define FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE _IOWR(FW_MGMT_IOCTL_BASE, 2, struct fw_mgmt_ioc_intf_load_and_validate)
++#define FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE _IOWR(FW_MGMT_IOCTL_BASE, 3, struct fw_mgmt_ioc_backend_fw_update)
++#define FW_MGMT_IOC_SET_TIMEOUT_MS _IOW(FW_MGMT_IOCTL_BASE, 4, unsigned int)
++#define FW_MGMT_IOC_MODE_SWITCH _IO(FW_MGMT_IOCTL_BASE, 5)
++
++1. FW_MGMT_IOC_GET_INTF_FW:
++
++ This ioctl shall be used by the user to get the version and firmware-tag of
++ the currently running Interface Firmware. All the fields of the 'struct
++ fw_mgmt_ioc_get_fw' are filled by the kernel.
++
++2. FW_MGMT_IOC_GET_BACKEND_FW:
++
++ This ioctl shall be used by the user to get the version of a currently
++ running Backend Interface Firmware identified by a firmware-tag. The user is
++ required to fill the 'firmware_tag' field of the 'struct fw_mgmt_ioc_get_fw'
++ in this case. The 'major' and 'minor' fields are set by the kernel in
++ response.
++
++3. FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE:
++
++ This ioctl shall be used by the user to load an Interface Firmware package on
++ an Interface. The user needs to fill the 'firmware_tag' and 'load_method'
++ fields of the 'struct fw_mgmt_ioc_intf_load_and_validate'. The 'status',
++ 'major' and 'minor' fields are set by the kernel in response.
++
++4. FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE:
++
++ This ioctl shall be used by the user to request an Interface to update a
++ Backend Interface Firmware. The user is required to fill the 'firmware_tag'
++ field of the 'struct fw_mgmt_ioc_get_fw' in this case. The 'status' field is
++ set by the kernel in response.
++
++5. FW_MGMT_IOC_SET_TIMEOUT_MS:
++
++ This ioctl shall be used by the user to increase the timeout interval within
++ which the firmware must get loaded by the Module. The default timeout is 1
++ second. The user needs to pass the timeout in milliseconds.
++
++6. FW_MGMT_IOC_MODE_SWITCH:
++
++ This ioctl shall be used by the user to mode-switch the module to the
++ previously loaded interface firmware. If the interface firmware isn't loaded
++ previously, or if another unsuccessful FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE
++ operation is started after loading interface firmware, then the firmware core
++ wouldn't allow mode-switch.
++
++
++Sysfs Interfaces - Authentication
++---------------------------------
++
++The Component Authentication Protocol interacts with Userspace using the
++character device interface. The character device will be present in /dev/
++directory and will be named gb-authenticate-<N>. The number <N> is assigned at
++runtime.
++
++Identifying the Character Device
++================================
++
++There can be multiple devices present in /dev/ directory with name
++gb-authenticate-N and user first needs to identify the character device used for
++authentication a of particular interface.
++
++The Authentication core creates a device of class 'gb_authenticate', which shall
++be used by the user to identify the right character device for it. The class
++device is created within the Bundle directory for a particular Interface.
++
++For example this is how the class-device can be present:
++
++/sys/bus/greybus/devices/1-1/1-1.1/1-1.1.1/gb_authenticate/gb-authenticate-0
++
++The last name in this path: gb-authenticate-0 is precisely the name of the char
++device and so the device in this case will be:
++
++/dev/gb-authenticate-0.
++
++Operations on the Char device
++=============================
++
++The Character device (/dev/gb-authenticate-0 in above example) can be opened by
++the userspace application and it can perform various 'ioctl' operations on the
++device. The device doesn't support any read/write operations.
++
++Following are the IOCTLs and their data structures available to the user:
++
++#define CAP_CERTIFICATE_MAX_SIZE 1600
++#define CAP_SIGNATURE_MAX_SIZE 320
++
++/* Certificate class types */
++#define CAP_CERT_IMS_EAPC 0x00000001
++#define CAP_CERT_IMS_EASC 0x00000002
++#define CAP_CERT_IMS_EARC 0x00000003
++#define CAP_CERT_IMS_IAPC 0x00000004
++#define CAP_CERT_IMS_IASC 0x00000005
++#define CAP_CERT_IMS_IARC 0x00000006
++
++/* IMS Certificate response result codes */
++#define CAP_IMS_RESULT_CERT_FOUND 0x00
++#define CAP_IMS_RESULT_CERT_CLASS_INVAL 0x01
++#define CAP_IMS_RESULT_CERT_CORRUPT 0x02
++#define CAP_IMS_RESULT_CERT_NOT_FOUND 0x03
++
++/* Authentication types */
++#define CAP_AUTH_IMS_PRI 0x00000001
++#define CAP_AUTH_IMS_SEC 0x00000002
++#define CAP_AUTH_IMS_RSA 0x00000003
++
++/* Authenticate response result codes */
++#define CAP_AUTH_RESULT_CR_SUCCESS 0x00
++#define CAP_AUTH_RESULT_CR_BAD_TYPE 0x01
++#define CAP_AUTH_RESULT_CR_WRONG_EP 0x02
++#define CAP_AUTH_RESULT_CR_NO_KEY 0x03
++#define CAP_AUTH_RESULT_CR_SIG_FAIL 0x04
++
++
++/* IOCTL support */
++struct cap_ioc_get_endpoint_uid {
++ __u8 uid[8];
++} __attribute__ ((__packed__));
++
++struct cap_ioc_get_ims_certificate {
++ __u32 certificate_class;
++ __u32 certificate_id;
++
++ __u8 result_code;
++ __u32 cert_size;
++ __u8 certificate[CAP_CERTIFICATE_MAX_SIZE];
++} __attribute__ ((__packed__));
++
++struct cap_ioc_authenticate {
++ __u32 auth_type;
++ __u8 uid[8];
++ __u8 challenge[32];
++
++ __u8 result_code;
++ __u8 response[64];
++ __u32 signature_size;
++ __u8 signature[CAP_SIGNATURE_MAX_SIZE];
++} __attribute__ ((__packed__));
++
++#define CAP_IOCTL_BASE 'C'
++#define CAP_IOC_GET_ENDPOINT_UID _IOR(CAP_IOCTL_BASE, 0, struct cap_ioc_get_endpoint_uid)
++#define CAP_IOC_GET_IMS_CERTIFICATE _IOWR(CAP_IOCTL_BASE, 1, struct cap_ioc_get_ims_certificate)
++#define CAP_IOC_AUTHENTICATE _IOWR(CAP_IOCTL_BASE, 2, struct cap_ioc_authenticate)
++
++
++1. CAP_IOC_GET_ENDPOINT_UID:
++
++ This ioctl shall be used by the user to get the endpoint UID associated with
++ the Interface. All the fields of the 'struct cap_ioc_get_endpoint_uid' are
++ filled by the kernel.
++
++2. CAP_IOC_GET_IMS_CERTIFICATE:
++
++ This ioctl shall be used by the user to retrieve one of the available
++ cryptographic certificates held by the Interface for use in Component
++ Authentication. The user is required to fill the 'certificate_class' and
++ 'certificate_id' field of the 'struct cap_ioc_get_ims_certificate' in this
++ case. The other fields will be set by the kernel in response. The first
++ 'cert_size' bytes of the 'certificate' shall be read by the user and others
++ must be discarded.
++
++3. CAP_IOC_AUTHENTICATE:
++
++ This ioctl shall be used by the user to authenticate the Module attached to
++ an Interface. The user needs to fill the 'auth_type', 'uid', and 'challenge'
++ fields of the 'struct cap_ioc_authenticate'. The other fields will be set by
++ the kernel in response. The first 'signature_size' bytes of the 'signature'
++ shall be read by the user and others must be discarded.
++
++
++Sysfs Interfaces - Firmware Download
++------------------------------------
++
++The Firmware Download Protocol uses the existing Linux Kernel's Firmware class
++and the interface provided to userspace are described in:
++Documentation/firmware_class/.
++
++
++Sysfs Interfaces - SPI Flash
++----------------------------
++
++The SPI flash is exposed in userspace as a MTD device and is created
++within the Bundle directory. For example, this is how the path may look like:
++
++$ ls /sys/bus/greybus/devices/1-1/1-1.1/1-1.1.1/spi_master/spi32766/spi32766.0/mtd
++mtd0 mtd0ro
++
++
++Sample Applications
++-------------------
++
++The current directory also provides a firmware.c test application, which can be
++referenced while developing userspace application to talk to firmware-management
++protocol.
++
++The current directory also provides a authenticate.c test application, which can
++be referenced while developing userspace application to talk to
++component authentication protocol.
+--- /dev/null
++++ b/drivers/greybus/Documentation/firmware/firmware.c
+@@ -0,0 +1,262 @@
++/*
++ * Sample code to test firmware-management protocol
++ *
++ * This file is provided under a dual BSD/GPLv2 license. When using or
++ * redistributing this file, you may do so under either license.
++ *
++ * GPL LICENSE SUMMARY
++ *
++ * Copyright(c) 2016 Google Inc. All rights reserved.
++ * Copyright(c) 2016 Linaro Ltd. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License 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 version 2 for more details.
++ *
++ * BSD LICENSE
++ *
++ * Copyright(c) 2016 Google Inc. All rights reserved.
++ * Copyright(c) 2016 Linaro Ltd. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * * Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * * Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ * * Neither the name of Google Inc. or Linaro Ltd. nor the names of
++ * its contributors may be used to endorse or promote products
++ * derived from this software without specific prior written
++ * permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR
++ * LINARO LTD. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++#include <sys/ioctl.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++
++#include "../../greybus_firmware.h"
++
++#define FW_DEV_DEFAULT "/dev/gb-fw-mgmt-0"
++#define FW_TAG_INT_DEFAULT "s3f"
++#define FW_TAG_BCND_DEFAULT "bf_01"
++#define FW_UPDATE_TYPE_DEFAULT 0
++#define FW_TIMEOUT_DEFAULT 10000;
++
++static const char *firmware_tag;
++static const char *fwdev = FW_DEV_DEFAULT;
++static int fw_update_type = FW_UPDATE_TYPE_DEFAULT;
++static int fw_timeout = FW_TIMEOUT_DEFAULT;
++
++static struct fw_mgmt_ioc_get_intf_version intf_fw_info;
++static struct fw_mgmt_ioc_get_backend_version backend_fw_info;
++static struct fw_mgmt_ioc_intf_load_and_validate intf_load;
++static struct fw_mgmt_ioc_backend_fw_update backend_update;
++
++static void usage(void)
++{
++ printf("\nUsage: ./firmware <gb-fw-mgmt-X (default: gb-fw-mgmt-0)> <interface: 0, backend: 1 (default: 0)> <firmware-tag> (default: \"s3f\"/\"bf_01\") <timeout (default: 10000 ms)>\n");
++}
++
++static int update_intf_firmware(int fd)
++{
++ int ret;
++
++ /* Get Interface Firmware Version */
++ printf("Get Interface Firmware Version\n");
++
++ ret = ioctl(fd, FW_MGMT_IOC_GET_INTF_FW, &intf_fw_info);
++ if (ret < 0) {
++ printf("Failed to get interface firmware version: %s (%d)\n",
++ fwdev, ret);
++ return -1;
++ }
++
++ printf("Interface Firmware tag (%s), major (%d), minor (%d)\n",
++ intf_fw_info.firmware_tag, intf_fw_info.major,
++ intf_fw_info.minor);
++
++ /* Try Interface Firmware load over Unipro */
++ printf("Loading Interface Firmware\n");
++
++ intf_load.load_method = GB_FW_U_LOAD_METHOD_UNIPRO;
++ intf_load.status = 0;
++ intf_load.major = 0;
++ intf_load.minor = 0;
++
++ strncpy((char *)&intf_load.firmware_tag, firmware_tag,
++ GB_FIRMWARE_U_TAG_MAX_SIZE);
++
++ ret = ioctl(fd, FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE, &intf_load);
++ if (ret < 0) {
++ printf("Failed to load interface firmware: %s (%d)\n", fwdev,
++ ret);
++ return -1;
++ }
++
++ if (intf_load.status != GB_FW_U_LOAD_STATUS_VALIDATED &&
++ intf_load.status != GB_FW_U_LOAD_STATUS_UNVALIDATED) {
++ printf("Load status says loading failed: %d\n",
++ intf_load.status);
++ return -1;
++ }
++
++ printf("Interface Firmware (%s) Load done: major: %d, minor: %d, status: %d\n",
++ firmware_tag, intf_load.major, intf_load.minor,
++ intf_load.status);
++
++ /* Initiate Mode-switch to the newly loaded firmware */
++ printf("Initiate Mode switch\n");
++
++ ret = ioctl(fd, FW_MGMT_IOC_MODE_SWITCH);
++ if (ret < 0)
++ printf("Failed to initiate mode-switch (%d)\n", ret);
++
++ return ret;
++}
++
++static int update_backend_firmware(int fd)
++{
++ int ret;
++
++ /* Get Backend Firmware Version */
++ printf("Getting Backend Firmware Version\n");
++
++ strncpy((char *)&backend_fw_info.firmware_tag, firmware_tag,
++ GB_FIRMWARE_U_TAG_MAX_SIZE);
++
++retry_fw_version:
++ ret = ioctl(fd, FW_MGMT_IOC_GET_BACKEND_FW, &backend_fw_info);
++ if (ret < 0) {
++ printf("Failed to get backend firmware version: %s (%d)\n",
++ fwdev, ret);
++ return -1;
++ }
++
++ printf("Backend Firmware tag (%s), major (%d), minor (%d), status (%d)\n",
++ backend_fw_info.firmware_tag, backend_fw_info.major,
++ backend_fw_info.minor, backend_fw_info.status);
++
++ if (backend_fw_info.status == GB_FW_U_BACKEND_VERSION_STATUS_RETRY)
++ goto retry_fw_version;
++
++ if ((backend_fw_info.status != GB_FW_U_BACKEND_VERSION_STATUS_SUCCESS)
++ && (backend_fw_info.status != GB_FW_U_BACKEND_VERSION_STATUS_NOT_AVAILABLE)) {
++ printf("Failed to get backend firmware version: %s (%d)\n",
++ fwdev, backend_fw_info.status);
++ return -1;
++ }
++
++ /* Try Backend Firmware Update over Unipro */
++ printf("Updating Backend Firmware\n");
++
++ strncpy((char *)&backend_update.firmware_tag, firmware_tag,
++ GB_FIRMWARE_U_TAG_MAX_SIZE);
++
++retry_fw_update:
++ backend_update.status = 0;
++
++ ret = ioctl(fd, FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE, &backend_update);
++ if (ret < 0) {
++ printf("Failed to load backend firmware: %s (%d)\n", fwdev, ret);
++ return -1;
++ }
++
++ if (backend_update.status == GB_FW_U_BACKEND_FW_STATUS_RETRY) {
++ printf("Retrying firmware update: %d\n", backend_update.status);
++ goto retry_fw_update;
++ }
++
++ if (backend_update.status != GB_FW_U_BACKEND_FW_STATUS_SUCCESS) {
++ printf("Load status says loading failed: %d\n",
++ backend_update.status);
++ } else {
++ printf("Backend Firmware (%s) Load done: status: %d\n",
++ firmware_tag, backend_update.status);
++ }
++
++ return 0;
++}
++
++int main(int argc, char *argv[])
++{
++ int fd, ret;
++
++ if (argc > 1 &&
++ (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
++ usage();
++ return -1;
++ }
++
++ if (argc > 1)
++ fwdev = argv[1];
++
++ if (argc > 2)
++ sscanf(argv[2], "%u", &fw_update_type);
++
++ if (argc > 3) {
++ firmware_tag = argv[3];
++ } else if (!fw_update_type) {
++ firmware_tag = FW_TAG_INT_DEFAULT;
++ } else {
++ firmware_tag = FW_TAG_BCND_DEFAULT;
++ }
++
++ if (argc > 4)
++ sscanf(argv[4], "%u", &fw_timeout);
++
++ printf("Trying Firmware update: fwdev: %s, type: %s, tag: %s, timeout: %d\n",
++ fwdev, fw_update_type == 0 ? "interface" : "backend",
++ firmware_tag, fw_timeout);
++
++ printf("Opening %s firmware management device\n", fwdev);
++
++ fd = open(fwdev, O_RDWR);
++ if (fd < 0) {
++ printf("Failed to open: %s\n", fwdev);
++ return -1;
++ }
++
++ /* Set Timeout */
++ printf("Setting timeout to %u ms\n", fw_timeout);
++
++ ret = ioctl(fd, FW_MGMT_IOC_SET_TIMEOUT_MS, &fw_timeout);
++ if (ret < 0) {
++ printf("Failed to set timeout: %s (%d)\n", fwdev, ret);
++ ret = -1;
++ goto close_fd;
++ }
++
++ if (!fw_update_type)
++ ret = update_intf_firmware(fd);
++ else
++ ret = update_backend_firmware(fd);
++
++close_fd:
++ close(fd);
++
++ return ret;
++}
+--- /dev/null
++++ b/drivers/greybus/firmware.h
+@@ -0,0 +1,42 @@
++/*
++ * Greybus Firmware Management Header
++ *
++ * Copyright 2016 Google Inc.
++ * Copyright 2016 Linaro Ltd.
++ *
++ * Released under the GPLv2 only.
++ */
++
++#ifndef __FIRMWARE_H
++#define __FIRMWARE_H
++
++#include "greybus.h"
++
++#define FW_NAME_PREFIX "gmp_"
++
++/*
++ * Length of the string in format: "FW_NAME_PREFIX""%08x_%08x_%08x_%08x_%s.tftf"
++ * (3 + 1 + 4 * (8 + 1) + 10 + 1 + 4 + 1)
++ */
++#define FW_NAME_SIZE 56
++
++/* Firmware Management Protocol specific functions */
++int fw_mgmt_init(void);
++void fw_mgmt_exit(void);
++struct gb_connection *to_fw_mgmt_connection(struct device *dev);
++int gb_fw_mgmt_request_handler(struct gb_operation *op);
++int gb_fw_mgmt_connection_init(struct gb_connection *connection);
++void gb_fw_mgmt_connection_exit(struct gb_connection *connection);
++
++/* Firmware Download Protocol specific functions */
++int gb_fw_download_request_handler(struct gb_operation *op);
++int gb_fw_download_connection_init(struct gb_connection *connection);
++void gb_fw_download_connection_exit(struct gb_connection *connection);
++
++/* CAP Protocol specific functions */
++int cap_init(void);
++void cap_exit(void);
++int gb_cap_connection_init(struct gb_connection *connection);
++void gb_cap_connection_exit(struct gb_connection *connection);
++
++#endif /* __FIRMWARE_H */
+--- /dev/null
++++ b/drivers/greybus/fw-core.c
+@@ -0,0 +1,312 @@
++/*
++ * Greybus Firmware Core Bundle Driver.
++ *
++ * Copyright 2016 Google Inc.
++ * Copyright 2016 Linaro Ltd.
++ *
++ * Released under the GPLv2 only.
++ */
++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
++
++#include <linux/firmware.h>
++#include "firmware.h"
++#include "greybus.h"
++#include "spilib.h"
++
++struct gb_fw_core {
++ struct gb_connection *download_connection;
++ struct gb_connection *mgmt_connection;
++ struct gb_connection *spi_connection;
++ struct gb_connection *cap_connection;
++};
++
++static struct spilib_ops *spilib_ops;
++
++struct gb_connection *to_fw_mgmt_connection(struct device *dev)
++{
++ struct gb_fw_core *fw_core = dev_get_drvdata(dev);
++
++ return fw_core->mgmt_connection;
++}
++
++static int gb_fw_spi_connection_init(struct gb_connection *connection)
++{
++ int ret;
++
++ if (!connection)
++ return 0;
++
++ ret = gb_connection_enable(connection);
++ if (ret)
++ return ret;
++
++ ret = gb_spilib_master_init(connection, &connection->bundle->dev,
++ spilib_ops);
++ if (ret) {
++ gb_connection_disable(connection);
++ return ret;
++ }
++
++ return 0;
++}
++
++static void gb_fw_spi_connection_exit(struct gb_connection *connection)
++{
++ if (!connection)
++ return;
++
++ gb_spilib_master_exit(connection);
++ gb_connection_disable(connection);
++}
++
++static int gb_fw_core_probe(struct gb_bundle *bundle,
++ const struct greybus_bundle_id *id)
++{
++ struct greybus_descriptor_cport *cport_desc;
++ struct gb_connection *connection;
++ struct gb_fw_core *fw_core;
++ int ret, i;
++ u16 cport_id;
++ u8 protocol_id;
++
++ fw_core = kzalloc(sizeof(*fw_core), GFP_KERNEL);
++ if (!fw_core)
++ return -ENOMEM;
++
++ /* Parse CPorts and create connections */
++ for (i = 0; i < bundle->num_cports; i++) {
++ cport_desc = &bundle->cport_desc[i];
++ cport_id = le16_to_cpu(cport_desc->id);
++ protocol_id = cport_desc->protocol_id;
++
++ switch (protocol_id) {
++ case GREYBUS_PROTOCOL_FW_MANAGEMENT:
++ /* Disallow multiple Firmware Management CPorts */
++ if (fw_core->mgmt_connection) {
++ dev_err(&bundle->dev,
++ "multiple management CPorts found\n");
++ ret = -EINVAL;
++ goto err_destroy_connections;
++ }
++
++ connection = gb_connection_create(bundle, cport_id,
++ gb_fw_mgmt_request_handler);
++ if (IS_ERR(connection)) {
++ ret = PTR_ERR(connection);
++ dev_err(&bundle->dev,
++ "failed to create management connection (%d)\n",
++ ret);
++ goto err_destroy_connections;
++ }
++
++ fw_core->mgmt_connection = connection;
++ break;
++ case GREYBUS_PROTOCOL_FW_DOWNLOAD:
++ /* Disallow multiple Firmware Download CPorts */
++ if (fw_core->download_connection) {
++ dev_err(&bundle->dev,
++ "multiple download CPorts found\n");
++ ret = -EINVAL;
++ goto err_destroy_connections;
++ }
++
++ connection = gb_connection_create(bundle, cport_id,
++ gb_fw_download_request_handler);
++ if (IS_ERR(connection)) {
++ dev_err(&bundle->dev, "failed to create download connection (%ld)\n",
++ PTR_ERR(connection));
++ } else {
++ fw_core->download_connection = connection;
++ }
++
++ break;
++ case GREYBUS_PROTOCOL_SPI:
++ /* Disallow multiple SPI CPorts */
++ if (fw_core->spi_connection) {
++ dev_err(&bundle->dev,
++ "multiple SPI CPorts found\n");
++ ret = -EINVAL;
++ goto err_destroy_connections;
++ }
++
++ connection = gb_connection_create(bundle, cport_id,
++ NULL);
++ if (IS_ERR(connection)) {
++ dev_err(&bundle->dev, "failed to create SPI connection (%ld)\n",
++ PTR_ERR(connection));
++ } else {
++ fw_core->spi_connection = connection;
++ }
++
++ break;
++ case GREYBUS_PROTOCOL_AUTHENTICATION:
++ /* Disallow multiple CAP CPorts */
++ if (fw_core->cap_connection) {
++ dev_err(&bundle->dev, "multiple Authentication CPorts found\n");
++ ret = -EINVAL;
++ goto err_destroy_connections;
++ }
++
++ connection = gb_connection_create(bundle, cport_id,
++ NULL);
++ if (IS_ERR(connection)) {
++ dev_err(&bundle->dev, "failed to create Authentication connection (%ld)\n",
++ PTR_ERR(connection));
++ } else {
++ fw_core->cap_connection = connection;
++ }
++
++ break;
++ default:
++ dev_err(&bundle->dev, "invalid protocol id (0x%02x)\n",
++ protocol_id);
++ ret = -EINVAL;
++ goto err_destroy_connections;
++ }
++ }
++
++ /* Firmware Management connection is mandatory */
++ if (!fw_core->mgmt_connection) {
++ dev_err(&bundle->dev, "missing management connection\n");
++ ret = -ENODEV;
++ goto err_destroy_connections;
++ }
++
++ ret = gb_fw_download_connection_init(fw_core->download_connection);
++ if (ret) {
++ /* We may still be able to work with the Interface */
++ dev_err(&bundle->dev, "failed to initialize firmware download connection, disable it (%d)\n",
++ ret);
++ gb_connection_destroy(fw_core->download_connection);
++ fw_core->download_connection = NULL;
++ }
++
++ ret = gb_fw_spi_connection_init(fw_core->spi_connection);
++ if (ret) {
++ /* We may still be able to work with the Interface */
++ dev_err(&bundle->dev, "failed to initialize SPI connection, disable it (%d)\n",
++ ret);
++ gb_connection_destroy(fw_core->spi_connection);
++ fw_core->spi_connection = NULL;
++ }
++
++ ret = gb_cap_connection_init(fw_core->cap_connection);
++ if (ret) {
++ /* We may still be able to work with the Interface */
++ dev_err(&bundle->dev, "failed to initialize CAP connection, disable it (%d)\n",
++ ret);
++ gb_connection_destroy(fw_core->cap_connection);
++ fw_core->cap_connection = NULL;
++ }
++
++ ret = gb_fw_mgmt_connection_init(fw_core->mgmt_connection);
++ if (ret) {
++ /* We may still be able to work with the Interface */
++ dev_err(&bundle->dev, "failed to initialize firmware management connection, disable it (%d)\n",
++ ret);
++ goto err_exit_connections;
++ }
++
++ greybus_set_drvdata(bundle, fw_core);
++
++ /* FIXME: Remove this after S2 Loader gets runtime PM support */
++ if (!(bundle->intf->quirks & GB_INTERFACE_QUIRK_NO_PM))
++ gb_pm_runtime_put_autosuspend(bundle);
++
++ return 0;
++
++err_exit_connections:
++ gb_cap_connection_exit(fw_core->cap_connection);
++ gb_fw_spi_connection_exit(fw_core->spi_connection);
++ gb_fw_download_connection_exit(fw_core->download_connection);
++err_destroy_connections:
++ gb_connection_destroy(fw_core->mgmt_connection);
++ gb_connection_destroy(fw_core->cap_connection);
++ gb_connection_destroy(fw_core->spi_connection);
++ gb_connection_destroy(fw_core->download_connection);
++ kfree(fw_core);
++
++ return ret;
++}
++
++static void gb_fw_core_disconnect(struct gb_bundle *bundle)
++{
++ struct gb_fw_core *fw_core = greybus_get_drvdata(bundle);
++ int ret;
++
++ /* FIXME: Remove this after S2 Loader gets runtime PM support */
++ if (!(bundle->intf->quirks & GB_INTERFACE_QUIRK_NO_PM)) {
++ ret = gb_pm_runtime_get_sync(bundle);
++ if (ret)
++ gb_pm_runtime_get_noresume(bundle);
++ }
++
++ gb_fw_mgmt_connection_exit(fw_core->mgmt_connection);
++ gb_cap_connection_exit(fw_core->cap_connection);
++ gb_fw_spi_connection_exit(fw_core->spi_connection);
++ gb_fw_download_connection_exit(fw_core->download_connection);
++
++ gb_connection_destroy(fw_core->mgmt_connection);
++ gb_connection_destroy(fw_core->cap_connection);
++ gb_connection_destroy(fw_core->spi_connection);
++ gb_connection_destroy(fw_core->download_connection);
++
++ kfree(fw_core);
++}
++
++static const struct greybus_bundle_id gb_fw_core_id_table[] = {
++ { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_FW_MANAGEMENT) },
++ { }
++};
++
++static struct greybus_driver gb_fw_core_driver = {
++ .name = "gb-firmware",
++ .probe = gb_fw_core_probe,
++ .disconnect = gb_fw_core_disconnect,
++ .id_table = gb_fw_core_id_table,
++};
++
++static int fw_core_init(void)
++{
++ int ret;
++
++ ret = fw_mgmt_init();
++ if (ret) {
++ pr_err("Failed to initialize fw-mgmt core (%d)\n", ret);
++ return ret;
++ }
++
++ ret = cap_init();
++ if (ret) {
++ pr_err("Failed to initialize component authentication core (%d)\n",
++ ret);
++ goto fw_mgmt_exit;
++ }
++
++ ret = greybus_register(&gb_fw_core_driver);
++ if (ret)
++ goto cap_exit;
++
++ return 0;
++
++cap_exit:
++ cap_exit();
++fw_mgmt_exit:
++ fw_mgmt_exit();
++
++ return ret;
++}
++module_init(fw_core_init);
++
++static void __exit fw_core_exit(void)
++{
++ greybus_deregister(&gb_fw_core_driver);
++ cap_exit();
++ fw_mgmt_exit();
++}
++module_exit(fw_core_exit);
++
++MODULE_ALIAS("greybus:firmware");
++MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>");
++MODULE_DESCRIPTION("Greybus Firmware Bundle Driver");
++MODULE_LICENSE("GPL v2");
+--- /dev/null
++++ b/drivers/greybus/fw-download.c
+@@ -0,0 +1,465 @@
++/*
++ * Greybus Firmware Download Protocol Driver.
++ *
++ * Copyright 2016 Google Inc.
++ * Copyright 2016 Linaro Ltd.
++ *
++ * Released under the GPLv2 only.
++ */
++
++#include <linux/firmware.h>
++#include <linux/jiffies.h>
++#include <linux/mutex.h>
++#include <linux/workqueue.h>
++#include "firmware.h"
++#include "greybus.h"
++
++/* Estimated minimum buffer size, actual size can be smaller than this */
++#define MIN_FETCH_SIZE 512
++/* Timeout, in jiffies, within which fetch or release firmware must be called */
++#define NEXT_REQ_TIMEOUT_J msecs_to_jiffies(1000)
++
++struct fw_request {
++ u8 firmware_id;
++ bool disabled;
++ bool timedout;
++ char name[FW_NAME_SIZE];
++ const struct firmware *fw;
++ struct list_head node;
++
++ struct delayed_work dwork;
++ /* Timeout, in jiffies, within which the firmware shall download */
++ unsigned long release_timeout_j;
++ struct kref kref;
++ struct fw_download *fw_download;
++};
++
++struct fw_download {
++ struct device *parent;
++ struct gb_connection *connection;
++ struct list_head fw_requests;
++ struct ida id_map;
++ struct mutex mutex;
++};
++
++static void fw_req_release(struct kref *kref)
++{
++ struct fw_request *fw_req = container_of(kref, struct fw_request, kref);
++
++ dev_dbg(fw_req->fw_download->parent, "firmware %s released\n",
++ fw_req->name);
++
++ release_firmware(fw_req->fw);
++
++ /*
++ * The request timed out and the module may send a fetch-fw or
++ * release-fw request later. Lets block the id we allocated for this
++ * request, so that the AP doesn't refer to a later fw-request (with
++ * same firmware_id) for the old timedout fw-request.
++ *
++ * NOTE:
++ *
++ * This also means that after 255 timeouts we will fail to service new
++ * firmware downloads. But what else can we do in that case anyway? Lets
++ * just hope that it never happens.
++ */
++ if (!fw_req->timedout)
++ ida_simple_remove(&fw_req->fw_download->id_map,
++ fw_req->firmware_id);
++
++ kfree(fw_req);
++}
++
++/*
++ * Incoming requests are serialized for a connection, and the only race possible
++ * is between the timeout handler freeing this and an incoming request.
++ *
++ * The operations on the fw-request list are protected by the mutex and
++ * get_fw_req() increments the reference count before returning a fw_req pointer
++ * to the users.
++ *
++ * free_firmware() also takes the mutex while removing an entry from the list,
++ * it guarantees that every user of fw_req has taken a kref-reference by now and
++ * we wouldn't have any new users.
++ *
++ * Once the last user drops the reference, the fw_req structure is freed.
++ */
++static void put_fw_req(struct fw_request *fw_req)
++{
++ kref_put(&fw_req->kref, fw_req_release);
++}
++
++/* Caller must call put_fw_req() after using struct fw_request */
++static struct fw_request *get_fw_req(struct fw_download *fw_download,
++ u8 firmware_id)
++{
++ struct fw_request *fw_req;
++
++ mutex_lock(&fw_download->mutex);
++
++ list_for_each_entry(fw_req, &fw_download->fw_requests, node) {
++ if (fw_req->firmware_id == firmware_id) {
++ kref_get(&fw_req->kref);
++ goto unlock;
++ }
++ }
++
++ fw_req = NULL;
++
++unlock:
++ mutex_unlock(&fw_download->mutex);
++
++ return fw_req;
++}
++
++static void free_firmware(struct fw_download *fw_download,
++ struct fw_request *fw_req)
++{
++ /* Already disabled from timeout handlers */
++ if (fw_req->disabled)
++ return;
++
++ mutex_lock(&fw_download->mutex);
++ list_del(&fw_req->node);
++ mutex_unlock(&fw_download->mutex);
++
++ fw_req->disabled = true;
++ put_fw_req(fw_req);
++}
++
++static void fw_request_timedout(struct work_struct *work)
++{
++ struct delayed_work *dwork = to_delayed_work(work);
++ struct fw_request *fw_req = container_of(dwork, struct fw_request, dwork);
++ struct fw_download *fw_download = fw_req->fw_download;
++
++ dev_err(fw_download->parent,
++ "Timed out waiting for fetch / release firmware requests: %u\n",
++ fw_req->firmware_id);
++
++ fw_req->timedout = true;
++ free_firmware(fw_download, fw_req);
++}
++
++static int exceeds_release_timeout(struct fw_request *fw_req)
++{
++ struct fw_download *fw_download = fw_req->fw_download;
++
++ if (time_before(jiffies, fw_req->release_timeout_j))
++ return 0;
++
++ dev_err(fw_download->parent,
++ "Firmware download didn't finish in time, abort: %d\n",
++ fw_req->firmware_id);
++
++ fw_req->timedout = true;
++ free_firmware(fw_download, fw_req);
++
++ return -ETIMEDOUT;
++}
++
++/* This returns path of the firmware blob on the disk */
++static struct fw_request *find_firmware(struct fw_download *fw_download,
++ const char *tag)
++{
++ struct gb_interface *intf = fw_download->connection->bundle->intf;
++ struct fw_request *fw_req;
++ int ret, req_count;
++
++ fw_req = kzalloc(sizeof(*fw_req), GFP_KERNEL);
++ if (!fw_req)
++ return ERR_PTR(-ENOMEM);
++
++ /* Allocate ids from 1 to 255 (u8-max), 0 is an invalid id */
++ ret = ida_simple_get(&fw_download->id_map, 1, 256, GFP_KERNEL);
++ if (ret < 0) {
++ dev_err(fw_download->parent,
++ "failed to allocate firmware id (%d)\n", ret);
++ goto err_free_req;
++ }
++ fw_req->firmware_id = ret;
++
++ snprintf(fw_req->name, sizeof(fw_req->name),
++ FW_NAME_PREFIX "%08x_%08x_%08x_%08x_%s.tftf",
++ intf->ddbl1_manufacturer_id, intf->ddbl1_product_id,
++ intf->vendor_id, intf->product_id, tag);
++
++ dev_info(fw_download->parent, "Requested firmware package '%s'\n",
++ fw_req->name);
++
++ ret = request_firmware(&fw_req->fw, fw_req->name, fw_download->parent);
++ if (ret) {
++ dev_err(fw_download->parent,
++ "firmware request failed for %s (%d)\n", fw_req->name,
++ ret);
++ goto err_free_id;
++ }
++
++ fw_req->fw_download = fw_download;
++ kref_init(&fw_req->kref);
++
++ mutex_lock(&fw_download->mutex);
++ list_add(&fw_req->node, &fw_download->fw_requests);
++ mutex_unlock(&fw_download->mutex);
++
++ /* Timeout, in jiffies, within which firmware should get loaded */
++ req_count = DIV_ROUND_UP(fw_req->fw->size, MIN_FETCH_SIZE);
++ fw_req->release_timeout_j = jiffies + req_count * NEXT_REQ_TIMEOUT_J;
++
++ INIT_DELAYED_WORK(&fw_req->dwork, fw_request_timedout);
++ schedule_delayed_work(&fw_req->dwork, NEXT_REQ_TIMEOUT_J);
++
++ return fw_req;
++
++err_free_id:
++ ida_simple_remove(&fw_download->id_map, fw_req->firmware_id);
++err_free_req:
++ kfree(fw_req);
++
++ return ERR_PTR(ret);
++}
++
++static int fw_download_find_firmware(struct gb_operation *op)
++{
++ struct gb_connection *connection = op->connection;
++ struct fw_download *fw_download = gb_connection_get_data(connection);
++ struct gb_fw_download_find_firmware_request *request;
++ struct gb_fw_download_find_firmware_response *response;
++ struct fw_request *fw_req;
++ const char *tag;
++
++ if (op->request->payload_size != sizeof(*request)) {
++ dev_err(fw_download->parent,
++ "illegal size of find firmware request (%zu != %zu)\n",
++ op->request->payload_size, sizeof(*request));
++ return -EINVAL;
++ }
++
++ request = op->request->payload;
++ tag = (const char *)request->firmware_tag;
++
++ /* firmware_tag must be null-terminated */
++ if (strnlen(tag, GB_FIRMWARE_TAG_MAX_SIZE) == GB_FIRMWARE_TAG_MAX_SIZE) {
++ dev_err(fw_download->parent,
++ "firmware-tag is not null-terminated\n");
++ return -EINVAL;
++ }
++
++ fw_req = find_firmware(fw_download, tag);
++ if (IS_ERR(fw_req))
++ return PTR_ERR(fw_req);
++
++ if (!gb_operation_response_alloc(op, sizeof(*response), GFP_KERNEL)) {
++ dev_err(fw_download->parent, "error allocating response\n");
++ free_firmware(fw_download, fw_req);
++ return -ENOMEM;
++ }
++
++ response = op->response->payload;
++ response->firmware_id = fw_req->firmware_id;
++ response->size = cpu_to_le32(fw_req->fw->size);
++
++ dev_dbg(fw_download->parent,
++ "firmware size is %zu bytes\n", fw_req->fw->size);
++
++ return 0;
++}
++
++static int fw_download_fetch_firmware(struct gb_operation *op)
++{
++ struct gb_connection *connection = op->connection;
++ struct fw_download *fw_download = gb_connection_get_data(connection);
++ struct gb_fw_download_fetch_firmware_request *request;
++ struct gb_fw_download_fetch_firmware_response *response;
++ struct fw_request *fw_req;
++ const struct firmware *fw;
++ unsigned int offset, size;
++ u8 firmware_id;
++ int ret = 0;
++
++ if (op->request->payload_size != sizeof(*request)) {
++ dev_err(fw_download->parent,
++ "Illegal size of fetch firmware request (%zu %zu)\n",
++ op->request->payload_size, sizeof(*request));
++ return -EINVAL;
++ }
++
++ request = op->request->payload;
++ offset = le32_to_cpu(request->offset);
++ size = le32_to_cpu(request->size);
++ firmware_id = request->firmware_id;
++
++ fw_req = get_fw_req(fw_download, firmware_id);
++ if (!fw_req) {
++ dev_err(fw_download->parent,
++ "firmware not available for id: %02u\n", firmware_id);
++ return -EINVAL;
++ }
++
++ /* Make sure work handler isn't running in parallel */
++ cancel_delayed_work_sync(&fw_req->dwork);
++
++ /* We timed-out before reaching here ? */
++ if (fw_req->disabled) {
++ ret = -ETIMEDOUT;
++ goto put_fw;
++ }
++
++ /*
++ * Firmware download must finish within a limited time interval. If it
++ * doesn't, then we might have a buggy Module on the other side. Abort
++ * download.
++ */
++ ret = exceeds_release_timeout(fw_req);
++ if (ret)
++ goto put_fw;
++
++ fw = fw_req->fw;
++
++ if (offset >= fw->size || size > fw->size - offset) {
++ dev_err(fw_download->parent,
++ "bad fetch firmware request (offs = %u, size = %u)\n",
++ offset, size);
++ ret = -EINVAL;
++ goto put_fw;
++ }
++
++ if (!gb_operation_response_alloc(op, sizeof(*response) + size,
++ GFP_KERNEL)) {
++ dev_err(fw_download->parent,
++ "error allocating fetch firmware response\n");
++ ret = -ENOMEM;
++ goto put_fw;
++ }
++
++ response = op->response->payload;
++ memcpy(response->data, fw->data + offset, size);
++
++ dev_dbg(fw_download->parent,
++ "responding with firmware (offs = %u, size = %u)\n", offset,
++ size);
++
++ /* Refresh timeout */
++ schedule_delayed_work(&fw_req->dwork, NEXT_REQ_TIMEOUT_J);
++
++put_fw:
++ put_fw_req(fw_req);
++
++ return ret;
++}
++
++static int fw_download_release_firmware(struct gb_operation *op)
++{
++ struct gb_connection *connection = op->connection;
++ struct fw_download *fw_download = gb_connection_get_data(connection);
++ struct gb_fw_download_release_firmware_request *request;
++ struct fw_request *fw_req;
++ u8 firmware_id;
++
++ if (op->request->payload_size != sizeof(*request)) {
++ dev_err(fw_download->parent,
++ "Illegal size of release firmware request (%zu %zu)\n",
++ op->request->payload_size, sizeof(*request));
++ return -EINVAL;
++ }
++
++ request = op->request->payload;
++ firmware_id = request->firmware_id;
++
++ fw_req = get_fw_req(fw_download, firmware_id);
++ if (!fw_req) {
++ dev_err(fw_download->parent,
++ "firmware not available for id: %02u\n", firmware_id);
++ return -EINVAL;
++ }
++
++ cancel_delayed_work_sync(&fw_req->dwork);
++
++ free_firmware(fw_download, fw_req);
++ put_fw_req(fw_req);
++
++ dev_dbg(fw_download->parent, "release firmware\n");
++
++ return 0;
++}
++
++int gb_fw_download_request_handler(struct gb_operation *op)
++{
++ u8 type = op->type;
++
++ switch (type) {
++ case GB_FW_DOWNLOAD_TYPE_FIND_FIRMWARE:
++ return fw_download_find_firmware(op);
++ case GB_FW_DOWNLOAD_TYPE_FETCH_FIRMWARE:
++ return fw_download_fetch_firmware(op);
++ case GB_FW_DOWNLOAD_TYPE_RELEASE_FIRMWARE:
++ return fw_download_release_firmware(op);
++ default:
++ dev_err(&op->connection->bundle->dev,
++ "unsupported request: %u\n", type);
++ return -EINVAL;
++ }
++}
++
++int gb_fw_download_connection_init(struct gb_connection *connection)
++{
++ struct fw_download *fw_download;
++ int ret;
++
++ if (!connection)
++ return 0;
++
++ fw_download = kzalloc(sizeof(*fw_download), GFP_KERNEL);
++ if (!fw_download)
++ return -ENOMEM;
++
++ fw_download->parent = &connection->bundle->dev;
++ INIT_LIST_HEAD(&fw_download->fw_requests);
++ ida_init(&fw_download->id_map);
++ gb_connection_set_data(connection, fw_download);
++ fw_download->connection = connection;
++ mutex_init(&fw_download->mutex);
++
++ ret = gb_connection_enable(connection);
++ if (ret)
++ goto err_destroy_id_map;
++
++ return 0;
++
++err_destroy_id_map:
++ ida_destroy(&fw_download->id_map);
++ kfree(fw_download);
++
++ return ret;
++}
++
++void gb_fw_download_connection_exit(struct gb_connection *connection)
++{
++ struct fw_download *fw_download;
++ struct fw_request *fw_req, *tmp;
++
++ if (!connection)
++ return;
++
++ fw_download = gb_connection_get_data(connection);
++ gb_connection_disable(fw_download->connection);
++
++ /*
++ * Make sure we have a reference to the pending requests, before they
++ * are freed from the timeout handler.
++ */
++ mutex_lock(&fw_download->mutex);
++ list_for_each_entry(fw_req, &fw_download->fw_requests, node)
++ kref_get(&fw_req->kref);
++ mutex_unlock(&fw_download->mutex);
++
++ /* Release pending firmware packages */
++ list_for_each_entry_safe(fw_req, tmp, &fw_download->fw_requests, node) {
++ cancel_delayed_work_sync(&fw_req->dwork);
++ free_firmware(fw_download, fw_req);
++ put_fw_req(fw_req);
++ }
++
++ ida_destroy(&fw_download->id_map);
++ kfree(fw_download);
++}
+--- /dev/null
++++ b/drivers/greybus/fw-management.c
+@@ -0,0 +1,721 @@
++/*
++ * Greybus Firmware Management Protocol Driver.
++ *
++ * Copyright 2016 Google Inc.
++ * Copyright 2016 Linaro Ltd.
++ *
++ * Released under the GPLv2 only.
++ */
++
++#include <linux/cdev.h>
++#include <linux/completion.h>
++#include <linux/firmware.h>
++#include <linux/fs.h>
++#include <linux/idr.h>
++#include <linux/ioctl.h>
++#include <linux/uaccess.h>
++
++#include "firmware.h"
++#include "greybus_firmware.h"
++#include "greybus.h"
++
++#define FW_MGMT_TIMEOUT_MS 1000
++
++struct fw_mgmt {
++ struct device *parent;
++ struct gb_connection *connection;
++ struct kref kref;
++ struct list_head node;
++
++ /* Common id-map for interface and backend firmware requests */
++ struct ida id_map;
++ struct mutex mutex;
++ struct completion completion;
++ struct cdev cdev;
++ struct device *class_device;
++ dev_t dev_num;
++ unsigned int timeout_jiffies;
++ bool disabled; /* connection getting disabled */
++
++ /* Interface Firmware specific fields */
++ bool mode_switch_started;
++ bool intf_fw_loaded;
++ u8 intf_fw_request_id;
++ u8 intf_fw_status;
++ u16 intf_fw_major;
++ u16 intf_fw_minor;
++
++ /* Backend Firmware specific fields */
++ u8 backend_fw_request_id;
++ u8 backend_fw_status;
++};
++
++/*
++ * Number of minor devices this driver supports.
++ * There will be exactly one required per Interface.
++ */
++#define NUM_MINORS U8_MAX
++
++static struct class *fw_mgmt_class;
++static dev_t fw_mgmt_dev_num;
++static DEFINE_IDA(fw_mgmt_minors_map);
++static LIST_HEAD(fw_mgmt_list);
++static DEFINE_MUTEX(list_mutex);
++
++static void fw_mgmt_kref_release(struct kref *kref)
++{
++ struct fw_mgmt *fw_mgmt = container_of(kref, struct fw_mgmt, kref);
++
++ ida_destroy(&fw_mgmt->id_map);
++ kfree(fw_mgmt);
++}
++
++/*
++ * All users of fw_mgmt take a reference (from within list_mutex lock), before
++ * they get a pointer to play with. And the structure will be freed only after
++ * the last user has put the reference to it.
++ */
++static void put_fw_mgmt(struct fw_mgmt *fw_mgmt)
++{
++ kref_put(&fw_mgmt->kref, fw_mgmt_kref_release);
++}
++
++/* Caller must call put_fw_mgmt() after using struct fw_mgmt */
++static struct fw_mgmt *get_fw_mgmt(struct cdev *cdev)
++{
++ struct fw_mgmt *fw_mgmt;
++
++ mutex_lock(&list_mutex);
++
++ list_for_each_entry(fw_mgmt, &fw_mgmt_list, node) {
++ if (&fw_mgmt->cdev == cdev) {
++ kref_get(&fw_mgmt->kref);
++ goto unlock;
++ }
++ }
++
++ fw_mgmt = NULL;
++
++unlock:
++ mutex_unlock(&list_mutex);
++
++ return fw_mgmt;
++}
++
++static int fw_mgmt_interface_fw_version_operation(struct fw_mgmt *fw_mgmt,
++ struct fw_mgmt_ioc_get_intf_version *fw_info)
++{
++ struct gb_connection *connection = fw_mgmt->connection;
++ struct gb_fw_mgmt_interface_fw_version_response response;
++ int ret;
++
++ ret = gb_operation_sync(connection,
++ GB_FW_MGMT_TYPE_INTERFACE_FW_VERSION, NULL, 0,
++ &response, sizeof(response));
++ if (ret) {
++ dev_err(fw_mgmt->parent,
++ "failed to get interface firmware version (%d)\n", ret);
++ return ret;
++ }
++
++ fw_info->major = le16_to_cpu(response.major);
++ fw_info->minor = le16_to_cpu(response.minor);
++
++ strncpy(fw_info->firmware_tag, response.firmware_tag,
++ GB_FIRMWARE_TAG_MAX_SIZE);
++
++ /*
++ * The firmware-tag should be NULL terminated, otherwise throw error but
++ * don't fail.
++ */
++ if (fw_info->firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE - 1] != '\0') {
++ dev_err(fw_mgmt->parent,
++ "fw-version: firmware-tag is not NULL terminated\n");
++ fw_info->firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE - 1] = '\0';
++ }
++
++ return 0;
++}
++
++static int fw_mgmt_load_and_validate_operation(struct fw_mgmt *fw_mgmt,
++ u8 load_method, const char *tag)
++{
++ struct gb_fw_mgmt_load_and_validate_fw_request request;
++ int ret;
++
++ if (load_method != GB_FW_LOAD_METHOD_UNIPRO &&
++ load_method != GB_FW_LOAD_METHOD_INTERNAL) {
++ dev_err(fw_mgmt->parent,
++ "invalid load-method (%d)\n", load_method);
++ return -EINVAL;
++ }
++
++ request.load_method = load_method;
++ strncpy(request.firmware_tag, tag, GB_FIRMWARE_TAG_MAX_SIZE);
++
++ /*
++ * The firmware-tag should be NULL terminated, otherwise throw error and
++ * fail.
++ */
++ if (request.firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE - 1] != '\0') {
++ dev_err(fw_mgmt->parent, "load-and-validate: firmware-tag is not NULL terminated\n");
++ return -EINVAL;
++ }
++
++ /* Allocate ids from 1 to 255 (u8-max), 0 is an invalid id */
++ ret = ida_simple_get(&fw_mgmt->id_map, 1, 256, GFP_KERNEL);
++ if (ret < 0) {
++ dev_err(fw_mgmt->parent, "failed to allocate request id (%d)\n",
++ ret);
++ return ret;
++ }
++
++ fw_mgmt->intf_fw_request_id = ret;
++ fw_mgmt->intf_fw_loaded = false;
++ request.request_id = ret;
++
++ ret = gb_operation_sync(fw_mgmt->connection,
++ GB_FW_MGMT_TYPE_LOAD_AND_VALIDATE_FW, &request,
++ sizeof(request), NULL, 0);
++ if (ret) {
++ ida_simple_remove(&fw_mgmt->id_map,
++ fw_mgmt->intf_fw_request_id);
++ fw_mgmt->intf_fw_request_id = 0;
++ dev_err(fw_mgmt->parent,
++ "load and validate firmware request failed (%d)\n",
++ ret);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int fw_mgmt_interface_fw_loaded_operation(struct gb_operation *op)
++{
++ struct gb_connection *connection = op->connection;
++ struct fw_mgmt *fw_mgmt = gb_connection_get_data(connection);
++ struct gb_fw_mgmt_loaded_fw_request *request;
++
++ /* No pending load and validate request ? */
++ if (!fw_mgmt->intf_fw_request_id) {
++ dev_err(fw_mgmt->parent,
++ "unexpected firmware loaded request received\n");
++ return -ENODEV;
++ }
++
++ if (op->request->payload_size != sizeof(*request)) {
++ dev_err(fw_mgmt->parent, "illegal size of firmware loaded request (%zu != %zu)\n",
++ op->request->payload_size, sizeof(*request));
++ return -EINVAL;
++ }
++
++ request = op->request->payload;
++
++ /* Invalid request-id ? */
++ if (request->request_id != fw_mgmt->intf_fw_request_id) {
++ dev_err(fw_mgmt->parent, "invalid request id for firmware loaded request (%02u != %02u)\n",
++ fw_mgmt->intf_fw_request_id, request->request_id);
++ return -ENODEV;
++ }
++
++ ida_simple_remove(&fw_mgmt->id_map, fw_mgmt->intf_fw_request_id);
++ fw_mgmt->intf_fw_request_id = 0;
++ fw_mgmt->intf_fw_status = request->status;
++ fw_mgmt->intf_fw_major = le16_to_cpu(request->major);
++ fw_mgmt->intf_fw_minor = le16_to_cpu(request->minor);
++
++ if (fw_mgmt->intf_fw_status == GB_FW_LOAD_STATUS_FAILED)
++ dev_err(fw_mgmt->parent,
++ "failed to load interface firmware, status:%02x\n",
++ fw_mgmt->intf_fw_status);
++ else if (fw_mgmt->intf_fw_status == GB_FW_LOAD_STATUS_VALIDATION_FAILED)
++ dev_err(fw_mgmt->parent,
++ "failed to validate interface firmware, status:%02x\n",
++ fw_mgmt->intf_fw_status);
++ else
++ fw_mgmt->intf_fw_loaded = true;
++
++ complete(&fw_mgmt->completion);
++
++ return 0;
++}
++
++static int fw_mgmt_backend_fw_version_operation(struct fw_mgmt *fw_mgmt,
++ struct fw_mgmt_ioc_get_backend_version *fw_info)
++{
++ struct gb_connection *connection = fw_mgmt->connection;
++ struct gb_fw_mgmt_backend_fw_version_request request;
++ struct gb_fw_mgmt_backend_fw_version_response response;
++ int ret;
++
++ strncpy(request.firmware_tag, fw_info->firmware_tag,
++ GB_FIRMWARE_TAG_MAX_SIZE);
++
++ /*
++ * The firmware-tag should be NULL terminated, otherwise throw error and
++ * fail.
++ */
++ if (request.firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE - 1] != '\0') {
++ dev_err(fw_mgmt->parent, "backend-version: firmware-tag is not NULL terminated\n");
++ return -EINVAL;
++ }
++
++ ret = gb_operation_sync(connection,
++ GB_FW_MGMT_TYPE_BACKEND_FW_VERSION, &request,
++ sizeof(request), &response, sizeof(response));
++ if (ret) {
++ dev_err(fw_mgmt->parent, "failed to get version of %s backend firmware (%d)\n",
++ fw_info->firmware_tag, ret);
++ return ret;
++ }
++
++ fw_info->status = response.status;
++
++ /* Reset version as that should be non-zero only for success case */
++ fw_info->major = 0;
++ fw_info->minor = 0;
++
++ switch (fw_info->status) {
++ case GB_FW_BACKEND_VERSION_STATUS_SUCCESS:
++ fw_info->major = le16_to_cpu(response.major);
++ fw_info->minor = le16_to_cpu(response.minor);
++ break;
++ case GB_FW_BACKEND_VERSION_STATUS_NOT_AVAILABLE:
++ case GB_FW_BACKEND_VERSION_STATUS_RETRY:
++ break;
++ case GB_FW_BACKEND_VERSION_STATUS_NOT_SUPPORTED:
++ dev_err(fw_mgmt->parent,
++ "Firmware with tag %s is not supported by Interface\n",
++ fw_info->firmware_tag);
++ break;
++ default:
++ dev_err(fw_mgmt->parent, "Invalid status received: %u\n",
++ fw_info->status);
++ }
++
++ return 0;
++}
++
++static int fw_mgmt_backend_fw_update_operation(struct fw_mgmt *fw_mgmt,
++ char *tag)
++{
++ struct gb_fw_mgmt_backend_fw_update_request request;
++ int ret;
++
++ strncpy(request.firmware_tag, tag, GB_FIRMWARE_TAG_MAX_SIZE);
++
++ /*
++ * The firmware-tag should be NULL terminated, otherwise throw error and
++ * fail.
++ */
++ if (request.firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE - 1] != '\0') {
++ dev_err(fw_mgmt->parent, "backend-update: firmware-tag is not NULL terminated\n");
++ return -EINVAL;
++ }
++
++ /* Allocate ids from 1 to 255 (u8-max), 0 is an invalid id */
++ ret = ida_simple_get(&fw_mgmt->id_map, 1, 256, GFP_KERNEL);
++ if (ret < 0) {
++ dev_err(fw_mgmt->parent, "failed to allocate request id (%d)\n",
++ ret);
++ return ret;
++ }
++
++ fw_mgmt->backend_fw_request_id = ret;
++ request.request_id = ret;
++
++ ret = gb_operation_sync(fw_mgmt->connection,
++ GB_FW_MGMT_TYPE_BACKEND_FW_UPDATE, &request,
++ sizeof(request), NULL, 0);
++ if (ret) {
++ ida_simple_remove(&fw_mgmt->id_map,
++ fw_mgmt->backend_fw_request_id);
++ fw_mgmt->backend_fw_request_id = 0;
++ dev_err(fw_mgmt->parent,
++ "backend %s firmware update request failed (%d)\n", tag,
++ ret);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int fw_mgmt_backend_fw_updated_operation(struct gb_operation *op)
++{
++ struct gb_connection *connection = op->connection;
++ struct fw_mgmt *fw_mgmt = gb_connection_get_data(connection);
++ struct gb_fw_mgmt_backend_fw_updated_request *request;
++
++ /* No pending load and validate request ? */
++ if (!fw_mgmt->backend_fw_request_id) {
++ dev_err(fw_mgmt->parent, "unexpected backend firmware updated request received\n");
++ return -ENODEV;
++ }
++
++ if (op->request->payload_size != sizeof(*request)) {
++ dev_err(fw_mgmt->parent, "illegal size of backend firmware updated request (%zu != %zu)\n",
++ op->request->payload_size, sizeof(*request));
++ return -EINVAL;
++ }
++
++ request = op->request->payload;
++
++ /* Invalid request-id ? */
++ if (request->request_id != fw_mgmt->backend_fw_request_id) {
++ dev_err(fw_mgmt->parent, "invalid request id for backend firmware updated request (%02u != %02u)\n",
++ fw_mgmt->backend_fw_request_id, request->request_id);
++ return -ENODEV;
++ }
++
++ ida_simple_remove(&fw_mgmt->id_map, fw_mgmt->backend_fw_request_id);
++ fw_mgmt->backend_fw_request_id = 0;
++ fw_mgmt->backend_fw_status = request->status;
++
++ if ((fw_mgmt->backend_fw_status != GB_FW_BACKEND_FW_STATUS_SUCCESS) &&
++ (fw_mgmt->backend_fw_status != GB_FW_BACKEND_FW_STATUS_RETRY))
++ dev_err(fw_mgmt->parent,
++ "failed to load backend firmware: %02x\n",
++ fw_mgmt->backend_fw_status);
++
++ complete(&fw_mgmt->completion);
++
++ return 0;
++}
++
++/* Char device fops */
++
++static int fw_mgmt_open(struct inode *inode, struct file *file)
++{
++ struct fw_mgmt *fw_mgmt = get_fw_mgmt(inode->i_cdev);
++
++ /* fw_mgmt structure can't get freed until file descriptor is closed */
++ if (fw_mgmt) {
++ file->private_data = fw_mgmt;
++ return 0;
++ }
++
++ return -ENODEV;
++}
++
++static int fw_mgmt_release(struct inode *inode, struct file *file)
++{
++ struct fw_mgmt *fw_mgmt = file->private_data;
++
++ put_fw_mgmt(fw_mgmt);
++ return 0;
++}
++
++static int fw_mgmt_ioctl(struct fw_mgmt *fw_mgmt, unsigned int cmd,
++ void __user *buf)
++{
++ struct fw_mgmt_ioc_get_intf_version intf_fw_info;
++ struct fw_mgmt_ioc_get_backend_version backend_fw_info;
++ struct fw_mgmt_ioc_intf_load_and_validate intf_load;
++ struct fw_mgmt_ioc_backend_fw_update backend_update;
++ unsigned int timeout;
++ int ret;
++
++ /* Reject any operations after mode-switch has started */
++ if (fw_mgmt->mode_switch_started)
++ return -EBUSY;
++
++ switch (cmd) {
++ case FW_MGMT_IOC_GET_INTF_FW:
++ ret = fw_mgmt_interface_fw_version_operation(fw_mgmt,
++ &intf_fw_info);
++ if (ret)
++ return ret;
++
++ if (copy_to_user(buf, &intf_fw_info, sizeof(intf_fw_info)))
++ return -EFAULT;
++
++ return 0;
++ case FW_MGMT_IOC_GET_BACKEND_FW:
++ if (copy_from_user(&backend_fw_info, buf,
++ sizeof(backend_fw_info)))
++ return -EFAULT;
++
++ ret = fw_mgmt_backend_fw_version_operation(fw_mgmt,
++ &backend_fw_info);
++ if (ret)
++ return ret;
++
++ if (copy_to_user(buf, &backend_fw_info,
++ sizeof(backend_fw_info)))
++ return -EFAULT;
++
++ return 0;
++ case FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE:
++ if (copy_from_user(&intf_load, buf, sizeof(intf_load)))
++ return -EFAULT;
++
++ ret = fw_mgmt_load_and_validate_operation(fw_mgmt,
++ intf_load.load_method, intf_load.firmware_tag);
++ if (ret)
++ return ret;
++
++ if (!wait_for_completion_timeout(&fw_mgmt->completion,
++ fw_mgmt->timeout_jiffies)) {
++ dev_err(fw_mgmt->parent, "timed out waiting for firmware load and validation to finish\n");
++ return -ETIMEDOUT;
++ }
++
++ intf_load.status = fw_mgmt->intf_fw_status;
++ intf_load.major = fw_mgmt->intf_fw_major;
++ intf_load.minor = fw_mgmt->intf_fw_minor;
++
++ if (copy_to_user(buf, &intf_load, sizeof(intf_load)))
++ return -EFAULT;
++
++ return 0;
++ case FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE:
++ if (copy_from_user(&backend_update, buf,
++ sizeof(backend_update)))
++ return -EFAULT;
++
++ ret = fw_mgmt_backend_fw_update_operation(fw_mgmt,
++ backend_update.firmware_tag);
++ if (ret)
++ return ret;
++
++ if (!wait_for_completion_timeout(&fw_mgmt->completion,
++ fw_mgmt->timeout_jiffies)) {
++ dev_err(fw_mgmt->parent, "timed out waiting for backend firmware update to finish\n");
++ return -ETIMEDOUT;
++ }
++
++ backend_update.status = fw_mgmt->backend_fw_status;
++
++ if (copy_to_user(buf, &backend_update, sizeof(backend_update)))
++ return -EFAULT;
++
++ return 0;
++ case FW_MGMT_IOC_SET_TIMEOUT_MS:
++ if (get_user(timeout, (unsigned int __user *)buf))
++ return -EFAULT;
++
++ if (!timeout) {
++ dev_err(fw_mgmt->parent, "timeout can't be zero\n");
++ return -EINVAL;
++ }
++
++ fw_mgmt->timeout_jiffies = msecs_to_jiffies(timeout);
++
++ return 0;
++ case FW_MGMT_IOC_MODE_SWITCH:
++ if (!fw_mgmt->intf_fw_loaded) {
++ dev_err(fw_mgmt->parent,
++ "Firmware not loaded for mode-switch\n");
++ return -EPERM;
++ }
++
++ /*
++ * Disallow new ioctls as the fw-core bundle driver is going to
++ * get disconnected soon and the character device will get
++ * removed.
++ */
++ fw_mgmt->mode_switch_started = true;
++
++ ret = gb_interface_request_mode_switch(fw_mgmt->connection->intf);
++ if (ret) {
++ dev_err(fw_mgmt->parent, "Mode-switch failed: %d\n",
++ ret);
++ fw_mgmt->mode_switch_started = false;
++ return ret;
++ }
++
++ return 0;
++ default:
++ return -ENOTTY;
++ }
++}
++
++static long fw_mgmt_ioctl_unlocked(struct file *file, unsigned int cmd,
++ unsigned long arg)
++{
++ struct fw_mgmt *fw_mgmt = file->private_data;
++ struct gb_bundle *bundle = fw_mgmt->connection->bundle;
++ int ret = -ENODEV;
++
++ /*
++ * Serialize ioctls.
++ *
++ * We don't want the user to do few operations in parallel. For example,
++ * updating Interface firmware in parallel for the same Interface. There
++ * is no need to do things in parallel for speed and we can avoid having
++ * complicated code for now.
++ *
++ * This is also used to protect ->disabled, which is used to check if
++ * the connection is getting disconnected, so that we don't start any
++ * new operations.
++ */
++ mutex_lock(&fw_mgmt->mutex);
++ if (!fw_mgmt->disabled) {
++ ret = gb_pm_runtime_get_sync(bundle);
++ if (!ret) {
++ ret = fw_mgmt_ioctl(fw_mgmt, cmd, (void __user *)arg);
++ gb_pm_runtime_put_autosuspend(bundle);
++ }
++ }
++ mutex_unlock(&fw_mgmt->mutex);
++
++ return ret;
++}
++
++static const struct file_operations fw_mgmt_fops = {
++ .owner = THIS_MODULE,
++ .open = fw_mgmt_open,
++ .release = fw_mgmt_release,
++ .unlocked_ioctl = fw_mgmt_ioctl_unlocked,
++};
++
++int gb_fw_mgmt_request_handler(struct gb_operation *op)
++{
++ u8 type = op->type;
++
++ switch (type) {
++ case GB_FW_MGMT_TYPE_LOADED_FW:
++ return fw_mgmt_interface_fw_loaded_operation(op);
++ case GB_FW_MGMT_TYPE_BACKEND_FW_UPDATED:
++ return fw_mgmt_backend_fw_updated_operation(op);
++ default:
++ dev_err(&op->connection->bundle->dev,
++ "unsupported request: %u\n", type);
++ return -EINVAL;
++ }
++}
++
++int gb_fw_mgmt_connection_init(struct gb_connection *connection)
++{
++ struct fw_mgmt *fw_mgmt;
++ int ret, minor;
++
++ if (!connection)
++ return 0;
++
++ fw_mgmt = kzalloc(sizeof(*fw_mgmt), GFP_KERNEL);
++ if (!fw_mgmt)
++ return -ENOMEM;
++
++ fw_mgmt->parent = &connection->bundle->dev;
++ fw_mgmt->timeout_jiffies = msecs_to_jiffies(FW_MGMT_TIMEOUT_MS);
++ fw_mgmt->connection = connection;
++
++ gb_connection_set_data(connection, fw_mgmt);
++ init_completion(&fw_mgmt->completion);
++ ida_init(&fw_mgmt->id_map);
++ mutex_init(&fw_mgmt->mutex);
++ kref_init(&fw_mgmt->kref);
++
++ mutex_lock(&list_mutex);
++ list_add(&fw_mgmt->node, &fw_mgmt_list);
++ mutex_unlock(&list_mutex);
++
++ ret = gb_connection_enable(connection);
++ if (ret)
++ goto err_list_del;
++
++ minor = ida_simple_get(&fw_mgmt_minors_map, 0, NUM_MINORS, GFP_KERNEL);
++ if (minor < 0) {
++ ret = minor;
++ goto err_connection_disable;
++ }
++
++ /* Add a char device to allow userspace to interact with fw-mgmt */
++ fw_mgmt->dev_num = MKDEV(MAJOR(fw_mgmt_dev_num), minor);
++ cdev_init(&fw_mgmt->cdev, &fw_mgmt_fops);
++
++ ret = cdev_add(&fw_mgmt->cdev, fw_mgmt->dev_num, 1);
++ if (ret)
++ goto err_remove_ida;
++
++ /* Add a soft link to the previously added char-dev within the bundle */
++ fw_mgmt->class_device = device_create(fw_mgmt_class, fw_mgmt->parent,
++ fw_mgmt->dev_num, NULL,
++ "gb-fw-mgmt-%d", minor);
++ if (IS_ERR(fw_mgmt->class_device)) {
++ ret = PTR_ERR(fw_mgmt->class_device);
++ goto err_del_cdev;
++ }
++
++ return 0;
++
++err_del_cdev:
++ cdev_del(&fw_mgmt->cdev);
++err_remove_ida:
++ ida_simple_remove(&fw_mgmt_minors_map, minor);
++err_connection_disable:
++ gb_connection_disable(connection);
++err_list_del:
++ mutex_lock(&list_mutex);
++ list_del(&fw_mgmt->node);
++ mutex_unlock(&list_mutex);
++
++ put_fw_mgmt(fw_mgmt);
++
++ return ret;
++}
++
++void gb_fw_mgmt_connection_exit(struct gb_connection *connection)
++{
++ struct fw_mgmt *fw_mgmt;
++
++ if (!connection)
++ return;
++
++ fw_mgmt = gb_connection_get_data(connection);
++
++ device_destroy(fw_mgmt_class, fw_mgmt->dev_num);
++ cdev_del(&fw_mgmt->cdev);
++ ida_simple_remove(&fw_mgmt_minors_map, MINOR(fw_mgmt->dev_num));
++
++ /*
++ * Disallow any new ioctl operations on the char device and wait for
++ * existing ones to finish.
++ */
++ mutex_lock(&fw_mgmt->mutex);
++ fw_mgmt->disabled = true;
++ mutex_unlock(&fw_mgmt->mutex);
++
++ /* All pending greybus operations should have finished by now */
++ gb_connection_disable(fw_mgmt->connection);
++
++ /* Disallow new users to get access to the fw_mgmt structure */
++ mutex_lock(&list_mutex);
++ list_del(&fw_mgmt->node);
++ mutex_unlock(&list_mutex);
++
++ /*
++ * All current users of fw_mgmt would have taken a reference to it by
++ * now, we can drop our reference and wait the last user will get
++ * fw_mgmt freed.
++ */
++ put_fw_mgmt(fw_mgmt);
++}
++
++int fw_mgmt_init(void)
++{
++ int ret;
++
++ fw_mgmt_class = class_create(THIS_MODULE, "gb_fw_mgmt");
++ if (IS_ERR(fw_mgmt_class))
++ return PTR_ERR(fw_mgmt_class);
++
++ ret = alloc_chrdev_region(&fw_mgmt_dev_num, 0, NUM_MINORS,
++ "gb_fw_mgmt");
++ if (ret)
++ goto err_remove_class;
++
++ return 0;
++
++err_remove_class:
++ class_destroy(fw_mgmt_class);
++ return ret;
++}
++
++void fw_mgmt_exit(void)
++{
++ unregister_chrdev_region(fw_mgmt_dev_num, NUM_MINORS);
++ class_destroy(fw_mgmt_class);
++ ida_destroy(&fw_mgmt_minors_map);
++}
+--- /dev/null
++++ b/drivers/greybus/greybus_firmware.h
+@@ -0,0 +1,120 @@
++/*
++ * Greybus Firmware Management User Header
++ *
++ * This file is provided under a dual BSD/GPLv2 license. When using or
++ * redistributing this file, you may do so under either license.
++ *
++ * GPL LICENSE SUMMARY
++ *
++ * Copyright(c) 2016 Google Inc. All rights reserved.
++ * Copyright(c) 2016 Linaro Ltd. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License 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 version 2 for more details.
++ *
++ * BSD LICENSE
++ *
++ * Copyright(c) 2016 Google Inc. All rights reserved.
++ * Copyright(c) 2016 Linaro Ltd. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * * Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * * Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ * * Neither the name of Google Inc. or Linaro Ltd. nor the names of
++ * its contributors may be used to endorse or promote products
++ * derived from this software without specific prior written
++ * permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR
++ * LINARO LTD. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#ifndef __GREYBUS_FIRMWARE_USER_H
++#define __GREYBUS_FIRMWARE_USER_H
++
++#include <linux/ioctl.h>
++#include <linux/types.h>
++
++#define GB_FIRMWARE_U_TAG_MAX_SIZE 10
++
++#define GB_FW_U_LOAD_METHOD_UNIPRO 0x01
++#define GB_FW_U_LOAD_METHOD_INTERNAL 0x02
++
++#define GB_FW_U_LOAD_STATUS_FAILED 0x00
++#define GB_FW_U_LOAD_STATUS_UNVALIDATED 0x01
++#define GB_FW_U_LOAD_STATUS_VALIDATED 0x02
++#define GB_FW_U_LOAD_STATUS_VALIDATION_FAILED 0x03
++
++#define GB_FW_U_BACKEND_FW_STATUS_SUCCESS 0x01
++#define GB_FW_U_BACKEND_FW_STATUS_FAIL_FIND 0x02
++#define GB_FW_U_BACKEND_FW_STATUS_FAIL_FETCH 0x03
++#define GB_FW_U_BACKEND_FW_STATUS_FAIL_WRITE 0x04
++#define GB_FW_U_BACKEND_FW_STATUS_INT 0x05
++#define GB_FW_U_BACKEND_FW_STATUS_RETRY 0x06
++#define GB_FW_U_BACKEND_FW_STATUS_NOT_SUPPORTED 0x07
++
++#define GB_FW_U_BACKEND_VERSION_STATUS_SUCCESS 0x01
++#define GB_FW_U_BACKEND_VERSION_STATUS_NOT_AVAILABLE 0x02
++#define GB_FW_U_BACKEND_VERSION_STATUS_NOT_SUPPORTED 0x03
++#define GB_FW_U_BACKEND_VERSION_STATUS_RETRY 0x04
++#define GB_FW_U_BACKEND_VERSION_STATUS_FAIL_INT 0x05
++
++/* IOCTL support */
++struct fw_mgmt_ioc_get_intf_version {
++ __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_SIZE];
++ __u16 major;
++ __u16 minor;
++} __attribute__ ((__packed__));
++
++struct fw_mgmt_ioc_get_backend_version {
++ __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_SIZE];
++ __u16 major;
++ __u16 minor;
++ __u8 status;
++} __attribute__ ((__packed__));
++
++struct fw_mgmt_ioc_intf_load_and_validate {
++ __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_SIZE];
++ __u8 load_method;
++ __u8 status;
++ __u16 major;
++ __u16 minor;
++} __attribute__ ((__packed__));
++
++struct fw_mgmt_ioc_backend_fw_update {
++ __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_SIZE];
++ __u8 status;
++} __attribute__ ((__packed__));
++
++#define FW_MGMT_IOCTL_BASE 'F'
++#define FW_MGMT_IOC_GET_INTF_FW _IOR(FW_MGMT_IOCTL_BASE, 0, struct fw_mgmt_ioc_get_intf_version)
++#define FW_MGMT_IOC_GET_BACKEND_FW _IOWR(FW_MGMT_IOCTL_BASE, 1, struct fw_mgmt_ioc_get_backend_version)
++#define FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE _IOWR(FW_MGMT_IOCTL_BASE, 2, struct fw_mgmt_ioc_intf_load_and_validate)
++#define FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE _IOWR(FW_MGMT_IOCTL_BASE, 3, struct fw_mgmt_ioc_backend_fw_update)
++#define FW_MGMT_IOC_SET_TIMEOUT_MS _IOW(FW_MGMT_IOCTL_BASE, 4, unsigned int)
++#define FW_MGMT_IOC_MODE_SWITCH _IO(FW_MGMT_IOCTL_BASE, 5)
++
++#endif /* __GREYBUS_FIRMWARE_USER_H */
++