From: Greg KH <gregkh@linuxfoundation.org>
To: Arnd Bergmann <arnd@arndb.de>, linux-kernel@vger.kernel.org
Cc: Johan Hovold <johan@hovoldconsulting.com>,
Rui Miguel Silva <rmfrfs@gmail.com>,
Laurent Pinchart <laurent.pinchart@ideasonboard.com>,
Sandeep Patil <sspatil@google.com>,
Matt Porter <mporter@kernel.crashing.org>,
John Stultz <john.stultz@linaro.org>,
Rob Herring <robh@kernel.org>,
Viresh Kumar <viresh.kumar@linaro.org>,
Alex Elder <elder@linaro.org>, David Lin <dtwlin@google.com>,
"Bryan O'Donoghue" <pure.logic@nexus-software.ie>,
Vaibhav Agarwal <vaibhav.agarwal@linaro.org>,
Mark Greer <mgreer@animalcreek.com>
Subject: [patch 09/32] greybus: firmware download class driver
Date: Fri, 16 Sep 2016 15:24:38 +0200 [thread overview]
Message-ID: <20160916132438.GG31174@kroah.com> (raw)
In-Reply-To: <20160916064058.GA17821@kroah.com>
This driver implements the Greybus firmware download protocol.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
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 */
+
next prev parent reply other threads:[~2016-09-16 13:24 UTC|newest]
Thread overview: 84+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-09-14 10:09 [GIT PULL] Greybus driver subsystem for 4.9-rc1 Greg KH
2016-09-14 17:36 ` Mark Rutland
2016-09-14 17:36 ` Mark Rutland
2016-09-14 18:07 ` Greg KH
2016-09-14 18:07 ` Greg KH
2016-09-14 18:29 ` Greg KH
2016-09-14 18:29 ` Greg KH
2016-09-14 19:05 ` Joe Perches
2016-09-14 19:05 ` Joe Perches
2016-09-15 9:35 ` Bryan O'Donoghue
2016-09-15 9:35 ` Bryan O'Donoghue
2016-09-15 10:13 ` Mark Rutland
2016-09-15 10:13 ` Mark Rutland
2016-09-15 10:35 ` Bryan O'Donoghue
2016-09-15 10:35 ` Bryan O'Donoghue
2016-09-15 10:47 ` Bryan O'Donoghue
2016-09-15 10:47 ` Bryan O'Donoghue
2016-09-15 11:20 ` Mark Rutland
2016-09-15 11:20 ` Mark Rutland
2016-09-15 11:48 ` Bryan O'Donoghue
2016-09-15 11:48 ` Bryan O'Donoghue
2016-09-15 12:46 ` Mark Rutland
2016-09-15 12:46 ` Mark Rutland
2016-09-15 15:40 ` Bryan O'Donoghue
2016-09-15 15:40 ` Bryan O'Donoghue
2016-09-15 15:47 ` Mark Rutland
2016-09-15 15:47 ` Mark Rutland
2016-09-15 16:09 ` Bryan O'Donoghue
2016-09-15 16:09 ` Bryan O'Donoghue
2016-09-14 20:07 ` Rob Herring
2016-09-14 20:07 ` Rob Herring
2016-09-15 10:17 ` Greg KH
2016-09-15 10:17 ` Greg KH
2016-09-15 11:02 ` Bryan O'Donoghue
2016-09-15 11:02 ` Bryan O'Donoghue
[not found] ` <20160915122141.650632149@bubbles.kroah.org>
[not found] ` <20160915122234.640367870@bubbles.kroah.org>
2016-09-15 13:16 ` [patch 11/32] greybus: camera driver Laurent Pinchart
2016-09-15 14:45 ` [GIT PULL] Greybus driver subsystem for 4.9-rc1 Mark Brown
2016-09-16 6:05 ` Greg KH
2016-09-16 10:18 ` Mark Brown
2016-09-16 13:22 ` Greg KH
2016-09-16 14:24 ` Greg KH
2016-09-20 6:41 ` Greg KH
2016-09-20 7:12 ` Vaibhav Agarwal
2016-09-16 12:18 ` Arnd Bergmann
2016-09-21 13:02 ` Mark Rutland
2016-09-21 14:13 ` Greg KH
2016-09-16 6:40 ` [patch 00/32] Greybus driver subsystem Greg KH
2016-09-16 6:41 ` [patch 02/32] greybus: interface control logic Greg KH
2016-09-16 13:22 ` [patch 03/32] greybus: operations logic Greg KH
2016-09-16 13:23 ` [patch 04/32] greybus: host driver framework Greg KH
2016-09-16 13:23 ` [patch 05/32] greybus: trace.h Greg KH
2016-09-16 13:23 ` [patch 06/32] greybus: svc driver/watchdog Greg KH
2016-09-16 13:23 ` [patch 07/32] greybus: core code Greg KH
2016-09-16 13:24 ` [patch 08/32] greybus: bootrom driver Greg KH
2016-09-16 13:24 ` Greg KH [this message]
2016-09-16 13:24 ` [patch 10/32] greybus: audio driver Greg KH
2016-09-16 13:25 ` [patch 11/32] greybus: camera driver Greg KH
2016-09-16 13:25 ` [patch 12/32] greybus: es2 host driver Greg KH
2016-10-07 13:43 ` Pavel Machek
2016-09-16 14:09 ` [patch 13/32] greybus: HID driver Greg KH
2016-09-16 14:10 ` [patch 14/32] greybus: LED driver Greg KH
2016-10-07 13:36 ` Pavel Machek
2016-10-07 13:41 ` Greg KH
2016-09-16 14:10 ` [patch 15/32] greybus: logging driver Greg KH
2016-09-16 14:10 ` [patch 16/32] greybus: loopback driver Greg KH
2016-09-16 14:10 ` [patch 17/32] greybus: power supply driver Greg KH
2016-10-07 13:49 ` Pavel Machek
2016-10-07 14:12 ` Greg KH
2016-10-07 18:15 ` Pavel Machek
2016-09-16 14:11 ` [patch 18/32] greybus: raw driver Greg KH
2016-09-16 14:11 ` [patch 19/32] greybus: timesync driver Greg KH
2016-09-16 14:11 ` [patch 20/32] greybus: vibrator driver Greg KH
2016-09-16 14:19 ` [patch 21/32] greybus: arche platform driver Greg KH
2016-09-16 14:20 ` [patch 22/32] greybus: bridged phy bus code Greg KH
2016-09-16 14:20 ` [patch 23/32] greybus: bridged phy gpio driver Greg KH
2016-09-16 14:20 ` [patch 24/32] greybus: bridged phy i2c driver Greg KH
2016-09-16 14:20 ` [patch 25/32] greybus: bridged phy pwm driver Greg KH
2016-09-16 14:21 ` [patch 26/32] greybus: bridged phy sdio driver Greg KH
2016-09-16 14:21 ` [patch 27/32] greybus: bridged phy spi driver Greg KH
2016-09-16 14:21 ` [patch 28/32] greybus: bridged phy uart driver Greg KH
2016-09-16 14:21 ` [patch 29/32] greybus: bridged phy usb driver Greg KH
2016-09-16 14:22 ` [patch 30/32] greybus: tools Greg KH
2016-09-16 14:22 ` [patch 31/32] greybus: documentation Greg KH
2016-09-16 14:22 ` [patch 32/32] greybus: add to the build Greg KH
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20160916132438.GG31174@kroah.com \
--to=gregkh@linuxfoundation.org \
--cc=arnd@arndb.de \
--cc=dtwlin@google.com \
--cc=elder@linaro.org \
--cc=johan@hovoldconsulting.com \
--cc=john.stultz@linaro.org \
--cc=laurent.pinchart@ideasonboard.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mgreer@animalcreek.com \
--cc=mporter@kernel.crashing.org \
--cc=pure.logic@nexus-software.ie \
--cc=rmfrfs@gmail.com \
--cc=robh@kernel.org \
--cc=sspatil@google.com \
--cc=vaibhav.agarwal@linaro.org \
--cc=viresh.kumar@linaro.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.