USB Flightsim for Teensy 4, add FlightSimData and FlightSimEvent data types for T3teensy4-core
| FlightSimInteger KEYWORD2 | FlightSimInteger KEYWORD2 | ||||
| FlightSimFloat KEYWORD2 | FlightSimFloat KEYWORD2 | ||||
| FlightSimElapsedFrames KEYWORD2 | FlightSimElapsedFrames KEYWORD2 | ||||
| FlightSimData KEYWORD2 | |||||
| FlightSimEvent KEYWORD2 | |||||
| onChange KEYWORD2 | onChange KEYWORD2 | ||||
| update KEYWORD2 | update KEYWORD2 | ||||
| isEnabled KEYWORD2 | isEnabled KEYWORD2 |
| FlightSimInteger * FlightSimInteger::last = NULL; | FlightSimInteger * FlightSimInteger::last = NULL; | ||||
| FlightSimFloat * FlightSimFloat::first = NULL; | FlightSimFloat * FlightSimFloat::first = NULL; | ||||
| FlightSimFloat * FlightSimFloat::last = NULL; | FlightSimFloat * FlightSimFloat::last = NULL; | ||||
| /// JB | |||||
| FlightSimEvent * FlightSimEvent::first = NULL; | |||||
| FlightSimEvent * FlightSimEvent::last = NULL; | |||||
| FlightSimData * FlightSimData::first = NULL; | |||||
| FlightSimData * FlightSimData::last = NULL; | |||||
| /// JB End | |||||
| uint8_t FlightSimClass::enabled = 0; | uint8_t FlightSimClass::enabled = 0; | ||||
| uint8_t FlightSimClass::request_id_messages = 0; | uint8_t FlightSimClass::request_id_messages = 0; | ||||
| FlightSimClass::xmit(buf, 4, NULL, 0); | FlightSimClass::xmit(buf, 4, NULL, 0); | ||||
| } | } | ||||
| /// JB | |||||
| FlightSimEvent::FlightSimEvent() | |||||
| { | |||||
| id = unassigned_id++; | |||||
| if (!first) { | |||||
| first = this; | |||||
| } else { | |||||
| last->next = this; | |||||
| } | |||||
| last = this; | |||||
| name = NULL; | |||||
| next = NULL; | |||||
| occur_callback = NULL; | |||||
| occurredFlag = 0; | |||||
| callbackInfo = NULL; | |||||
| hasCallbackInfo = 0; | |||||
| value = 0; | |||||
| FlightSimClass::request_id_messages = 1; | |||||
| } | |||||
| void FlightSimEvent::identify(void) | |||||
| { | |||||
| uint8_t len, buf[6]; | |||||
| if (!FlightSim.enabled || !name) return; | |||||
| len = strlen((const char *)name); | |||||
| buf[0] = len + 6; | |||||
| buf[1] = 1; | |||||
| buf[2] = id; | |||||
| buf[3] = id >> 8; | |||||
| buf[4] = 3; | |||||
| buf[5] = 0; | |||||
| FlightSimClass::xmit(buf, 6, name, len); | |||||
| } | |||||
| void FlightSimEvent::send(unsigned int data, unsigned int flags) | |||||
| { | |||||
| uint8_t buf[4]; | |||||
| uint32_t txData[2]; | |||||
| if (!FlightSim.enabled || !name) return; | |||||
| buf[0] = 12; | |||||
| buf[1] = 7; | |||||
| buf[2] = id; | |||||
| buf[3] = id >> 8; | |||||
| value = data; | |||||
| txData[0] = data; | |||||
| txData[1] = flags; | |||||
| FlightSimClass::xmit(buf, 4, (uint8_t *)&txData, 8); | |||||
| } | |||||
| void FlightSimEvent::update(long val) | |||||
| { | |||||
| value = (unsigned int) val; | |||||
| occurredFlag = true; | |||||
| if (occur_callback) { | |||||
| if (!hasCallbackInfo) { | |||||
| (*occur_callback)(val); | |||||
| } else { | |||||
| (*(void(*)(long,void*))occur_callback)(val,callbackInfo); | |||||
| } | |||||
| } | |||||
| } | |||||
| FlightSimEvent * FlightSimEvent::find(unsigned int n) | |||||
| { | |||||
| for (FlightSimEvent *p = first; p; p = p->next) { | |||||
| if (p->id == n) return p; | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| FlightSimData::FlightSimData() | |||||
| { | |||||
| id = unassigned_id++; | |||||
| if (!first) { | |||||
| first = this; | |||||
| } else { | |||||
| last->next = this; | |||||
| } | |||||
| last = this; | |||||
| name = NULL; | |||||
| next = NULL; | |||||
| valueLen = 0; | |||||
| hasCallbackInfo = 0; | |||||
| callbackWithObject = 0; | |||||
| callbackInfo = NULL; | |||||
| change_callback = NULL; | |||||
| FlightSimClass::request_id_messages = 1; | |||||
| } | |||||
| void FlightSimData::identify(void) | |||||
| { | |||||
| uint8_t len, buf[6]; | |||||
| if (!FlightSim.enabled || !name) return; | |||||
| len = strlen((const char *)name); | |||||
| buf[0] = len + 6; | |||||
| buf[1] = 1; | |||||
| buf[2] = id; | |||||
| buf[3] = id >> 8; | |||||
| buf[4] = 4; | |||||
| buf[5] = 0; | |||||
| FlightSimClass::xmit(buf, 6, name, len); | |||||
| } | |||||
| void FlightSimData::update(char *val, size_t len) | |||||
| { | |||||
| valueLen = len; | |||||
| memcpy(value, val, len); | |||||
| if (len<FLIGHTSIM_DATA_MAXLEN) { | |||||
| memset(value+len,0,FLIGHTSIM_DATA_MAXLEN-len); | |||||
| } | |||||
| if (change_callback) { | |||||
| if (!callbackWithObject) { | |||||
| if (!hasCallbackInfo) { | |||||
| (*change_callback)(value); | |||||
| } else { | |||||
| (*(void(*)(char*,void*))change_callback)(value,callbackInfo); | |||||
| } | |||||
| } else { | |||||
| if (!hasCallbackInfo) { | |||||
| (*(void(*)(FlightSimData*))change_callback)(this); | |||||
| } else { | |||||
| (*(void(*)(FlightSimData*,void*))change_callback)(this,callbackInfo); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| FlightSimData * FlightSimData::find(unsigned int n) | |||||
| { | |||||
| for (FlightSimData *p = first; p; p = p->next) { | |||||
| if (p->id == n) return p; | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| /// JB End | |||||
| FlightSimInteger::FlightSimInteger() | FlightSimInteger::FlightSimInteger() | ||||
| { | { | ||||
| data.b[2] = p[8]; | data.b[2] = p[8]; | ||||
| data.b[3] = p[9]; | data.b[3] = p[9]; | ||||
| item->update(data.f); | item->update(data.f); | ||||
| /// JB | |||||
| } else if (type == 3) { | |||||
| FlightSimEvent *item = FlightSimEvent::find(id); | |||||
| if (!item) break; | |||||
| #ifdef KINETISK | |||||
| data.l = *(long *)(p + 6); | |||||
| #else | |||||
| data.b[0] = p[6]; | |||||
| data.b[1] = p[7]; | |||||
| data.b[2] = p[8]; | |||||
| data.b[3] = p[9]; | |||||
| #endif | |||||
| item->update(data.f); | |||||
| } else if (type == 4) { | |||||
| FlightSimData *item = FlightSimData::find(id); | |||||
| if (!item) break; | |||||
| item->update(((char*)p)+6,len-6); | |||||
| /// JB End | |||||
| } | } | ||||
| break; | break; | ||||
| case 0x03: // enable/disable | case 0x03: // enable/disable | ||||
| for (FlightSimCommand *p = FlightSimCommand::first; p; p = p->next) { | for (FlightSimCommand *p = FlightSimCommand::first; p; p = p->next) { | ||||
| p->identify(); | p->identify(); | ||||
| } | } | ||||
| /// JB | |||||
| for (FlightSimEvent *p = FlightSimEvent::first; p; p = p->next) { | |||||
| p->identify(); | |||||
| } | |||||
| for (FlightSimData *p = FlightSimData::first; p; p=p->next) { | |||||
| p->identify(); | |||||
| } | |||||
| /// JB End | |||||
| for (FlightSimInteger *p = FlightSimInteger::first; p; p = p->next) { | for (FlightSimInteger *p = FlightSimInteger::first; p; p = p->next) { | ||||
| p->identify(); | p->identify(); | ||||
| // TODO: send any dirty data | // TODO: send any dirty data |
| friend class FlightSimCommand; | friend class FlightSimCommand; | ||||
| friend class FlightSimInteger; | friend class FlightSimInteger; | ||||
| friend class FlightSimFloat; | friend class FlightSimFloat; | ||||
| /// JB | |||||
| friend class FlightSimEvent; | |||||
| friend class FlightSimData; | |||||
| /// JB End | |||||
| }; | }; | ||||
| friend class FlightSimClass; | friend class FlightSimClass; | ||||
| }; | }; | ||||
| /// JB | |||||
| class FlightSimEvent | |||||
| { | |||||
| public: | |||||
| FlightSimEvent(); | |||||
| void assign(const _XpRefStr_ *s) { name = s; if (FlightSimClass::enabled) identify(); } | |||||
| FlightSimEvent & operator = (const _XpRefStr_ *s) { assign(s); return *this; } | |||||
| void send() { send(0,0); } | |||||
| void send(int data) { send(data,0); } | |||||
| void sendOnce() { send(0,0); } | |||||
| void sendOnce(int data) { send(data,0); } | |||||
| void sendRepeat(int data, uint16_t initialDelay, uint16_t repeatDelay) { send(data, initialDelay<<16 | repeatDelay); } | |||||
| void sendRepeat(uint16_t initialDelay, uint16_t repeatDelay) { send(0, initialDelay<<16 | repeatDelay); } | |||||
| void stopRepeat() { send(0,-1); } | |||||
| FlightSimEvent & operator = (int n) { send(n,0); return *this; } | |||||
| bool occurred() { bool hasOccurred = occurredFlag; occurredFlag = 0; return hasOccurred; } | |||||
| void identify(); | |||||
| static FlightSimEvent * find(unsigned int n); | |||||
| void update(long val); | |||||
| void onOccur(void (*fptr)(long)) { | |||||
| hasCallbackInfo=false; | |||||
| occur_callback = fptr; | |||||
| } | |||||
| void onOccur(void (*fptr)(long,void*), void* info) { | |||||
| hasCallbackInfo=true; | |||||
| occur_callback = (void (*)(long))fptr; | |||||
| callbackInfo = info; | |||||
| } | |||||
| private: | |||||
| void send(unsigned int data, unsigned int flags); | |||||
| unsigned int id; | |||||
| const _XpRefStr_ *name; | |||||
| bool occurredFlag; | |||||
| unsigned int value; | |||||
| FlightSimEvent *next; | |||||
| void (*occur_callback)(long); | |||||
| void* callbackInfo; | |||||
| bool hasCallbackInfo; | |||||
| static FlightSimEvent *first; | |||||
| static FlightSimEvent *last; | |||||
| friend class FlightSimClass; | |||||
| }; | |||||
| #define FLIGHTSIM_DATA_MAXLEN 58 | |||||
| class FlightSimData | |||||
| { | |||||
| public: | |||||
| FlightSimData(); | |||||
| void assign(const _XpRefStr_ *s) { name = s; if (FlightSimClass::enabled) identify(); } | |||||
| FlightSimData & operator = (const _XpRefStr_ *s) { assign(s); return *this; } | |||||
| char *read() { return value; } | |||||
| operator char* () { return value; } | |||||
| void identify(); | |||||
| void update(char *val, size_t len); | |||||
| size_t len() { return valueLen; } | |||||
| static FlightSimData * find(unsigned int n); | |||||
| void onChange(void (*fptr)(char *)) { | |||||
| hasCallbackInfo = false; | |||||
| change_callback = fptr; | |||||
| } | |||||
| void onChange(void (*fptr)(char *,void *), void *info) { | |||||
| hasCallbackInfo = true; | |||||
| change_callback = (void (*)(char*)) fptr; | |||||
| callbackInfo = info; | |||||
| } | |||||
| void onChange(void (*fptr)(FlightSimData *)) { | |||||
| callbackWithObject = true; | |||||
| hasCallbackInfo = false; | |||||
| change_callback = (void (*)(char*)) fptr; | |||||
| } | |||||
| void onChange(void (*fptr)(FlightSimData *, void*), void* info) { | |||||
| callbackWithObject = true; | |||||
| hasCallbackInfo = true; | |||||
| change_callback = (void (*)(char*)) fptr; | |||||
| callbackInfo = info; | |||||
| } | |||||
| private: | |||||
| unsigned int id; | |||||
| const _XpRefStr_ *name; | |||||
| char value[FLIGHTSIM_DATA_MAXLEN]; | |||||
| size_t valueLen; | |||||
| void (*change_callback)(char *); | |||||
| void* callbackInfo; | |||||
| bool hasCallbackInfo; | |||||
| bool callbackWithObject; | |||||
| FlightSimData *next; | |||||
| static FlightSimData *first; | |||||
| static FlightSimData *last; | |||||
| friend class FlightSimClass; | |||||
| }; | |||||
| /// JB End | |||||
| class FlightSimInteger | class FlightSimInteger | ||||
| { | { |
| #include "usb_joystick.h" | #include "usb_joystick.h" | ||||
| #include "usb_midi.h" | #include "usb_midi.h" | ||||
| #include "usb_rawhid.h" | #include "usb_rawhid.h" | ||||
| //#include "usb_flightsim.h" | |||||
| #include "usb_flightsim.h" | |||||
| //#include "usb_mtp.h" | //#include "usb_mtp.h" | ||||
| //#include "usb_audio.h" | //#include "usb_audio.h" | ||||
| #include "usb_touch.h" | #include "usb_touch.h" |
| #include "usb_keyboard.h" | #include "usb_keyboard.h" | ||||
| #include "usb_mouse.h" | #include "usb_mouse.h" | ||||
| #include "usb_joystick.h" | #include "usb_joystick.h" | ||||
| #include "usb_flightsim.h" | |||||
| #include "usb_touch.h" | #include "usb_touch.h" | ||||
| #include "usb_midi.h" | #include "usb_midi.h" | ||||
| #include "core_pins.h" // for delay() | #include "core_pins.h" // for delay() | ||||
| #ifdef MULTITOUCH_INTERFACE | #ifdef MULTITOUCH_INTERFACE | ||||
| usb_touchscreen_update_callback(); | usb_touchscreen_update_callback(); | ||||
| #endif | #endif | ||||
| #ifdef FLIGHTSIM_INTERFACE | |||||
| usb_flightsim_flush_output(); | |||||
| #endif | |||||
| } | } | ||||
| } | } | ||||
| #if defined(MOUSE_INTERFACE) | #if defined(MOUSE_INTERFACE) | ||||
| usb_mouse_configure(); | usb_mouse_configure(); | ||||
| #endif | #endif | ||||
| #if defined(FLIGHTSIM_INTERFACE) | |||||
| usb_flightsim_configure(); | |||||
| #endif | |||||
| #if defined(JOYSTICK_INTERFACE) | #if defined(JOYSTICK_INTERFACE) | ||||
| usb_joystick_configure(); | usb_joystick_configure(); | ||||
| #endif | #endif |
| #define PRODUCT_NAME {'T','e','e','n','s','y',' ','F','l','i','g','h','t',' ','S','i','m',' ','C','o','n','t','r','o','l','s'} | #define PRODUCT_NAME {'T','e','e','n','s','y',' ','F','l','i','g','h','t',' ','S','i','m',' ','C','o','n','t','r','o','l','s'} | ||||
| #define PRODUCT_NAME_LEN 26 | #define PRODUCT_NAME_LEN 26 | ||||
| #define EP0_SIZE 64 | #define EP0_SIZE 64 | ||||
| #define NUM_ENDPOINTS 4 | |||||
| #define NUM_ENDPOINTS 3 | |||||
| #define NUM_INTERFACE 2 | #define NUM_INTERFACE 2 | ||||
| #define FLIGHTSIM_INTERFACE 0 // Flight Sim Control | #define FLIGHTSIM_INTERFACE 0 // Flight Sim Control | ||||
| #define FLIGHTSIM_TX_ENDPOINT 3 | #define FLIGHTSIM_TX_ENDPOINT 3 | ||||
| #define FLIGHTSIM_TX_SIZE 64 | #define FLIGHTSIM_TX_SIZE 64 | ||||
| #define FLIGHTSIM_TX_INTERVAL 1 | #define FLIGHTSIM_TX_INTERVAL 1 | ||||
| #define FLIGHTSIM_RX_ENDPOINT 4 | |||||
| #define FLIGHTSIM_RX_ENDPOINT 3 | |||||
| #define FLIGHTSIM_RX_SIZE 64 | #define FLIGHTSIM_RX_SIZE 64 | ||||
| #define FLIGHTSIM_RX_INTERVAL 1 | #define FLIGHTSIM_RX_INTERVAL 1 | ||||
| #define SEREMU_INTERFACE 1 // Serial emulation | #define SEREMU_INTERFACE 1 // Serial emulation | ||||
| #define SEREMU_TX_ENDPOINT 1 | |||||
| #define SEREMU_TX_ENDPOINT 2 | |||||
| #define SEREMU_TX_SIZE 64 | #define SEREMU_TX_SIZE 64 | ||||
| #define SEREMU_TX_INTERVAL 1 | #define SEREMU_TX_INTERVAL 1 | ||||
| #define SEREMU_RX_ENDPOINT 2 | #define SEREMU_RX_ENDPOINT 2 | ||||
| #define SEREMU_RX_SIZE 32 | #define SEREMU_RX_SIZE 32 | ||||
| #define SEREMU_RX_INTERVAL 2 | #define SEREMU_RX_INTERVAL 2 | ||||
| #define ENDPOINT1_CONFIG ENDPOINT_TRANSMIT_ONLY | |||||
| #define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_ONLY | |||||
| #define ENDPOINT3_CONFIG ENDPOINT_TRANSMIT_ONLY | |||||
| #define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_ONLY | |||||
| #define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_INTERRUPT + ENDPOINT_TRANSMIT_INTERRUPT | |||||
| #define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK | |||||
| #elif defined(USB_FLIGHTSIM_JOYSTICK) | #elif defined(USB_FLIGHTSIM_JOYSTICK) | ||||
| #define VENDOR_ID 0x16C0 | #define VENDOR_ID 0x16C0 | ||||
| #define PRODUCT_NAME {'T','e','e','n','s','y',' ','F','l','i','g','h','t',' ','S','i','m',' ','C','o','n','t','r','o','l','s'} | #define PRODUCT_NAME {'T','e','e','n','s','y',' ','F','l','i','g','h','t',' ','S','i','m',' ','C','o','n','t','r','o','l','s'} | ||||
| #define PRODUCT_NAME_LEN 26 | #define PRODUCT_NAME_LEN 26 | ||||
| #define EP0_SIZE 64 | #define EP0_SIZE 64 | ||||
| #define NUM_ENDPOINTS 5 | |||||
| #define NUM_ENDPOINTS 4 | |||||
| #define NUM_INTERFACE 3 | #define NUM_INTERFACE 3 | ||||
| #define FLIGHTSIM_INTERFACE 0 // Flight Sim Control | #define FLIGHTSIM_INTERFACE 0 // Flight Sim Control | ||||
| #define FLIGHTSIM_TX_ENDPOINT 3 | #define FLIGHTSIM_TX_ENDPOINT 3 | ||||
| #define FLIGHTSIM_TX_SIZE 64 | #define FLIGHTSIM_TX_SIZE 64 | ||||
| #define FLIGHTSIM_TX_INTERVAL 1 | #define FLIGHTSIM_TX_INTERVAL 1 | ||||
| #define FLIGHTSIM_RX_ENDPOINT 4 | |||||
| #define FLIGHTSIM_RX_ENDPOINT 3 | |||||
| #define FLIGHTSIM_RX_SIZE 64 | #define FLIGHTSIM_RX_SIZE 64 | ||||
| #define FLIGHTSIM_RX_INTERVAL 1 | #define FLIGHTSIM_RX_INTERVAL 1 | ||||
| #define SEREMU_INTERFACE 1 // Serial emulation | #define SEREMU_INTERFACE 1 // Serial emulation | ||||
| #define SEREMU_TX_ENDPOINT 1 | |||||
| #define SEREMU_TX_ENDPOINT 2 | |||||
| #define SEREMU_TX_SIZE 64 | #define SEREMU_TX_SIZE 64 | ||||
| #define SEREMU_TX_INTERVAL 1 | #define SEREMU_TX_INTERVAL 1 | ||||
| #define SEREMU_RX_ENDPOINT 2 | #define SEREMU_RX_ENDPOINT 2 | ||||
| #define SEREMU_RX_SIZE 32 | #define SEREMU_RX_SIZE 32 | ||||
| #define SEREMU_RX_INTERVAL 2 | #define SEREMU_RX_INTERVAL 2 | ||||
| #define JOYSTICK_INTERFACE 2 // Joystick | #define JOYSTICK_INTERFACE 2 // Joystick | ||||
| #define JOYSTICK_ENDPOINT 5 | |||||
| #define JOYSTICK_ENDPOINT 4 | |||||
| #define JOYSTICK_SIZE 12 // 12 = normal, 64 = extreme joystick | #define JOYSTICK_SIZE 12 // 12 = normal, 64 = extreme joystick | ||||
| #define JOYSTICK_INTERVAL 1 | #define JOYSTICK_INTERVAL 1 | ||||
| #define ENDPOINT1_CONFIG ENDPOINT_TRANSMIT_ONLY | |||||
| #define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_ONLY | |||||
| #define ENDPOINT3_CONFIG ENDPOINT_TRANSMIT_ONLY | |||||
| #define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_ONLY | |||||
| #define ENDPOINT5_CONFIG ENDPOINT_TRANSMIT_ONLY | |||||
| #define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_INTERRUPT + ENDPOINT_TRANSMIT_INTERRUPT | |||||
| #define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK | |||||
| #define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_INTERRUPT | |||||
| #elif defined(USB_MTPDISK) | #elif defined(USB_MTPDISK) | ||||
| #define VENDOR_ID 0x16C0 | #define VENDOR_ID 0x16C0 |
| /* Teensyduino Core Library | |||||
| * http://www.pjrc.com/teensy/ | |||||
| * Copyright (c) 2013 PJRC.COM, LLC. | |||||
| * | |||||
| * 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: | |||||
| * | |||||
| * 1. The above copyright notice and this permission notice shall be | |||||
| * included in all copies or substantial portions of the Software. | |||||
| * | |||||
| * 2. If the Software is incorporated into a build system that allows | |||||
| * selection among a list of target devices, then similar target | |||||
| * devices manufactured by PJRC.COM must be included in the list of | |||||
| * target devices and selectable in the same manner. | |||||
| * | |||||
| * 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. | |||||
| */ | |||||
| extern "C" { | |||||
| #include "usb_dev.h" | |||||
| } | |||||
| #include "usb_flightsim.h" | |||||
| #include "debug/printf.h" | |||||
| #include "avr/pgmspace.h" | |||||
| #include "core_pins.h" // for yield(), millis() | |||||
| #include <string.h> // for memcpy() | |||||
| #ifdef FLIGHTSIM_INTERFACE // defined by usb_dev.h -> usb_desc.h | |||||
| #if F_CPU >= 20000000 | |||||
| FlightSimCommand * FlightSimCommand::first = NULL; | |||||
| FlightSimCommand * FlightSimCommand::last = NULL; | |||||
| FlightSimInteger * FlightSimInteger::first = NULL; | |||||
| FlightSimInteger * FlightSimInteger::last = NULL; | |||||
| FlightSimFloat * FlightSimFloat::first = NULL; | |||||
| FlightSimFloat * FlightSimFloat::last = NULL; | |||||
| /// JB | |||||
| FlightSimEvent * FlightSimEvent::first = NULL; | |||||
| FlightSimEvent * FlightSimEvent::last = NULL; | |||||
| FlightSimData * FlightSimData::first = NULL; | |||||
| FlightSimData * FlightSimData::last = NULL; | |||||
| /// JB End | |||||
| uint8_t FlightSimClass::enabled = 0; | |||||
| uint8_t FlightSimClass::request_id_messages = 0; | |||||
| unsigned long FlightSimClass::frameCount = 0; | |||||
| elapsedMillis FlightSimClass::enableTimeout; | |||||
| static unsigned int unassigned_id = 1; // TODO: move into FlightSimClass | |||||
| static uint8_t tx_noautoflush=0; | |||||
| static uint8_t transmit_previous_timeout=0; | |||||
| #define TX_NUM 8 | |||||
| static transfer_t tx_transfer[TX_NUM] __attribute__ ((used, aligned(32))); | |||||
| DMAMEM static uint8_t txbuffer[FLIGHTSIM_TX_SIZE * TX_NUM] __attribute__ ((aligned(32))); | |||||
| static uint8_t tx_head=0; | |||||
| static uint16_t tx_available=0; | |||||
| #define RX_NUM 6 | |||||
| static transfer_t rx_transfer[RX_NUM] __attribute__ ((used, aligned(32))); | |||||
| DMAMEM static uint8_t rx_buffer[RX_NUM * FLIGHTSIM_RX_SIZE] __attribute__ ((aligned(32))); | |||||
| static volatile uint8_t rx_head; | |||||
| static volatile uint8_t rx_tail; | |||||
| static uint8_t rx_list[RX_NUM + 1]; | |||||
| static volatile uint32_t rx_available; | |||||
| extern "C" { | |||||
| static void rx_queue_transfer(int i); | |||||
| static void rx_event(transfer_t *t); | |||||
| static void* usb_flightsim_get_packet(); | |||||
| static void usb_flightsim_free_packet(); | |||||
| static bool wait_for_tx_buf(transfer_t *xfer); | |||||
| static void send_packet(transfer_t *xfer); | |||||
| } | |||||
| // When the PC isn't listening, how long do we wait before discarding data? | |||||
| #define TX_TIMEOUT_MSEC 40 | |||||
| extern volatile uint8_t usb_configuration; | |||||
| FlightSimCommand::FlightSimCommand() | |||||
| { | |||||
| id = unassigned_id++; | |||||
| if (!first) { | |||||
| first = this; | |||||
| } else { | |||||
| last->next = this; | |||||
| } | |||||
| last = this; | |||||
| name = NULL; | |||||
| next = NULL; | |||||
| FlightSimClass::request_id_messages = 1; | |||||
| } | |||||
| void FlightSimCommand::identify(void) | |||||
| { | |||||
| uint8_t len, buf[6]; | |||||
| if (!FlightSim.enabled || !name) return; | |||||
| len = strlen((const char *)name); | |||||
| buf[0] = len + 6; | |||||
| buf[1] = 1; | |||||
| buf[2] = id; | |||||
| buf[3] = id >> 8; | |||||
| buf[4] = 0; | |||||
| buf[5] = 0; | |||||
| FlightSimClass::xmit(buf, 6, name, len); | |||||
| } | |||||
| void FlightSimCommand::sendcmd(uint8_t n) | |||||
| { | |||||
| uint8_t buf[4]; | |||||
| if (!FlightSim.enabled || !name) return; | |||||
| buf[0] = 4; | |||||
| buf[1] = n; | |||||
| buf[2] = id; | |||||
| buf[3] = id >> 8; | |||||
| FlightSimClass::xmit(buf, 4, NULL, 0); | |||||
| } | |||||
| /// JB | |||||
| FlightSimEvent::FlightSimEvent() | |||||
| { | |||||
| id = unassigned_id++; | |||||
| if (!first) { | |||||
| first = this; | |||||
| } else { | |||||
| last->next = this; | |||||
| } | |||||
| last = this; | |||||
| name = NULL; | |||||
| next = NULL; | |||||
| occur_callback = NULL; | |||||
| occurredFlag = 0; | |||||
| callbackInfo = NULL; | |||||
| hasCallbackInfo = 0; | |||||
| value = 0; | |||||
| FlightSimClass::request_id_messages = 1; | |||||
| } | |||||
| void FlightSimEvent::identify(void) | |||||
| { | |||||
| uint8_t len, buf[6]; | |||||
| if (!FlightSim.enabled || !name) return; | |||||
| len = strlen((const char *)name); | |||||
| buf[0] = len + 6; | |||||
| buf[1] = 1; | |||||
| buf[2] = id; | |||||
| buf[3] = id >> 8; | |||||
| buf[4] = 3; | |||||
| buf[5] = 0; | |||||
| FlightSimClass::xmit(buf, 6, name, len); | |||||
| } | |||||
| void FlightSimEvent::send(unsigned int data, unsigned int flags) | |||||
| { | |||||
| uint8_t buf[4]; | |||||
| uint32_t txData[2]; | |||||
| if (!FlightSim.enabled || !name) return; | |||||
| buf[0] = 12; | |||||
| buf[1] = 7; | |||||
| buf[2] = id; | |||||
| buf[3] = id >> 8; | |||||
| value = data; | |||||
| txData[0] = data; | |||||
| txData[1] = flags; | |||||
| FlightSimClass::xmit(buf, 4, (uint8_t *)&txData, 8); | |||||
| } | |||||
| void FlightSimEvent::update(long val) | |||||
| { | |||||
| value = (unsigned int) val; | |||||
| occurredFlag = true; | |||||
| if (occur_callback) { | |||||
| if (!hasCallbackInfo) { | |||||
| (*occur_callback)(val); | |||||
| } else { | |||||
| (*(void(*)(long,void*))occur_callback)(val,callbackInfo); | |||||
| } | |||||
| } | |||||
| } | |||||
| FlightSimEvent * FlightSimEvent::find(unsigned int n) | |||||
| { | |||||
| for (FlightSimEvent *p = first; p; p = p->next) { | |||||
| if (p->id == n) return p; | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| FlightSimData::FlightSimData() | |||||
| { | |||||
| id = unassigned_id++; | |||||
| if (!first) { | |||||
| first = this; | |||||
| } else { | |||||
| last->next = this; | |||||
| } | |||||
| last = this; | |||||
| name = NULL; | |||||
| next = NULL; | |||||
| valueLen = 0; | |||||
| hasCallbackInfo = 0; | |||||
| callbackWithObject = 0; | |||||
| callbackInfo = NULL; | |||||
| change_callback = NULL; | |||||
| FlightSimClass::request_id_messages = 1; | |||||
| } | |||||
| void FlightSimData::identify(void) | |||||
| { | |||||
| uint8_t len, buf[6]; | |||||
| if (!FlightSim.enabled || !name) return; | |||||
| len = strlen((const char *)name); | |||||
| buf[0] = len + 6; | |||||
| buf[1] = 1; | |||||
| buf[2] = id; | |||||
| buf[3] = id >> 8; | |||||
| buf[4] = 4; | |||||
| buf[5] = 0; | |||||
| FlightSimClass::xmit(buf, 6, name, len); | |||||
| } | |||||
| void FlightSimData::update(char *val, size_t len) | |||||
| { | |||||
| valueLen = len; | |||||
| memcpy(value, val, len); | |||||
| if (len<FLIGHTSIM_DATA_MAXLEN) { | |||||
| memset(value+len,0,FLIGHTSIM_DATA_MAXLEN-len); | |||||
| } | |||||
| if (change_callback) { | |||||
| if (!callbackWithObject) { | |||||
| if (!hasCallbackInfo) { | |||||
| (*change_callback)(value); | |||||
| } else { | |||||
| (*(void(*)(char*,void*))change_callback)(value,callbackInfo); | |||||
| } | |||||
| } else { | |||||
| if (!hasCallbackInfo) { | |||||
| (*(void(*)(FlightSimData*))change_callback)(this); | |||||
| } else { | |||||
| (*(void(*)(FlightSimData*,void*))change_callback)(this,callbackInfo); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| FlightSimData * FlightSimData::find(unsigned int n) | |||||
| { | |||||
| for (FlightSimData *p = first; p; p = p->next) { | |||||
| if (p->id == n) return p; | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| /// JB End | |||||
| FlightSimInteger::FlightSimInteger() | |||||
| { | |||||
| id = unassigned_id++; | |||||
| if (!first) { | |||||
| first = this; | |||||
| } else { | |||||
| last->next = this; | |||||
| } | |||||
| last = this; | |||||
| name = NULL; | |||||
| next = NULL; | |||||
| value = 0; | |||||
| change_callback = NULL; | |||||
| callbackInfo = NULL; | |||||
| hasCallbackInfo = false; | |||||
| FlightSimClass::request_id_messages = 1; | |||||
| } | |||||
| void FlightSimInteger::identify(void) | |||||
| { | |||||
| uint8_t len, buf[6]; | |||||
| if (!FlightSim.enabled || !name) return; | |||||
| len = strlen((const char *)name); | |||||
| buf[0] = len + 6; | |||||
| buf[1] = 1; | |||||
| buf[2] = id; | |||||
| buf[3] = id >> 8; | |||||
| buf[4] = 1; | |||||
| buf[5] = 0; | |||||
| FlightSimClass::xmit(buf, 6, name, len); | |||||
| } | |||||
| void FlightSimInteger::write(long val) | |||||
| { | |||||
| uint8_t buf[6]; | |||||
| value = val; | |||||
| if (!FlightSim.enabled || !name) return; // TODO: mark as dirty | |||||
| buf[0] = 10; | |||||
| buf[1] = 2; | |||||
| buf[2] = id; | |||||
| buf[3] = id >> 8; | |||||
| buf[4] = 1; | |||||
| buf[5] = 0; | |||||
| FlightSimClass::xmit(buf, 6, (uint8_t *)&value, 4); | |||||
| } | |||||
| void FlightSimInteger::update(long val) | |||||
| { | |||||
| value = val; | |||||
| if (change_callback) { | |||||
| if (!hasCallbackInfo) { | |||||
| (*change_callback)(val); | |||||
| } else { | |||||
| (*(void(*)(long,void*))change_callback)(val,callbackInfo); | |||||
| } | |||||
| } | |||||
| } | |||||
| FlightSimInteger * FlightSimInteger::find(unsigned int n) | |||||
| { | |||||
| for (FlightSimInteger *p = first; p; p = p->next) { | |||||
| if (p->id == n) return p; | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| FlightSimFloat::FlightSimFloat() | |||||
| { | |||||
| id = unassigned_id++; | |||||
| if (!first) { | |||||
| first = this; | |||||
| } else { | |||||
| last->next = this; | |||||
| } | |||||
| last = this; | |||||
| name = NULL; | |||||
| next = NULL; | |||||
| value = 0; | |||||
| change_callback = NULL; | |||||
| hasCallbackInfo = false; | |||||
| callbackInfo = NULL; | |||||
| FlightSimClass::request_id_messages = 1; | |||||
| } | |||||
| void FlightSimFloat::identify(void) | |||||
| { | |||||
| uint8_t len, buf[6]; | |||||
| if (!FlightSim.enabled || !name) return; | |||||
| len = strlen((const char *)name); | |||||
| buf[0] = len + 6; | |||||
| buf[1] = 1; | |||||
| buf[2] = id; | |||||
| buf[3] = id >> 8; | |||||
| buf[4] = 2; | |||||
| buf[5] = 0; | |||||
| FlightSimClass::xmit(buf, 6, name, len); | |||||
| } | |||||
| void FlightSimFloat::write(float val) | |||||
| { | |||||
| uint8_t buf[6]; | |||||
| value = val; | |||||
| if (!FlightSim.enabled || !name) return; // TODO: mark as dirty | |||||
| buf[0] = 10; | |||||
| buf[1] = 2; | |||||
| buf[2] = id; | |||||
| buf[3] = id >> 8; | |||||
| buf[4] = 2; | |||||
| buf[5] = 0; | |||||
| FlightSimClass::xmit(buf, 6, (uint8_t *)&value, 4); | |||||
| } | |||||
| void FlightSimFloat::update(float val) | |||||
| { | |||||
| value = val; | |||||
| if (change_callback) { // add: JB | |||||
| if (!hasCallbackInfo) { | |||||
| (*change_callback)(val); | |||||
| } else { | |||||
| (*(void(*)(float,void*))change_callback)(val,callbackInfo); | |||||
| } | |||||
| } | |||||
| } | |||||
| FlightSimFloat * FlightSimFloat::find(unsigned int n) | |||||
| { | |||||
| for (FlightSimFloat *p = first; p; p = p->next) { | |||||
| if (p->id == n) return p; | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| FlightSimClass::FlightSimClass() | |||||
| { | |||||
| } | |||||
| void FlightSimClass::update(void) | |||||
| { | |||||
| uint8_t len, maxlen, type, *p, *end; | |||||
| union { | |||||
| uint8_t b[4]; | |||||
| long l; | |||||
| float f; | |||||
| } data; | |||||
| void *rx_packet; | |||||
| uint16_t id; | |||||
| while (1) { | |||||
| if (!usb_configuration) break; | |||||
| rx_packet = usb_flightsim_get_packet(); | |||||
| if (!rx_packet) break; | |||||
| p = (uint8_t*) rx_packet; | |||||
| end = p + FLIGHTSIM_RX_SIZE; | |||||
| maxlen = FLIGHTSIM_RX_SIZE; | |||||
| do { | |||||
| len = p[0]; | |||||
| if (len < 2 || len > maxlen) break; | |||||
| switch (p[1]) { | |||||
| case 0x02: // write data | |||||
| if (len < 10) break; | |||||
| id = p[2] | (p[3] << 8); | |||||
| type = p[4]; | |||||
| if (type == 1) { | |||||
| FlightSimInteger *item = FlightSimInteger::find(id); | |||||
| if (!item) break; | |||||
| data.b[0] = p[6]; | |||||
| data.b[1] = p[7]; | |||||
| data.b[2] = p[8]; | |||||
| data.b[3] = p[9]; | |||||
| item->update(data.l); | |||||
| } else if (type == 2) { | |||||
| FlightSimFloat *item = FlightSimFloat::find(id); | |||||
| if (!item) break; | |||||
| data.b[0] = p[6]; | |||||
| data.b[1] = p[7]; | |||||
| data.b[2] = p[8]; | |||||
| data.b[3] = p[9]; | |||||
| item->update(data.f); | |||||
| /// JB | |||||
| } else if (type == 3) { | |||||
| FlightSimEvent *item = FlightSimEvent::find(id); | |||||
| if (!item) break; | |||||
| data.b[0] = p[6]; | |||||
| data.b[1] = p[7]; | |||||
| data.b[2] = p[8]; | |||||
| data.b[3] = p[9]; | |||||
| item->update(data.f); | |||||
| } else if (type == 4) { | |||||
| FlightSimData *item = FlightSimData::find(id); | |||||
| if (!item) break; | |||||
| item->update(((char*)p)+6,len-6); | |||||
| /// JB End | |||||
| } | |||||
| break; | |||||
| case 0x03: // enable/disable | |||||
| if (len < 4) break; | |||||
| switch (p[2]) { | |||||
| case 1: | |||||
| request_id_messages = 1; | |||||
| /* no break */ | |||||
| case 2: | |||||
| enable(); | |||||
| frameCount++; | |||||
| break; | |||||
| case 3: | |||||
| disable(); | |||||
| } | |||||
| } | |||||
| p += len; | |||||
| maxlen -= len; | |||||
| } while (p < end); | |||||
| usb_flightsim_free_packet(); | |||||
| } | |||||
| if (enabled && request_id_messages) { | |||||
| request_id_messages = 0; | |||||
| for (FlightSimCommand *p = FlightSimCommand::first; p; p = p->next) { | |||||
| p->identify(); | |||||
| } | |||||
| /// JB | |||||
| for (FlightSimEvent *p = FlightSimEvent::first; p; p = p->next) { | |||||
| p->identify(); | |||||
| } | |||||
| for (FlightSimData *p = FlightSimData::first; p; p=p->next) { | |||||
| p->identify(); | |||||
| } | |||||
| /// JB End | |||||
| for (FlightSimInteger *p = FlightSimInteger::first; p; p = p->next) { | |||||
| p->identify(); | |||||
| // TODO: send any dirty data | |||||
| } | |||||
| for (FlightSimFloat *p = FlightSimFloat::first; p; p = p->next) { | |||||
| p->identify(); | |||||
| // TODO: send any dirty data | |||||
| } | |||||
| } | |||||
| } | |||||
| bool FlightSimClass::isEnabled(void) | |||||
| { | |||||
| if (!usb_configuration) return false; | |||||
| if (!enabled) return false; | |||||
| if (enableTimeout > 1500) return false; | |||||
| return true; | |||||
| } | |||||
| void FlightSimClass::xmit(const void *p1, uint8_t n1, const void *p2, uint8_t n2) | |||||
| { | |||||
| if (!enabled || !usb_configuration) return; | |||||
| uint16_t total = n1 + n2; | |||||
| if (total > FLIGHTSIM_TX_SIZE) { | |||||
| xmit_big_packet(p1, n1, p2, n2); | |||||
| return; | |||||
| } | |||||
| // handle small packets | |||||
| tx_noautoflush = 1; | |||||
| transfer_t *xfer = tx_transfer + tx_head; | |||||
| if (!wait_for_tx_buf(xfer)) return; | |||||
| if (total > tx_available) { | |||||
| // send previous packet | |||||
| uint8_t *txdata = (uint8_t *)(txbuffer + (tx_head * FLIGHTSIM_TX_SIZE) + (FLIGHTSIM_TX_SIZE - tx_available)); | |||||
| while (tx_available > 0) { | |||||
| // fill packet | |||||
| *txdata++ = 0; | |||||
| tx_available--; | |||||
| } | |||||
| send_packet(xfer); | |||||
| xfer = tx_transfer + tx_head; | |||||
| if (!wait_for_tx_buf(xfer)) return; | |||||
| } | |||||
| uint8_t *txdata = (uint8_t *)(txbuffer + (tx_head * FLIGHTSIM_TX_SIZE) + (FLIGHTSIM_TX_SIZE - tx_available)); | |||||
| memcpy(txdata, p1, n1); | |||||
| tx_available -= n1; | |||||
| txdata += n1; | |||||
| if (n2 > 0) { | |||||
| memcpy(txdata, p2, n2); | |||||
| tx_available -= n2; | |||||
| } | |||||
| if (tx_available == 0) { | |||||
| // packet filled, send it | |||||
| send_packet(xfer); | |||||
| } else { | |||||
| // wait for send until next SOF | |||||
| usb_start_sof_interrupts(FLIGHTSIM_INTERFACE); | |||||
| } | |||||
| tx_noautoflush = 0; | |||||
| } | |||||
| void FlightSimClass::xmit_big_packet(const void *p1, uint8_t n1, const void *p2, uint8_t n2) | |||||
| { | |||||
| uint16_t remaining = n1 + n2; | |||||
| if (remaining > 255) { | |||||
| printf("Invalid flight sim packet length (>255)"); | |||||
| return; | |||||
| } | |||||
| tx_noautoflush =1; // don't mess with my data, I'm working on it! | |||||
| transfer_t *xfer = tx_transfer + tx_head; | |||||
| if (!wait_for_tx_buf(xfer)) return; // after this, tx_available is guaranteed to be > 0 | |||||
| bool part2 = false; | |||||
| uint8_t remainingPart1 = n1; | |||||
| const uint8_t *dataPtr = (const uint8_t*)p1; | |||||
| bool writeFragmentHeader = false; | |||||
| uint8_t fragmentCounter = 1; | |||||
| uint8_t *txdata = (uint8_t *)(txbuffer + (tx_head * FLIGHTSIM_TX_SIZE) + (FLIGHTSIM_TX_SIZE - tx_available)); | |||||
| // fill first packet with whatever fits | |||||
| uint8_t partLen = tx_available > n1 ? n1 : tx_available; | |||||
| // copy first part, containing total packet length | |||||
| printf("%d bytes free, adding first %d bytes from p1, writing to %x\n", tx_available, partLen, txdata); | |||||
| memcpy(txdata, dataPtr, partLen); | |||||
| remainingPart1 -= partLen; | |||||
| txdata += partLen; | |||||
| tx_available -= partLen; | |||||
| if (remainingPart1) { | |||||
| // there still is data from the first part that | |||||
| // will go to the next packet. The boolean variable | |||||
| // part2 remains false | |||||
| remaining = remainingPart1+n2; | |||||
| dataPtr += partLen; | |||||
| } else { | |||||
| // maybe we have space for some data from the second part | |||||
| part2=true; | |||||
| // there is no need here to check whether tx_available is | |||||
| // bigger than n2. It's not. If it were, all the data | |||||
| // would have fit in a single packet and xmit_big_packet | |||||
| // would never have been called... | |||||
| printf("adding first %d bytes from p2, writing to %x\n", tx_available, txdata); | |||||
| remaining = n2; | |||||
| if (tx_available) { | |||||
| memcpy(txdata, p2, tx_available); | |||||
| remaining -= tx_available; | |||||
| } | |||||
| dataPtr = (const uint8_t*)p2 + tx_available; | |||||
| } | |||||
| // first packet filled, send it | |||||
| tx_available = 0; | |||||
| send_packet(xfer); | |||||
| xfer = tx_transfer + tx_head; | |||||
| writeFragmentHeader = true; | |||||
| printf("remaining bytes to send: %d\n", remaining); | |||||
| while (remaining >0) { | |||||
| if (!wait_for_tx_buf(xfer)) return; | |||||
| uint8_t *txdata = (uint8_t *)(txbuffer + (tx_head * FLIGHTSIM_TX_SIZE) + (FLIGHTSIM_TX_SIZE - tx_available)); | |||||
| if (writeFragmentHeader) { | |||||
| printf("writing header of fragment %d to %x\n", fragmentCounter, txdata); | |||||
| *txdata++=(remaining+3 <= FLIGHTSIM_TX_SIZE) ? (uint8_t) remaining+3 : FLIGHTSIM_TX_SIZE; | |||||
| *txdata++=0xff; | |||||
| *txdata++=fragmentCounter++; | |||||
| tx_available -= 3; | |||||
| } | |||||
| if (!part2) { | |||||
| // we still need to send the first part | |||||
| uint8_t partLen = tx_available > remainingPart1 ? remainingPart1 : tx_available; | |||||
| printf("copying remaining %d bytes from first part to %x\n", partLen, txdata); | |||||
| memcpy(txdata, dataPtr, partLen); | |||||
| dataPtr += partLen; | |||||
| txdata += partLen; | |||||
| tx_available -= partLen; | |||||
| remaining -= partLen; | |||||
| remainingPart1 -= partLen; | |||||
| if (!remainingPart1) { | |||||
| part2=true; | |||||
| dataPtr = (const uint8_t*)p2; | |||||
| } | |||||
| } | |||||
| if (part2) { | |||||
| uint8_t partLen = tx_available > remaining ? remaining : tx_available; | |||||
| printf("copying %d bytes from second part to %x\n", partLen, txdata); | |||||
| if (partLen) { | |||||
| memcpy(txdata, dataPtr, partLen); | |||||
| remaining -= partLen; | |||||
| tx_available -= partLen; | |||||
| txdata += partLen; | |||||
| dataPtr += partLen; | |||||
| } | |||||
| } | |||||
| writeFragmentHeader = true; | |||||
| if (!tx_available) { | |||||
| // packet filled, send it | |||||
| send_packet(xfer); | |||||
| xfer = tx_transfer + tx_head; | |||||
| } else { | |||||
| // send on next SOF | |||||
| printf("tx_available: %d\n", tx_available); | |||||
| usb_start_sof_interrupts(FLIGHTSIM_INTERFACE); | |||||
| } | |||||
| } | |||||
| tx_noautoflush = 0; // data is ready to be transmitted on start of USB token | |||||
| } | |||||
| extern "C" { | |||||
| void usb_flightsim_configure() { | |||||
| printf("Flightsim_configure\n"); | |||||
| memset(tx_transfer, 0, sizeof(tx_transfer)); | |||||
| tx_head = 0; | |||||
| tx_available = 0; | |||||
| memset(rx_transfer, 0, sizeof(rx_transfer)); | |||||
| printf("tx_transfer: %x\n", tx_transfer); | |||||
| printf("txbuffer: %x\n", txbuffer); | |||||
| printf("rx_transfer: %x\n", rx_transfer); | |||||
| printf("rxbuffer: %x\n", rx_buffer); | |||||
| rx_head = 0; | |||||
| rx_tail = 0; | |||||
| rx_available = 0; | |||||
| usb_config_rx(FLIGHTSIM_RX_ENDPOINT, FLIGHTSIM_RX_SIZE, 0, rx_event); | |||||
| usb_config_tx(FLIGHTSIM_TX_ENDPOINT, FLIGHTSIM_TX_SIZE, 0, NULL); // TODO: is ZLP needed? | |||||
| int i; | |||||
| for (i=0; i < RX_NUM; i++) rx_queue_transfer(i); | |||||
| tx_noautoflush = 0; | |||||
| transmit_previous_timeout = 0; | |||||
| } | |||||
| // This gets called from usb_isr when a USB start token arrives. | |||||
| // If we have a packet to transmit AND transmission isn't disabled | |||||
| // by tx_noautoflush, we fill it up with zeros and send it out | |||||
| // to USB | |||||
| void usb_flightsim_flush_output(void) | |||||
| { | |||||
| if (tx_noautoflush == 0 && tx_available > 0) { | |||||
| printf(" flush, %d %d\n", FLIGHTSIM_TX_SIZE, tx_available); | |||||
| uint32_t head = tx_head; | |||||
| transfer_t *xfer = tx_transfer + head; | |||||
| uint8_t *txbuf = txbuffer + (head * FLIGHTSIM_TX_SIZE); | |||||
| uint8_t *txPtr = txbuf + (FLIGHTSIM_TX_SIZE - tx_available); | |||||
| while (tx_available>0) { | |||||
| *txPtr++ = 0; | |||||
| tx_available--; | |||||
| } | |||||
| usb_prepare_transfer(xfer, txbuf, FLIGHTSIM_TX_SIZE, 0); | |||||
| arm_dcache_flush_delete(txbuf, FLIGHTSIM_TX_SIZE); | |||||
| usb_transmit(FLIGHTSIM_TX_ENDPOINT, xfer); | |||||
| if (++head >= TX_NUM) head = 0; | |||||
| tx_head = head; | |||||
| usb_stop_sof_interrupts(FLIGHTSIM_INTERFACE); | |||||
| } | |||||
| } | |||||
| static bool wait_for_tx_buf(transfer_t *xfer) { | |||||
| uint32_t wait_begin_at = systick_millis_count; | |||||
| while (!tx_available) { | |||||
| uint32_t status = usb_transfer_status(xfer); | |||||
| if (!(status & 0x80)) { | |||||
| if (status & 0x68) { | |||||
| // TODO: what if status has errors??? | |||||
| } | |||||
| tx_available = FLIGHTSIM_TX_SIZE; | |||||
| transmit_previous_timeout = 0; | |||||
| break; | |||||
| } | |||||
| if (systick_millis_count - wait_begin_at > TX_TIMEOUT_MSEC) { | |||||
| transmit_previous_timeout = 1; | |||||
| } | |||||
| if (transmit_previous_timeout) { | |||||
| printf("Flight sim tx timeout"); | |||||
| return false; | |||||
| } | |||||
| if (!usb_configuration) return false; | |||||
| yield(); | |||||
| } | |||||
| return true; | |||||
| } | |||||
| static void send_packet(transfer_t *xfer) { | |||||
| uint32_t head = tx_head; | |||||
| uint8_t *txbuf = txbuffer + (tx_head * FLIGHTSIM_TX_SIZE); | |||||
| usb_prepare_transfer(xfer, txbuf, FLIGHTSIM_TX_SIZE, 0); | |||||
| arm_dcache_flush_delete(txbuf, FLIGHTSIM_TX_SIZE); | |||||
| usb_transmit(FLIGHTSIM_TX_ENDPOINT, xfer); | |||||
| if (++head >= TX_NUM) head = 0; | |||||
| tx_head = head; | |||||
| usb_stop_sof_interrupts(FLIGHTSIM_INTERFACE); | |||||
| } | |||||
| static void rx_queue_transfer(int i) | |||||
| { | |||||
| NVIC_DISABLE_IRQ(IRQ_USB1); | |||||
| void *buffer = rx_buffer + i * FLIGHTSIM_RX_SIZE; | |||||
| usb_prepare_transfer(rx_transfer + i, buffer, FLIGHTSIM_RX_SIZE, i); | |||||
| arm_dcache_delete(buffer, FLIGHTSIM_RX_SIZE); | |||||
| usb_receive(FLIGHTSIM_RX_ENDPOINT, rx_transfer + i); | |||||
| NVIC_ENABLE_IRQ(IRQ_USB1); | |||||
| } | |||||
| static void rx_event(transfer_t *t) | |||||
| { | |||||
| int len = FLIGHTSIM_RX_SIZE - ((t->status >> 16) & 0x7FFF); | |||||
| len &= 0xFFFC; // MIDI packets must be multiple of 4 bytes | |||||
| int i = t->callback_param; | |||||
| // printf("Flight sim rx event, len=%d, i=%d", len, i); | |||||
| if (len == FLIGHTSIM_RX_SIZE) { | |||||
| uint32_t head = rx_head; | |||||
| if (++head > RX_NUM) head = 0; | |||||
| rx_list[head] = i; | |||||
| rx_head = head; | |||||
| rx_available += len; | |||||
| } else { | |||||
| // received packet with invalid length | |||||
| rx_queue_transfer(i); | |||||
| } | |||||
| // printf(" ...done\n"); | |||||
| } | |||||
| static void* usb_flightsim_get_packet(void) | |||||
| { | |||||
| void *result = NULL; | |||||
| NVIC_DISABLE_IRQ(IRQ_USB1); | |||||
| uint32_t tail = rx_tail; | |||||
| if (tail != rx_head) { | |||||
| if (++tail > RX_NUM) tail = 0; | |||||
| uint32_t i = rx_list[tail]; | |||||
| result = rx_buffer + i * FLIGHTSIM_RX_SIZE; | |||||
| rx_available -= FLIGHTSIM_RX_SIZE; | |||||
| } | |||||
| NVIC_ENABLE_IRQ(IRQ_USB1); | |||||
| return result; | |||||
| } | |||||
| static void usb_flightsim_free_packet() { | |||||
| NVIC_DISABLE_IRQ(IRQ_USB1); | |||||
| uint32_t tail = rx_tail; | |||||
| if (tail != rx_head) { | |||||
| if (++tail > RX_NUM) tail = 0; | |||||
| uint32_t i = rx_list[tail]; | |||||
| rx_tail = tail; | |||||
| rx_queue_transfer(i); | |||||
| } | |||||
| NVIC_ENABLE_IRQ(IRQ_USB1); | |||||
| } | |||||
| } // extern "C" | |||||
| #endif // F_CPU | |||||
| #endif // FLIGHTSIM_INTERFACE |
| /* Teensyduino Core Library | |||||
| * http://www.pjrc.com/teensy/ | |||||
| * Copyright (c) 2013 PJRC.COM, LLC. | |||||
| * | |||||
| * 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: | |||||
| * | |||||
| * 1. The above copyright notice and this permission notice shall be | |||||
| * included in all copies or substantial portions of the Software. | |||||
| * | |||||
| * 2. If the Software is incorporated into a build system that allows | |||||
| * selection among a list of target devices, then similar target | |||||
| * devices manufactured by PJRC.COM must be included in the list of | |||||
| * target devices and selectable in the same manner. | |||||
| * | |||||
| * 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. | |||||
| */ | |||||
| #ifndef USBflightsim_h_ | |||||
| #define USBflightsim_h_ | |||||
| #include "usb_desc.h" | |||||
| #if defined(FLIGHTSIM_INTERFACE) | |||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #endif | |||||
| void usb_flightsim_configure(); | |||||
| void usb_flightsim_flush_output(void); | |||||
| #ifdef __cplusplus | |||||
| } | |||||
| #endif | |||||
| #ifdef __cplusplus | |||||
| #include <inttypes.h> | |||||
| #include "elapsedMillis.h" | |||||
| // workaround for elapsedMillis.h bringing in WProgram.h which brings usb_undef.h | |||||
| //#undef USB_DESC_LIST_DEFINE | |||||
| //#include "usb_desc.h" | |||||
| class FlightSimClass; | |||||
| class FlightSimCommand; | |||||
| class FlightSimInteger; | |||||
| class _XpRefStr_; | |||||
| #define XPlaneRef(str) ((const _XpRefStr_ *)(str)) | |||||
| class FlightSimClass | |||||
| { | |||||
| public: | |||||
| FlightSimClass(); | |||||
| static void update(void); | |||||
| static bool isEnabled(void); | |||||
| static unsigned long getFrameCount(void) { return frameCount; } | |||||
| private: | |||||
| static uint8_t request_id_messages; | |||||
| static uint8_t enabled; | |||||
| static elapsedMillis enableTimeout; | |||||
| static unsigned long frameCount; | |||||
| static void enable(void) { enabled = 1; enableTimeout = 0; } | |||||
| static void disable(void) { enabled = 0; } | |||||
| static void xmit(const void *p1, uint8_t n1, const void *p2, uint8_t n2); | |||||
| static void xmit_big_packet(const void *p1, uint8_t n1, const void *p2, uint8_t n2); | |||||
| friend class FlightSimCommand; | |||||
| friend class FlightSimInteger; | |||||
| friend class FlightSimFloat; | |||||
| /// JB | |||||
| friend class FlightSimEvent; | |||||
| friend class FlightSimData; | |||||
| /// JB End | |||||
| }; | |||||
| class FlightSimCommand | |||||
| { | |||||
| public: | |||||
| FlightSimCommand(); | |||||
| void assign(const _XpRefStr_ *s) { name = s; if (FlightSimClass::enabled) identify(); } | |||||
| FlightSimCommand & operator = (const _XpRefStr_ *s) { assign(s); return *this; } | |||||
| void begin(void) { sendcmd(4); } | |||||
| void end(void) { sendcmd(5); } | |||||
| FlightSimCommand & operator = (int n) { sendcmd((n) ? 4 : 5); return *this; } | |||||
| void once(void) { sendcmd(6); } | |||||
| void identify(void); | |||||
| private: | |||||
| unsigned int id; | |||||
| const _XpRefStr_ *name; | |||||
| void sendcmd(uint8_t n); | |||||
| FlightSimCommand *next; | |||||
| static FlightSimCommand *first; | |||||
| static FlightSimCommand *last; | |||||
| friend class FlightSimClass; | |||||
| }; | |||||
| /// JB | |||||
| class FlightSimEvent | |||||
| { | |||||
| public: | |||||
| FlightSimEvent(); | |||||
| void assign(const _XpRefStr_ *s) { name = s; if (FlightSimClass::enabled) identify(); } | |||||
| FlightSimEvent & operator = (const _XpRefStr_ *s) { assign(s); return *this; } | |||||
| void send() { send(0,0); } | |||||
| void send(int data) { send(data,0); } | |||||
| void sendOnce() { send(0,0); } | |||||
| void sendOnce(int data) { send(data,0); } | |||||
| void sendRepeat(int data, uint16_t initialDelay, uint16_t repeatDelay) { send(data, initialDelay<<16 | repeatDelay); } | |||||
| void sendRepeat(uint16_t initialDelay, uint16_t repeatDelay) { send(0, initialDelay<<16 | repeatDelay); } | |||||
| void stopRepeat() { send(0,-1); } | |||||
| FlightSimEvent & operator = (int n) { send(n,0); return *this; } | |||||
| bool occurred() { bool hasOccurred = occurredFlag; occurredFlag = 0; return hasOccurred; } | |||||
| void identify(); | |||||
| static FlightSimEvent * find(unsigned int n); | |||||
| void update(long val); | |||||
| void onOccur(void (*fptr)(long)) { | |||||
| hasCallbackInfo=false; | |||||
| occur_callback = fptr; | |||||
| } | |||||
| void onOccur(void (*fptr)(long,void*), void* info) { | |||||
| hasCallbackInfo=true; | |||||
| occur_callback = (void (*)(long))fptr; | |||||
| callbackInfo = info; | |||||
| } | |||||
| private: | |||||
| void send(unsigned int data, unsigned int flags); | |||||
| unsigned int id; | |||||
| const _XpRefStr_ *name; | |||||
| bool occurredFlag; | |||||
| unsigned int value; | |||||
| FlightSimEvent *next; | |||||
| void (*occur_callback)(long); | |||||
| void* callbackInfo; | |||||
| bool hasCallbackInfo; | |||||
| static FlightSimEvent *first; | |||||
| static FlightSimEvent *last; | |||||
| friend class FlightSimClass; | |||||
| }; | |||||
| #define FLIGHTSIM_DATA_MAXLEN 58 | |||||
| class FlightSimData | |||||
| { | |||||
| public: | |||||
| FlightSimData(); | |||||
| void assign(const _XpRefStr_ *s) { name = s; if (FlightSimClass::enabled) identify(); } | |||||
| FlightSimData & operator = (const _XpRefStr_ *s) { assign(s); return *this; } | |||||
| char *read() { return value; } | |||||
| operator char* () { return value; } | |||||
| void identify(); | |||||
| void update(char *val, size_t len); | |||||
| size_t len() { return valueLen; } | |||||
| static FlightSimData * find(unsigned int n); | |||||
| void onChange(void (*fptr)(char *)) { | |||||
| hasCallbackInfo = false; | |||||
| change_callback = fptr; | |||||
| } | |||||
| void onChange(void (*fptr)(char *,void *), void *info) { | |||||
| hasCallbackInfo = true; | |||||
| change_callback = (void (*)(char*)) fptr; | |||||
| callbackInfo = info; | |||||
| } | |||||
| void onChange(void (*fptr)(FlightSimData *)) { | |||||
| callbackWithObject = true; | |||||
| hasCallbackInfo = false; | |||||
| change_callback = (void (*)(char*)) fptr; | |||||
| } | |||||
| void onChange(void (*fptr)(FlightSimData *, void*), void* info) { | |||||
| callbackWithObject = true; | |||||
| hasCallbackInfo = true; | |||||
| change_callback = (void (*)(char*)) fptr; | |||||
| callbackInfo = info; | |||||
| } | |||||
| private: | |||||
| unsigned int id; | |||||
| const _XpRefStr_ *name; | |||||
| char value[FLIGHTSIM_DATA_MAXLEN]; | |||||
| size_t valueLen; | |||||
| void (*change_callback)(char *); | |||||
| void* callbackInfo; | |||||
| bool hasCallbackInfo; | |||||
| bool callbackWithObject; | |||||
| FlightSimData *next; | |||||
| static FlightSimData *first; | |||||
| static FlightSimData *last; | |||||
| friend class FlightSimClass; | |||||
| }; | |||||
| /// JB End | |||||
| class FlightSimInteger | |||||
| { | |||||
| public: | |||||
| FlightSimInteger(); | |||||
| void assign(const _XpRefStr_ *s) { name = s; if (FlightSimClass::enabled) identify(); } | |||||
| FlightSimInteger & operator = (const _XpRefStr_ *s) { assign(s); return *this; } | |||||
| void write(long val); | |||||
| FlightSimInteger & operator = (char n) { write((long)n); return *this; } | |||||
| FlightSimInteger & operator = (int n) { write((long)n); return *this; } | |||||
| FlightSimInteger & operator = (long n) { write(n); return *this; } | |||||
| FlightSimInteger & operator = (unsigned char n) { write((long)n); return *this; } | |||||
| FlightSimInteger & operator = (unsigned int n) { write((long)n); return *this; } | |||||
| FlightSimInteger & operator = (unsigned long n) { write((long)n); return *this; } | |||||
| FlightSimInteger & operator = (float n) { write((long)n); return *this; } | |||||
| FlightSimInteger & operator = (double n) { write((long)n); return *this; } | |||||
| long read(void) const { return value; } | |||||
| operator long () const { return value; } | |||||
| void identify(void); | |||||
| void update(long val); | |||||
| static FlightSimInteger * find(unsigned int n); | |||||
| void onChange(void (*fptr)(long)) { | |||||
| hasCallbackInfo=false; | |||||
| change_callback = fptr; | |||||
| } | |||||
| void onChange(void (*fptr)(long,void*), void* info) { | |||||
| hasCallbackInfo=true; | |||||
| change_callback = (void (*)(long))fptr; | |||||
| callbackInfo = info; | |||||
| } | |||||
| // TODO: math operators.... + - * / % ++ -- | |||||
| private: | |||||
| unsigned int id; | |||||
| const _XpRefStr_ *name; | |||||
| long value; | |||||
| void (*change_callback)(long); | |||||
| void* callbackInfo; | |||||
| bool hasCallbackInfo; | |||||
| FlightSimInteger *next; | |||||
| static FlightSimInteger *first; | |||||
| static FlightSimInteger *last; | |||||
| friend class FlightSimClass; | |||||
| }; | |||||
| class FlightSimFloat | |||||
| { | |||||
| public: | |||||
| FlightSimFloat(); | |||||
| void assign(const _XpRefStr_ *s) { name = s; if (FlightSimClass::enabled) identify(); } | |||||
| FlightSimFloat & operator = (const _XpRefStr_ *s) { assign(s); return *this; } | |||||
| void write(float val); | |||||
| FlightSimFloat & operator = (char n) { write((float)n); return *this; } | |||||
| FlightSimFloat & operator = (int n) { write((float)n); return *this; } | |||||
| FlightSimFloat & operator = (long n) { write((float)n); return *this; } | |||||
| FlightSimFloat & operator = (unsigned char n) { write((float)n); return *this; } | |||||
| FlightSimFloat & operator = (unsigned int n) { write((float)n); return *this; } | |||||
| FlightSimFloat & operator = (unsigned long n) { write((float)n); return *this; } | |||||
| FlightSimFloat & operator = (float n) { write(n); return *this; } | |||||
| FlightSimFloat & operator = (double n) { write((float)n); return *this; } | |||||
| float read(void) const { return value; } | |||||
| operator float () const { return value; } | |||||
| void identify(void); | |||||
| void update(float val); | |||||
| static FlightSimFloat * find(unsigned int n); | |||||
| void onChange(void (*fptr)(float)) { | |||||
| hasCallbackInfo=false; | |||||
| change_callback = fptr; | |||||
| } | |||||
| void onChange(void (*fptr)(float,void*), void* info) { | |||||
| hasCallbackInfo=true; | |||||
| change_callback = (void (*)(float))fptr; | |||||
| callbackInfo = info; | |||||
| } | |||||
| // TODO: math operators.... + - * / % ++ -- | |||||
| private: | |||||
| unsigned int id; | |||||
| const _XpRefStr_ *name; | |||||
| float value; | |||||
| void (*change_callback)(float); | |||||
| void* callbackInfo; | |||||
| bool hasCallbackInfo; | |||||
| FlightSimFloat *next; | |||||
| static FlightSimFloat *first; | |||||
| static FlightSimFloat *last; | |||||
| friend class FlightSimClass; | |||||
| }; | |||||
| class FlightSimElapsedFrames | |||||
| { | |||||
| private: | |||||
| unsigned long count; | |||||
| public: | |||||
| FlightSimElapsedFrames(void) { count = FlightSimClass::getFrameCount(); } | |||||
| FlightSimElapsedFrames(unsigned long val) { count = FlightSimClass::getFrameCount() - val; } | |||||
| FlightSimElapsedFrames(const FlightSimElapsedFrames &orig) { count = orig.count; } | |||||
| operator unsigned long () const { return FlightSimClass::getFrameCount() - count; } | |||||
| FlightSimElapsedFrames & operator = (const FlightSimElapsedFrames &rhs) { count = rhs.count; return *this; } | |||||
| FlightSimElapsedFrames & operator = (unsigned long val) { count = FlightSimClass::getFrameCount() - val; return *this; } | |||||
| FlightSimElapsedFrames & operator -= (unsigned long val) { count += val; return *this; } | |||||
| FlightSimElapsedFrames & operator += (unsigned long val) { count -= val; return *this; } | |||||
| FlightSimElapsedFrames operator - (int val) const { FlightSimElapsedFrames r(*this); r.count += val; return r; } | |||||
| FlightSimElapsedFrames operator - (unsigned int val) const { FlightSimElapsedFrames r(*this); r.count += val; return r; } | |||||
| FlightSimElapsedFrames operator - (long val) const { FlightSimElapsedFrames r(*this); r.count += val; return r; } | |||||
| FlightSimElapsedFrames operator - (unsigned long val) const { FlightSimElapsedFrames r(*this); r.count += val; return r; } | |||||
| FlightSimElapsedFrames operator + (int val) const { FlightSimElapsedFrames r(*this); r.count -= val; return r; } | |||||
| FlightSimElapsedFrames operator + (unsigned int val) const { FlightSimElapsedFrames r(*this); r.count -= val; return r; } | |||||
| FlightSimElapsedFrames operator + (long val) const { FlightSimElapsedFrames r(*this); r.count -= val; return r; } | |||||
| FlightSimElapsedFrames operator + (unsigned long val) const { FlightSimElapsedFrames r(*this); r.count -= val; return r; } | |||||
| }; | |||||
| extern FlightSimClass FlightSim; | |||||
| #endif // __cplusplus | |||||
| #endif // FLIGHTSIM_INTERFACE | |||||
| #endif // USBflightsim_h_ |