mirror of
https://github.com/tillitis/tillitis-key1.git
synced 2025-11-29 11:46:57 -05:00
fw: Import tkey-libs fw-2
This is an import of the fw-2 tag of tkey-libs. We import the entire tkey-libs repo minus dot files into the tillitis-key1 repo to make it very simple not to make mistakes regarding which firmware tag depends on which tkey-libs tag, especially considering locking down with NVCM. Please see README for information about developing with another tkey-libs or how to import future tkey-libs. Since tkey-libs is now a part of the repo we also add tkey-libs to the clean_fw target.
This commit is contained in:
parent
3dbc31f54c
commit
16a9e8c367
38 changed files with 5973 additions and 0 deletions
35
hw/application_fpga/tkey-libs/libcommon/assert.c
Normal file
35
hw/application_fpga/tkey-libs/libcommon/assert.c
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// SPDX-FileCopyrightText: 2022 Tillitis AB <tillitis.se>
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
#include <tkey/assert.h>
|
||||
#include <tkey/io.h>
|
||||
#include <tkey/lib.h>
|
||||
|
||||
void assert_fail(enum ioend dest, const char *assertion, const char *file,
|
||||
unsigned int line, const char *function)
|
||||
{
|
||||
puts(dest, "assert: ");
|
||||
puts(dest, assertion);
|
||||
puts(dest, " ");
|
||||
puts(dest, file);
|
||||
puts(dest, ":");
|
||||
putinthex(dest, line);
|
||||
puts(dest, " ");
|
||||
puts(dest, function);
|
||||
puts(dest, "\n");
|
||||
|
||||
// Force illegal instruction to halt CPU
|
||||
asm volatile("unimp");
|
||||
|
||||
// Not reached
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
void assert_halt(void)
|
||||
{
|
||||
// Force illegal instruction to halt CPU
|
||||
asm volatile("unimp");
|
||||
|
||||
// Not reached
|
||||
__builtin_unreachable();
|
||||
}
|
||||
14
hw/application_fpga/tkey-libs/libcommon/blake2s.c
Normal file
14
hw/application_fpga/tkey-libs/libcommon/blake2s.c
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
// SPDX-FileCopyrightText: 2023 Tillitis AB <tillitis.se>
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
#include <stdint.h>
|
||||
#include <tkey/blake2s.h>
|
||||
#include <tkey/tk1_mem.h>
|
||||
|
||||
int blake2s(void *out, unsigned long outlen, const void *key,
|
||||
unsigned long keylen, const void *in, unsigned long inlen,
|
||||
blake2s_ctx *ctx)
|
||||
{
|
||||
// Not implemented.
|
||||
return -1;
|
||||
}
|
||||
350
hw/application_fpga/tkey-libs/libcommon/io.c
Normal file
350
hw/application_fpga/tkey-libs/libcommon/io.c
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
// SPDX-FileCopyrightText: 2025 Tillitis AB <tillitis.se>
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
#include <stdint.h>
|
||||
#include <tkey/assert.h>
|
||||
#include <tkey/debug.h>
|
||||
#include <tkey/lib.h>
|
||||
#include <tkey/proto.h>
|
||||
#include <tkey/tk1_mem.h>
|
||||
|
||||
// Maximum payload size sent over the USB Mode Protocol.
|
||||
//
|
||||
// USB Mode Protocol:
|
||||
// 1 byte mode
|
||||
// 1 byte length
|
||||
//
|
||||
// Our USB Mode Protocol packets has room for 255 bytes according to
|
||||
// the header but we send at most 64 bytes of payload + the 2 byte
|
||||
// header. The header is removed in the USB controller and the maximum
|
||||
// payload fits in a single USB frame on the other side.
|
||||
#define USBMODE_PACKET_SIZE 64
|
||||
|
||||
static void hex(uint8_t buf[2], const uint8_t c);
|
||||
static int discard(size_t nbytes);
|
||||
static uint8_t readbyte(void);
|
||||
static void writebyte(uint8_t b);
|
||||
|
||||
struct usb_mode {
|
||||
enum ioend endpoint; // Current USB endpoint with data
|
||||
uint8_t len; // Data available in from current USB mode.
|
||||
};
|
||||
|
||||
static struct usb_mode cur_endpoint = {
|
||||
IO_NONE,
|
||||
0,
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
static volatile uint32_t* const can_rx = (volatile uint32_t *)TK1_MMIO_UART_RX_STATUS;
|
||||
static volatile uint32_t* const rx = (volatile uint32_t *)TK1_MMIO_UART_RX_DATA;
|
||||
static volatile uint32_t* const can_tx = (volatile uint32_t *)TK1_MMIO_UART_TX_STATUS;
|
||||
static volatile uint32_t* const tx = (volatile uint32_t *)TK1_MMIO_UART_TX_DATA;
|
||||
static volatile uint8_t* const debugtx = (volatile uint8_t *)TK1_MMIO_QEMU_DEBUG;
|
||||
// clang-format on
|
||||
|
||||
// writebyte blockingly writes byte b to UART
|
||||
static void writebyte(uint8_t b)
|
||||
{
|
||||
for (;;) {
|
||||
if (*can_tx) {
|
||||
*tx = b;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write_with_header writes nbytes of buf to UART with a USB Mode
|
||||
// Protocol header telling the receiver about the mode and length.
|
||||
static void write_with_header(enum ioend dest, const uint8_t *buf,
|
||||
size_t nbytes)
|
||||
{
|
||||
// USB Mode Protocol header:
|
||||
// 1 byte mode
|
||||
// 1 byte length
|
||||
|
||||
writebyte(dest);
|
||||
writebyte(nbytes);
|
||||
|
||||
for (int i = 0; i < nbytes; i++) {
|
||||
writebyte(buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// write blockingly writes nbytes bytes of data from buf to dest which
|
||||
// is either:
|
||||
//
|
||||
// - IO_QEMU: QEMU debug port
|
||||
//
|
||||
// - IO_UART: Low-level UART access, no USB Mode Header added.
|
||||
//
|
||||
// - IO_CDC: Through the UART for the CDC endpoint, with header.
|
||||
//
|
||||
// - IO_HID: Through the UART for the HID endpoint, with header.
|
||||
//
|
||||
// - IO_TKEYCTRL: Through the UART for the debug HID endpoint, with
|
||||
// header.
|
||||
void write(enum ioend dest, const uint8_t *buf, size_t nbytes)
|
||||
{
|
||||
if (dest == IO_QEMU) {
|
||||
for (int i = 0; i < nbytes; i++) {
|
||||
*debugtx = buf[i];
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (dest == IO_UART) {
|
||||
for (int i = 0; i < nbytes; i++) {
|
||||
writebyte(buf[i]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
while (nbytes > 0) {
|
||||
// We split the data into chunks that will fit in the
|
||||
// USB Mode Protocol and fits neatly in the USB frames
|
||||
// on the other side of the USB controller.
|
||||
uint8_t len =
|
||||
nbytes < USBMODE_PACKET_SIZE ? nbytes : USBMODE_PACKET_SIZE;
|
||||
|
||||
write_with_header(dest, (const uint8_t *)buf, len);
|
||||
|
||||
buf += len;
|
||||
nbytes -= len;
|
||||
}
|
||||
}
|
||||
|
||||
// readbyte reads a byte from UART and returns it. Blocking.
|
||||
static uint8_t readbyte(void)
|
||||
{
|
||||
for (;;) {
|
||||
if (*can_rx) {
|
||||
return *rx;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// read reads into buf of size bufsize from UART, nbytes or less, from
|
||||
// the current USB endpoint. It doesn't block.
|
||||
//
|
||||
// Returns the number of bytes read. Empty data returns 0.
|
||||
int read(enum ioend src, uint8_t *buf, size_t bufsize, size_t nbytes)
|
||||
{
|
||||
if (buf == NULL || nbytes > bufsize) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (src == IO_NONE || src == IO_UART || src == IO_QEMU) {
|
||||
// Destination only endpoints
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (src != cur_endpoint.endpoint) {
|
||||
// No data for this source available right now.
|
||||
return 0;
|
||||
}
|
||||
|
||||
int n = 0;
|
||||
|
||||
for (n = 0; n < nbytes; n++) {
|
||||
buf[n] = readbyte();
|
||||
cur_endpoint.len--;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
// uart_read reads blockingly into buf o size bufsize from UART nbytes
|
||||
// bytes.
|
||||
//
|
||||
// Returns negative on error.
|
||||
int uart_read(uint8_t *buf, size_t bufsize, size_t nbytes)
|
||||
{
|
||||
if (nbytes > bufsize) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int n = 0; n < nbytes; n++) {
|
||||
buf[n] = readbyte();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// discard nbytes of what's available.
|
||||
//
|
||||
// Returns how many bytes were discarded.
|
||||
static int discard(size_t nbytes)
|
||||
{
|
||||
int n = 0;
|
||||
uint8_t len = nbytes < cur_endpoint.len ? nbytes : cur_endpoint.len;
|
||||
|
||||
for (n = 0; n < len; n++) {
|
||||
(void)readbyte();
|
||||
cur_endpoint.len--;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
// readselect blocks and returns when there is something readable from
|
||||
// some mode.
|
||||
//
|
||||
// Use like this:
|
||||
//
|
||||
// readselect(IO_CDC|IO_HID, &endpoint, &len)
|
||||
//
|
||||
// to wait for some data from either the CDC or the HID endpoint.
|
||||
//
|
||||
// NOTE WELL: You need to call readselect() first, before doing any
|
||||
// calls to read().
|
||||
//
|
||||
// Only endpoints available for read are:
|
||||
//
|
||||
// - IO_TKEYCTRL
|
||||
// - IO_CDC
|
||||
// - IO_HID
|
||||
//
|
||||
// If you need blocking low-level UART reads, use uart_read() instead.
|
||||
//
|
||||
// Sets endpoint of the first endpoint in the bitmask with data
|
||||
// available. Indicates how many bytes available in len.
|
||||
//
|
||||
// Returns non-zero on error.
|
||||
int readselect(int bitmask, enum ioend *endpoint, uint8_t *len)
|
||||
{
|
||||
if (bitmask & IO_UART || bitmask & IO_QEMU) {
|
||||
// Not possible to use readselect() on these
|
||||
// endpoints.
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
// Check what is in the current UART buffer.
|
||||
//
|
||||
// - If nothing known, block until something comes along.
|
||||
//
|
||||
// - If not in bitmask, discard the data available
|
||||
// from that endpoint.
|
||||
//
|
||||
// - If in the bitmask, return the first endpoint with
|
||||
// data available and indicate how much data in len.
|
||||
if (cur_endpoint.len == 0) {
|
||||
// Read USB Mode Protocol header:
|
||||
// 1 byte mode
|
||||
// 1 byte length
|
||||
cur_endpoint.endpoint = readbyte();
|
||||
cur_endpoint.len = readbyte();
|
||||
}
|
||||
|
||||
*len = cur_endpoint.len;
|
||||
|
||||
if (cur_endpoint.endpoint & bitmask) {
|
||||
*endpoint = cur_endpoint.endpoint;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Not the USB endpoint caller asked for. Discard the
|
||||
// rest from this endpoint.
|
||||
if (discard(*len) != *len) {
|
||||
// We couldn't discard what the USB Mode
|
||||
// Protocol itself reported was available!
|
||||
// Something's fishy. Halt.
|
||||
assert(1 == 2);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void putchar(enum ioend dest, const uint8_t ch)
|
||||
{
|
||||
write(dest, &ch, 1);
|
||||
}
|
||||
|
||||
static void hex(uint8_t buf[2], const uint8_t c)
|
||||
{
|
||||
unsigned int upper = (c >> 4) & 0xf;
|
||||
unsigned int lower = c & 0xf;
|
||||
|
||||
buf[0] = upper < 10 ? '0' + upper : 'a' - 10 + upper;
|
||||
buf[1] = lower < 10 ? '0' + lower : 'a' - 10 + lower;
|
||||
}
|
||||
|
||||
void puthex(enum ioend dest, const uint8_t c)
|
||||
{
|
||||
uint8_t hexbuf[2] = {0};
|
||||
|
||||
hex(hexbuf, c);
|
||||
write(dest, hexbuf, 2);
|
||||
}
|
||||
|
||||
// Size of of a maximum integer in hex text format
|
||||
#define INTBUFSIZE 10
|
||||
|
||||
void putinthex(enum ioend dest, const uint32_t n)
|
||||
{
|
||||
uint8_t buf[INTBUFSIZE] = {0};
|
||||
uint8_t hexbuf[2] = {0};
|
||||
uint8_t *intbuf = (uint8_t *)&n;
|
||||
int j = 0;
|
||||
|
||||
buf[j++] = '0';
|
||||
buf[j++] = 'x';
|
||||
|
||||
for (int i = 3; i > -1; i--) {
|
||||
hex(hexbuf, intbuf[i]);
|
||||
buf[j++] = hexbuf[0];
|
||||
buf[j++] = hexbuf[1];
|
||||
}
|
||||
|
||||
write(dest, buf, INTBUFSIZE);
|
||||
}
|
||||
|
||||
void puts(enum ioend dest, const char *s)
|
||||
{
|
||||
write(dest, (const uint8_t *)s, strlen(s));
|
||||
}
|
||||
|
||||
// Size of a hex row: Contains 16 bytes where each byte is printed as
|
||||
// 3 characters (hex + hex + space). Every row ends with newline or at
|
||||
// most CR+LF.
|
||||
#define FULLROW (16 * 3)
|
||||
#define ROWBUFSIZE (FULLROW + 2)
|
||||
|
||||
void hexdump(enum ioend dest, void *buf, int len)
|
||||
{
|
||||
uint8_t rowbuf[ROWBUFSIZE] = {0};
|
||||
uint8_t hexbuf[2] = {0};
|
||||
uint8_t *byte_buf = (uint8_t *)buf;
|
||||
|
||||
int rowpos = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
hex(hexbuf, byte_buf[i]);
|
||||
rowbuf[rowpos++] = hexbuf[0];
|
||||
rowbuf[rowpos++] = hexbuf[1];
|
||||
rowbuf[rowpos++] = ' ';
|
||||
|
||||
// If the row is full, print it now.
|
||||
if (rowpos == FULLROW) {
|
||||
if (dest == IO_CDC) {
|
||||
rowbuf[rowpos++] = '\r';
|
||||
}
|
||||
rowbuf[rowpos++] = '\n';
|
||||
write(dest, rowbuf, rowpos);
|
||||
rowpos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// If final row wasn't full, print it now.
|
||||
if (rowpos != 0) {
|
||||
if (dest == IO_CDC) {
|
||||
rowbuf[rowpos++] = '\r';
|
||||
}
|
||||
rowbuf[rowpos++] = '\n';
|
||||
write(dest, rowbuf, rowpos);
|
||||
}
|
||||
}
|
||||
31
hw/application_fpga/tkey-libs/libcommon/led.c
Normal file
31
hw/application_fpga/tkey-libs/libcommon/led.c
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// SPDX-FileCopyrightText: 2022 Tillitis AB <tillitis.se>
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
#include <stdint.h>
|
||||
#include <tkey/led.h>
|
||||
|
||||
// clang-format off
|
||||
static volatile uint32_t* const led = (volatile uint32_t *)TK1_MMIO_TK1_LED;
|
||||
// clang-format on
|
||||
|
||||
void led_set(uint32_t ledvalue)
|
||||
{
|
||||
*led = ledvalue;
|
||||
}
|
||||
|
||||
uint32_t led_get()
|
||||
{
|
||||
return *led;
|
||||
}
|
||||
|
||||
void led_flash_forever(uint32_t ledvalue)
|
||||
{
|
||||
int led_on = 0;
|
||||
|
||||
for (;;) {
|
||||
*led = led_on ? ledvalue : LED_BLACK;
|
||||
for (volatile int i = 0; i < 800000; i++) {
|
||||
}
|
||||
led_on = !led_on;
|
||||
}
|
||||
}
|
||||
101
hw/application_fpga/tkey-libs/libcommon/lib.c
Normal file
101
hw/application_fpga/tkey-libs/libcommon/lib.c
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
// SPDX-FileCopyrightText: 2022 Tillitis AB <tillitis.se>
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
#include <stdint.h>
|
||||
#include <tkey/assert.h>
|
||||
#include <tkey/lib.h>
|
||||
#include <tkey/tk1_mem.h>
|
||||
|
||||
void *memset(void *dest, int c, unsigned n)
|
||||
{
|
||||
uint8_t *s = dest;
|
||||
|
||||
for (; n; n--, s++)
|
||||
*s = c;
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
__attribute__((used)) void *memcpy(void *dest, const void *src, unsigned n)
|
||||
{
|
||||
uint8_t *src_byte = (uint8_t *)src;
|
||||
uint8_t *dest_byte = (uint8_t *)dest;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
dest_byte[i] = src_byte[i];
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
void memcpy_s(void *dest, size_t destsize, const void *src, size_t n)
|
||||
{
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
assert(destsize >= n);
|
||||
|
||||
uint8_t *src_byte = (uint8_t *)src;
|
||||
uint8_t *dest_byte = (uint8_t *)dest;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dest_byte[i] = src_byte[i];
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((used)) void *wordcpy(void *dest, const void *src, unsigned n)
|
||||
{
|
||||
uint32_t *src_word = (uint32_t *)src;
|
||||
uint32_t *dest_word = (uint32_t *)dest;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
dest_word[i] = src_word[i];
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
void wordcpy_s(void *dest, size_t destsize, const void *src, size_t n)
|
||||
{
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
assert(destsize >= n);
|
||||
|
||||
uint32_t *src_word = (uint32_t *)src;
|
||||
uint32_t *dest_word = (uint32_t *)dest;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dest_word[i] = src_word[i];
|
||||
}
|
||||
}
|
||||
|
||||
int memeq(void *dest, const void *src, size_t n)
|
||||
{
|
||||
uint8_t *src_byte = (uint8_t *)src;
|
||||
uint8_t *dest_byte = (uint8_t *)dest;
|
||||
int res = -1;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (dest_byte[i] != src_byte[i]) {
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void secure_wipe(void *v, size_t n)
|
||||
{
|
||||
volatile uint8_t *p = (volatile uint8_t *)v;
|
||||
while (n--)
|
||||
*p++ = 0;
|
||||
}
|
||||
|
||||
size_t strlen(const char *str)
|
||||
{
|
||||
const char *s;
|
||||
|
||||
for (s = str; *s; ++s)
|
||||
;
|
||||
|
||||
return (s - str);
|
||||
}
|
||||
51
hw/application_fpga/tkey-libs/libcommon/proto.c
Normal file
51
hw/application_fpga/tkey-libs/libcommon/proto.c
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// SPDX-FileCopyrightText: 2022 Tillitis AB <tillitis.se>
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <tkey/assert.h>
|
||||
#include <tkey/debug.h>
|
||||
#include <tkey/proto.h>
|
||||
#include <tkey/tk1_mem.h>
|
||||
|
||||
uint8_t genhdr(uint8_t id, uint8_t endpoint, uint8_t status, enum cmdlen len)
|
||||
{
|
||||
return (id << 5) | (endpoint << 3) | (status << 2) | len;
|
||||
}
|
||||
|
||||
int parseframe(uint8_t b, struct frame_header *hdr)
|
||||
{
|
||||
if ((b & 0x80) != 0) {
|
||||
// Bad version
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((b & 0x4) != 0) {
|
||||
// Must be 0
|
||||
return -1;
|
||||
}
|
||||
|
||||
hdr->id = (b & 0x60) >> 5;
|
||||
hdr->endpoint = (b & 0x18) >> 3;
|
||||
|
||||
// Length
|
||||
switch (b & 0x3) {
|
||||
case LEN_1:
|
||||
hdr->len = 1;
|
||||
break;
|
||||
case LEN_4:
|
||||
hdr->len = 4;
|
||||
break;
|
||||
case LEN_32:
|
||||
hdr->len = 32;
|
||||
break;
|
||||
case LEN_128:
|
||||
hdr->len = 128;
|
||||
break;
|
||||
default:
|
||||
// Unknown length
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
67
hw/application_fpga/tkey-libs/libcommon/touch.c
Normal file
67
hw/application_fpga/tkey-libs/libcommon/touch.c
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// SPDX-FileCopyrightText: 2023 Tillitis AB <tillitis.se>
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <tkey/debug.h>
|
||||
#include <tkey/led.h>
|
||||
#include <tkey/touch.h>
|
||||
|
||||
// CPU clock frequenzy in Hz
|
||||
#define CPUFREQ 18000000
|
||||
|
||||
// clang-format off
|
||||
static volatile uint32_t *timer = (volatile uint32_t *)TK1_MMIO_TIMER_TIMER;
|
||||
static volatile uint32_t *timer_prescaler = (volatile uint32_t *)TK1_MMIO_TIMER_PRESCALER;
|
||||
static volatile uint32_t *timer_status = (volatile uint32_t *)TK1_MMIO_TIMER_STATUS;
|
||||
static volatile uint32_t *timer_ctrl = (volatile uint32_t *)TK1_MMIO_TIMER_CTRL;
|
||||
static volatile uint32_t *touch = (volatile uint32_t *)TK1_MMIO_TOUCH_STATUS;
|
||||
// clang-format on
|
||||
|
||||
// Returns !0 if touch sensor has been touched
|
||||
#define touched() (*touch & (1 << TK1_MMIO_TOUCH_STATUS_EVENT_BIT))
|
||||
|
||||
bool touch_wait(int color, int timeout_s)
|
||||
{
|
||||
int ledon = 0;
|
||||
int orig_color = led_get();
|
||||
uint32_t time = 0;
|
||||
uint32_t lasttime = 0;
|
||||
|
||||
// Tick once every decisecond
|
||||
*timer_prescaler = CPUFREQ / 10;
|
||||
*timer = timeout_s * 10; // Seconds
|
||||
|
||||
// Start timer
|
||||
*timer_ctrl |= (1 << TK1_MMIO_TIMER_CTRL_START_BIT);
|
||||
|
||||
// Acknowledge any stray touch events before waiting for real
|
||||
// touch
|
||||
*touch = 0;
|
||||
|
||||
// Blink until either the touch sensor has been touched or the
|
||||
// timer hits 0.
|
||||
while (!touched() && *timer_status != 0) {
|
||||
time = *timer;
|
||||
if (time % 2 == 0 && time != lasttime) {
|
||||
lasttime = time;
|
||||
ledon = !ledon;
|
||||
led_set(ledon ? color : LED_BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore LED
|
||||
led_set(orig_color);
|
||||
|
||||
// Do we have a timeout?
|
||||
if (*timer_status == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Stop timer
|
||||
*timer_ctrl |= (1 << TK1_MMIO_TIMER_CTRL_STOP_BIT);
|
||||
|
||||
// Confirm touch event
|
||||
*touch = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue