@@ -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; | |||
} |