12 Commits
v2.4 ... nbdkit

Author SHA1 Message Date
Bjorn Andersson
c6200a2068 nbdkit: Don't compile in qdl.c
qdl.c, with its main method, should not be included in the nbdkit.  Add
the global qdl_debug variable to nbdkit and expose it as an nbdkit
configuration option.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-10-02 22:27:25 -07:00
Bjorn Andersson
44d148a63b firhose: Fix max payload size
The returned max payload size is no longer passed back through the
return value of the response parser, resulting in us configuring a max
payload size of 0 (success). Update the logic to use the new calling
scheme.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-10-02 22:27:25 -07:00
Bjorn Andersson
c37088f3fd README: Document nbdkit-qdl-plugin
Document the nbdkit-qdl-plugin and how to use it.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-06 22:43:12 -07:00
Bjorn Andersson
e3d3811748 nbdkit: Add nbdkit plugin
With the refactoring leading up to the firehose implementation being
driven by the client code and the USB code extracted we can implement
nbdkit plugin exposing the flash storage to the host PC.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-06 22:40:41 -07:00
Bjorn Andersson
9aae1e9ff2 qdl: Extract USB functions
Move the USB functions to a separate file, so that we can build the
nbdkit plugin without including qdl.c.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-06 22:40:11 -07:00
Bjorn Andersson
bff56ea21b firehose: Apply programs and patches from main
Move the program and patch invocation to the main function.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-06 22:39:53 -07:00
Bjorn Andersson
5b4874667c firehose: Implement generic read and write functions
Add generic block read and write functions, to allow implementing the
block level access needed in the nbdkit plugin.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-06 22:38:40 -07:00
Bjorn Andersson
e1a3670d91 firehose: Move set_bootable call to main()
Move the call to set_bootable out of firehose_run(), with the goal of
removing firehose_run()

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-06 22:38:10 -07:00
Bjorn Andersson
bb1aa99068 firehose: Extract firehose init
By extracting the initial parts of firehose_run() we get closer to being
able to reuse the firehose code in the upcoming nbdkit plugin.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-06 22:35:36 -07:00
Bjorn Andersson
342e99b9be firehose: Extract call to firehose_reset()
Expose firehose_reset() and call this from the main() function instead,
allowing the code to be reused between qdl and the nbdkit plugin.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-06 22:34:56 -07:00
Bjorn Andersson
066022e6d8 firehose: Introduce interface for querying the size of a LUN
Implement a function to query the storage information, to allow the
nbdkit plugin to query the size of each storage device.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-06 22:33:18 -07:00
Bjorn Andersson
2d239db0ca firehose: Simplify firehose_read()
With the move the USBFS firehose_read() can be simplified and allow us
to implement the parser for the LOG lines carrying the response of the
getStorageInfo request.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-06 22:25:43 -07:00
11 changed files with 1337 additions and 431 deletions

View File

