mirror of
https://github.com/linux-msm/cdba.git
synced 2026-10-06 21:32:09 -07:00
cdba-server: Add support for flashing, using QDL
The client can now request the device to enter EDL mode and the server's device and driver model can power up the board to this state. Introduce four new messages to the cdba protocol: - EDL_PRESENT: used by the server to signal the presence of the EDL device - EDL_DOWNLOAD: used by the client to transfer a binary blob to the server, mimicing the fastboot protocol - EDL_WRITE: used by the client to indicate the target for the downloaded binary blob. The server may start flashing the image if supported. - EDL_RESET: used by the client to request that the flash operations are finalized and the board restarts. Introduce a new "EDL" module that will look for EDL USB devices and if found signal the client about its presence. Implement EDL_DOWNLOAD by streaming the incoming blob into a temporary file, let EDL_WRITE tag the image with information about where it should be written. Then as we're relying on QDL, invoke the external tool as part of the EDL_RESET, "flushing out" the pending operations. Support for matching EDL device by serial number is left as a todo item. Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
This commit is contained in:
1 parent
403e5c36ac
commit
fc100a6442
10 files changed
+435
No files matched your search
+132
@@ -11,6 +11,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <syslog.h>
|
||||
|
||||
@@ -27,6 +28,26 @@ static const char *username;
|
||||
|
||||
struct device *selected_device;
|
||||
|
||||
struct edl_file {
|
||||
struct list_head node;
|
||||
|
||||
int fd;
|
||||
|
||||
char *filename;
|
||||
char *target;
|
||||
};
|
||||
|
||||
static struct list_head edl_files = LIST_INIT(edl_files);
|
||||
|
||||
static void edl_present(bool present)
|
||||
{
|
||||
uint8_t value = present ? 1 : 0;
|
||||
|
||||
warnx("edl is %spresent", present? "" : "not ");
|
||||
|
||||
cdba_send_buf(MSG_EDL_PRESENT, 1, &value);
|
||||
}
|
||||
|
||||
static void fastboot_opened(struct fastboot *fb, void *data)
|
||||
{
|
||||
const uint8_t one = 1;
|
||||
@@ -61,6 +82,7 @@ static void msg_select_board(const void *param)
|
||||
fprintf(stderr, "failed to open %s\n", (const char *)param);
|
||||
watch_quit();
|
||||
} else {
|
||||
device_edl_open(selected_device, edl_present);
|
||||
device_fastboot_open(selected_device, &fastboot_ops);
|
||||
}
|
||||
|
||||
@@ -94,6 +116,107 @@ static void msg_fastboot_download(const void *data, size_t len)
|
||||
}
|
||||
}
|
||||
|
||||
static struct edl_file *current_edl_file;
|
||||
|
||||
static void msg_edl_download(const void *data, size_t len)
|
||||
{
|
||||
char template[] = "/tmp/cdba.XXXXXX";
|
||||
struct edl_file *edl;
|
||||
|
||||
edl = current_edl_file;
|
||||
|
||||
if (!edl) {
|
||||
edl = calloc(1, sizeof(*edl));
|
||||
|
||||
edl->filename = strdup(template);
|
||||
edl->fd = mkstemp(edl->filename);
|
||||
if (edl->fd < 0)
|
||||
err(1, "failed to create temporary file");
|
||||
|
||||
list_append(&edl_files, &edl->node);
|
||||
|
||||
current_edl_file = edl;
|
||||
}
|
||||
|
||||
write(edl->fd, data, len);
|
||||
|
||||
if (len == 0)
|
||||
close(edl->fd);
|
||||
}
|
||||
|
||||
static void msg_edl_flash(const void *data, size_t len)
|
||||
{
|
||||
const char *target = data;
|
||||
|
||||
fprintf(stderr, "edl flash into '%s'\n", target);
|
||||
|
||||
current_edl_file->target = strdup(target);
|
||||
current_edl_file = NULL;
|
||||
}
|
||||
|
||||
static void msg_edl_reset(void)
|
||||
{
|
||||
struct edl_file *edl;
|
||||
const char **argv;
|
||||
size_t args;
|
||||
size_t arg = 0;
|
||||
|
||||
fprintf(stderr, "edl reset\n");
|
||||
|
||||
if (!selected_device) {
|
||||
fprintf(stderr, "no device selected\n");
|
||||
watch_quit();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selected_device->qdl_programmer) {
|
||||
fprintf(stderr, "no EDL support configured for %s\n", selected_device->name);
|
||||
watch_quit();
|
||||
return;
|
||||
}
|
||||
|
||||
list_for_each_entry(edl, &edl_files, node) {
|
||||
if (!edl->target) {
|
||||
fprintf(stderr, "EDL image without flash target\n");
|
||||
watch_quit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
args = 7 + list_len(&edl_files) * 3 + 1;
|
||||
argv = calloc(args, sizeof(char *));
|
||||
|
||||
argv[arg++] = "qdl";
|
||||
argv[arg++] = selected_device->qdl_programmer;
|
||||
|
||||
if (selected_device->qdl_storage) {
|
||||
argv[arg++] = "--storage";
|
||||
argv[arg++] = selected_device->qdl_storage;
|
||||
}
|
||||
|
||||
if (selected_device->qdl_serial) {
|
||||
argv[arg++] = "--serial";
|
||||
argv[arg++] = selected_device->qdl_serial;
|
||||
}
|
||||
|
||||
list_for_each_entry(edl, &edl_files, node) {
|
||||
argv[arg++] = "write";
|
||||
argv[arg++] = edl->target;
|
||||
argv[arg++] = edl->filename;
|
||||
}
|
||||
argv[arg] = NULL;
|
||||
|
||||
if (fork() == 0) {
|
||||
dup2(STDERR_FILENO, STDOUT_FILENO);
|
||||
execvp("qdl", (char **)argv);
|
||||
err(127, "failed to spawn qdl");
|
||||
}
|
||||
wait(NULL);
|
||||
|
||||
list_for_each_entry(edl, &edl_files, node)
|
||||
unlink(edl->filename);
|
||||
}
|
||||
|
||||
static void msg_fastboot_continue(void)
|
||||
{
|
||||
device_fastboot_continue(selected_device);
|
||||
@@ -222,6 +345,15 @@ static int handle_stdin(int fd, void *buf)
|
||||
case MSG_KEY_PRESS:
|
||||
msg_key_press(msg->data, msg->len);
|
||||
break;
|
||||
case MSG_EDL_DOWNLOAD:
|
||||
msg_edl_download(msg->data, msg->len);
|
||||
break;
|
||||
case MSG_EDL_WRITE:
|
||||
msg_edl_flash(msg->data, msg->len);
|
||||
break;
|
||||
case MSG_EDL_RESET:
|
||||
msg_edl_reset();
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unk %d len %d\n", msg->type, msg->len);
|
||||
exit(1);
|
||||
|
||||
@@ -32,6 +32,10 @@ enum {
|
||||
MSG_BOARD_INFO,
|
||||
MSG_FASTBOOT_CONTINUE,
|
||||
MSG_KEY_PRESS,
|
||||
MSG_EDL_PRESENT,
|
||||
MSG_EDL_DOWNLOAD,
|
||||
MSG_EDL_WRITE,
|
||||
MSG_EDL_RESET,
|
||||
};
|
||||
|
||||
struct key_press {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
devices:
|
||||
- board: myboard
|
||||
name: "My Board"
|
||||
description: |
|
||||
My Awesome board with QDL flashing support
|
||||
console: /dev/ttyABC0
|
||||
fastboot: cacafada
|
||||
fastboot_set_active: true
|
||||
fastboot_key_timeout: 2
|
||||
qdl_programmer: /path/to/prog_firehose_ddr.elf
|
||||
qdl_serial: 1234567890ABCDEF
|
||||
qdl_storage: ufs
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "cdba-server.h"
|
||||
#include "device.h"
|
||||
#include "edl.h"
|
||||
#include "fastboot.h"
|
||||
#include "list.h"
|
||||
#include "ppps.h"
|
||||
@@ -306,6 +307,11 @@ int device_write(struct device *device, const void *buf, size_t len)
|
||||
return device_console(device, write, buf, len);
|
||||
}
|
||||
|
||||
void device_edl_open(struct device *device, void (*edl_present)(bool present))
|
||||
{
|
||||
device->edl = edl_open(device->serial, edl_present);
|
||||
}
|
||||
|
||||
void device_fastboot_open(struct device *device,
|
||||
struct fastboot_ops *fastboot_ops)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "list.h"
|
||||
|
||||
struct cdb_assist;
|
||||
struct edl;
|
||||
struct fastboot;
|
||||
struct fastboot_ops;
|
||||
struct device;
|
||||
@@ -43,6 +44,7 @@ struct device {
|
||||
bool tickle_mmc;
|
||||
bool usb_always_on;
|
||||
bool power_always_on;
|
||||
struct edl *edl;
|
||||
struct fastboot *fastboot;
|
||||
unsigned int fastboot_key_timeout;
|
||||
unsigned int power_key_press_ms;
|
||||
@@ -62,6 +64,10 @@ struct device {
|
||||
void *cdb;
|
||||
void *console;
|
||||
|
||||
char *qdl_programmer;
|
||||
char *qdl_serial;
|
||||
char *qdl_storage;
|
||||
|
||||
char *status_cmd;
|
||||
|
||||
struct list_head node;
|
||||
@@ -88,6 +94,7 @@ int device_write(struct device *device, const void *buf, size_t len);
|
||||
|
||||
void device_boot(struct device *device, const void *data, size_t len);
|
||||
|
||||
void device_edl_open(struct device *device, void (*edl_present)(bool present));
|
||||
void device_fastboot_open(struct device *device,
|
||||
struct fastboot_ops *fastboot_ops);
|
||||
void device_fastboot_boot(struct device *device);
|
||||
|
||||
@@ -215,6 +215,12 @@ static void parse_board(struct device_parser *dp)
|
||||
dev->status_cmd = strdup(value);
|
||||
} else if (!strcmp(key, "power_always_on")) {
|
||||
dev->power_always_on = !strcmp(value, "true");
|
||||
} else if (!strcmp(key, "qdl_programmer")) {
|
||||
dev->qdl_programmer = strdup(value);
|
||||
} else if (!strcmp(key, "qdl_serial")) {
|
||||
dev->qdl_serial = strdup(value);
|
||||
} else if (!strcmp(key, "qdl_storage")) {
|
||||
dev->qdl_storage = strdup(value);
|
||||
} else {
|
||||
fprintf(stderr, "device parser: unknown key \"%s\"\n", key);
|
||||
exit(1);
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
/*
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <libudev.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "edl.h"
|
||||
#include "watch.h"
|
||||
|
||||
#define MAX_USBFS_BULK_SIZE (16*1024)
|
||||
|
||||
enum {
|
||||
EDL_STATE_START,
|
||||
EDL_STATE_OPENED,
|
||||
EDL_STATE_CLOSED,
|
||||
};
|
||||
|
||||
struct edl {
|
||||
const char *serial;
|
||||
const char *dev_path;
|
||||
|
||||
void *data;
|
||||
|
||||
struct edl_ops *ops;
|
||||
void (*present)(bool present);
|
||||
|
||||
int state;
|
||||
|
||||
struct udev_monitor *mon;
|
||||
};
|
||||
|
||||
static int parse_usb_desc(int usbfd)
|
||||
{
|
||||
const struct usb_interface_descriptor *ifc;
|
||||
const struct usb_endpoint_descriptor *ept;
|
||||
const struct usb_device_descriptor *dev;
|
||||
const struct usb_config_descriptor *cfg;
|
||||
const struct usb_descriptor_header *hdr;
|
||||
unsigned type;
|
||||
unsigned k;
|
||||
unsigned l;
|
||||
ssize_t n;
|
||||
char *ptr;
|
||||
char *end;
|
||||
char desc[1024];
|
||||
|
||||
n = read(usbfd, desc, sizeof(desc));
|
||||
if (n < 0)
|
||||
return n;
|
||||
|
||||
ptr = desc;
|
||||
end = ptr + n;
|
||||
|
||||
dev = (void *)ptr;
|
||||
ptr += dev->bLength;
|
||||
if (ptr >= end || dev->bDescriptorType != USB_DT_DEVICE)
|
||||
return -EINVAL;
|
||||
|
||||
if (dev->idVendor != 0x05c6)
|
||||
return -ENOENT;
|
||||
if (dev->idProduct != 0x9008)
|
||||
return -ENOENT;
|
||||
|
||||
cfg = (void *)ptr;
|
||||
ptr += cfg->bLength;
|
||||
if (ptr >= end || cfg->bDescriptorType != USB_DT_CONFIG)
|
||||
return -EINVAL;
|
||||
|
||||
for (k = 0; k < cfg->bNumInterfaces; k++) {
|
||||
if (ptr >= end)
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
ifc = (void *)ptr;
|
||||
if (ifc->bLength < USB_DT_INTERFACE_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
ptr += ifc->bLength;
|
||||
} while (ptr < end && ifc->bDescriptorType != USB_DT_INTERFACE);
|
||||
|
||||
for (l = 0; l < ifc->bNumEndpoints; l++) {
|
||||
if (ptr >= end)
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
ept = (void *)ptr;
|
||||
if (ept->bLength < USB_DT_ENDPOINT_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
ptr += ept->bLength;
|
||||
} while (ptr < end && ept->bDescriptorType != USB_DT_ENDPOINT);
|
||||
|
||||
type = ept->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
|
||||
if (type != USB_ENDPOINT_XFER_BULK)
|
||||
continue;
|
||||
|
||||
if (ptr >= end)
|
||||
break;
|
||||
|
||||
hdr = (void *)ptr;
|
||||
if (hdr->bDescriptorType == USB_DT_SS_ENDPOINT_COMP)
|
||||
ptr += USB_DT_SS_EP_COMP_SIZE;
|
||||
}
|
||||
|
||||
if (ifc->bInterfaceClass != 0xff)
|
||||
continue;
|
||||
|
||||
if (ifc->bInterfaceSubClass != 0xff)
|
||||
continue;
|
||||
|
||||
if (ifc->bInterfaceProtocol != 0xff &&
|
||||
ifc->bInterfaceProtocol != 16 &&
|
||||
ifc->bInterfaceProtocol != 17)
|
||||
continue;
|
||||
|
||||
/* TODO: check serial number */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int handle_edl_add(struct edl *edl, struct udev_device *dev)
|
||||
{
|
||||
const char *dev_path;
|
||||
const char *dev_node;
|
||||
int usbfd;
|
||||
int ret;
|
||||
|
||||
dev_path = udev_device_get_devpath(dev);
|
||||
dev_node = udev_device_get_devnode(dev);
|
||||
|
||||
usbfd = open(dev_node, O_RDWR);
|
||||
if (usbfd < 0)
|
||||
return usbfd;
|
||||
|
||||
ret = parse_usb_desc(usbfd);
|
||||
if (ret < 0) {
|
||||
close(usbfd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
edl->dev_path = strdup(dev_path);
|
||||
|
||||
if (edl->present)
|
||||
edl->present(true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_udev_event(int fd, void *data)
|
||||
{
|
||||
struct edl *edl = data;
|
||||
struct udev_device* dev;
|
||||
const char *dev_path;
|
||||
const char *action;
|
||||
|
||||
dev = udev_monitor_receive_device(edl->mon);
|
||||
|
||||
action = udev_device_get_action(dev);
|
||||
dev_path = udev_device_get_devpath(dev);
|
||||
|
||||
if (!action || !dev_path)
|
||||
goto unref_dev;
|
||||
|
||||
if (!strcmp(action, "add")) {
|
||||
handle_edl_add(edl, dev);
|
||||
} else if (!strcmp(action, "remove")) {
|
||||
if (!edl->dev_path || strcmp(dev_path, edl->dev_path))
|
||||
goto unref_dev;
|
||||
|
||||
edl->dev_path = NULL;
|
||||
|
||||
if (edl->present)
|
||||
edl->present(false);
|
||||
}
|
||||
|
||||
unref_dev:
|
||||
udev_device_unref(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct edl *edl_open(const char *serial, void (*present)(bool present))
|
||||
{
|
||||
struct edl *edl;
|
||||
struct udev* udev;
|
||||
int fd;
|
||||
struct udev_enumerate* udev_enum;
|
||||
struct udev_list_entry* first, *item;
|
||||
|
||||
udev = udev_new();
|
||||
if (!udev)
|
||||
err(1, "udev_new() failed");
|
||||
|
||||
edl = calloc(1, sizeof(struct edl));
|
||||
if (!edl)
|
||||
err(1, "failed to allocate edl structure");
|
||||
|
||||
edl->serial = serial;
|
||||
edl->present = present;
|
||||
|
||||
edl->mon = udev_monitor_new_from_netlink(udev, "udev");
|
||||
udev_monitor_filter_add_match_subsystem_devtype(edl->mon, "usb", NULL);
|
||||
udev_monitor_enable_receiving(edl->mon);
|
||||
|
||||
fd = udev_monitor_get_fd(edl->mon);
|
||||
|
||||
watch_add_readfd(fd, handle_udev_event, edl);
|
||||
|
||||
udev_enum = udev_enumerate_new(udev);
|
||||
udev_enumerate_add_match_subsystem(udev_enum, "usb");
|
||||
udev_enumerate_scan_devices(udev_enum);
|
||||
|
||||
first = udev_enumerate_get_list_entry(udev_enum);
|
||||
udev_list_entry_foreach(item, first) {
|
||||
const char *path;
|
||||
struct udev_device *dev;
|
||||
|
||||
path = udev_list_entry_get_name(item);
|
||||
dev = udev_device_new_from_syspath(udev, path);
|
||||
handle_edl_add(edl, dev);
|
||||
}
|
||||
|
||||
udev_enumerate_unref(udev_enum);
|
||||
|
||||
return edl;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef __EDL_H__
|
||||
#define __EDL_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct edl;
|
||||
|
||||
struct edl *edl_open(const char *serial, void (*edl_present)(bool present));
|
||||
|
||||
#endif
|
||||
|
||||
@@ -85,6 +85,7 @@ endif
|
||||
cdbalib_srcs = ['circ_buf.c',
|
||||
'device.c',
|
||||
'device_parser.c',
|
||||
'edl.c',
|
||||
'fastboot.c',
|
||||
'console.c',
|
||||
'ppps.c',
|
||||
|
||||
+12
@@ -82,6 +82,18 @@ properties:
|
||||
description: Command to execute for generating status updates
|
||||
type: string
|
||||
|
||||
qdl_programmer:
|
||||
description: path to the firehose programmer to use for QDL flashing
|
||||
type: string
|
||||
|
||||
qdl_serial:
|
||||
description: serial number of the EDL device to use for QDL flashing
|
||||
type: string
|
||||
|
||||
qdl_storage:
|
||||
description: storage type to pass to QDL when flashing
|
||||
type: string
|
||||
|
||||
qcomlt_debug_board:
|
||||
description: Qlt Debug Board control tty device path
|
||||
$ref: "#/$defs/device_path"
|
||||
|
||||
Reference in new issue
Block a user