Add keyboard/mbed_onekey
This commit is contained in:
parent
79840c678e
commit
f15b2691c9
@ -26,7 +26,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
#define dprint(s) do { if (debug_enable) print(s); } while (0)
|
#define dprint(s) do { if (debug_enable) print(s); } while (0)
|
||||||
#define dprintln() do { if (debug_enable) print_crlf(); } while (0)
|
#define dprintln() do { if (debug_enable) print_crlf(); } while (0)
|
||||||
#define dprintf(fmt, ...) do { if (debug_enable) __xprintf(PSTR(fmt), ##__VA_ARGS__); } while (0)
|
#define dprintf(fmt, ...) do { if (debug_enable) xprintf(fmt, ##__VA_ARGS__); } while (0)
|
||||||
#define dmsg(s) dprintf("%s at %s: %S\n", __FILE__, __LINE__, PSTR(s))
|
#define dmsg(s) dprintf("%s at %s: %S\n", __FILE__, __LINE__, PSTR(s))
|
||||||
|
|
||||||
/* DO NOT USE these anymore */
|
/* DO NOT USE these anymore */
|
||||||
|
@ -37,26 +37,4 @@ void print_set_sendchar(int8_t (*sendchar_func)(uint8_t))
|
|||||||
xdev_out(sendchar_func);
|
xdev_out(sendchar_func);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_S(const char *s)
|
|
||||||
{
|
|
||||||
uint8_t c;
|
|
||||||
while (1) {
|
|
||||||
c = *s++;
|
|
||||||
if (!c) break;
|
|
||||||
if (c == '\n') sendchar('\r');
|
|
||||||
sendchar(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_lf(void)
|
|
||||||
{
|
|
||||||
sendchar('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_crlf(void)
|
|
||||||
{
|
|
||||||
sendchar('\r');
|
|
||||||
sendchar('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
143
common/print.h
143
common/print.h
@ -27,29 +27,60 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <avr/pgmspace.h>
|
|
||||||
#include "xprintf.h"
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
|
||||||
// this macro allows you to write print("some text") and
|
|
||||||
// the string is automatically placed into flash memory :)
|
|
||||||
|
#ifndef NO_PRINT
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __AVR__
|
||||||
|
|
||||||
|
#include "xprintf.h"
|
||||||
|
|
||||||
|
|
||||||
// TODO: avoid collision with arduino/Print.h
|
// TODO: avoid collision with arduino/Print.h
|
||||||
#ifndef __cplusplus
|
#ifndef __cplusplus
|
||||||
#define print(s) print_P(PSTR(s))
|
#define print(s) xputs(PSTR(s))
|
||||||
#endif
|
#endif
|
||||||
#define println(s) print_P(PSTR(s "\n"))
|
#define println(s) xputs(PSTR(s "\n"))
|
||||||
|
|
||||||
/* for old name */
|
#ifdef __cplusplus
|
||||||
#define pdec(data) print_dec(data)
|
extern "C" {
|
||||||
#define pdec16(data) print_dec(data)
|
#endif
|
||||||
#define phex(data) print_hex8(data)
|
/* function pointer of sendchar to be used by print utility */
|
||||||
#define phex16(data) print_hex16(data)
|
void print_set_sendchar(int8_t (*print_sendchar_func)(uint8_t));
|
||||||
#define pbin(data) print_bin8(data)
|
|
||||||
#define pbin16(data) print_bin16(data)
|
|
||||||
#define pbin_reverse(data) print_bin_reverse8(data)
|
|
||||||
#define pbin_reverse16(data) print_bin_reverse16(data)
|
|
||||||
|
|
||||||
|
#elif __arm__
|
||||||
|
|
||||||
|
#include "mbed.h"
|
||||||
|
Serial ser(UART_TX, UART_RX);
|
||||||
|
#define xprintf ser.printf
|
||||||
|
#define print(s) xprintf(s)
|
||||||
|
#define println(s) xprintf(s "\n")
|
||||||
|
/* TODO: to select output destinations: UART/USBSerial */
|
||||||
|
#define print_set_sendchar(func)
|
||||||
|
|
||||||
|
#endif /* __AVR__ */
|
||||||
|
|
||||||
|
|
||||||
|
/* decimal */
|
||||||
|
#define print_dec(i) xprintf("%u", i)
|
||||||
|
#define print_decs(i) xprintf("%d", i)
|
||||||
|
/* hex */
|
||||||
|
#define print_hex4(i) xprintf("%X", i)
|
||||||
|
#define print_hex8(i) xprintf("%02X", i)
|
||||||
|
#define print_hex16(i) xprintf("%04X", i)
|
||||||
|
#define print_hex32(i) xprintf("%08lX", i)
|
||||||
|
/* binary */
|
||||||
|
#define print_bin4(i) xprintf("%04b", i)
|
||||||
|
#define print_bin8(i) xprintf("%08b", i)
|
||||||
|
#define print_bin16(i) xprintf("%016b", i)
|
||||||
|
#define print_bin32(i) xprintf("%032lb", i)
|
||||||
|
#define print_bin_reverse8(i) xprintf("%08b", bitrev(i))
|
||||||
|
#define print_bin_reverse16(i) xprintf("%016b", bitrev16(i))
|
||||||
|
#define print_bin_reverse32(i) xprintf("%032lb", bitrev32(i))
|
||||||
/* print value utility */
|
/* print value utility */
|
||||||
#define print_val_dec(v) xprintf(#v ": %u\n", v)
|
#define print_val_dec(v) xprintf(#v ": %u\n", v)
|
||||||
#define print_val_decs(v) xprintf(#v ": %d\n", v)
|
#define print_val_decs(v) xprintf(#v ": %d\n", v)
|
||||||
@ -63,62 +94,12 @@
|
|||||||
#define print_val_bin_reverse16(v) xprintf(#v ": %016b\n", bitrev16(v))
|
#define print_val_bin_reverse16(v) xprintf(#v ": %016b\n", bitrev16(v))
|
||||||
#define print_val_bin_reverse32(v) xprintf(#v ": %032lb\n", bitrev32(v))
|
#define print_val_bin_reverse32(v) xprintf(#v ": %032lb\n", bitrev32(v))
|
||||||
|
|
||||||
|
#else /* NO_PRINT */
|
||||||
|
|
||||||
|
#define xprintf
|
||||||
#ifndef NO_PRINT
|
#define print
|
||||||
|
#define println
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* function pointer of sendchar to be used by print utility */
|
|
||||||
void print_set_sendchar(int8_t (*print_sendchar_func)(uint8_t));
|
|
||||||
|
|
||||||
/* print string stored in data memory(SRAM)
|
|
||||||
* print_S("hello world");
|
|
||||||
* This consumes precious SRAM memory space for string.
|
|
||||||
*/
|
|
||||||
void print_S(const char *s);
|
|
||||||
|
|
||||||
void print_lf(void);
|
|
||||||
void print_crlf(void);
|
|
||||||
|
|
||||||
|
|
||||||
/* print string stored in program memory(FLASH)
|
|
||||||
* print_P(PSTR("hello world");
|
|
||||||
* This consumes relatively abundant FLASH memory area not SRAM.
|
|
||||||
*/
|
|
||||||
#define print_P(s) xputs(s)
|
|
||||||
|
|
||||||
/* decimal */
|
|
||||||
#define print_dec(i) xprintf("%u", i)
|
|
||||||
#define print_decs(i) xprintf("%d", i)
|
|
||||||
|
|
||||||
/* hex */
|
|
||||||
#define print_hex4(i) xprintf("%X", i)
|
|
||||||
#define print_hex8(i) xprintf("%02X", i)
|
|
||||||
#define print_hex16(i) xprintf("%04X", i)
|
|
||||||
#define print_hex32(i) xprintf("%08lX", i)
|
|
||||||
|
|
||||||
/* binary */
|
|
||||||
#define print_bin4(i) xprintf("%04b", i)
|
|
||||||
#define print_bin8(i) xprintf("%08b", i)
|
|
||||||
#define print_bin16(i) xprintf("%016b", i)
|
|
||||||
#define print_bin32(i) xprintf("%032lb", i)
|
|
||||||
|
|
||||||
#define print_bin_reverse8(i) xprintf("%08b", bitrev(i))
|
|
||||||
#define print_bin_reverse16(i) xprintf("%016b", bitrev16(i))
|
|
||||||
#define print_bin_reverse32(i) xprintf("%032lb", bitrev32(i))
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define print_set_sendchar(func)
|
#define print_set_sendchar(func)
|
||||||
#define print_S(s)
|
|
||||||
#define print_P(s)
|
|
||||||
#define print_dec(data)
|
#define print_dec(data)
|
||||||
#define print_decs(data)
|
#define print_decs(data)
|
||||||
#define print_hex4(data)
|
#define print_hex4(data)
|
||||||
@ -132,8 +113,30 @@ void print_crlf(void);
|
|||||||
#define print_bin_reverse8(data)
|
#define print_bin_reverse8(data)
|
||||||
#define print_bin_reverse16(data)
|
#define print_bin_reverse16(data)
|
||||||
#define print_bin_reverse32(data)
|
#define print_bin_reverse32(data)
|
||||||
|
#define print_val_dec(v)
|
||||||
|
#define print_val_decs(v)
|
||||||
|
#define print_val_hex8(v)
|
||||||
|
#define print_val_hex16(v)
|
||||||
|
#define print_val_hex32(v)
|
||||||
|
#define print_val_bin8(v)
|
||||||
|
#define print_val_bin16(v)
|
||||||
|
#define print_val_bin32(v)
|
||||||
|
#define print_val_bin_reverse8(v)
|
||||||
|
#define print_val_bin_reverse16(v)
|
||||||
|
#define print_val_bin_reverse32(v)
|
||||||
|
|
||||||
#endif
|
#endif /* NO_PRINT */
|
||||||
|
|
||||||
|
|
||||||
|
/* Backward compatiblitly for old name */
|
||||||
|
#define pdec(data) print_dec(data)
|
||||||
|
#define pdec16(data) print_dec(data)
|
||||||
|
#define phex(data) print_hex8(data)
|
||||||
|
#define phex16(data) print_hex16(data)
|
||||||
|
#define pbin(data) print_bin8(data)
|
||||||
|
#define pbin16(data) print_bin16(data)
|
||||||
|
#define pbin_reverse(data) print_bin_reverse8(data)
|
||||||
|
#define pbin_reverse16(data) print_bin_reverse16(data)
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
256
keyboard/mbed_onekey/HIDKeyboard.cpp
Normal file
256
keyboard/mbed_onekey/HIDKeyboard.cpp
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include "USBHID.h"
|
||||||
|
#include "USBHID_Types.h"
|
||||||
|
#include "USBDescriptor.h"
|
||||||
|
#include "HIDKeyboard.h"
|
||||||
|
|
||||||
|
#define DEFAULT_CONFIGURATION (1)
|
||||||
|
|
||||||
|
|
||||||
|
HIDKeyboard::HIDKeyboard(uint16_t vendor_id, uint16_t product_id, uint16_t product_release): USBDevice(vendor_id, product_id, product_release)
|
||||||
|
{
|
||||||
|
USBDevice::connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HIDKeyboard::sendReport(report_keyboard_t report) {
|
||||||
|
USBDevice::write(EP1IN, report.raw, sizeof(report), MAX_PACKET_SIZE_EP1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HIDKeyboard::USBCallback_setConfiguration(uint8_t configuration) {
|
||||||
|
if (configuration != DEFAULT_CONFIGURATION) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure endpoints > 0
|
||||||
|
addEndpoint(EPINT_IN, MAX_PACKET_SIZE_EPINT);
|
||||||
|
//addEndpoint(EPINT_OUT, MAX_PACKET_SIZE_EPINT);
|
||||||
|
|
||||||
|
// We activate the endpoint to be able to recceive data
|
||||||
|
//readStart(EPINT_OUT, MAX_PACKET_SIZE_EPINT);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t * HIDKeyboard::stringImanufacturerDesc() {
|
||||||
|
static uint8_t stringImanufacturerDescriptor[] = {
|
||||||
|
0x18, /*bLength*/
|
||||||
|
STRING_DESCRIPTOR, /*bDescriptorType 0x03*/
|
||||||
|
't',0,'m',0,'k',0,'-',0,'k',0,'b',0,'d',0,'.',0,'c',0,'o',0,'m',0 /*bString iManufacturer*/
|
||||||
|
};
|
||||||
|
return stringImanufacturerDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t * HIDKeyboard::stringIproductDesc() {
|
||||||
|
static uint8_t stringIproductDescriptor[] = {
|
||||||
|
0x0a, /*bLength*/
|
||||||
|
STRING_DESCRIPTOR, /*bDescriptorType 0x03*/
|
||||||
|
'm',0,'b',0,'e',0,'d',0 /*bString iProduct*/
|
||||||
|
};
|
||||||
|
return stringIproductDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t * HIDKeyboard::stringIserialDesc() {
|
||||||
|
static uint8_t stringIserialDescriptor[] = {
|
||||||
|
0x04, /*bLength*/
|
||||||
|
STRING_DESCRIPTOR, /*bDescriptorType 0x03*/
|
||||||
|
'0',0 /*bString iSerial*/
|
||||||
|
};
|
||||||
|
return stringIserialDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t * HIDKeyboard::reportDesc() {
|
||||||
|
static uint8_t reportDescriptor[] = {
|
||||||
|
USAGE_PAGE(1), 0x01, // Generic Desktop
|
||||||
|
USAGE(1), 0x06, // Keyboard
|
||||||
|
COLLECTION(1), 0x01, // Application
|
||||||
|
|
||||||
|
USAGE_PAGE(1), 0x07, // Key Codes
|
||||||
|
USAGE_MINIMUM(1), 0xE0,
|
||||||
|
USAGE_MAXIMUM(1), 0xE7,
|
||||||
|
LOGICAL_MINIMUM(1), 0x00,
|
||||||
|
LOGICAL_MAXIMUM(1), 0x01,
|
||||||
|
REPORT_SIZE(1), 0x01,
|
||||||
|
REPORT_COUNT(1), 0x08,
|
||||||
|
INPUT(1), 0x02, // Data, Variable, Absolute
|
||||||
|
|
||||||
|
REPORT_COUNT(1), 0x01,
|
||||||
|
REPORT_SIZE(1), 0x08,
|
||||||
|
INPUT(1), 0x01, // Constant
|
||||||
|
|
||||||
|
REPORT_COUNT(1), 0x05,
|
||||||
|
REPORT_SIZE(1), 0x01,
|
||||||
|
USAGE_PAGE(1), 0x08, // LEDs
|
||||||
|
USAGE_MINIMUM(1), 0x01,
|
||||||
|
USAGE_MAXIMUM(1), 0x05,
|
||||||
|
OUTPUT(1), 0x02, // Data, Variable, Absolute
|
||||||
|
|
||||||
|
REPORT_COUNT(1), 0x01,
|
||||||
|
REPORT_SIZE(1), 0x03,
|
||||||
|
OUTPUT(1), 0x01, // Constant
|
||||||
|
|
||||||
|
|
||||||
|
REPORT_COUNT(1), 0x06,
|
||||||
|
REPORT_SIZE(1), 0x08,
|
||||||
|
LOGICAL_MINIMUM(1), 0x00,
|
||||||
|
LOGICAL_MAXIMUM(1), 0xFF,
|
||||||
|
USAGE_PAGE(1), 0x07, // Key Codes
|
||||||
|
USAGE_MINIMUM(1), 0x00,
|
||||||
|
USAGE_MAXIMUM(1), 0xFF,
|
||||||
|
INPUT(1), 0x00, // Data, Array
|
||||||
|
END_COLLECTION(0),
|
||||||
|
};
|
||||||
|
reportLength = sizeof(reportDescriptor);
|
||||||
|
return reportDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t HIDKeyboard::reportDescLength() {
|
||||||
|
reportDesc();
|
||||||
|
return reportLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TOTAL_DESCRIPTOR_LENGTH ((1 * CONFIGURATION_DESCRIPTOR_LENGTH) \
|
||||||
|
+ (1 * INTERFACE_DESCRIPTOR_LENGTH) \
|
||||||
|
+ (1 * HID_DESCRIPTOR_LENGTH) \
|
||||||
|
+ (1 * ENDPOINT_DESCRIPTOR_LENGTH))
|
||||||
|
uint8_t * HIDKeyboard::configurationDesc() {
|
||||||
|
static uint8_t configurationDescriptor[] = {
|
||||||
|
CONFIGURATION_DESCRIPTOR_LENGTH,// bLength
|
||||||
|
CONFIGURATION_DESCRIPTOR, // bDescriptorType
|
||||||
|
LSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (LSB)
|
||||||
|
MSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (MSB)
|
||||||
|
0x01, // bNumInterfaces
|
||||||
|
DEFAULT_CONFIGURATION, // bConfigurationValue
|
||||||
|
0x00, // iConfiguration
|
||||||
|
C_RESERVED | C_REMOTE_WAKEUP, // bmAttributes
|
||||||
|
C_POWER(100), // bMaxPowerHello World from Mbed
|
||||||
|
|
||||||
|
INTERFACE_DESCRIPTOR_LENGTH, // bLength
|
||||||
|
INTERFACE_DESCRIPTOR, // bDescriptorType
|
||||||
|
0x00, // bInterfaceNumber
|
||||||
|
0x00, // bAlternateSetting
|
||||||
|
0x01, // bNumEndpoints
|
||||||
|
HID_CLASS, // bInterfaceClass
|
||||||
|
1, // bInterfaceSubClass (boot)
|
||||||
|
1, // bInterfaceProtocol (keyboard)
|
||||||
|
0x00, // iInterface
|
||||||
|
|
||||||
|
HID_DESCRIPTOR_LENGTH, // bLength
|
||||||
|
HID_DESCRIPTOR, // bDescriptorType
|
||||||
|
LSB(HID_VERSION_1_11), // bcdHID (LSB)
|
||||||
|
MSB(HID_VERSION_1_11), // bcdHID (MSB)
|
||||||
|
0x00, // bCountryCode
|
||||||
|
0x01, // bNumDescriptors
|
||||||
|
REPORT_DESCRIPTOR, // bDescriptorType
|
||||||
|
(uint8_t)(LSB(reportDescLength())), // wDescriptorLength (LSB)
|
||||||
|
(uint8_t)(MSB(reportDescLength())), // wDescriptorLength (MSB)
|
||||||
|
|
||||||
|
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
|
||||||
|
ENDPOINT_DESCRIPTOR, // bDescriptorType
|
||||||
|
PHY_TO_DESC(EP1IN), // bEndpointAddress
|
||||||
|
E_INTERRUPT, // bmAttributes
|
||||||
|
LSB(MAX_PACKET_SIZE_EPINT), // wMaxPacketSize (LSB)
|
||||||
|
MSB(MAX_PACKET_SIZE_EPINT), // wMaxPacketSize (MSB)
|
||||||
|
1, // bInterval (milliseconds)
|
||||||
|
};
|
||||||
|
return configurationDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
uint8_t * HIDKeyboard::deviceDesc() {
|
||||||
|
static uint8_t deviceDescriptor[] = {
|
||||||
|
DEVICE_DESCRIPTOR_LENGTH, /* bLength */
|
||||||
|
DEVICE_DESCRIPTOR, /* bDescriptorType */
|
||||||
|
LSB(USB_VERSION_2_0), /* bcdUSB (LSB) */
|
||||||
|
MSB(USB_VERSION_2_0), /* bcdUSB (MSB) */
|
||||||
|
0x00, /* bDeviceClass */
|
||||||
|
0x00, /* bDeviceSubClass */
|
||||||
|
0x00, /* bDeviceprotocol */
|
||||||
|
MAX_PACKET_SIZE_EP0, /* bMaxPacketSize0 */
|
||||||
|
(uint8_t)(LSB(0xfeed)), /* idVendor (LSB) */
|
||||||
|
(uint8_t)(MSB(0xfeed)), /* idVendor (MSB) */
|
||||||
|
(uint8_t)(LSB(0x1bed)), /* idProduct (LSB) */
|
||||||
|
(uint8_t)(MSB(0x1bed)), /* idProduct (MSB) */
|
||||||
|
(uint8_t)(LSB(0x0002)), /* bcdDevice (LSB) */
|
||||||
|
(uint8_t)(MSB(0x0002)), /* bcdDevice (MSB) */
|
||||||
|
0, /* iManufacturer */
|
||||||
|
0, /* iProduct */
|
||||||
|
0, /* iSerialNumber */
|
||||||
|
0x01 /* bNumConfigurations */
|
||||||
|
};
|
||||||
|
return deviceDescriptor;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool HIDKeyboard::USBCallback_request() {
|
||||||
|
bool success = false;
|
||||||
|
CONTROL_TRANSFER * transfer = getTransferPtr();
|
||||||
|
uint8_t *hidDescriptor;
|
||||||
|
|
||||||
|
// Process additional standard requests
|
||||||
|
|
||||||
|
if ((transfer->setup.bmRequestType.Type == STANDARD_TYPE))
|
||||||
|
{
|
||||||
|
switch (transfer->setup.bRequest)
|
||||||
|
{
|
||||||
|
case GET_DESCRIPTOR:
|
||||||
|
switch (DESCRIPTOR_TYPE(transfer->setup.wValue))
|
||||||
|
{
|
||||||
|
case REPORT_DESCRIPTOR:
|
||||||
|
if ((reportDesc() != NULL) \
|
||||||
|
&& (reportDescLength() != 0))
|
||||||
|
{
|
||||||
|
transfer->remaining = reportDescLength();
|
||||||
|
transfer->ptr = reportDesc();
|
||||||
|
transfer->direction = DEVICE_TO_HOST;
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HID_DESCRIPTOR:
|
||||||
|
// Find the HID descriptor, after the configuration descriptor
|
||||||
|
hidDescriptor = findDescriptor(HID_DESCRIPTOR);
|
||||||
|
if (hidDescriptor != NULL)
|
||||||
|
{
|
||||||
|
transfer->remaining = HID_DESCRIPTOR_LENGTH;
|
||||||
|
transfer->ptr = hidDescriptor;
|
||||||
|
transfer->direction = DEVICE_TO_HOST;
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process class-specific requests
|
||||||
|
/*
|
||||||
|
if (transfer->setup.bmRequestType.Type == CLASS_TYPE)
|
||||||
|
{
|
||||||
|
switch (transfer->setup.bRequest)
|
||||||
|
{
|
||||||
|
case SET_REPORT:
|
||||||
|
// First byte will be used for report ID
|
||||||
|
//outputReport.data[0] = transfer->setup.wValue & 0xff;
|
||||||
|
//outputReport.length = transfer->setup.wLength + 1;
|
||||||
|
outputReport.length = transfer->setup.wLength;
|
||||||
|
|
||||||
|
//transfer->remaining = sizeof(outputReport.data) - 1;
|
||||||
|
//transfer->ptr = &outputReport.data[1];
|
||||||
|
transfer->remaining = sizeof(outputReport.data);
|
||||||
|
transfer->ptr = &outputReport.data[0];
|
||||||
|
transfer->direction = HOST_TO_DEVICE;
|
||||||
|
transfer->notify = true;
|
||||||
|
success = true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
36
keyboard/mbed_onekey/HIDKeyboard.h
Normal file
36
keyboard/mbed_onekey/HIDKeyboard.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#ifndef HIDKEYBOARD_H
|
||||||
|
|
||||||
|
#include "stdint.h"
|
||||||
|
#include "stdbool.h"
|
||||||
|
#include "USBHID.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
uint8_t raw[8];
|
||||||
|
struct {
|
||||||
|
uint8_t mods;
|
||||||
|
uint8_t reserved;
|
||||||
|
uint8_t keys[6];
|
||||||
|
};
|
||||||
|
} __attribute__ ((packed)) report_keyboard_t;
|
||||||
|
|
||||||
|
|
||||||
|
class HIDKeyboard : public USBDevice {
|
||||||
|
public:
|
||||||
|
HIDKeyboard(uint16_t vendor_id = 0xFEED, uint16_t product_id = 0xabed, uint16_t product_release = 0x0001);
|
||||||
|
|
||||||
|
bool sendReport(report_keyboard_t report);
|
||||||
|
protected:
|
||||||
|
uint16_t reportLength;
|
||||||
|
virtual bool USBCallback_setConfiguration(uint8_t configuration);
|
||||||
|
virtual uint8_t * stringImanufacturerDesc();
|
||||||
|
virtual uint8_t * stringIproductDesc();
|
||||||
|
virtual uint8_t * stringIserialDesc();
|
||||||
|
virtual uint16_t reportDescLength();
|
||||||
|
virtual uint8_t * reportDesc();
|
||||||
|
virtual uint8_t * configurationDesc();
|
||||||
|
//virtual uint8_t * deviceDesc();
|
||||||
|
virtual bool USBCallback_request();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
29
keyboard/mbed_onekey/Makefile
Normal file
29
keyboard/mbed_onekey/Makefile
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# This file was automagically generated by mbed.org. For more information,
|
||||||
|
# see http://mbed.org/handbook/Exporting-to-GCC-ARM-Embedded
|
||||||
|
|
||||||
|
PROJECT = USBKeyboard_HelloWorld
|
||||||
|
|
||||||
|
TMK_DIR = ../..
|
||||||
|
MBED_DIR = ./mbed-sdk
|
||||||
|
|
||||||
|
#VPATH += $(MBED_DIR):$(TMK_DIR)
|
||||||
|
vpath %.s .:$(MBED_DIR):$(TMK_DIR)
|
||||||
|
vpath %.c .:$(MBED_DIR):$(TMK_DIR)
|
||||||
|
vpath %.cpp .:$(MBED_DIR):$(TMK_DIR)
|
||||||
|
|
||||||
|
OBJDIR = ./build
|
||||||
|
|
||||||
|
OBJECTS = \
|
||||||
|
$(OBJDIR)/./HIDKeyboard.o \
|
||||||
|
$(OBJDIR)/./main.o
|
||||||
|
|
||||||
|
SYS_OBJECTS =
|
||||||
|
|
||||||
|
INCLUDE_PATHS = -I.
|
||||||
|
|
||||||
|
LIBRARY_PATHS =
|
||||||
|
LIBRARIES =
|
||||||
|
|
||||||
|
include mbed.mk
|
||||||
|
include common.mk
|
||||||
|
include gcc.mk
|
19
keyboard/mbed_onekey/common.mk
Normal file
19
keyboard/mbed_onekey/common.mk
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
COMMON_DIR = common
|
||||||
|
OBJECTS += \
|
||||||
|
# $(COMMON_DIR)/host.o \
|
||||||
|
# $(COMMON_DIR)/keyboard.o \
|
||||||
|
# $(COMMON_DIR)/action.o \
|
||||||
|
# $(COMMON_DIR)/action_tapping.o \
|
||||||
|
# $(COMMON_DIR)/action_macro.o \
|
||||||
|
# $(COMMON_DIR)/action_layer.o \
|
||||||
|
# $(COMMON_DIR)/action_util.o \
|
||||||
|
# $(COMMON_DIR)/keymap.o \
|
||||||
|
# $(COMMON_DIR)/timer.o \
|
||||||
|
$(COMMON_DIR)/print.o \
|
||||||
|
# $(COMMON_DIR)/bootloader.o \
|
||||||
|
# $(COMMON_DIR)/suspend.o \
|
||||||
|
$(COMMON_DIR)/xprintf.o \
|
||||||
|
$(COMMON_DIR)/util.o
|
||||||
|
|
||||||
|
INCLUDE_PATHS += \
|
||||||
|
-I$(TMK_DIR)/$(COMMON_DIR)
|
79
keyboard/mbed_onekey/gcc.mk
Normal file
79
keyboard/mbed_onekey/gcc.mk
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
###############################################################################
|
||||||
|
GCC_BIN =
|
||||||
|
AS = $(GCC_BIN)arm-none-eabi-as
|
||||||
|
CC = $(GCC_BIN)arm-none-eabi-gcc
|
||||||
|
CPP = $(GCC_BIN)arm-none-eabi-g++
|
||||||
|
LD = $(GCC_BIN)arm-none-eabi-gcc
|
||||||
|
OBJCOPY = $(GCC_BIN)arm-none-eabi-objcopy
|
||||||
|
OBJDUMP = $(GCC_BIN)arm-none-eabi-objdump
|
||||||
|
SIZE = $(GCC_BIN)arm-none-eabi-size
|
||||||
|
CHKSUM = ~/Dropbox/MBED/tool/lpc-vector-checksum
|
||||||
|
|
||||||
|
CPU = -mcpu=cortex-m0 -mthumb
|
||||||
|
CC_FLAGS = $(CPU) -c -g -fno-common -fmessage-length=0 -Wall -fno-exceptions -ffunction-sections -fdata-sections
|
||||||
|
CC_FLAGS += -MMD -MP
|
||||||
|
CC_SYMBOLS = -DTARGET_LPC11U35_401 -DTARGET_M0 -DTARGET_NXP -DTARGET_LPC11UXX -DTOOLCHAIN_GCC_ARM -DTOOLCHAIN_GCC -D__CORTEX_M0 -DARM_MATH_CM0 -DMBED_BUILD_TIMESTAMP=1399108688.49 -D__MBED__=1
|
||||||
|
|
||||||
|
LD_FLAGS = -mcpu=cortex-m0 -mthumb -Wl,--gc-sections --specs=nano.specs
|
||||||
|
LD_FLAGS += -Wl,-Map=$(OBJDIR)/$(PROJECT).map,--cref
|
||||||
|
LD_SYS_LIBS = -lstdc++ -lsupc++ -lm -lc -lgcc -lnosys
|
||||||
|
|
||||||
|
ifeq ($(DEBUG), 1)
|
||||||
|
CC_FLAGS += -DDEBUG -O0
|
||||||
|
else
|
||||||
|
CC_FLAGS += -DNDEBUG -Os
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: $(OBJDIR)/$(PROJECT).bin $(OBJDIR)/$(PROJECT).hex size
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(OBJDIR)/$(PROJECT).bin $(OBJDIR)/$(PROJECT).elf $(OBJDIR)/$(PROJECT).hex $(OBJDIR)/$(PROJECT).map $(OBJDIR)/$(PROJECT).lst $(OBJECTS) $(DEPS)
|
||||||
|
rm -fr $(OBJDIR)
|
||||||
|
|
||||||
|
$(OBJDIR)/%.o: %.s
|
||||||
|
mkdir -p $(@D)
|
||||||
|
$(AS) $(CPU) -o $@ $<
|
||||||
|
|
||||||
|
$(OBJDIR)/%.o: %.c
|
||||||
|
mkdir -p $(@D)
|
||||||
|
$(CC) $(CC_FLAGS) $(CC_SYMBOLS) -std=gnu99 $(INCLUDE_PATHS) -o $@ $<
|
||||||
|
|
||||||
|
$(OBJDIR)/%.o: %.cpp
|
||||||
|
mkdir -p $(@D)
|
||||||
|
$(CPP) $(CC_FLAGS) $(CC_SYMBOLS) -std=gnu++98 $(INCLUDE_PATHS) -o $@ $<
|
||||||
|
|
||||||
|
|
||||||
|
$(OBJDIR)/$(PROJECT).elf: $(OBJECTS) $(SYS_OBJECTS)
|
||||||
|
$(LD) $(LD_FLAGS) -T$(LINKER_SCRIPT) $(LIBRARY_PATHS) -o $@ $^ $(LIBRARIES) $(LD_SYS_LIBS) $(LIBRARIES) $(LD_SYS_LIBS)
|
||||||
|
@echo ""
|
||||||
|
@echo "*****"
|
||||||
|
@echo "***** You must modify vector checksum value in *.bin and *.hex files."
|
||||||
|
@echo "*****"
|
||||||
|
@echo ""
|
||||||
|
|
||||||
|
$(OBJDIR)/$(PROJECT).bin: $(OBJDIR)/$(PROJECT).elf
|
||||||
|
@$(OBJCOPY) -O binary $< $@
|
||||||
|
@echo "Writing vector checksum value into $@ ..."
|
||||||
|
@$(CHKSUM) $@
|
||||||
|
@echo
|
||||||
|
|
||||||
|
$(OBJDIR)/$(PROJECT).hex: $(OBJDIR)/$(PROJECT).elf
|
||||||
|
@$(OBJCOPY) -O ihex $< $@
|
||||||
|
|
||||||
|
$(OBJDIR)/$(PROJECT).lst: $(OBJDIR)/$(PROJECT).elf
|
||||||
|
@$(OBJDUMP) -Sdh $< > $@
|
||||||
|
|
||||||
|
lst: $(OBJDIR)/$(PROJECT).lst
|
||||||
|
|
||||||
|
size:
|
||||||
|
$(SIZE) $(OBJDIR)/$(PROJECT).elf
|
||||||
|
|
||||||
|
prog: $(OBJDIR)/$(PROJECT).bin
|
||||||
|
@echo "Program..."
|
||||||
|
ifneq ($(shell mount | grep 'CRP DISABLD'),)
|
||||||
|
umount /dev/nxpisp >/dev/null 2>&1
|
||||||
|
endif
|
||||||
|
dd if=$< of=/dev/nxpisp seek=4
|
||||||
|
|
||||||
|
DEPS = $(OBJECTS:.o=.d) $(SYS_OBJECTS:.o=.d)
|
||||||
|
-include $(DEPS)
|
55
keyboard/mbed_onekey/main.cpp
Normal file
55
keyboard/mbed_onekey/main.cpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#include "mbed.h"
|
||||||
|
#include "HIDKeyboard.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
//#define DEBUG
|
||||||
|
#ifdef DEBUG
|
||||||
|
Serial ser(UART_TX, UART_RX);
|
||||||
|
#define dprintf(fmt, ...) ser.printf(fmt, ## __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define dprintf(fmt, ...)
|
||||||
|
#endif
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// button and LEDs
|
||||||
|
DigitalIn isp(P0_1);
|
||||||
|
DigitalOut led_red(P0_20);
|
||||||
|
DigitalOut led_green(P0_21);
|
||||||
|
|
||||||
|
//USBKeyboard
|
||||||
|
HIDKeyboard keyboard;
|
||||||
|
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
//isp.mode(PullUp);
|
||||||
|
//led_red = 0;
|
||||||
|
//led_green = 0;
|
||||||
|
debug_enable = true;
|
||||||
|
dprintf("HIDKeyboard:\n");
|
||||||
|
print("aaa");
|
||||||
|
|
||||||
|
report_keyboard_t report = { 2, 0, 4, }; //a
|
||||||
|
report_keyboard_t report_off = { 0 };
|
||||||
|
while (1) {
|
||||||
|
//keyboard.mediaControl(KEY_VOLUME_DOWN);
|
||||||
|
//keyboard.printf("Hello World from Mbed\r\n");
|
||||||
|
//keyboard.keyCode('s', KEY_CTRL);
|
||||||
|
//keyboard.keyCode(KEY_CAPS_LOCK);
|
||||||
|
|
||||||
|
//led_green = !led_green;
|
||||||
|
//leds = keyboard.lockStatus();
|
||||||
|
//ser.putc(ser.getc());
|
||||||
|
|
||||||
|
if (isp == 0) {
|
||||||
|
led_red = 0; // on
|
||||||
|
keyboard.sendReport(report);
|
||||||
|
} else {
|
||||||
|
led_red = 1; // off
|
||||||
|
keyboard.sendReport(report_off);
|
||||||
|
}
|
||||||
|
led_green = !led_green;
|
||||||
|
//wait(0.5);
|
||||||
|
}
|
||||||
|
}
|
92
keyboard/mbed_onekey/mbed.mk
Normal file
92
keyboard/mbed_onekey/mbed.mk
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
OBJECTS += \
|
||||||
|
$(OBJDIR)/libraries/mbed/targets/cmsis/TARGET_NXP/TARGET_LPC11UXX/TOOLCHAIN_GCC_ARM/startup_LPC11xx.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/targets/cmsis/TARGET_NXP/TARGET_LPC11UXX/cmsis_nvic.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/targets/cmsis/TARGET_NXP/TARGET_LPC11UXX/system_LPC11Uxx.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC11UXX/port_api.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC11UXX/spi_api.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC11UXX/analogin_api.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC11UXX/gpio_api.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC11UXX/gpio_irq_api.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC11UXX/serial_api.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC11UXX/i2c_api.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC11UXX/pwmout_api.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC11UXX/sleep.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC11UXX/pinmap.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC11UXX/us_ticker.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/board.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/mbed_interface.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/exit.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/rtc_time.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/us_ticker_api.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/pinmap_common.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/error.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/gpio.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/semihost_api.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/wait_api.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/RawSerial.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/Ticker.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/FilePath.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/I2C.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/FileBase.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/retarget.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/Serial.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/Stream.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/FileSystemLike.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/CallChain.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/InterruptManager.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/SerialBase.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/BusInOut.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/SPISlave.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/I2CSlave.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/FunctionPointer.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/Timer.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/SPI.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/Timeout.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/Ethernet.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/TimerEvent.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/CAN.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/BusOut.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/FileLike.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/BusIn.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/InterruptIn.o \
|
||||||
|
$(OBJDIR)/libraries/mbed/common/LocalFileSystem.o \
|
||||||
|
$(OBJDIR)/libraries/USBDevice/USBHID/USBMouse.o \
|
||||||
|
$(OBJDIR)/libraries/USBDevice/USBHID/USBHID.o \
|
||||||
|
$(OBJDIR)/libraries/USBDevice/USBHID/USBMouseKeyboard.o \
|
||||||
|
$(OBJDIR)/libraries/USBDevice/USBHID/USBKeyboard.o \
|
||||||
|
$(OBJDIR)/libraries/USBDevice/USBDevice/USBHAL_KL25Z.o \
|
||||||
|
$(OBJDIR)/libraries/USBDevice/USBDevice/USBDevice.o \
|
||||||
|
$(OBJDIR)/libraries/USBDevice/USBDevice/USBHAL_LPC17.o \
|
||||||
|
$(OBJDIR)/libraries/USBDevice/USBDevice/USBHAL_LPC40.o \
|
||||||
|
$(OBJDIR)/libraries/USBDevice/USBDevice/USBHAL_LPC11U.o \
|
||||||
|
$(OBJDIR)/libraries/USBDevice/USBDevice/USBHAL_STM32F4.o \
|
||||||
|
$(OBJDIR)/libraries/USBDevice/USBAudio/USBAudio.o \
|
||||||
|
$(OBJDIR)/libraries/USBDevice/USBSerial/USBSerial.o \
|
||||||
|
$(OBJDIR)/libraries/USBDevice/USBSerial/USBCDC.o \
|
||||||
|
$(OBJDIR)/libraries/USBDevice/USBMSD/USBMSD.o \
|
||||||
|
$(OBJDIR)/libraries/USBDevice/USBMIDI/USBMIDI.o
|
||||||
|
|
||||||
|
INCLUDE_PATHS += \
|
||||||
|
-I$(MBED_DIR)/libraries/mbed/targets \
|
||||||
|
-I$(MBED_DIR)/libraries/mbed/targets/hal \
|
||||||
|
-I$(MBED_DIR)/libraries/mbed/targets/hal/TARGET_NXP \
|
||||||
|
-I$(MBED_DIR)/libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC11UXX \
|
||||||
|
-I$(MBED_DIR)/libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC11UXX/TARGET_LPC11U35_401 \
|
||||||
|
-I$(MBED_DIR)/libraries/mbed/targets/cmsis \
|
||||||
|
-I$(MBED_DIR)/libraries/mbed/targets/cmsis/TARGET_NXP \
|
||||||
|
-I$(MBED_DIR)/libraries/mbed/targets/cmsis/TARGET_NXP/TARGET_LPC11UXX \
|
||||||
|
-I$(MBED_DIR)/libraries/mbed/targets/cmsis/TARGET_NXP/TARGET_LPC11UXX/TOOLCHAIN_GCC_ARM \
|
||||||
|
-I$(MBED_DIR)/libraries/mbed/targets/cmsis/TARGET_NXP/TARGET_LPC11UXX/TOOLCHAIN_GCC_ARM/TARGET_LPC11U35_401 \
|
||||||
|
-I$(MBED_DIR)/libraries/mbed \
|
||||||
|
-I$(MBED_DIR)/libraries/mbed/hal \
|
||||||
|
-I$(MBED_DIR)/libraries/mbed/api \
|
||||||
|
-I$(MBED_DIR)/libraries/mbed/common \
|
||||||
|
-I$(MBED_DIR)/libraries/USBDevice \
|
||||||
|
-I$(MBED_DIR)/libraries/USBDevice/USBHID \
|
||||||
|
-I$(MBED_DIR)/libraries/USBDevice/USBDevice \
|
||||||
|
-I$(MBED_DIR)/libraries/USBDevice/USBAudio \
|
||||||
|
-I$(MBED_DIR)/libraries/USBDevice/USBSerial \
|
||||||
|
-I$(MBED_DIR)/libraries/USBDevice/USBMSD \
|
||||||
|
-I$(MBED_DIR)/libraries/USBDevice/USBMIDI
|
||||||
|
|
||||||
|
LINKER_SCRIPT = $(MBED_DIR)/libraries/mbed/targets/cmsis/TARGET_NXP/TARGET_LPC11UXX/TOOLCHAIN_GCC_ARM/TARGET_LPC11U35_401/LPC11U35.ld
|
Loading…
Reference in New Issue
Block a user