Skip to content

Instantly share code, notes, and snippets.

@btidor
Created February 21, 2022 02:37
Show Gist options
  • Save btidor/2144fcf4d5a14eb7a7ef17e67137b758 to your computer and use it in GitHub Desktop.
Save btidor/2144fcf4d5a14eb7a7ef17e67137b758 to your computer and use it in GitHub Desktop.
#include <mbed.h>
#include "USBFprint.h"
int main() {
Serial1.begin(115200);
Serial1.println("\r\n --8<------------------------\r\n");
Serial1.print("Connecting I");
USBFprint fp(false);
Serial1.print("C");
fp.connect();
Serial1.println("*");
while (true) {
thread_sleep_for(1000);
}
}
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdint.h>
#include <string.h>
#include "Arduino.h"
#include "USBFprint.h"
#include "EndpointResolver.h"
#include "usb_phy_api.h"
#define DEFAULT_CONFIGURATION (1)
USBFprint::USBFprint(bool connect_blocking, uint16_t vendor_id, uint16_t product_id, uint16_t product_release)
: USBDevice(get_usb_phy(), vendor_id, product_id, product_release)
{
_init();
if (connect_blocking) {
connect();
} else {
init();
}
}
USBFprint::USBFprint(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release)
: USBDevice(phy, vendor_id, product_id, product_release)
{
_init();
}
USBFprint::~USBFprint()
{
deinit();
}
void USBFprint::_init()
{
EndpointResolver resolver(endpoint_table());
resolver.endpoint_ctrl(BULK_MAX_PACKET_SIZE);
_bulk_in = resolver.endpoint_in(USB_EP_TYPE_BULK, BULK_MAX_PACKET_SIZE);
_bulk_out = resolver.endpoint_out(USB_EP_TYPE_BULK, BULK_MAX_PACKET_SIZE);
_int_in = resolver.endpoint_in(USB_EP_TYPE_INT, INT_MAX_PACKET_SIZE);
MBED_ASSERT(resolver.valid());
}
void USBFprint::callback_state_change(DeviceState new_state)
{
Serial1.print("callback_state_change new_state=");
assert_locked();
/* Called in ISR context */
switch (new_state) {
case Attached:
Serial1.print("Attached");
break;
case Powered:
Serial1.print("Powered");
break;
case Default:
Serial1.print("Default");
break;
case Address:
Serial1.print("Address");
break;
case Configured:
Serial1.print("Configured");
break;
default:
Serial1.print("Unknown");
break;
}
Serial1.println("*");
}
void USBFprint::callback_request(const setup_packet_t *setup)
{
Serial1.print("callback_request bRequest=0x");
Serial1.println(setup->bRequest, HEX);
assert_locked();
/* Called in ISR context */
complete_request(PassThrough, NULL, 0);
// The Windows driver always asks to un-stall the endpoints, but we must
// re-begin the read after this is handled.
if (setup->bRequest == 0x01) {
read_start(_bulk_out, _rx_buffer, sizeof(_rx_buffer));
}
}
void USBFprint::callback_request_xfer_done(const setup_packet_t *setup, bool aborted)
{
Serial1.print("callback_xfer_done");
assert_locked();
/* Called in ISR context */
complete_request_xfer_done(false);
}
void USBFprint::callback_set_configuration(uint8_t configuration)
{
Serial1.print("callback_set_configuration cfg=0x");
Serial1.println(configuration, HEX);
assert_locked();
/* Called in ISR context */
bool ret = false;
if (configuration == DEFAULT_CONFIGURATION) {
// Configure endpoints > 0
endpoint_add(_int_in, INT_MAX_PACKET_SIZE, USB_EP_TYPE_INT);
endpoint_add(_bulk_in, BULK_MAX_PACKET_SIZE, USB_EP_TYPE_BULK, &USBFprint::_send_isr);
endpoint_add(_bulk_out, BULK_MAX_PACKET_SIZE, USB_EP_TYPE_BULK, &USBFprint::_receive_isr);
read_start(_bulk_out, _rx_buffer, sizeof(_rx_buffer));
ret = true;
}
complete_set_configuration(ret);
}
void USBFprint::callback_set_interface(uint16_t interface, uint8_t alternate)
{
Serial1.print("callback_set_interface cfg=0x");
Serial1.println(interface, HEX);
assert_locked();
complete_set_interface(true);
}
/*
* Called by when CDC data is sent
* Warning: Called in ISR
*/
void USBFprint::_send_isr()
{
assert_locked();
write_finish(_bulk_in);
}
uint8_t version_data[] = {
// Padding
0x00, 0x00,
// Build Time (little endian)
0x47, 0x51, 0x2a, 0x5f,
// Build Num. (little endian)
0x27, 0xf2, 0x31, 0x00,
// Version Major, Minor
0x0a, 0x01,
// Target, Product
0x01, 0x41,
// Silicon Rev., Formal Release, Platform, Patch
0x01, 0xc1, 0x00, 0x00,
// Serial Num. (021354, swapped nibbles)
0x21, 0x65, 0x43, 0x87, 0xcb, 0xa9,
// Security Level (little endian)
0x0f, 0xa9,
// Interface, Device Type
0x00, 0x00,
// ???
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
};
bmkt_resp_t resp = {
// These fields shouldn't be touched
.padding = 0,
.header_id = BMKT_MESSAGE_HEADER_ID,
};
uint8_t uid[64];
uint8_t uid_len;
int remaining;
uint8_t resp8e09[] = {
0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00,
0x09, 0x00, 0x86, 0x0d, 0x00, 0x00, 0xf4, 0x01,
0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
};
uint8_t resp8e1a[] = {
0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x44, 0x00,
0x1a, 0x00, 0xab, 0xc2, 0x02, 0x00, 0xbe, 0xc0,
0x02, 0x00, 0x5b, 0x1b, 0x00, 0x00, 0x0a, 0x00,
0x00, 0x00, 0x44, 0x00, 0x56, 0x00, 0x68, 0x00,
0x03, 0x00, 0x6b, 0x01, 0x20, 0x03, 0x00, 0x00,
0x00, 0x00, 0x10, 0x27, 0x41, 0x00, 0xa0, 0x0f,
0xd7, 0x00, 0x00, 0x00, 0x19, 0x00, 0x19, 0x00,
0x19, 0x00, 0x19, 0x00, 0x19, 0x00, 0x19, 0x00,
0x19, 0x00, 0x19, 0x00, 0x19, 0x00, 0x19, 0x00,
0x19, 0x00, 0x19, 0x00, 0x19, 0x00,
};
uint8_t resp8e2f[] = {
0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x18, 0x00,
0x2f, 0x00, 0x01, 0x00, 0x00, 0x01, 0x08, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x32, 0x00,
0x00, 0x00,
};
uint8_t respaf01[] = {
0x00, 0x00, 0xea, 0x5c, 0x59, 0x15, 0x01, 0xff,
0xff, 0xff,
};
uint8_t resp19[] = {
0x00, 0x00, 0x00, 0x03, 0x01, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xf4, 0xd0, 0xea, 0x5a, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
};
/*
* Called by when CDC data is received
* Warning: Called in ISR
*/
void USBFprint::_receive_isr()
{
assert_locked();
uint32_t size = read_finish(_bulk_out);
Serial1.print("recv");
_print_buffer_hex(_rx_buffer, size);
Serial1.println();
_handle(size);
// Get ready to read next message
read_start(_bulk_out, _rx_buffer, sizeof(_rx_buffer));
}
void USBFprint::_handle(uint32_t size)
{
bmkt_msg_t *msg;
cmd_id_user_t *id_user;
MBED_ASSERT(size > 0);
switch (_rx_buffer[0]) {
case SENSOR_CMD_GET_VERSION:
MBED_ASSERT(size == 1);
_send_buffer(version_data, sizeof(version_data));
Serial1.println("> GET_VERSION -> VERSION(...)");
break;
case SENSOR_CMD_ACE_COMMAND:
msg = (bmkt_msg_t*)(&_rx_buffer[1]);
MBED_ASSERT(msg->header_id == BMKT_MESSAGE_HEADER_ID);
switch (msg->msg_id) {
case BMKT_CMD_FPS_INIT:
MBED_ASSERT(size == 1 + sizeof(msg));
resp = {
.padding = 0,
.header_id = BMKT_MESSAGE_HEADER_ID,
.seq_num = msg->seq_num,
.msg_id = BMKT_RSP_FPS_INIT_OK,
.payload_len = 1,
};
resp.payload[0] = 0;
_send_buffer((uint8_t*)&resp, RESP_BASE_LEN + 1);
Serial1.println("> FPS_INIT() -> FPS_INIT_OK(0)");
break;
case BMKT_CMD_GET_TEMPLATE_RECORDS:
// Report 0 enrolled fingers if queried
MBED_ASSERT(size == 1 + sizeof(msg));
resp = {
.padding = 0,
.header_id = BMKT_MESSAGE_HEADER_ID,
.seq_num = msg->seq_num,
.msg_id = BMKT_RSP_QUERY_RESPONSE_COMPLETE,
.payload_len = 0,
};
_send_buffer((uint8_t*)&resp, RESP_BASE_LEN);
Serial1.println("> GET_TEMPLATE_RECORDS() -> QUERY_RESPONSE_COMPLETE()");
break;
case BMKT_CMD_ID_USER_IN_ORDER:
// Save first UID only
id_user = (cmd_id_user_t*)(msg->payload);
MBED_ASSERT(id_user->payload_ct == 1);
resp = {
.padding = 0,
.header_id = BMKT_MESSAGE_HEADER_ID,
.seq_num = msg->seq_num,
.msg_id = 0,
.payload_len = 0,
};
memcpy(uid, id_user->uid, id_user->uid_len);
uid_len = id_user->uid_len;
remaining = id_user->total_ct - id_user->payload_ct;
if (remaining > 0) {
resp.msg_id = BMKT_RSP_SEND_NEXT_USER_ID;
} else {
resp.msg_id = BMKT_RSP_ID_READY;
}
_send_buffer((uint8_t*)&resp, RESP_BASE_LEN);
Serial1.print("> ID_USER_IN_ORDER(");
Serial1.print(id_user->total_ct, DEC);
Serial1.print(", ");
Serial1.print(id_user->payload_ct, DEC);
Serial1.print(", ");
Serial1.print(id_user->uid_len, DEC);
Serial1.print(", \"");
_print_buffer_char(id_user->uid, id_user->uid_len);
if (remaining > 0) {
Serial1.println("\") -> SEND_NEXT_USER_ID()");
} else {
Serial1.println("\") -> ID_READY()");
}
break;
case BMKT_CMD_ID_NEXT_USER:
// XXX: we *should* respond with ID_READY
// but short-circuiting to ID_OK works...
remaining -= msg->payload[0];
MBED_ASSERT(remaining == 0);
resp = {
.padding = 0,
.header_id = BMKT_MESSAGE_HEADER_ID,
.seq_num = msg->seq_num,
.msg_id = BMKT_RSP_ID_OK,
.payload_len = 3 + uid_len,
};
resp.payload[0] = 0x2b;
resp.payload[1] = 0x38;
resp.payload[2] = 0x01;
memcpy(&resp.payload[3], uid, uid_len);
_send_buffer((uint8_t*)&resp, RESP_BASE_LEN + 3 + uid_len);
Serial1.print("> ID_NEXT_USER(");
Serial1.print(msg->payload[0], DEC);
Serial1.println(", ...) -> ID_OK(56.43, 1, ...)");
break;
case BMKT_CMD_POWER_DOWN_NOTIFY:
MBED_ASSERT(size == 1 + sizeof(msg));
resp = {
.padding = 0,
.header_id = BMKT_MESSAGE_HEADER_ID,
.seq_num = msg->seq_num,
.msg_id = BMKT_RSP_POWER_DOWN_READY,
.payload_len = 0,
};
_send_buffer((uint8_t*)&resp, RESP_BASE_LEN);
Serial1.println("> POWER_DOWN_NOTIFY() -> POWER_DOWN_READY()");
break;
default:
Serial1.print("! UNKNOWN:ACE:0x");
Serial1.println(msg->msg_id, HEX);
break;
}
break;
case 0x8E:
MBED_ASSERT(size > 1);
switch (_rx_buffer[1]) {
case 0x09:
_send_buffer(resp8e09, sizeof(resp8e09));
Serial1.println("> 8E:09() -> ...");
break;
case 0x1A:
_send_buffer(resp8e1a, sizeof(resp8e1a));
Serial1.println("> 8E:1A() -> ...");
break;
case 0x2F:
_send_buffer(resp8e2f, sizeof(resp8e2f));
Serial1.println("> 8E:2F() -> ...");
break;
default:
Serial1.print("! UNKNOWN:0x8E:0x");
Serial1.println(msg->msg_id, HEX);
break;
}
break;
case 0xAF:
_send_buffer(respaf01, sizeof(respaf01));
Serial1.println("> AF:01() -> ...");
break;
case 0x19:
_send_buffer(resp19, sizeof(resp19));
Serial1.println("> 19() -> ...");
break;
default:
Serial1.print("! UNKNOWN:SENSOR:0x");
Serial1.println(_rx_buffer[0], HEX);
break;
}
}
void USBFprint::_send_buffer(uint8_t *buffer, uint32_t size)
{
Serial1.print("send");
_print_buffer_hex(buffer, size);
Serial1.println();
write_start(_bulk_in, buffer, size);
}
void USBFprint::_print_buffer_hex(uint8_t *buffer, uint32_t size)
{
Serial1.print("(");
Serial1.print(size, DEC);
Serial1.print("): 0x");
for (int i = 0; i < size; i++) {
if (buffer[i] < 0x10) {
Serial1.print("0");
}
Serial1.print(buffer[i], HEX);
Serial1.print(" ");
}
}
void USBFprint::_print_buffer_char(uint8_t *buffer, uint32_t size)
{
for (int i = 0; i < size; i++) {
Serial1.print((char)buffer[i]);
}
}
const uint8_t *USBFprint::string_iserial_desc()
{
static const uint8_t stringIserialDescriptor[] = {
0x1a,
STRING_DESCRIPTOR,
'1', 0, '2', 0, '3', 0, '4', 0, '5', 0, '6', 0, '7', 0, '8', 0, '9', 0, 'a', 0, 'b', 0, 'c', 0,
};
return stringIserialDescriptor;
}
const uint8_t *USBFprint::device_desc()
{
uint8_t ep0_size = endpoint_max_packet_size(0x00);
uint8_t device_descriptor_temp[] = {
18, // bLength
1, // bDescriptorType
0x00, 0x02, // bcdUSB
255, // bDeviceClass
16, // bDeviceSubClass
255, // bDeviceProtocol
ep0_size, // bMaxPacketSize0 // XXX: should be 8, not 64
(uint8_t)(LSB(vendor_id)), (uint8_t)(MSB(vendor_id)), // idVendor
(uint8_t)(LSB(product_id)), (uint8_t)(MSB(product_id)),// idProduct
0x00, 0x00, // bcdDevice
0, // iManufacturer
0, // iProduct
STRING_OFFSET_ISERIAL,// iSerialNumber
1 // bNumConfigurations
};
MBED_ASSERT(sizeof(device_descriptor_temp) == sizeof(device_descriptor));
memcpy(device_descriptor, device_descriptor_temp, sizeof(device_descriptor));
return device_descriptor;
}
#define CONFIG1_DESC_SIZE (9+9+7+7+7)
const uint8_t *USBFprint::configuration_desc(uint8_t index)
{
uint8_t config_descriptor_temp[] = {
// configuration descriptor
9, // bLength
2, // bDescriptorType
LSB(CONFIG1_DESC_SIZE), // wTotalLength
MSB(CONFIG1_DESC_SIZE),
1, // bNumInterfaces
1, // bConfigurationValue
0, // iConfiguration
0xa0, // bmAttributes
50, // bMaxPower
// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
9, // bLength
4, // bDescriptorType
0, // bInterfaceNumber
0, // bAlternateSetting
3, // bNumEndpoints
255, // bInterfaceClass
0, // bInterfaceSubClass
0, // bInterfaceProtocol
0, // iInterface
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_bulk_out, // bEndpointAddress
E_BULK, // bmAttributes (0x02=bulk)
LSB(BULK_MAX_PACKET_SIZE), // wMaxPacketSize (LSB)
MSB(BULK_MAX_PACKET_SIZE), // wMaxPacketSize (MSB)
0, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_bulk_in, // bEndpointAddress
E_BULK, // bmAttributes (0x02=bulk)
LSB(BULK_MAX_PACKET_SIZE), // wMaxPacketSize (LSB)
MSB(BULK_MAX_PACKET_SIZE), // wMaxPacketSize (MSB)
0, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_int_in, // bEndpointAddress // XXX: should be 0x83, not 0x82
E_INTERRUPT, // bmAttributes (0x03=intr)
LSB(INT_MAX_PACKET_SIZE), // wMaxPacketSize (LSB)
MSB(INT_MAX_PACKET_SIZE), // wMaxPacketSize (MSB)
4 // bInterval
};
if (index == 0) {
MBED_ASSERT(sizeof(config_descriptor_temp) == sizeof(_config_descriptor));
memcpy(_config_descriptor, config_descriptor_temp, sizeof(_config_descriptor));
return _config_descriptor;
} else {
return NULL;
}
}
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef USBFPRINT_H
#define USBFPRINT_H
/* These headers are included for child class. */
#include "USBDescriptor.h"
#include "USBDevice_Types.h"
#include "USBDevice.h"
#include "OperationList.h"
/**
* \defgroup drivers_USBFprint USBFprint class
* \ingroup drivers-public-api-usb
* @{
*/
// XXX: Synaptics lies, says 64 but actually transfers packets of 206+
// unfortunately mbedOS checks
#define BULK_MAX_PACKET_SIZE 256
#define INT_MAX_PACKET_SIZE 8
#define SENSOR_CMD_GET_VERSION 0x01
#define SENSOR_CMD_ACE_COMMAND 0xA7
#define SENSOR_CMD_ASYNCMSG_READ 0xA8
#define BMKT_MESSAGE_HEADER_ID 0xFE
#define BMKT_CMD_FPS_INIT 0x11
#define BMKT_RSP_FPS_INIT_OK 0x13
#define BMKT_RSP_FPS_INIT_FAIL 0x12
#define BMKT_CMD_GET_TEMPLATE_RECORDS 0x71
#define BMKT_RSP_QUERY_RESPONSE_COMPLETE 0x76
#define BMKT_CMD_POWER_DOWN_NOTIFY 0xA1
#define BMKT_RSP_POWER_DOWN_READY 0xA2
#define BMKT_RSP_POWER_DOWN_FAIL 0xA3
#define BMKT_CMD_ID_USER_IN_ORDER 0xE1
#define BMKT_CMD_ID_NEXT_USER 0xE3
#define BMKT_RSP_SEND_NEXT_USER_ID 0xE2
#define BMKT_RSP_ID_READY 0x62
#define BMKT_RSP_ID_OK 0x64
typedef struct {
uint8_t header_id;
uint8_t seq_num;
uint8_t msg_id;
uint8_t payload_len;
uint8_t payload[64];
} bmkt_msg_t;
typedef struct {
uint16_t padding;
uint8_t header_id;
uint8_t seq_num;
uint8_t msg_id;
uint8_t payload_len;
uint8_t payload[64];
} bmkt_resp_t;
#define RESP_BASE_LEN 6
typedef struct {
uint8_t total_ct;
uint8_t payload_ct;
// XXX: we don't support payloads containing multiple UIDs
uint8_t uid_len;
uint8_t uid[64];
} cmd_id_user_t;
class USBFprint: public USBDevice {
public:
/**
* Basic constructor
*
* Construct this object optionally connecting and blocking until it is ready.
*
* @note Do not use this constructor in derived classes.
*
* @param connect_blocking true to perform a blocking connect, false to start in a disconnected state
* @param vendor_id Your vendor_id
* @param product_id Your product_id
* @param product_release Your product_release
*/
USBFprint(bool connect_blocking = true, uint16_t vendor_id = 0x06cb, uint16_t product_id = 0x00bd, uint16_t product_release = 0x0001);
/**
* Fully featured constructor
*
* Construct this object with the supplied USBPhy and parameters. The user
* this object is responsible for calling connect() or init().
*
* @note Derived classes must use this constructor and call init() or
* connect() themselves. Derived classes should also call deinit() in
* their destructor. This ensures that no interrupts can occur when the
* object is partially constructed or destroyed.
*
* @param phy USB phy to use
* @param vendor_id Your vendor_id
* @param product_id Your product_id
* @param product_release Your product_release
*/
USBFprint(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release);
/**
* Destroy this object
*
* Any classes which inherit from this class must call deinit
* before this destructor runs.
*/
virtual ~USBFprint();
protected:
virtual const uint8_t *string_iserial_desc();
virtual const uint8_t *device_desc();
virtual const uint8_t *configuration_desc(uint8_t index);
protected:
virtual void callback_reset() {};
virtual void callback_state_change(DeviceState new_state);
virtual void callback_request(const setup_packet_t *setup);
virtual void callback_request_xfer_done(const setup_packet_t *setup, bool aborted);
virtual void callback_set_configuration(uint8_t configuration);
virtual void callback_set_interface(uint16_t interface, uint8_t alternate);
void _init();
void _send_isr_start();
void _send_isr();
void _receive_isr_start();
void _receive_isr();
void _handle(uint32_t size);
void _send_buffer(uint8_t *buffer, uint32_t size);
void _print_buffer_hex(uint8_t *buffer, uint32_t size);
void _print_buffer_char(uint8_t *buffer, uint32_t size);
usb_ep_t _bulk_in;
usb_ep_t _bulk_out;
usb_ep_t _int_in;
uint8_t _config_descriptor[39];
uint8_t _tx_buffer[BULK_MAX_PACKET_SIZE];
uint8_t _rx_buffer[BULK_MAX_PACKET_SIZE];
};
/** @}*/
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment