mirror of
https://github.com/linux-msm/qdl.git
synced 2026-02-25 13:12:25 -08:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cbd46184d3 | ||
|
|
a1cd535439 | ||
|
|
a60d45c3b7 | ||
|
|
c647d6d9eb | ||
|
|
b761963272 | ||
|
|
adfdcc6b01 | ||
|
|
1d8a3fff7d | ||
|
|
8b01b6254e | ||
|
|
95be64392f | ||
|
|
aeb70e0645 | ||
|
|
b1ec56fc9e | ||
|
|
fac09b2980 | ||
|
|
b18bca7d28 | ||
|
|
ba6b6e5b3a | ||
|
|
0132a8eb5a | ||
|
|
7cc2748c61 | ||
|
|
25ce7fad22 | ||
|
|
37b0d0cf69 | ||
|
|
983ec51d02 | ||
|
|
5f2f82a526 | ||
|
|
2fd923ec97 | ||
|
|
5b69b112f5 | ||
|
|
74779c7a77 | ||
|
|
a10d2bf57b | ||
|
|
7cc077d066 | ||
|
|
7d4020fedd | ||
|
|
a629f43428 | ||
|
|
3b22df2bc7 | ||
|
|
dbcb6fed63 | ||
|
|
1acf511839 | ||
|
|
392394086d | ||
|
|
bdcf93b4a3 | ||
|
|
e25c981207 | ||
|
|
a8d10da050 | ||
|
|
0fcf944ab5 | ||
|
|
2021b303a8 | ||
|
|
f02ed6abca | ||
|
|
2e258d9d56 | ||
|
|
13681fcb35 | ||
|
|
3c0405c03d | ||
|
|
ec6c8a034e | ||
|
|
650b477ca5 | ||
|
|
00d93b29f5 | ||
|
|
7f75f17c53 | ||
|
|
40df480740 | ||
|
|
91e609b611 | ||
|
|
da8dd7139a | ||
|
|
b6e0ea31d7 | ||
|
|
56076936a6 | ||
|
|
bead963d33 |
32
.github/workflows/build.yml
vendored
Normal file
32
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
name: Buildtest
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ ubuntu-24.04, ubuntu-22.04, ubuntu-20.04, macos-latest ]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Dependencies (Ubuntu)
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libxml2-dev libusb-1.0-0-dev
|
||||||
|
|
||||||
|
- name: Dependencies (macOS)
|
||||||
|
if: runner.os == 'macOS'
|
||||||
|
run: |
|
||||||
|
brew install libxml2
|
||||||
|
brew install libusb
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: make
|
||||||
36
.github/workflows/codeql.yml
vendored
Normal file
36
.github/workflows/codeql.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
name: CodeQL
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
codeql:
|
||||||
|
permissions:
|
||||||
|
# required for all workflows
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
# required to fetch internal or private CodeQL packs
|
||||||
|
packages: read
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libxml2-dev libusb-1.0-0-dev
|
||||||
|
|
||||||
|
- name: CodeQL init
|
||||||
|
uses: github/codeql-action/init@v3
|
||||||
|
with:
|
||||||
|
languages: c-cpp
|
||||||
|
build-mode: autobuild
|
||||||
|
|
||||||
|
- name: CodeQL build
|
||||||
|
uses: github/codeql-action/autobuild@v3
|
||||||
|
|
||||||
|
- name: CodeQL analysis
|
||||||
|
uses: github/codeql-action/analyze@v3
|
||||||
|
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,2 +1,6 @@
|
|||||||
*.o
|
*.o
|
||||||
qdl
|
qdl
|
||||||
|
qdl-ramdump
|
||||||
|
ks
|
||||||
|
compile_commands.json
|
||||||
|
.cache
|
||||||
|
|||||||
43
Makefile
43
Makefile
@@ -1,17 +1,40 @@
|
|||||||
OUT := qdl
|
QDL := qdl
|
||||||
|
RAMDUMP := qdl-ramdump
|
||||||
|
|
||||||
CFLAGS := -O2 -Wall -g `xml2-config --cflags`
|
CFLAGS += -O2 -Wall -g `pkg-config --cflags libxml-2.0 libusb-1.0`
|
||||||
LDFLAGS := `xml2-config --libs` -ludev
|
LDFLAGS += `pkg-config --libs libxml-2.0 libusb-1.0`
|
||||||
prefix := /usr/local
|
prefix := /usr/local
|
||||||
|
|
||||||
SRCS := firehose.c qdl.c sahara.c util.c patch.c program.c ufs.c
|
QDL_SRCS := firehose.c qdl.c sahara.c util.c patch.c program.c read.c ufs.c usb.c
|
||||||
OBJS := $(SRCS:.c=.o)
|
QDL_OBJS := $(QDL_SRCS:.c=.o)
|
||||||
|
|
||||||
$(OUT): $(OBJS)
|
RAMDUMP_SRCS := ramdump.c sahara.c usb.c util.c
|
||||||
|
RAMDUMP_OBJS := $(RAMDUMP_SRCS:.c=.o)
|
||||||
|
|
||||||
|
KS_OUT := ks
|
||||||
|
KS_SRCS := ks.c sahara.c util.c
|
||||||
|
KS_OBJS := $(KS_SRCS:.c=.o)
|
||||||
|
|
||||||
|
default: $(QDL) $(RAMDUMP) $(KS_OUT)
|
||||||
|
|
||||||
|
$(QDL): $(QDL_OBJS)
|
||||||
$(CC) -o $@ $^ $(LDFLAGS)
|
$(CC) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
clean:
|
$(RAMDUMP): $(RAMDUMP_OBJS)
|
||||||
rm -f $(OUT) $(OBJS)
|
$(CC) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
install: $(OUT)
|
$(KS_OUT): $(KS_OBJS)
|
||||||
install -D -m 755 $< $(DESTDIR)$(prefix)/bin/$<
|
$(CC) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
|
compile_commands.json: $(QDL_SRCS) $(KS_SRCS)
|
||||||
|
@echo -n $^ | jq -snR "[inputs|split(\" \")[]|{directory:\"$(PWD)\", command: \"$(CC) $(CFLAGS) -c \(.)\", file:.}]" > $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(QDL) $(QDL_OBJS)
|
||||||
|
rm -f $(RAMDUMP) $(RAMDUMP_OBJS)
|
||||||
|
rm -f $(KS_OUT) $(KS_OBJS)
|
||||||
|
rm -f compile_commands.json
|
||||||
|
|
||||||
|
install: $(QDL) $(RAMDUMP) $(KS_OUT)
|
||||||
|
install -d $(DESTDIR)$(prefix)/bin
|
||||||
|
install -m 755 $^ $(DESTDIR)$(prefix)/bin
|
||||||
|
|||||||
6
README
6
README
@@ -9,8 +9,8 @@ Usage:
|
|||||||
|
|
||||||
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 and libusb-1.0 headers
|
||||||
e.g. the libxml2-dev package.
|
and libraries, found in e.g. the libxml2-dev and libusb-1.0.0-dev packages
|
||||||
|
|
||||||
With this installed run:
|
With these installed run:
|
||||||
make
|
make
|
||||||
|
|||||||
349
firehose.c
349
firehose.c
File diff suppressed because it is too large
Load Diff
117
ks.c
Normal file
117
ks.c
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
#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 <poll.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "qdl.h"
|
||||||
|
|
||||||
|
static struct qdl_device qdl;
|
||||||
|
|
||||||
|
bool qdl_debug;
|
||||||
|
|
||||||
|
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout)
|
||||||
|
{
|
||||||
|
return read(qdl->fd, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len)
|
||||||
|
{
|
||||||
|
|
||||||
|
return write(qdl->fd, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_usage(void)
|
||||||
|
{
|
||||||
|
extern const char *__progname;
|
||||||
|
fprintf(stderr,
|
||||||
|
"%s -p <sahara dev_node> -s <id:file path> ...\n",
|
||||||
|
__progname);
|
||||||
|
fprintf(stderr,
|
||||||
|
" -p --port Sahara device node to use\n"
|
||||||
|
" -s <id:file path> --sahara <id:file path> Sahara protocol file mapping\n"
|
||||||
|
"\n"
|
||||||
|
"One -p instance is required. One or more -s instances are required.\n"
|
||||||
|
"\n"
|
||||||
|
"Example: \n"
|
||||||
|
"ks -p /dev/mhi0_QAIC_SAHARA -s 1:/opt/qti-aic/firmware/fw1.bin -s 2:/opt/qti-aic/firmware/fw2.bin\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
bool found_mapping = false;
|
||||||
|
char *dev_node = NULL;
|
||||||
|
long file_id;
|
||||||
|
char *colon;
|
||||||
|
int opt;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
static struct option options[] = {
|
||||||
|
{"port", required_argument, 0, 'p'},
|
||||||
|
{"sahara", required_argument, 0, 's'},
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
while ((opt = getopt_long(argc, argv, "p:s:", options, NULL )) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'p':
|
||||||
|
dev_node = optarg;
|
||||||
|
printf("Using port - %s\n", dev_node);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
found_mapping = true;
|
||||||
|
file_id = strtol(optarg, NULL, 10);
|
||||||
|
if (file_id < 0) {
|
||||||
|
print_usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (file_id >= MAPPING_SZ) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ID:%ld exceeds the max value of %d\n",
|
||||||
|
file_id,
|
||||||
|
MAPPING_SZ - 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
colon = strchr(optarg, ':');
|
||||||
|
if (!colon) {
|
||||||
|
print_usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
qdl.mappings[file_id] = &optarg[colon - optarg + 1];
|
||||||
|
printf("Created mapping ID:%ld File:%s\n", file_id, qdl.mappings[file_id]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
print_usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -p and -s is required
|
||||||
|
if (!dev_node || !found_mapping) {
|
||||||
|
print_usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
qdl.fd = open(dev_node, O_RDWR);
|
||||||
|
if (qdl.fd < 0) {
|
||||||
|
fprintf(stderr, "Unable to open %s\n", dev_node);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sahara_run(&qdl, qdl.mappings, false, NULL, NULL);
|
||||||
|
if (ret < 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
1
patch.c
1
patch.c
@@ -30,6 +30,7 @@
|
|||||||
*/
|
*/
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <libxml/parser.h>
|
#include <libxml/parser.h>
|
||||||
#include <libxml/tree.h>
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
|
|||||||
150
program.c
150
program.c
@@ -31,23 +31,103 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <libxml/parser.h>
|
#include <libxml/parser.h>
|
||||||
#include <libxml/tree.h>
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
#include "program.h"
|
#include "program.h"
|
||||||
#include "qdl.h"
|
#include "qdl.h"
|
||||||
|
|
||||||
static struct program *programes;
|
static struct program *programes;
|
||||||
static struct program *programes_last;
|
static struct program *programes_last;
|
||||||
|
|
||||||
int program_load(const char *program_file)
|
static int load_erase_tag(xmlNode *node, bool is_nand)
|
||||||
{
|
{
|
||||||
struct program *program;
|
struct program *program;
|
||||||
|
int errors = 0;
|
||||||
|
|
||||||
|
if (!is_nand) {
|
||||||
|
fprintf(stderr, "got \"erase\" tag for non-NAND storage\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
program = calloc(1, sizeof(struct program));
|
||||||
|
|
||||||
|
|
||||||
|
program->is_nand = true;
|
||||||
|
program->is_erase = true;
|
||||||
|
|
||||||
|
program->pages_per_block = attr_as_unsigned(node, "PAGES_PER_BLOCK", &errors);
|
||||||
|
program->sector_size = attr_as_unsigned(node, "SECTOR_SIZE_IN_BYTES", &errors);
|
||||||
|
program->num_sectors = attr_as_unsigned(node, "num_partition_sectors", &errors);
|
||||||
|
program->start_sector = attr_as_string(node, "start_sector", &errors);
|
||||||
|
|
||||||
|
if (errors) {
|
||||||
|
fprintf(stderr, "[PROGRAM] errors while parsing erase tag\n");
|
||||||
|
free(program);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (programes) {
|
||||||
|
programes_last->next = program;
|
||||||
|
programes_last = program;
|
||||||
|
} else {
|
||||||
|
programes = program;
|
||||||
|
programes_last = program;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int load_program_tag(xmlNode *node, bool is_nand)
|
||||||
|
{
|
||||||
|
struct program *program;
|
||||||
|
int errors = 0;
|
||||||
|
|
||||||
|
program = calloc(1, sizeof(struct program));
|
||||||
|
|
||||||
|
program->is_nand = is_nand;
|
||||||
|
|
||||||
|
program->sector_size = attr_as_unsigned(node, "SECTOR_SIZE_IN_BYTES", &errors);
|
||||||
|
program->filename = attr_as_string(node, "filename", &errors);
|
||||||
|
program->label = attr_as_string(node, "label", &errors);
|
||||||
|
program->num_sectors = attr_as_unsigned(node, "num_partition_sectors", &errors);
|
||||||
|
program->partition = attr_as_unsigned(node, "physical_partition_number", &errors);
|
||||||
|
program->start_sector = attr_as_string(node, "start_sector", &errors);
|
||||||
|
|
||||||
|
if (is_nand) {
|
||||||
|
program->pages_per_block = attr_as_unsigned(node, "PAGES_PER_BLOCK", &errors);
|
||||||
|
if (NULL != xmlGetProp(node, (xmlChar *)"last_sector")) {
|
||||||
|
program->last_sector = attr_as_unsigned(node, "last_sector", &errors);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
program->file_offset = attr_as_unsigned(node, "file_sector_offset", &errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors) {
|
||||||
|
fprintf(stderr, "[PROGRAM] errors while parsing program\n");
|
||||||
|
free(program);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (programes) {
|
||||||
|
programes_last->next = program;
|
||||||
|
programes_last = program;
|
||||||
|
} else {
|
||||||
|
programes = program;
|
||||||
|
programes_last = program;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int program_load(const char *program_file, bool is_nand)
|
||||||
|
{
|
||||||
xmlNode *node;
|
xmlNode *node;
|
||||||
xmlNode *root;
|
xmlNode *root;
|
||||||
xmlDoc *doc;
|
xmlDoc *doc;
|
||||||
int errors;
|
int errors = 0;
|
||||||
|
|
||||||
doc = xmlReadFile(program_file, NULL, 0);
|
doc = xmlReadFile(program_file, NULL, 0);
|
||||||
if (!doc) {
|
if (!doc) {
|
||||||
@@ -60,43 +140,25 @@ int program_load(const char *program_file)
|
|||||||
if (node->type != XML_ELEMENT_NODE)
|
if (node->type != XML_ELEMENT_NODE)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (xmlStrcmp(node->name, (xmlChar*)"program")) {
|
if (!xmlStrcmp(node->name, (xmlChar *)"erase"))
|
||||||
fprintf(stderr, "[PROGRAM] unrecognized tag \"%s\", ignoring\n", node->name);
|
errors = load_erase_tag(node, is_nand);
|
||||||
continue;
|
else if (!xmlStrcmp(node->name, (xmlChar *)"program"))
|
||||||
|
errors = load_program_tag(node, is_nand);
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "[PROGRAM] unrecognized tag \"%s\"\n", node->name);
|
||||||
|
errors = -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
errors = 0;
|
if (errors)
|
||||||
|
goto out;
|
||||||
program = calloc(1, sizeof(struct program));
|
|
||||||
|
|
||||||
program->sector_size = attr_as_unsigned(node, "SECTOR_SIZE_IN_BYTES", &errors);
|
|
||||||
program->file_offset = attr_as_unsigned(node, "file_sector_offset", &errors);
|
|
||||||
program->filename = attr_as_string(node, "filename", &errors);
|
|
||||||
program->label = attr_as_string(node, "label", &errors);
|
|
||||||
program->num_sectors = attr_as_unsigned(node, "num_partition_sectors", &errors);
|
|
||||||
program->partition = attr_as_unsigned(node, "physical_partition_number", &errors);
|
|
||||||
program->start_sector = attr_as_string(node, "start_sector", &errors);
|
|
||||||
|
|
||||||
if (errors) {
|
|
||||||
fprintf(stderr, "[PROGRAM] errors while parsing program\n");
|
|
||||||
free(program);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (programes) {
|
|
||||||
programes_last->next = program;
|
|
||||||
programes_last = program;
|
|
||||||
} else {
|
|
||||||
programes = program;
|
|
||||||
programes_last = program;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
xmlFreeDoc(doc);
|
xmlFreeDoc(doc);
|
||||||
|
|
||||||
return 0;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd),
|
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd),
|
||||||
const char *incdir)
|
const char *incdir)
|
||||||
{
|
{
|
||||||
@@ -107,7 +169,7 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
|
|||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
for (program = programes; program; program = program->next) {
|
for (program = programes; program; program = program->next) {
|
||||||
if (!program->filename)
|
if (program->is_erase || !program->filename)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
filename = program->filename;
|
filename = program->filename;
|
||||||
@@ -134,6 +196,24 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int erase_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program))
|
||||||
|
{
|
||||||
|
struct program *program;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
|
||||||
|
for (program = programes; program; program = program->next) {
|
||||||
|
if (!program->is_erase)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = apply(qdl, program);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* program_find_bootable_partition() - find one bootable partition
|
* program_find_bootable_partition() - find one bootable partition
|
||||||
*
|
*
|
||||||
@@ -151,6 +231,8 @@ int program_find_bootable_partition(void)
|
|||||||
|
|
||||||
for (program = programes; program; program = program->next) {
|
for (program = programes; program; program = program->next) {
|
||||||
label = program->label;
|
label = program->label;
|
||||||
|
if (!label)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (!strcmp(label, "xbl") || !strcmp(label, "xbl_a") ||
|
if (!strcmp(label, "xbl") || !strcmp(label, "xbl_a") ||
|
||||||
!strcmp(label, "sbl1")) {
|
!strcmp(label, "sbl1")) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "qdl.h"
|
#include "qdl.h"
|
||||||
|
|
||||||
struct program {
|
struct program {
|
||||||
|
unsigned pages_per_block;
|
||||||
unsigned sector_size;
|
unsigned sector_size;
|
||||||
unsigned file_offset;
|
unsigned file_offset;
|
||||||
const char *filename;
|
const char *filename;
|
||||||
@@ -12,13 +13,18 @@ struct program {
|
|||||||
unsigned num_sectors;
|
unsigned num_sectors;
|
||||||
unsigned partition;
|
unsigned partition;
|
||||||
const char *start_sector;
|
const char *start_sector;
|
||||||
|
unsigned last_sector;
|
||||||
|
|
||||||
|
bool is_nand;
|
||||||
|
bool is_erase;
|
||||||
|
|
||||||
struct program *next;
|
struct program *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
int program_load(const char *program_file);
|
int program_load(const char *program_file, bool is_nand);
|
||||||
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd),
|
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd),
|
||||||
const char *incdir);
|
const char *incdir);
|
||||||
|
int erase_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program));
|
||||||
int program_find_bootable_partition(void);
|
int program_find_bootable_partition(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
356
qdl.c
356
qdl.c
@@ -29,26 +29,13 @@
|
|||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
#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 <err.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <libudev.h>
|
|
||||||
#include <poll.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <termios.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <libxml/parser.h>
|
#include <libxml/parser.h>
|
||||||
#include <libxml/tree.h>
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
@@ -62,21 +49,13 @@ enum {
|
|||||||
QDL_FILE_UNKNOWN,
|
QDL_FILE_UNKNOWN,
|
||||||
QDL_FILE_PATCH,
|
QDL_FILE_PATCH,
|
||||||
QDL_FILE_PROGRAM,
|
QDL_FILE_PROGRAM,
|
||||||
|
QDL_FILE_READ,
|
||||||
QDL_FILE_UFS,
|
QDL_FILE_UFS,
|
||||||
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 struct qdl_device qdl;
|
||||||
|
|
||||||
static int detect_type(const char *xml_file)
|
static int detect_type(const char *xml_file)
|
||||||
{
|
{
|
||||||
@@ -102,6 +81,10 @@ static int detect_type(const char *xml_file)
|
|||||||
type = QDL_FILE_PROGRAM;
|
type = QDL_FILE_PROGRAM;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (!xmlStrcmp(node->name, (xmlChar*)"read")) {
|
||||||
|
type = QDL_FILE_READ;
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (!xmlStrcmp(node->name, (xmlChar*)"ufs")) {
|
if (!xmlStrcmp(node->name, (xmlChar*)"ufs")) {
|
||||||
type = QDL_FILE_UFS;
|
type = QDL_FILE_UFS;
|
||||||
break;
|
break;
|
||||||
@@ -116,328 +99,40 @@ 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;
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"%s [--debug] [--storage <emmc|ufs>] [--finalize-provisioning] [--include <PATH>] <prog.mbn> [<program> <patch> ...]\n",
|
"%s [--debug] [--storage <emmc|nand|ufs>] [--finalize-provisioning] [--include <PATH>] <prog.mbn> [<program> <patch> ...]\n",
|
||||||
__progname);
|
__progname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
OPT_OUT_CHUNK_SIZE = 1000,
|
||||||
|
};
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
char *prog_mbn, *storage="ufs";
|
char *prog_mbn, *storage="ufs";
|
||||||
char *incdir = NULL;
|
char *incdir = NULL;
|
||||||
|
char *serial = NULL;
|
||||||
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;
|
long out_chunk_size;
|
||||||
|
|
||||||
|
|
||||||
static struct option options[] = {
|
static struct option options[] = {
|
||||||
{"debug", no_argument, 0, 'd'},
|
{"debug", no_argument, 0, 'd'},
|
||||||
{"include", required_argument, 0, 'i'},
|
{"include", required_argument, 0, 'i'},
|
||||||
{"finalize-provisioning", no_argument, 0, 'l'},
|
{"finalize-provisioning", no_argument, 0, 'l'},
|
||||||
|
{"out-chunk-size", required_argument, 0, OPT_OUT_CHUNK_SIZE },
|
||||||
|
{"serial", required_argument, 0, 'S'},
|
||||||
{"storage", required_argument, 0, 's'},
|
{"storage", required_argument, 0, 's'},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
while ((opt = getopt_long(argc, argv, "di:", options, NULL )) != -1) {
|
while ((opt = getopt_long(argc, argv, "di:S:", options, NULL )) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'd':
|
case 'd':
|
||||||
qdl_debug = true;
|
qdl_debug = true;
|
||||||
@@ -448,9 +143,16 @@ int main(int argc, char **argv)
|
|||||||
case 'l':
|
case 'l':
|
||||||
qdl_finalize_provisioning = true;
|
qdl_finalize_provisioning = true;
|
||||||
break;
|
break;
|
||||||
|
case OPT_OUT_CHUNK_SIZE:
|
||||||
|
out_chunk_size = strtol(optarg, NULL, 10);
|
||||||
|
qdl_set_out_chunk_size(&qdl, out_chunk_size);
|
||||||
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
storage = optarg;
|
storage = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'S':
|
||||||
|
serial = optarg;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
print_usage();
|
print_usage();
|
||||||
return 1;
|
return 1;
|
||||||
@@ -477,10 +179,15 @@ int main(int argc, char **argv)
|
|||||||
errx(1, "patch_load %s failed", argv[optind]);
|
errx(1, "patch_load %s failed", argv[optind]);
|
||||||
break;
|
break;
|
||||||
case QDL_FILE_PROGRAM:
|
case QDL_FILE_PROGRAM:
|
||||||
ret = program_load(argv[optind]);
|
ret = program_load(argv[optind], !strcmp(storage, "nand"));
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
errx(1, "program_load %s failed", argv[optind]);
|
errx(1, "program_load %s failed", argv[optind]);
|
||||||
break;
|
break;
|
||||||
|
case QDL_FILE_READ:
|
||||||
|
ret = read_op_load(argv[optind]);
|
||||||
|
if (ret < 0)
|
||||||
|
errx(1, "read_op_load %s failed", argv[optind]);
|
||||||
|
break;
|
||||||
case QDL_FILE_UFS:
|
case QDL_FILE_UFS:
|
||||||
ret = ufs_load(argv[optind],qdl_finalize_provisioning);
|
ret = ufs_load(argv[optind],qdl_finalize_provisioning);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@@ -492,11 +199,12 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
} while (++optind < argc);
|
} while (++optind < argc);
|
||||||
|
|
||||||
ret = usb_open(&qdl);
|
ret = qdl_open(&qdl, serial);
|
||||||
if (ret)
|
if (ret)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
ret = sahara_run(&qdl, prog_mbn);
|
qdl.mappings[0] = prog_mbn;
|
||||||
|
ret = sahara_run(&qdl, qdl.mappings, true, NULL, NULL);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
|||||||
26
qdl.h
26
qdl.h
@@ -5,15 +5,35 @@
|
|||||||
|
|
||||||
#include "patch.h"
|
#include "patch.h"
|
||||||
#include "program.h"
|
#include "program.h"
|
||||||
|
#include "read.h"
|
||||||
#include <libxml/tree.h>
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
struct qdl_device;
|
#define MAPPING_SZ 64
|
||||||
|
|
||||||
|
struct libusb_device_handle;
|
||||||
|
|
||||||
|
struct qdl_device {
|
||||||
|
struct libusb_device_handle *usb_handle;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
int in_ep;
|
||||||
|
int out_ep;
|
||||||
|
|
||||||
|
size_t in_maxpktsize;
|
||||||
|
size_t out_maxpktsize;
|
||||||
|
size_t out_chunk_size;
|
||||||
|
|
||||||
|
char *mappings[MAPPING_SZ]; // array index is the id from the device
|
||||||
|
};
|
||||||
|
|
||||||
|
int qdl_open(struct qdl_device *qdl, const char *serial);
|
||||||
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout);
|
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 qdl_write(struct qdl_device *qdl, const void *buf, size_t len);
|
||||||
|
void qdl_set_out_chunk_size(struct qdl_device *qdl, long size);
|
||||||
|
|
||||||
int firehose_run(struct qdl_device *qdl, const char *incdir, const char *storage);
|
int firehose_run(struct qdl_device *qdl, const char *incdir, const char *storage);
|
||||||
int sahara_run(struct qdl_device *qdl, char *prog_mbn);
|
int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
|
||||||
|
const char *ramdump_path, const char *ramdump_filter);
|
||||||
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);
|
||||||
|
|||||||
66
ramdump.c
Normal file
66
ramdump.c
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#include <getopt.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "qdl.h"
|
||||||
|
|
||||||
|
bool qdl_debug;
|
||||||
|
|
||||||
|
static void print_usage(void)
|
||||||
|
{
|
||||||
|
extern const char *__progname;
|
||||||
|
fprintf(stderr,
|
||||||
|
"%s [--debug] [-o <ramdump-path>] [segment-filter,...]\n",
|
||||||
|
__progname);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct qdl_device qdl;
|
||||||
|
char *ramdump_path = ".";
|
||||||
|
char *filter = NULL;
|
||||||
|
char *serial = NULL;
|
||||||
|
int ret;
|
||||||
|
int opt;
|
||||||
|
|
||||||
|
static struct option options[] = {
|
||||||
|
{"debug", no_argument, 0, 'd'},
|
||||||
|
{"output", required_argument, 0, 'o'},
|
||||||
|
{"serial", required_argument, 0, 'S'},
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
while ((opt = getopt_long(argc, argv, "do:S:", options, NULL )) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'd':
|
||||||
|
qdl_debug = true;
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
ramdump_path = optarg;
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
serial = optarg;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
print_usage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind < argc)
|
||||||
|
filter = argv[optind++];
|
||||||
|
|
||||||
|
if (optind != argc)
|
||||||
|
print_usage();
|
||||||
|
|
||||||
|
ret = qdl_open(&qdl, serial);
|
||||||
|
if (ret)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
ret = sahara_run(&qdl, NULL, true, ramdump_path, filter);
|
||||||
|
if (ret < 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
131
read.c
Normal file
131
read.c
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016-2017, 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 <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <libxml/parser.h>
|
||||||
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
|
#include "read.h"
|
||||||
|
#include "qdl.h"
|
||||||
|
|
||||||
|
static struct read_op *read_ops;
|
||||||
|
static struct read_op *read_ops_last;
|
||||||
|
|
||||||
|
int read_op_load(const char *read_op_file)
|
||||||
|
{
|
||||||
|
struct read_op *read_op;
|
||||||
|
xmlNode *node;
|
||||||
|
xmlNode *root;
|
||||||
|
xmlDoc *doc;
|
||||||
|
int errors;
|
||||||
|
|
||||||
|
doc = xmlReadFile(read_op_file, NULL, 0);
|
||||||
|
if (!doc) {
|
||||||
|
fprintf(stderr, "[READ] failed to parse %s\n", read_op_file);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
root = xmlDocGetRootElement(doc);
|
||||||
|
for (node = root->children; node ; node = node->next) {
|
||||||
|
if (node->type != XML_ELEMENT_NODE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (xmlStrcmp(node->name, (xmlChar*)"read")) {
|
||||||
|
fprintf(stderr, "[READ] unrecognized tag \"%s\", ignoring\n", node->name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
errors = 0;
|
||||||
|
|
||||||
|
read_op = calloc(1, sizeof(struct read_op));
|
||||||
|
|
||||||
|
read_op->sector_size = attr_as_unsigned(node, "SECTOR_SIZE_IN_BYTES", &errors);
|
||||||
|
read_op->filename = attr_as_string(node, "filename", &errors);
|
||||||
|
read_op->partition = attr_as_unsigned(node, "physical_partition_number", &errors);
|
||||||
|
read_op->num_sectors = attr_as_unsigned(node, "num_partition_sectors", &errors);
|
||||||
|
read_op->start_sector = attr_as_string(node, "start_sector", &errors);
|
||||||
|
|
||||||
|
if (errors) {
|
||||||
|
fprintf(stderr, "[READ] errors while parsing read\n");
|
||||||
|
free(read_op);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_ops) {
|
||||||
|
read_ops_last->next = read_op;
|
||||||
|
read_ops_last = read_op;
|
||||||
|
} else {
|
||||||
|
read_ops = read_op;
|
||||||
|
read_ops_last = read_op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xmlFreeDoc(doc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int read_op_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct read_op *read_op, int fd),
|
||||||
|
const char *incdir)
|
||||||
|
{
|
||||||
|
struct read_op *read_op;
|
||||||
|
const char *filename;
|
||||||
|
char tmp[PATH_MAX];
|
||||||
|
int ret;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
for (read_op = read_ops; read_op; read_op = read_op->next) {
|
||||||
|
filename = read_op->filename;
|
||||||
|
if (incdir) {
|
||||||
|
snprintf(tmp, PATH_MAX, "%s/%s", incdir, filename);
|
||||||
|
if (access(tmp, F_OK) != -1)
|
||||||
|
filename = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
|
||||||
|
|
||||||
|
if (fd < 0) {
|
||||||
|
printf("Unable to open %s...\n", read_op->filename);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = apply(qdl, read_op, fd);
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
22
read.h
Normal file
22
read.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#ifndef __READ_H__
|
||||||
|
#define __READ_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
struct qdl_device;
|
||||||
|
|
||||||
|
struct read_op {
|
||||||
|
unsigned sector_size;
|
||||||
|
const char *filename;
|
||||||
|
unsigned partition;
|
||||||
|
unsigned num_sectors;
|
||||||
|
const char *start_sector;
|
||||||
|
struct read_op *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
int read_op_load(const char *read_op_file);
|
||||||
|
int read_op_execute(struct qdl_device *qdl,
|
||||||
|
int (*apply)(struct qdl_device *qdl, struct read_op *read_op, int fd),
|
||||||
|
const char *incdir);
|
||||||
|
|
||||||
|
#endif
|
||||||
329
sahara.c
329
sahara.c
@@ -35,6 +35,7 @@
|
|||||||
#include <err.h>
|
#include <err.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <fnmatch.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
@@ -46,6 +47,49 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "qdl.h"
|
#include "qdl.h"
|
||||||
|
|
||||||
|
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||||
|
|
||||||
|
#define SAHARA_HELLO_CMD 0x1 /* Min protocol version 1.0 */
|
||||||
|
#define SAHARA_HELLO_RESP_CMD 0x2 /* Min protocol version 1.0 */
|
||||||
|
#define SAHARA_READ_DATA_CMD 0x3 /* Min protocol version 1.0 */
|
||||||
|
#define SAHARA_END_OF_IMAGE_CMD 0x4 /* Min protocol version 1.0 */
|
||||||
|
#define SAHARA_DONE_CMD 0x5 /* Min protocol version 1.0 */
|
||||||
|
#define SAHARA_DONE_RESP_CMD 0x6 /* Min protocol version 1.0 */
|
||||||
|
#define SAHARA_RESET_CMD 0x7 /* Min protocol version 1.0 */
|
||||||
|
#define SAHARA_RESET_RESP_CMD 0x8 /* Min protocol version 1.0 */
|
||||||
|
#define SAHARA_MEM_DEBUG_CMD 0x9 /* Min protocol version 2.0 */
|
||||||
|
#define SAHARA_MEM_READ_CMD 0xa /* Min protocol version 2.0 */
|
||||||
|
#define SAHARA_CMD_READY_CMD 0xb /* Min protocol version 2.1 */
|
||||||
|
#define SAHARA_SWITCH_MODE_CMD 0xc /* Min protocol version 2.1 */
|
||||||
|
#define SAHARA_EXECUTE_CMD 0xd /* Min protocol version 2.1 */
|
||||||
|
#define SAHARA_EXECUTE_RESP_CMD 0xe /* Min protocol version 2.1 */
|
||||||
|
#define SAHARA_EXECUTE_DATA_CMD 0xf /* Min protocol version 2.1 */
|
||||||
|
#define SAHARA_MEM_DEBUG64_CMD 0x10 /* Min protocol version 2.5 */
|
||||||
|
#define SAHARA_MEM_READ64_CMD 0x11 /* Min protocol version 2.5 */
|
||||||
|
#define SAHARA_READ_DATA64_CMD 0x12 /* Min protocol version 2.8 */
|
||||||
|
#define SAHARA_RESET_STATE_CMD 0x13 /* Min protocol version 2.9 */
|
||||||
|
#define SAHARA_WRITE_DATA_CMD 0x14 /* Min protocol version 3.0 */
|
||||||
|
|
||||||
|
#define SAHARA_VERSION 2
|
||||||
|
#define SAHARA_SUCCESS 0
|
||||||
|
|
||||||
|
#define SAHARA_MODE_IMAGE_TX_PENDING 0x0
|
||||||
|
#define SAHARA_MODE_IMAGE_TX_COMPLETE 0x1
|
||||||
|
#define SAHARA_MODE_MEMORY_DEBUG 0x2
|
||||||
|
#define SAHARA_MODE_COMMAND 0x3
|
||||||
|
|
||||||
|
#define SAHARA_HELLO_LENGTH 0x30
|
||||||
|
#define SAHARA_READ_DATA_LENGTH 0x14
|
||||||
|
#define SAHARA_READ_DATA64_LENGTH 0x20
|
||||||
|
#define SAHARA_END_OF_IMAGE_LENGTH 0x10
|
||||||
|
#define SAHARA_MEM_READ64_LENGTH 0x18
|
||||||
|
#define SAHARA_MEM_DEBUG64_LENGTH 0x18
|
||||||
|
#define SAHARA_DONE_LENGTH 0x8
|
||||||
|
#define SAHARA_DONE_RESP_LENGTH 0xc
|
||||||
|
#define SAHARA_RESET_LENGTH 0x8
|
||||||
|
|
||||||
|
#define DEBUG_BLOCK_SIZE (512*1024)
|
||||||
|
|
||||||
struct sahara_pkt {
|
struct sahara_pkt {
|
||||||
uint32_t cmd;
|
uint32_t cmd;
|
||||||
uint32_t length;
|
uint32_t length;
|
||||||
@@ -77,6 +121,10 @@ struct sahara_pkt {
|
|||||||
struct {
|
struct {
|
||||||
uint32_t status;
|
uint32_t status;
|
||||||
} done_resp;
|
} done_resp;
|
||||||
|
struct {
|
||||||
|
uint64_t addr;
|
||||||
|
uint64_t length;
|
||||||
|
} debug64_req;
|
||||||
struct {
|
struct {
|
||||||
uint64_t image;
|
uint64_t image;
|
||||||
uint64_t offset;
|
uint64_t offset;
|
||||||
@@ -85,36 +133,49 @@ struct sahara_pkt {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct sahara_debug_region64 {
|
||||||
|
uint64_t type;
|
||||||
|
uint64_t addr;
|
||||||
|
uint64_t length;
|
||||||
|
char region[20];
|
||||||
|
char filename[20];
|
||||||
|
};
|
||||||
|
|
||||||
|
static void sahara_send_reset(struct qdl_device *qdl)
|
||||||
|
{
|
||||||
|
struct sahara_pkt resp;
|
||||||
|
|
||||||
|
resp.cmd = SAHARA_RESET_CMD;
|
||||||
|
resp.length = SAHARA_RESET_LENGTH;
|
||||||
|
|
||||||
|
qdl_write(qdl, &resp, resp.length);
|
||||||
|
}
|
||||||
|
|
||||||
static void sahara_hello(struct qdl_device *qdl, struct sahara_pkt *pkt)
|
static void sahara_hello(struct qdl_device *qdl, struct sahara_pkt *pkt)
|
||||||
{
|
{
|
||||||
struct sahara_pkt resp;
|
struct sahara_pkt resp;
|
||||||
|
|
||||||
assert(pkt->length == 0x30);
|
assert(pkt->length == SAHARA_HELLO_LENGTH);
|
||||||
|
|
||||||
printf("HELLO version: 0x%x compatible: 0x%x max_len: %d mode: %d\n",
|
printf("HELLO version: 0x%x compatible: 0x%x max_len: %d mode: %d\n",
|
||||||
pkt->hello_req.version, pkt->hello_req.compatible, pkt->hello_req.max_len, pkt->hello_req.mode);
|
pkt->hello_req.version, pkt->hello_req.compatible, pkt->hello_req.max_len, pkt->hello_req.mode);
|
||||||
|
|
||||||
resp.cmd = 2;
|
resp.cmd = SAHARA_HELLO_RESP_CMD;
|
||||||
resp.length = 0x30;
|
resp.length = SAHARA_HELLO_LENGTH;
|
||||||
resp.hello_resp.version = 2;
|
resp.hello_resp.version = SAHARA_VERSION;
|
||||||
resp.hello_resp.compatible = 1;
|
resp.hello_resp.compatible = 1;
|
||||||
resp.hello_resp.status = 0;
|
resp.hello_resp.status = SAHARA_SUCCESS;
|
||||||
resp.hello_resp.mode = pkt->hello_req.mode;
|
resp.hello_resp.mode = pkt->hello_req.mode;
|
||||||
|
|
||||||
qdl_write(qdl, &resp, resp.length, true);
|
qdl_write(qdl, &resp, resp.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sahara_read_common(struct qdl_device *qdl, const char *mbn, off_t offset, size_t len)
|
static int sahara_read_common(struct qdl_device *qdl, int progfd, off_t offset, size_t len)
|
||||||
{
|
{
|
||||||
int progfd;
|
|
||||||
ssize_t n;
|
ssize_t n;
|
||||||
void *buf;
|
void *buf;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
progfd = open(mbn, O_RDONLY);
|
|
||||||
if (progfd < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
buf = malloc(len);
|
buf = malloc(len);
|
||||||
if (!buf)
|
if (!buf)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@@ -126,50 +187,96 @@ static int sahara_read_common(struct qdl_device *qdl, const char *mbn, off_t off
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = qdl_write(qdl, buf, n, true);
|
n = qdl_write(qdl, buf, n);
|
||||||
if (n != len)
|
if (n != len)
|
||||||
err(1, "failed to write %zu bytes to sahara", len);
|
err(1, "failed to write %zu bytes to sahara", len);
|
||||||
|
|
||||||
free(buf);
|
free(buf);
|
||||||
close(progfd);
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sahara_read(struct qdl_device *qdl, struct sahara_pkt *pkt, const char *mbn)
|
static void sahara_read(struct qdl_device *qdl, struct sahara_pkt *pkt, char *img_arr[], bool single_image)
|
||||||
{
|
{
|
||||||
|
unsigned int image;
|
||||||
int ret;
|
int ret;
|
||||||
|
int fd;
|
||||||
|
|
||||||
assert(pkt->length == 0x14);
|
assert(pkt->length == SAHARA_READ_DATA_LENGTH);
|
||||||
|
|
||||||
printf("READ image: %d offset: 0x%x length: 0x%x\n",
|
printf("READ image: %d offset: 0x%x length: 0x%x\n",
|
||||||
pkt->read_req.image, pkt->read_req.offset, pkt->read_req.length);
|
pkt->read_req.image, pkt->read_req.offset, pkt->read_req.length);
|
||||||
|
|
||||||
ret = sahara_read_common(qdl, mbn, pkt->read_req.offset, pkt->read_req.length);
|
if (single_image)
|
||||||
|
image = 0;
|
||||||
|
else
|
||||||
|
image = pkt->read_req.image;
|
||||||
|
|
||||||
|
if (image >= MAPPING_SZ || !img_arr[image]) {
|
||||||
|
fprintf(stderr, "Device specified invalid image: %u\n", image);
|
||||||
|
sahara_send_reset(qdl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = open(img_arr[image], O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
fprintf(stderr, "Can not open %s: %s\n", img_arr[image], strerror(errno));
|
||||||
|
// Maybe this read was optional. Notify device of error and let
|
||||||
|
// it decide how to proceed.
|
||||||
|
sahara_send_reset(qdl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sahara_read_common(qdl, fd, pkt->read_req.offset, pkt->read_req.length);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
errx(1, "failed to read image chunk to sahara");
|
errx(1, "failed to read image chunk to sahara");
|
||||||
|
|
||||||
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sahara_read64(struct qdl_device *qdl, struct sahara_pkt *pkt, const char *mbn)
|
static void sahara_read64(struct qdl_device *qdl, struct sahara_pkt *pkt, char *img_arr[], bool single_image)
|
||||||
{
|
{
|
||||||
|
unsigned int image;
|
||||||
int ret;
|
int ret;
|
||||||
|
int fd;
|
||||||
|
|
||||||
assert(pkt->length == 0x20);
|
assert(pkt->length == SAHARA_READ_DATA64_LENGTH);
|
||||||
|
|
||||||
printf("READ64 image: %" PRId64 " offset: 0x%" PRIx64 " length: 0x%" PRIx64 "\n",
|
printf("READ64 image: %" PRId64 " offset: 0x%" PRIx64 " length: 0x%" PRIx64 "\n",
|
||||||
pkt->read64_req.image, pkt->read64_req.offset, pkt->read64_req.length);
|
pkt->read64_req.image, pkt->read64_req.offset, pkt->read64_req.length);
|
||||||
|
|
||||||
ret = sahara_read_common(qdl, mbn, pkt->read64_req.offset, pkt->read64_req.length);
|
if (single_image)
|
||||||
|
image = 0;
|
||||||
|
else
|
||||||
|
image = pkt->read64_req.image;
|
||||||
|
|
||||||
|
if (image >= MAPPING_SZ || !img_arr[image]) {
|
||||||
|
fprintf(stderr, "Device specified invalid image: %u\n", image);
|
||||||
|
sahara_send_reset(qdl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fd = open(img_arr[image], O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
fprintf(stderr, "Can not open %s: %s\n", img_arr[image], strerror(errno));
|
||||||
|
// Maybe this read was optional. Notify device of error and let
|
||||||
|
// it decide how to proceed.
|
||||||
|
sahara_send_reset(qdl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sahara_read_common(qdl, fd, pkt->read64_req.offset, pkt->read64_req.length);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
errx(1, "failed to read image chunk to sahara");
|
errx(1, "failed to read image chunk to sahara");
|
||||||
|
|
||||||
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sahara_eoi(struct qdl_device *qdl, struct sahara_pkt *pkt)
|
static void sahara_eoi(struct qdl_device *qdl, struct sahara_pkt *pkt)
|
||||||
{
|
{
|
||||||
struct sahara_pkt done;
|
struct sahara_pkt done;
|
||||||
|
|
||||||
assert(pkt->length == 0x10);
|
assert(pkt->length == SAHARA_END_OF_IMAGE_LENGTH);
|
||||||
|
|
||||||
printf("END OF IMAGE image: %d status: %d\n", pkt->eoi.image, pkt->eoi.status);
|
printf("END OF IMAGE image: %d status: %d\n", pkt->eoi.image, pkt->eoi.status);
|
||||||
|
|
||||||
@@ -178,28 +285,165 @@ static void sahara_eoi(struct qdl_device *qdl, struct sahara_pkt *pkt)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
done.cmd = 5;
|
done.cmd = SAHARA_DONE_CMD;
|
||||||
done.length = 0x8;
|
done.length = SAHARA_DONE_LENGTH;
|
||||||
qdl_write(qdl, &done, done.length, true);
|
qdl_write(qdl, &done, done.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sahara_done(struct qdl_device *qdl, struct sahara_pkt *pkt)
|
static int sahara_done(struct qdl_device *qdl, struct sahara_pkt *pkt)
|
||||||
{
|
{
|
||||||
assert(pkt->length == 0xc);
|
assert(pkt->length == SAHARA_DONE_RESP_LENGTH);
|
||||||
|
|
||||||
printf("DONE status: %d\n", pkt->done_resp.status);
|
printf("DONE status: %d\n", pkt->done_resp.status);
|
||||||
|
|
||||||
|
// 0 == PENDING, 1 == COMPLETE. Device expects more images if
|
||||||
|
// PENDING is set in status.
|
||||||
return pkt->done_resp.status;
|
return pkt->done_resp.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sahara_run(struct qdl_device *qdl, char *prog_mbn)
|
static ssize_t sahara_debug64_one(struct qdl_device *qdl,
|
||||||
|
struct sahara_debug_region64 region,
|
||||||
|
int ramdump_dir)
|
||||||
|
{
|
||||||
|
struct sahara_pkt read_req;
|
||||||
|
uint64_t remain;
|
||||||
|
size_t offset;
|
||||||
|
size_t chunk;
|
||||||
|
ssize_t n;
|
||||||
|
void *buf;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
buf = malloc(DEBUG_BLOCK_SIZE);
|
||||||
|
if (!buf)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
fd = openat(ramdump_dir, region.filename, O_WRONLY | O_CREAT, 0644);
|
||||||
|
if (fd < 0) {
|
||||||
|
warn("failed to open \"%s\"", region.filename);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk = 0;
|
||||||
|
while (chunk < region.length) {
|
||||||
|
remain = MIN(region.length - chunk, DEBUG_BLOCK_SIZE);
|
||||||
|
|
||||||
|
read_req.cmd = SAHARA_MEM_READ64_CMD;
|
||||||
|
read_req.length = SAHARA_MEM_READ64_LENGTH;
|
||||||
|
read_req.debug64_req.addr = region.addr + chunk;
|
||||||
|
read_req.debug64_req.length = remain;
|
||||||
|
n = qdl_write(qdl, &read_req, read_req.length);
|
||||||
|
if (n < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
offset = 0;
|
||||||
|
while (offset < remain) {
|
||||||
|
n = qdl_read(qdl, buf, DEBUG_BLOCK_SIZE, 30000);
|
||||||
|
if (n < 0) {
|
||||||
|
warn("failed to read ramdump chunk");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
write(fd, buf, n);
|
||||||
|
offset += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
qdl_read(qdl, buf, DEBUG_BLOCK_SIZE, 10);
|
||||||
|
|
||||||
|
chunk += DEBUG_BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool sahara_debug64_filter(const char *filename, const char *filter)
|
||||||
|
{
|
||||||
|
bool anymatch = false;
|
||||||
|
char *ptr;
|
||||||
|
char *tmp;
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
if (!filter)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
tmp = strdup(filter);
|
||||||
|
for (s = strtok_r(tmp, ",", &ptr); s; s = strtok_r(NULL, ",", &ptr)) {
|
||||||
|
if (fnmatch(s, filename, 0) == 0) {
|
||||||
|
anymatch = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(tmp);
|
||||||
|
|
||||||
|
return !anymatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sahara_debug64(struct qdl_device *qdl, struct sahara_pkt *pkt,
|
||||||
|
int ramdump_dir, const char *filter)
|
||||||
|
{
|
||||||
|
struct sahara_debug_region64 *table;
|
||||||
|
struct sahara_pkt read_req;
|
||||||
|
ssize_t n;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
assert(pkt->length == SAHARA_MEM_DEBUG64_LENGTH);
|
||||||
|
|
||||||
|
printf("DEBUG64 address: 0x%" PRIx64 " length: 0x%" PRIx64 "\n",
|
||||||
|
pkt->debug64_req.addr, pkt->debug64_req.length);
|
||||||
|
|
||||||
|
read_req.cmd = SAHARA_MEM_READ64_CMD;
|
||||||
|
read_req.length = SAHARA_MEM_READ64_LENGTH;
|
||||||
|
read_req.debug64_req.addr = pkt->debug64_req.addr;
|
||||||
|
read_req.debug64_req.length = pkt->debug64_req.length;
|
||||||
|
|
||||||
|
n = qdl_write(qdl, &read_req, read_req.length);
|
||||||
|
if (n < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
table = malloc(read_req.debug64_req.length);
|
||||||
|
|
||||||
|
n = qdl_read(qdl, table, pkt->debug64_req.length, 1000);
|
||||||
|
if (n < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < pkt->debug64_req.length / sizeof(table[0]); i++) {
|
||||||
|
if (sahara_debug64_filter(table[i].filename, filter))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
printf("%-2d: type 0x%" PRIx64 " address: 0x%" PRIx64 " length: 0x%" PRIx64 " region: %s filename: %s\n",
|
||||||
|
i, table[i].type, table[i].addr, table[i].length, table[i].region, table[i].filename);
|
||||||
|
|
||||||
|
|
||||||
|
n = sahara_debug64_one(qdl, table[i], ramdump_dir);
|
||||||
|
if (n < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
free(table);
|
||||||
|
|
||||||
|
sahara_send_reset(qdl);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
|
||||||
|
const char *ramdump_path, const char *ramdump_filter)
|
||||||
{
|
{
|
||||||
struct sahara_pkt *pkt;
|
struct sahara_pkt *pkt;
|
||||||
|
int ramdump_dir = -1;
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
char tmp[32];
|
char tmp[32];
|
||||||
bool done = false;
|
bool done = false;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
|
if (ramdump_path) {
|
||||||
|
ramdump_dir = open(ramdump_path, O_DIRECTORY);
|
||||||
|
if (ramdump_dir < 0)
|
||||||
|
err(1, "failed to open directory for ramdump output");
|
||||||
|
}
|
||||||
|
|
||||||
while (!done) {
|
while (!done) {
|
||||||
n = qdl_read(qdl, buf, sizeof(buf), 1000);
|
n = qdl_read(qdl, buf, sizeof(buf), 1000);
|
||||||
if (n < 0)
|
if (n < 0)
|
||||||
@@ -207,26 +451,37 @@ int sahara_run(struct qdl_device *qdl, char *prog_mbn)
|
|||||||
|
|
||||||
pkt = (struct sahara_pkt*)buf;
|
pkt = (struct sahara_pkt*)buf;
|
||||||
if (n != pkt->length) {
|
if (n != pkt->length) {
|
||||||
fprintf(stderr, "length not matching");
|
fprintf(stderr, "length not matching\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (pkt->cmd) {
|
switch (pkt->cmd) {
|
||||||
case 1:
|
case SAHARA_HELLO_CMD:
|
||||||
sahara_hello(qdl, pkt);
|
sahara_hello(qdl, pkt);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case SAHARA_READ_DATA_CMD:
|
||||||
sahara_read(qdl, pkt, prog_mbn);
|
sahara_read(qdl, pkt, img_arr, single_image);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case SAHARA_END_OF_IMAGE_CMD:
|
||||||
sahara_eoi(qdl, pkt);
|
sahara_eoi(qdl, pkt);
|
||||||
break;
|
break;
|
||||||
case 6:
|
case SAHARA_DONE_RESP_CMD:
|
||||||
sahara_done(qdl, pkt);
|
done = sahara_done(qdl, pkt);
|
||||||
done = true;
|
|
||||||
|
/* E.g MSM8916 EDL reports done = 0 here */
|
||||||
|
if (single_image)
|
||||||
|
done = true;
|
||||||
break;
|
break;
|
||||||
case 0x12:
|
case SAHARA_MEM_DEBUG64_CMD:
|
||||||
sahara_read64(qdl, pkt, prog_mbn);
|
sahara_debug64(qdl, pkt, ramdump_dir, ramdump_filter);
|
||||||
|
break;
|
||||||
|
case SAHARA_READ_DATA64_CMD:
|
||||||
|
sahara_read64(qdl, pkt, img_arr, single_image);
|
||||||
|
break;
|
||||||
|
case SAHARA_RESET_RESP_CMD:
|
||||||
|
assert(pkt->length == SAHARA_RESET_LENGTH);
|
||||||
|
if (ramdump_path)
|
||||||
|
done = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
sprintf(tmp, "CMD%x", pkt->cmd);
|
sprintf(tmp, "CMD%x", pkt->cmd);
|
||||||
@@ -235,5 +490,7 @@ int sahara_run(struct qdl_device *qdl, char *prog_mbn)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
close(ramdump_dir);
|
||||||
|
|
||||||
return done ? 0 : -1;
|
return done ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|||||||
262
usb.c
Normal file
262
usb.c
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <libusb.h>
|
||||||
|
|
||||||
|
#include "qdl.h"
|
||||||
|
|
||||||
|
#define DEFAULT_OUT_CHUNK_SIZE (1024 * 1024)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* libusb commit f0cce43f882d ("core: Fix definition and use of enum
|
||||||
|
* libusb_transfer_type") split transfer type and endpoint transfer types.
|
||||||
|
* Provide an alias in order to make the code compile with the old (non-split)
|
||||||
|
* definition.
|
||||||
|
*/
|
||||||
|
#ifndef LIBUSB_ENDPOINT_TRANSFER_TYPE_BULK
|
||||||
|
#define LIBUSB_ENDPOINT_TRANSFER_TYPE_BULK LIBUSB_TRANSFER_TYPE_BULK
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static bool qdl_match_usb_serial(struct libusb_device_handle *handle, const char *serial,
|
||||||
|
const struct libusb_device_descriptor *desc)
|
||||||
|
{
|
||||||
|
char buf[128];
|
||||||
|
char *p;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* If no serial is requested, consider everything a match */
|
||||||
|
if (!serial)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
ret = libusb_get_string_descriptor_ascii(handle, desc->iProduct, (unsigned char *)buf, sizeof(buf));
|
||||||
|
if (ret < 0) {
|
||||||
|
warnx("failed to read iProduct descriptor: %s", libusb_strerror(ret));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = strstr(buf, "_SN:");
|
||||||
|
if (!p)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
p += strlen("_SN:");
|
||||||
|
p[strcspn(p, " _")] = '\0';
|
||||||
|
|
||||||
|
return strcmp(p, serial) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qdl_try_open(libusb_device *dev, struct qdl_device *qdl, const char *serial)
|
||||||
|
{
|
||||||
|
const struct libusb_endpoint_descriptor *endpoint;
|
||||||
|
const struct libusb_interface_descriptor *ifc;
|
||||||
|
struct libusb_config_descriptor *config;
|
||||||
|
struct libusb_device_descriptor desc;
|
||||||
|
struct libusb_device_handle *handle;
|
||||||
|
size_t out_size;
|
||||||
|
size_t in_size;
|
||||||
|
uint8_t type;
|
||||||
|
int ret;
|
||||||
|
int out;
|
||||||
|
int in;
|
||||||
|
int k;
|
||||||
|
int l;
|
||||||
|
|
||||||
|
ret = libusb_get_device_descriptor(dev, &desc);
|
||||||
|
if (ret < 0) {
|
||||||
|
warnx("failed to get USB device descriptor");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Consider only devices with vid 0x0506 and product id 0x9008 or 0x900e */
|
||||||
|
if (desc.idVendor != 0x05c6 || (desc.idProduct != 0x9008 && desc.idProduct != 0x900e))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = libusb_get_active_config_descriptor(dev, &config);
|
||||||
|
if (ret < 0) {
|
||||||
|
warnx("failed to acquire USB device's active config descriptor");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (k = 0; k < config->bNumInterfaces; k++) {
|
||||||
|
ifc = config->interface[k].altsetting;
|
||||||
|
|
||||||
|
in = -1;
|
||||||
|
out = -1;
|
||||||
|
in_size = 0;
|
||||||
|
out_size = 0;
|
||||||
|
|
||||||
|
for (l = 0; l < ifc->bNumEndpoints; l++) {
|
||||||
|
endpoint = &ifc->endpoint[l];
|
||||||
|
|
||||||
|
type = endpoint->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK;
|
||||||
|
if (type != LIBUSB_ENDPOINT_TRANSFER_TYPE_BULK)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) {
|
||||||
|
in = endpoint->bEndpointAddress;
|
||||||
|
in_size = endpoint->wMaxPacketSize;
|
||||||
|
} else {
|
||||||
|
out = endpoint->bEndpointAddress;
|
||||||
|
out_size = endpoint->wMaxPacketSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ifc->bInterfaceClass != 0xff)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (ifc->bInterfaceSubClass != 0xff)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* bInterfaceProtocol of 0xff, 0x10 and 0x11 has been seen */
|
||||||
|
if (ifc->bInterfaceProtocol != 0xff &&
|
||||||
|
ifc->bInterfaceProtocol != 16 &&
|
||||||
|
ifc->bInterfaceProtocol != 17)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = libusb_open(dev, &handle);
|
||||||
|
if (ret < 0) {
|
||||||
|
warnx("unable to open USB device");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!qdl_match_usb_serial(handle, serial, &desc)) {
|
||||||
|
libusb_close(handle);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
libusb_detach_kernel_driver(handle, ifc->bInterfaceNumber);
|
||||||
|
|
||||||
|
ret = libusb_claim_interface(handle, ifc->bInterfaceNumber);
|
||||||
|
if (ret < 0) {
|
||||||
|
warnx("failed to claim USB interface");
|
||||||
|
libusb_close(handle);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
qdl->usb_handle = handle;
|
||||||
|
qdl->in_ep = in;
|
||||||
|
qdl->out_ep = out;
|
||||||
|
qdl->in_maxpktsize = in_size;
|
||||||
|
qdl->out_maxpktsize = out_size;
|
||||||
|
|
||||||
|
if (qdl->out_chunk_size && qdl->out_chunk_size % out_size) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"WARNING: requested out-chunk-size must be multiple of the device's wMaxPacketSize %ld, using %ld\n",
|
||||||
|
out_size, out_size);
|
||||||
|
qdl->out_chunk_size = out_size;
|
||||||
|
} else if (!qdl->out_chunk_size) {
|
||||||
|
qdl->out_chunk_size = DEFAULT_OUT_CHUNK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qdl_debug) {
|
||||||
|
fprintf(stderr, "USB: using out-chunk-size of %ld\n",
|
||||||
|
qdl->out_chunk_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int qdl_open(struct qdl_device *qdl, const char *serial)
|
||||||
|
{
|
||||||
|
struct libusb_device **devs;
|
||||||
|
struct libusb_device *dev;
|
||||||
|
bool wait_printed = false;
|
||||||
|
bool found = false;
|
||||||
|
ssize_t n;
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ret = libusb_init(NULL);
|
||||||
|
if (ret < 0)
|
||||||
|
err(1, "failed to initialize libusb");
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
n = libusb_get_device_list(NULL, &devs);
|
||||||
|
if (n < 0)
|
||||||
|
err(1, "failed to list USB devices");
|
||||||
|
|
||||||
|
for (i = 0; devs[i]; i++) {
|
||||||
|
dev = devs[i];
|
||||||
|
|
||||||
|
ret = qdl_try_open(dev, qdl, serial);
|
||||||
|
if (ret == 1) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
libusb_free_device_list(devs, 1);
|
||||||
|
|
||||||
|
if (found)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!wait_printed) {
|
||||||
|
fprintf(stderr, "Waiting for EDL device\n");
|
||||||
|
wait_printed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep(250000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout)
|
||||||
|
{
|
||||||
|
int actual;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = libusb_bulk_transfer(qdl->usb_handle, qdl->in_ep, buf, len, &actual, timeout);
|
||||||
|
if ((ret != 0 && ret != LIBUSB_ERROR_TIMEOUT) ||
|
||||||
|
(ret == LIBUSB_ERROR_TIMEOUT && actual == 0))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return actual;
|
||||||
|
}
|
||||||
|
|
||||||
|
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len)
|
||||||
|
{
|
||||||
|
unsigned char *data = (unsigned char*) buf;
|
||||||
|
unsigned int count = 0;
|
||||||
|
size_t len_orig = len;
|
||||||
|
int actual;
|
||||||
|
int xfer;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
while (len > 0) {
|
||||||
|
xfer = (len > qdl->out_chunk_size) ? qdl->out_chunk_size : len;
|
||||||
|
|
||||||
|
ret = libusb_bulk_transfer(qdl->usb_handle, qdl->out_ep, data,
|
||||||
|
xfer, &actual, 1000);
|
||||||
|
if ((ret != 0 && ret != LIBUSB_ERROR_TIMEOUT) ||
|
||||||
|
(ret == LIBUSB_ERROR_TIMEOUT && actual == 0)) {
|
||||||
|
warnx("bulk write failed: %s", libusb_strerror(ret));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
count += actual;
|
||||||
|
len -= actual;
|
||||||
|
data += actual;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len_orig % qdl->out_maxpktsize == 0) {
|
||||||
|
ret = libusb_bulk_transfer(qdl->usb_handle, qdl->out_ep, NULL,
|
||||||
|
0, &actual, 1000);
|
||||||
|
if (ret < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void qdl_set_out_chunk_size(struct qdl_device *qdl, long size)
|
||||||
|
{
|
||||||
|
qdl->out_chunk_size = size;
|
||||||
|
}
|
||||||
3
util.c
3
util.c
@@ -32,6 +32,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <libxml/parser.h>
|
#include <libxml/parser.h>
|
||||||
#include <libxml/tree.h>
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
@@ -91,7 +92,7 @@ unsigned attr_as_unsigned(xmlNode *node, const char *attr, int *errors)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (unsigned int) strtoul((char*)value, NULL, 10);
|
return (unsigned int) strtoul((char*)value, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *attr_as_string(xmlNode *node, const char *attr, int *errors)
|
const char *attr_as_string(xmlNode *node, const char *attr, int *errors)
|
||||||
|
|||||||
Reference in New Issue
Block a user