@@ -1,17 +1,33 @@
OUT := qdl
NBDKIT := nbdkit-qdl-plugin.so
CFLAGS := -O2 -Wall -g `xml2-config --cflags`
CFLAGS := -O2 -Wall -g `xml2-config --cflags` -fPIC
LDFLAGS := `xml2-config --libs` -ludev
prefix := /usr/local
SRCS := firehose.c qdl.c sahara.c util.c patch.c program.c ufs.c
OBJS := $(SRCS:.c=.o)
COMMON_SRCS := firehose.c json.c sahara.c util.c patch.c program.c ufs.c usb.c
$(OUT): $(OBJS)
QDL_SRCS := qdl.c
QDL_OBJS := $(COMMON_SRCS:.c=.o) $(QDL_SRCS:.c=.o)
$(OUT): $(QDL_OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
NBDKIT_SRCS := nbdkit-qdl-plugin.c
NBDKIT_OBJS := $(COMMON_SRCS:.c=.o) $(NBDKIT_SRCS:.c=.o)
$(NBDKIT): LDFLAGS += -shared
$(NBDKIT): $(NBDKIT_OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
.PHONY: lib
lib: $(NBDKIT)
.PHONY: all
all: $(OUT) $(NBDKIT)
clean:
rm -f $(OUT) $(OBJS)
rm -f $(OUT) $(QDL_OBJS) $(NBDKIT_OBJS)
install: $(OUT)
install -D -m 755 $< $(DESTDIR)$(prefix)/bin/$<

27
README
View File

@@ -7,6 +7,30 @@ loader and use this to flash images.
Usage:
qdl <prog.mbn> [<program> <patch> ...]
nbdkit plugin
=============
In addition to the qdl programmer a nbdkit plugin can be built. This provides
block layer access to the flash on the device, allowing partition tables to be
modified and partitions mounted on the host PC.
The nbdkit plugin has the following configuration parameters:
programmer: firehose programmer
lun: LUN number to be managed, defaults to 1
storage: "ufs" or "emmc" to select firehose configuration
To launch a nbd server, launch nbdkit as follows:
modprobe nbd
nbdkit -fv ./nbdkit-qdl-plugin.so programmer=prog_firehose_ddr.elf lun=0
Then connect nbd to this, using:
sudo nbd-client -b 4096 localhost /dev/nbd0
After this /dev/nbd0 and /dev/nbd0p* can be used to access the full storage
device, as well as the individual partitions on the device.
To disconnect the nbd client, unmount any partitions and run:
sudo nbd-client -d /dev/nbd0
Building
========
In order to build the project you need libxml2 headers and libraries, found in
@@ -14,3 +38,6 @@ e.g. the libxml2-dev package.
With this installed run:
make
or to build the nbdkit plugin run:
make lib

File diff suppressed because it is too large Load Diff

451
json.c Normal file
View File

@@ -0,0 +1,451 @@
/*
* Copyright (c) 2018-2019, 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:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "json.h"
static const char *input_buf;
static int input_pos;
static int input_len;
static int json_parse_array(struct json_value *array);
static int json_parse_object(struct json_value *object);
static int json_parse_property(struct json_value *value);
static int input(void)
{
if (input_pos >= input_len)
return 0;
return input_buf[input_pos++];
}
static void unput(void)
{
input_pos--;
}
static void json_skip_whitespace(void)
{
int ch;
while ((ch = input()) && isspace(ch))
;
unput();
}
static int json_parse_string(struct json_value *value)
{
char buf[128];
char *b = buf;
int ch;
ch = input();
if (ch != '"') {
unput();
return 0;
}
while ((ch = input()) && ch != '"')
*b++ = ch;
*b = '\0';
if (!ch)
return -1;
value->type = JSON_TYPE_STRING;
value->u.string = strdup(buf);
return 1;
}
static int json_parse_number(struct json_value *value)
{
char buf[20];
char *b = buf;
int ch;
while ((ch = input()) && isdigit(ch) && b - buf < sizeof(buf) - 1)
*b++ = ch;
*b = '\0';
unput();
if (b == buf)
return 0;
value->type = JSON_TYPE_NUMBER;
value->u.number = strtod(buf, NULL);
return 1;
}
static int json_parse_keyword(struct json_value *value)
{
const char *match;
const char *m;
int ch;
ch = input();
switch (ch) {
case 't':
match = "true";
value->type = JSON_TYPE_TRUE;
break;
case 'f':
match = "false";
value->type = JSON_TYPE_FALSE;
break;
case 'n':
match = "null";
value->type = JSON_TYPE_NULL;
break;
default:
unput();
return 0;
}
m = match;
while (*m && *m++ == ch)
ch = input();
unput();
return *m == '\0' ? 1 : -1;
}
static int json_parse_value(struct json_value *value)
{
int ret;
json_skip_whitespace();
ret = json_parse_object(value);
if (ret)
goto out;
ret = json_parse_array(value);
if (ret)
goto out;
ret = json_parse_string(value);
if (ret)
goto out;
ret = json_parse_number(value);
if (ret)
goto out;
ret = json_parse_keyword(value);
if (ret)
goto out;
fprintf(stderr, "unable to match a value\n");
return -1;
out:
json_skip_whitespace();
return ret;
}
static int json_parse_array(struct json_value *array)
{
struct json_value *value;
struct json_value *last = NULL;
int ret;
int ch;
ch = input();
if (ch != '[') {
unput();
return 0;
}
array->type = JSON_TYPE_ARRAY;
do {
value = calloc(1, sizeof(*value));
if (!value)
return -1;
ret = json_parse_value(value);
if (ret <= 0) {
free(value);
return -1;
}
if (!array->u.value)
array->u.value = value;
if (last)
last->next = value;
last = value;
ch = input();
if (ch == ']') {
return 1;
}
} while (ch == ',');
fprintf(stderr, "expected ',' got '%c'\n", ch);
return -1;
}
static int json_parse_object(struct json_value *object)
{
struct json_value *value;
struct json_value *last = NULL;
int ret;
int ch;
ch = input();
if (ch != '{') {
unput();
return 0;
}
object->type = JSON_TYPE_OBJECT;
do {
value = calloc(1, sizeof(*value));
if (!value)
return -1;
ret = json_parse_property(value);
if (ret <= 0) {
free(value);
return -1;
}
if (!object->u.value)
object->u.value = value;
if (last)
last->next = value;
last = value;
ch = input();
if (ch == '}') {
return 1;
}
} while (ch == ',');
return -1;
}
static int json_parse_property(struct json_value *value)
{
struct json_value key;
int ret;
int ch;
json_skip_whitespace();
ret = json_parse_string(&key);
if (ret <= 0)
return -1;
value->key = key.u.string;
json_skip_whitespace();
ch = input();
if (ch != ':')
return -1;
ret = json_parse_value(value);
if (ret <= 0)
return -1;
return 1;
}
struct json_value *json_parse(const char *json)
{
struct json_value *root;
int ret;
input_buf = json;
input_pos = 0;
input_len = strlen(input_buf);
root = calloc(1, sizeof(*root));
if (!root)
return NULL;
ret = json_parse_value(root);
if (ret != 1) {
free(root);
return NULL;
}
return root;
}
struct json_value *json_parse_file(const char *file)
{
struct json_value *root;
struct stat sb;
int ret;
int fd;
fd = open(file, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "failed to open %s: %s\n", file, strerror(errno));
return NULL;
}
ret = fstat(fd, &sb);
if (ret < 0)
return NULL;
input_pos = 0;
input_len = sb.st_size;
input_buf = malloc(sb.st_size);
ret = read(fd, (char *)input_buf, input_len);
close(fd);
if (ret != input_len) {
fprintf(stderr, "failed to read %d bytes form %s\n", input_len, file);
return NULL;
}
root = calloc(1, sizeof(*root));
if (!root)
return NULL;
ret = json_parse_value(root);
if (ret != 1) {
free(root);
return NULL;
}
return root;
}
struct json_value *json_get_child(struct json_value *object, const char *key)
{
struct json_value *it;
if(object->type != JSON_TYPE_OBJECT)
return NULL;
for (it = object->u.value; it; it = it->next) {
if (!strcmp(it->key, key))
return it;
}
return NULL;
}
int json_count_children(struct json_value *array)
{
struct json_value *it;
int count = 0;
if (!array || array->type != JSON_TYPE_ARRAY)
return -1;
for (it = array->u.value; it; it = it->next)
count++;
return count;
}
int json_get_number(struct json_value *object, const char *key, double *number)
{
struct json_value *it;
if (!object || object->type != JSON_TYPE_OBJECT)
return -1;
for (it = object->u.value; it; it = it->next) {
if (!strcmp(it->key, key)) {
if (it->type != JSON_TYPE_NUMBER)
return -1;
*number = it->u.number;
return 0;
}
}
return -1;
}
const char *json_get_string(struct json_value *object, const char *key)
{
struct json_value *it;
if (!object || object->type != JSON_TYPE_OBJECT)
return NULL;
for (it = object->u.value; it; it = it->next) {
if (!strcmp(it->key, key)) {
if (it->type != JSON_TYPE_STRING)
return NULL;
return it->u.string;
}
}
return NULL;
}
void json_free(struct json_value *value)
{
struct json_value *next;
struct json_value *it;
free((char *)value->key);
switch (value->type) {
case JSON_TYPE_OBJECT:
case JSON_TYPE_ARRAY:
it = value->u.value;
while (it) {
next = it->next;
json_free(it);
it = next;
}
break;
case JSON_TYPE_STRING:
free((char *)value->u.string);
break;
}
free(value);
}

67
json.h Normal file
View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2019, 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:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 __JSON_H__
#define __JSON_H__
enum {
JSON_TYPE_UNKNOWN,
JSON_TYPE_TRUE,
JSON_TYPE_FALSE,
JSON_TYPE_NULL,
JSON_TYPE_NUMBER,
JSON_TYPE_STRING,
JSON_TYPE_ARRAY,
JSON_TYPE_OBJECT,
};
struct json_value {
const char *key;
int type;
union {
double number;
const char *string;
struct json_value *value;
} u;
struct json_value *next;
};
struct json_value *json_parse(const char *json);
struct json_value *json_parse_file(const char *file);
int json_count_children(struct json_value *array);
struct json_value *json_get_child(struct json_value *object, const char *key);
int json_get_number(struct json_value *object, const char *key, double *number);
const char *json_get_string(struct json_value *object, const char *key);
void json_free(struct json_value *value);
#endif

146
nbdkit-qdl-plugin.c Normal file
View File

@@ -0,0 +1,146 @@
#define NBDKIT_API_VERSION 2
#include <nbdkit-plugin.h>
#define THREAD_MODEL NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS
#include <linux/usbdevice_fs.h>
#include <linux/usb/ch9.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <libudev.h>
#include <poll.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "qdl.h"
#include "patch.h"
#include "ufs.h"
#include "usb.h"
static const char *config_programmer;
static bool config_ufs = true;
static int config_lun = 1;
static size_t sector_size;
bool qdl_debug;
static int qdl_config(const char *key, const char *value)
{
if (!strcmp(key, "programmer")) {
config_programmer = nbdkit_absolute_path(value);
} else if (!strcmp(key, "storage")) {
if (!strcmp(value, "ufs")) {
config_ufs = true;
} else if (!strcmp(value, "emmc")) {
config_ufs = false;
} else {
nbdkit_error("unknown storage type '%s'", value);
return -1;
}
} else if (!strcmp(key, "lun")) {
config_lun = atoi(value);
} else if (!strcmp(key, "debug")) {
qdl_debug = true;
} else {
return -1;
}
return 0;
}
static int qdl_config_complete(void)
{
if (!config_programmer)
return -1;
return 0;
}
static void *qdl_open(int readonly)
{
struct qdl_device *qdl;
int ret;
qdl = usb_open();
if (!qdl)
return NULL;
ret = sahara_run(qdl, config_programmer);
if (ret < 0)
return NULL;
ret = firehose_open(qdl, config_ufs);
if (ret < 0)
return NULL;
return qdl;
}
static void qdl_close(void *handle)
{
struct qdl_device *qdl = handle;
firehose_reset(qdl);
free(qdl);
}
static int64_t qdl_get_size(void *handle)
{
struct qdl_device *qdl = handle;
size_t num_sectors;
int ret;
ret = firehose_getsize(qdl, config_lun, &sector_size, &num_sectors);
if (ret < 0)
return -1;
return sector_size * num_sectors;
}
static int qdl_pread(void *handle, void *buf, uint32_t count, uint64_t offset,
uint32_t flags)
{
struct qdl_device *qdl = handle;
return firehose_pread(qdl, config_lun, offset / sector_size, buf,
sector_size, count / sector_size);
}
static int qdl_pwrite(void *handle, const void *buf, uint32_t count,
uint64_t offset, uint32_t flags)
{
struct qdl_device *qdl = handle;
return firehose_pwrite(qdl, config_lun, offset / sector_size, buf,
sector_size, count / sector_size);
}
static struct nbdkit_plugin qdl_plugin = {
.name = "qdl",
.description = "nbdkit Qualcomm Download plugin",
.open = qdl_open,
.close = qdl_close,
.config = qdl_config,
.config_complete = qdl_config_complete,
.get_size = qdl_get_size,
.pread = qdl_pread,
.pwrite = qdl_pwrite,
};
NBDKIT_REGISTER_PLUGIN(qdl_plugin)

332
qdl.c
View File

@@ -55,8 +55,7 @@
#include "qdl.h"
#include "patch.h"
#include "ufs.h"
#define MAX_USBFS_BULK_SIZE (16*1024)
#include "usb.h"
enum {
QDL_FILE_UNKNOWN,
@@ -66,16 +65,6 @@ enum {
QDL_FILE_CONTENTS,
};
struct qdl_device {
int fd;
int in_ep;
int out_ep;
size_t in_maxpktsize;
size_t out_maxpktsize;
};
bool qdl_debug;
static int detect_type(const char *xml_file)
@@ -116,300 +105,6 @@ static int detect_type(const char *xml_file)
return type;
}
static int parse_usb_desc(int fd, struct qdl_device *qdl, int *intf)
{
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 out;
unsigned in;
unsigned k;
unsigned l;
ssize_t n;
size_t out_size;
size_t in_size;
void *ptr;
void *end;
char desc[1024];
n = read(fd, desc, sizeof(desc));
if (n < 0)
return n;
ptr = (void*)desc;
end = ptr + n;
dev = ptr;
/* Consider only devices with vid 0x0506 and product id 0x9008 */
if (dev->idVendor != 0x05c6 || dev->idProduct != 0x9008)
return -EINVAL;
ptr += dev->bLength;
if (ptr >= end || dev->bDescriptorType != USB_DT_DEVICE)
return -EINVAL;
cfg = 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 = ptr;
if (ifc->bLength < USB_DT_INTERFACE_SIZE)
return -EINVAL;
ptr += ifc->bLength;
} while (ptr < end && ifc->bDescriptorType != USB_DT_INTERFACE);
in = -1;
out = -1;
in_size = 0;
out_size = 0;
for (l = 0; l < ifc->bNumEndpoints; l++) {
if (ptr >= end)
return -EINVAL;
do {
ept = 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 (ept->bEndpointAddress & USB_DIR_IN) {
in = ept->bEndpointAddress;
in_size = ept->wMaxPacketSize;
} else {
out = ept->bEndpointAddress;
out_size = ept->wMaxPacketSize;
}
if (ptr >= end)
break;
hdr = 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;
/* bInterfaceProtocol of 0xff and 0x10 has been seen */
if (ifc->bInterfaceProtocol != 0xff &&
ifc->bInterfaceProtocol != 16)
continue;
qdl->fd = fd;
qdl->in_ep = in;
qdl->out_ep = out;
qdl->in_maxpktsize = in_size;
qdl->out_maxpktsize = out_size;
*intf = ifc->bInterfaceNumber;
return 0;
}
return -ENOENT;
}
static int usb_open(struct qdl_device *qdl)
{
struct udev_enumerate *enumerate;
struct udev_list_entry *devices;
struct udev_list_entry *dev_list_entry;
struct udev_monitor *mon;
struct udev_device *dev;
const char *dev_node;
struct udev *udev;
const char *path;
struct usbdevfs_ioctl cmd;
int mon_fd;
int intf = -1;
int ret;
int fd;
udev = udev_new();
if (!udev)
err(1, "failed to initialize udev");
mon = udev_monitor_new_from_netlink(udev, "udev");
udev_monitor_filter_add_match_subsystem_devtype(mon, "usb", NULL);
udev_monitor_enable_receiving(mon);
mon_fd = udev_monitor_get_fd(mon);
enumerate = udev_enumerate_new(udev);
udev_enumerate_add_match_subsystem(enumerate, "usb");
udev_enumerate_scan_devices(enumerate);
devices = udev_enumerate_get_list_entry(enumerate);
udev_list_entry_foreach(dev_list_entry, devices) {
path = udev_list_entry_get_name(dev_list_entry);
dev = udev_device_new_from_syspath(udev, path);
dev_node = udev_device_get_devnode(dev);
if (!dev_node)
continue;
fd = open(dev_node, O_RDWR);
if (fd < 0)
continue;
ret = parse_usb_desc(fd, qdl, &intf);
if (!ret)
goto found;
close(fd);
}
fprintf(stderr, "Waiting for EDL device\n");
for (;;) {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(mon_fd, &rfds);
ret = select(mon_fd + 1, &rfds, NULL, NULL, NULL);
if (ret < 0)
return -1;
if (!FD_ISSET(mon_fd, &rfds))
continue;
dev = udev_monitor_receive_device(mon);
dev_node = udev_device_get_devnode(dev);
if (!dev_node)
continue;
printf("%s\n", dev_node);
fd = open(dev_node, O_RDWR);
if (fd < 0)
continue;
ret = parse_usb_desc(fd, qdl, &intf);
if (!ret)
goto found;
close(fd);
}
udev_enumerate_unref(enumerate);
udev_monitor_unref(mon);
udev_unref(udev);
return -ENOENT;
found:
udev_enumerate_unref(enumerate);
udev_monitor_unref(mon);
udev_unref(udev);
cmd.ifno = intf;
cmd.ioctl_code = USBDEVFS_DISCONNECT;
cmd.data = NULL;
ret = ioctl(qdl->fd, USBDEVFS_IOCTL, &cmd);
if (ret && errno != ENODATA)
err(1, "failed to disconnect kernel driver");
ret = ioctl(qdl->fd, USBDEVFS_CLAIMINTERFACE, &intf);
if (ret < 0)
err(1, "failed to claim USB interface");
return 0;
}
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout)
{
struct usbdevfs_bulktransfer bulk = {};
bulk.ep = qdl->in_ep;
bulk.len = len;
bulk.data = buf;
bulk.timeout = timeout;
return ioctl(qdl->fd, USBDEVFS_BULK, &bulk);
}
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len, bool eot)
{
unsigned char *data = (unsigned char*) buf;
struct usbdevfs_bulktransfer bulk = {};
unsigned count = 0;
size_t len_orig = len;
int n;
if(len == 0) {
bulk.ep = qdl->out_ep;
bulk.len = 0;
bulk.data = data;
bulk.timeout = 1000;
n = ioctl(qdl->fd, USBDEVFS_BULK, &bulk);
if(n != 0) {
fprintf(stderr,"ERROR: n = %d, errno = %d (%s)\n",
n, errno, strerror(errno));
return -1;
}
return 0;
}
while(len > 0) {
int xfer;
xfer = (len > qdl->out_maxpktsize) ? qdl->out_maxpktsize : len;
bulk.ep = qdl->out_ep;
bulk.len = xfer;
bulk.data = data;
bulk.timeout = 1000;
n = ioctl(qdl->fd, USBDEVFS_BULK, &bulk);
if(n != xfer) {
fprintf(stderr, "ERROR: n = %d, errno = %d (%s)\n",
n, errno, strerror(errno));
return -1;
}
count += xfer;
len -= xfer;
data += xfer;
}
if (eot && (len_orig % qdl->out_maxpktsize) == 0) {
bulk.ep = qdl->out_ep;
bulk.len = 0;
bulk.data = NULL;
bulk.timeout = 1000;
n = ioctl(qdl->fd, USBDEVFS_BULK, &bulk);
if (n < 0)
return n;
}
return count;
}
static void print_usage(void)
{
extern const char *__progname;
@@ -420,13 +115,14 @@ static void print_usage(void)
int main(int argc, char **argv)
{
struct qdl_device *qdl;
char *prog_mbn, *storage="ufs";
char *incdir = NULL;
int bootable;
int type;
int ret;
int opt;
bool qdl_finalize_provisioning = false;
struct qdl_device qdl;
static struct option options[] = {
@@ -492,17 +188,33 @@ int main(int argc, char **argv)
}
} while (++optind < argc);
ret = usb_open(&qdl);
qdl = usb_open();
if (ret)
return 1;
ret = sahara_run(&qdl, prog_mbn);
ret = sahara_run(qdl, prog_mbn);
if (ret < 0)
return 1;
ret = firehose_run(&qdl, incdir, storage);
ret = firehose_open(qdl, !strcmp(storage, "ufs"));
if (ret < 0)
return 1;
ret = program_execute(qdl, firehose_program, incdir);
if (ret)
return 1;
ret = patch_execute(qdl, firehose_apply_patch);
if (ret)
return 1;
bootable = program_find_bootable_partition();
if (bootable < 0)
fprintf(stderr, "no boot partition found\n");
else
firehose_set_bootable(qdl, bootable);
firehose_reset(qdl);
return 0;
}

21
qdl.h
View File

@@ -8,16 +8,27 @@
#include <libxml/tree.h>
struct qdl_device;
struct program;
struct patch;
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout);
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len, bool eot);
int firehose_run(struct qdl_device *qdl, const char *incdir, const char *storage);
int sahara_run(struct qdl_device *qdl, char *prog_mbn);
int firehose_open(struct qdl_device *qdl, bool ufs);
int sahara_run(struct qdl_device *qdl, const char *prog_mbn);
void print_hex_dump(const char *prefix, const void *buf, size_t len);
unsigned attr_as_unsigned(xmlNode *node, const char *attr, int *errors);
const char *attr_as_string(xmlNode *node, const char *attr, int *errors);
int firehose_getsize(struct qdl_device *qdl, int lun, size_t *sector_size,
size_t *num_sectors);
int firehose_reset(struct qdl_device *qdl);
int firehose_set_bootable(struct qdl_device *qdl, int part);
int firehose_program(struct qdl_device *qdl, struct program *program, int fd);
int firehose_apply_patch(struct qdl_device *qdl, struct patch *patch);
ssize_t firehose_pread(struct qdl_device *qdl, int lun, size_t offset, void *buf,
size_t sector_size, size_t num_sectors);
ssize_t firehose_pwrite(struct qdl_device *qdl, int lun, size_t offset,
const void *buf, size_t sector_size, size_t num_sectors);
extern bool qdl_debug;
#endif

