diff --git a/USBHost_t36.h b/USBHost_t36.h index 90355a9..131cff5 100644 --- a/USBHost_t36.h +++ b/USBHost_t36.h @@ -56,7 +56,7 @@ // your best effort to read chapter 4 before asking USB questions! -//#define USBHOST_PRINT_DEBUG +#define USBHOST_PRINT_DEBUG /************************************************/ /* Data Types */ @@ -80,6 +80,7 @@ class USBHost; typedef struct Device_struct Device_t; typedef struct Pipe_struct Pipe_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. // Drivers build user-visible functionality on top @@ -91,6 +92,7 @@ class USBDriverTimer; /************************************************/ /* Added Defines */ /************************************************/ +// Keyboard special Keys #define KEYD_UP 0xDA #define KEYD_DOWN 0xD9 #define KEYD_LEFT 0xD8 @@ -114,6 +116,22 @@ class USBDriverTimer; #define KEYD_F11 0xCC #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 */ /************************************************/ @@ -252,11 +270,12 @@ protected: static void disconnect_Device(Device_t *dev); static void enumeration(const Transfer_t *transfer); 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_Pipes(Pipe_t *pipes, 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 volatile bool enumeration_busy; private: static void isr(); static void convertStringDescriptorToASCIIString(uint8_t string_index, Device_t *dev, const Transfer_t *transfer); @@ -452,6 +471,8 @@ private: // Device drivers may inherit from this base class, if they wish to receive // HID input data fully decoded by the USBHIDParser driver +class USBHIDParser; + class USBHIDInput { public: operator bool() { return (mydevice != nullptr); } @@ -465,7 +486,9 @@ public: { return ((mydevice == nullptr) || (mydevice->strbuf == nullptr)) ? nullptr : &mydevice->strbuf->buffer[mydevice->strbuf->iStrings[strbuf_t::STR_ID_SERIAL]]; } 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_data(uint32_t usage, int32_t value); virtual void hid_input_end(); @@ -559,10 +582,12 @@ private: //-------------------------------------------------------------------------- + class USBHIDParser : public USBDriver { public: USBHIDParser(USBHost &host) { init(); } static void driver_ready_for_hid_collection(USBHIDInput *driver); + bool sendPacket(const uint8_t *buffer); protected: enum { TOPUSAGE_LIST_LEN = 4 }; enum { USAGE_LIST_LEN = 24 }; @@ -578,6 +603,14 @@ protected: USBHIDInput * find_driver(uint32_t topusage); void parse(uint16_t type_and_report_id, const uint8_t *data, uint32_t len); 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: Pipe_t *in_pipe; Pipe_t *out_pipe; @@ -594,11 +627,15 @@ private: Pipe_t mypipes[3] __attribute__ ((aligned(32))); Transfer_t mytransfers[4] __attribute__ ((aligned(32))); 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: typedef union { struct { @@ -614,8 +651,10 @@ typedef union { public: 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; } uint8_t getModifiers() { return modifiers; } uint8_t getOemKey() { return keyOEM; } @@ -630,6 +669,13 @@ public: void numLock(bool f); void capsLock(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: virtual bool claim(Device_t *device, int type, const uint8_t *descriptors, uint32_t len); virtual void control(const Transfer_t *transfer); @@ -637,6 +683,14 @@ protected: static void callback(const Transfer_t *transfer); void new_data(const Transfer_t *transfer); 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: void update(); uint16_t convert_to_unicode(uint32_t mod, uint32_t key); @@ -652,43 +706,22 @@ private: uint8_t keyOEM; uint8_t prev_report[8]; KBDLeds_t leds_ = {0}; - bool update_leds_ = false; - bool processing_new_data_ = false; Pipe_t mypipes[2] __attribute__ ((aligned(32))); Transfer_t mytransfers[4] __attribute__ ((aligned(32))); 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? uint8_t collections_claimed_ = 0; - volatile bool event_ = false; volatile bool hid_input_begin_ = false; volatile bool hid_input_data_ = false; // did we receive any valid data with report? uint8_t count_keys_down_ = 0; uint16_t keys_down[MAX_KEYS_DOWN]; + }; -//-------------------------------------------------------------------------- class MouseController : public USBHIDInput { public: @@ -701,7 +734,7 @@ public: int getWheel() { return wheel; } int getWheelH() { return wheelH; } 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_data(uint32_t usage, int32_t value); virtual void hid_input_end(); @@ -719,27 +752,76 @@ private: //-------------------------------------------------------------------------- -class JoystickController : public USBHIDInput { +class JoystickController : public USBDriver, public USBHIDInput { 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; } void joystickDataClear(); uint32_t getButtons() { return buttons; } 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: - 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_data(uint32_t usage, int32_t value); virtual void hid_input_end(); virtual void disconnect_collection(Device_t *dev); private: - uint8_t collections_claimed = 0; + + // Class specific + void init(); + bool anychange = false; volatile bool joystickEvent = false; 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 { @@ -889,17 +971,23 @@ private: //-------------------------------------------------------------------------- class USBSerial: public USBDriver, public Stream { -public: + public: + + // 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 { DEFAULT_WRITE_TIMEOUT = 3500}; 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); + 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 peek(void); virtual int read(void); virtual int availableForWrite(); virtual size_t write(uint8_t c); + virtual void flush(void); using Print::write; protected: @@ -916,6 +1004,7 @@ private: void init(); static bool check_rxtx_ep(uint32_t &rxep, uint32_t &txep); bool init_buffers(uint32_t rsize, uint32_t tsize); + void ch341_setBaud(uint8_t byte_index); private: Pipe_t mypipes[3] __attribute__ ((aligned(32))); Transfer_t mytransfers[7] __attribute__ ((aligned(32))); @@ -923,8 +1012,10 @@ private: USBDriverTimer txtimer; uint32_t bigbuffer[(BUFFER_SIZE+3)/4]; setup_t setup; - uint8_t setupdata[8]; + uint8_t setupdata[16]; // uint32_t baudrate; + uint32_t format_; + uint32_t write_timeout_ = DEFAULT_WRITE_TIMEOUT; Pipe_t *rxpipe; Pipe_t *txpipe; uint8_t *rx1; // location for first incoming packet @@ -947,7 +1038,16 @@ private: uint8_t pl2303_v2; uint8_t interface; 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[]; + }; //-------------------------------------------------------------------------- @@ -1171,5 +1271,35 @@ private: 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 diff --git a/ehci.cpp b/ehci.cpp index 2fb2949..d0d5427 100644 --- a/ehci.cpp +++ b/ehci.cpp @@ -1252,12 +1252,28 @@ void USBHost::delete_Pipe(Pipe_t *pipe) // TODO: does this write interfere UPI & UAI (bits 18 & 19) ?? } // find & free all the transfers which completed + println(" Free transfers"); Transfer_t *t = async_followup_first; while (t) { + print(" * ", (uint32_t)t); Transfer_t *next = t->next_followup; if (t->pipe == pipe) { + print(" * remove"); 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; } @@ -1286,12 +1302,28 @@ void USBHost::delete_Pipe(Pipe_t *pipe) // TODO: subtract bandwidth from uframe_bandwidth array // find & free all the transfers which completed + println(" Free transfers"); Transfer_t *t = periodic_followup_first; while (t) { + print(" * ", (uint32_t)t); Transfer_t *next = t->next_followup; if (t->pipe == pipe) { + print(" * remove"); 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; } @@ -1300,14 +1332,17 @@ void USBHost::delete_Pipe(Pipe_t *pipe) // TODO: do we need to look at pipe->qh.current ?? // // free all the transfers still attached to the QH + println(" Free transfers attached to QH"); Transfer_t *tr = (Transfer_t *)(pipe->qh.next); while ((uint32_t)tr & 0xFFFFFFE0) { + println(" * ", (uint32_t)tr); Transfer_t *next = (Transfer_t *)(tr->qtd.next); free_Transfer(tr); tr = next; } // hopefully we found everything... free_Pipe(pipe); + println("* Delete Pipe completed"); } diff --git a/examples/Mouse/Mouse.ino b/examples/Mouse/Mouse.ino index 14955a9..8950e2f 100644 --- a/examples/Mouse/Mouse.ino +++ b/examples/Mouse/Mouse.ino @@ -7,11 +7,9 @@ USBHost myusb; USBHub hub1(myusb); USBHub hub2(myusb); -USBHub hub3(myusb); -USBHub hub4(myusb); KeyboardController keyboard1(myusb); KeyboardController keyboard2(myusb); -KeyboardHIDExtrasController hidextras(myusb); +//KeyboardHIDExtrasController hidextras(myusb); USBHIDParser hid1(myusb); USBHIDParser hid2(myusb); USBHIDParser hid3(myusb); @@ -19,16 +17,18 @@ USBHIDParser hid4(myusb); USBHIDParser hid5(myusb); MouseController mouse1(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])) -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}; // 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])) -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}; @@ -40,8 +40,13 @@ void setup() myusb.begin(); keyboard1.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); } @@ -104,25 +109,31 @@ void loop() mouse1.mouseDataClear(); } if (joystick1.available()) { + uint32_t axis_mask = joystick1.axisMask(); Serial.print("Joystick: buttons = "); 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(); 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); + } + } } @@ -421,3 +432,38 @@ void OnHIDExtrasRelease(uint32_t top, uint16_t key) Serial.print(") key release:"); 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; +} diff --git a/examples/Mouse/defs.h b/examples/Mouse/defs.h new file mode 100644 index 0000000..e69de29 diff --git a/examples/SerialTest/SerialTest.ino b/examples/SerialTest/SerialTest.ino index b745e1f..45189b0 100644 --- a/examples/SerialTest/SerialTest.ino +++ b/examples/SerialTest/SerialTest.ino @@ -4,6 +4,8 @@ #include "USBHost_t36.h" #define USBBAUD 115200 +uint32_t baud = USBBAUD; +uint32_t format = USBHOST_SERIAL_8N1; USBHost myusb; USBHub hub1(myusb); USBHub hub2(myusb); @@ -60,23 +62,8 @@ void loop() // If this is a new Serial device. if (drivers[i] == &userial) { // Lets try first outputting something to our USerial to see if it will go out... - userial.begin(USBBAUD); + userial.begin(baud); -// 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 } } } @@ -89,8 +76,55 @@ void loop() if (ch == '$') { BioloidTest(); 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); } } @@ -106,14 +140,25 @@ void loop() } } +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 0xfd // Extract stuff from Bioloid library.. #define AX12_BUFFER_SIZE 128 #define COUNTER_TIMEOUT 12000 -/** Instruction Set **/ -#define AX_PING 1 +/** Instruction Set **/ #define AX_PING 1 #define AX_READ_DATA 2 #define AX_WRITE_DATA 3 #define AX_REG_WRITE 4 @@ -244,7 +289,7 @@ int ax12ReadPacket(int length) { while ((ch = userial.read()) == -1) { if ((millis() - ulStart) > 10) { //if (!--ulCounter) { - // Serial.println("Timeout"); + Serial.println("Timeout"); return 0; // Timeout } } diff --git a/examples/SerialTest/defs.h b/examples/SerialTest/defs.h new file mode 100644 index 0000000..e69de29 diff --git a/hid.cpp b/hid.cpp index 2d320b9..3a8bd0a 100644 --- a/hid.cpp +++ b/hid.cpp @@ -128,21 +128,19 @@ bool USBHIDParser::claim(Device_t *dev, int type, const uint8_t *descriptors, ui if (((endpoint1 & 0xF0) == 0x80) && ((endpoint2 & 0xF0) == 0)) { // first endpoint is IN, second endpoint is OUT 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; out_size = size2; } else if (((endpoint1 & 0xF0) == 0) && ((endpoint2 & 0xF0) == 0x80)) { // first endpoint is OUT, second endpoint is IN 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; out_size = size1; } else { return false; } - //out_pipe->callback_function = out_callback; + out_pipe->callback_function = out_callback; } in_pipe->callback_function = in_callback; for (uint32_t i=0; i < TOPUSAGE_LIST_LEN; i++) { @@ -185,6 +183,7 @@ void USBHIDParser::in_callback(const Transfer_t *transfer) void USBHIDParser::out_callback(const Transfer_t *transfer) { + //println("USBHIDParser:: out_callback (static)"); if (transfer->driver) { ((USBHIDParser*)(transfer->driver))->out_data(transfer); } @@ -215,14 +214,22 @@ void USBHIDParser::in_data(const Transfer_t *transfer) Serial.println(); */ print("HID: "); + print(use_report_id); + print(" - "); print_hexbytes(transfer->buffer, transfer->length); const uint8_t *buf = (const uint8_t *)transfer->buffer; 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); @@ -231,8 +238,44 @@ void USBHIDParser::in_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 // HID report descriptor. It finds all the top level collections // and allows drivers to claim them. This is always where we @@ -333,9 +376,11 @@ USBHIDInput * USBHIDParser::find_driver(uint32_t topusage) { println("find_driver"); USBHIDInput *driver = available_hid_drivers_list; + hidclaim_t claim_type; while (driver) { 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; } driver = driver->next; diff --git a/joystick.cpp b/joystick.cpp index 1c179c7..9513127 100644 --- a/joystick.cpp +++ b/joystick.cpp @@ -24,24 +24,80 @@ #include #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; +} + +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; +} -bool JoystickController::claim_collection(Device_t *dev, uint32_t topusage) + +//***************************************************************************** +// 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 - if (topusage != 0x10004 && topusage != 0x10005) return false; + if (topusage != 0x10004 && topusage != 0x10005) return CLAIM_NO; // only claim from one physical device - if (mydevice != NULL && dev != mydevice) return false; + if (mydevice != NULL && dev != mydevice) return CLAIM_NO; mydevice = dev; collections_claimed++; anychange = true; // always report values on first read - return true; + return CLAIM_REPORT; } void JoystickController::disconnect_collection(Device_t *dev) { if (--collections_claimed == 0) { mydevice = NULL; + axis_mask_ = 0; } } @@ -70,8 +126,9 @@ void JoystickController::hid_input_data(uint32_t usage, int32_t value) } } else if (usage_page == 1 && usage >= 0x30 && usage <= 0x39) { // 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; + axis_mask_ |= (1 << i); // Keep record of which axis we have data on. if (axis[i] != value) { axis[i] = value; anychange = true; @@ -92,4 +149,167 @@ void JoystickController::joystickDataClear() { 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 +} + diff --git a/keyboard.cpp b/keyboard.cpp index b3e9123..bfc127c 100644 --- a/keyboard.cpp +++ b/keyboard.cpp @@ -98,6 +98,7 @@ void KeyboardController::init() 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); } bool KeyboardController::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) @@ -179,7 +180,6 @@ static bool contains(uint8_t b, const uint8_t *data) void KeyboardController::new_data(const Transfer_t *transfer) { - processing_new_data_ = true; println("KeyboardController Callback (member)"); print(" KB Data: "); print_hexbytes(transfer->buffer, 8); @@ -197,12 +197,6 @@ void KeyboardController::new_data(const Transfer_t *transfer) } memcpy(prev_report, report, 8); queue_Data_Transfer(datapipe, report, 8, this); - processing_new_data_ = false; - - // See if we have any outstanding leds to update - if (update_leds_) { - updateLEDS(); - } } @@ -328,31 +322,107 @@ void KeyboardController::LEDS(uint8_t leds) { } 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. - 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; + } +} diff --git a/keyboardHIDExtras.cpp b/keyboardHIDExtras.cpp deleted file mode 100644 index f98eed1..0000000 --- a/keyboardHIDExtras.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* 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 -#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; - } -} - - - diff --git a/keywords.txt b/keywords.txt index f9c13cb..5df2d68 100644 --- a/keywords.txt +++ b/keywords.txt @@ -8,7 +8,7 @@ MIDIDevice KEYWORD1 USBSerial KEYWORD1 AntPlus KEYWORD1 JoystickController KEYWORD1 -KeyboardHIDExtrasController KEYWORD1 +RawHIDController KEYWORD1 # Common Functions Task KEYWORD2 @@ -24,11 +24,13 @@ getModifiers KEYWORD2 getOemKey KEYWORD2 attachPress KEYWORD2 attachRelease KEYWORD2 +attachExtrasPress KEYWORD2 +attachExtrasRelease KEYWORD2 LEDS KEYWORD2 updateLEDS KEYWORD2 numLock KEYWORD2 capsLock KEYWORD2 -scrollLock +scrollLock KEYWORD2 # MIDIDevice getType KEYWORD2 @@ -78,4 +80,17 @@ getWheelH KEYWORD2 # JoystickController joystickDataClear 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 diff --git a/mouse.cpp b/mouse.cpp index 8ab1219..79fa395 100644 --- a/mouse.cpp +++ b/mouse.cpp @@ -26,15 +26,15 @@ -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 - if (topusage != 0x10002) return false; + if (topusage != 0x10002) return CLAIM_NO; // only claim from one physical device - if (mydevice != NULL && dev != mydevice) return false; + if (mydevice != NULL && dev != mydevice) return CLAIM_NO; mydevice = dev; collections_claimed++; - return true; + return CLAIM_REPORT; } void MouseController::disconnect_collection(Device_t *dev) diff --git a/rawhid.cpp b/rawhid.cpp new file mode 100644 index 0000000..29439b1 --- /dev/null +++ b/rawhid.cpp @@ -0,0 +1,120 @@ +/* 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 +#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; +// } +} + + diff --git a/serial.cpp b/serial.cpp index ffd7893..dd53639 100644 --- a/serial.cpp +++ b/serial.cpp @@ -19,6 +19,8 @@ * 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. + * + * Note: special thanks to the Linux kernel for the CH341's method of operation, particularly how the baud rate is encoded. */ #include @@ -27,15 +29,35 @@ #define print USBHost::print_ #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 @@ -47,6 +69,7 @@ void USBSerial::init() contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t)); contribute_String_Buffers(mystring_bufs, sizeof(mystring_bufs)/sizeof(strbuf_t)); driver_ready_for_device(this); + format_ = USBHOST_SERIAL_8N1; } bool USBSerial::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) @@ -60,51 +83,9 @@ bool USBSerial::claim(Device_t *dev, int type, const uint8_t *descriptors, uint3 println(", bDeviceProtocol = ", dev->bDeviceProtocol); print_hexbytes(descriptors, len); 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... // Try some ttyACM types? // This code may be similar to MIDI code. @@ -197,6 +178,8 @@ bool USBSerial::claim(Device_t *dev, int type, const uint8_t *descriptors, uint3 println(", tx:", tx_ep); if (!rx_ep || !tx_ep) return false; // did not get our two end points 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); if (!rxpipe) return false; txpipe = new_Pipe(dev, 2, tx_ep, 0, tx_size); @@ -226,72 +209,133 @@ bool USBSerial::claim(Device_t *dev, int type, const uint8_t *descriptors, uint3 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; // TTYACM: @@ -424,7 +468,15 @@ void USBSerial::control(const Transfer_t *transfer) if (pending_control & 1) { pending_control &= ~1; // 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); control_queued = true; return; @@ -454,6 +506,15 @@ void USBSerial::control(const Transfer_t *transfer) control_queued = true; 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; + } } @@ -467,9 +528,9 @@ void USBSerial::control(const Transfer_t *transfer) setupdata[1] = (baudrate >> 8) & 0xff; setupdata[2] = (baudrate >> 16) & 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_hexbytes(&setupdata, 7); mk_setup(setup, 0x21, 0x20, 0, 0, 7); @@ -487,6 +548,15 @@ void USBSerial::control(const Transfer_t *transfer) control_queued = true; 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; + } } //------------------------------------------------------------------------- @@ -607,9 +677,9 @@ void USBSerial::control(const Transfer_t *transfer) setupdata[1] = (baudrate >> 8) & 0xff; setupdata[2] = (baudrate >> 16) & 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(" = "); print_hexbytes(&setupdata, 7); @@ -639,20 +709,258 @@ void USBSerial::control(const Transfer_t *transfer) print("PL2303: Returned configuration data: "); print_hexbytes(setupdata, 7); + // This sets the control lines (0x1=DTR, 0x2=RTS) println("PL2303: 0x21, 0x22, 0x3"); mk_setup(setup, 0x21, 0x22, 3, 0, 0); // queue_Control_Transfer(device, &setup, NULL, this); control_queued = true; return; } - if (pending_control & 0x30) { - pending_control &= ~0x30; + if (pending_control & 0x20) { + pending_control &= ~0x20; println("PL2303: 0x21, 0x22, 0x3"); mk_setup(setup, 0x21, 0x22, 3, 0, 0); // queue_Control_Transfer(device, &setup, NULL, this); 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; } @@ -677,6 +985,7 @@ void USBSerial::rx_data(const Transfer_t *transfer) { uint32_t len = transfer->length - ((transfer->qtd.token >> 16) & 0x7FFF); + debugDigitalToggle(6); // first update rxstate bitmask, since buffer is no longer queued if (transfer->buffer == rx1) { rxstate &= 0xFE; @@ -694,6 +1003,11 @@ void USBSerial::rx_data(const Transfer_t *transfer) } } 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_hexbytes(p, len); } @@ -760,6 +1074,7 @@ void USBSerial::tx_data(const Transfer_t *transfer) { uint32_t mask; uint8_t *p = (uint8_t *)transfer->buffer; + debugDigitalWrite(5, HIGH); if (p == tx1) { println("tx1:"); mask = 1; @@ -769,6 +1084,7 @@ void USBSerial::tx_data(const Transfer_t *transfer) mask = 2; //txstate &= 0xFD; } else { + debugDigitalWrite(5, LOW); return; // should never happen } // check how much more data remains in the transmit buffer @@ -781,50 +1097,72 @@ void USBSerial::tx_data(const Transfer_t *transfer) count = txsize + head - tail; } 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 - txstate &= ~mask; + txstate &= ~(mask | 4); // turn off that transfer and make sure the flush bit is not set + debugDigitalWrite(5, LOW); return; } // 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!!!!"); if (++tail >= txsize) tail = 0; uint32_t n = txsize - tail; - if (n > packetsize) n = packetsize; + if (n > count) n = count; memcpy(p, txbuf + tail, n); - if (n >= packetsize) { + if (n >= count) { tail += n - 1; if (tail >= txsize) tail = 0; } else { - uint32_t len = packetsize - n; + uint32_t len = count - n; memcpy(p + n, txbuf, len); tail = len - 1; } 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) { + debugDigitalWrite(7, HIGH); println("txtimer"); uint32_t count; uint32_t head = txhead; 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) { println(" *** Empty ***"); + debugDigitalWrite(7, LOW); return; // nothing to transmit } else if (head > tail) { count = head - tail; } else { count = txsize + head - tail; } + uint8_t *p; if ((txstate & 0x01) == 0) { p = tx1; @@ -833,10 +1171,20 @@ void USBSerial::timer_event(USBDriverTimer *whichTimer) p = tx2; txstate |= 0x02; } else { - txtimer.start(1200); + txstate |= 4; // Tell the TX code to do flush code. println(" *** No buffers ***"); + debugDigitalWrite(7, LOW); 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; uint32_t n = txsize - tail; if (n > count) n = count; @@ -854,6 +1202,7 @@ void USBSerial::timer_event(USBDriverTimer *whichTimer) print(") "); print_hexbytes(p, count); queue_Data_Transfer(txpipe, p, count, this); + debugDigitalWrite(7, LOW); } @@ -866,14 +1215,41 @@ void USBSerial::begin(uint32_t baud, uint32_t format) { NVIC_DISABLE_IRQ(IRQ_USBHS); 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); 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) { - // 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) @@ -975,14 +1351,16 @@ size_t USBSerial::write(uint8_t c) } txtail = tail; //println("queue tx packet, newtail=", tail); + debugDigitalWrite(7, HIGH); queue_Data_Transfer(txpipe, p, packetsize, this); + debugDigitalWrite(7, LOW); NVIC_ENABLE_IRQ(IRQ_USBHS); return 1; } } // otherwise, set a latency timer to later transmit partial packet txtimer.stop(); - txtimer.start(3500); + txtimer.start(write_timeout_); NVIC_ENABLE_IRQ(IRQ_USBHS); return 1; }