Compare commits

...
4 Commits
Author SHA1 Message Date
dkeruza f677f1cd3f Merge pull request #76 from nadenislamarre/fixevdevarrays
evdev: add array allocation checks on snprintf and for loops
2025-03-29 15:22:39 -04:00
Nicolas Adenis-Lamarre 3cd8e7019f evdev: add array allocation checks on snprintf and for loops
Signed-off-by: Nicolas Adenis-Lamarre <nicolas.adenis.lamarre@gmail.com>
2025-03-26 08:00:31 +01:00
Nicolas Adenis-Lamarre 38643436f2 Revert Reverted reverse axis and shake in evdev.
Signed-off-by: Nicolas Adenis-Lamarre <nicolas.adenis.lamarre@gmail.com>
2025-03-26 07:59:11 +01:00
dkeruza-neo 527bdc418f Fixed LGJ, VT3 and ID4/ID5 JVS passthrough. 2025-03-25 15:36:26 -04:00
7 changed files with 178 additions and 41 deletions
-3
View File
@@ -40,9 +40,6 @@ jobs:
NEW_RUN_NUMBER=$(( ${{ github.run_number }} + 161 )) NEW_RUN_NUMBER=$(( ${{ github.run_number }} + 161 ))
echo "NEW_RUN_NUMBER=$NEW_RUN_NUMBER" >> $GITHUB_ENV echo "NEW_RUN_NUMBER=$NEW_RUN_NUMBER" >> $GITHUB_ENV
# - name: Display Updated Number
# run: echo "Updated Run Number: ${{ env.NEW_RUN_NUMBER }}"
- name: Create Preview Release - name: Create Preview Release
if: github.ref == 'refs/heads/master' if: github.ref == 'refs/heads/master'
uses: "marvinpinto/action-automatic-releases@latest" uses: "marvinpinto/action-automatic-releases@latest"
+4
View File
@@ -2,6 +2,10 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [2.0.5]
### Fixed
- Fixed LGJ, VT3 and ID4/ID5 JVS passthrough.
## [2.0.4] ## [2.0.4]
### Added ### Added
- Boost render res option for Rambo, LGJ, HOD4, HOD-EX, ID4/ID5, 2Spicy and Harley. - Boost render res option for Rambo, LGJ, HOD4, HOD-EX, ID4/ID5, 2Spicy and Harley.
+4
View File
@@ -90,6 +90,10 @@ sudo ln -s /lib/i386-linux-gnu/libglut.so /lib/i386-linux-gnu/libglut.so.3
- You need to set `SKIP_OUTRUN_CABINET_CHECK` to 1 in `lindbergh.conf` to play in DX, SDX or Standard cabinet types. - You need to set `SKIP_OUTRUN_CABINET_CHECK` to 1 in `lindbergh.conf` to play in DX, SDX or Standard cabinet types.
### Hummer (all versions)
The games will ask for libGLcore.so.1 and libnvidia-tls.so.1 you can copy for example libkswapapi.so as libGLcore.so.1 and libnvidia-tls.so.1 and the game will work fine.
## Configuration File : lindbergh.conf ## Configuration File : lindbergh.conf
All configurable options are set and explained in the [`lindbergh.conf`](lindbergh.conf) file itself, and should be reasonably easy to understand. This chapter further explains some of those setup options. All configurable options are set and explained in the [`lindbergh.conf`](lindbergh.conf) file itself, and should be reasonably easy to understand. This chapter further explains some of those setup options.
+151 -30
View File
@@ -862,7 +862,7 @@ static ControllerStatus listControllers(Controllers *controllers)
int controller = open(controllers->controller[i].path, O_RDONLY); int controller = open(controllers->controller[i].path, O_RDONLY);
if (controller < 0) if (controller < 0)
{ {
strcpy(controllers->controller[i].name, "Unknown"); strncpy(controllers->controller[i].name, "Unknown", SIZE);
continue; continue;
} }
@@ -912,16 +912,26 @@ static ControllerStatus listControllers(Controllers *controllers)
{ {
if (test_bit(code, bit[EV_KEY])) if (test_bit(code, bit[EV_KEY]))
{ {
if(controllers->controller[i].inputCount >= MAX_INPUTS) { // the number of input is limited
fprintf(stderr, "warning, maximum number of inputs reached !\n");
break;
}
controllers->controller[i].enabled = 1; controllers->controller[i].enabled = 1;
ControllerInput *controllerInput = ControllerInput *controllerInput =
&controllers->controller[i].inputs[controllers->controller[i].inputCount++]; &controllers->controller[i].inputs[controllers->controller[i].inputCount++];
controllerInput->evType = EV_KEY; controllerInput->evType = EV_KEY;
controllerInput->evCode = code; controllerInput->evCode = code;
controllerInput->specialFunction = NO_SPECIAL_FUNCTION; controllerInput->specialFunction = NO_SPECIAL_FUNCTION;
strcpy(controllerInput->inputName, controllers->controller[i].name); strncpy(controllerInput->inputName, controllers->controller[i].name, SIZE);
strcat(controllerInput->inputName, "_"); strcat(controllerInput->inputName, "_");
strcat(controllerInput->inputName, codename(EV_KEY, code)); strcat(controllerInput->inputName, codename(EV_KEY, code));
normaliseName(controllerInput->inputName); normaliseName(controllerInput->inputName);
if (snprintf(controllerInput->inputTechName, SIZE, "%s:KEY:%i", controllers->controller[i].path, code) >= 1024)
{
// hum ok, truncated value
}
strncpy(controllerInput->inputTechNegName, "-", SIZE); // unassignable value (not "" while some conf are empty)
} }
} }
} }
@@ -939,31 +949,70 @@ static ControllerStatus listControllers(Controllers *controllers)
controllerInput->evType = EV_ABS; controllerInput->evType = EV_ABS;
controllerInput->evCode = code; controllerInput->evCode = code;
controllerInput->specialFunction = NO_SPECIAL_FUNCTION; controllerInput->specialFunction = NO_SPECIAL_FUNCTION;
strcpy(controllerInput->inputName, controllers->controller[i].name); strncpy(controllerInput->inputName, controllers->controller[i].name, SIZE);
strcat(controllerInput->inputName, "_"); strcat(controllerInput->inputName, "_");
strcat(controllerInput->inputName, codename(EV_ABS, code)); strcat(controllerInput->inputName, codename(EV_ABS, code));
normaliseName(controllerInput->inputName); normaliseName(controllerInput->inputName);
if (snprintf(controllerInput->inputTechName, SIZE, "%s:ABS:%i", controllers->controller[i].path, code) >= 1024)
{
// hum ok, truncated value
}
if (snprintf(controllerInput->inputTechNegName, SIZE, "%s:ABS_NEG:%i", controllers->controller[i].path, code) >= 1024)
{
// hum ok, truncated value
}
ControllerInput *minControllerInput = ControllerInput *minControllerInput =
&controllers->controller[i].inputs[controllers->controller[i].inputCount++]; &controllers->controller[i].inputs[controllers->controller[i].inputCount++];
minControllerInput->evType = EV_ABS; minControllerInput->evType = EV_ABS;
minControllerInput->evCode = code; minControllerInput->evCode = code;
minControllerInput->specialFunction = ANALOGUE_TO_DIGITAL_MIN; minControllerInput->specialFunction = ANALOGUE_TO_DIGITAL_MIN;
strcpy(minControllerInput->inputName, controllerInput->inputName); strncpy(minControllerInput->inputName, controllerInput->inputName, SIZE);
strcat(minControllerInput->inputName, "_MIN"); strcat(minControllerInput->inputName, "_MIN");
if (snprintf(minControllerInput->inputTechName, SIZE, "%s:ABS:%i:MIN", controllers->controller[i].path, code) >= 1024)
{
// hum ok, truncated value
}
if (snprintf(minControllerInput->inputTechNegName, SIZE, "%s:ABS_NEG:%i:MIN", controllers->controller[i].path, code) >=
1024)
{
// hum ok, truncated value
}
ControllerInput *maxControllerInput = ControllerInput *maxControllerInput =
&controllers->controller[i].inputs[controllers->controller[i].inputCount++]; &controllers->controller[i].inputs[controllers->controller[i].inputCount++];
maxControllerInput->evType = EV_ABS; maxControllerInput->evType = EV_ABS;
maxControllerInput->evCode = code; maxControllerInput->evCode = code;
maxControllerInput->specialFunction = ANALOGUE_TO_DIGITAL_MAX; maxControllerInput->specialFunction = ANALOGUE_TO_DIGITAL_MAX;
strcpy(maxControllerInput->inputName, controllerInput->inputName); strncpy(maxControllerInput->inputName, controllerInput->inputName, SIZE);
strcat(maxControllerInput->inputName, "_MAX"); strcat(maxControllerInput->inputName, "_MAX");
if (snprintf(maxControllerInput->inputTechName, SIZE, "%s:ABS:%i:MAX", controllers->controller[i].path, code) >= 1024)
{
// hum ok, truncated value
}
if (snprintf(maxControllerInput->inputTechNegName, SIZE, "%s:ABS_NEG:%i:MAX", controllers->controller[i].path, code) >=
1024)
{
// hum ok, truncated value
}
ControllerInput *shakeControllerInput = &controllers->controller[i].inputs[controllers->controller[i].inputCount++];
shakeControllerInput->evType = EV_ABS;
shakeControllerInput->evCode = code;
shakeControllerInput->specialFunction = ANALOGUE_SHAKE;
strncpy(shakeControllerInput->inputName, controllerInput->inputName, SIZE);
strcat(shakeControllerInput->inputName, "_SHAKE");
if (snprintf(shakeControllerInput->inputTechName, SIZE, "%s:ABS:%i:SHAKE", controllers->controller[i].path, code) >=
1024)
{
// hum ok, truncated value
}
struct input_absinfo absoluteFeatures; struct input_absinfo absoluteFeatures;
ioctl(controller, EVIOCGABS(code), &absoluteFeatures); ioctl(controller, EVIOCGABS(code), &absoluteFeatures);
controllers->controller[i].absMin[code] = absoluteFeatures.minimum; controllers->controller[i].absMin[code] = absoluteFeatures.minimum;
controllers->controller[i].absMax[code] = absoluteFeatures.maximum; controllers->controller[i].absMax[code] = absoluteFeatures.maximum;
} }
} }
} }
@@ -1066,31 +1115,67 @@ void *controllerThread(void *_args)
((double)event.value - (double)args->controller->absMin[event.code]) / ((double)event.value - (double)args->controller->absMin[event.code]) /
((double)args->controller->absMax[event.code] - (double)args->controller->absMin[event.code]); ((double)args->controller->absMax[event.code] - (double)args->controller->absMin[event.code]);
if(args->controller->absTriggers[event.code].isNeg == 1) {
scaled = 1.0 - scaled;
}
if (args->controller->absTriggers[event.code].enabled) if (args->controller->absTriggers[event.code].enabled)
{ {
int channel = args->controller->absTriggers[event.code].channel; int channel = args->controller->absTriggers[event.code].channel;
// Deadzone handling
if (scaled < analogue_deadzones[channel].start_max) if (args->controller->absTriggers[event.code].isAnalogue) {
// Deadzone handling
if (scaled < analogue_deadzones[channel].start_max)
scaled = 0.0; scaled = 0.0;
if (scaled > analogue_deadzones[channel].middle_min && scaled < analogue_deadzones[channel].middle_max) if (scaled > analogue_deadzones[channel].middle_min && scaled < analogue_deadzones[channel].middle_max)
scaled = 0.5; scaled = 0.5;
if (scaled > analogue_deadzones[channel].end_min) if (scaled > analogue_deadzones[channel].end_min)
scaled = 1.0; scaled = 1.0;
setAnalogue(channel, scaled * (pow(2, jvsBits) - 1));
setAnalogue(channel, scaled * (pow(2, jvsBits) - 1)); } else {
setSwitch(args->controller->absTriggers[event.code].player,
args->controller->absTriggers[event.code].channel, scaled < 0.8 ? 0 : 1);
}
} }
if (args->controller->absTriggers[event.code].minEnabled) if(event.value <= ((args->controller->absMin[event.code] + args->controller->absMax[event.code]) / 2)) {
{ if (args->controller->absTriggers[event.code].minEnabled)
setSwitch(args->controller->absTriggers[event.code].minPlayer, {
args->controller->absTriggers[event.code].minChannel, scaled < 0.2); int channel = args->controller->absTriggers[event.code].minChannel;
if (args->controller->absTriggers[event.code].isAnalogue) {
setAnalogue(channel, scaled < 0.2 ? 0.0 : pow(2, jvsBits) * 1.0);
} else {
setSwitch(args->controller->absTriggers[event.code].minPlayer,
args->controller->absTriggers[event.code].minChannel, scaled < 0.2 ? 1 : 0);
}
}
} }
if (args->controller->absTriggers[event.code].maxEnabled) if(event.value >= ((args->controller->absMin[event.code] + args->controller->absMax[event.code]) / 2)) {
{ if (args->controller->absTriggers[event.code].maxEnabled)
setSwitch(args->controller->absTriggers[event.code].maxPlayer, {
args->controller->absTriggers[event.code].maxChannel, scaled > 0.8); int channel = args->controller->absTriggers[event.code].maxChannel;
if (args->controller->absTriggers[event.code].isAnalogue) {
setAnalogue(channel, scaled > 0.8 ? pow(2, jvsBits) : 0.0);
} else {
setSwitch(args->controller->absTriggers[event.code].maxPlayer,
args->controller->absTriggers[event.code].maxChannel, scaled > 0.8 ? 1 : 0);
}
}
} }
if (args->controller->absTriggers[event.code].shakeEnabled)
{
int channel = args->controller->absTriggers[event.code].shakeChannel;
if( (scaled > args->controller->absTriggers[event.code].shakePreviousScaled && scaled - args->controller->absTriggers[event.code].shakePreviousScaled > 0.1) ||
(scaled < args->controller->absTriggers[event.code].shakePreviousScaled && -scaled + args->controller->absTriggers[event.code].shakePreviousScaled > 0.1)) {
setAnalogue(channel, 0);
} else {
setAnalogue(channel, pow(2, jvsBits) / 2);
}
args->controller->absTriggers[event.code].shakePreviousScaled = scaled;
}
} }
break; break;
@@ -1215,7 +1300,7 @@ ControllerStatus getArcadeInputByName(char *name, ArcadeInput *input)
{ {
if (strcmp(tempName, name) == 0) if (strcmp(tempName, name) == 0)
{ {
strcpy(input->name, arcadeInputs[index].name); strncpy(input->name, arcadeInputs[index].name, SIZE);
input->channel = arcadeInputs[index].channel; input->channel = arcadeInputs[index].channel;
input->player = arcadeInputs[index].player; input->player = arcadeInputs[index].player;
return CONTROLLER_STATUS_SUCCESS; return CONTROLLER_STATUS_SUCCESS;
@@ -1243,6 +1328,9 @@ ControllerStatus startControllerThreads(Controllers *controllers)
controllers->controller[i].absTriggers[j].enabled = 0; controllers->controller[i].absTriggers[j].enabled = 0;
controllers->controller[i].absTriggers[j].minEnabled = 0; controllers->controller[i].absTriggers[j].minEnabled = 0;
controllers->controller[i].absTriggers[j].maxEnabled = 0; controllers->controller[i].absTriggers[j].maxEnabled = 0;
controllers->controller[i].absTriggers[j].shakeEnabled = 0;
controllers->controller[i].absTriggers[j].isNeg = 0;
controllers->controller[i].absTriggers[j].isAnalogue = 1;
} }
for (int j = 0; j < KEY_MAX; j++) for (int j = 0; j < KEY_MAX; j++)
@@ -1255,8 +1343,20 @@ ControllerStatus startControllerThreads(Controllers *controllers)
for (int j = 0; j < controllers->controller[i].inputCount; j++) for (int j = 0; j < controllers->controller[i].inputCount; j++)
{ {
char *mapping = getMapping(controllers->controller[i].inputs[j].inputName); char *mapping = getMapping(controllers->controller[i].inputs[j].inputName);
if (mapping == NULL) int negabs = 0;
continue;
if (mapping == NULL) {
// give a 2nd chance with a techninal mapping
mapping = getMapping(controllers->controller[i].inputs[j].inputTechName);
if (mapping == NULL) {
// give a 3rd change with negativ technical mapping
mapping = getMapping(controllers->controller[i].inputs[j].inputTechNegName);
negabs = 1;
if (mapping == NULL) {
continue;
}
}
}
ArcadeInput input = {0}; ArcadeInput input = {0};
ControllerStatus status = getArcadeInputByName(mapping, &input); ControllerStatus status = getArcadeInputByName(mapping, &input);
@@ -1277,10 +1377,13 @@ ControllerStatus startControllerThreads(Controllers *controllers)
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].enabled = 1; controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].enabled = 1;
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].channel = controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].channel =
input.channel; input.channel;
strcpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].name, strncpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].name,
input.name); input.name, SIZE);
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].player = controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].player =
input.player; input.player;
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isNeg = negabs;
if (strstr(input.name, "ANALOGUE") == NULL)
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isAnalogue = 0;
} }
break; break;
@@ -1289,10 +1392,13 @@ ControllerStatus startControllerThreads(Controllers *controllers)
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxEnabled = 1; controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxEnabled = 1;
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxChannel = controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxChannel =
input.channel; input.channel;
strcpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxName, strncpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxName,
input.name); input.name, SIZE);
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxPlayer = controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxPlayer =
input.player; input.player;
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isNeg = negabs;
if (strstr(input.name, "ANALOGUE") == NULL)
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isAnalogue = 0;
} }
break; break;
@@ -1301,10 +1407,25 @@ ControllerStatus startControllerThreads(Controllers *controllers)
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minEnabled = 1; controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minEnabled = 1;
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minChannel = controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minChannel =
input.channel; input.channel;
strcpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minName, strncpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minName,
input.name); input.name, SIZE);
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minPlayer = controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minPlayer =
input.player; input.player;
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isNeg = negabs;
if (strstr(input.name, "ANALOGUE") == NULL)
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isAnalogue = 0;
}
break;
case ANALOGUE_SHAKE:
{
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].shakeEnabled = 1;
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].shakeChannel =
input.channel;
strncpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].shakeName,
input.name, SIZE);
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].shakePlayer =
input.player;
} }
break; break;
@@ -1319,8 +1440,8 @@ ControllerStatus startControllerThreads(Controllers *controllers)
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].enabled = 1; controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].enabled = 1;
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].channel = controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].channel =
input.channel; input.channel;
strcpy(controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].name, strncpy(controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].name,
input.name); input.name, SIZE);
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].player = controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].player =
input.player; input.player;
+15 -4
View File
@@ -2,9 +2,9 @@
#include <linux/input-event-codes.h> #include <linux/input-event-codes.h>
#define SIZE 1024 #define SIZE 300
#define CONTROLLER_THREAD_MAX 256 #define CONTROLLER_THREAD_MAX 256
#define MAX_INPUTS 512
typedef struct typedef struct
{ {
@@ -31,6 +31,14 @@ typedef struct
int maxEnabled; int maxEnabled;
int isAnalogue; int isAnalogue;
int isNeg; // reversed axis
char shakeName[SIZE];
int shakeChannel;
int shakeEnabled;
int shakePlayer;
double shakePreviousScaled;
int isCoin; int isCoin;
} ArcadeInput; } ArcadeInput;
@@ -44,12 +52,15 @@ typedef enum {
NO_SPECIAL_FUNCTION = 0, NO_SPECIAL_FUNCTION = 0,
ANALOGUE_TO_DIGITAL_MAX, ANALOGUE_TO_DIGITAL_MAX,
ANALOGUE_TO_DIGITAL_MIN, ANALOGUE_TO_DIGITAL_MIN,
DIGITAL_TO_ANALOGUE DIGITAL_TO_ANALOGUE,
ANALOGUE_SHAKE
} SpecialFunction; } SpecialFunction;
typedef struct typedef struct
{ {
char inputName[SIZE]; char inputName[SIZE];
char inputTechName[SIZE];
char inputTechNegName[SIZE];
int evType; int evType;
int evCode; int evCode;
SpecialFunction specialFunction; SpecialFunction specialFunction;
@@ -63,7 +74,7 @@ typedef struct
int absMax[ABS_MAX]; int absMax[ABS_MAX];
int absMin[ABS_MAX]; int absMin[ABS_MAX];
ControllerInput inputs[SIZE]; ControllerInput inputs[MAX_INPUTS];
int inputCount; int inputCount;
ArcadeInput keyTriggers[KEY_MAX]; ArcadeInput keyTriggers[KEY_MAX];
+1 -1
View File
@@ -444,7 +444,7 @@ int main(int argc, char *argv[])
continue; continue;
} }
if (strcmp(argv[i], "-z") == 0 || strcmp(argv[i], "--zink") == 0) if (strcmp(argv[i], "-z") == 0 || strcmp(argv[i], "--ziink") == 0)
{ {
zink = 1; zink = 1;
continue; continue;
+3 -3
View File
@@ -160,10 +160,10 @@ void *readJVSFrameThread(void * arg)
waitForEnd = 0; waitForEnd = 0;
do { do {
while(jvsFrameBuffer.ready == 1) { while (jvsFrameBuffer.ready == 1)
usleep(10); {
usleep(1);
} }
// printf("SERIAL thread debug: trying to read byte.\n"); // printf("SERIAL thread debug: trying to read byte.\n");
// Try to read a byte from serial, this call will be blocking if VMIN > 0 and VTIME = 0 // Try to read a byte from serial, this call will be blocking if VMIN > 0 and VTIME = 0
bytesRead = read(fd, &localBuffer[byteCount], 1); bytesRead = read(fd, &localBuffer[byteCount], 1);