Initial commit of base code

This commit is contained in:
2025-05-28 18:16:00 -04:00
commit 65cb6080de
22 changed files with 3852 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
/build/**/*
!/build/**/*.uf2
/external/**/*
+53
View File
@@ -0,0 +1,53 @@
cmake_minimum_required(VERSION 3.13)
set(PROJECT webkeyboard)
set(PICO_SDK_PATH /home/kenji/programming/pico/c/pico-sdk)
set(PICO_BOARD pico_w)
include (${PICO_SDK_PATH}/external/pico_sdk_import.cmake)
project(${PROJECT} C CXX ASM)
set(MAKE_FS_DATA_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/external/makefsdata)
set(HTML_DIR ${CMAKE_CURRENT_LIST_DIR}/html)
if (NOT EXISTS ${MAKE_FS_DATA_SCRIPT})
file(DOWNLOAD
https://raw.githubusercontent.com/lwip-tcpip/lwip/e799c266facc3c70190676eccad49d6c2db2caac/src/apps/http/makefsdata/makefsdata
${MAKE_FS_DATA_SCRIPT}
)
endif()
message("Running makefsdata script")
execute_process(COMMAND
perl ${MAKE_FS_DATA_SCRIPT}
WORKING_DIRECTORY ${HTML_DIR}
ECHO_OUTPUT_VARIABLE
ECHO_ERROR_VARIABLE
)
file(RENAME ${HTML_DIR}/fsdata.c ${CMAKE_CURRENT_LIST_DIR}/my_fsdata.c)
pico_sdk_init()
add_executable(${PROJECT})
target_sources(${PROJECT} PRIVATE
main.c
server.c
hid.c
usb_descriptors.c
parse_keys.c
dhcpserver.c
)
pico_enable_stdio_usb(${PROJECT} 1)
pico_enable_stdio_uart(${PROJECT} 1)
pico_add_extra_outputs(${PROJECT})
target_include_directories(${PROJECT} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(${PROJECT}
pico_cyw43_arch_lwip_threadsafe_background
pico_lwip_http
pico_stdlib
pico_multicore
tinyusb_device
)
Binary file not shown.
+300
View File
@@ -0,0 +1,300 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2018-2019 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
// For DHCP specs see:
// https://www.ietf.org/rfc/rfc2131.txt
// https://tools.ietf.org/html/rfc2132 -- DHCP Options and BOOTP Vendor Extensions
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "cyw43_config.h"
#include "dhcpserver.h"
#include "lwip/udp.h"
#define DHCPDISCOVER (1)
#define DHCPOFFER (2)
#define DHCPREQUEST (3)
#define DHCPDECLINE (4)
#define DHCPACK (5)
#define DHCPNACK (6)
#define DHCPRELEASE (7)
#define DHCPINFORM (8)
#define DHCP_OPT_PAD (0)
#define DHCP_OPT_SUBNET_MASK (1)
#define DHCP_OPT_ROUTER (3)
#define DHCP_OPT_DNS (6)
#define DHCP_OPT_HOST_NAME (12)
#define DHCP_OPT_REQUESTED_IP (50)
#define DHCP_OPT_IP_LEASE_TIME (51)
#define DHCP_OPT_MSG_TYPE (53)
#define DHCP_OPT_SERVER_ID (54)
#define DHCP_OPT_PARAM_REQUEST_LIST (55)
#define DHCP_OPT_MAX_MSG_SIZE (57)
#define DHCP_OPT_VENDOR_CLASS_ID (60)
#define DHCP_OPT_CLIENT_ID (61)
#define DHCP_OPT_END (255)
#define PORT_DHCP_SERVER (67)
#define PORT_DHCP_CLIENT (68)
#define DEFAULT_DNS MAKE_IP4(8, 8, 8, 8)
#define DEFAULT_LEASE_TIME_S (24 * 60 * 60) // in seconds
#define MAC_LEN (6)
#define MAKE_IP4(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
typedef struct {
uint8_t op; // message opcode
uint8_t htype; // hardware address type
uint8_t hlen; // hardware address length
uint8_t hops;
uint32_t xid; // transaction id, chosen by client
uint16_t secs; // client seconds elapsed
uint16_t flags;
uint8_t ciaddr[4]; // client IP address
uint8_t yiaddr[4]; // your IP address
uint8_t siaddr[4]; // next server IP address
uint8_t giaddr[4]; // relay agent IP address
uint8_t chaddr[16]; // client hardware address
uint8_t sname[64]; // server host name
uint8_t file[128]; // boot file name
uint8_t options[312]; // optional parameters, variable, starts with magic
} dhcp_msg_t;
static int dhcp_socket_new_dgram(struct udp_pcb **udp, void *cb_data, udp_recv_fn cb_udp_recv) {
// family is AF_INET
// type is SOCK_DGRAM
*udp = udp_new();
if (*udp == NULL) {
return -ENOMEM;
}
// Register callback
udp_recv(*udp, cb_udp_recv, (void *)cb_data);
return 0; // success
}
static void dhcp_socket_free(struct udp_pcb **udp) {
if (*udp != NULL) {
udp_remove(*udp);
*udp = NULL;
}
}
static int dhcp_socket_bind(struct udp_pcb **udp, uint32_t ip, uint16_t port) {
ip_addr_t addr;
IP4_ADDR(&addr, ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff);
// TODO convert lwIP errors to errno
return udp_bind(*udp, &addr, port);
}
static int dhcp_socket_sendto(struct udp_pcb **udp, const void *buf, size_t len, uint32_t ip, uint16_t port) {
if (len > 0xffff) {
len = 0xffff;
}
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
if (p == NULL) {
return -ENOMEM;
}
memcpy(p->payload, buf, len);
ip_addr_t dest;
IP4_ADDR(&dest, ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff);
err_t err = udp_sendto(*udp, p, &dest, port);
pbuf_free(p);
if (err != ERR_OK) {
return err;
}
return len;
}
static uint8_t *opt_find(uint8_t *opt, uint8_t cmd) {
for (int i = 0; i < 308 && opt[i] != DHCP_OPT_END;) {
if (opt[i] == cmd) {
return &opt[i];
}
i += 2 + opt[i + 1];
}
return NULL;
}
static void opt_write_n(uint8_t **opt, uint8_t cmd, size_t n, void *data) {
uint8_t *o = *opt;
*o++ = cmd;
*o++ = n;
memcpy(o, data, n);
*opt = o + n;
}
static void opt_write_u8(uint8_t **opt, uint8_t cmd, uint8_t val) {
uint8_t *o = *opt;
*o++ = cmd;
*o++ = 1;
*o++ = val;
*opt = o;
}
static void opt_write_u32(uint8_t **opt, uint8_t cmd, uint32_t val) {
uint8_t *o = *opt;
*o++ = cmd;
*o++ = 4;
*o++ = val >> 24;
*o++ = val >> 16;
*o++ = val >> 8;
*o++ = val;
*opt = o;
}
static void dhcp_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *src_addr, u16_t src_port) {
dhcp_server_t *d = arg;
(void)upcb;
(void)src_addr;
(void)src_port;
// This is around 548 bytes
dhcp_msg_t dhcp_msg;
#define DHCP_MIN_SIZE (240 + 3)
if (p->tot_len < DHCP_MIN_SIZE) {
goto ignore_request;
}
size_t len = pbuf_copy_partial(p, &dhcp_msg, sizeof(dhcp_msg), 0);
if (len < DHCP_MIN_SIZE) {
goto ignore_request;
}
dhcp_msg.op = DHCPOFFER;
memcpy(&dhcp_msg.yiaddr, &d->ip.addr, 4);
uint8_t *opt = (uint8_t *)&dhcp_msg.options;
opt += 4; // assume magic cookie: 99, 130, 83, 99
switch (opt[2]) {
case DHCPDISCOVER: {
int yi = DHCPS_MAX_IP;
for (int i = 0; i < DHCPS_MAX_IP; ++i) {
if (memcmp(d->lease[i].mac, dhcp_msg.chaddr, MAC_LEN) == 0) {
// MAC match, use this IP address
yi = i;
break;
}
if (yi == DHCPS_MAX_IP) {
// Look for a free IP address
if (memcmp(d->lease[i].mac, "\x00\x00\x00\x00\x00\x00", MAC_LEN) == 0) {
// IP available
yi = i;
}
uint32_t expiry = d->lease[i].expiry << 16 | 0xffff;
if ((int32_t)(expiry - cyw43_hal_ticks_ms()) < 0) {
// IP expired, reuse it
memset(d->lease[i].mac, 0, MAC_LEN);
yi = i;
}
}
}
if (yi == DHCPS_MAX_IP) {
// No more IP addresses left
goto ignore_request;
}
dhcp_msg.yiaddr[3] = DHCPS_BASE_IP + yi;
opt_write_u8(&opt, DHCP_OPT_MSG_TYPE, DHCPOFFER);
break;
}
case DHCPREQUEST: {
uint8_t *o = opt_find(opt, DHCP_OPT_REQUESTED_IP);
if (o == NULL) {
// Should be NACK
goto ignore_request;
}
if (memcmp(o + 2, &d->ip.addr, 3) != 0) {
// Should be NACK
goto ignore_request;
}
uint8_t yi = o[5] - DHCPS_BASE_IP;
if (yi >= DHCPS_MAX_IP) {
// Should be NACK
goto ignore_request;
}
if (memcmp(d->lease[yi].mac, dhcp_msg.chaddr, MAC_LEN) == 0) {
// MAC match, ok to use this IP address
} else if (memcmp(d->lease[yi].mac, "\x00\x00\x00\x00\x00\x00", MAC_LEN) == 0) {
// IP unused, ok to use this IP address
memcpy(d->lease[yi].mac, dhcp_msg.chaddr, MAC_LEN);
} else {
// IP already in use
// Should be NACK
goto ignore_request;
}
d->lease[yi].expiry = (cyw43_hal_ticks_ms() + DEFAULT_LEASE_TIME_S * 1000) >> 16;
dhcp_msg.yiaddr[3] = DHCPS_BASE_IP + yi;
opt_write_u8(&opt, DHCP_OPT_MSG_TYPE, DHCPACK);
// printf("DHCPS: client connected: MAC=%02x:%02x:%02x:%02x:%02x:%02x IP=%u.%u.%u.%u\n",
// dhcp_msg.chaddr[0], dhcp_msg.chaddr[1], dhcp_msg.chaddr[2], dhcp_msg.chaddr[3], dhcp_msg.chaddr[4], dhcp_msg.chaddr[5],
// dhcp_msg.yiaddr[0], dhcp_msg.yiaddr[1], dhcp_msg.yiaddr[2], dhcp_msg.yiaddr[3]);
break;
}
default:
goto ignore_request;
}
opt_write_n(&opt, DHCP_OPT_SERVER_ID, 4, &d->ip.addr);
opt_write_n(&opt, DHCP_OPT_SUBNET_MASK, 4, &d->nm.addr);
opt_write_n(&opt, DHCP_OPT_ROUTER, 4, &d->ip.addr); // aka gateway; can have mulitple addresses
opt_write_u32(&opt, DHCP_OPT_DNS, DEFAULT_DNS); // can have mulitple addresses
opt_write_u32(&opt, DHCP_OPT_IP_LEASE_TIME, DEFAULT_LEASE_TIME_S);
*opt++ = DHCP_OPT_END;
dhcp_socket_sendto(&d->udp, &dhcp_msg, opt - (uint8_t *)&dhcp_msg, 0xffffffff, PORT_DHCP_CLIENT);
ignore_request:
pbuf_free(p);
}
void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm) {
ip_addr_copy(d->ip, *ip);
ip_addr_copy(d->nm, *nm);
memset(d->lease, 0, sizeof(d->lease));
if (dhcp_socket_new_dgram(&d->udp, d, dhcp_server_process) != 0) {
return;
}
dhcp_socket_bind(&d->udp, 0, PORT_DHCP_SERVER);
}
void dhcp_server_deinit(dhcp_server_t *d) {
dhcp_socket_free(&d->udp);
}
+49
View File
@@ -0,0 +1,49 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2018-2019 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H
#define MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H
#include "lwip/ip_addr.h"
#define DHCPS_BASE_IP (16)
#define DHCPS_MAX_IP (8)
typedef struct _dhcp_server_lease_t {
uint8_t mac[6];
uint16_t expiry;
} dhcp_server_lease_t;
typedef struct _dhcp_server_t {
ip_addr_t ip;
ip_addr_t nm;
dhcp_server_lease_t lease[DHCPS_MAX_IP];
struct udp_pcb *udp;
} dhcp_server_t;
void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm);
void dhcp_server_deinit(dhcp_server_t *d);
#endif // MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H
+136
View File
@@ -0,0 +1,136 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
* sekigon-gonnoc
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "tusb.h"
#include "usb_descriptors.h"
#include "server.h"
#include "main.h"
/*------------- MAIN -------------*/
int run_hid_device(void) {
// init device stack on native usb (roothub port0)
tud_init(0);
while (true) {
tud_task(); // tinyusb device task
//tud_cdc_write_flush();
}
return 0;
}
// Invoked when received SET_REPORT control request or
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize)
{
if (instance == 0 && report_type==HID_REPORT_TYPE_OUTPUT && report_id==REPORT_ID_KEYBOARD && bufsize==1) {
//received keyboard LED status, so update
set_indicator(buffer);
}
char tempbuf[128];
size_t count;
if(bufsize>0) {
count = sprintf(tempbuf, "\nReceived device set report type %u on interface %u with ID %u (%u)\n", report_type, instance, report_id, bufsize);
tud_cdc_write(tempbuf, count);
for(int i=0;i<bufsize;i++){
count=sprintf(tempbuf,"%02X ",buffer[i]);
tud_cdc_write(tempbuf,count);
}
count = sprintf(tempbuf, "\n");
tud_cdc_write(tempbuf,count);
tud_cdc_write_flush();
}
}
// Invoked when received GET_REPORT control request
// Application must fill buffer report's content and return its length.
// Return zero will cause the stack to STALL request
uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen)
{
// TODO not Implemented
(void) instance;
(void) report_id;
(void) report_type;
(void) buffer;
(void) reqlen;
char tempbuf[128];
size_t count;
if(reqlen>0) {
count = sprintf(tempbuf, "\nReceived device get report type %u on interface %u with ID %u (%u)\n", report_type, instance, report_id, reqlen);
tud_cdc_write(tempbuf, count);
for(int i=0;i<reqlen;i++){
count=sprintf(tempbuf,"%02X ",buffer[i]);
tud_cdc_write(tempbuf,count);
}
count = sprintf(tempbuf, "\n");
tud_cdc_write(tempbuf,count);
tud_cdc_write_flush();
}
return 0;
}
// Invoked when sent REPORT successfully to host
// Application can use this to send the next report
// Note: For composite reports, report[0] is report ID
/*
void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len)
{
(void) instance;
(void) len;
char tempbuf[128];
size_t count;
if(len>0) {
count = sprintf(tempbuf, "\nDevice sent report on interface %u (%u)\n", instance, len);
tud_cdc_write(tempbuf, count);
for(int i=0;i<len;i++){
count=sprintf(tempbuf,"%02X ",report[i]);
tud_cdc_write(tempbuf,count);
}
count = sprintf(tempbuf, "\n");
tud_cdc_write(tempbuf,count);
tud_cdc_write_flush();
}
}
*/
+6
View File
@@ -0,0 +1,6 @@
#ifndef HID_H_
#define HID_H_
int run_hid_device(void);
#endif
+393
View File
@@ -0,0 +1,393 @@
<!DOCTYPE html>
<html>
<head>
<title>Web Keyboard</title>
<style>
div.container > div {
display: inline-block;
}
div#indicators {
text-align: center;
vertical-align: top;
font-size: 1.2em;
}
div#indicators .on {
background-color: #40ff40;
}
div#indicators .off {
background-color: #dddddd;
}
div#keyboard {
background-color: #000000;
}
div.key {
text-align:center;
vertical-align: top;
display: inline-block;
background-color: #dddddd;
margin: 1px;
width: 3em;
height: 3.5em;
font-size: 0.8em;
}
div.pressed {
background-color: #ff4040;
}
div.blank {
opacity: 0;
}
div.info div {
font-size: 1.5em;
display: inline-block;
margin: 1em;
}
div#macros div {
width: 9em;
display: inline-block;
background-color: #dddddd;
margin: 1em;
text-align: center;
}
div#macros .mousedown{
background-color: #4040ff;
}
div#status_frames {
visibility: hidden;
}
div.menu {
display: inline-block;
padding: 1em;
background-color: #dddddd;
}
div.menu a {
text-decoration: none;
color: #0000ff;
}
div.menu a:hover {
text-decoration: underline;
}
</style>
<script>
var keys = [];
var fetchqueue = [];
var curLed = 0;
const indicator_list = [
{id: "num", label: "NumLock"},
{id: "caps", label: "CapsLock"},
{id: "scroll", label: "ScrollLock"}
];
const macro_list = [
{id: "power", label: "Power", keys: ["WakeUp"]},
{id: "reset", label: "CTRL+ALT+DEL", keys: ["ControlLeft","AltLeft","Delete"]},
{id: "close", label: "ALT+F4", keys: ["AltLeft", "F4"]},
{id: "cycle", label: "ALT+Tab", keys: ["AltLeft", "Tab"]}
];
const key_list = [
[
{id: "Escape", label: "ESC", width: 2},
{id: "F1", label: "F1"},
{id: "F2", label: "F2"},
{id: "F3", label: "F3"},
{id: "F4", label: "F4"},
{id: "F5", label: "F5"},
{id: "F6", label: "F6"},
{id: "F7", label: "F7"},
{id: "F8", label: "F8"},
{id: "F9", label: "F9"},
{id: "F10", label: "F10"},
{id: "F11", label: "F11"},
{id: "F12", label: "F12"},
{id: "", label: ""},
{id: "PrintScreen", label: "Print<br>Screen"},
{id: "ScrollLock", label: "Scroll<br>Lock"},
{id: "Pause", label: "Pause"}
],
[
{id: "Backquote", label: "~<br>`"},
{id: "Digit1", label: "!<br>1"},
{id: "Digit2", label: "@<br>2"},
{id: "Digit3", label: "#<br>3"},
{id: "Digit4", label: "$<br>4"},
{id: "Digit5", label: "%<br>5"},
{id: "Digit6", label: "^<br>6"},
{id: "Digit7", label: "&<br>7"},
{id: "Digit8", label: "*<br>8"},
{id: "Digit9", label: "(<br>9"},
{id: "Digit0", label: ")<br>0"},
{id: "Minus", label: "_<br>-"},
{id: "Equal", label: "+<br>="},
{id: "Backspace", label: "Backspace", width: 2},
{id: "Insert", label: "Insert" },
{id: "Home", label: "Home" },
{id: "PageUp", label: "Page<br>Up"},
{id: "NumLock", label: "Num"},
{id: "NumpadDivide", label: "/"},
{id: "NumpadMultiply", label: "*"},
{id: "NumpadSubtract", label: "-"}
],
[
{id: "Tab", label: "Tab", width: 1.5},
{id: "KeyQ", label: "Q"},
{id: "KeyW", label: "W"},
{id: "KeyE", label: "E"},
{id: "KeyR", label: "R"},
{id: "KeyT", label: "T"},
{id: "KeyY", label: "Y"},
{id: "KeyU", label: "U"},
{id: "KeyI", label: "I"},
{id: "KeyO", label: "O"},
{id: "KeyP", label: "P"},
{id: "BracketLeft", label: "{<br>["},
{id: "BracketRight", label: "}<br>]"},
{id: "Backslash", label: "|<br>\\", width: 1.5},
{id: "Delete", label: "Delete"},
{id: "End", label: "End"},
{id: "PageDown", label: "Page<br>Down"},
{id: "Numpad7", label: "Home<br>7"},
{id: "Numpad8", label: "Up<br>8"},
{id: "Numpad9", label: "PgUp<br>9"},
{id: "NumpadAdd", label: "+", height: 2}
],
[
{id: "CapsLock", label: "Caps<br>Lock", width: 2},
{id: "KeyA", label: "A"},
{id: "KeyS", label: "S"},
{id: "KeyD", label: "D"},
{id: "KeyF", label: "F"},
{id: "KeyG", label: "G"},
{id: "KeyH", label: "H"},
{id: "KeyJ", label: "J"},
{id: "KeyK", label: "K"},
{id: "KeyL", label: "L"},
{id: "Semicolon", label: ":<br>;"},
{id: "Quote", label: "\"<br>\'"},
{id: "Enter", label: "Enter", width: 2},
{id: "", label: "", width: 3.17},
{id: "Numpad4", label: "Left<br>4"},
{id: "Numpad5", label: "5"},
{id: "Numpad6", label: "Right<br>6"}
],
[
{id: "ShiftLeft", label: "Shift", width: 2.5},
{id: "KeyZ", label: "Z"},
{id: "KeyX", label: "X"},
{id: "KeyC", label: "C"},
{id: "KeyV", label: "V"},
{id: "KeyB", label: "B"},
{id: "KeyN", label: "N"},
{id: "KeyM", label: "M"},
{id: "Comma", label: "\<<br>,"},
{id: "Period", label: "\><br>."},
{id: "Slash", label: "?<br>/"},
{id: "ShiftRight", label: "Shift", width: 2.6},
{id: "", label: ""},
{id: "ArrowUp", label: "Up"},
{id: "", label: ""},
{id: "Numpad1", label: "End<br>1"},
{id: "Numpad2", label: "Down<br>2"},
{id: "Numpad3", label: "PgDn<br>3"},
{id: "NumpadEnter", label: "Enter", height: 2}
],
[
{id: "ControlLeft", label: "Control", width: 2},
{id: "MetaLeft", label: "Meta"},
{id: "AltLeft", label: "Alt", width: 1.5},
{id: "Space", label: "Space", width: 6.4},
{id: "AltRight", label: "Alt", width: 1.5},
{id: "MetaRight", label: "Meta"},
{id: "ControlRight", label: "Control", width: 2},
{id: "ArrowLeft", label: "Left"},
{id: "ArrowDown", label: "Down"},
{id: "ArrowRight", label: "Right"},
{id: "Numpad0", label: "Ins<br>0", width: 2},
{id: "NumpadDecimal", label: "Del<br>."}
],
];
window.setInterval("refreshIndicators();",2000);
window.addEventListener("keydown", onKeyDown, true);
window.addEventListener("keyup", onKeyUp, true);
window.onload = (event) => {
createMacros("macros");
createKeys("keyboard");
}
function onKeyDown(event) {
if (event.defaultPrevented) {
return; // Do nothing if the event was already processed
}
pressKey(event.code);
// Cancel the default action to avoid it being handled twice
event.preventDefault();
}
function pressKey(code) {
if (keys.includes(code)) { return; }
keys.push(code);
sendKeys(keys);
}
function onKeyUp(event) {
if (event.defaultPrevented) {
return; // Do nothing if the event was already processed
}
releaseKey(event.code);
// Cancel the default action to avoid it being handled twice
event.preventDefault();
}
function releaseKey(code) {
if (keys.includes(code)) {
keys.splice(keys.indexOf(code),1);
}
sendKeys(keys);
}
function sendKeys(curKeys) {
const url="sendkeys.cgi?keys=".concat(curKeys);
fetchqueue.push(url);
console.log(fetchqueue);
document.getElementById("downKeys")
.innerHTML = curKeys;
prev_keys = document.getElementsByClassName("pressed");
for (let key of curKeys) {
keyDiv = document.getElementById(key);
keyDiv.classList.add("pressed");
}
for (let key of prev_keys) {
if (!curKeys.includes(key.id)) {
key.classList.remove("pressed");
}
}
if (fetchqueue.length == 1) {
runQueue();
}
}
async function runQueue() {
const url = fetchqueue[0];
await fetch(url);
fetchqueue.shift();
if (fetchqueue.length > 0) {
runQueue();
}
}
function updateIndicators() {
let indicatorFrame = document.getElementById("indicatorFrame");
let indicatorDiv = document.getElementById("indicators");
indicatorDiv.innerHTML = indicatorFrame.contentDocument.body.innerHTML;
}
function refreshIndicators() {
let indicatorFrame = document.getElementById("indicatorFrame");
indicatorFrame.contentWindow.location.reload(true);
}
function createKeys(keyboard_id) {
let keyboardDiv = document.getElementById(keyboard_id);
for (let row of key_list) {
let newRow = document.createElement("div");
keyboardDiv.appendChild(newRow);
for (let key of row) {
let newDiv = document.createElement("div");
if (key.id) {
newDiv.id = key.id;
newDiv.innerHTML = key.label;
newDiv.className = "key";
newDiv.addEventListener("mousedown", function () {
pressKey(key.id); });
newDiv.addEventListener("mouseup", function () {
releaseKey(key.id); });
} else {
newDiv.className = "key blank";
}
newRow.appendChild(newDiv);
if (key.width) {
newDiv.style = "width:" + (key.width*newDiv.clientWidth ) + "px;";
}
if (key.height) {
newDiv.style = "height:" + (key.height*newDiv.clientHeight ) + "px; float: right;";
}
}
}
}
function createMacros(macro_id) {
let macroDiv = document.getElementById(macro_id);
for (let macro of macro_list) {
let newDiv = document.createElement("div");
newDiv.id = macro.id;
newDiv.textContent = macro.label;
newDiv.addEventListener("mousedown", function () {
sendKeys(macro.keys); });
newDiv.addEventListener("mouseup", function () {
sendKeys([]); });
macroDiv.appendChild(newDiv);
}
}
</script>
</head>
<body>
<div class="container">
<div id="keyboard"></div>
<div id="indicators"></div>
</div>
<div id="macros"></div>
<div class="info">
<div>Currently pressed keys:</div>
<div id="downKeys"></div>
</div>
<div>
<div class="menu">
<a href="wifi.shtml">Configure Wi-Fi</a>
</div>
<div class="menu">
<a href="reboot.cgi">Reboot</a>
</div>
</div>
<div id="status_frames">
<iframe id="indicatorFrame" src="indicators.shtml"
onload="updateIndicators();"></iframe>
</div>
</body>
</html>
+15
View File
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<title>Keyboard Indicators</title>
</head>
<body>
<div id="num" class="<!--#num-->">NumLock</div>
<div id="caps" class="<!--#caps-->">CapsLock</div>
<div id="scroll" class="<!--#scroll-->">ScrollLock</div>
</body>
</html>
+35
View File
@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head>
<title>Success</title>
<style>
span.count {
display: inline-block;
}
</style>
<script>
var count = 15;
setInterval( function () {
var counter = document.getElementById("count");
count--;
counter.innerText= count;
if (count <= 0) {
window.location = "index.html";
}
}, 1000);
</script>
</head>
<body>
<p>Success</p>
<p>Redirecting in <span id="count">15</span>...</p>
</body>
</html>
+137
View File
@@ -0,0 +1,137 @@
<!DOCTYPE html>
<html>
<head>
<title>Web Keyboard Wi-Fi Settings</title>
<style>
div.container {
background-color: #eeeeee;
}
div.field > div {
display: inline-block;
}
div.label {
text-align: right;
width: 150px;
}
div.input {
text-align: left;
}
input.network {
width: 3em;
}
</style>
<script>
function toggleDHCP() {
let boxes = document.getElementsByClassName('network');
if (document.getElementById('dhcp').checked ) {
for (box of boxes) {
box.disabled = true;
}
} else {
for (box of boxes) {
box.disabled = false;
}
}
}
window.onload = () => {
toggleDHCP();
document.getElementById('dhcp').onchange = toggleDHCP;
};
</script>
</head>
<body>
<div><h1>Web Keyboard Wi-Fi Settings</h1></div>
<div class="container">
<form method="get" action="wifi.cgi">
<div><h2>Wi-Fi Network</h2></div>
<div class="field">
<div class="label">SSID:</div>
<div class="input"><input type="text" id="ssid" name="ssid"
maxlength="32" <!--#ssid-->></div>
</div>
<div class="field">
<div class="label">Password:</div>
<div class="input"><input type="password" id="pass" name="pass"
maxlength="63" <!--#pass-->></div>
</div>
<div class="field">
<div class="label">Hostname:</div>
<div class="input"><input type="text" id="host" name="host"
maxlength="63" <!--#host-->></div>
</div>
<div><h2>Manual Network Configuration</h2>
<div class="field">
<div class="label">Use DHCP:</div>
<div class="input"><input type="checkbox" id="dhcp" name="dhcp"
<!--#dhcp-->>
</div>
</div>
<div class="field">
<div class="label">IP Address:</div>
<div class="input"><input type="text" id="ip0" name="ip0"
class="network" pattern="[0-9]{1,3}" <!--#ip0-->>.
<input type="text" id="ip1" name="ip1"
class="network" pattern="[0-9]{1,3}" <!--#ip1-->>.
<input type="text" id="ip2" name="ip2"
class="network" pattern="[0-9]{1,3}" <!--#ip2-->>.
<input type="text" id="ip3" name="ip3"
class="network" pattern="[0-9]{1,3}" <!--#ip3-->>
</div>
</div>
<div class="field">
<div class="label">Netmask:</div>
<div class="input"><input type="text" id="mask0" name="mask0"
class="network" pattern="[0-9]{1,3}" <!--#mask0-->>.
<input type="text" id="mask1" name="mask1"
class="network" pattern="[0-9]{1,3}" <!--#mask1-->>.
<input type="text" id="mask2" name="mask2"
class="network" pattern="[0-9]{1,3}" <!--#mask2-->>.
<input type="text" id="mask3" name="mask3"
class="network" pattern="[0-9]{1,3}" <!--#mask3-->>
</div>
</div>
<div class="field">
<div class="label">Default Gateway:</div>
<div class="input"><input type="text" id="gw0" name="gw0"
class="network" pattern="[0-9]{1,3}" <!--#gw0-->>.
<input type="text" id="gw1" name="gw1"
class="network" pattern="[0-9]{1,3}" <!--#gw1-->>.
<input type="text" id="gw2" name="gw2"
class="network" pattern="[0-9]{1,3}" <!--#gw2-->>.
<input type="text" id="gw3" name="gw3"
class="network" pattern="[0-9]{1,3}" <!--#gw3-->>
</div>
</div>
<div class="field">
<div class="label"></div>
<div class="input"><input value="Save" type="submit">*<br>
*Device will need to be rebooted for settings to take effect.</div>
</div>
</form>
</div>
</body>
</html>
+100
View File
@@ -0,0 +1,100 @@
#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__
// Common settings used in most of the pico_w examples
// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details)
// allow override in some examples
#ifndef NO_SYS
#define NO_SYS 1
#endif
// allow override in some examples
#ifndef LWIP_SOCKET
#define LWIP_SOCKET 0
#endif
#if PICO_CYW43_ARCH_POLL
#define MEM_LIBC_MALLOC 1
#else
// MEM_LIBC_MALLOC is incompatible with non polling versions
#define MEM_LIBC_MALLOC 0
#endif
#define MEM_ALIGNMENT 4
#ifndef MEM_SIZE
#define MEM_SIZE 4000
#endif
#define MEMP_NUM_TCP_SEG 32
#define MEMP_NUM_ARP_QUEUE 10
#define PBUF_POOL_SIZE 24
#define LWIP_ARP 1
#define LWIP_ETHERNET 1
#define LWIP_ICMP 1
#define LWIP_RAW 1
#define TCP_WND (8 * TCP_MSS)
#define TCP_MSS 1460
#define TCP_SND_BUF (8 * TCP_MSS)
#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS))
#define LWIP_NETIF_STATUS_CALLBACK 1
#define LWIP_NETIF_LINK_CALLBACK 1
#define LWIP_NETIF_HOSTNAME 1
#define LWIP_NETCONN 0
#define MEM_STATS 0
#define SYS_STATS 0
#define MEMP_STATS 0
#define LINK_STATS 0
// #define ETH_PAD_SIZE 2
#define LWIP_CHKSUM_ALGORITHM 3
#define LWIP_DHCP 1
#define LWIP_IPV4 1
#define LWIP_TCP 1
#define LWIP_UDP 1
#define LWIP_DNS 1
#define LWIP_TCP_KEEPALIVE 1
#define LWIP_NETIF_TX_SINGLE_PBUF 1
#define DHCP_DOES_ARP_CHECK 0
#define LWIP_DHCP_DOES_ACD_CHECK 0
#ifndef NDEBUG
#define LWIP_DEBUG 1
#define LWIP_STATS 1
#define LWIP_STATS_DISPLAY 1
#endif
#define ETHARP_DEBUG LWIP_DBG_OFF
#define NETIF_DEBUG LWIP_DBG_OFF
#define PBUF_DEBUG LWIP_DBG_OFF
#define API_LIB_DEBUG LWIP_DBG_OFF
#define API_MSG_DEBUG LWIP_DBG_OFF
#define SOCKETS_DEBUG LWIP_DBG_OFF
#define ICMP_DEBUG LWIP_DBG_OFF
#define INET_DEBUG LWIP_DBG_OFF
#define IP_DEBUG LWIP_DBG_OFF
#define IP_REASS_DEBUG LWIP_DBG_OFF
#define RAW_DEBUG LWIP_DBG_OFF
#define MEM_DEBUG LWIP_DBG_OFF
#define MEMP_DEBUG LWIP_DBG_OFF
#define SYS_DEBUG LWIP_DBG_OFF
#define TCP_DEBUG LWIP_DBG_OFF
#define TCP_INPUT_DEBUG LWIP_DBG_OFF
#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF
#define TCP_RTO_DEBUG LWIP_DBG_OFF
#define TCP_CWND_DEBUG LWIP_DBG_OFF
#define TCP_WND_DEBUG LWIP_DBG_OFF
#define TCP_FR_DEBUG LWIP_DBG_OFF
#define TCP_QLEN_DEBUG LWIP_DBG_OFF
#define TCP_RST_DEBUG LWIP_DBG_OFF
#define UDP_DEBUG LWIP_DBG_OFF
#define TCPIP_DEBUG LWIP_DBG_OFF
#define PPP_DEBUG LWIP_DBG_OFF
#define SLIP_DEBUG LWIP_DBG_OFF
#define DHCP_DEBUG LWIP_DBG_OFF
// HTTPD stuff
#define LWIP_HTTPD 1
#define LWIP_HTTPD_CGI 1
#define LWIP_HTTPD_SSI 1
#define LWIP_HTTPD_SSI_INCLUDE_TAG 0
#define HTTPD_FSDATA_FILE "my_fsdata.c"
#endif /* __LWIPOPTS_H__ */
+26
View File
@@ -0,0 +1,26 @@
#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/multicore.h"
#include "main.h"
#include "server.h"
#include "hid.h"
int main() {
stdio_init_all();
multicore_reset_core1();
multicore_launch_core1(core1_main);
multicore_lockout_victim_init();
run_hid_device();
}
void core1_main() {
run_http_server();
}
+7
View File
@@ -0,0 +1,7 @@
#ifndef MAIN_H_
#define MAIN_H_
int main();
void core1_main();
#endif
+1388
View File
File diff suppressed because it is too large Load Diff
+64
View File
@@ -0,0 +1,64 @@
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "tusb.h"
#include "parse_keys.h"
#include "usb_descriptors.h"
void parse_key_list(char * keys) {
uint8_t keypos = 2;
uint8_t code = 0x00;
static unsigned char boot_report[8];
memset(boot_report, 0x00, 8);
char * token = strtok(keys, ",");
while (token != NULL) {
code = parse_key_single(token);
if (code && keypos < 9) {
boot_report[keypos] = code;
keypos++;
}
if (code == 0x00) {
code = parse_mod(token);
if (code) {
boot_report[0] = boot_report[0] | code;
}
}
token = strtok(NULL, ",");
}
printf("HID report: ");
for(int i=0; i<8; i++){
printf("%02X ",boot_report[i]);
}
printf("\n");
tud_hid_report(REPORT_ID_KEYBOARD, boot_report, 8);
}
uint8_t parse_key_single(char * key) {
//printf("single key: %s\n", key);
for (int i=0; i < NKEYS; i++) {
keycode_dict * keycode = &keytable[i];
if (strcmp(keycode->key, key) == 0){
//printf("key found");
return keycode->val;
}
}
return 0x00;
}
uint8_t parse_mod(char * key) {
for (int i=0; i < NMODS; i++) {
keycode_dict * modcode = &modtable[i];
if (strcmp(modcode->key, key) == 0){
//printf("key found");
return modcode->val;
}
}
return 0x00;
}
+393
View File
@@ -0,0 +1,393 @@
#ifndef PARSE_KEYS_H_
#define PRASE_KEYS_H_
static unsigned char boot_buf[8];
void parse_key_list(char * keys);
uint8_t parse_key_single(char * key);
uint8_t parse_mod(char * key);
typedef struct { char * key; uint8_t val; } keycode_dict;
#define KEY_MOD_LCTRL 0x01
#define KEY_MOD_LSHIFT 0x02
#define KEY_MOD_LALT 0x04
#define KEY_MOD_LMETA 0x08
#define KEY_MOD_RCTRL 0x10
#define KEY_MOD_RSHIFT 0x20
#define KEY_MOD_RALT 0x40
#define KEY_MOD_RMETA 0x80
/**
* Scan codes - last N slots in the HID report (usually 6).
* 0x00 if no key pressed.
*
* If more than N keys are pressed, the HID reports
* KEY_ERR_OVF in all slots to indicate this condition.
*/
#define KEY_NONE 0x00 // No key pressed
#define KEY_ERR_OVF 0x01 // Keyboard Error Roll Over - used for all slots if too many keys are pressed ("Phantom key")
// 0x02 // Keyboard POST Fail
// 0x03 // Keyboard Error Undefined
#define KEY_A 0x04 // Keyboard a and A
#define KEY_B 0x05 // Keyboard b and B
#define KEY_C 0x06 // Keyboard c and C
#define KEY_D 0x07 // Keyboard d and D
#define KEY_E 0x08 // Keyboard e and E
#define KEY_F 0x09 // Keyboard f and F
#define KEY_G 0x0a // Keyboard g and G
#define KEY_H 0x0b // Keyboard h and H
#define KEY_I 0x0c // Keyboard i and I
#define KEY_J 0x0d // Keyboard j and J
#define KEY_K 0x0e // Keyboard k and K
#define KEY_L 0x0f // Keyboard l and L
#define KEY_M 0x10 // Keyboard m and M
#define KEY_N 0x11 // Keyboard n and N
#define KEY_O 0x12 // Keyboard o and O
#define KEY_P 0x13 // Keyboard p and P
#define KEY_Q 0x14 // Keyboard q and Q
#define KEY_R 0x15 // Keyboard r and R
#define KEY_S 0x16 // Keyboard s and S
#define KEY_T 0x17 // Keyboard t and T
#define KEY_U 0x18 // Keyboard u and U
#define KEY_V 0x19 // Keyboard v and V
#define KEY_W 0x1a // Keyboard w and W
#define KEY_X 0x1b // Keyboard x and X
#define KEY_Y 0x1c // Keyboard y and Y
#define KEY_Z 0x1d // Keyboard z and Z
#define KEY_1 0x1e // Keyboard 1 and !
#define KEY_2 0x1f // Keyboard 2 and @
#define KEY_3 0x20 // Keyboard 3 and #
#define KEY_4 0x21 // Keyboard 4 and $
#define KEY_5 0x22 // Keyboard 5 and %
#define KEY_6 0x23 // Keyboard 6 and ^
#define KEY_7 0x24 // Keyboard 7 and &
#define KEY_8 0x25 // Keyboard 8 and *
#define KEY_9 0x26 // Keyboard 9 and (
#define KEY_0 0x27 // Keyboard 0 and )
#define KEY_ENTER 0x28 // Keyboard Return (ENTER)
#define KEY_ESC 0x29 // Keyboard ESCAPE
#define KEY_BACKSPACE 0x2a // Keyboard DELETE (Backspace)
#define KEY_TAB 0x2b // Keyboard Tab
#define KEY_SPACE 0x2c // Keyboard Spacebar
#define KEY_MINUS 0x2d // Keyboard - and _
#define KEY_EQUAL 0x2e // Keyboard = and +
#define KEY_LEFTBRACE 0x2f // Keyboard [ and {
#define KEY_RIGHTBRACE 0x30 // Keyboard ] and }
#define KEY_BACKSLASH 0x31 // Keyboard \ and |
#define KEY_HASHTILDE 0x32 // Keyboard Non-US # and ~
#define KEY_SEMICOLON 0x33 // Keyboard ; and :
#define KEY_APOSTROPHE 0x34 // Keyboard ' and "
#define KEY_GRAVE 0x35 // Keyboard ` and ~
#define KEY_COMMA 0x36 // Keyboard , and <
#define KEY_DOT 0x37 // Keyboard . and >
#define KEY_SLASH 0x38 // Keyboard / and ?
#define KEY_CAPSLOCK 0x39 // Keyboard Caps Lock
#define KEY_F1 0x3a // Keyboard F1
#define KEY_F2 0x3b // Keyboard F2
#define KEY_F3 0x3c // Keyboard F3
#define KEY_F4 0x3d // Keyboard F4
#define KEY_F5 0x3e // Keyboard F5
#define KEY_F6 0x3f // Keyboard F6
#define KEY_F7 0x40 // Keyboard F7
#define KEY_F8 0x41 // Keyboard F8
#define KEY_F9 0x42 // Keyboard F9
#define KEY_F10 0x43 // Keyboard F10
#define KEY_F11 0x44 // Keyboard F11
#define KEY_F12 0x45 // Keyboard F12
#define KEY_SYSRQ 0x46 // Keyboard Print Screen
#define KEY_SCROLLLOCK 0x47 // Keyboard Scroll Lock
#define KEY_PAUSE 0x48 // Keyboard Pause
#define KEY_INSERT 0x49 // Keyboard Insert
#define KEY_HOME 0x4a // Keyboard Home
#define KEY_PAGEUP 0x4b // Keyboard Page Up
#define KEY_DELETE 0x4c // Keyboard Delete Forward
#define KEY_END 0x4d // Keyboard End
#define KEY_PAGEDOWN 0x4e // Keyboard Page Down
#define KEY_RIGHT 0x4f // Keyboard Right Arrow
#define KEY_LEFT 0x50 // Keyboard Left Arrow
#define KEY_DOWN 0x51 // Keyboard Down Arrow
#define KEY_UP 0x52 // Keyboard Up Arrow
#define KEY_NUMLOCK 0x53 // Keyboard Num Lock and Clear
#define KEY_KPSLASH 0x54 // Keypad /
#define KEY_KPASTERISK 0x55 // Keypad *
#define KEY_KPMINUS 0x56 // Keypad -
#define KEY_KPPLUS 0x57 // Keypad +
#define KEY_KPENTER 0x58 // Keypad ENTER
#define KEY_KP1 0x59 // Keypad 1 and End
#define KEY_KP2 0x5a // Keypad 2 and Down Arrow
#define KEY_KP3 0x5b // Keypad 3 and PageDn
#define KEY_KP4 0x5c // Keypad 4 and Left Arrow
#define KEY_KP5 0x5d // Keypad 5
#define KEY_KP6 0x5e // Keypad 6 and Right Arrow
#define KEY_KP7 0x5f // Keypad 7 and Home
#define KEY_KP8 0x60 // Keypad 8 and Up Arrow
#define KEY_KP9 0x61 // Keypad 9 and Page Up
#define KEY_KP0 0x62 // Keypad 0 and Insert
#define KEY_KPDOT 0x63 // Keypad . and Delete
#define KEY_102ND 0x64 // Keyboard Non-US \ and |
#define KEY_COMPOSE 0x65 // Keyboard Application
#define KEY_POWER 0x66 // Keyboard Power
#define KEY_KPEQUAL 0x67 // Keypad =
#define KEY_F13 0x68 // Keyboard F13
#define KEY_F14 0x69 // Keyboard F14
#define KEY_F15 0x6a // Keyboard F15
#define KEY_F16 0x6b // Keyboard F16
#define KEY_F17 0x6c // Keyboard F17
#define KEY_F18 0x6d // Keyboard F18
#define KEY_F19 0x6e // Keyboard F19
#define KEY_F20 0x6f // Keyboard F20
#define KEY_F21 0x70 // Keyboard F21
#define KEY_F22 0x71 // Keyboard F22
#define KEY_F23 0x72 // Keyboard F23
#define KEY_F24 0x73 // Keyboard F24
#define KEY_OPEN 0x74 // Keyboard Execute
#define KEY_HELP 0x75 // Keyboard Help
#define KEY_PROPS 0x76 // Keyboard Menu
#define KEY_FRONT 0x77 // Keyboard Select
#define KEY_STOP 0x78 // Keyboard Stop
#define KEY_AGAIN 0x79 // Keyboard Again
#define KEY_UNDO 0x7a // Keyboard Undo
#define KEY_CUT 0x7b // Keyboard Cut
#define KEY_COPY 0x7c // Keyboard Copy
#define KEY_PASTE 0x7d // Keyboard Paste
#define KEY_FIND 0x7e // Keyboard Find
#define KEY_MUTE 0x7f // Keyboard Mute
#define KEY_VOLUMEUP 0x80 // Keyboard Volume Up
#define KEY_VOLUMEDOWN 0x81 // Keyboard Volume Down
// 0x82 Keyboard Locking Caps Lock
// 0x83 Keyboard Locking Num Lock
// 0x84 Keyboard Locking Scroll Lock
#define KEY_KPCOMMA 0x85 // Keypad Comma
// 0x86 Keypad Equal Sign
#define KEY_RO 0x87 // Keyboard International1
#define KEY_KATAKANAHIRAGANA 0x88 // Keyboard International2
#define KEY_YEN 0x89 // Keyboard International3
#define KEY_HENKAN 0x8a // Keyboard International4
#define KEY_MUHENKAN 0x8b // Keyboard International5
#define KEY_KPJPCOMMA 0x8c // Keyboard International6
// 0x8d Keyboard International7
// 0x8e Keyboard International8
// 0x8f Keyboard International9
#define KEY_HANGEUL 0x90 // Keyboard LANG1
#define KEY_HANJA 0x91 // Keyboard LANG2
#define KEY_KATAKANA 0x92 // Keyboard LANG3
#define KEY_HIRAGANA 0x93 // Keyboard LANG4
#define KEY_ZENKAKUHANKAKU 0x94 // Keyboard LANG5
// 0x95 Keyboard LANG6
// 0x96 Keyboard LANG7
// 0x97 Keyboard LANG8
// 0x98 Keyboard LANG9
// 0x99 Keyboard Alternate Erase
// 0x9a Keyboard SysReq/Attention
// 0x9b Keyboard Cancel
// 0x9c Keyboard Clear
// 0x9d Keyboard Prior
// 0x9e Keyboard Return
// 0x9f Keyboard Separator
// 0xa0 Keyboard Out
// 0xa1 Keyboard Oper
// 0xa2 Keyboard Clear/Again
// 0xa3 Keyboard CrSel/Props
// 0xa4 Keyboard ExSel
// 0xb0 Keypad 00
// 0xb1 Keypad 000
// 0xb2 Thousands Separator
// 0xb3 Decimal Separator
// 0xb4 Currency Unit
// 0xb5 Currency Sub-unit
#define KEY_KPLEFTPAREN 0xb6 // Keypad (
#define KEY_KPRIGHTPAREN 0xb7 // Keypad )
// 0xb8 Keypad {
// 0xb9 Keypad }
// 0xba Keypad Tab
// 0xbb Keypad Backspace
// 0xbc Keypad A
// 0xbd Keypad B
// 0xbe Keypad C
// 0xbf Keypad D
// 0xc0 Keypad E
// 0xc1 Keypad F
// 0xc2 Keypad XOR
// 0xc3 Keypad ^
// 0xc4 Keypad %
// 0xc5 Keypad <
// 0xc6 Keypad >
// 0xc7 Keypad &
// 0xc8 Keypad &&
// 0xc9 Keypad |
// 0xca Keypad ||
// 0xcb Keypad :
// 0xcc Keypad #
// 0xcd Keypad Space
// 0xce Keypad @
// 0xcf Keypad !
// 0xd0 Keypad Memory Store
// 0xd1 Keypad Memory Recall
// 0xd2 Keypad Memory Clear
// 0xd3 Keypad Memory Add
// 0xd4 Keypad Memory Subtract
// 0xd5 Keypad Memory Multiply
// 0xd6 Keypad Memory Divide
// 0xd7 Keypad +/-
// 0xd8 Keypad Clear
// 0xd9 Keypad Clear Entry
// 0xda Keypad Binary
// 0xdb Keypad Octal
// 0xdc Keypad Decimal
// 0xdd Keypad Hexadecimal
#define KEY_LEFTCTRL 0xe0 // Keyboard Left Control
#define KEY_LEFTSHIFT 0xe1 // Keyboard Left Shift
#define KEY_LEFTALT 0xe2 // Keyboard Left Alt
#define KEY_LEFTMETA 0xe3 // Keyboard Left GUI
#define KEY_RIGHTCTRL 0xe4 // Keyboard Right Control
#define KEY_RIGHTSHIFT 0xe5 // Keyboard Right Shift
#define KEY_RIGHTALT 0xe6 // Keyboard Right Alt
#define KEY_RIGHTMETA 0xe7 // Keyboard Right GUI
#define KEY_MEDIA_PLAYPAUSE 0xe8
#define KEY_MEDIA_STOPCD 0xe9
#define KEY_MEDIA_PREVIOUSSONG 0xea
#define KEY_MEDIA_NEXTSONG 0xeb
#define KEY_MEDIA_EJECTCD 0xec
#define KEY_MEDIA_VOLUMEUP 0xed
#define KEY_MEDIA_VOLUMEDOWN 0xee
#define KEY_MEDIA_MUTE 0xef
#define KEY_MEDIA_WWW 0xf0
#define KEY_MEDIA_BACK 0xf1
#define KEY_MEDIA_FORWARD 0xf2
#define KEY_MEDIA_STOP 0xf3
#define KEY_MEDIA_FIND 0xf4
#define KEY_MEDIA_SCROLLUP 0xf5
#define KEY_MEDIA_SCROLLDOWN 0xf6
#define KEY_MEDIA_EDIT 0xf7
#define KEY_MEDIA_SLEEP 0xf8
#define KEY_MEDIA_COFFEE 0xf9
#define KEY_MEDIA_REFRESH 0xfa
#define KEY_MEDIA_CALC 0xfb
static keycode_dict keytable[] = {
{"KeyA", KEY_A},
{"KeyB", KEY_B},
{"KeyC", KEY_C},
{"KeyD", KEY_D},
{"KeyE", KEY_E},
{"KeyF", KEY_F},
{"KeyG", KEY_G},
{"KeyH", KEY_H},
{"KeyI", KEY_I},
{"KeyJ", KEY_J},
{"KeyK", KEY_K},
{"KeyL", KEY_L},
{"KeyM", KEY_M},
{"KeyN", KEY_N},
{"KeyO", KEY_O},
{"KeyP", KEY_P},
{"KeyQ", KEY_Q},
{"KeyR", KEY_R},
{"KeyS", KEY_S},
{"KeyT", KEY_T},
{"KeyU", KEY_U},
{"KeyV", KEY_V},
{"KeyW", KEY_W},
{"KeyX", KEY_X},
{"KeyY", KEY_Y},
{"KeyZ", KEY_Z},
{"Digit1", KEY_1},
{"Digit2", KEY_2},
{"Digit3", KEY_3},
{"Digit4", KEY_4},
{"Digit5", KEY_5},
{"Digit6", KEY_6},
{"Digit7", KEY_7},
{"Digit8", KEY_8},
{"Digit9", KEY_9},
{"Digit0", KEY_0},
{"Enter", KEY_ENTER},
{"Escape", KEY_ESC},
{"Backspace", KEY_BACKSPACE},
{"Tab", KEY_TAB},
{"Space", KEY_SPACE},
{"Minus", KEY_MINUS},
{"Equal", KEY_EQUAL},
{"BracketLeft", KEY_LEFTBRACE},
{"BracketRight", KEY_RIGHTBRACE},
{"Backslash", KEY_BACKSLASH},
{"Semicolon", KEY_SEMICOLON},
{"Quote", KEY_APOSTROPHE},
{"Backquote", KEY_GRAVE},
{"Comma", KEY_COMMA},
{"Period", KEY_DOT},
{"Slash", KEY_SLASH},
{"CapsLock", KEY_CAPSLOCK},
{"F1", KEY_F1},
{"F2", KEY_F2},
{"F3", KEY_F3},
{"F4", KEY_F4},
{"F5", KEY_F5},
{"F6", KEY_F6},
{"F7", KEY_F7},
{"F8", KEY_F8},
{"F9", KEY_F9},
{"F10", KEY_F10},
{"F11", KEY_F11},
{"F12", KEY_F12},
{"PrintScreen", KEY_SYSRQ},
{"ScrollLock", KEY_SCROLLLOCK},
{"Pause", KEY_PAUSE},
{"Insert", KEY_INSERT},
{"Home", KEY_HOME},
{"PageUp", KEY_PAGEUP},
{"Delete", KEY_DELETE},
{"End", KEY_END},
{"PageDown", KEY_PAGEDOWN},
{"ArrowRight", KEY_RIGHT},
{"ArrowLeft", KEY_LEFT},
{"ArrowDown", KEY_DOWN},
{"ArrowUp", KEY_UP},
{"NumLock", KEY_NUMLOCK},
{"NumpadDivide", KEY_KPSLASH},
{"NumpadMultiply", KEY_KPASTERISK},
{"NumpadSubtract", KEY_KPMINUS},
{"NumpadAdd", KEY_KPPLUS},
{"NumpadEnter", KEY_KPENTER},
{"Numpad1", KEY_KP1},
{"Numpad2", KEY_KP2},
{"Numpad3", KEY_KP3},
{"Numpad4", KEY_KP4},
{"Numpad5", KEY_KP5},
{"Numpad6", KEY_KP6},
{"Numpad7", KEY_KP7},
{"Numpad8", KEY_KP8},
{"Numpad9", KEY_KP9},
{"Numpad0", KEY_KP0},
{"NumpadDecimal", KEY_KPDOT},
{"WakeUp", KEY_POWER}
};
static keycode_dict modtable[] = {
{"ControlLeft", KEY_MOD_LCTRL},
{"ShiftLeft", KEY_MOD_LSHIFT},
{"AltLeft", KEY_MOD_LALT},
{"MetaLeft", KEY_MOD_LMETA},
{"ControlRight", KEY_MOD_RCTRL},
{"ShiftRight", KEY_MOD_RSHIFT},
{"AltRight", KEY_MOD_RALT},
{"MetaRight", KEY_MOD_RMETA}
};
#define NKEYS ( sizeof(keytable)/sizeof(keycode_dict) )
#define NMODS ( sizeof(modtable)/sizeof(keycode_dict) )
#endif
+388
View File
@@ -0,0 +1,388 @@
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "lwip/apps/httpd.h"
#include "lwip/ip4_addr.h"
#include "pico/cyw43_arch.h"
#include "hardware/watchdog.h"
#include "hardware/flash.h"
#include "hardware/sync.h"
#include "pico/multicore.h"
#include "server.h"
#include "parse_keys.h"
#include "dhcpserver.h"
uint8_t led_code = 0;
static uint8_t ip[4];
static uint8_t mask[4];
static uint8_t gw[4];
static absolute_time_t lastCheck;
static absolute_time_t lastActive;
static net_config wifi;
static bool clientmode = false;
static bool config_loaded = false;
static bool bad_config = false;
static bool reboot = false;
const char * __not_in_flash("httpd") ssi_tags[] = {
"num",
"caps",
"scroll",
"ssid",
"pass",
"host",
"ip0",
"ip1",
"ip2",
"ip3",
"mask0",
"mask1",
"mask2",
"mask3",
"gw0",
"gw1",
"gw2",
"gw3",
"dhcp"
};
static const tCGI cgi_handlers[] = {
{ "/sendkeys.cgi", sendkeys_cgi },
{ "/wifi.cgi", save_wifi },
{ "/reboot.cgi", reboot_cgi },
};
void run_http_server() {
//sleep_ms(5000);
if (cyw43_arch_init_with_country(CYW43_COUNTRY_USA)) {
printf("failed to initialize\n");
return;
}
clientmode = false;
config_loaded = net_config_load(&wifi);
if ( config_loaded ){
printf("read config file\n");
cyw43_arch_enable_sta_mode();
// set hostname
//cyw43_arch_lwip_begin();
struct netif *n = &cyw43_state.netif[CYW43_ITF_STA];
if (wifi.host) {
netif_set_hostname(n, wifi.host);
} else {
netif_set_hostname(n, DEFAULTHOST);
}
if (wifi.manual) {
dhcp_release_and_stop(n);
netif_set_addr(n, &(wifi.ip), &(wifi.mask), &(wifi.gw));
dhcp_inform(n);
}
netif_set_up(n);
//cyw43_arch_lwip_end();
if(cyw43_arch_wifi_connect_timeout_ms(wifi.ssid, wifi.pass, CYW43_AUTH_WPA2_AES_PSK, 10000)){
printf("failed to connect to %s\n", wifi.ssid);
} else{
clientmode = true;
}
}
if (!clientmode) {
//start AP mode
cyw43_arch_enable_ap_mode(DEFAULTHOST, DEFAULTPASS, CYW43_AUTH_WPA2_AES_PSK);
ip4_addr_t ip, mask, gw;
IP4_ADDR(&ip, 192, 168, 0, 1);
IP4_ADDR(&mask, 255, 255, 255, 0);
IP4_ADDR(&gw, 192, 168, 0, 1);
netif_set_ipaddr(netif_default, &ip);
netif_set_up(netif_default);
dhcp_server_t dhcp;
dhcp_server_init(&dhcp, &gw, &mask);
printf("launched in AP mode\n");
}
// start the server
httpd_init();
http_set_cgi_handlers(cgi_handlers, 3);
for (size_t i = 0; i < LWIP_ARRAYSIZE(ssi_tags); i++) {
LWIP_ASSERT("tag too long for LWIP_HTTPD_MAX_TAG_NAME_LEN",
strlen(ssi_tags[i]) <= LWIP_HTTPD_MAX_TAG_NAME_LEN);
}
http_set_ssi_handler(ssi_handler, ssi_tags, LWIP_ARRAYSIZE(ssi_tags));
printf("HTTP server initialized\n");
watchdog_enable(8000,1);
if (clientmode) {
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN,1);
while(true) {
if ( absolute_time_diff_us(lastCheck, get_absolute_time()) > 5000000) {
if ( cyw43_tcpip_link_status(&cyw43_state, CYW43_ITF_STA) == 3 ) {
lastCheck = get_absolute_time();
if(!reboot){
watchdog_update();
}
}
}
}
} else {
lastActive = get_absolute_time(); //initialize activity timer
static int led = 1;
while(true) {
if (absolute_time_diff_us(lastCheck, get_absolute_time()) > 1000000) {
led = !led;
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led);
lastCheck = get_absolute_time();
if ( config_loaded && (absolute_time_diff_us(lastActive, lastCheck) > 600000000)) {
reboot = true;
}
if(!reboot) {
watchdog_update();
}
}
}
}
}
const char * sendkeys_cgi (int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) {
for (int i=0; i < iNumParams; i++) {
if (strcmp(pcParam[i], "keys") == 0 ) {
//printf("Web input: %s\n", pcValue[i]);
parse_key_list(pcValue[i]);
}
}
lastActive= get_absolute_time();
// send return page
return "/success.html";
}
const char * save_wifi (int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) {
// clear all values in manual network settings
memset(ip, 0, sizeof(ip));
memset(mask, 0, sizeof(mask));
memset(gw, 0, sizeof(gw));
bad_config = false;
wifi.manual = true;
for (int i=0; i < iNumParams; i++) {
if (strcmp(pcParam[i], "ssid") == 0){
if (pcValue[i][0] == "\0") {
bad_config = true;
} else {
urldecode(pcValue[i], wifi.ssid);
}
} else if (strcmp(pcParam[i], "pass") == 0) {
urldecode(pcValue[i], wifi.pass);
} else if (strcmp(pcParam[i], "host") == 0) {
urldecode(pcValue[i], wifi.host);
} else if (strcmp(pcParam[i], "dhcp") == 0) {
wifi.manual = false;
} else if (strncmp(pcParam[i], "ip", 2) ==0 ) {
uint8_t part = atoi(&(pcParam[i][2]));
int val = atoi(pcValue[i]);
if (pcValue[i][0] == "\0" || val > 255 || val < 0 || part > 4) {
bad_config = true;
} else {
ip[part] = (uint8_t) val;
}
} else if (strncmp(pcParam[i], "gw", 2) ==0 ) {
uint8_t part = atoi(&(pcParam[i][2]));
int val = atoi(pcValue[i]);
if (pcValue[i][0] == "\0" || val > 255 || val < 0 || part > 4) {
bad_config = true;
} else {
gw[part] = (uint8_t) val;
}
} else if (strncmp(pcParam[i], "mask", 4) ==0 ) {
uint8_t part = atoi(&(pcParam[i][4]));
int val = atoi(pcValue[i]);
if (pcValue[i][0] == "\0" || val > 255 || val < 0 || part > 4) {
bad_config = true;
} else {
mask[part] = (uint8_t) val;
}
}
}
if (bad_config) {
return "/wifi.shtml";
} else {
IP4_ADDR(&(wifi.ip), ip[0], ip[1], ip[2], ip[3]);
if(!wifi.ip.addr) {
wifi.ip.addr = IPADDR_NONE;
}
IP4_ADDR(&(wifi.mask), mask[0], mask[1], mask[2], mask[3]);
if(!wifi.mask.addr) {
wifi.mask.addr = IPADDR_NONE;
}
IP4_ADDR(&(wifi.gw), gw[0], gw[1], gw[2], gw[3]);
if(!wifi.gw.addr) {
wifi.gw.addr = IPADDR_NONE;
}
wifi.header = STARTFILE;
wifi.footer = ENDFILE;
config_loaded = true;
watchdog_update();
net_config_write(&wifi);
printf("wifi settings saved\n");
return "/success.html";
}
}
const char * reboot_cgi(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) {
reboot = true;
return "/success.html";
}
void net_config_write(net_config *config) {
multicore_lockout_start_blocking();
uint8_t buf[FLASH_PAGE_SIZE];
memset(buf, 0x00, FLASH_PAGE_SIZE);
memcpy(buf, config, sizeof(net_config));
uint32_t interrupts = save_and_disable_interrupts();
flash_range_erase(FLASH_TARGET_OFFSET, FLASH_SECTOR_SIZE);
flash_range_program(FLASH_TARGET_OFFSET, buf, FLASH_PAGE_SIZE);
restore_interrupts(interrupts);
multicore_lockout_end_blocking();
}
bool net_config_load(net_config *config) {
const uint8_t *data = (const uint8_t *) (XIP_BASE+FLASH_TARGET_OFFSET);
memcpy(config, data, sizeof(net_config));
return ( (config->header == STARTFILE) && (config->footer == ENDFILE) );
}
uint16_t __time_critical_func(ssi_handler)(int iIndex, char *pcInsert, int iInsertLen) {
size_t printed;
switch (iIndex) {
case 0: //num
if (led_code & 1) {
printed = snprintf(pcInsert, iInsertLen, "on");
} else {
printed = snprintf(pcInsert, iInsertLen, "off");
}
break;
case 1: //caps
if (led_code & 2) {
printed = snprintf(pcInsert, iInsertLen, "on");
} else {
printed = snprintf(pcInsert, iInsertLen, "off");
}
break;
case 2: //scroll
if (led_code & 4) {
printed = snprintf(pcInsert, iInsertLen, "on");
} else {
printed = snprintf(pcInsert, iInsertLen, "off");
}
break;
case 3: //ssid
if (config_loaded && wifi.ssid) {
printed = snprintf(pcInsert, iInsertLen, "value=\"%s\"", wifi.ssid);
} else {
printed = 0;
}
break;
case 4: //pass
if (config_loaded && wifi.pass) {
printed = snprintf(pcInsert, iInsertLen, "value=\"%s\"", wifi.pass);
} else {
printed = 0;
}
break;
case 5: //host
if (config_loaded && wifi.host) {
printed = snprintf(pcInsert, iInsertLen, "value=\"%s\"", wifi.host);
} else {
printed = 0;
}
break;
case 6:
case 7:
case 8:
case 9: //ip0-3
if (config_loaded && wifi.ip.addr) {
printed = snprintf(pcInsert, iInsertLen, "value=\"%d\"", ip4_addr_get_byte(&(wifi.ip), iIndex-6));
} else {
printed = 0;
}
break;
case 10:
case 11:
case 12:
case 13: //mask0-3
if (config_loaded && wifi.mask.addr) {
printed = snprintf(pcInsert, iInsertLen, "value=\"%d\"", ip4_addr_get_byte(&(wifi.mask), iIndex-10));
} else {
printed = 0;
}
break;
case 14:
case 15:
case 16:
case 17: //gw0-3
if (config_loaded && wifi.gw.addr) {
printed = snprintf(pcInsert, iInsertLen, "value=\"%d\"", ip4_addr_get_byte(&(wifi.gw), iIndex-14));
} else {
printed = 0;
}
break;
case 18: // dhcp
if ((!config_loaded) || !(wifi.manual) ){
printed = snprintf(pcInsert, iInsertLen, "checked");
} else {
printed = 0;
}
break;
default: //undefined tag
printed = 0;
break;
}
LWIP_ASSERT("illegal length returned", printed <= 0xFFFF);
return (uint16_t)printed;
}
void set_indicator(uint8_t const* buffer) {
led_code = *buffer;
}
void urldecode(char *urlstring, char *decoded) {
uint8_t conv;
while(*urlstring) {
if(*urlstring == '+') {
*decoded=' ';
} else if (*urlstring == '%') {
urlstring++;
sscanf(urlstring, "%02hhx", &conv);
*decoded = conv;
urlstring++;
} else {
*decoded = *urlstring;
}
urlstring++;;
decoded++;
}
*decoded = '\0';
}
+35
View File
@@ -0,0 +1,35 @@
#ifndef SERVER_H_
#define SERVER_H_
#include "lwip/ip4_addr.h"
#define STARTFILE 0x7fc6
#define ENDFILE 0x0464
#define DEFAULTHOST "picokb"
#define DEFAULTPASS "password"
typedef struct {
uint16_t header;
char ssid[32+1];
char pass[63+1];
char host[63+1];
bool manual;
ip4_addr_t ip;
ip4_addr_t mask;
ip4_addr_t gw;
uint16_t footer;
} net_config;
void run_http_server();
const char * sendkeys_cgi(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]);
const char * save_wifi(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]);
const char * reboot_cgi(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]);
uint16_t __time_critical_func(ssi_handler)(int iIndex, char *pcInsert, int iInsertLen);
void set_indicator(uint8_t const* buffer);
void urldecode(char *urlstring, char *dest);
bool net_config_load(net_config *wifi);
void net_config_write(net_config *wifi);
#define FLASH_TARGET_OFFSET ((PICO_FLASH_SIZE_BYTES) - FLASH_SECTOR_SIZE)
#endif
+85
View File
@@ -0,0 +1,85 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#ifndef _TUSB_CONFIG_H_
#define _TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------
// COMMON CONFIGURATION
//--------------------------------------------------------------------
#define CFG_TUSB_OS OPT_OS_PICO
// Enable device stack
#define CFG_TUD_ENABLED 1
// Enable host stack with pio-usb if Pico-PIO-USB library is available
#define CFG_TUH_ENABLED 0
#define CFG_TUH_RPI_PIO_USB 0
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
// #define CFG_TUSB_DEBUG 0
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
* e.g
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
*/
#ifndef CFG_TUSB_MEM_SECTION
#define CFG_TUSB_MEM_SECTION
#endif
#ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
//------------- CLASS -------------//
#define CFG_TUD_CDC 1
#define CFG_TUD_HID 1
#define CFG_TUD_CDC_RX_BUFSIZE 64
#define CFG_TUD_CDC_TX_BUFSIZE 64
#define CFG_TUD_HID_EP_BUFSIZE 16
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_CONFIG_H_ */
+195
View File
@@ -0,0 +1,195 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
* sekigon-gonnoc
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include "tusb.h"
#include "usb_descriptors.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
_PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
#define USB_VID 0xCafe
#define USB_BCD 0x0200
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = USB_BCD,
// Use Interface Association Descriptor (IAD) for CDC
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = USB_VID,
.idProduct = USB_PID,
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
uint8_t const * tud_descriptor_device_cb(void)
{
return (uint8_t const *) &desc_device;
}
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
#define EPNUM_CDC_NOTIF 0x81
#define EPNUM_CDC_OUT 0x02
#define EPNUM_CDC_IN 0x82
#define EPNUM_HID 0x83
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_DESC_LEN)
uint8_t const desc_hid_report[] =
{
TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(REPORT_ID_KEYBOARD))
};
// full speed configuration
uint8_t const desc_fs_configuration[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
TUD_HID_DESCRIPTOR(ITF_NUM_HID, 5, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 5),
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
{
(void) index; // for multiple configurations
return desc_fs_configuration;
}
//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
// array of pointer to string descriptors
char const* string_desc_arr [] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"Raspberry Pi", // 1: Manufacturer
"Pico Web Keyboard", // 2: Product
"1234567890123456789", // 3: Serials, should use chip ID
"Pico Web Keyboard CDC", // 4: CC Interface
"TinyUSB Keyboard", // 5: HID Keyboard Interface
};
static uint16_t _desc_str[32];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
{
(void) langid;
uint8_t chr_count;
if ( index == 0)
{
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
}else
{
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL;
const char* str = string_desc_arr[index];
// Cap at max char
chr_count = (uint8_t) strlen(str);
if ( chr_count > 31 ) chr_count = 31;
// Convert ASCII string into UTF-16
for(uint8_t i=0; i<chr_count; i++)
{
_desc_str[1+i] = str[i];
}
}
// first byte is length (including header), second byte is string type
_desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2*chr_count + 2);
return _desc_str;
}
//--------------------------------------------------------------------+
// Device HID
//--------------------------------------------------------------------+
// Invoked when received GET HID REPORT DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf)
{
(void) itf;
//switch(itf) {
// case 0:
return desc_hid_report;
// case 1:
// return desc_nkro_report;
// case 2:
// return desc_media_report;
// default:
// return 0;
//}
}
+44
View File
@@ -0,0 +1,44 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef USB_DESCRIPTORS_H_
#define USB_DESCRIPTORS_H_
enum
{
REPORT_ID_KEYBOARD = 1,
REPORT_ID_CONSUMER_CONTROL,
REPORT_ID_COUNT
};
enum
{
ITF_NUM_CDC=0,
ITF_NUM_CDC_DATA,
ITF_NUM_HID,
ITF_NUM_TOTAL
};
#endif /* USB_DESCRIPTORS_H_ */