mirror of
https://github.com/linux-msm/qdl.git
synced 2026-02-25 13:12:25 -08:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6200a2068 | ||
|
|
44d148a63b | ||
|
|
c37088f3fd | ||
|
|
e3d3811748 | ||
|
|
9aae1e9ff2 | ||
|
|
bff56ea21b | ||
|
|
5b4874667c | ||
|
|
e1a3670d91 | ||
|
|
bb1aa99068 | ||
|
|
342e99b9be | ||
|
|
066022e6d8 | ||
|
|
2d239db0ca |
26
Makefile
26
Makefile
@@ -1,17 +1,33 @@
|
|||||||
OUT := qdl
|
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
|
LDFLAGS := `xml2-config --libs` -ludev
|
||||||
prefix := /usr/local
|
prefix := /usr/local
|
||||||
|
|
||||||
SRCS := firehose.c qdl.c sahara.c util.c patch.c program.c ufs.c
|
COMMON_SRCS := firehose.c json.c sahara.c util.c patch.c program.c ufs.c usb.c
|
||||||
OBJS := $(SRCS:.c=.o)
|
|
||||||
|
|
||||||
$(OUT): $(OBJS)
|
QDL_SRCS := qdl.c
|
||||||
|
QDL_OBJS := $(COMMON_SRCS:.c=.o) $(QDL_SRCS:.c=.o)
|
||||||
|
|
||||||
|
$(OUT): $(QDL_OBJS)
|
||||||
$(CC) -o $@ $^ $(LDFLAGS)
|
$(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:
|
clean:
|
||||||
rm -f $(OUT) $(OBJS)
|
rm -f $(OUT) $(QDL_OBJS) $(NBDKIT_OBJS)
|
||||||
|
|
||||||
install: $(OUT)
|
install: $(OUT)
|
||||||
install -D -m 755 $< $(DESTDIR)$(prefix)/bin/$<
|
install -D -m 755 $< $(DESTDIR)$(prefix)/bin/$<
|
||||||
|
|||||||
27
README
27
README
@@ -7,6 +7,30 @@ loader and use this to flash images.
|
|||||||
Usage:
|
Usage:
|
||||||
qdl <prog.mbn> [<program> <patch> ...]
|
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
|
Building
|
||||||
========
|
========
|
||||||
In order to build the project you need libxml2 headers and libraries, found in
|
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:
|
With this installed run:
|
||||||
make
|
make
|
||||||
|
|
||||||
|
or to build the nbdkit plugin run:
|
||||||
|
make lib
|
||||||
|
|||||||
353
firehose.c
353
firehose.c
File diff suppressed because it is too large
Load Diff
451
json.c
Normal file
451
json.c
Normal 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
67
json.h
Normal 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
146
nbdkit-qdl-plugin.c
Normal 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, §or_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
332
qdl.c
@@ -55,8 +55,7 @@
|
|||||||
#include "qdl.h"
|
#include "qdl.h"
|
||||||
#include "patch.h"
|
#include "patch.h"
|
||||||
#include "ufs.h"
|
#include "ufs.h"
|
||||||
|
#include "usb.h"
|
||||||
#define MAX_USBFS_BULK_SIZE (16*1024)
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
QDL_FILE_UNKNOWN,
|
QDL_FILE_UNKNOWN,
|
||||||
@@ -66,16 +65,6 @@ enum {
|
|||||||
QDL_FILE_CONTENTS,
|
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;
|
bool qdl_debug;
|
||||||
|
|
||||||
static int detect_type(const char *xml_file)
|
static int detect_type(const char *xml_file)
|
||||||
@@ -116,300 +105,6 @@ static int detect_type(const char *xml_file)
|
|||||||
return type;
|
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)
|
static void print_usage(void)
|
||||||
{
|
{
|
||||||
extern const char *__progname;
|
extern const char *__progname;
|
||||||
@@ -420,13 +115,14 @@ static void print_usage(void)
|
|||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
struct qdl_device *qdl;
|
||||||
char *prog_mbn, *storage="ufs";
|
char *prog_mbn, *storage="ufs";
|
||||||
char *incdir = NULL;
|
char *incdir = NULL;
|
||||||
|
int bootable;
|
||||||
int type;
|
int type;
|
||||||
int ret;
|
int ret;
|
||||||
int opt;
|
int opt;
|
||||||
bool qdl_finalize_provisioning = false;
|
bool qdl_finalize_provisioning = false;
|
||||||
struct qdl_device qdl;
|
|
||||||
|
|
||||||
|
|
||||||
static struct option options[] = {
|
static struct option options[] = {
|
||||||
@@ -492,17 +188,33 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
} while (++optind < argc);
|
} while (++optind < argc);
|
||||||
|
|
||||||
ret = usb_open(&qdl);
|
qdl = usb_open();
|
||||||
if (ret)
|
if (ret)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
ret = sahara_run(&qdl, prog_mbn);
|
ret = sahara_run(qdl, prog_mbn);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
ret = firehose_run(&qdl, incdir, storage);
|
ret = firehose_open(qdl, !strcmp(storage, "ufs"));
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return 1;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
21
qdl.h
21
qdl.h
@@ -8,16 +8,27 @@
|
|||||||
#include <libxml/tree.h>
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
struct qdl_device;
|
struct qdl_device;
|
||||||
|
struct program;
|
||||||
|
struct patch;
|
||||||
|
|
||||||
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout);
|
int firehose_open(struct qdl_device *qdl, bool ufs);
|
||||||
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len, bool eot);
|
int sahara_run(struct qdl_device *qdl, const char *prog_mbn);
|
||||||
|
|
||||||
int firehose_run(struct qdl_device *qdl, const char *incdir, const char *storage);
|
|
||||||
int sahara_run(struct qdl_device *qdl, char *prog_mbn);
|
|
||||||
void print_hex_dump(const char *prefix, const void *buf, size_t len);
|
void print_hex_dump(const char *prefix, const void *buf, size_t len);
|
||||||
unsigned attr_as_unsigned(xmlNode *node, const char *attr, int *errors);
|
unsigned attr_as_unsigned(xmlNode *node, const char *attr, int *errors);
|
||||||
const char *attr_as_string(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;
|
extern bool qdl_debug;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
3
sahara.c
3
sahara.c
@@ -45,6 +45,7 @@
|
|||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "qdl.h"
|
#include "qdl.h"
|
||||||
|
#include "usb.h"
|
||||||
|
|
||||||
struct sahara_pkt {
|
struct sahara_pkt {
|
||||||
uint32_t cmd;
|
uint32_t cmd;
|
||||||
@@ -192,7 +193,7 @@ static int sahara_done(struct qdl_device *qdl, struct sahara_pkt *pkt)
|
|||||||
return pkt->done_resp.status;
|
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;
|
struct sahara_pkt *pkt;
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
|
|||||||
332
usb.c
Normal file
332
usb.c
Normal 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
10
usb.h
Normal 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
|
||||||
Reference in New Issue
Block a user