| @@ -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 | |||
| @@ -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"); | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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); | |||
| // 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); | |||
| } | |||
| } | |||
| } | |||
| @@ -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 | |||
| } | |||
| } | |||
| @@ -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; | |||
| @@ -24,24 +24,80 @@ | |||
| #include <Arduino.h> | |||
| #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 | |||
| 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 | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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 <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; | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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) | |||
| @@ -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 <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; | |||
| // } | |||
| } | |||
| @@ -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 <Arduino.h> | |||
| @@ -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: <Composit device> | |||
| @@ -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; | |||
| } | |||