*/ | */ | ||||
#include "usb_dev.h" | #include "usb_dev.h" | ||||
#include "usb_desc.h" | |||||
#include "usb_flightsim.h" | #include "usb_flightsim.h" | ||||
#include "core_pins.h" // for yield(), millis() | #include "core_pins.h" // for yield(), millis() | ||||
#include <string.h> // for memcpy() | #include <string.h> // for memcpy() | ||||
next = NULL; | next = NULL; | ||||
value = 0; | value = 0; | ||||
change_callback = NULL; | change_callback = NULL; | ||||
callbackInfo = NULL; | |||||
hasCallbackInfo = false; | |||||
FlightSimClass::request_id_messages = 1; | FlightSimClass::request_id_messages = 1; | ||||
} | } | ||||
void FlightSimInteger::update(long val) | void FlightSimInteger::update(long val) | ||||
{ | { | ||||
value = val; | value = val; | ||||
if (change_callback) (*change_callback)(val); | |||||
if (change_callback) { | |||||
if (!hasCallbackInfo) { | |||||
(*change_callback)(val); | |||||
} else { | |||||
(*(void(*)(long,void*))change_callback)(val,callbackInfo); | |||||
} | |||||
} | |||||
} | } | ||||
FlightSimInteger * FlightSimInteger::find(unsigned int n) | FlightSimInteger * FlightSimInteger::find(unsigned int n) | ||||
next = NULL; | next = NULL; | ||||
value = 0; | value = 0; | ||||
change_callback = NULL; | change_callback = NULL; | ||||
hasCallbackInfo = false; | |||||
callbackInfo = NULL; | |||||
FlightSimClass::request_id_messages = 1; | FlightSimClass::request_id_messages = 1; | ||||
} | } | ||||
void FlightSimFloat::update(float val) | void FlightSimFloat::update(float val) | ||||
{ | { | ||||
value = val; | value = val; | ||||
if (change_callback) (*change_callback)(val); | |||||
if (change_callback) { // add: JB | |||||
if (!hasCallbackInfo) { | |||||
(*change_callback)(val); | |||||
} else { | |||||
(*(void(*)(long,void*))change_callback)(val,callbackInfo); | |||||
} | |||||
} | |||||
} | } | ||||
FlightSimFloat * FlightSimFloat::find(unsigned int n) | FlightSimFloat * FlightSimFloat::find(unsigned int n) | ||||
switch (p[2]) { | switch (p[2]) { | ||||
case 1: | case 1: | ||||
request_id_messages = 1; | request_id_messages = 1; | ||||
/* no break */ | |||||
case 2: | case 2: | ||||
enable(); | enable(); | ||||
frameCount++; | frameCount++; | ||||
uint8_t total; | uint8_t total; | ||||
total = n1 + n2; | total = n1 + n2; | ||||
if (total > FLIGHTSIM_TX_SIZE) return; | |||||
if (total > FLIGHTSIM_TX_SIZE) { | |||||
xmit_big_packet(p1, n1, p2, n2); | |||||
return; | |||||
} | |||||
if (!enabled || !usb_configuration) return; | if (!enabled || !usb_configuration) return; | ||||
tx_noautoflush = 1; | tx_noautoflush = 1; | ||||
if (tx_packet) { | if (tx_packet) { | ||||
tx_noautoflush = 0; | tx_noautoflush = 0; | ||||
} | } | ||||
void FlightSimClass::xmit_big_packet(const void *p1, uint16_t n1, const void *p2, uint16_t n2) | |||||
{ | |||||
if (!enabled || !usb_configuration) return; | |||||
bool part2 = false; | |||||
uint16_t remainingPart1 = n1; | |||||
uint16_t remaining; | |||||
const void *dataPtr = p1; | |||||
bool needAdditionalFragment = false; | |||||
uint8_t fragmentCounter = 1; | |||||
tx_noautoflush =1; // don't mess with my data, I'm working on it! | |||||
if (tx_packet) { | |||||
// If we have a current packet, fill it with whatever fits | |||||
uint8_t partLen = FLIGHTSIM_TX_SIZE - tx_packet->index; | |||||
if (partLen > n1) partLen=n1; | |||||
// copy first part | |||||
memcpy(tx_packet->buf + tx_packet->index, dataPtr, partLen); | |||||
remainingPart1 -= partLen; | |||||
tx_packet->index += partLen; | |||||
if (remainingPart1) { | |||||
remaining = remainingPart1+n2; | |||||
dataPtr += partLen; | |||||
} else { | |||||
// maybe we have space for more | |||||
part2=true; | |||||
partLen = FLIGHTSIM_TX_SIZE - tx_packet->index; | |||||
remaining = n2; | |||||
if (partLen) { | |||||
memcpy(tx_packet->buf + tx_packet->index, p2, partLen); | |||||
remaining -= partLen; | |||||
tx_packet->index += partLen; | |||||
dataPtr = p2 + partLen; | |||||
} | |||||
} | |||||
// Packet padding should not be necessary, as xmit_big_packet | |||||
// will only be called for data that doesn't fit in a single | |||||
// packet. So, the previous code should always fill up the | |||||
// first packet. Right? | |||||
for (int i = tx_packet->index; i < FLIGHTSIM_TX_SIZE; i++) { | |||||
tx_packet->buf[i] = 0; | |||||
} | |||||
tx_packet->len = FLIGHTSIM_TX_SIZE; | |||||
usb_tx(FLIGHTSIM_TX_ENDPOINT, tx_packet); | |||||
tx_packet = NULL; | |||||
needAdditionalFragment = true; | |||||
} else { | |||||
remaining = n1+n2; | |||||
} | |||||
while (remaining >0) { | |||||
while (1) { | |||||
if (usb_tx_packet_count(FLIGHTSIM_TX_ENDPOINT) < TX_PACKET_LIMIT) { | |||||
tx_packet = usb_malloc(); | |||||
if (tx_packet) break; | |||||
} | |||||
if (!enabled || !usb_configuration) { | |||||
tx_noautoflush = 0; | |||||
return; | |||||
} | |||||
tx_noautoflush = 0; // you can pick up my data, if you like | |||||
yield(); | |||||
tx_noautoflush = 1; // wait, I'm working on the packet data | |||||
} | |||||
if (needAdditionalFragment) { | |||||
// fragment header | |||||
tx_packet->buf[0]=(remaining+3 <= FLIGHTSIM_TX_SIZE) ? (byte) remaining+3 : FLIGHTSIM_TX_SIZE; | |||||
tx_packet->buf[1]=0xff; | |||||
tx_packet->buf[2]=fragmentCounter++; | |||||
tx_packet->index=3; | |||||
} | |||||
if (!part2) { | |||||
// we still need to send the first part | |||||
uint8_t partLen = FLIGHTSIM_TX_SIZE - tx_packet->index; | |||||
if (partLen > remainingPart1) | |||||
partLen=remainingPart1; | |||||
memcpy(tx_packet->buf + tx_packet->index, dataPtr, partLen); | |||||
dataPtr += partLen; | |||||
remainingPart1 -= partLen; | |||||
tx_packet->index += partLen; | |||||
remaining -= partLen; | |||||
if (!remainingPart1) { | |||||
part2=true; | |||||
dataPtr = p2; | |||||
} | |||||
} | |||||
if (part2) { | |||||
uint8_t partLen = FLIGHTSIM_TX_SIZE - tx_packet->index; | |||||
if (partLen) { | |||||
if (partLen > remaining) | |||||
partLen=remaining; | |||||
memcpy(tx_packet->buf + tx_packet->index, dataPtr, partLen); | |||||
remaining -= partLen; | |||||
tx_packet->index += partLen; | |||||
dataPtr += partLen; | |||||
} | |||||
} | |||||
needAdditionalFragment = true; | |||||
if (tx_packet->index >= FLIGHTSIM_TX_SIZE) { | |||||
tx_packet->len = FLIGHTSIM_TX_SIZE; | |||||
usb_tx(FLIGHTSIM_TX_ENDPOINT, tx_packet); | |||||
tx_packet = NULL; | |||||
} | |||||
} | |||||
tx_noautoflush = 0; // data is ready to be transmitted on start of USB token | |||||
} | |||||
extern "C" { | extern "C" { | ||||
// This gets called 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_callback(void) | void usb_flightsim_flush_callback(void) | ||||
{ | { | ||||
if (tx_noautoflush || !tx_packet || tx_packet->index == 0) return; | if (tx_noautoflush || !tx_packet || tx_packet->index == 0) return; |
static void enable(void) { enabled = 1; enableTimeout = 0; } | static void enable(void) { enabled = 1; enableTimeout = 0; } | ||||
static void disable(void) { enabled = 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(const void *p1, uint8_t n1, const void *p2, uint8_t n2); | ||||
static void xmit_big_packet(const void *p1, uint16_t n1, const void *p2, uint16_t n2); | |||||
friend class FlightSimCommand; | friend class FlightSimCommand; | ||||
friend class FlightSimInteger; | friend class FlightSimInteger; | ||||
friend class FlightSimFloat; | friend class FlightSimFloat; | ||||
void identify(void); | void identify(void); | ||||
void update(long val); | void update(long val); | ||||
static FlightSimInteger * find(unsigned int n); | static FlightSimInteger * find(unsigned int n); | ||||
void onChange(void (*fptr)(long)) { change_callback = fptr; } | |||||
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.... + - * / % ++ -- | // TODO: math operators.... + - * / % ++ -- | ||||
private: | private: | ||||
unsigned int id; | unsigned int id; | ||||
const _XpRefStr_ *name; | const _XpRefStr_ *name; | ||||
long value; | long value; | ||||
void (*change_callback)(long); | void (*change_callback)(long); | ||||
void* callbackInfo; | |||||
bool hasCallbackInfo; | |||||
FlightSimInteger *next; | FlightSimInteger *next; | ||||
static FlightSimInteger *first; | static FlightSimInteger *first; | ||||
static FlightSimInteger *last; | static FlightSimInteger *last; | ||||
void identify(void); | void identify(void); | ||||
void update(float val); | void update(float val); | ||||
static FlightSimFloat * find(unsigned int n); | static FlightSimFloat * find(unsigned int n); | ||||
void onChange(void (*fptr)(float)) { change_callback = fptr; } | |||||
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.... + - * / % ++ -- | // TODO: math operators.... + - * / % ++ -- | ||||
private: | private: | ||||
unsigned int id; | unsigned int id; | ||||
const _XpRefStr_ *name; | const _XpRefStr_ *name; | ||||
float value; | float value; | ||||
void (*change_callback)(float); | void (*change_callback)(float); | ||||
void* callbackInfo; | |||||
bool hasCallbackInfo; | |||||
FlightSimFloat *next; | FlightSimFloat *next; | ||||
static FlightSimFloat *first; | static FlightSimFloat *first; | ||||
static FlightSimFloat *last; | static FlightSimFloat *last; |
void FlightSimCommand::identify(void) | void FlightSimCommand::identify(void) | ||||
{ | { | ||||
uint8_t len, buf[6]; | |||||
uint16_t len; | |||||
uint8_t buf[6]; | |||||
if (!FlightSim.enabled || !name) return; | if (!FlightSim.enabled || !name) return; | ||||
len = strlen_P((const char *)name); | len = strlen_P((const char *)name); | ||||
void FlightSimInteger::identify(void) | void FlightSimInteger::identify(void) | ||||
{ | { | ||||
uint8_t len, buf[6]; | |||||
uint16_t len; | |||||
uint8_t buf[6]; | |||||
if (!FlightSim.enabled || !name) return; | if (!FlightSim.enabled || !name) return; | ||||
len = strlen_P((const char *)name); | len = strlen_P((const char *)name); | ||||
void FlightSimInteger::update(long val) | void FlightSimInteger::update(long val) | ||||
{ | { | ||||
value = val; | value = val; | ||||
if (change_callback) (*change_callback)(val); | |||||
if (change_callback) { | |||||
if (!hasCallbackInfo) { | |||||
(*change_callback)(val); | |||||
} else { | |||||
(*(void(*)(long,void*))change_callback)(val,callbackInfo); | |||||
} | |||||
} | |||||
} | } | ||||
FlightSimInteger * FlightSimInteger::find(unsigned int n) | FlightSimInteger * FlightSimInteger::find(unsigned int n) | ||||
void FlightSimFloat::identify(void) | void FlightSimFloat::identify(void) | ||||
{ | { | ||||
uint8_t len, buf[6]; | |||||
uint16_t len; | |||||
uint8_t buf[6]; | |||||
if (!FlightSim.enabled || !name) return; | if (!FlightSim.enabled || !name) return; | ||||
len = strlen_P((const char *)name); | len = strlen_P((const char *)name); | ||||
void FlightSimFloat::update(float val) | void FlightSimFloat::update(float val) | ||||
{ | { | ||||
value = val; | value = val; | ||||
if (change_callback) (*change_callback)(val); | |||||
if (change_callback) { | |||||
if (!hasCallbackInfo) { | |||||
(*change_callback)(val); | |||||
} else { | |||||
(*(void(*)(long,void*))change_callback)(val,callbackInfo); | |||||
} | |||||
} | |||||
} | } | ||||
FlightSimFloat * FlightSimFloat::find(unsigned int n) | FlightSimFloat * FlightSimFloat::find(unsigned int n) | ||||
{ | { | ||||
uint8_t intr_state, avail; | uint8_t intr_state, avail; | ||||
if (n1 > FLIGHTSIM_TX_SIZE) return; | |||||
if (!enabled || !usb_configuration) return; | if (!enabled || !usb_configuration) return; | ||||
intr_state = SREG; | intr_state = SREG; | ||||
cli(); | cli(); | ||||
uint8_t intr_state, total, avail; | uint8_t intr_state, total, avail; | ||||
total = n1 + n2; | total = n1 + n2; | ||||
if (total > FLIGHTSIM_TX_SIZE) return; | |||||
if (!enabled || !usb_configuration) return; | if (!enabled || !usb_configuration) return; | ||||
intr_state = SREG; | intr_state = SREG; | ||||
cli(); | cli(); | ||||
} | } | ||||
void FlightSimClass::xmit(const uint8_t *p1, uint8_t n1, const _XpRefStr_ *p2, uint8_t n2) | |||||
{ | |||||
void FlightSimClass::xmit(const uint8_t *p1, uint8_t n1, const _XpRefStr_ *p2, uint16_t n2) { | |||||
uint8_t intr_state, total, avail; | uint8_t intr_state, total, avail; | ||||
const char *s2 = (const char *)p2; | |||||
const char * PROGMEM s2 = (const char *)p2; | |||||
total = n1 + n2; | total = n1 + n2; | ||||
if (total > FLIGHTSIM_TX_SIZE) return; | |||||
if (total > FLIGHTSIM_TX_SIZE) { | |||||
xmit_big_packet(p1, n1, p2, n2); | |||||
return; | |||||
} | |||||
if (!enabled || !usb_configuration) return; | if (!enabled || !usb_configuration) return; | ||||
intr_state = SREG; | intr_state = SREG; | ||||
cli(); | cli(); | ||||
SREG = intr_state; | SREG = intr_state; | ||||
} | } | ||||
void FlightSimClass::xmit_big_packet(const uint8_t *p1, uint8_t n1, const _XpRefStr_ *p2, uint16_t n2) { | |||||
uint8_t intr_state, avail; | |||||
uint16_t total; | |||||
bool part1 = true; | |||||
const char * PROGMEM s2 = (const char *)p2; | |||||
uint8_t packet_id = 1; | |||||
total = n1 + n2; | |||||
if (!enabled || !usb_configuration) return; | |||||
intr_state = SREG; | |||||
cli(); | |||||
UENUM = FLIGHTSIM_TX_ENDPOINT; | |||||
avail = FLIGHTSIM_TX_SIZE - UEBCLX; | |||||
while (total>0) { | |||||
if (part1) { | |||||
UEDATX = *p1++; | |||||
part1 = (--n1 != 0); | |||||
} else { | |||||
pgm_read_byte_postinc(UEDATX, s2); | |||||
n2--; | |||||
} | |||||
total--; | |||||
if (!--avail) { | |||||
// transmit packet | |||||
UEINTX = 0x3A; | |||||
while (1) { | |||||
if (UEINTX & (1<<RWAL)) break; | |||||
SREG = intr_state; | |||||
if (!enabled || !usb_configuration) return; | |||||
intr_state = SREG; | |||||
cli(); | |||||
UENUM = FLIGHTSIM_TX_ENDPOINT; | |||||
} | |||||
// start new fragment with length and fragment ID | |||||
UEDATX = (total<(FLIGHTSIM_TX_SIZE-3) ? total+3 : FLIGHTSIM_TX_SIZE); // length byte | |||||
UEDATX = 0xff; | |||||
UEDATX = packet_id++; | |||||
avail = FLIGHTSIM_TX_SIZE - 3; | |||||
} | |||||
} | |||||
if (UEBCLX == FLIGHTSIM_TX_SIZE) UEINTX = 0x3A; | |||||
SREG = intr_state; | |||||
} | |||||
FlightSimClass FlightSim; | FlightSimClass FlightSim; |
static uint8_t recv(uint8_t *buffer); | static uint8_t recv(uint8_t *buffer); | ||||
static void xmit(const uint8_t *p1, uint8_t n1); | static void xmit(const uint8_t *p1, uint8_t n1); | ||||
static void xmit(const uint8_t *p1, uint8_t n1, const uint8_t *p2, uint8_t n2); | static void xmit(const uint8_t *p1, uint8_t n1, const uint8_t *p2, uint8_t n2); | ||||
static void xmit(const uint8_t *p1, uint8_t n1, const _XpRefStr_ *p2, uint8_t n2); | |||||
static void xmit(const uint8_t *p1, uint8_t n1, const _XpRefStr_ *p2, uint16_t n2); | |||||
static void xmit_big_packet(const uint8_t *p1, uint8_t n1, const _XpRefStr_ *p2, uint16_t n2); | |||||
friend class FlightSimCommand; | friend class FlightSimCommand; | ||||
friend class FlightSimInteger; | friend class FlightSimInteger; | ||||
friend class FlightSimFloat; | friend class FlightSimFloat; | ||||
void identify(void); | void identify(void); | ||||
void update(long val); | void update(long val); | ||||
static FlightSimInteger * find(unsigned int n); | static FlightSimInteger * find(unsigned int n); | ||||
void onChange(void (*fptr)(long)) { change_callback = fptr; } | |||||
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.... + - * / % ++ -- | // TODO: math operators.... + - * / % ++ -- | ||||
private: | private: | ||||
unsigned int id; | unsigned int id; | ||||
const _XpRefStr_ *name; | const _XpRefStr_ *name; | ||||
long value; | long value; | ||||
void (*change_callback)(long); | void (*change_callback)(long); | ||||
void* callbackInfo; | |||||
bool hasCallbackInfo; | |||||
FlightSimInteger *next; | FlightSimInteger *next; | ||||
static FlightSimInteger *first; | static FlightSimInteger *first; | ||||
static FlightSimInteger *last; | static FlightSimInteger *last; | ||||
void identify(void); | void identify(void); | ||||
void update(float val); | void update(float val); | ||||
static FlightSimFloat * find(unsigned int n); | static FlightSimFloat * find(unsigned int n); | ||||
void onChange(void (*fptr)(float)) { change_callback = fptr; } | |||||
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.... + - * / % ++ -- | // TODO: math operators.... + - * / % ++ -- | ||||
private: | private: | ||||
unsigned int id; | unsigned int id; | ||||
const _XpRefStr_ *name; | const _XpRefStr_ *name; | ||||
float value; | float value; | ||||
void (*change_callback)(float); | void (*change_callback)(float); | ||||
void* callbackInfo; | |||||
bool hasCallbackInfo; | |||||
FlightSimFloat *next; | FlightSimFloat *next; | ||||
static FlightSimFloat *first; | static FlightSimFloat *first; | ||||
static FlightSimFloat *last; | static FlightSimFloat *last; |