// your best effort to read chapter 4 before asking USB questions! | // your best effort to read chapter 4 before asking USB questions! | ||||
//#define USBHOST_PRINT_DEBUG | |||||
#define USBHOST_PRINT_DEBUG | |||||
/************************************************/ | /************************************************/ | ||||
/* Data Types */ | /* Data Types */ | ||||
typedef struct Device_struct Device_t; | typedef struct Device_struct Device_t; | ||||
typedef struct Pipe_struct Pipe_t; | typedef struct Pipe_struct Pipe_t; | ||||
typedef struct Transfer_struct Transfer_t; | typedef struct Transfer_struct Transfer_t; | ||||
typedef enum { CLAIM_NO=0, CLAIM_REPORT, CLAIM_INTERFACE} hidclaim_t; | |||||
// All USB device drivers inherit use these classes. | // All USB device drivers inherit use these classes. | ||||
// Drivers build user-visible functionality on top | // Drivers build user-visible functionality on top | ||||
/************************************************/ | /************************************************/ | ||||
/* Added Defines */ | /* Added Defines */ | ||||
/************************************************/ | /************************************************/ | ||||
// Keyboard special Keys | |||||
#define KEYD_UP 0xDA | #define KEYD_UP 0xDA | ||||
#define KEYD_DOWN 0xD9 | #define KEYD_DOWN 0xD9 | ||||
#define KEYD_LEFT 0xD8 | #define KEYD_LEFT 0xD8 | ||||
#define KEYD_F11 0xCC | #define KEYD_F11 0xCC | ||||
#define KEYD_F12 0xCD | #define KEYD_F12 0xCD | ||||
// USBSerial formats - Lets encode format into bits | |||||
// Bits: 0-4 - Number of data bits | |||||
// Bits: 5-7 - Parity (0=none, 1=odd, 2 = even) | |||||
// bits: 8-9 - Stop bits. 0=1, 1=2 | |||||
#define USBHOST_SERIAL_7E1 0x047 | |||||
#define USBHOST_SERIAL_7O1 0x027 | |||||
#define USBHOST_SERIAL_8N1 0x08 | |||||
#define USBHOST_SERIAL_8N2 0x108 | |||||
#define USBHOST_SERIAL_8E1 0x048 | |||||
#define USBHOST_SERIAL_8O1 0x028 | |||||
/************************************************/ | /************************************************/ | ||||
/* Data Structure Definitions */ | /* Data Structure Definitions */ | ||||
/************************************************/ | /************************************************/ | ||||
static void disconnect_Device(Device_t *dev); | static void disconnect_Device(Device_t *dev); | ||||
static void enumeration(const Transfer_t *transfer); | static void enumeration(const Transfer_t *transfer); | ||||
static void driver_ready_for_device(USBDriver *driver); | static void driver_ready_for_device(USBDriver *driver); | ||||
static volatile bool enumeration_busy; | |||||
public: // Maybe others may want/need to contribute memory example HID devices may want to add transfers. | |||||
static void contribute_Devices(Device_t *devices, uint32_t num); | static void contribute_Devices(Device_t *devices, uint32_t num); | ||||
static void contribute_Pipes(Pipe_t *pipes, uint32_t num); | static void contribute_Pipes(Pipe_t *pipes, uint32_t num); | ||||
static void contribute_Transfers(Transfer_t *transfers, uint32_t num); | static void contribute_Transfers(Transfer_t *transfers, uint32_t num); | ||||
static void contribute_String_Buffers(strbuf_t *strbuf, uint32_t num); | static void contribute_String_Buffers(strbuf_t *strbuf, uint32_t num); | ||||
static volatile bool enumeration_busy; | |||||
private: | private: | ||||
static void isr(); | static void isr(); | ||||
static void convertStringDescriptorToASCIIString(uint8_t string_index, Device_t *dev, const Transfer_t *transfer); | static void convertStringDescriptorToASCIIString(uint8_t string_index, Device_t *dev, const Transfer_t *transfer); | ||||
// Device drivers may inherit from this base class, if they wish to receive | // Device drivers may inherit from this base class, if they wish to receive | ||||
// HID input data fully decoded by the USBHIDParser driver | // HID input data fully decoded by the USBHIDParser driver | ||||
class USBHIDParser; | |||||
class USBHIDInput { | class USBHIDInput { | ||||
public: | public: | ||||
operator bool() { return (mydevice != nullptr); } | operator bool() { return (mydevice != nullptr); } | ||||
{ return ((mydevice == nullptr) || (mydevice->strbuf == nullptr)) ? nullptr : &mydevice->strbuf->buffer[mydevice->strbuf->iStrings[strbuf_t::STR_ID_SERIAL]]; } | { return ((mydevice == nullptr) || (mydevice->strbuf == nullptr)) ? nullptr : &mydevice->strbuf->buffer[mydevice->strbuf->iStrings[strbuf_t::STR_ID_SERIAL]]; } | ||||
private: | private: | ||||
virtual bool claim_collection(Device_t *dev, uint32_t topusage); | |||||
virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage); | |||||
virtual bool hid_process_in_data(const Transfer_t *transfer) {return false;} | |||||
virtual bool hid_process_out_data(const Transfer_t *transfer) {return false;} | |||||
virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax); | virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax); | ||||
virtual void hid_input_data(uint32_t usage, int32_t value); | virtual void hid_input_data(uint32_t usage, int32_t value); | ||||
virtual void hid_input_end(); | virtual void hid_input_end(); | ||||
//-------------------------------------------------------------------------- | //-------------------------------------------------------------------------- | ||||
class USBHIDParser : public USBDriver { | class USBHIDParser : public USBDriver { | ||||
public: | public: | ||||
USBHIDParser(USBHost &host) { init(); } | USBHIDParser(USBHost &host) { init(); } | ||||
static void driver_ready_for_hid_collection(USBHIDInput *driver); | static void driver_ready_for_hid_collection(USBHIDInput *driver); | ||||
bool sendPacket(const uint8_t *buffer); | |||||
protected: | protected: | ||||
enum { TOPUSAGE_LIST_LEN = 4 }; | enum { TOPUSAGE_LIST_LEN = 4 }; | ||||
enum { USAGE_LIST_LEN = 24 }; | enum { USAGE_LIST_LEN = 24 }; | ||||
USBHIDInput * find_driver(uint32_t topusage); | USBHIDInput * find_driver(uint32_t topusage); | ||||
void parse(uint16_t type_and_report_id, const uint8_t *data, uint32_t len); | void parse(uint16_t type_and_report_id, const uint8_t *data, uint32_t len); | ||||
void init(); | void init(); | ||||
// Atempt for RAWhid to take over processing of data | |||||
// | |||||
uint16_t inSize(void) {return in_size;} | |||||
uint16_t outSize(void) {return out_size;} | |||||
uint8_t activeSendMask(void) {return txstate;} | |||||
private: | private: | ||||
Pipe_t *in_pipe; | Pipe_t *in_pipe; | ||||
Pipe_t *out_pipe; | Pipe_t *out_pipe; | ||||
Pipe_t mypipes[3] __attribute__ ((aligned(32))); | Pipe_t mypipes[3] __attribute__ ((aligned(32))); | ||||
Transfer_t mytransfers[4] __attribute__ ((aligned(32))); | Transfer_t mytransfers[4] __attribute__ ((aligned(32))); | ||||
strbuf_t mystring_bufs[1]; | strbuf_t mystring_bufs[1]; | ||||
uint8_t txstate = 0; | |||||
uint8_t *tx1 = nullptr; | |||||
uint8_t *tx2 = nullptr; | |||||
bool hid_driver_claimed_control_ = false; | |||||
}; | }; | ||||
//-------------------------------------------------------------------------- | //-------------------------------------------------------------------------- | ||||
class KeyboardController : public USBDriver /* , public USBHIDInput */ { | |||||
class KeyboardController : public USBDriver , public USBHIDInput { | |||||
public: | public: | ||||
typedef union { | typedef union { | ||||
struct { | struct { | ||||
public: | public: | ||||
KeyboardController(USBHost &host) { init(); } | KeyboardController(USBHost &host) { init(); } | ||||
KeyboardController(USBHost *host) { init(); } | KeyboardController(USBHost *host) { init(); } | ||||
int available(); | |||||
int read(); | |||||
// Some methods are in both public classes so we need to figure out which one to use | |||||
operator bool() { return (device != nullptr); } | |||||
// Main boot keyboard functions. | |||||
uint16_t getKey() { return keyCode; } | uint16_t getKey() { return keyCode; } | ||||
uint8_t getModifiers() { return modifiers; } | uint8_t getModifiers() { return modifiers; } | ||||
uint8_t getOemKey() { return keyOEM; } | uint8_t getOemKey() { return keyOEM; } | ||||
void numLock(bool f); | void numLock(bool f); | ||||
void capsLock(bool f); | void capsLock(bool f); | ||||
void scrollLock(bool f); | void scrollLock(bool f); | ||||
// Added for extras information. | |||||
void attachExtrasPress(void (*f)(uint32_t top, uint16_t code)) { extrasKeyPressedFunction = f; } | |||||
void attachExtrasRelease(void (*f)(uint32_t top, uint16_t code)) { extrasKeyReleasedFunction = f; } | |||||
enum {MAX_KEYS_DOWN=4}; | |||||
protected: | protected: | ||||
virtual bool claim(Device_t *device, int type, const uint8_t *descriptors, uint32_t len); | virtual bool claim(Device_t *device, int type, const uint8_t *descriptors, uint32_t len); | ||||
virtual void control(const Transfer_t *transfer); | virtual void control(const Transfer_t *transfer); | ||||
static void callback(const Transfer_t *transfer); | static void callback(const Transfer_t *transfer); | ||||
void new_data(const Transfer_t *transfer); | void new_data(const Transfer_t *transfer); | ||||
void init(); | void init(); | ||||
protected: // HID functions for extra keyboard data. | |||||
virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage); | |||||
virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax); | |||||
virtual void hid_input_data(uint32_t usage, int32_t value); | |||||
virtual void hid_input_end(); | |||||
virtual void disconnect_collection(Device_t *dev); | |||||
private: | private: | ||||
void update(); | void update(); | ||||
uint16_t convert_to_unicode(uint32_t mod, uint32_t key); | uint16_t convert_to_unicode(uint32_t mod, uint32_t key); | ||||
uint8_t keyOEM; | uint8_t keyOEM; | ||||
uint8_t prev_report[8]; | uint8_t prev_report[8]; | ||||
KBDLeds_t leds_ = {0}; | KBDLeds_t leds_ = {0}; | ||||
bool update_leds_ = false; | |||||
bool processing_new_data_ = false; | |||||
Pipe_t mypipes[2] __attribute__ ((aligned(32))); | Pipe_t mypipes[2] __attribute__ ((aligned(32))); | ||||
Transfer_t mytransfers[4] __attribute__ ((aligned(32))); | Transfer_t mytransfers[4] __attribute__ ((aligned(32))); | ||||
strbuf_t mystring_bufs[1]; | strbuf_t mystring_bufs[1]; | ||||
}; | |||||
//-------------------------------------------------------------------------- | |||||
class KeyboardHIDExtrasController : public USBHIDInput { | |||||
public: | |||||
KeyboardHIDExtrasController(USBHost &host) { USBHIDParser::driver_ready_for_hid_collection(this); } | |||||
void clear() { event_ = false;} | |||||
bool available() { return event_; } | |||||
void attachPress(void (*f)(uint32_t top, uint16_t code)) { keyPressedFunction = f; } | |||||
void attachRelease(void (*f)(uint32_t top, uint16_t code)) { keyReleasedFunction = f; } | |||||
enum {MAX_KEYS_DOWN=4}; | |||||
// uint32_t buttons() { return buttons_; } | |||||
protected: | |||||
virtual bool claim_collection(Device_t *dev, uint32_t topusage); | |||||
virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax); | |||||
virtual void hid_input_data(uint32_t usage, int32_t value); | |||||
virtual void hid_input_end(); | |||||
virtual void disconnect_collection(Device_t *dev); | |||||
private: | |||||
void (*keyPressedFunction)(uint32_t top, uint16_t code); | |||||
void (*keyReleasedFunction)(uint32_t top, uint16_t code); | |||||
// Added to process secondary HID data. | |||||
void (*extrasKeyPressedFunction)(uint32_t top, uint16_t code); | |||||
void (*extrasKeyReleasedFunction)(uint32_t top, uint16_t code); | |||||
uint32_t topusage_ = 0; // What top report am I processing? | uint32_t topusage_ = 0; // What top report am I processing? | ||||
uint8_t collections_claimed_ = 0; | uint8_t collections_claimed_ = 0; | ||||
volatile bool event_ = false; | |||||
volatile bool hid_input_begin_ = false; | volatile bool hid_input_begin_ = false; | ||||
volatile bool hid_input_data_ = false; // did we receive any valid data with report? | volatile bool hid_input_data_ = false; // did we receive any valid data with report? | ||||
uint8_t count_keys_down_ = 0; | uint8_t count_keys_down_ = 0; | ||||
uint16_t keys_down[MAX_KEYS_DOWN]; | uint16_t keys_down[MAX_KEYS_DOWN]; | ||||
}; | }; | ||||
//-------------------------------------------------------------------------- | |||||
class MouseController : public USBHIDInput { | class MouseController : public USBHIDInput { | ||||
public: | public: | ||||
int getWheel() { return wheel; } | int getWheel() { return wheel; } | ||||
int getWheelH() { return wheelH; } | int getWheelH() { return wheelH; } | ||||
protected: | protected: | ||||
virtual bool claim_collection(Device_t *dev, uint32_t topusage); | |||||
virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage); | |||||
virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax); | virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax); | ||||
virtual void hid_input_data(uint32_t usage, int32_t value); | virtual void hid_input_data(uint32_t usage, int32_t value); | ||||
virtual void hid_input_end(); | virtual void hid_input_end(); | ||||
//-------------------------------------------------------------------------- | //-------------------------------------------------------------------------- | ||||
class JoystickController : public USBHIDInput { | |||||
class JoystickController : public USBDriver, public USBHIDInput { | |||||
public: | public: | ||||
JoystickController(USBHost &host) { USBHIDParser::driver_ready_for_hid_collection(this); } | |||||
JoystickController(USBHost &host) { init(); } | |||||
uint16_t idVendor(); | |||||
uint16_t idProduct(); | |||||
const uint8_t *manufacturer(); | |||||
const uint8_t *product(); | |||||
const uint8_t *serialNumber(); | |||||
operator bool() { return ((device != nullptr) || (mydevice != nullptr)); } // override as in both USBDriver and in USBHIDInput | |||||
bool available() { return joystickEvent; } | bool available() { return joystickEvent; } | ||||
void joystickDataClear(); | void joystickDataClear(); | ||||
uint32_t getButtons() { return buttons; } | uint32_t getButtons() { return buttons; } | ||||
int getAxis(uint32_t index) { return (index < (sizeof(axis)/sizeof(axis[0]))) ? axis[index] : 0; } | int getAxis(uint32_t index) { return (index < (sizeof(axis)/sizeof(axis[0]))) ? axis[index] : 0; } | ||||
uint32_t axisMask() {return axis_mask_;} | |||||
enum { AXIS_COUNT = 10 }; | |||||
protected: | protected: | ||||
virtual bool claim_collection(Device_t *dev, uint32_t topusage); | |||||
// From USBDriver | |||||
virtual bool claim(Device_t *device, int type, const uint8_t *descriptors, uint32_t len); | |||||
virtual void control(const Transfer_t *transfer); | |||||
virtual void disconnect(); | |||||
// From USBHIDInput | |||||
virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage); | |||||
virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax); | virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax); | ||||
virtual void hid_input_data(uint32_t usage, int32_t value); | virtual void hid_input_data(uint32_t usage, int32_t value); | ||||
virtual void hid_input_end(); | virtual void hid_input_end(); | ||||
virtual void disconnect_collection(Device_t *dev); | virtual void disconnect_collection(Device_t *dev); | ||||
private: | private: | ||||
uint8_t collections_claimed = 0; | |||||
// Class specific | |||||
void init(); | |||||
bool anychange = false; | bool anychange = false; | ||||
volatile bool joystickEvent = false; | volatile bool joystickEvent = false; | ||||
uint32_t buttons = 0; | uint32_t buttons = 0; | ||||
int16_t axis[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; | |||||
int axis[AXIS_COUNT] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; | |||||
uint32_t axis_mask_ = 0; // which axis have valid data | |||||
// Used by HID code | |||||
uint8_t collections_claimed = 0; | |||||
// Used by USBDriver code | |||||
static void rx_callback(const Transfer_t *transfer); | |||||
static void tx_callback(const Transfer_t *transfer); | |||||
void rx_data(const Transfer_t *transfer); | |||||
void tx_data(const Transfer_t *transfer); | |||||
Pipe_t mypipes[3] __attribute__ ((aligned(32))); | |||||
Transfer_t mytransfers[7] __attribute__ ((aligned(32))); | |||||
strbuf_t mystring_bufs[1]; | |||||
uint16_t rx_size_ = 0; | |||||
uint16_t tx_size_ = 0; | |||||
Pipe_t *rxpipe_; | |||||
Pipe_t *txpipe_; | |||||
uint8_t rxbuf_[64]; // receive circular buffer | |||||
// Mapping table to say which devices we handle | |||||
typedef struct { | |||||
uint16_t idVendor; | |||||
uint16_t idProduct; | |||||
} product_vendor_mapping_t; | |||||
static product_vendor_mapping_t pid_vid_mapping[]; | |||||
}; | }; | ||||
//-------------------------------------------------------------------------- | //-------------------------------------------------------------------------- | ||||
class MIDIDevice : public USBDriver { | class MIDIDevice : public USBDriver { | ||||
//-------------------------------------------------------------------------- | //-------------------------------------------------------------------------- | ||||
class USBSerial: public USBDriver, public Stream { | class USBSerial: public USBDriver, public Stream { | ||||
public: | |||||
public: | |||||
// FIXME: need different USBSerial, with bigger buffers for 480 Mbit & faster speed | // FIXME: need different USBSerial, with bigger buffers for 480 Mbit & faster speed | ||||
enum { BUFFER_SIZE = 648 }; // must hold at least 6 max size packets, plus 2 extra bytes | enum { BUFFER_SIZE = 648 }; // must hold at least 6 max size packets, plus 2 extra bytes | ||||
enum { DEFAULT_WRITE_TIMEOUT = 3500}; | |||||
USBSerial(USBHost &host) : txtimer(this) { init(); } | USBSerial(USBHost &host) : txtimer(this) { init(); } | ||||
void begin(uint32_t baud, uint32_t format=0); | |||||
void begin(uint32_t baud, uint32_t format=USBHOST_SERIAL_8N1); | |||||
void end(void); | void end(void); | ||||
uint32_t writeTimeout() {return write_timeout_;} | |||||
void writeTimeOut(uint32_t write_timeout) {write_timeout_ = write_timeout;} // Will not impact current ones. | |||||
virtual int available(void); | virtual int available(void); | ||||
virtual int peek(void); | virtual int peek(void); | ||||
virtual int read(void); | virtual int read(void); | ||||
virtual int availableForWrite(); | virtual int availableForWrite(); | ||||
virtual size_t write(uint8_t c); | virtual size_t write(uint8_t c); | ||||
virtual void flush(void); | |||||
using Print::write; | using Print::write; | ||||
protected: | protected: | ||||
void init(); | void init(); | ||||
static bool check_rxtx_ep(uint32_t &rxep, uint32_t &txep); | static bool check_rxtx_ep(uint32_t &rxep, uint32_t &txep); | ||||
bool init_buffers(uint32_t rsize, uint32_t tsize); | bool init_buffers(uint32_t rsize, uint32_t tsize); | ||||
void ch341_setBaud(uint8_t byte_index); | |||||
private: | private: | ||||
Pipe_t mypipes[3] __attribute__ ((aligned(32))); | Pipe_t mypipes[3] __attribute__ ((aligned(32))); | ||||
Transfer_t mytransfers[7] __attribute__ ((aligned(32))); | Transfer_t mytransfers[7] __attribute__ ((aligned(32))); | ||||
USBDriverTimer txtimer; | USBDriverTimer txtimer; | ||||
uint32_t bigbuffer[(BUFFER_SIZE+3)/4]; | uint32_t bigbuffer[(BUFFER_SIZE+3)/4]; | ||||
setup_t setup; | setup_t setup; | ||||
uint8_t setupdata[8]; | |||||
uint8_t setupdata[16]; // | |||||
uint32_t baudrate; | uint32_t baudrate; | ||||
uint32_t format_; | |||||
uint32_t write_timeout_ = DEFAULT_WRITE_TIMEOUT; | |||||
Pipe_t *rxpipe; | Pipe_t *rxpipe; | ||||
Pipe_t *txpipe; | Pipe_t *txpipe; | ||||
uint8_t *rx1; // location for first incoming packet | uint8_t *rx1; // location for first incoming packet | ||||
uint8_t pl2303_v2; | uint8_t pl2303_v2; | ||||
uint8_t interface; | uint8_t interface; | ||||
bool control_queued; | bool control_queued; | ||||
enum { CDCACM, FTDI, PL2303, CH341 } sertype; | |||||
typedef enum { UNKNOWN=0, CDCACM, FTDI, PL2303, CH341, CP210X } sertype_t; | |||||
sertype_t sertype; | |||||
typedef struct { | |||||
uint16_t idVendor; | |||||
uint16_t idProduct; | |||||
sertype_t sertype; | |||||
} product_vendor_mapping_t; | |||||
static product_vendor_mapping_t pid_vid_mapping[]; | |||||
}; | }; | ||||
//-------------------------------------------------------------------------- | //-------------------------------------------------------------------------- | ||||
uint16_t wheelCircumference; // default is WHEEL_CIRCUMFERENCE (2122cm) | uint16_t wheelCircumference; // default is WHEEL_CIRCUMFERENCE (2122cm) | ||||
}; | }; | ||||
//-------------------------------------------------------------------------- | |||||
class RawHIDController : public USBHIDInput { | |||||
public: | |||||
RawHIDController(USBHost &host, uint32_t usage = 0) : fixed_usage_(usage) { init(); } | |||||
uint32_t usage(void) {return usage_;} | |||||
void attachReceive(bool (*f)(uint32_t usage, const uint8_t *data, uint32_t len)) {receiveCB = f;} | |||||
bool sendPacket(const uint8_t *buffer); | |||||
protected: | |||||
virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage); | |||||
virtual bool hid_process_in_data(const Transfer_t *transfer); | |||||
virtual bool hid_process_out_data(const Transfer_t *transfer); | |||||
virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax); | |||||
virtual void hid_input_data(uint32_t usage, int32_t value); | |||||
virtual void hid_input_end(); | |||||
virtual void disconnect_collection(Device_t *dev); | |||||
private: | |||||
void init(); | |||||
USBHIDParser *driver_; | |||||
enum { MAX_PACKET_SIZE = 64 }; | |||||
bool (*receiveCB)(uint32_t usage, const uint8_t *data, uint32_t len) = nullptr; | |||||
uint8_t collections_claimed = 0; | |||||
//volatile bool hid_input_begin_ = false; | |||||
uint32_t fixed_usage_; | |||||
uint32_t usage_ = 0; | |||||
// See if we can contribute transfers | |||||
Transfer_t mytransfers[2] __attribute__ ((aligned(32))); | |||||
}; | |||||
#endif | #endif |
// TODO: does this write interfere UPI & UAI (bits 18 & 19) ?? | // TODO: does this write interfere UPI & UAI (bits 18 & 19) ?? | ||||
} | } | ||||
// find & free all the transfers which completed | // find & free all the transfers which completed | ||||
println(" Free transfers"); | |||||
Transfer_t *t = async_followup_first; | Transfer_t *t = async_followup_first; | ||||
while (t) { | while (t) { | ||||
print(" * ", (uint32_t)t); | |||||
Transfer_t *next = t->next_followup; | Transfer_t *next = t->next_followup; | ||||
if (t->pipe == pipe) { | if (t->pipe == pipe) { | ||||
print(" * remove"); | |||||
remove_from_async_followup_list(t); | remove_from_async_followup_list(t); | ||||
free_Transfer(t); | |||||
// Only free if not in QH list | |||||
Transfer_t *tr = (Transfer_t *)(pipe->qh.next); | |||||
while (((uint32_t)tr & 0xFFFFFFE0) && (tr != t)){ | |||||
tr = (Transfer_t *)(tr->qtd.next); | |||||
} | |||||
if (tr == t) { | |||||
println(" * defer free until QH"); | |||||
} else { | |||||
println(" * free"); | |||||
free_Transfer(t); // The later code should actually free it... | |||||
} | |||||
} else { | |||||
println(""); | |||||
} | } | ||||
t = next; | t = next; | ||||
} | } | ||||
// TODO: subtract bandwidth from uframe_bandwidth array | // TODO: subtract bandwidth from uframe_bandwidth array | ||||
// find & free all the transfers which completed | // find & free all the transfers which completed | ||||
println(" Free transfers"); | |||||
Transfer_t *t = periodic_followup_first; | Transfer_t *t = periodic_followup_first; | ||||
while (t) { | while (t) { | ||||
print(" * ", (uint32_t)t); | |||||
Transfer_t *next = t->next_followup; | Transfer_t *next = t->next_followup; | ||||
if (t->pipe == pipe) { | if (t->pipe == pipe) { | ||||
print(" * remove"); | |||||
remove_from_periodic_followup_list(t); | remove_from_periodic_followup_list(t); | ||||
free_Transfer(t); | |||||
// Only free if not in QH list | |||||
Transfer_t *tr = (Transfer_t *)(pipe->qh.next); | |||||
while (((uint32_t)tr & 0xFFFFFFE0) && (tr != t)){ | |||||
tr = (Transfer_t *)(tr->qtd.next); | |||||
} | |||||
if (tr == t) { | |||||
println(" * defer free until QH"); | |||||
} else { | |||||
println(" * free"); | |||||
free_Transfer(t); // The later code should actually free it... | |||||
} | |||||
} else { | |||||
println(""); | |||||
} | } | ||||
t = next; | t = next; | ||||
} | } | ||||
// TODO: do we need to look at pipe->qh.current ?? | // TODO: do we need to look at pipe->qh.current ?? | ||||
// | // | ||||
// free all the transfers still attached to the QH | // free all the transfers still attached to the QH | ||||
println(" Free transfers attached to QH"); | |||||
Transfer_t *tr = (Transfer_t *)(pipe->qh.next); | Transfer_t *tr = (Transfer_t *)(pipe->qh.next); | ||||
while ((uint32_t)tr & 0xFFFFFFE0) { | while ((uint32_t)tr & 0xFFFFFFE0) { | ||||
println(" * ", (uint32_t)tr); | |||||
Transfer_t *next = (Transfer_t *)(tr->qtd.next); | Transfer_t *next = (Transfer_t *)(tr->qtd.next); | ||||
free_Transfer(tr); | free_Transfer(tr); | ||||
tr = next; | tr = next; | ||||
} | } | ||||
// hopefully we found everything... | // hopefully we found everything... | ||||
free_Pipe(pipe); | free_Pipe(pipe); | ||||
println("* Delete Pipe completed"); | |||||
} | } | ||||
USBHost myusb; | USBHost myusb; | ||||
USBHub hub1(myusb); | USBHub hub1(myusb); | ||||
USBHub hub2(myusb); | USBHub hub2(myusb); | ||||
USBHub hub3(myusb); | |||||
USBHub hub4(myusb); | |||||
KeyboardController keyboard1(myusb); | KeyboardController keyboard1(myusb); | ||||
KeyboardController keyboard2(myusb); | KeyboardController keyboard2(myusb); | ||||
KeyboardHIDExtrasController hidextras(myusb); | |||||
//KeyboardHIDExtrasController hidextras(myusb); | |||||
USBHIDParser hid1(myusb); | USBHIDParser hid1(myusb); | ||||
USBHIDParser hid2(myusb); | USBHIDParser hid2(myusb); | ||||
USBHIDParser hid3(myusb); | USBHIDParser hid3(myusb); | ||||
USBHIDParser hid5(myusb); | USBHIDParser hid5(myusb); | ||||
MouseController mouse1(myusb); | MouseController mouse1(myusb); | ||||
JoystickController joystick1(myusb); | JoystickController joystick1(myusb); | ||||
RawHIDController rawhid1(myusb); | |||||
RawHIDController rawhid2(myusb, 0xffc90004); | |||||
USBDriver *drivers[] = {&hub1, &hub2, &hub3, &hub4, &keyboard1, &keyboard2, &hid1, &hid2, &hid3, &hid4, &hid5}; | |||||
USBDriver *drivers[] = {&hub1, &hub2,&keyboard1, &keyboard2, &joystick1, &hid1, &hid2, &hid3, &hid4, &hid5}; | |||||
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0])) | #define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0])) | ||||
const char * driver_names[CNT_DEVICES] = {"Hub1","Hub2", "Hub3", "Hub4" "KB1", "KB2", "HID1", "HID2", "HID3", "HID4", "HID5" }; | |||||
const char * driver_names[CNT_DEVICES] = {"Hub1","Hub2", "KB1", "KB2", "JOY1D", "HID1", "HID2", "HID3", "HID4", "HID5"}; | |||||
bool driver_active[CNT_DEVICES] = {false, false, false, false}; | bool driver_active[CNT_DEVICES] = {false, false, false, false}; | ||||
// Lets also look at HID Input devices | // Lets also look at HID Input devices | ||||
USBHIDInput *hiddrivers[] = {&mouse1, &joystick1}; | |||||
USBHIDInput *hiddrivers[] = {&mouse1, &joystick1, &rawhid1, &rawhid2}; | |||||
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0])) | #define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0])) | ||||
const char * hid_driver_names[CNT_DEVICES] = {"Mouse1","Joystick1"}; | |||||
const char * hid_driver_names[CNT_DEVICES] = {"Mouse1","Joystick1", "RawHid1", "RawHid2"}; | |||||
bool hid_driver_active[CNT_DEVICES] = {false, false}; | bool hid_driver_active[CNT_DEVICES] = {false, false}; | ||||
myusb.begin(); | myusb.begin(); | ||||
keyboard1.attachPress(OnPress); | keyboard1.attachPress(OnPress); | ||||
keyboard2.attachPress(OnPress); | keyboard2.attachPress(OnPress); | ||||
hidextras.attachPress(OnHIDExtrasPress); | |||||
hidextras.attachRelease(OnHIDExtrasRelease); | |||||
keyboard1.attachExtrasPress(OnHIDExtrasPress); | |||||
keyboard1.attachExtrasRelease(OnHIDExtrasRelease); | |||||
keyboard2.attachExtrasPress(OnHIDExtrasPress); | |||||
keyboard2.attachExtrasRelease(OnHIDExtrasRelease); | |||||
rawhid1.attachReceive(OnReceiveHidData); | |||||
rawhid2.attachReceive(OnReceiveHidData); | |||||
} | } | ||||
mouse1.mouseDataClear(); | mouse1.mouseDataClear(); | ||||
} | } | ||||
if (joystick1.available()) { | if (joystick1.available()) { | ||||
uint32_t axis_mask = joystick1.axisMask(); | |||||
Serial.print("Joystick: buttons = "); | Serial.print("Joystick: buttons = "); | ||||
Serial.print(joystick1.getButtons(), HEX); | Serial.print(joystick1.getButtons(), HEX); | ||||
Serial.print(", X = "); | |||||
Serial.print(joystick1.getAxis(0)); | |||||
Serial.print(", Y = "); | |||||
Serial.print(joystick1.getAxis(1)); | |||||
Serial.print(", Z = "); | |||||
Serial.print(joystick1.getAxis(2)); | |||||
Serial.print(", Rz = "); | |||||
Serial.print(joystick1.getAxis(5)); | |||||
Serial.print(", Rx = "); | |||||
Serial.print(joystick1.getAxis(3)); | |||||
Serial.print(", Ry = "); | |||||
Serial.print(joystick1.getAxis(4)); | |||||
Serial.print(", Hat = "); | |||||
Serial.print(joystick1.getAxis(9)); | |||||
for (uint8_t i = 0; axis_mask != 0; i++, axis_mask >>= 1) { | |||||
if (axis_mask & 1) { | |||||
Serial.printf(" %d:%d", i, joystick1.getAxis(i)); | |||||
} | |||||
} | |||||
Serial.println(); | Serial.println(); | ||||
joystick1.joystickDataClear(); | joystick1.joystickDataClear(); | ||||
} | } | ||||
// See if we have some RAW data | |||||
if (rawhid1) { | |||||
int ch; | |||||
uint8_t buffer[64]; | |||||
uint8_t count_chars = 0; | |||||
memset(buffer, 0, sizeof(buffer)); | |||||
if (Serial.available()) { | |||||
while (((ch = Serial.read()) != -1) && (count_chars < sizeof(buffer))) { | |||||
buffer[count_chars++] = ch; | |||||
} | |||||
rawhid1.sendPacket(buffer); | |||||
} | |||||
} | |||||
} | } | ||||
Serial.print(") key release:"); | Serial.print(") key release:"); | ||||
Serial.println(key, HEX); | Serial.println(key, HEX); | ||||
} | } | ||||
bool OnReceiveHidData(uint32_t usage, const uint8_t *data, uint32_t len) { | |||||
// Called for maybe both HIDS for rawhid basic test. One is for the Teensy | |||||
// to output to Serial. while still having Raw Hid... | |||||
if (usage == 0xffc90004) { | |||||
// Lets trim off trailing null characters. | |||||
while ((len > 0) && (data[len-1] == 0)) { | |||||
len--; | |||||
} | |||||
if (len) { | |||||
Serial.print("RawHid Serial: "); | |||||
Serial.write(data, len); | |||||
} | |||||
} else { | |||||
Serial.print("RawHID data: "); | |||||
Serial.println(usage, HEX); | |||||
while (len) { | |||||
uint8_t cb = (len > 16)? 16 : len; | |||||
const uint8_t *p = data; | |||||
uint8_t i; | |||||
for (i = 0; i < cb; i++) { | |||||
Serial.printf("%02x ", *p++); | |||||
} | |||||
Serial.print(": "); | |||||
for (i = 0; i < cb; i++) { | |||||
Serial.write(((*data >= ' ')&&(*data <= '~'))? *data : '.'); | |||||
data++; | |||||
} | |||||
len -= cb; | |||||
Serial.println(); | |||||
} | |||||
} | |||||
return true; | |||||
} |
#include "USBHost_t36.h" | #include "USBHost_t36.h" | ||||
#define USBBAUD 115200 | #define USBBAUD 115200 | ||||
uint32_t baud = USBBAUD; | |||||
uint32_t format = USBHOST_SERIAL_8N1; | |||||
USBHost myusb; | USBHost myusb; | ||||
USBHub hub1(myusb); | USBHub hub1(myusb); | ||||
USBHub hub2(myusb); | USBHub hub2(myusb); | ||||
// If this is a new Serial device. | // If this is a new Serial device. | ||||
if (drivers[i] == &userial) { | if (drivers[i] == &userial) { | ||||
// Lets try first outputting something to our USerial to see if it will go out... | // Lets try first outputting something to our USerial to see if it will go out... | ||||
userial.begin(USBBAUD); | |||||
// delay(5); | |||||
// userial.println("ver"); | |||||
#if 0 | |||||
userial.println("abcdefghijklmnopqrstuvwxyz"); | |||||
userial.println("ABCDEFGHIJKLMNOPQURSTUVWYZ"); | |||||
userial.flush(); // force it out now. | |||||
userial.println("0123456789"); | |||||
userial.flush(); | |||||
delay(2); | |||||
userial.println("abcdefghijklmnopqrstuvwxyz"); | |||||
userial.println("ABCDEFGHIJKLMNOPQURSTUVWYZ"); | |||||
delay(2); | |||||
userial.println("!@#$%^&*()"); | |||||
userial.flush(); | |||||
#endif | |||||
userial.begin(baud); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
if (ch == '$') { | if (ch == '$') { | ||||
BioloidTest(); | BioloidTest(); | ||||
while (Serial.read() != -1); | while (Serial.read() != -1); | ||||
} else if (ch == '#') { | |||||
// Lets see if we have a baud rate specified here... | |||||
uint32_t new_baud = 0; | |||||
for(;;) { | |||||
ch = Serial.read(); | |||||
if ((ch < '0') || (ch > '9')) | |||||
break; | |||||
new_baud = new_baud*10 + ch - '0'; | |||||
} | |||||
// See if the user is specifying a format: 8n1, 7e1, 7e2, 8n2 | |||||
// Note this is Quick and very dirty code... | |||||
// | |||||
if (ch == ',') { | |||||
char command_line[10]; | |||||
ch = Serial.read(); | |||||
while (ch == ' ') Serial.read(); // ignore any spaces. | |||||
uint8_t cb = 0; | |||||
while ((ch > ' ') && (cb < sizeof(command_line))) { | |||||
command_line[cb++] = ch; | |||||
ch = Serial.read(); | |||||
} | |||||
command_line[cb] = '\0'; | |||||
if (CompareStrings(command_line, "8N1")) format = USBHOST_SERIAL_8N1; | |||||
else if (CompareStrings(command_line, "8N2")) format = USBHOST_SERIAL_8N2; | |||||
else if (CompareStrings(command_line, "7E1")) format = USBHOST_SERIAL_7E1; | |||||
else if (CompareStrings(command_line, "7O1")) format = USBHOST_SERIAL_7O1; | |||||
} | |||||
Serial.println("\n*** Set new Baud command ***\n do userial.end()"); | |||||
digitalWriteFast(2, HIGH); | |||||
userial.end(); // Do the end statement; | |||||
digitalWriteFast(2, LOW); | |||||
if (new_baud) { | |||||
baud = new_baud; | |||||
Serial.print(" New Baud: "); | |||||
Serial.println(baud); | |||||
Serial.print(" Format: "); | |||||
Serial.println(format, HEX); | |||||
digitalWriteFast(3, HIGH); | |||||
userial.begin(baud, format); | |||||
digitalWriteFast(3, LOW); | |||||
Serial.println(" Completed "); | |||||
} else { | |||||
Serial.println(" New Baud 0 - leave disabled"); | |||||
} | |||||
while (Serial.read() != -1); | |||||
} else { | |||||
userial.write(ch); | |||||
} | } | ||||
else userial.write(ch); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
bool CompareStrings(const char *sz1, const char *sz2) { | |||||
while (*sz2 != 0) { | |||||
if (toupper(*sz1) != toupper(*sz2)) | |||||
return false; | |||||
sz1++; | |||||
sz2++; | |||||
} | |||||
return true; // end of string so show as match | |||||
} | |||||
//#define ID_MASTER 200 | //#define ID_MASTER 200 | ||||
#define ID_MASTER 0xfd | #define ID_MASTER 0xfd | ||||
// Extract stuff from Bioloid library.. | // Extract stuff from Bioloid library.. | ||||
#define AX12_BUFFER_SIZE 128 | #define AX12_BUFFER_SIZE 128 | ||||
#define COUNTER_TIMEOUT 12000 | #define COUNTER_TIMEOUT 12000 | ||||
/** Instruction Set **/ | |||||
#define AX_PING 1 | |||||
/** Instruction Set **/ #define AX_PING 1 | |||||
#define AX_READ_DATA 2 | #define AX_READ_DATA 2 | ||||
#define AX_WRITE_DATA 3 | #define AX_WRITE_DATA 3 | ||||
#define AX_REG_WRITE 4 | #define AX_REG_WRITE 4 | ||||
while ((ch = userial.read()) == -1) { | while ((ch = userial.read()) == -1) { | ||||
if ((millis() - ulStart) > 10) { | if ((millis() - ulStart) > 10) { | ||||
//if (!--ulCounter) { | //if (!--ulCounter) { | ||||
// Serial.println("Timeout"); | |||||
Serial.println("Timeout"); | |||||
return 0; // Timeout | return 0; // Timeout | ||||
} | } | ||||
} | } |
if (((endpoint1 & 0xF0) == 0x80) && ((endpoint2 & 0xF0) == 0)) { | if (((endpoint1 & 0xF0) == 0x80) && ((endpoint2 & 0xF0) == 0)) { | ||||
// first endpoint is IN, second endpoint is OUT | // first endpoint is IN, second endpoint is OUT | ||||
in_pipe = new_Pipe(dev, 3, endpoint1 & 0x0F, 1, size1, interval1); | in_pipe = new_Pipe(dev, 3, endpoint1 & 0x0F, 1, size1, interval1); | ||||
//out_pipe = new_Pipe(dev, 3, endpoint2, 0, size2, interval2); | |||||
out_pipe = NULL; // TODO; fixme | |||||
out_pipe = new_Pipe(dev, 3, endpoint2, 0, size2, interval2); | |||||
in_size = size1; | in_size = size1; | ||||
out_size = size2; | out_size = size2; | ||||
} else if (((endpoint1 & 0xF0) == 0) && ((endpoint2 & 0xF0) == 0x80)) { | } else if (((endpoint1 & 0xF0) == 0) && ((endpoint2 & 0xF0) == 0x80)) { | ||||
// first endpoint is OUT, second endpoint is IN | // first endpoint is OUT, second endpoint is IN | ||||
in_pipe = new_Pipe(dev, 3, endpoint2 & 0x0F, 1, size2, interval2); | in_pipe = new_Pipe(dev, 3, endpoint2 & 0x0F, 1, size2, interval2); | ||||
//out_pipe = new_Pipe(dev, 3, endpoint1, 0, size1, interval1); | |||||
out_pipe = NULL; // TODO; fixme | |||||
out_pipe = new_Pipe(dev, 3, endpoint1, 0, size1, interval1); | |||||
in_size = size2; | in_size = size2; | ||||
out_size = size1; | out_size = size1; | ||||
} else { | } else { | ||||
return false; | return false; | ||||
} | } | ||||
//out_pipe->callback_function = out_callback; | |||||
out_pipe->callback_function = out_callback; | |||||
} | } | ||||
in_pipe->callback_function = in_callback; | in_pipe->callback_function = in_callback; | ||||
for (uint32_t i=0; i < TOPUSAGE_LIST_LEN; i++) { | for (uint32_t i=0; i < TOPUSAGE_LIST_LEN; i++) { | ||||
void USBHIDParser::out_callback(const Transfer_t *transfer) | void USBHIDParser::out_callback(const Transfer_t *transfer) | ||||
{ | { | ||||
//println("USBHIDParser:: out_callback (static)"); | |||||
if (transfer->driver) { | if (transfer->driver) { | ||||
((USBHIDParser*)(transfer->driver))->out_data(transfer); | ((USBHIDParser*)(transfer->driver))->out_data(transfer); | ||||
} | } | ||||
Serial.println(); */ | Serial.println(); */ | ||||
print("HID: "); | print("HID: "); | ||||
print(use_report_id); | |||||
print(" - "); | |||||
print_hexbytes(transfer->buffer, transfer->length); | print_hexbytes(transfer->buffer, transfer->length); | ||||
const uint8_t *buf = (const uint8_t *)transfer->buffer; | const uint8_t *buf = (const uint8_t *)transfer->buffer; | ||||
uint32_t len = transfer->length; | uint32_t len = transfer->length; | ||||
if (use_report_id == false) { | |||||
parse(0x0100, buf, len); | |||||
} else { | |||||
if (len > 1) { | |||||
parse(0x0100 | buf[0], buf + 1, len - 1); | |||||
// See if the first top report wishes to bypass the | |||||
// parse... | |||||
if (!(topusage_drivers[0] && topusage_drivers[0]->hid_process_in_data(transfer))) { | |||||
if (use_report_id == false) { | |||||
parse(0x0100, buf, len); | |||||
} else { | |||||
if (len > 1) { | |||||
parse(0x0100 | buf[0], buf + 1, len - 1); | |||||
} | |||||
} | } | ||||
} | } | ||||
queue_Data_Transfer(in_pipe, report, in_size, this); | queue_Data_Transfer(in_pipe, report, in_size, this); | ||||
void USBHIDParser::out_data(const Transfer_t *transfer) | void USBHIDParser::out_data(const Transfer_t *transfer) | ||||
{ | { | ||||
println("USBHIDParser:out_data called (instance)"); | |||||
// A packet completed. lets mark it as done and call back | |||||
// to top reports handler. We unmark our checkmark to | |||||
// handle case where they may want to queue up another one. | |||||
if (transfer->buffer == tx1) txstate &= ~1; | |||||
if (transfer->buffer == tx2) txstate &= ~2; | |||||
if (topusage_drivers[0]) { | |||||
topusage_drivers[0]->hid_process_out_data(transfer); | |||||
} | |||||
} | } | ||||
bool USBHIDParser::sendPacket(const uint8_t *buffer) { | |||||
if (!out_size || !out_pipe) return false; | |||||
if (!tx1) { | |||||
// Was not init before, for now lets put it at end of descriptor | |||||
// TODO: should verify that either don't exceed overlap descsize | |||||
// Or that we have taken over this device | |||||
tx1 = &descriptor[sizeof(descriptor) - out_size]; | |||||
tx2 = tx1 - out_size; | |||||
} | |||||
if ((txstate & 3) == 3) return false; // both transmit buffers are full | |||||
uint8_t *p = tx1; | |||||
if ((txstate & 1) == 0) { | |||||
txstate |= 1; | |||||
} else { | |||||
txstate |= 2; | |||||
p = tx2; | |||||
} | |||||
// copy the users data into our out going buffer | |||||
memcpy(p, buffer, out_size); | |||||
println("USBHIDParser Send packet"); | |||||
print_hexbytes(buffer, out_size); | |||||
queue_Data_Transfer(out_pipe, p, out_size, this); | |||||
println(" Queue_data transfer returned"); | |||||
return true; | |||||
} | |||||
// This no-inputs parse is meant to be used when we first get the | // This no-inputs parse is meant to be used when we first get the | ||||
// HID report descriptor. It finds all the top level collections | // HID report descriptor. It finds all the top level collections | ||||
// and allows drivers to claim them. This is always where we | // and allows drivers to claim them. This is always where we | ||||
{ | { | ||||
println("find_driver"); | println("find_driver"); | ||||
USBHIDInput *driver = available_hid_drivers_list; | USBHIDInput *driver = available_hid_drivers_list; | ||||
hidclaim_t claim_type; | |||||
while (driver) { | while (driver) { | ||||
println(" driver ", (uint32_t)driver, HEX); | println(" driver ", (uint32_t)driver, HEX); | ||||
if (driver->claim_collection(device, topusage)) { | |||||
if ((claim_type = driver->claim_collection(this, device, topusage)) != CLAIM_NO) { | |||||
if (claim_type == CLAIM_INTERFACE) hid_driver_claimed_control_ = true; | |||||
return driver; | return driver; | ||||
} | } | ||||
driver = driver->next; | driver = driver->next; |
#include <Arduino.h> | #include <Arduino.h> | ||||
#include "USBHost_t36.h" // Read this header first for key info | #include "USBHost_t36.h" // Read this header first for key info | ||||
#define print USBHost::print_ | |||||
#define println USBHost::println_ | |||||
void JoystickController::init() | |||||
{ | |||||
contribute_Pipes(mypipes, sizeof(mypipes)/sizeof(Pipe_t)); | |||||
contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t)); | |||||
contribute_String_Buffers(mystring_bufs, sizeof(mystring_bufs)/sizeof(strbuf_t)); | |||||
driver_ready_for_device(this); | |||||
USBHIDParser::driver_ready_for_hid_collection(this); | |||||
} | |||||
//***************************************************************************** | |||||
// Some simple query functions depend on which interface we are using... | |||||
//***************************************************************************** | |||||
uint16_t JoystickController::idVendor() | |||||
{ | |||||
if (device != nullptr) return device->idVendor; | |||||
if (mydevice != nullptr) return mydevice->idVendor; | |||||
return 0; | |||||
} | |||||
bool JoystickController::claim_collection(Device_t *dev, uint32_t topusage) | |||||
uint16_t JoystickController::idProduct() | |||||
{ | |||||
if (device != nullptr) return device->idProduct; | |||||
if (mydevice != nullptr) return mydevice->idProduct; | |||||
return 0; | |||||
} | |||||
const uint8_t *JoystickController::manufacturer() | |||||
{ | |||||
if ((device != nullptr) && (device->strbuf != nullptr)) return &device->strbuf->buffer[device->strbuf->iStrings[strbuf_t::STR_ID_MAN]]; | |||||
if ((mydevice != nullptr) && (mydevice->strbuf != nullptr)) return &mydevice->strbuf->buffer[mydevice->strbuf->iStrings[strbuf_t::STR_ID_MAN]]; | |||||
return nullptr; | |||||
} | |||||
const uint8_t *JoystickController::product() | |||||
{ | |||||
if ((device != nullptr) && (device->strbuf != nullptr)) return &device->strbuf->buffer[device->strbuf->iStrings[strbuf_t::STR_ID_PROD]]; | |||||
if ((mydevice != nullptr) && (mydevice->strbuf != nullptr)) return &mydevice->strbuf->buffer[mydevice->strbuf->iStrings[strbuf_t::STR_ID_PROD]]; | |||||
return nullptr; | |||||
} | |||||
const uint8_t *JoystickController::serialNumber() | |||||
{ | |||||
if ((device != nullptr) && (device->strbuf != nullptr)) return &device->strbuf->buffer[device->strbuf->iStrings[strbuf_t::STR_ID_SERIAL]]; | |||||
if ((mydevice != nullptr) && (mydevice->strbuf != nullptr)) return &mydevice->strbuf->buffer[mydevice->strbuf->iStrings[strbuf_t::STR_ID_SERIAL]]; | |||||
return nullptr; | |||||
} | |||||
//***************************************************************************** | |||||
// Support for Joysticks that USe HID data. | |||||
//***************************************************************************** | |||||
hidclaim_t JoystickController::claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage) | |||||
{ | { | ||||
// only claim Desktop/Joystick and Desktop/Gamepad | // only claim Desktop/Joystick and Desktop/Gamepad | ||||
if (topusage != 0x10004 && topusage != 0x10005) return false; | |||||
if (topusage != 0x10004 && topusage != 0x10005) return CLAIM_NO; | |||||
// only claim from one physical device | // only claim from one physical device | ||||
if (mydevice != NULL && dev != mydevice) return false; | |||||
if (mydevice != NULL && dev != mydevice) return CLAIM_NO; | |||||
mydevice = dev; | mydevice = dev; | ||||
collections_claimed++; | collections_claimed++; | ||||
anychange = true; // always report values on first read | anychange = true; // always report values on first read | ||||
return true; | |||||
return CLAIM_REPORT; | |||||
} | } | ||||
void JoystickController::disconnect_collection(Device_t *dev) | void JoystickController::disconnect_collection(Device_t *dev) | ||||
{ | { | ||||
if (--collections_claimed == 0) { | if (--collections_claimed == 0) { | ||||
mydevice = NULL; | mydevice = NULL; | ||||
axis_mask_ = 0; | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} else if (usage_page == 1 && usage >= 0x30 && usage <= 0x39) { | } else if (usage_page == 1 && usage >= 0x30 && usage <= 0x39) { | ||||
// TODO: need scaling of value to consistent API, 16 bit signed? | // TODO: need scaling of value to consistent API, 16 bit signed? | ||||
// TODO: many joysticks repeat slider usage. Detect & map to axes? | |||||
// TODO: many joysticks repeat slider usage. Detect & map to axis? | |||||
uint32_t i = usage - 0x30; | uint32_t i = usage - 0x30; | ||||
axis_mask_ |= (1 << i); // Keep record of which axis we have data on. | |||||
if (axis[i] != value) { | if (axis[i] != value) { | ||||
axis[i] = value; | axis[i] = value; | ||||
anychange = true; | anychange = true; | ||||
anychange = false; | anychange = false; | ||||
} | } | ||||
//***************************************************************************** | |||||
// Support for Joysticks that are class specific and do not use HID | |||||
// Example: XBox One controller. | |||||
//***************************************************************************** | |||||
// Note: currently just XBOX one. | |||||
JoystickController::product_vendor_mapping_t JoystickController::pid_vid_mapping[] = { | |||||
{ 0x045e, 0x02ea },{ 0x045e, 0x02dd } }; | |||||
static uint8_t start_input[] = {0x05, 0x20, 0x00, 0x01, 0x00}; | |||||
bool JoystickController::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) | |||||
{ | |||||
println("JoystickController claim this=", (uint32_t)this, HEX); | |||||
// only claim at device level | |||||
if (type != 0) return false; | |||||
print_hexbytes(descriptors, len); | |||||
uint8_t i = 0; | |||||
for (; i < (sizeof(pid_vid_mapping)/sizeof(pid_vid_mapping[0])); i++) { | |||||
if ((dev->idVendor == pid_vid_mapping[i].idVendor) && (dev->idProduct == pid_vid_mapping[i].idProduct)) { | |||||
break; | |||||
} | |||||
} | |||||
if (i == (sizeof(pid_vid_mapping)/sizeof(pid_vid_mapping[0]))) return false; // Not in our list | |||||
// 0 1 2 3 4 5 6 7 8 *9 10 1 2 3 4 5 *6 7 8 9 20 1 2 3 4 5 6 7 8 9 30 1... | |||||
// 09 04 00 00 02 FF 47 D0 00 07 05 02 03 40 00 04 07 05 82 03 40 00 04 09 04 01 00 00 FF 47 D0 00 | |||||
// Lets do some verifications to make sure. | |||||
if (len < 9+7+7) return false; | |||||
uint32_t count_end_points = descriptors[4]; | |||||
if (count_end_points < 2) return false; | |||||
if (descriptors[5] != 0xff) return false; // bInterfaceClass, 3 = HID | |||||
uint32_t rxep = 0; | |||||
uint32_t txep = 0; | |||||
rx_size_ = 0; | |||||
tx_size_ = 0; | |||||
uint32_t descriptor_index = 9; | |||||
while (count_end_points-- && ((rxep == 0) || txep == 0)) { | |||||
if (descriptors[descriptor_index] != 7) return false; // length 7 | |||||
if (descriptors[descriptor_index+1] != 5) return false; // ep desc | |||||
if ((descriptors[descriptor_index+3] == 3) // Type 3... | |||||
&& (descriptors[descriptor_index+4] <= 64) | |||||
&& (descriptors[descriptor_index+5] == 0)) { | |||||
// have a bulk EP size | |||||
if (descriptors[descriptor_index+2] & 0x80 ) { | |||||
rxep = descriptors[descriptor_index+2]; | |||||
rx_size_ = descriptors[descriptor_index+4]; | |||||
} else { | |||||
txep = descriptors[descriptor_index+2]; | |||||
tx_size_ = descriptors[descriptor_index+4]; | |||||
} | |||||
} | |||||
descriptor_index += 7; // setup to look at next one... | |||||
} | |||||
if ((rxep == 0) || (txep == 0)) return false; // did not find two end points. | |||||
print("JoystickController, rxep=", rxep & 15); | |||||
print("(", rx_size_); | |||||
print("), txep=", txep); | |||||
print("(", tx_size_); | |||||
println(")"); | |||||
rxpipe_ = new_Pipe(dev, 2, rxep & 15, 1, rx_size_); | |||||
if (!rxpipe_) return false; | |||||
txpipe_ = new_Pipe(dev, 2, txep, 0, tx_size_); | |||||
if (!txpipe_) { | |||||
//free_Pipe(rxpipe_); | |||||
return false; | |||||
} | |||||
rxpipe_->callback_function = rx_callback; | |||||
queue_Data_Transfer(rxpipe_, rxbuf_, rx_size_, this); | |||||
txpipe_->callback_function = tx_callback; | |||||
queue_Data_Transfer(txpipe_, start_input, sizeof(start_input), this); | |||||
memset(axis, 0, sizeof(axis)); // clear out any data. | |||||
return true; | |||||
} | |||||
void JoystickController::control(const Transfer_t *transfer) | |||||
{ | |||||
} | |||||
/************************************************************/ | |||||
// Interrupt-based Data Movement | |||||
/************************************************************/ | |||||
void JoystickController::rx_callback(const Transfer_t *transfer) | |||||
{ | |||||
if (!transfer->driver) return; | |||||
((JoystickController *)(transfer->driver))->rx_data(transfer); | |||||
} | |||||
void JoystickController::tx_callback(const Transfer_t *transfer) | |||||
{ | |||||
if (!transfer->driver) return; | |||||
((JoystickController *)(transfer->driver))->tx_data(transfer); | |||||
} | |||||
/************************************************************/ | |||||
// Interrupt-based Data Movement | |||||
// XBox one input data when type == 0x20 | |||||
// Information came from several places on the web including: | |||||
// https://github.com/quantus/xbox-one-controller-protocol | |||||
/************************************************************/ | |||||
typedef struct { | |||||
uint8_t type; | |||||
uint8_t const_0; | |||||
uint16_t id; | |||||
// From online references button order: | |||||
// sync, dummy, start, back, a, b, x, y | |||||
// dpad up, down left, right | |||||
// lb, rb, left stick, right stick | |||||
// Axis: | |||||
// lt, rt, lx, xy, rx, ry | |||||
// | |||||
uint16_t buttons; | |||||
int16_t axis[6]; | |||||
} xbox1data20_t; | |||||
static const uint8_t xbox_axis_order_mapping[] = {4, 5, 0, 1, 2, 3}; | |||||
void JoystickController::rx_data(const Transfer_t *transfer) | |||||
{ | |||||
// print("JoystickController::rx_data: "); | |||||
// print_hexbytes((uint8_t*)transfer->buffer, transfer->length); | |||||
axis_mask_ = 0x3f; | |||||
xbox1data20_t *xb1d = (xbox1data20_t *)transfer->buffer; | |||||
if ((xb1d->type == 0x20) && (transfer->length >= sizeof (xbox1data20_t))) { | |||||
// We have a data transfer. Lets see what is new... | |||||
if (xb1d->buttons != buttons) { | |||||
buttons = xb1d->buttons; | |||||
anychange = true; | |||||
} | |||||
for (uint8_t i = 0; i < sizeof (xbox_axis_order_mapping); i++) { | |||||
// The first two values were unsigned. | |||||
int axis_value = (i < 2)? (int)(uint16_t)xb1d->axis[i] : xb1d->axis[i]; | |||||
if (axis_value != axis[xbox_axis_order_mapping[i]]) { | |||||
axis[xbox_axis_order_mapping[i]] = axis_value; | |||||
anychange = true; | |||||
} | |||||
} | |||||
joystickEvent = true; | |||||
} | |||||
queue_Data_Transfer(rxpipe_, rxbuf_, rx_size_, this); | |||||
} | |||||
void JoystickController::tx_data(const Transfer_t *transfer) | |||||
{ | |||||
} | |||||
void JoystickController::disconnect() | |||||
{ | |||||
axis_mask_ = 0; | |||||
// TODO: free resources | |||||
} | |||||
contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t)); | contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t)); | ||||
contribute_String_Buffers(mystring_bufs, sizeof(mystring_bufs)/sizeof(strbuf_t)); | contribute_String_Buffers(mystring_bufs, sizeof(mystring_bufs)/sizeof(strbuf_t)); | ||||
driver_ready_for_device(this); | driver_ready_for_device(this); | ||||
USBHIDParser::driver_ready_for_hid_collection(this); | |||||
} | } | ||||
bool KeyboardController::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) | bool KeyboardController::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) | ||||
void KeyboardController::new_data(const Transfer_t *transfer) | void KeyboardController::new_data(const Transfer_t *transfer) | ||||
{ | { | ||||
processing_new_data_ = true; | |||||
println("KeyboardController Callback (member)"); | println("KeyboardController Callback (member)"); | ||||
print(" KB Data: "); | print(" KB Data: "); | ||||
print_hexbytes(transfer->buffer, 8); | print_hexbytes(transfer->buffer, 8); | ||||
} | } | ||||
memcpy(prev_report, report, 8); | memcpy(prev_report, report, 8); | ||||
queue_Data_Transfer(datapipe, report, 8, this); | queue_Data_Transfer(datapipe, report, 8, this); | ||||
processing_new_data_ = false; | |||||
// See if we have any outstanding leds to update | |||||
if (update_leds_) { | |||||
updateLEDS(); | |||||
} | |||||
} | } | ||||
} | } | ||||
void KeyboardController::updateLEDS() { | void KeyboardController::updateLEDS() { | ||||
println("KBD: Update LEDS", leds_.byte, HEX); | |||||
if (processing_new_data_) { | |||||
println(" Update defered"); | |||||
update_leds_ = true; | |||||
return; // defer until later | |||||
} | |||||
// Now lets tell keyboard new state. | // Now lets tell keyboard new state. | ||||
static uint8_t keyboard_keys_report[1] = {0}; | |||||
setup_t keys_setup; | |||||
keyboard_keys_report[0] = leds_.byte; | |||||
queue_Data_Transfer(datapipe, report, 8, this); | |||||
mk_setup(keys_setup, 0x21, 9, 0x200, 0, sizeof(keyboard_keys_report)); // hopefully this sets leds | |||||
queue_Control_Transfer(device, &keys_setup, keyboard_keys_report, this); | |||||
update_leds_ = false; | |||||
mk_setup(setup, 0x21, 9, 0x200, 0, sizeof(leds_.byte)); // hopefully this sets leds | |||||
queue_Control_Transfer(device, &setup, &leds_.byte, this); | |||||
} | } | ||||
//============================================================================= | |||||
// Keyboard Extras - Combined from other object | |||||
//============================================================================= | |||||
#define TOPUSAGE_SYS_CONTROL 0x10080 | |||||
#define TOPUSAGE_CONSUMER_CONTROL 0x0c0001 | |||||
hidclaim_t KeyboardController::claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage) | |||||
{ | |||||
// Lets try to claim a few specific Keyboard related collection/reports | |||||
//Serial.printf("KBH Claim %x\n", topusage); | |||||
if ((topusage != TOPUSAGE_SYS_CONTROL) | |||||
&& (topusage != TOPUSAGE_CONSUMER_CONTROL) | |||||
) return CLAIM_NO; | |||||
// only claim from one physical device | |||||
//Serial.println("KeyboardController claim collection"); | |||||
// Lets only claim if this is the same device as claimed Keyboard... | |||||
if (dev != device) return CLAIM_NO; | |||||
if (mydevice != NULL && dev != mydevice) return CLAIM_NO; | |||||
mydevice = dev; | |||||
collections_claimed_++; | |||||
return CLAIM_REPORT; | |||||
} | |||||
void KeyboardController::disconnect_collection(Device_t *dev) | |||||
{ | |||||
if (--collections_claimed_ == 0) { | |||||
mydevice = NULL; | |||||
} | |||||
} | |||||
void KeyboardController::hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax) | |||||
{ | |||||
//Serial.printf("KPC:hid_input_begin TUSE: %x TYPE: %x Range:%x %x\n", topusage, type, lgmin, lgmax); | |||||
topusage_ = topusage; // remember which report we are processing. | |||||
hid_input_begin_ = true; | |||||
hid_input_data_ = false; | |||||
} | |||||
void KeyboardController::hid_input_data(uint32_t usage, int32_t value) | |||||
{ | |||||
// Hack ignore 0xff00 high words as these are user values... | |||||
if ((usage & 0xffff0000) == 0xff000000) return; | |||||
//Serial.printf("KeyboardController: topusage= %x usage=%X, value=%d\n", topusage_, usage, value); | |||||
// See if the value is in our keys_down list | |||||
usage &= 0xffff; // only keep the actual key | |||||
if (usage == 0) return; // lets not process 0, if only 0 happens, we will handle it on the end to remove existing pressed items. | |||||
// Remember if we have received any logical key up events. Some keyboard appear to send them | |||||
// others do no... | |||||
hid_input_data_ = true; | |||||
uint8_t key_index; | |||||
for (key_index = 0; key_index < count_keys_down_; key_index++) { | |||||
if (keys_down[key_index] == usage) { | |||||
if (value) return; // still down | |||||
if (extrasKeyReleasedFunction) { | |||||
extrasKeyReleasedFunction(topusage_, usage); | |||||
} | |||||
// Remove from list | |||||
count_keys_down_--; | |||||
for (;key_index < count_keys_down_; key_index++) { | |||||
keys_down[key_index] = keys_down[key_index+1]; | |||||
} | |||||
return; | |||||
} | |||||
} | |||||
// Was not in list | |||||
if (!value) return; // still 0 | |||||
if (extrasKeyPressedFunction) { | |||||
extrasKeyPressedFunction(topusage_, usage); | |||||
} | |||||
if (count_keys_down_ < MAX_KEYS_DOWN) { | |||||
keys_down[count_keys_down_++] = usage; | |||||
} | |||||
} | |||||
void KeyboardController::hid_input_end() | |||||
{ | |||||
//Serial.println("KPC:hid_input_end"); | |||||
if (hid_input_begin_) { | |||||
// See if we received any data from parser if not, assume all keys released... | |||||
if (!hid_input_data_ ) { | |||||
if (extrasKeyReleasedFunction) { | |||||
while (count_keys_down_) { | |||||
count_keys_down_--; | |||||
extrasKeyReleasedFunction(topusage_, keys_down[count_keys_down_]); | |||||
} | |||||
} | |||||
count_keys_down_ = 0; | |||||
} | |||||
hid_input_begin_ = false; | |||||
} | |||||
} |
/* USB keyboard power Host for Teensy 3.6 | |||||
* Copyright 2017 Paul Stoffregen (paul@pjrc.com) | |||||
* | |||||
* 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 <Arduino.h> | |||||
#include "USBHost_t36.h" // Read this header first for key info | |||||
#define TOPUSAGE_SYS_CONTROL 0x10080 | |||||
#define TOPUSAGE_CONSUMER_CONTROL 0x0c0001 | |||||
bool KeyboardHIDExtrasController::claim_collection(Device_t *dev, uint32_t topusage) | |||||
{ | |||||
// Lets try to claim a few specific Keyboard related collection/reports | |||||
//Serial.printf("KBH Claim %x\n", topusage); | |||||
if ((topusage != TOPUSAGE_SYS_CONTROL) | |||||
&& (topusage != TOPUSAGE_CONSUMER_CONTROL) | |||||
) return false; | |||||
// only claim from one physical device | |||||
//Serial.println("KeyboardHIDExtrasController claim collection"); | |||||
if (mydevice != NULL && dev != mydevice) return false; | |||||
mydevice = dev; | |||||
collections_claimed_++; | |||||
return true; | |||||
} | |||||
void KeyboardHIDExtrasController::disconnect_collection(Device_t *dev) | |||||
{ | |||||
if (--collections_claimed_ == 0) { | |||||
mydevice = NULL; | |||||
} | |||||
} | |||||
void KeyboardHIDExtrasController::hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax) | |||||
{ | |||||
//Serial.printf("KPC:hid_input_begin TUSE: %x TYPE: %x Range:%x %x\n", topusage, type, lgmin, lgmax); | |||||
topusage_ = topusage; // remember which report we are processing. | |||||
hid_input_begin_ = true; | |||||
hid_input_data_ = false; | |||||
} | |||||
void KeyboardHIDExtrasController::hid_input_data(uint32_t usage, int32_t value) | |||||
{ | |||||
// Hack ignore 0xff00 high words as these are user values... | |||||
if ((usage & 0xffff0000) == 0xff000000) return; | |||||
//Serial.printf("KeyboardHIDExtrasController: topusage= %x usage=%X, value=%d\n", topusage_, usage, value); | |||||
// See if the value is in our keys_down list | |||||
usage &= 0xffff; // only keep the actual key | |||||
if (usage == 0) return; // lets not process 0, if only 0 happens, we will handle it on the end to remove existing pressed items. | |||||
// Remember if we have received any logical key up events. Some keyboard appear to send them | |||||
// others do no... | |||||
hid_input_data_ = true; | |||||
uint8_t key_index; | |||||
for (key_index = 0; key_index < count_keys_down_; key_index++) { | |||||
if (keys_down[key_index] == usage) { | |||||
if (value) return; // still down | |||||
if (keyReleasedFunction) { | |||||
keyReleasedFunction(topusage_, usage); | |||||
} | |||||
// Remove from list | |||||
count_keys_down_--; | |||||
for (;key_index < count_keys_down_; key_index++) { | |||||
keys_down[key_index] = keys_down[key_index+1]; | |||||
} | |||||
return; | |||||
} | |||||
} | |||||
// Was not in list | |||||
if (!value) return; // still 0 | |||||
if (keyPressedFunction) { | |||||
keyPressedFunction(topusage_, usage); | |||||
} | |||||
if (count_keys_down_ < MAX_KEYS_DOWN) { | |||||
keys_down[count_keys_down_++] = usage; | |||||
} | |||||
} | |||||
void KeyboardHIDExtrasController::hid_input_end() | |||||
{ | |||||
//Serial.println("KPC:hid_input_end"); | |||||
if (hid_input_begin_) { | |||||
// See if we received any data from parser if not, assume all keys released... | |||||
if (!hid_input_data_ ) { | |||||
if (keyPressedFunction) { | |||||
while (count_keys_down_) { | |||||
count_keys_down_--; | |||||
keyReleasedFunction(topusage_, keys_down[count_keys_down_]); | |||||
} | |||||
} | |||||
count_keys_down_ = 0; | |||||
} | |||||
event_ = true; | |||||
hid_input_begin_ = false; | |||||
} | |||||
} | |||||
USBSerial KEYWORD1 | USBSerial KEYWORD1 | ||||
AntPlus KEYWORD1 | AntPlus KEYWORD1 | ||||
JoystickController KEYWORD1 | JoystickController KEYWORD1 | ||||
KeyboardHIDExtrasController KEYWORD1 | |||||
RawHIDController KEYWORD1 | |||||
# Common Functions | # Common Functions | ||||
Task KEYWORD2 | Task KEYWORD2 | ||||
getOemKey KEYWORD2 | getOemKey KEYWORD2 | ||||
attachPress KEYWORD2 | attachPress KEYWORD2 | ||||
attachRelease KEYWORD2 | attachRelease KEYWORD2 | ||||
attachExtrasPress KEYWORD2 | |||||
attachExtrasRelease KEYWORD2 | |||||
LEDS KEYWORD2 | LEDS KEYWORD2 | ||||
updateLEDS KEYWORD2 | updateLEDS KEYWORD2 | ||||
numLock KEYWORD2 | numLock KEYWORD2 | ||||
capsLock KEYWORD2 | capsLock KEYWORD2 | ||||
scrollLock | |||||
scrollLock KEYWORD2 | |||||
# MIDIDevice | # MIDIDevice | ||||
getType KEYWORD2 | getType KEYWORD2 | ||||
# JoystickController | # JoystickController | ||||
joystickDataClear KEYWORD2 | joystickDataClear KEYWORD2 | ||||
getAxis KEYWORD2 | getAxis KEYWORD2 | ||||
axisMask KEYWORD2 | |||||
# USBSerial | |||||
USBHOST_SERIAL_7E1 LITERAL1 | |||||
USBHOST_SERIAL_7O1 LITERAL1 | |||||
USBHOST_SERIAL_8N1 LITERAL1 | |||||
USBHOST_SERIAL_8N2 LITERAL1 | |||||
USBHOST_SERIAL_8E1 LITERAL1 | |||||
USBHOST_SERIAL_8O1 LITERAL1 | |||||
# RAWHid | |||||
usage KEYWORD2 | |||||
attachReceive KEYWORD2 | |||||
sendPacket KEYWORD2 |
bool MouseController::claim_collection(Device_t *dev, uint32_t topusage) | |||||
hidclaim_t MouseController::claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage) | |||||
{ | { | ||||
// only claim Desktop/Mouse | // only claim Desktop/Mouse | ||||
if (topusage != 0x10002) return false; | |||||
if (topusage != 0x10002) return CLAIM_NO; | |||||
// only claim from one physical device | // only claim from one physical device | ||||
if (mydevice != NULL && dev != mydevice) return false; | |||||
if (mydevice != NULL && dev != mydevice) return CLAIM_NO; | |||||
mydevice = dev; | mydevice = dev; | ||||
collections_claimed++; | collections_claimed++; | ||||
return true; | |||||
return CLAIM_REPORT; | |||||
} | } | ||||
void MouseController::disconnect_collection(Device_t *dev) | void MouseController::disconnect_collection(Device_t *dev) |
/* USB EHCI Host for Teensy 3.6 | |||||
* Copyright 2017 Paul Stoffregen (paul@pjrc.com) | |||||
* | |||||
* 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 <Arduino.h> | |||||
#include "USBHost_t36.h" // Read this header first for key info | |||||
void RawHIDController::init() | |||||
{ | |||||
USBHost::contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t)); | |||||
USBHIDParser::driver_ready_for_hid_collection(this); | |||||
} | |||||
hidclaim_t RawHIDController::claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage) | |||||
{ | |||||
// only claim RAWHID devices currently: 16c0:0486 | |||||
#ifdef USBHOST_PRINT_DEBUG | |||||
Serial.printf("Rawhid Claim: %x:%x usage: %x\n", dev->idVendor, dev->idProduct, topusage); | |||||
#endif | |||||
if ((dev->idVendor != 0x16c0 || (dev->idProduct) != 0x486)) return CLAIM_NO; | |||||
if (mydevice != NULL && dev != mydevice) return CLAIM_NO; | |||||
if (usage_) return CLAIM_NO; // Only claim one | |||||
if (fixed_usage_ && (fixed_usage_ != topusage)) return CLAIM_NO; // See if we want specific one and if so is it this one | |||||
mydevice = dev; | |||||
collections_claimed++; | |||||
usage_ = topusage; | |||||
driver_ = driver; // remember the driver. | |||||
return CLAIM_INTERFACE; // We wa | |||||
} | |||||
void RawHIDController::disconnect_collection(Device_t *dev) | |||||
{ | |||||
if (--collections_claimed == 0) { | |||||
mydevice = NULL; | |||||
usage_ = 0; | |||||
} | |||||
} | |||||
bool RawHIDController::hid_process_in_data(const Transfer_t *transfer) | |||||
{ | |||||
#ifdef USBHOST_PRINT_DEBUG | |||||
Serial.printf("RawHIDController::hid_process_in_data: %x\n", usage_); | |||||
#endif | |||||
if (receiveCB) { | |||||
return (*receiveCB)(usage_, (const uint8_t *)transfer->buffer, transfer->length); | |||||
} | |||||
return true; | |||||
} | |||||
bool RawHIDController::hid_process_out_data(const Transfer_t *transfer) | |||||
{ | |||||
#ifdef USBHOST_PRINT_DEBUG | |||||
Serial.printf("RawHIDController::hid_process_out_data: %x\n", usage_); | |||||
#endif | |||||
return true; | |||||
} | |||||
bool RawHIDController::sendPacket(const uint8_t *buffer) | |||||
{ | |||||
if (!driver_) return false; | |||||
return driver_->sendPacket(buffer); | |||||
} | |||||
void RawHIDController::hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax) | |||||
{ | |||||
// These should not be called as we are claiming the whole interface and not | |||||
// allowing the parse to happen | |||||
#ifdef USBHOST_PRINT_DEBUG | |||||
Serial.printf("RawHID::hid_input_begin %x %x %x %x\n", topusage, type, lgmin, lgmax); | |||||
#endif | |||||
//hid_input_begin_ = true; | |||||
} | |||||
void RawHIDController::hid_input_data(uint32_t usage, int32_t value) | |||||
{ | |||||
// These should not be called as we are claiming the whole interface and not | |||||
// allowing the parse to happen | |||||
#ifdef USBHOST_PRINT_DEBUG | |||||
Serial.printf("RawHID: usage=%X, value=%d", usage, value); | |||||
if ((value >= ' ') && (value <='~')) Serial.printf("(%c)", value); | |||||
Serial.println(); | |||||
#endif | |||||
} | |||||
void RawHIDController::hid_input_end() | |||||
{ | |||||
// These should not be called as we are claiming the whole interface and not | |||||
// allowing the parse to happen | |||||
#ifdef USBHOST_PRINT_DEBUG | |||||
Serial.println("RawHID::hid_input_end"); | |||||
#endif | |||||
// if (hid_input_begin_) { | |||||
// hid_input_begin_ = false; | |||||
// } | |||||
} | |||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
* | |||||
* Note: special thanks to the Linux kernel for the CH341's method of operation, particularly how the baud rate is encoded. | |||||
*/ | */ | ||||
#include <Arduino.h> | #include <Arduino.h> | ||||
#define print USBHost::print_ | #define print USBHost::print_ | ||||
#define println USBHost::println_ | #define println USBHost::println_ | ||||
//#define ENABLE_DEBUG_PINS | |||||
#ifdef ENABLE_DEBUG_PINS | |||||
#define debugDigitalToggle(pin) {digitalWriteFast(pin, !digitalReadFast(pin));} | |||||
#define debugDigitalWrite(pin, state) {digitalWriteFast(pin, state);} | |||||
#else | |||||
#define debugDigitalToggle(pin) {;} | |||||
#define debugDigitalWrite(pin, state) {;} | |||||
#endif | |||||
/************************************************************/ | /************************************************************/ | ||||
// Control Transfer For Configuration | |||||
// Define mapping VID/PID - to Serial Device type. | |||||
/************************************************************/ | /************************************************************/ | ||||
typedef struct { | |||||
uint32_t dwDTERate; // Data Terminal Rate in bits per second | |||||
uint8_t bCharFormat; // 0 - 1 stop bit, 1 - 1.5 stop bits, 2 - 2 stop bits | |||||
uint8_t bParityType; // 0 - None, 1 - Odd, 2 - Even, 3 - Mark, 4 - Space | |||||
uint8_t bDataBits; // Data bits (5, 6, 7, 8 or 16) | |||||
} LINE_CODING; | |||||
USBSerial::product_vendor_mapping_t USBSerial::pid_vid_mapping[] = { | |||||
// FTDI mappings. | |||||
{0x0403, 0x6001, USBSerial::FTDI}, | |||||
// PL2303 | |||||
{0x67B,0x2303, USBSerial::PL2303}, | |||||
// CH341 | |||||
{0x4348, 0x5523, USBSerial::CH341 }, | |||||
{0x1a86, 0x7523, USBSerial::CH341 }, | |||||
{0x1a86, 0x5523, USBSerial::CH341 }, | |||||
// Silex CP210... | |||||
{0x10c4, 0xea60, USBSerial::CP210X } | |||||
}; | |||||
/************************************************************/ | /************************************************************/ | ||||
// Initialization and claiming of devices & interfaces | // Initialization and claiming of devices & interfaces | ||||
contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t)); | contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t)); | ||||
contribute_String_Buffers(mystring_bufs, sizeof(mystring_bufs)/sizeof(strbuf_t)); | contribute_String_Buffers(mystring_bufs, sizeof(mystring_bufs)/sizeof(strbuf_t)); | ||||
driver_ready_for_device(this); | driver_ready_for_device(this); | ||||
format_ = USBHOST_SERIAL_8N1; | |||||
} | } | ||||
bool USBSerial::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) | bool USBSerial::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) | ||||
println(", bDeviceProtocol = ", dev->bDeviceProtocol); | println(", bDeviceProtocol = ", dev->bDeviceProtocol); | ||||
print_hexbytes(descriptors, len); | print_hexbytes(descriptors, len); | ||||
if (type == 0) { | if (type == 0) { | ||||
if (dev->idVendor == 0x0403 && dev->idProduct == 0x6001) { | |||||
// FTDI FT232 | |||||
println("len = ", len); | |||||
if (len < 23) return false; | |||||
if (descriptors[0] != 9) return false; // length 9 | |||||
if (descriptors[9] != 7) return false; // length 7 | |||||
if (descriptors[10] != 5) return false; // ep desc | |||||
uint32_t rxep = descriptors[11]; | |||||
if (descriptors[12] != 2) return false; // bulk type | |||||
if (descriptors[13] != 64) return false; // size 64 | |||||
if (descriptors[14] != 0) return false; | |||||
if (descriptors[16] != 7) return false; // length 7 | |||||
if (descriptors[17] != 5) return false; // ep desc | |||||
uint32_t txep = descriptors[18]; | |||||
if (descriptors[19] != 2) return false; // bulk type | |||||
if (descriptors[20] != 64) return false; // size 64 | |||||
if (descriptors[21] != 0) return false; | |||||
if (!check_rxtx_ep(rxep, txep)) return false; | |||||
print("FTDI, rxep=", rxep & 15); | |||||
println(", txep=", txep); | |||||
if (!init_buffers(64, 64)) return false; | |||||
rxpipe = new_Pipe(dev, 2, rxep & 15, 1, 64); | |||||
if (!rxpipe) return false; | |||||
txpipe = new_Pipe(dev, 2, txep, 0, 64); | |||||
if (!txpipe) { | |||||
// TODO: free rxpipe | |||||
return false; | |||||
} | |||||
sertype = FTDI; | |||||
rxpipe->callback_function = rx_callback; | |||||
queue_Data_Transfer(rxpipe, rx1, 64, this); | |||||
rxstate = 1; | |||||
if (rxsize > 128) { | |||||
queue_Data_Transfer(rxpipe, rx2, 64, this); | |||||
rxstate = 3; | |||||
} | |||||
txstate = 0; | |||||
txpipe->callback_function = tx_callback; | |||||
baudrate = 115200; | |||||
pending_control = 0x0F; | |||||
mk_setup(setup, 0x40, 0, 0, 0, 0); // reset port | |||||
queue_Control_Transfer(dev, &setup, NULL, this); | |||||
control_queued = true; | |||||
return true; | |||||
} else if ((dev->bDeviceClass == 2) && (dev->bDeviceSubClass == 0)) { | |||||
//--------------------------------------------------------------------- | |||||
// CDCACM | |||||
if ((dev->bDeviceClass == 2) && (dev->bDeviceSubClass == 0)) { | |||||
// It is a communication device see if we can extract the data... | // It is a communication device see if we can extract the data... | ||||
// Try some ttyACM types? | // Try some ttyACM types? | ||||
// This code may be similar to MIDI code. | // This code may be similar to MIDI code. | ||||
println(", tx:", tx_ep); | println(", tx:", tx_ep); | ||||
if (!rx_ep || !tx_ep) return false; // did not get our two end points | if (!rx_ep || !tx_ep) return false; // did not get our two end points | ||||
if (!init_buffers(rx_size, tx_size)) return false; | if (!init_buffers(rx_size, tx_size)) return false; | ||||
println(" rx buffer size:", rxsize); | |||||
println(" tx buffer size:", txsize); | |||||
rxpipe = new_Pipe(dev, 2, rx_ep & 15, 1, rx_size); | rxpipe = new_Pipe(dev, 2, rx_ep & 15, 1, rx_size); | ||||
if (!rxpipe) return false; | if (!rxpipe) return false; | ||||
txpipe = new_Pipe(dev, 2, tx_ep, 0, tx_size); | txpipe = new_Pipe(dev, 2, tx_ep, 0, tx_size); | ||||
return true; | return true; | ||||
} | } | ||||
// TODO: Note: there are probably more vendor/product pairs.. Maybe should create table of them | |||||
if (dev->idVendor == 0x67B && dev->idProduct == 0x2303) { | |||||
// Prolific Technology, Inc. PL2303 Serial Port | |||||
println("len = ", len); | |||||
uint8_t count_end_points = descriptors[4]; | |||||
if (count_end_points < 2) return false; // not enough end points | |||||
if (len < 23) return false; | |||||
if (descriptors[0] != 9) return false; // length 9 | |||||
// See if the vendor_id:product_id is in our list of products. | |||||
sertype = UNKNOWN; | |||||
for (uint8_t i = 0; i < (sizeof(pid_vid_mapping)/sizeof(pid_vid_mapping[0])); i++) { | |||||
if ((dev->idVendor == pid_vid_mapping[i].idVendor) && (dev->idProduct == pid_vid_mapping[i].idProduct)) { | |||||
sertype = pid_vid_mapping[i].sertype; | |||||
break; | |||||
} | |||||
} | |||||
if (sertype == UNKNOWN) return false; // not one of ours | |||||
// Lets walk through end points and see if we | |||||
// can find an RX and TX bulk transfer end point. | |||||
//vid=67B, pid=2303 | |||||
// 0 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3 4 5 6 7 8 9 | |||||
//09 04 00 00 03 FF 00 00 00 07 05 81 03 0A 00 01 07 05 02 02 40 00 00 07 05 83 02 40 00 00 | |||||
uint32_t rxep = 0; | |||||
uint32_t txep = 0; | |||||
uint32_t descriptor_index = 9; | |||||
while (count_end_points-- && ((rxep == 0) || txep == 0)) { | |||||
if (descriptors[descriptor_index] != 7) return false; // length 7 | |||||
if (descriptors[descriptor_index+1] != 5) return false; // ep desc | |||||
if ((descriptors[descriptor_index+3] == 2) | |||||
&& (descriptors[descriptor_index+4] == 64) | |||||
&& (descriptors[descriptor_index+5] == 0)) { | |||||
// have a bulk EP size | |||||
if (descriptors[descriptor_index+2] & 0x80 ) { | |||||
rxep = descriptors[descriptor_index+2]; | |||||
} else { | |||||
txep = descriptors[descriptor_index+2]; | |||||
} | |||||
// Lets try to locate the end points. Code is common across these devices | |||||
println("len = ", len); | |||||
uint8_t count_end_points = descriptors[4]; | |||||
if (count_end_points < 2) return false; // not enough end points | |||||
if (len < 23) return false; | |||||
if (descriptors[0] != 9) return false; // length 9 | |||||
// Lets walk through end points and see if we | |||||
// can find an RX and TX bulk transfer end point. | |||||
//Example vid=67B, pid=2303 | |||||
// 0 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3 4 5 6 7 8 9 | |||||
//09 04 00 00 03 FF 00 00 00 07 05 81 03 0A 00 01 07 05 02 02 40 00 00 07 05 83 02 40 00 00 | |||||
uint32_t rxep = 0; | |||||
uint32_t txep = 0; | |||||
uint16_t rx_size = 0; | |||||
uint16_t tx_size = 0; | |||||
uint32_t descriptor_index = 9; | |||||
while (count_end_points-- && ((rxep == 0) || txep == 0)) { | |||||
if (descriptors[descriptor_index] != 7) return false; // length 7 | |||||
if (descriptors[descriptor_index+1] != 5) return false; // ep desc | |||||
if ((descriptors[descriptor_index+3] == 2) | |||||
&& (descriptors[descriptor_index+4] <= 64) | |||||
&& (descriptors[descriptor_index+5] == 0)) { | |||||
// have a bulk EP size | |||||
if (descriptors[descriptor_index+2] & 0x80 ) { | |||||
rxep = descriptors[descriptor_index+2]; | |||||
rx_size = descriptors[descriptor_index+4]; | |||||
} else { | |||||
txep = descriptors[descriptor_index+2]; | |||||
tx_size = descriptors[descriptor_index+4]; | |||||
} | } | ||||
descriptor_index += 7; // setup to look at next one... | |||||
} | |||||
// Try to verify the end points. | |||||
if (!check_rxtx_ep(rxep, txep)) return false; | |||||
print("FTDI, rxep=", rxep & 15); | |||||
println(", txep=", txep); | |||||
if (!init_buffers(64, 64)) return false; | |||||
rxpipe = new_Pipe(dev, 2, rxep & 15, 1, 64); | |||||
if (!rxpipe) return false; | |||||
txpipe = new_Pipe(dev, 2, txep, 0, 64); | |||||
if (!txpipe) { | |||||
// TODO: free rxpipe | |||||
return false; | |||||
} | } | ||||
descriptor_index += 7; // setup to look at next one... | |||||
} | |||||
// Try to verify the end points. | |||||
if (!check_rxtx_ep(rxep, txep)) return false; | |||||
print("USBSerial, rxep=", rxep & 15); | |||||
print("(", rx_size); | |||||
print("), txep=", txep); | |||||
print("(", tx_size); | |||||
println(")"); | |||||
sertype = PL2303; | |||||
rxpipe->callback_function = rx_callback; | |||||
queue_Data_Transfer(rxpipe, rx1, 64, this); | |||||
rxstate = 1; | |||||
if (rxsize > 128) { | |||||
queue_Data_Transfer(rxpipe, rx2, 64, this); | |||||
rxstate = 3; | |||||
} | |||||
txstate = 0; | |||||
txpipe->callback_function = tx_callback; | |||||
baudrate = 115200; | |||||
if (!init_buffers(rx_size, tx_size)) return false; | |||||
println(" rx buffer size:", rxsize); | |||||
println(" tx buffer size:", txsize); | |||||
// Lets see if it will handle the same CDCACM - messages? | |||||
println("PL2303: readRegister(0x04)"); | |||||
// Need to setup the data the line coding data | |||||
mk_setup(setup, 0xC0, 0x1, 0x8484, 0, 1); | |||||
queue_Control_Transfer(dev, &setup, setupdata, this); | |||||
control_queued = true; | |||||
setup_state = 1; // We are at step one of setup... | |||||
pending_control = 0x3f; // Maybe don't need to do... | |||||
return true; | |||||
rxpipe = new_Pipe(dev, 2, rxep & 15, 1, rx_size); | |||||
if (!rxpipe) return false; | |||||
txpipe = new_Pipe(dev, 2, txep, 0, tx_size); | |||||
if (!txpipe) { | |||||
//free_Pipe(rxpipe); | |||||
return false; | |||||
} | |||||
rxpipe->callback_function = rx_callback; | |||||
queue_Data_Transfer(rxpipe, rx1, rx_size, this); | |||||
rxstate = 1; | |||||
txstate = 0; | |||||
txpipe->callback_function = tx_callback; | |||||
baudrate = 115200; | |||||
// Now do specific setup per type | |||||
switch (sertype) { | |||||
//--------------------------------------------------------------------- | |||||
// FTDI | |||||
case FTDI: | |||||
{ | |||||
pending_control = 0x0F; | |||||
mk_setup(setup, 0x40, 0, 0, 0, 0); // reset port | |||||
queue_Control_Transfer(dev, &setup, NULL, this); | |||||
control_queued = true; | |||||
return true; | |||||
} | |||||
//------------------------------------------------------------------------ | |||||
// Prolific | |||||
// TODO: Note: there are probably more vendor/product pairs.. Maybe should create table of them | |||||
case PL2303: | |||||
{ | |||||
// First attempt keep it simple... | |||||
println("PL2303: readRegister(0x04)"); | |||||
// Need to setup the data the line coding data | |||||
mk_setup(setup, 0xC0, 0x1, 0x8484, 0, 1); | |||||
queue_Control_Transfer(dev, &setup, setupdata, this); | |||||
control_queued = true; | |||||
setup_state = 1; // We are at step one of setup... | |||||
pending_control = 0x3f; | |||||
return true; | |||||
} | |||||
//------------------------------------------------------------------------ | |||||
// CH341 | |||||
case CH341: | |||||
{ | |||||
println("CH341: 0xC0, 0x5f, 0, 0, 8"); | |||||
// Need to setup the data the line coding data | |||||
mk_setup(setup, 0xC0, 0x5f, 0, 0, sizeof(setupdata)); | |||||
queue_Control_Transfer(dev, &setup, setupdata, this); | |||||
control_queued = true; | |||||
setup_state = 1; // We are at step one of setup... | |||||
pending_control = 0x7f; | |||||
return true; | |||||
} | |||||
//------------------------------------------------------------------------ | |||||
// CP210X | |||||
case CP210X: | |||||
{ | |||||
println("CP210X: 0x41, 0x11, 0, 0, 0 - reset port"); | |||||
// Need to setup the data the line coding data | |||||
mk_setup(setup, 0x41, 0x11, 0, 0, 0); | |||||
queue_Control_Transfer(dev, &setup, NULL, this); | |||||
control_queued = true; | |||||
setup_state = 1; // We are at step one of setup... | |||||
pending_control = 0xf; | |||||
return true; | |||||
} | |||||
//------------------------------------------------------------------------ | |||||
// PID:VID - not in our product list. | |||||
default: | |||||
return false; | |||||
} | } | ||||
} else if (type != 1) return false; | } else if (type != 1) return false; | ||||
// TTYACM: <Composit device> | // TTYACM: <Composit device> | ||||
if (pending_control & 1) { | if (pending_control & 1) { | ||||
pending_control &= ~1; | pending_control &= ~1; | ||||
// set data format | // set data format | ||||
mk_setup(setup, 0x40, 4, 8, 0, 0); // data format 8N1 | |||||
uint16_t ftdi_format = format_ & 0xf; // This should give us the number of bits. | |||||
// now lets extract the parity from our encoding | |||||
ftdi_format |= (format_ & 0xe0) << 3; // they encode bits 9-11 | |||||
// See if two stop bits | |||||
if (format_ & 0x100) ftdi_format |= (0x2 << 11); | |||||
mk_setup(setup, 0x40, 4, ftdi_format, 0, 0); // data format 8N1 | |||||
queue_Control_Transfer(device, &setup, NULL, this); | queue_Control_Transfer(device, &setup, NULL, this); | ||||
control_queued = true; | control_queued = true; | ||||
return; | return; | ||||
control_queued = true; | control_queued = true; | ||||
return; | return; | ||||
} | } | ||||
// clear DTR | |||||
if (pending_control & 0x80) { | |||||
pending_control &= ~0x80; | |||||
println("FTDI clear DTR"); | |||||
mk_setup(setup, 0x40, 1, 0x0100, 0, 0); | |||||
queue_Control_Transfer(device, &setup, NULL, this); | |||||
control_queued = true; | |||||
return; | |||||
} | |||||
} | } | ||||
setupdata[1] = (baudrate >> 8) & 0xff; | setupdata[1] = (baudrate >> 8) & 0xff; | ||||
setupdata[2] = (baudrate >> 16) & 0xff; | setupdata[2] = (baudrate >> 16) & 0xff; | ||||
setupdata[3] = (baudrate >> 24) & 0xff; | setupdata[3] = (baudrate >> 24) & 0xff; | ||||
setupdata[4] = 0; // 0 - 1 stop bit, 1 - 1.5 stop bits, 2 - 2 stop bits | |||||
setupdata[5] = 0; // 0 - None, 1 - Odd, 2 - Even, 3 - Mark, 4 - Space | |||||
setupdata[6] = 8; // Data bits (5, 6, 7, 8 or 16) | |||||
setupdata[4] = (format_ & 0x100)? 2 : 0; // 0 - 1 stop bit, 1 - 1.5 stop bits, 2 - 2 stop bits | |||||
setupdata[5] = (format_ & 0xe0) >> 5; // 0 - None, 1 - Odd, 2 - Even, 3 - Mark, 4 - Space | |||||
setupdata[6] = format_ & 0x1f; // Data bits (5, 6, 7, 8 or 16) | |||||
print("CDCACM setup: "); | print("CDCACM setup: "); | ||||
print_hexbytes(&setupdata, 7); | print_hexbytes(&setupdata, 7); | ||||
mk_setup(setup, 0x21, 0x20, 0, 0, 7); | mk_setup(setup, 0x21, 0x20, 0, 0, 7); | ||||
control_queued = true; | control_queued = true; | ||||
return; | return; | ||||
} | } | ||||
if (pending_control & 0x80) { | |||||
pending_control &= ~0x80; | |||||
println("Control - 0x21,0x22, 0x0 - clear DTR"); | |||||
// Need to setup the data the line coding data | |||||
mk_setup(setup, 0x21, 0x22, 0, 0, 0); | |||||
queue_Control_Transfer(device, &setup, NULL, this); | |||||
control_queued = true; | |||||
return; | |||||
} | |||||
} | } | ||||
//------------------------------------------------------------------------- | //------------------------------------------------------------------------- | ||||
setupdata[1] = (baudrate >> 8) & 0xff; | setupdata[1] = (baudrate >> 8) & 0xff; | ||||
setupdata[2] = (baudrate >> 16) & 0xff; | setupdata[2] = (baudrate >> 16) & 0xff; | ||||
setupdata[3] = (baudrate >> 24) & 0xff; | setupdata[3] = (baudrate >> 24) & 0xff; | ||||
setupdata[4] = 0; // 0 - 1 stop bit, 1 - 1.5 stop bits, 2 - 2 stop bits | |||||
setupdata[5] = 0; // 0 - None, 1 - Odd, 2 - Even, 3 - Mark, 4 - Space | |||||
setupdata[6] = 8; // Data bits (5, 6, 7, 8 or 16) | |||||
setupdata[4] = (format_ & 0x100)? 2 : 0; // 0 - 1 stop bit, 1 - 1.5 stop bits, 2 - 2 stop bits | |||||
setupdata[5] = (format_ & 0xe0) >> 5; // 0 - None, 1 - Odd, 2 - Even, 3 - Mark, 4 - Space | |||||
setupdata[6] = format_ & 0x1f; // Data bits (5, 6, 7, 8 or 16) | |||||
print("PL2303: Set baud/control: ", baudrate, HEX); | print("PL2303: Set baud/control: ", baudrate, HEX); | ||||
print(" = "); | print(" = "); | ||||
print_hexbytes(&setupdata, 7); | print_hexbytes(&setupdata, 7); | ||||
print("PL2303: Returned configuration data: "); | print("PL2303: Returned configuration data: "); | ||||
print_hexbytes(setupdata, 7); | print_hexbytes(setupdata, 7); | ||||
// This sets the control lines (0x1=DTR, 0x2=RTS) | |||||
println("PL2303: 0x21, 0x22, 0x3"); | println("PL2303: 0x21, 0x22, 0x3"); | ||||
mk_setup(setup, 0x21, 0x22, 3, 0, 0); // | mk_setup(setup, 0x21, 0x22, 3, 0, 0); // | ||||
queue_Control_Transfer(device, &setup, NULL, this); | queue_Control_Transfer(device, &setup, NULL, this); | ||||
control_queued = true; | control_queued = true; | ||||
return; | return; | ||||
} | } | ||||
if (pending_control & 0x30) { | |||||
pending_control &= ~0x30; | |||||
if (pending_control & 0x20) { | |||||
pending_control &= ~0x20; | |||||
println("PL2303: 0x21, 0x22, 0x3"); | println("PL2303: 0x21, 0x22, 0x3"); | ||||
mk_setup(setup, 0x21, 0x22, 3, 0, 0); // | mk_setup(setup, 0x21, 0x22, 3, 0, 0); // | ||||
queue_Control_Transfer(device, &setup, NULL, this); | queue_Control_Transfer(device, &setup, NULL, this); | ||||
control_queued = true; | control_queued = true; | ||||
} | } | ||||
if (pending_control & 0x80) { | |||||
pending_control &= ~0x80; | |||||
println("PL2303: 0x21, 0x22, 0x0"); // Clear DTR/RTS | |||||
mk_setup(setup, 0x21, 0x22, 0, 0, 0); // | |||||
queue_Control_Transfer(device, &setup, NULL, this); | |||||
control_queued = true; | |||||
} | |||||
} | } | ||||
if (sertype == CH341) { | |||||
#if 0 | |||||
print(" Transfer: "); | |||||
print_hexbytes(&transfer->setup, sizeof(setup_t)); | |||||
if (transfer->length) { | |||||
print(" data: "); | |||||
print_hexbytes(transfer->buffer, transfer->length); | |||||
} | |||||
#endif | |||||
if (pending_control & 1) { | |||||
// Still in larger setup state mode | |||||
switch (setup_state) { | |||||
case 1: | |||||
print(" Returned: "); | |||||
print_hexbytes(transfer->buffer, transfer->length); | |||||
println("CH341: 40, a1, 0, 0, 0"); | |||||
mk_setup(setup, 0x40, 0xa1, 0, 0, 0); // | |||||
queue_Control_Transfer(device, &setup, NULL, this); | |||||
setup_state = 2; | |||||
control_queued = true; | |||||
return; | |||||
case 2: | |||||
ch341_setBaud(0); // send the first byte of the baud rate | |||||
control_queued = true; | |||||
setup_state = 3; | |||||
return; | |||||
case 3: | |||||
ch341_setBaud(1); // send the second byte of the baud rate | |||||
control_queued = true; | |||||
setup_state = 4; | |||||
return; | |||||
case 4: | |||||
println("CH341: c0, 95, 2518, 0, 8"); | |||||
mk_setup(setup, 0xc0, 0x95, 0x2518, 0, sizeof(setup)); // | |||||
queue_Control_Transfer(device, &setup, setupdata, this); | |||||
setup_state = 5; | |||||
control_queued = true; | |||||
return; | |||||
case 5: | |||||
print(" Returned: "); | |||||
print_hexbytes(transfer->buffer, transfer->length); | |||||
println("CH341: 40, 0x9a, 0x2518, 0x0050, 0"); | |||||
mk_setup(setup, 0x40, 0x9a, 0x2518, 0x0050, 0); // | |||||
queue_Control_Transfer(device, &setup, NULL, this); | |||||
setup_state = 6; | |||||
control_queued = true; | |||||
return; | |||||
case 6: | |||||
println("CH341: c0, 95, 0x706, 0, 8 - get status"); | |||||
mk_setup(setup, 0xc0, 0x95, 0x706, 0, sizeof(setup)); // | |||||
queue_Control_Transfer(device, &setup, setupdata, this); | |||||
setup_state = 7; | |||||
control_queued = true; | |||||
return; | |||||
case 7: | |||||
print(" Returned: "); | |||||
print_hexbytes(transfer->buffer, transfer->length); | |||||
println("CH341: 40, 0xa1, 0x501f, 0xd90a, 0"); | |||||
mk_setup(setup, 0x40, 0xa1, 0x501f, 0xd90a, 0); // | |||||
queue_Control_Transfer(device, &setup, NULL, this); | |||||
setup_state = 8; | |||||
control_queued = true; | |||||
break; | |||||
} | |||||
pending_control &= ~1; // We are finally going to leave this list and join the rest | |||||
if (control_queued) return; | |||||
} | |||||
// set baud rate | |||||
if (pending_control & 2) { | |||||
pending_control &= ~2; | |||||
ch341_setBaud(0); // send the first byte of the baud rate | |||||
control_queued = true; | |||||
return; | |||||
} | |||||
if (pending_control & 4) { | |||||
pending_control &= ~4; | |||||
ch341_setBaud(1); // send the first byte of the baud rate | |||||
control_queued = true; | |||||
return; | |||||
} | |||||
if (pending_control & 8) { | |||||
pending_control &= ~8; | |||||
uint16_t ch341_format; | |||||
switch (format_) { | |||||
default: | |||||
// These values were observed when used on PC... Need to flush out others. | |||||
case USBHOST_SERIAL_8N1: ch341_format = 0xc3; break; | |||||
case USBHOST_SERIAL_7E1: ch341_format = 0xda; break; | |||||
case USBHOST_SERIAL_7O1: ch341_format = 0xca; break; | |||||
case USBHOST_SERIAL_8N2: ch341_format = 0xc7; break; | |||||
} | |||||
println("CH341: 40, 0x9a, 0x2518: ", ch341_format, HEX); | |||||
mk_setup(setup, 0x40, 0x9a, 0x2518, ch341_format, 0); // | |||||
queue_Control_Transfer(device, &setup, NULL, this); | |||||
control_queued = true; | |||||
return; | |||||
} | |||||
if (pending_control & 0x10) { | |||||
pending_control &= ~0x10; | |||||
// This is setting handshake need to figure out what... | |||||
// 0x20=DTR, 0x40=RTS send ~ of values. | |||||
println("CH341: 0x40, 0xa4, 0xff9f, 0, 0 - Handshake"); | |||||
mk_setup(setup, 0x40, 0xa4, 0xff9f, 0, 0); // | |||||
queue_Control_Transfer(device, &setup, NULL, this); | |||||
control_queued = true; | |||||
return; | |||||
} | |||||
if (pending_control & 0x20) { | |||||
pending_control &= ~0x20; | |||||
// This is setting handshake need to figure out what... | |||||
println("CH341: c0, 95, 0x706, 0, 8 - get status"); | |||||
mk_setup(setup, 0xc0, 0x95, 0x706, 0, sizeof(setup)); // | |||||
queue_Control_Transfer(device, &setup, setupdata, this); | |||||
control_queued = true; | |||||
return; | |||||
} | |||||
if (pending_control & 0x40) { | |||||
pending_control &= ~0x40; | |||||
print(" Returned: "); | |||||
print_hexbytes(transfer->buffer, transfer->length); | |||||
println("CH341: 0x40, 0x9a, 0x2727, 0, 0"); | |||||
mk_setup(setup, 0x40, 0x9a, 0x2727, 0, 0); // | |||||
queue_Control_Transfer(device, &setup, NULL, this); | |||||
return; | |||||
} | |||||
if (pending_control & 0x80) { | |||||
pending_control &= ~0x80; | |||||
println("CH341: 0x40, 0xa4, 0xffff, 0, 0 - Handshake"); | |||||
mk_setup(setup, 0x40, 0xa4, 0xffff, 0, 0); // | |||||
queue_Control_Transfer(device, &setup, NULL, this); | |||||
control_queued = true; | |||||
return; | |||||
} | |||||
} | |||||
//------------------------------------------------------------------------- | |||||
// First CP210X | |||||
if (sertype == CP210X) { | |||||
if (pending_control & 1) { | |||||
pending_control &= ~1; | |||||
// set data format | |||||
uint16_t cp210x_format = (format_ & 0xf) << 8; // This should give us the number of bits. | |||||
// now lets extract the parity from our encoding bits 5-7 and in theres 4-7 | |||||
cp210x_format |= (format_ & 0xe0) >> 1; // they encode bits 9-11 | |||||
// See if two stop bits | |||||
if (format_ & 0x100) cp210x_format |= 2; | |||||
mk_setup(setup, 0x41, 3, cp210x_format, 0, 0); // data format 8N1 | |||||
queue_Control_Transfer(device, &setup, NULL, this); | |||||
control_queued = true; | |||||
return; | |||||
} | |||||
// set baud rate | |||||
if (pending_control & 2) { | |||||
pending_control &= ~2; | |||||
setupdata[0] = (baudrate) & 0xff; // Setup baud rate 115200 - 0x1C200 | |||||
setupdata[1] = (baudrate >> 8) & 0xff; | |||||
setupdata[2] = (baudrate >> 16) & 0xff; | |||||
setupdata[3] = (baudrate >> 24) & 0xff; | |||||
mk_setup(setup, 0x40, 0x1e, 0, 0, 4); | |||||
queue_Control_Transfer(device, &setup, setupdata, this); | |||||
control_queued = true; | |||||
return; | |||||
} | |||||
// configure flow control | |||||
if (pending_control & 4) { | |||||
pending_control &= ~4; | |||||
memset(setupdata, 0, sizeof(setupdata)); // clear out the data | |||||
setupdata[0] = 1; // Set dtr active? | |||||
mk_setup(setup, 0x41, 13, 0, 0, 0x10); | |||||
queue_Control_Transfer(device, &setup, setupdata, this); | |||||
control_queued = true; | |||||
return; | |||||
} | |||||
// set DTR | |||||
if (pending_control & 8) { | |||||
pending_control &= ~8; | |||||
mk_setup(setup, 0x41, 7, 0x0101, 0, 0); | |||||
queue_Control_Transfer(device, &setup, NULL, this); | |||||
control_queued = true; | |||||
return; | |||||
} | |||||
// clear DTR | |||||
if (pending_control & 0x80) { | |||||
pending_control &= ~0x80; | |||||
println("CP210x clear DTR"); | |||||
mk_setup(setup, 0x40, 1, 0x0100, 0, 0); | |||||
queue_Control_Transfer(device, &setup, NULL, this); | |||||
control_queued = true; | |||||
return; | |||||
} | |||||
} | |||||
} | |||||
#define CH341_BAUDBASE_FACTOR 1532620800 | |||||
#define CH341_BAUDBASE_DIVMAX 3 | |||||
void USBSerial::ch341_setBaud(uint8_t byte_index) { | |||||
if (byte_index == 0) { | |||||
uint32_t factor; | |||||
uint16_t divisor; | |||||
factor = (CH341_BAUDBASE_FACTOR / baudrate); | |||||
divisor = CH341_BAUDBASE_DIVMAX; | |||||
while ((factor > 0xfff0) && divisor) { | |||||
factor >>= 3; | |||||
divisor--; | |||||
} | |||||
factor = 0x10000 - factor; | |||||
factor = (factor & 0xff00) | divisor; | |||||
setupdata[0] = factor & 0xff; // save away the low byte for 2nd message | |||||
println("CH341: 40, 0x9a, 0x1312... (Baud word 0):", factor, HEX); | |||||
mk_setup(setup, 0x40, 0x9a, 0x1312, factor, 0); // | |||||
} else { | |||||
// Second packet use the byte we saved away during the calculation above | |||||
println("CH341: 40, 0x9a, 0x0f2c... (Baud word 1):", setupdata[0], HEX); | |||||
mk_setup(setup, 0x40, 0x9a, 0x0f2c, setupdata[0], 0); // | |||||
} | |||||
queue_Control_Transfer(device, &setup, setupdata, this); | |||||
control_queued = true; | |||||
} | } | ||||
{ | { | ||||
uint32_t len = transfer->length - ((transfer->qtd.token >> 16) & 0x7FFF); | uint32_t len = transfer->length - ((transfer->qtd.token >> 16) & 0x7FFF); | ||||
debugDigitalToggle(6); | |||||
// first update rxstate bitmask, since buffer is no longer queued | // first update rxstate bitmask, since buffer is no longer queued | ||||
if (transfer->buffer == rx1) { | if (transfer->buffer == rx1) { | ||||
rxstate &= 0xFE; | rxstate &= 0xFE; | ||||
} | } | ||||
} | } | ||||
if (len > 0) { | if (len > 0) { | ||||
print("rx token: ", transfer->qtd.token, HEX); | |||||
print(" transfer length: ", transfer->length, DEC); | |||||
print(" len:", len, DEC); | |||||
print(" - ", *p, HEX); | |||||
println(" ", *(p+1), HEX); | |||||
print("rx: "); | print("rx: "); | ||||
print_hexbytes(p, len); | print_hexbytes(p, len); | ||||
} | } | ||||
{ | { | ||||
uint32_t mask; | uint32_t mask; | ||||
uint8_t *p = (uint8_t *)transfer->buffer; | uint8_t *p = (uint8_t *)transfer->buffer; | ||||
debugDigitalWrite(5, HIGH); | |||||
if (p == tx1) { | if (p == tx1) { | ||||
println("tx1:"); | println("tx1:"); | ||||
mask = 1; | mask = 1; | ||||
mask = 2; | mask = 2; | ||||
//txstate &= 0xFD; | //txstate &= 0xFD; | ||||
} else { | } else { | ||||
debugDigitalWrite(5, LOW); | |||||
return; // should never happen | return; // should never happen | ||||
} | } | ||||
// check how much more data remains in the transmit buffer | // check how much more data remains in the transmit buffer | ||||
count = txsize + head - tail; | count = txsize + head - tail; | ||||
} | } | ||||
uint32_t packetsize = tx2 - tx1; | uint32_t packetsize = tx2 - tx1; | ||||
if (count < packetsize) { | |||||
// Only output full packets unless the flush bit was set. | |||||
if ((count == 0) || ((count < packetsize) && ((txstate & 0x4) == 0) )) { | |||||
// not enough data in buffer to fill a full packet | // not enough data in buffer to fill a full packet | ||||
txstate &= ~mask; | |||||
txstate &= ~(mask | 4); // turn off that transfer and make sure the flush bit is not set | |||||
debugDigitalWrite(5, LOW); | |||||
return; | return; | ||||
} | } | ||||
// immediately transmit another full packet, if we have enough data | // immediately transmit another full packet, if we have enough data | ||||
if (count >= packetsize) count = packetsize; | |||||
else txstate &= ~(mask | 4); // This packet will complete any outstanding flush | |||||
println("TX:moar data!!!!"); | println("TX:moar data!!!!"); | ||||
if (++tail >= txsize) tail = 0; | if (++tail >= txsize) tail = 0; | ||||
uint32_t n = txsize - tail; | uint32_t n = txsize - tail; | ||||
if (n > packetsize) n = packetsize; | |||||
if (n > count) n = count; | |||||
memcpy(p, txbuf + tail, n); | memcpy(p, txbuf + tail, n); | ||||
if (n >= packetsize) { | |||||
if (n >= count) { | |||||
tail += n - 1; | tail += n - 1; | ||||
if (tail >= txsize) tail = 0; | if (tail >= txsize) tail = 0; | ||||
} else { | } else { | ||||
uint32_t len = packetsize - n; | |||||
uint32_t len = count - n; | |||||
memcpy(p + n, txbuf, len); | memcpy(p + n, txbuf, len); | ||||
tail = len - 1; | tail = len - 1; | ||||
} | } | ||||
txtail = tail; | txtail = tail; | ||||
queue_Data_Transfer(txpipe, p, packetsize, this); | |||||
queue_Data_Transfer(txpipe, p, count, this); | |||||
debugDigitalWrite(5, LOW); | |||||
} | |||||
void USBSerial::flush() | |||||
{ | |||||
print("USBSerial::flush"); | |||||
if (txhead == txtail) { | |||||
println(" - Empty"); | |||||
return; // empty. | |||||
} | |||||
debugDigitalWrite(32, HIGH); | |||||
NVIC_DISABLE_IRQ(IRQ_USBHS); | |||||
txtimer.stop(); // Stop longer timer. | |||||
txtimer.start(100); // Start a mimimal timeout | |||||
// timer_event(nullptr); // Try calling direct - fails to work | |||||
NVIC_ENABLE_IRQ(IRQ_USBHS); | |||||
while (txstate & 3) ; // wait for all of the USB packets to be sent. | |||||
println(" completed"); | |||||
debugDigitalWrite(32, LOW); | |||||
} | } | ||||
void USBSerial::timer_event(USBDriverTimer *whichTimer) | void USBSerial::timer_event(USBDriverTimer *whichTimer) | ||||
{ | { | ||||
debugDigitalWrite(7, HIGH); | |||||
println("txtimer"); | println("txtimer"); | ||||
uint32_t count; | uint32_t count; | ||||
uint32_t head = txhead; | uint32_t head = txhead; | ||||
uint32_t tail = txtail; | uint32_t tail = txtail; | ||||
if (pending_control) { | |||||
// We are still doing setup postpone for awhile.. | |||||
txtimer.start(1200); | |||||
println(" Postpone: setup pending_control"); | |||||
return; // no outgoing buffers available, try again later | |||||
} | |||||
if (head == tail) { | if (head == tail) { | ||||
println(" *** Empty ***"); | println(" *** Empty ***"); | ||||
debugDigitalWrite(7, LOW); | |||||
return; // nothing to transmit | return; // nothing to transmit | ||||
} else if (head > tail) { | } else if (head > tail) { | ||||
count = head - tail; | count = head - tail; | ||||
} else { | } else { | ||||
count = txsize + head - tail; | count = txsize + head - tail; | ||||
} | } | ||||
uint8_t *p; | uint8_t *p; | ||||
if ((txstate & 0x01) == 0) { | if ((txstate & 0x01) == 0) { | ||||
p = tx1; | p = tx1; | ||||
p = tx2; | p = tx2; | ||||
txstate |= 0x02; | txstate |= 0x02; | ||||
} else { | } else { | ||||
txtimer.start(1200); | |||||
txstate |= 4; // Tell the TX code to do flush code. | |||||
println(" *** No buffers ***"); | println(" *** No buffers ***"); | ||||
debugDigitalWrite(7, LOW); | |||||
return; // no outgoing buffers available, try again later | return; // no outgoing buffers available, try again later | ||||
} | } | ||||
uint32_t packetsize = tx2 - tx1; | |||||
// Possible for remaining ? packet size and not have both? | |||||
if (count > packetsize) { | |||||
txstate |= 4; // One of the active transfers will handle the remaining parts | |||||
count = packetsize; | |||||
} | |||||
if (++tail >= txsize) tail = 0; | if (++tail >= txsize) tail = 0; | ||||
uint32_t n = txsize - tail; | uint32_t n = txsize - tail; | ||||
if (n > count) n = count; | if (n > count) n = count; | ||||
print(") "); | print(") "); | ||||
print_hexbytes(p, count); | print_hexbytes(p, count); | ||||
queue_Data_Transfer(txpipe, p, count, this); | queue_Data_Transfer(txpipe, p, count, this); | ||||
debugDigitalWrite(7, LOW); | |||||
} | } | ||||
{ | { | ||||
NVIC_DISABLE_IRQ(IRQ_USBHS); | NVIC_DISABLE_IRQ(IRQ_USBHS); | ||||
baudrate = baud; | baudrate = baud; | ||||
pending_control |= 2; | |||||
bool format_changed = format != format_; | |||||
format_ = format; | |||||
switch (sertype) { | |||||
default: | |||||
case CDCACM: pending_control |= 0x6; break; | |||||
case FTDI: pending_control |= (format_changed? 0xf : 0xe); break; // Set BAUD, FLOW, DTR | |||||
case PL2303: pending_control |= 0x1e; break; // set more stuff... | |||||
case CH341: pending_control |= 0x1e; break; | |||||
case CP210X: pending_control |= 0xf; break; | |||||
} | |||||
if (!control_queued) control(NULL); | if (!control_queued) control(NULL); | ||||
NVIC_ENABLE_IRQ(IRQ_USBHS); | NVIC_ENABLE_IRQ(IRQ_USBHS); | ||||
// Wait until all packets have been queued before we return to caller. | |||||
while (pending_control) { | |||||
yield(); // not sure if we want to yield or what? | |||||
} | |||||
} | } | ||||
void USBSerial::end(void) | void USBSerial::end(void) | ||||
{ | { | ||||
// TODO: lower DTR | |||||
NVIC_DISABLE_IRQ(IRQ_USBHS); | |||||
switch (sertype) { | |||||
default: | |||||
case CDCACM: pending_control |= 0x80; break; | |||||
case FTDI: pending_control |= 0x80; break; // clear DTR | |||||
case PL2303: pending_control |= 0x80; break; | |||||
case CH341: pending_control |= 0x80; break; | |||||
} | |||||
if (!control_queued) control(NULL); | |||||
NVIC_ENABLE_IRQ(IRQ_USBHS); | |||||
// Wait until all packets have been queued before we return to caller. | |||||
while (pending_control) { | |||||
yield(); // not sure if we want to yield or what? | |||||
} | |||||
} | } | ||||
int USBSerial::available(void) | int USBSerial::available(void) | ||||
} | } | ||||
txtail = tail; | txtail = tail; | ||||
//println("queue tx packet, newtail=", tail); | //println("queue tx packet, newtail=", tail); | ||||
debugDigitalWrite(7, HIGH); | |||||
queue_Data_Transfer(txpipe, p, packetsize, this); | queue_Data_Transfer(txpipe, p, packetsize, this); | ||||
debugDigitalWrite(7, LOW); | |||||
NVIC_ENABLE_IRQ(IRQ_USBHS); | NVIC_ENABLE_IRQ(IRQ_USBHS); | ||||
return 1; | return 1; | ||||
} | } | ||||
} | } | ||||
// otherwise, set a latency timer to later transmit partial packet | // otherwise, set a latency timer to later transmit partial packet | ||||
txtimer.stop(); | txtimer.stop(); | ||||
txtimer.start(3500); | |||||
txtimer.start(write_timeout_); | |||||
NVIC_ENABLE_IRQ(IRQ_USBHS); | NVIC_ENABLE_IRQ(IRQ_USBHS); | ||||
return 1; | return 1; | ||||
} | } |