aboutsummaryrefslogtreecommitdiffstats
path: root/halfkay.c
diff options
context:
space:
mode:
authorTomasz Kramkowski <tk@the-tk.com>2016-11-23 21:23:09 +0000
committerTomasz Kramkowski <tk@the-tk.com>2016-11-23 21:23:30 +0000
commit464847c5b25588448e550017a6d91447c076b944 (patch)
treecb48ce0793232ab0046ac1ad18ad50862ed77f5c /halfkay.c
downloadhktool-464847c5b25588448e550017a6d91447c076b944.tar.gz
hktool-464847c5b25588448e550017a6d91447c076b944.tar.xz
hktool-464847c5b25588448e550017a6d91447c076b944.zip
init commit
Diffstat (limited to 'halfkay.c')
-rw-r--r--halfkay.c164
1 files changed, 164 insertions, 0 deletions
diff --git a/halfkay.c b/halfkay.c
new file mode 100644
index 0000000..c2a92a9
--- /dev/null
+++ b/halfkay.c
@@ -0,0 +1,164 @@
+/* HalfKay implementation
+ * Based on Teensy Loader (cli) (http://www.pjrc.com/teensy/loader_cli.html)
+ *
+ * Copyright (C) 2016 Tomasz Kramkowski <tk@the-tk.com>
+ *
+ * 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 _DEFAULT_SOURCE
+#include <assert.h>
+#include <errno.h>
+#include <libusb-1.0/libusb.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "halfkay.h"
+#include "log.h"
+
+enum {
+ HK_USB_VID = 0x16c0,
+ HK_USB_PID = 0x0478,
+ HK_USB_REQTYP = (LIBUSB_ENDPOINT_OUT \
+ | LIBUSB_REQUEST_TYPE_CLASS \
+ | LIBUSB_RECIPIENT_INTERFACE),
+ HK_USB_REQ = LIBUSB_REQUEST_SET_CONFIGURATION,
+ HK_USB_VAL = 0x200,
+};
+
+libusb_device_handle *hk_usb_hnd;
+
+static inline void libusb_err(const char *action, int code)
+{
+ error(0, "libusb %s failed: %s", action, libusb_strerror(code));
+}
+
+static void usbopen(void)
+{
+ int rc;
+
+ rc = libusb_init(NULL);
+ if (libusb_init(NULL) != LIBUSB_SUCCESS)
+ libusb_err("init", rc);
+
+ hk_usb_hnd = libusb_open_device_with_vid_pid(NULL, HK_USB_VID, HK_USB_PID);
+ if (!hk_usb_hnd)
+ error(0, "HalfKay device not found");
+
+ libusb_set_auto_detach_kernel_driver(hk_usb_hnd, 1);
+ rc = libusb_claim_interface(hk_usb_hnd, 0);
+ if (rc < 0)
+ libusb_err("claim", rc);
+}
+
+static void usbclose(void)
+{
+ libusb_release_interface(hk_usb_hnd, 0);
+ libusb_close(hk_usb_hnd);
+ libusb_exit(NULL);
+}
+
+static void usbsendcmd(void *data, size_t size, bool firstxfer)
+{
+ unsigned int timeout = firstxfer ? 3000 : 2000;
+ int rc;
+
+ rc = libusb_control_transfer(hk_usb_hnd, HK_USB_REQTYP, HK_USB_REQ,
+ HK_USB_VAL, 0, data, size, timeout);
+
+ if (rc < 0)
+ libusb_err("transfer", rc);
+
+ /* TODO: Replace this with the correct way to handle a halt... ffs idiot */
+ usleep(10000); /* HalfKay says no if you write too fast */
+}
+
+static void fmtcmd(void *_dest, const struct flashparams *fp, size_t addr)
+{
+ unsigned char *dest = _dest;
+
+ addr >>= fp->addrshft;
+ for (size_t i = 0; i < fp->cmdsz; i++)
+ dest[i] = (addr >> (i * CHAR_BIT)) & 0xFF;
+}
+
+int flash(const struct flashparams *fp, const char *file)
+{
+ FILE *f;
+ unsigned char *cmd;
+ size_t tsize;
+
+ assert(file);
+
+ /* TODO: move checks to parser */
+ /*if (!fp->blksz || !(fp->blksz & fp->blksz - 1))*/
+ /*error(0, "Block size is not a power of 2");*/
+ /*if (fp->memsz % fp->blksz)*/
+ /*error(0, "Memory size is not divisible by block size");*/
+
+ tsize = fp->blksz + fp->cmdsz;
+
+ f = fopen(file, "rb");
+ if (!f)
+ error(errno, EMSG_OPEN, file);
+
+ /* TODO: precalc and check ovf */
+ cmd = malloc(tsize);
+ if (!cmd)
+ error(errno, EMSG_ALLOC);
+
+ usbopen();
+
+ for (size_t blkaddr = 0; blkaddr < fp->memsz; blkaddr += fp->blksz) {
+ size_t count;
+
+ fmtcmd(cmd, fp, blkaddr);
+ count = fread(cmd + fp->cmdsz, 1, fp->blksz, f);
+ if (count == 0)
+ break;
+ if (count < fp->blksz) {
+ memset(cmd + fp->cmdsz + count, 0, fp->blksz - count);
+ }
+
+ usbsendcmd(cmd, tsize, blkaddr == 0);
+ }
+
+ if (!feof(f))
+ fprintf(stderr, "Device ran out of space during writing!");
+
+ usbclose();
+
+ free(cmd);
+ fclose(f);
+
+ return 0;
+}
+
+int reboot(const struct flashparams *fp)
+{
+ size_t tsize = fp->blksz + fp->cmdsz;
+ unsigned char *cmd = malloc(tsize);
+
+ usbopen();
+
+ fmtcmd(cmd, fp, SIZE_MAX);
+ usbsendcmd(cmd, tsize, true);
+
+ usbclose();
+
+ return 0;
+}