mirror of
https://xff.cz/git/pinephone-keyboard
synced 2024-12-22 12:58:48 -05:00
Tidy up the flashing tool
This commit is contained in:
parent
c1d2adfc96
commit
212dea4914
@ -2,4 +2,5 @@
|
||||
|
||||
set -e
|
||||
|
||||
gcc -o kbprog-usb kbprog-usb.c
|
||||
gcc -o ppkb-flasher flasher.c
|
||||
gcc -o ppkb-debugger debugger.c
|
||||
|
205
usb-flasher/common.c
Normal file
205
usb-flasher/common.c
Normal file
@ -0,0 +1,205 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include <linux/usbdevice_fs.h>
|
||||
|
||||
static bool verbose;
|
||||
#define debug(args...) { if (verbose) printf(args); }
|
||||
|
||||
static void syscall_error(int is_err, const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (!is_err)
|
||||
return;
|
||||
|
||||
fprintf(stderr, "ERROR: ");
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
fprintf(stderr, ": %s\n", strerror(errno));
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void error(const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
fprintf(stderr, "ERROR: ");
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static bool read_file(const char* path, char* buf, size_t size)
|
||||
{
|
||||
int fd;
|
||||
ssize_t ret;
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
|
||||
ret = read(fd, buf, size);
|
||||
close(fd);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
if (ret < size) {
|
||||
buf[ret] = 0;
|
||||
return true;
|
||||
} else {
|
||||
buf[size - 1] = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int open_usb_dev(uint16_t vid, uint16_t pid)
|
||||
{
|
||||
char path[256], buf[256];
|
||||
struct dirent *e;
|
||||
unsigned e_vid, e_pid, bus, dev;
|
||||
int fd = -1, ret;
|
||||
DIR* d;
|
||||
|
||||
d = opendir("/sys/bus/usb/devices");
|
||||
syscall_error(d == NULL, "opendir(/sys/bus/usb/devices) failed");
|
||||
|
||||
while (true) {
|
||||
errno = 0;
|
||||
e = readdir(d);
|
||||
syscall_error(e == NULL && errno, "readdir(/sys/bus/usb/devices) failed");
|
||||
if (!e)
|
||||
break;
|
||||
|
||||
if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, ".."))
|
||||
continue;
|
||||
|
||||
snprintf(path, sizeof path,
|
||||
"/sys/bus/usb/devices/%s/idVendor", e->d_name);
|
||||
if (!read_file(path, buf, sizeof buf))
|
||||
continue;
|
||||
|
||||
ret = sscanf(buf, "%x", &e_vid);
|
||||
if (ret != 1)
|
||||
error("Failed to parse %s", path);
|
||||
|
||||
snprintf(path, sizeof path,
|
||||
"/sys/bus/usb/devices/%s/idProduct", e->d_name);
|
||||
if (!read_file(path, buf, sizeof buf))
|
||||
continue;
|
||||
|
||||
ret = sscanf(buf, "%x", &e_pid);
|
||||
if (ret != 1)
|
||||
error("Failed to parse %s", path);
|
||||
|
||||
if (e_vid == vid && e_pid == pid) {
|
||||
snprintf(path, sizeof path,
|
||||
"/sys/bus/usb/devices/%s/busnum", e->d_name);
|
||||
if (!read_file(path, buf, sizeof buf))
|
||||
error("Failed to read %s", path);
|
||||
|
||||
ret = sscanf(buf, "%u", &bus);
|
||||
if (ret != 1)
|
||||
error("Failed to parse %s", path);
|
||||
|
||||
snprintf(path, sizeof path,
|
||||
"/sys/bus/usb/devices/%s/devnum", e->d_name);
|
||||
if (!read_file(path, buf, sizeof buf))
|
||||
error("Failed to read %s", path);
|
||||
|
||||
ret = sscanf(buf, "%u", &dev);
|
||||
if (ret != 1)
|
||||
error("Failed to parse %s", path);
|
||||
|
||||
snprintf(path, sizeof path,
|
||||
"/dev/bus/usb/%03u/%03u", bus, dev);
|
||||
|
||||
debug("Found %04x:%04x at %s\n", e_vid, e_pid, path);
|
||||
|
||||
fd = open(path, O_RDWR);
|
||||
syscall_error(fd < 0, "open(%s) failed", path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
errno = ENOENT;
|
||||
closedir(d);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int handle_urb(int usb_fd, struct usbdevfs_urb* urb, int timeout)
|
||||
{
|
||||
int ret;
|
||||
struct usbdevfs_urb* reaped_urb;
|
||||
int retries = 0;
|
||||
|
||||
retry:
|
||||
ret = ioctl(usb_fd, USBDEVFS_SUBMITURB, urb);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
struct pollfd fd = {
|
||||
.fd = usb_fd,
|
||||
.events = POLLOUT,
|
||||
};
|
||||
|
||||
ret = poll(&fd, 1, timeout);
|
||||
if (ret <= 0) {
|
||||
if (ret == 0)
|
||||
errno = ETIMEDOUT;
|
||||
|
||||
int save_errno = errno;
|
||||
|
||||
// on timeout or other poll error, we need to discard and reap the submitted URB
|
||||
ret = ioctl(usb_fd, USBDEVFS_DISCARDURB, urb);
|
||||
|
||||
// even if discard fails, URB may still be reapable, we need to try reaping anyway
|
||||
ret = ioctl(usb_fd, USBDEVFS_REAPURBNDELAY, &reaped_urb);
|
||||
|
||||
// reap must immediately succeed, otherwise this is fatal
|
||||
syscall_error(ret < 0, "USBDEVFS_REAPURBNDELAY failed");
|
||||
|
||||
errno = save_errno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// hopefully POLLERR means we get some error immediately on reap
|
||||
|
||||
ret = ioctl(usb_fd, USBDEVFS_REAPURB, &reaped_urb);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
// EPROTO errors are recoverable
|
||||
if (urb->status == -71 && retries < 3) {
|
||||
retries++;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (urb->status != 0) {
|
||||
errno = -urb->status;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
195
usb-flasher/debugger.c
Normal file
195
usb-flasher/debugger.c
Normal file
@ -0,0 +1,195 @@
|
||||
/**
|
||||
* USB debuggign tool for Pinephone keyboard
|
||||
*
|
||||
* Copyright (C) 2021 Ondřej Jirman <megi@xff.cz>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define DEBUG 1
|
||||
#include "common.c"
|
||||
|
||||
int kb_open(void)
|
||||
{
|
||||
int ret, fd;
|
||||
|
||||
// first check if keyboard USB device is available, if it is
|
||||
// we need to first switch to bootloader mode
|
||||
|
||||
fd = open_usb_dev(0x04f3, 0xb001);
|
||||
if (fd >= 0) {
|
||||
struct usbdevfs_disconnect_claim dc = {
|
||||
.interface = 0,
|
||||
};
|
||||
|
||||
ret = ioctl(fd, USBDEVFS_DISCONNECT_CLAIM, &dc);
|
||||
syscall_error(ret < 0, "USBDEVFS_DISCONNECT_CLAIM failed");
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int usb_fd = -1;
|
||||
|
||||
int command(uint8_t req[8])
|
||||
{
|
||||
int ret;
|
||||
struct usbdevfs_urb urb = {
|
||||
.type = USBDEVFS_URB_TYPE_INTERRUPT,
|
||||
.endpoint = 0x01,
|
||||
// .flags = USBDEVFS_URB_ZERO_PACKET,
|
||||
.buffer = req,
|
||||
.buffer_length = 8,
|
||||
.actual_length = 8,
|
||||
};
|
||||
|
||||
ret = handle_urb(usb_fd, &urb, 100);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
debug("CMD:");
|
||||
for (int i = 0; i < urb.actual_length; i++)
|
||||
debug(" %02hhx", req[i]);
|
||||
debug("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int response(uint8_t res[8])
|
||||
{
|
||||
int ret;
|
||||
struct usbdevfs_urb urb = {
|
||||
.type = USBDEVFS_URB_TYPE_INTERRUPT,
|
||||
.endpoint = 0x82,
|
||||
.flags = USBDEVFS_URB_SHORT_NOT_OK,
|
||||
.buffer = res,
|
||||
.buffer_length = 8,
|
||||
.actual_length = 0,
|
||||
};
|
||||
|
||||
ret = handle_urb(usb_fd, &urb, 100);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (urb.actual_length != 8)
|
||||
error("Status response size invalid, must be 8 bytes, got %d", urb.actual_length);
|
||||
|
||||
debug("RES:");
|
||||
for (int i = 0; i < urb.actual_length; i++)
|
||||
debug(" %02hhx", res[i]);
|
||||
debug("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t xwrite(int fd, uint8_t* buf, size_t len)
|
||||
{
|
||||
size_t off = 0;
|
||||
|
||||
while (off < len) {
|
||||
ssize_t ret = write(fd, buf + off, len - off);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
off += ret;
|
||||
}
|
||||
|
||||
return off;
|
||||
}
|
||||
|
||||
int read_stdout(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
uint8_t buf[64];
|
||||
struct usbdevfs_urb urb = {
|
||||
.type = USBDEVFS_URB_TYPE_INTERRUPT,
|
||||
.endpoint = 0x83,
|
||||
.buffer = buf,
|
||||
.buffer_length = 64,
|
||||
.actual_length = 0,
|
||||
};
|
||||
|
||||
ret = handle_urb(usb_fd, &urb, 10);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (urb.actual_length > 0) {
|
||||
ssize_t rv = xwrite(1, buf, urb.actual_length);
|
||||
if (rv < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int read_keys(uint8_t buf[12])
|
||||
{
|
||||
int ret;
|
||||
struct usbdevfs_urb urb = {
|
||||
.type = USBDEVFS_URB_TYPE_INTERRUPT,
|
||||
.endpoint = 0x84,
|
||||
.flags = USBDEVFS_URB_SHORT_NOT_OK,
|
||||
.buffer = buf,
|
||||
.buffer_length = 12,
|
||||
.actual_length = 0,
|
||||
};
|
||||
|
||||
ret = handle_urb(usb_fd, &urb, 10);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void print_bitmap(uint8_t* map)
|
||||
{
|
||||
// printf("\033[H");
|
||||
for (int r = 0; r < 6; r++) {
|
||||
if (r == 0) {
|
||||
printf(" C");
|
||||
for (int c = 0; c < 12; c++)
|
||||
printf("%-3d", c + 1);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
printf("R%d", r + 1);
|
||||
for (int c = 0; c < 12; c++)
|
||||
printf(" %s", map[c] & (1u << r) ? "X" : ".");
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(int ac, char* av[])
|
||||
{
|
||||
int ret;
|
||||
uint8_t keys[12];
|
||||
|
||||
usb_fd = kb_open();
|
||||
if (usb_fd < 0)
|
||||
error("Failed to open the keyboard");
|
||||
|
||||
int i = 0;
|
||||
while (1) {
|
||||
ret = read_stdout();
|
||||
|
||||
ret = read_keys(keys);
|
||||
if (ret == 0)
|
||||
print_bitmap(keys);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -17,164 +17,12 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// {{{ includes
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <linux/usbdevice_fs.h>
|
||||
|
||||
#define DEBUG 1
|
||||
|
||||
#if DEBUG
|
||||
#define debug(args...) printf(args)
|
||||
#else
|
||||
#define debug(args...)
|
||||
#endif
|
||||
#define VERSION "1.0"
|
||||
#include "common.c"
|
||||
|
||||
// }}}
|
||||
// {{{ utils
|
||||
|
||||
static void syscall_error(int is_err, const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (!is_err)
|
||||
return;
|
||||
|
||||
fprintf(stderr, "ERROR: ");
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
fprintf(stderr, ": %s\n", strerror(errno));
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void error(const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
fprintf(stderr, "ERROR: ");
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
bool read_file(const char* path, char* buf, size_t size)
|
||||
{
|
||||
int fd;
|
||||
ssize_t ret;
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
|
||||
ret = read(fd, buf, size);
|
||||
close(fd);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
if (ret < size) {
|
||||
buf[ret] = 0;
|
||||
return true;
|
||||
} else {
|
||||
buf[size - 1] = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int open_usb_dev(uint16_t vid, uint16_t pid)
|
||||
{
|
||||
char path[256], buf[256];
|
||||
struct dirent *e;
|
||||
unsigned e_vid, e_pid, bus, dev;
|
||||
int fd = -1, ret;
|
||||
DIR* d;
|
||||
|
||||
d = opendir("/sys/bus/usb/devices");
|
||||
syscall_error(d == NULL, "opendir(/sys/bus/usb/devices) failed");
|
||||
|
||||
while (true) {
|
||||
errno = 0;
|
||||
e = readdir(d);
|
||||
syscall_error(e == NULL && errno, "readdir(/sys/bus/usb/devices) failed");
|
||||
if (!e)
|
||||
break;
|
||||
|
||||
if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, ".."))
|
||||
continue;
|
||||
|
||||
snprintf(path, sizeof path,
|
||||
"/sys/bus/usb/devices/%s/idVendor", e->d_name);
|
||||
if (!read_file(path, buf, sizeof buf))
|
||||
continue;
|
||||
|
||||
ret = sscanf(buf, "%x", &e_vid);
|
||||
if (ret != 1)
|
||||
error("Failed to parse %s", path);
|
||||
|
||||
snprintf(path, sizeof path,
|
||||
"/sys/bus/usb/devices/%s/idProduct", e->d_name);
|
||||
if (!read_file(path, buf, sizeof buf))
|
||||
continue;
|
||||
|
||||
ret = sscanf(buf, "%x", &e_pid);
|
||||
if (ret != 1)
|
||||
error("Failed to parse %s", path);
|
||||
|
||||
if (e_vid == vid && e_pid == pid) {
|
||||
snprintf(path, sizeof path,
|
||||
"/sys/bus/usb/devices/%s/busnum", e->d_name);
|
||||
if (!read_file(path, buf, sizeof buf))
|
||||
error("Failed to read %s", path);
|
||||
|
||||
ret = sscanf(buf, "%u", &bus);
|
||||
if (ret != 1)
|
||||
error("Failed to parse %s", path);
|
||||
|
||||
snprintf(path, sizeof path,
|
||||
"/sys/bus/usb/devices/%s/devnum", e->d_name);
|
||||
if (!read_file(path, buf, sizeof buf))
|
||||
error("Failed to read %s", path);
|
||||
|
||||
ret = sscanf(buf, "%u", &dev);
|
||||
if (ret != 1)
|
||||
error("Failed to parse %s", path);
|
||||
|
||||
snprintf(path, sizeof path,
|
||||
"/dev/bus/usb/%03u/%03u", bus, dev);
|
||||
|
||||
debug("Found %04x:%04x at %s\n", e_vid, e_pid, path);
|
||||
|
||||
fd = open(path, O_RDWR);
|
||||
syscall_error(fd < 0, "open(%s) failed", path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
errno = ENOENT;
|
||||
closedir(d);
|
||||
return fd;
|
||||
}
|
||||
|
||||
int bootloader_open(void)
|
||||
{
|
||||
int ret, fd;
|
||||
@ -185,6 +33,8 @@ int bootloader_open(void)
|
||||
|
||||
fd = open_usb_dev(0x04f3, 0x1812);
|
||||
if (fd >= 0) {
|
||||
printf("Found USB device for the vendor firmware, swithing to USB bootloader\n");
|
||||
|
||||
for (unsigned i = 0; i <= 1; i++) {
|
||||
struct usbdevfs_disconnect_claim dc = {
|
||||
.interface = i,
|
||||
@ -223,14 +73,46 @@ int bootloader_open(void)
|
||||
|
||||
had_switch = true;
|
||||
close(fd);
|
||||
goto wait_bl;
|
||||
}
|
||||
|
||||
// open the bootloader USB device (wait for it if we just switched to bootloader mode)
|
||||
fd = open_usb_dev(0x04f3, 0xb001);
|
||||
if (fd >= 0) {
|
||||
printf("Found debugger USB device, swithing to USB bootloader\n");
|
||||
|
||||
struct usbdevfs_disconnect_claim dc = {
|
||||
.interface = 0,
|
||||
};
|
||||
|
||||
ret = ioctl(fd, USBDEVFS_DISCONNECT_CLAIM, &dc);
|
||||
syscall_error(ret < 0, "USBDEVFS_DISCONNECT_CLAIM failed");
|
||||
|
||||
uint8_t buf[8] = { 0x01, };
|
||||
|
||||
struct usbdevfs_urb urb = {
|
||||
.type = USBDEVFS_URB_TYPE_INTERRUPT,
|
||||
.endpoint = 0x01,
|
||||
.buffer = buf,
|
||||
.buffer_length = 8,
|
||||
.actual_length = 0,
|
||||
};
|
||||
|
||||
ret = handle_urb(fd, &urb, 500);
|
||||
syscall_error(ret < 0, "handle_urb failed");
|
||||
|
||||
had_switch = true;
|
||||
close(fd);
|
||||
goto wait_bl;
|
||||
}
|
||||
|
||||
wait_bl:
|
||||
// open the bootloader USB device (wait for it if we just switched to bootloader mode)
|
||||
for (int i = 0;; i++) {
|
||||
fd = open_usb_dev(0x04f3, 0x0905);
|
||||
if (fd >= 0)
|
||||
if (fd >= 0) {
|
||||
printf("Found USB bootloader device\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!had_switch)
|
||||
error("Bootloader USB device not found");
|
||||
@ -261,12 +143,11 @@ enum {
|
||||
EP_DATAIN = 0x84,
|
||||
};
|
||||
|
||||
static int bootloader_fd = -1;
|
||||
static int usb_fd = -1;
|
||||
|
||||
void bootloader_command(uint8_t req[8])
|
||||
{
|
||||
int ret;
|
||||
struct usbdevfs_urb* reaped_urb;
|
||||
struct usbdevfs_urb urb = {
|
||||
.type = USBDEVFS_URB_TYPE_INTERRUPT,
|
||||
.endpoint = EP_CMD,
|
||||
@ -276,14 +157,8 @@ void bootloader_command(uint8_t req[8])
|
||||
.actual_length = 8,
|
||||
};
|
||||
|
||||
ret = ioctl(bootloader_fd, USBDEVFS_SUBMITURB, &urb);
|
||||
syscall_error(ret < 0, "USBDEVFS_SUBMITURB failed");
|
||||
|
||||
ret = ioctl(bootloader_fd, USBDEVFS_REAPURB, &reaped_urb);
|
||||
syscall_error(ret < 0, "USBDEVFS_REAPURB failed");
|
||||
|
||||
if (urb.status != 0)
|
||||
error("URB failed with status=%s (%d)", strerror(urb.status), urb.status);
|
||||
ret = handle_urb(usb_fd, &urb, 1000);
|
||||
syscall_error(ret < 0, "handle_urb failed");
|
||||
|
||||
debug("CMD:");
|
||||
for (int i = 0; i < urb.actual_length; i++)
|
||||
@ -294,7 +169,6 @@ void bootloader_command(uint8_t req[8])
|
||||
void bootloader_status(uint8_t res[4])
|
||||
{
|
||||
int ret;
|
||||
struct usbdevfs_urb* reaped_urb;
|
||||
struct usbdevfs_urb urb = {
|
||||
.type = USBDEVFS_URB_TYPE_INTERRUPT,
|
||||
.endpoint = EP_STATUS,
|
||||
@ -304,17 +178,9 @@ void bootloader_status(uint8_t res[4])
|
||||
.actual_length = 0,
|
||||
};
|
||||
|
||||
ret = ioctl(bootloader_fd, USBDEVFS_SUBMITURB, &urb);
|
||||
syscall_error(ret < 0, "USBDEVFS_SUBMITURB failed");
|
||||
|
||||
ret = ioctl(bootloader_fd, USBDEVFS_REAPURB, &reaped_urb);
|
||||
syscall_error(ret < 0, "USBDEVFS_REAPURB failed");
|
||||
ret = handle_urb(usb_fd, &urb, 1000);
|
||||
syscall_error(ret < 0, "handle_urb failed");
|
||||
|
||||
if (urb.status != 0)
|
||||
error("URB failed with status=%s (%d)", strerror(urb.status), urb.status);
|
||||
if (urb.actual_length != 4)
|
||||
error("Status response too short, must be 4 bytes, got %d", urb.actual_length);
|
||||
|
||||
debug("RES:");
|
||||
for (int i = 0; i < urb.actual_length; i++)
|
||||
debug(" %02hhx", res[i]);
|
||||
@ -373,7 +239,6 @@ void bootloader_standard_status_check(void)
|
||||
int bootloader_read_data(uint8_t res[64])
|
||||
{
|
||||
int ret;
|
||||
struct usbdevfs_urb* reaped_urb;
|
||||
struct usbdevfs_urb urb = {
|
||||
.type = USBDEVFS_URB_TYPE_INTERRUPT,
|
||||
.endpoint = EP_DATAIN,
|
||||
@ -384,29 +249,20 @@ int bootloader_read_data(uint8_t res[64])
|
||||
// .usercontext = (void*)(uintptr_t)0,
|
||||
};
|
||||
|
||||
ret = ioctl(bootloader_fd, USBDEVFS_SUBMITURB, &urb);
|
||||
syscall_error(ret < 0, "USBDEVFS_SUBMITURB failed");
|
||||
|
||||
ret = ioctl(bootloader_fd, USBDEVFS_REAPURB, &reaped_urb);
|
||||
syscall_error(ret < 0, "USBDEVFS_REAPURB failed");
|
||||
ret = handle_urb(usb_fd, &urb, 1000);
|
||||
syscall_error(ret < 0, "handle_urb failed");
|
||||
|
||||
debug("DATA:");
|
||||
for (int i = 0; i < urb.actual_length; i++)
|
||||
debug(" %02hhx", res[i]);
|
||||
debug("\n");
|
||||
|
||||
if (urb.status != 0)
|
||||
error("URB failed with status=%s (%d)", strerror(urb.status), urb.status);
|
||||
if (urb.actual_length != 64)
|
||||
error("Data response too short, must be 64 bytes, got %d", urb.actual_length);
|
||||
|
||||
return urb.actual_length;
|
||||
}
|
||||
|
||||
void bootloader_write_data(uint8_t res[64])
|
||||
{
|
||||
int ret;
|
||||
struct usbdevfs_urb* reaped_urb;
|
||||
struct usbdevfs_urb urb = {
|
||||
.type = USBDEVFS_URB_TYPE_INTERRUPT,
|
||||
.endpoint = EP_DATAOUT,
|
||||
@ -417,14 +273,8 @@ void bootloader_write_data(uint8_t res[64])
|
||||
// .usercontext = (void*)(uintptr_t)0,
|
||||
};
|
||||
|
||||
ret = ioctl(bootloader_fd, USBDEVFS_SUBMITURB, &urb);
|
||||
syscall_error(ret < 0, "USBDEVFS_SUBMITURB failed");
|
||||
|
||||
ret = ioctl(bootloader_fd, USBDEVFS_REAPURB, &reaped_urb);
|
||||
syscall_error(ret < 0, "USBDEVFS_REAPURB failed");
|
||||
|
||||
if (urb.status != 0)
|
||||
error("URB failed with status=%s (%d)", strerror(urb.status), urb.status);
|
||||
ret = handle_urb(usb_fd, &urb, 1000);
|
||||
syscall_error(ret < 0, "handle_urb failed");
|
||||
|
||||
debug("DATA:");
|
||||
for (int i = 0; i < urb.actual_length; i++)
|
||||
@ -705,82 +555,239 @@ const char* boot_cond_text(uint8_t status)
|
||||
}
|
||||
}
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf(
|
||||
"Usage: ppkb-flasher [--rom-in <path>] [--rom-out <path>] [--verbose]\n"
|
||||
" [--help] [<read|write|info|reset>...]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -i, --rom-in <path> Specify path to binary file you want to flash.\n"
|
||||
" -o, --rom-out <path> Specify path where you want to store the contents\n"
|
||||
" of code ROM read from the device.\n"
|
||||
" -s, --rom-size <size> Specify how many bytes of code rom to flash\n"
|
||||
" starting from offset 0x2000 in the rom file.\n"
|
||||
" -v, --verbose Show details of what's going on.\n"
|
||||
" -h, --help This help.\n"
|
||||
"\n"
|
||||
"Commands:\n"
|
||||
" info Display information about the firmware.\n"
|
||||
" read Read ROM from the device to --rom-out file.\n"
|
||||
" write Flash ROM file to the device from --rom-in.\n"
|
||||
" reset Perform software reset of the MCU.\n"
|
||||
"\n"
|
||||
"Format of the ROM files is a flat binary. Only the part of it starting\n"
|
||||
"from 0x2000 will be flashed. Use -s to specify how many bytes to write.\n"
|
||||
"\n"
|
||||
"Pinephone keyboard flashing tool " VERSION "\n"
|
||||
"Written by Ondrej Jirman <megi@xff.cz>, 2021\n"
|
||||
"Licensed under GPLv3, see https://xff.cz/git/pinephone-keyboard/ for\n"
|
||||
"more information.\n"
|
||||
);
|
||||
|
||||
exit(2);
|
||||
}
|
||||
|
||||
int main(int ac, char* av[])
|
||||
{
|
||||
bootloader_fd = bootloader_open();
|
||||
char* rom_in = NULL;
|
||||
char* rom_out = NULL;
|
||||
int size = 0x1000;
|
||||
int ret;
|
||||
|
||||
if (ac == 2 && !strcmp(av[1], "reset")) {
|
||||
cmd_software_reset();
|
||||
return 0;
|
||||
while (1) {
|
||||
int option_index = 0;
|
||||
struct option long_options[] = {
|
||||
{ "rom-in", required_argument, 0, 'i' },
|
||||
{ "rom-out", required_argument, 0, 'o' },
|
||||
{ "size" , required_argument, 0, 's' },
|
||||
{ "verbose", no_argument, 0, 'v' },
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
int c = getopt_long(ac, av, "i:o:s:vh", long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'o':
|
||||
rom_out = strdup(optarg);
|
||||
break;
|
||||
case 'i':
|
||||
rom_in = strdup(optarg);
|
||||
break;
|
||||
case 's':
|
||||
if (strstr("0x", optarg) == optarg) {
|
||||
errno = 0;
|
||||
char* next = NULL;
|
||||
size = strtol(optarg + 2, &next, 16);
|
||||
if (errno || next == optarg + 2) {
|
||||
printf("ERROR: Can't parse --size %s\n\n", optarg);
|
||||
usage();
|
||||
}
|
||||
} else {
|
||||
errno = 0;
|
||||
char* next = NULL;
|
||||
size = strtol(optarg, &next, 10);
|
||||
if (errno || next == optarg) {
|
||||
printf("ERROR: Can't parse --size %s\n\n", optarg);
|
||||
usage();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind == ac)
|
||||
usage();
|
||||
|
||||
if (size < 64) {
|
||||
printf("ERROR: --size 0x%04x too small\n\n", size);
|
||||
usage();
|
||||
}
|
||||
|
||||
if (size > 0x6000) {
|
||||
printf("ERROR: --size 0x%04x too large\n\n", size);
|
||||
usage();
|
||||
}
|
||||
|
||||
if (size % 64 != 0) {
|
||||
printf("ERROR: --size 0x%04x is not multiple of 64\n\n", size);
|
||||
usage();
|
||||
}
|
||||
|
||||
for (int i = optind; i < ac; i++) {
|
||||
if (!strcmp(av[i], "read")) {
|
||||
if (!rom_out) {
|
||||
printf("ERROR: You must specify target file to write rom contents to via --rom-out\n\n");
|
||||
usage();
|
||||
}
|
||||
} else if (!strcmp(av[i], "write")) {
|
||||
if (!rom_in) {
|
||||
printf("ERROR: You must source file for flashing via --rom-in\n\n");
|
||||
usage();
|
||||
}
|
||||
} else if (!strcmp(av[i], "info")) {
|
||||
;
|
||||
} else if (!strcmp(av[i], "reset")) {
|
||||
;
|
||||
} else {
|
||||
printf("ERROR: Unknown command: %s\n\n", av[i]);
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
printf("Searching for bootloader USB device\n");
|
||||
usb_fd = bootloader_open();
|
||||
|
||||
printf("Resetting bootloader state\n");
|
||||
cmd_abort();
|
||||
|
||||
printf("FW=0x%04hx BOOT=0x%04hx\n", cmd_get_ver_fw(), cmd_get_ver_spec());
|
||||
for (int i = optind; i < ac; i++) {
|
||||
if (!strcmp(av[i], "read")) {
|
||||
uint8_t rom[0x8000];
|
||||
memset(rom, 0xff, sizeof rom);
|
||||
|
||||
uint8_t opts[128];
|
||||
cmd_read_option(opts);
|
||||
printf("Reading code ROM\n");
|
||||
cmd_read_rom(rom, 0, 0x8000);
|
||||
|
||||
uint16_t icid = (opts[124] | opts[125] << 8) ^ (opts[121] | opts[122] << 8);
|
||||
printf("ICID=%04hx\n", icid);
|
||||
int fd = open(rom_out, O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
||||
if (fd >= 0) {
|
||||
ssize_t wr = write(fd, rom, 0x8000);
|
||||
syscall_error(wr < 0, "write failed");
|
||||
close(fd);
|
||||
}
|
||||
} else if (!strcmp(av[i], "write")) {
|
||||
int fd;
|
||||
|
||||
cmd_unlock();
|
||||
uint8_t rom[0x8000];
|
||||
memset(rom, 0xff, sizeof rom);
|
||||
|
||||
uint8_t bootcond = cmd_boot_condition();
|
||||
printf("Booted via %s\n", boot_cond_text(bootcond));
|
||||
fd = open(rom_in, O_RDONLY);
|
||||
syscall_error(fd < 0, "open(%s) failed", rom_in);
|
||||
ssize_t len = read(fd, rom, 0x8000);
|
||||
syscall_error(len < 0, "read failed");
|
||||
close(fd);
|
||||
if (len != 0x8000)
|
||||
error("Invalid ROM file (%s) size (%d), must be 32768 bytes", rom_in, (int)len);
|
||||
|
||||
uint16_t csum_boot = cmd_get_checksum(CHECKSUM_TYPE_BOOT);
|
||||
uint16_t csum_app = cmd_get_checksum(CHECKSUM_TYPE_MAIN);
|
||||
printf("Checksums: boot=%04hx app=%04hx\n", csum_boot, csum_app);
|
||||
printf("Unlocking\n");
|
||||
cmd_unlock();
|
||||
|
||||
/*
|
||||
* Checksums: boot=d355 app=449b
|
||||
*
|
||||
* Option ROM from factory:
|
||||
*
|
||||
* CODE0 at 116: 0xff
|
||||
* - 24MHz intosc mode, WDT disabled, 256kHz low freq mode
|
||||
* CODE3 at 119: 0xff
|
||||
* - eeprom and hw reset disabled
|
||||
* - BITS(2..0) = reset button config
|
||||
* - BIT(3) = eeprom enable
|
||||
*
|
||||
* 0: fc 39 01 7f
|
||||
* 4: fe ff ff ff
|
||||
*
|
||||
* ...: ff ff ff ff
|
||||
*
|
||||
* 120: df // read by bootloader & 0x6 = 0x6: 24MHz,
|
||||
* 121: 0a 4f // part of ICID (ICID is this value XORed with CSUM at 124, wtf?)
|
||||
* 123: 20 // ???
|
||||
* 124: 9b 44 // checksum (written by CMD_WRITECHECKSUM)
|
||||
* 126: 49 // ???
|
||||
* 127: aa // app OK flag (0xaa = ok)
|
||||
*/
|
||||
printf("Entering flasing mode\n");
|
||||
cmd_entry_iap();
|
||||
|
||||
int fd;
|
||||
uint8_t rom[0x8000];
|
||||
memset(rom, 0xff, sizeof rom);
|
||||
printf("Flashing code ROM\n");
|
||||
cmd_write_rom(rom + 0x2000, 0x2000, size);
|
||||
|
||||
fd = open("fw.bin", O_RDONLY);
|
||||
syscall_error(fd < 0, "open(fw.bin) failed");
|
||||
ssize_t len = read(fd, rom, 0x8000);
|
||||
close(fd);
|
||||
if (len != 0x8000)
|
||||
error("Invalid rom size, must be 32768 bytes");
|
||||
printf("Finishing flashing\n");
|
||||
cmd_finished_iap();
|
||||
} else if (!strcmp(av[i], "info")) {
|
||||
printf("Bootlaoder version: 0x%04hx\n", cmd_get_ver_spec());
|
||||
printf("Firmware version: 0x%04hx\n", cmd_get_ver_fw());
|
||||
|
||||
cmd_entry_iap();
|
||||
cmd_write_rom(rom + 0x2000, 0x2000, 0x600);
|
||||
cmd_finished_iap();
|
||||
uint8_t opts[128];
|
||||
cmd_read_option(opts);
|
||||
|
||||
cmd_read_rom(rom, 0, 0x8000);
|
||||
fd = open("rom.bin", O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
||||
if (fd >= 0) {
|
||||
write(fd, rom, 0x8000);
|
||||
close(fd);
|
||||
uint16_t icid = (opts[124] | opts[125] << 8) ^ (opts[121] | opts[122] << 8);
|
||||
printf("ICID: 0x%04hx\n", icid);
|
||||
|
||||
uint8_t bootcond = cmd_boot_condition();
|
||||
printf("Booted via: %s\n", boot_cond_text(bootcond));
|
||||
|
||||
uint16_t csum_boot = cmd_get_checksum(CHECKSUM_TYPE_BOOT);
|
||||
uint16_t csum_app = cmd_get_checksum(CHECKSUM_TYPE_MAIN);
|
||||
printf("Checksums: boot=0x%04hx app=0x%04hx\n", csum_boot, csum_app);
|
||||
|
||||
/*
|
||||
* Checksums: boot=d355 app=449b
|
||||
*
|
||||
* Option ROM from factory:
|
||||
*
|
||||
* CODE0 at 116: 0xff
|
||||
* - 24MHz intosc mode, WDT disabled, 256kHz low freq mode
|
||||
* CODE2:
|
||||
* - BIT(0)
|
||||
* CODE3 at 119: 0xff
|
||||
* - eeprom and hw reset disabled
|
||||
* - BITS(2..0) = reset button config
|
||||
* - BIT(3) = eeprom enable
|
||||
*
|
||||
* 0: fc 39 01 7f
|
||||
* 4: fe ff ff ff
|
||||
*
|
||||
* ...: ff ff ff ff
|
||||
*
|
||||
* 120: df // read by bootloader & 0x6 = 0x6: 24MHz,
|
||||
* 121: 0a // part of ICID (ICID is this value XORed with CSUM at 124, wtf?)
|
||||
* 122: 4f // part of ICID (ICID is this value XORed with CSUM at 125, wtf?)
|
||||
* 123: 20 // ??? some count?
|
||||
* 124: 9b 44 // checksum (written by CMD_WRITECHECKSUM)
|
||||
* 126: 49 // ???
|
||||
* 127: aa // app OK flag (0xaa = ok)
|
||||
*/
|
||||
|
||||
printf("Option ROM dump:\n");
|
||||
for (int i = 0; i < 128; i++)
|
||||
if (opts[i] != 0xff)
|
||||
printf("0x%02x (%d): 0x%02hhx\n", i + 128, i, opts[i]);
|
||||
} else if (!strcmp(av[i], "reset")) {
|
||||
printf("Restarting the MCU\n");
|
||||
cmd_software_reset();
|
||||
} else {
|
||||
printf("ERROR: Unknown command: %s\n\n", av[i]);
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
cmd_software_reset();
|
||||
|
||||
return 0;
|
||||
}
|
@ -1 +0,0 @@
|
||||
../firmware/build/fw.bin
|
Loading…
Reference in New Issue
Block a user