View File

@@ -45,6 +45,7 @@
#include <termios.h>
#include <unistd.h>
#include "qdl.h"
#include "usb.h"
struct sahara_pkt {
uint32_t cmd;
@@ -192,7 +193,7 @@ static int sahara_done(struct qdl_device *qdl, struct sahara_pkt *pkt)
return pkt->done_resp.status;
}
int sahara_run(struct qdl_device *qdl, char *prog_mbn)
int sahara_run(struct qdl_device *qdl, const char *prog_mbn)
{
struct sahara_pkt *pkt;
char buf[4096];

332
usb.c Normal file
View File

@@ -0,0 +1,332 @@
#include <linux/usbdevice_fs.h>
#include <linux/usb/ch9.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <libudev.h>
#include <poll.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "qdl.h"
#include "usb.h"
struct qdl_device {
int fd;
int in_ep;
int out_ep;
size_t in_maxpktsize;
size_t out_maxpktsize;
};
static int parse_usb_desc(int fd, struct qdl_device *qdl, int *intf)
{
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 out;
unsigned in;
unsigned k;
unsigned l;
ssize_t n;
size_t out_size;
size_t in_size;
void *ptr;
void *end;
char desc[1024];
n = read(fd, desc, sizeof(desc));
if (n < 0)
return n;
ptr = (void*)desc;
end = ptr + n;
dev = ptr;
/* Consider only devices with vid 0x0506 and product id 0x9008 */
if (dev->idVendor != 0x05c6 || dev->idProduct != 0x9008)
return -EINVAL;
ptr += dev->bLength;
if (ptr >= end || dev->bDescriptorType != USB_DT_DEVICE)
return -EINVAL;
cfg = 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 = ptr;
if (ifc->bLength < USB_DT_INTERFACE_SIZE)
return -EINVAL;
ptr += ifc->bLength;
} while (ptr < end && ifc->bDescriptorType != USB_DT_INTERFACE);
in = -1;
out = -1;
in_size = 0;
out_size = 0;
for (l = 0; l < ifc->bNumEndpoints; l++) {
if (ptr >= end)
return -EINVAL;
do {
ept = 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 (ept->bEndpointAddress & USB_DIR_IN) {
in = ept->bEndpointAddress;
in_size = ept->wMaxPacketSize;
} else {
out = ept->bEndpointAddress;
out_size = ept->wMaxPacketSize;
}
if (ptr >= end)
break;
hdr = 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;
/* bInterfaceProtocol of 0xff and 0x10 has been seen */
if (ifc->bInterfaceProtocol != 0xff &&
ifc->bInterfaceProtocol != 16)
continue;
qdl->fd = fd;
qdl->in_ep = in;
qdl->out_ep = out;
qdl->in_maxpktsize = in_size;
qdl->out_maxpktsize = out_size;
*intf = ifc->bInterfaceNumber;
return 0;
}
return -ENOENT;
}
struct qdl_device *usb_open(void)
{
struct udev_enumerate *enumerate;
struct udev_list_entry *devices;
struct udev_list_entry *dev_list_entry;
struct udev_monitor *mon;
struct udev_device *dev;
struct qdl_device *qdl;
const char *dev_node;
struct udev *udev;
const char *path;
struct usbdevfs_ioctl cmd;
int mon_fd;
int intf = -1;
int ret;
int fd;
qdl = calloc(1, sizeof(*qdl));
udev = udev_new();
if (!udev)
err(1, "failed to initialize udev");
mon = udev_monitor_new_from_netlink(udev, "udev");
udev_monitor_filter_add_match_subsystem_devtype(mon, "usb", NULL);
udev_monitor_enable_receiving(mon);
mon_fd = udev_monitor_get_fd(mon);
enumerate = udev_enumerate_new(udev);
udev_enumerate_add_match_subsystem(enumerate, "usb");
udev_enumerate_scan_devices(enumerate);
devices = udev_enumerate_get_list_entry(enumerate);
udev_list_entry_foreach(dev_list_entry, devices) {
path = udev_list_entry_get_name(dev_list_entry);
dev = udev_device_new_from_syspath(udev, path);
dev_node = udev_device_get_devnode(dev);
if (!dev_node)
continue;
fd = open(dev_node, O_RDWR);
if (fd < 0)
continue;
ret = parse_usb_desc(fd, qdl, &intf);
if (!ret)
goto found;
close(fd);
}
fprintf(stderr, "Waiting for EDL device\n");
for (;;) {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(mon_fd, &rfds);
ret = select(mon_fd + 1, &rfds, NULL, NULL, NULL);
if (ret < 0)
err(1, "failed to select");
if (!FD_ISSET(mon_fd, &rfds))
continue;
dev = udev_monitor_receive_device(mon);
dev_node = udev_device_get_devnode(dev);
if (!dev_node)
continue;
printf("%s\n", dev_node);
fd = open(dev_node, O_RDWR);
if (fd < 0)
continue;
ret = parse_usb_desc(fd, qdl, &intf);
if (!ret)
goto found;
close(fd);
}
udev_enumerate_unref(enumerate);
udev_monitor_unref(mon);
udev_unref(udev);
return NULL;
found:
udev_enumerate_unref(enumerate);
udev_monitor_unref(mon);
udev_unref(udev);
cmd.ifno = intf;
cmd.ioctl_code = USBDEVFS_DISCONNECT;
cmd.data = NULL;
ret = ioctl(qdl->fd, USBDEVFS_IOCTL, &cmd);
if (ret && errno != ENODATA)
err(1, "failed to disconnect kernel driver");
ret = ioctl(qdl->fd, USBDEVFS_CLAIMINTERFACE, &intf);
if (ret < 0)
err(1, "failed to claim USB interface");
return qdl;
}
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout)
{
struct usbdevfs_bulktransfer bulk = {};
bulk.ep = qdl->in_ep;
bulk.len = len;
bulk.data = buf;
bulk.timeout = timeout;
return ioctl(qdl->fd, USBDEVFS_BULK, &bulk);
}
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len, bool eot)
{
unsigned char *data = (unsigned char*) buf;
struct usbdevfs_bulktransfer bulk = {};
unsigned count = 0;
size_t len_orig = len;
int n;
if(len == 0) {
bulk.ep = qdl->out_ep;
bulk.len = 0;
bulk.data = data;
bulk.timeout = 1000;
n = ioctl(qdl->fd, USBDEVFS_BULK, &bulk);
if(n != 0) {
fprintf(stderr,"ERROR: n = %d, errno = %d (%s)\n",
n, errno, strerror(errno));
return -1;
}
return 0;
}
while(len > 0) {
int xfer;
xfer = (len > qdl->out_maxpktsize) ? qdl->out_maxpktsize : len;
bulk.ep = qdl->out_ep;
bulk.len = xfer;
bulk.data = data;
bulk.timeout = 1000;
n = ioctl(qdl->fd, USBDEVFS_BULK, &bulk);
if(n != xfer) {
fprintf(stderr, "ERROR: n = %d, errno = %d (%s)\n",
n, errno, strerror(errno));
return -1;
}
count += xfer;
len -= xfer;
data += xfer;
}
if (eot && (len_orig % qdl->out_maxpktsize) == 0) {
bulk.ep = qdl->out_ep;
bulk.len = 0;
bulk.data = NULL;
bulk.timeout = 1000;
n = ioctl(qdl->fd, USBDEVFS_BULK, &bulk);
if (n < 0)
return n;
}
return count;
}

10
usb.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef __QDL_USB_H__
#define __QDL_USB_H__
struct qdl_device;
struct qdl_device *usb_open(void);
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout);
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len, bool eot);
#endif