Pārlūkot izejas kodu

Merge branch 'developer' into musicInclude

# Conflicts:
#	Enemy.cpp
#	World.cpp
Aaldert 9 gadi atpakaļ
vecāks
revīzija
b09311d810

+ 2 - 1
CMakeLists.txt

@@ -1,9 +1,10 @@
 cmake_minimum_required(VERSION 3.5)
 project(CrystalPoint)
 
-file(GLOB SOURCE_FILES
+file(GLOB_RECURSE SOURCE_FILES
         "*.h"
         "*.cpp"
+        "*.cc"
         )
 
 add_executable(CrystalPoint ${SOURCE_FILES})

+ 42 - 0
Controller.cpp

@@ -0,0 +1,42 @@
+//
+// Created by janco on 5/24/16.
+//
+
+#include "Controller.h"
+
+Controller::Controller(int controllerId) {
+    this->controllerId = controllerId;
+    this->ypr = Vec3f();
+    this->joystick = Vec2f();
+    this->setConnected(false);
+    this->setConnected(true);
+}
+
+Controller::~Controller() { }
+
+void Controller::rumble(int duration, int power){
+  //  controllerhandler->rumble(controllerId, duration, power);
+}
+
+void Controller::setConnected(bool connected) {
+    if(connected){
+        this->connected = true;
+    }else{
+        this->connected = false;
+        this->ypr = Vec3f(0,0,0);
+        this->joystick = Vec2f(0,0);
+        this->button = false;
+        this->magnetSwitch = false;
+        this ->joystickButton = false;
+    }
+}
+
+bool Controller::isConnected(void) {
+    return connected;
+}
+
+
+
+
+
+

+ 20 - 0
Controller.h

@@ -0,0 +1,20 @@
+#pragma once
+
+#include "Vector.h"
+
+class Controller{
+public:
+    Controller(int controllerId);
+    ~Controller();
+    Vec3f ypr;
+    Vec2f joystick;
+    bool button, joystickButton, magnetSwitch;
+    int controllerId;
+
+    void setConnected(bool connected);
+    bool isConnected(void);
+
+    void rumble(int duration, int power);
+private:
+    bool connected = false;
+};

+ 154 - 0
ControllerHandler.cpp

@@ -0,0 +1,154 @@
+//
+// Created by janco on 5/23/16.
+//
+
+#include "ControllerHandler.h"
+#include <iostream>
+
+/*
+ * String split helper functions
+ */
+std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
+    std::stringstream ss(s);
+    std::string item;
+    while (std::getline(ss, item, delim)) {
+        elems.push_back(item);
+    }
+    return elems;
+}
+std::vector<std::string> split(const std::string &s, char delim) {
+    std::vector<std::string> elems;
+    split(s, delim, elems);
+    return elems;
+}
+
+ControllerHandler::ControllerHandler(){
+    readthread = std::thread(&ControllerHandler::BasestationReader, this);
+};
+
+Controller* ControllerHandler::getLeftController(void){
+    for (int i = 0; i < 4; i++)
+    {
+        if(controllers[i] != nullptr && controllers[i]->isConnected()){
+            return controllers[i];
+        }
+    }
+    return nullptr;
+}
+
+Controller* ControllerHandler::getRightController(void){
+    bool c1found = false;
+    for (int i = 0; i < 4; i++)
+    {
+        if(controllers[i] != nullptr && controllers[i]->isConnected()){
+            if(c1found){
+                return controllers[i];
+            }
+            c1found = true;
+        }
+    }
+    return nullptr;
+}
+
+void ControllerHandler::SearchBasestation(void){
+    basestationFound = false;
+    std::vector<serial::PortInfo> devices_found = serial::list_ports();
+    std::vector<serial::PortInfo>::iterator iter = devices_found.begin();
+    while( iter != devices_found.end() )
+    {
+        serial::PortInfo device = *iter++;
+        if(device.hardware_id == "USB VID:PID=10c4:ea60 SNR=02133"){
+            basestationFound = true;
+            baseStation = new serial::Serial(device.port, BasestationBaudrate, serial::Timeout::simpleTimeout(1000));
+            break;
+        }
+    }
+}
+
+void ControllerHandler::rumble(int idController, int duration, int power){
+	if (basestationFound)
+	{
+		char sendbuffer[16];
+		sprintf(sendbuffer, "1|%01d|%06d|%03d\n", idController, duration, power);
+		baseStation->write(sendbuffer);
+	}
+}
+
+/*
+ * Reading from basestation
+ */
+
+void ControllerHandler::BasestationReader() {
+    while(1){
+        while(!basestationFound){
+            SearchBasestation();
+            if(basestationFound){
+                baseStation ->write("stop\n");
+                baseStation ->write("clientlist\n");
+            }
+            std::this_thread::sleep_for(std::chrono::milliseconds(2000));
+        }
+        while(baseStation->isOpen()){
+            std::string basestationMessage = baseStation->readline();
+            std::vector<std::string> basestationMessageSplit = split(basestationMessage, '|');
+            if(basestationMessageSplit.size() > 1){ //Valid message
+                std::cout << "DEBUG: Message received. Length: " << basestationMessageSplit.size() << " Type: " << basestationMessageSplit[0] << " Message: " << basestationMessage;
+                int messageId = std::stoi(basestationMessageSplit[0]);
+                switch(messageId){
+                    //commandList[messageId](basestationMessageSplit);
+                    case 0: commandDebug(basestationMessageSplit); break;  //Debug message
+                    case 1: commandControllerData(basestationMessageSplit); break;  //Data from controller
+                    case 2: commandControllerEvent(basestationMessageSplit); break;  //Controller events
+                    case 3: commandControllerList(basestationMessageSplit); break;  //Connected controller list
+                }
+            }
+        }
+    }
+}
+
+void ControllerHandler::commandDebug(std::vector<std::string> data) {
+    std::cout << "DEBUG: " << data[1];
+}
+
+void ControllerHandler::commandControllerData(std::vector<std::string> data) {
+    if(data.size() == 10){
+        Controller* c = controllers[std::stoi(data[1])];
+        if(c != nullptr && c->isConnected()){
+            c->ypr.x = std::stoi(data[5])/100.0f;
+            c->ypr.y = std::stoi(data[6])/100.0f;
+            c->ypr.z = std::stoi(data[7])/100.0f;
+
+            c->joystick.x = std::stoi(data[2])/2000.0f;
+            c->joystick.y = std::stoi(data[3])/2000.0f;
+            c->joystickButton = !(data[4] == "0");
+
+            c->magnetSwitch = !(data[9] == "0");
+        }
+    }
+}
+
+void ControllerHandler::commandControllerEvent(std::vector<std::string> data) {
+    if(data.size() == 3){
+        if(data[1] == "connected"){
+            int controllerId = std::stoi(data[2]);
+            controllers[controllerId] = new Controller(controllerId);
+            controllers[controllerId]->setConnected(true);
+        }else if(data[1] == "disconnected"){
+         //   controllers.erase(*controllers[std::stoi(data[2])]);
+        }
+    }
+}
+
+void ControllerHandler::commandControllerList(std::vector<std::string> data) {
+   for(unsigned int i = 1; i < data.size() - 1; i++){
+       int controllerId = std::stoi(data[i]);
+       controllers[controllerId] = new Controller(controllerId);
+       rumble(controllerId, 100, 100);
+   }
+   if(basestationFound)
+	 baseStation->write("start\n");
+}
+
+
+
+

+ 41 - 0
ControllerHandler.h

@@ -0,0 +1,41 @@
+#pragma once
+//
+// Created by janco on 5/23/16.
+//
+
+#define BasestationBaudrate 115200
+#include <thread>
+
+#ifdef WIN32
+#include "include/serial.h"
+#else
+#include "lib/serial/include/serial.h"
+#endif
+
+
+#include "Controller.h"
+
+class ControllerHandler{
+public:
+    ControllerHandler();
+    Controller* getLeftController(void);
+    Controller* getRightController(void);
+    void rumble(int idController, int duration, int power);
+private:
+    void SearchBasestation(void);
+    void BasestationReader(void);
+
+    bool basestationFound;
+    serial::Serial *baseStation;
+    std::thread readthread;
+    Controller* controllers[4];
+
+    //Command functions
+    void commandDebug(std::vector<std::string> data);
+    void commandControllerData(std::vector<std::string> data);
+    void commandControllerEvent(std::vector<std::string> data);
+    void commandControllerList(std::vector<std::string> data);
+
+    //void(*commandList [1])(std::vector<std::string> data);
+};
+

+ 57 - 4
CrystalPoint.cpp

@@ -52,6 +52,7 @@ void CrystalPoint::draw()
 	
 	if(!state)
 		menu->draw();
+
 	glutSwapBuffers();
 }
 
@@ -71,15 +72,23 @@ void CrystalPoint::update()
 			worldhandler->PreviousWorld();
 		if (keyboardState.special[GLUT_KEY_RIGHT] && !prevKeyboardState.special[GLUT_KEY_RIGHT])
 			worldhandler->NextWorld();
+		if (keyboardState.keys[27])
+			state = false;
+		
 
 		Player* player = Player::getInstance();
 
+		//testing code
+		if (keyboardState.keys['u'])
+			player->HpUp(1);
+		if (keyboardState.keys['i'])
+			player->HpDown(1);
+		if (keyboardState.keys['o'])
+			player->XpUp(1);
+
 		player->rotation.y += mouseOffset.x / 10.0f;
 		player->rotation.x += mouseOffset.y / 10.0f;
-		if (player->rotation.x > 90)
-			player->rotation.x = 90;
-		if (player->rotation.x < -90)
-			player->rotation.x = -90;
+
 
 		float speed = 10;
 
@@ -91,6 +100,50 @@ void CrystalPoint::update()
 		if (keyboardState.keys['q']) player->setPosition(1, deltaTime*speed, true);
 		if (keyboardState.keys['e']) player->setPosition(-1, deltaTime*speed, true);
 
+		Controller *leftcontroller = controller.getLeftController();
+		if (leftcontroller != nullptr) {
+			Vec2f *leftControllerJoystick = &leftcontroller->joystick;
+
+
+			if (leftcontroller->joystickButton) {
+				controller.rumble(leftcontroller->controllerId, 100, 100);
+			}
+
+
+			if (leftControllerJoystick->y > 0.3) {
+				player->setPosition(270, leftControllerJoystick->y * deltaTime * 2.0f, false);
+			}
+			else if (leftControllerJoystick->y < -0.3) {
+				player->setPosition(90, leftControllerJoystick->y * -1 * deltaTime * 2.0f, false);
+			}
+			if (leftControllerJoystick->x > 0.3) {
+				player->setPosition(180, leftControllerJoystick->x * deltaTime * 2.0f, false);
+			}
+			else if (leftControllerJoystick->x < -0.3) {
+				player->setPosition(0, leftControllerJoystick->x * -1 * deltaTime * 2.0f, false);
+			}
+
+			player->leftWeapon->rotateWeapon(Vec3f(leftcontroller->ypr.y + 140, 0, -leftcontroller->ypr.z));
+
+		}
+		Controller *rightcontroller = controller.getRightController();
+		if(rightcontroller != nullptr){
+			Vec2f *rightControllerJoystick = &rightcontroller->joystick;
+			if (rightControllerJoystick->y > 0.3 || rightControllerJoystick->y < -0.3) {
+				player->rotation.x += rightcontroller->joystick.y/4;
+			}
+
+			if (rightControllerJoystick->x > 0.3 || rightControllerJoystick->x < -0.3) {
+				player->rotation.y += rightcontroller->joystick.x/4;
+			}
+            player->rightWeapon->rotateWeapon(Vec3f(rightcontroller->ypr.y + 140, 0, -rightcontroller->ypr.z));
+        }
+
+		if (player->rotation.x > 90)
+			player->rotation.x = 90;
+		if (player->rotation.x < -90)
+			player->rotation.x = -90;
+
 		if (!worldhandler->isPlayerPositionValid())
 			player->position = oldPosition;
 		player->position.y = worldhandler->getHeight(player->position.x, player->position.z) + 1.7f;

+ 2 - 1
CrystalPoint.h

@@ -7,6 +7,7 @@ class Cursor;
 class Menu;
 #include "Vector.h"
 #include "SoundSystem.h"
+#include "ControllerHandler.h"
 
 class KeyboardState
 {
@@ -27,8 +28,8 @@ public:
 
 	WorldHandler* worldhandler;
 	Player* player;
+	ControllerHandler controller;
 	Cursor* cursor;
-
 	Menu* menu;
 
 	static int width, height;

+ 20 - 7
CrystalPoint.vcxproj

@@ -89,13 +89,13 @@
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PrecompiledHeaderFile />
-      <AdditionalIncludeDirectories>freeglut/include; openAL/include</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>lib/serial;freeglut/include; openAL/include</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <AdditionalLibraryDirectories>freeglut/lib; openAL/libs/Win32</AdditionalLibraryDirectories>
-      <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;OpenAL32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>setupapi.lib;openal32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -105,13 +105,13 @@
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PrecompiledHeaderFile />
-      <AdditionalIncludeDirectories>freeglut/include;C:\Program Files (x86)\OpenAL 1.1 SDK\include</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>lib/serial;freeglut/include;C:\Program Files (x86)\OpenAL 1.1 SDK\include</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <AdditionalLibraryDirectories>freeglut/lib;C:\Program Files (x86)\OpenAL 1.1 SDK\libs\Win32</AdditionalLibraryDirectories>
-      <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;OpenAL32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>setupapi.lib;openal32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -123,7 +123,7 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PrecompiledHeaderFile />
-      <AdditionalIncludeDirectories>freeglut/include; openAL/include</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>lib/serial;freeglut/include; openAL/include</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
@@ -131,7 +131,7 @@
       <OptimizeReferences>true</OptimizeReferences>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <AdditionalLibraryDirectories>freeglut/lib; openAL/libs/Win32</AdditionalLibraryDirectories>
-      <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;OpenAL32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>setupapi.lib;openal32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -143,7 +143,7 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PrecompiledHeaderFile />
-      <AdditionalIncludeDirectories>freeglut/include</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>lib/serial;freeglut/include;</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
@@ -151,10 +151,13 @@
       <OptimizeReferences>true</OptimizeReferences>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <AdditionalLibraryDirectories>freeglut/lib</AdditionalLibraryDirectories>
+      <AdditionalDependencies>setupapi.lib;openal32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
     <ClCompile Include="Button.cpp" />
+    <ClCompile Include="Controller.cpp" />
+    <ClCompile Include="ControllerHandler.cpp" />
     <ClCompile Include="Crystal.cpp" />
     <ClCompile Include="CrystalPoint.cpp" />
     <ClCompile Include="Cursor.cpp" />
@@ -164,6 +167,9 @@
     <ClCompile Include="Interface.cpp" />
     <ClCompile Include="json.cpp" />
     <ClCompile Include="LevelObject.cpp" />
+    <ClCompile Include="lib\serial\src\impl\list_ports\list_ports_win.cc" />
+    <ClCompile Include="lib\serial\src\impl\win.cc" />
+    <ClCompile Include="lib\serial\src\serial.cc" />
     <ClCompile Include="Main.cpp" />
     <ClCompile Include="Menu.cpp" />
     <ClCompile Include="MenuElement.cpp" />
@@ -177,11 +183,14 @@
     <ClCompile Include="Util.cpp" />
     <ClCompile Include="Vector.cpp" />
     <ClCompile Include="Vertex.cpp" />
+    <ClCompile Include="Weapon.cpp" />
     <ClCompile Include="World.cpp" />
     <ClCompile Include="WorldHandler.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="Button.h" />
+    <ClInclude Include="Controller.h" />
+    <ClInclude Include="ControllerHandler.h" />
     <ClInclude Include="Crystal.h" />
     <ClInclude Include="CrystalPoint.h" />
     <ClInclude Include="Cursor.h" />
@@ -191,6 +200,9 @@
     <ClInclude Include="Interface.h" />
     <ClInclude Include="json.h" />
     <ClInclude Include="LevelObject.h" />
+    <ClInclude Include="lib\serial\include\impl\win.h" />
+    <ClInclude Include="lib\serial\include\serial.h" />
+    <ClInclude Include="lib\serial\include\v8stdint.h" />
     <ClInclude Include="Main.h" />
     <ClInclude Include="Menu.h" />
     <ClInclude Include="MenuElement.h" />
@@ -205,6 +217,7 @@
     <ClInclude Include="Util.h" />
     <ClInclude Include="vector.h" />
     <ClInclude Include="Vertex.h" />
+    <ClInclude Include="Weapon.h" />
     <ClInclude Include="World.h" />
     <ClInclude Include="WorldHandler.h" />
   </ItemGroup>

+ 47 - 11
CrystalPoint.vcxproj.filters

@@ -22,8 +22,8 @@
     <Filter Include="Source Files\json">
       <UniqueIdentifier>{9c655946-3f99-44ea-bc97-2817656954e0}</UniqueIdentifier>
     </Filter>
-    <Filter Include="Source Files\Menu">
-      <UniqueIdentifier>{e7a88896-78e1-4544-bfc8-cfd55323728e}</UniqueIdentifier>
+    <Filter Include="Source Files\Serial">
+      <UniqueIdentifier>{98128574-9e08-4c28-9372-8d50b6e1a101}</UniqueIdentifier>
     </Filter>
   </ItemGroup>
   <ItemGroup>
@@ -90,20 +90,38 @@
     <ClCompile Include="Portal.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="Controller.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="ControllerHandler.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="lib\serial\src\impl\list_ports\list_ports_win.cc">
+      <Filter>Source Files\Serial</Filter>
+    </ClCompile>
+    <ClCompile Include="lib\serial\src\impl\win.cc">
+      <Filter>Source Files\Serial</Filter>
+    </ClCompile>
+    <ClCompile Include="lib\serial\src\serial.cc">
+      <Filter>Source Files\Serial</Filter>
+    </ClCompile>
+    <ClCompile Include="Weapon.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="Button.cpp">
-      <Filter>Source Files\Menu</Filter>
+      <Filter>Source Files</Filter>
     </ClCompile>
     <ClCompile Include="Menu.cpp">
-      <Filter>Source Files\Menu</Filter>
+      <Filter>Source Files</Filter>
     </ClCompile>
     <ClCompile Include="MenuElement.cpp">
-      <Filter>Source Files\Menu</Filter>
+      <Filter>Source Files</Filter>
     </ClCompile>
     <ClCompile Include="Text.cpp">
-      <Filter>Source Files\Menu</Filter>
+      <Filter>Source Files</Filter>
     </ClCompile>
     <ClCompile Include="Util.cpp">
-      <Filter>Source Files\Menu</Filter>
+      <Filter>Source Files</Filter>
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
@@ -170,21 +188,39 @@
     <ClInclude Include="Portal.h">
       <Filter>Header Files</Filter>
     </ClInclude>
-    <ClInclude Include="Text.h">
+    <ClInclude Include="ControllerHandler.h">
       <Filter>Header Files</Filter>
     </ClInclude>
-    <ClInclude Include="Util.h">
+    <ClInclude Include="Controller.h">
       <Filter>Header Files</Filter>
     </ClInclude>
-    <ClInclude Include="MenuElement.h">
+    <ClInclude Include="lib\serial\include\serial.h">
       <Filter>Header Files</Filter>
     </ClInclude>
-    <ClInclude Include="Menu.h">
+    <ClInclude Include="lib\serial\include\v8stdint.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="lib\serial\include\impl\win.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Weapon.h">
       <Filter>Header Files</Filter>
     </ClInclude>
     <ClInclude Include="Button.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="Menu.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="MenuElement.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Text.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Util.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="worlds\worlds.json">

+ 1 - 0
Enemy.cpp

@@ -18,6 +18,7 @@ Enemy::Enemy(const std::string &fileName,
 	target = position;
 	speed = 1;
 	radius = 10;
+	xp = 10;
 	hasTarget = false;
 	hit_sound_id = CrystalPoint::GetSoundSystem().LoadSound(fileMusic.c_str(), false);
 	music = CrystalPoint::GetSoundSystem().GetSound(hit_sound_id);

+ 1 - 0
Enemy.h

@@ -16,6 +16,7 @@ public:
 	bool hasTarget;
 	Vec3f target;
 	float speed,radius;
+	int xp;
 	bool attack;
 
 	void update(float);

+ 31 - 8
HeightMap.cpp

@@ -29,28 +29,51 @@ HeightMap::HeightMap(const std::string &file, World* world)
 		return imgData[(x + y * width) * 4 + offset];
 	};
 
-	for (int y = 0; y < height-1; y++)
+	std::vector<std::vector<Vec3f>> faceNormals(width-1, std::vector<Vec3f>(height-1, Vec3f(0,1,0)));
+	for (int y = 0; y < height - 1; y++)
 	{
-		for (int x = 0; x < width-1; x++)
+		for (int x = 0; x < width - 1; x++)
 		{
 			int offsets[4][2] = { { 0, 0 },{ 1, 0 },{ 1, 1 },{ 0, 1 } };
 			Vec3f ca(0, heightAt(x, y + 1) - heightAt(x, y), 1);
 			Vec3f ba(1, heightAt(x + 1, y) - heightAt(x, y), 0);
+			Vec3f normal = ca.cross(ba);
+			normal.Normalize();
+			faceNormals[x][y] = normal;
+		}
+	}
+
+	for (int y = 0; y < height-1; y++)
+	{
+		for (int x = 0; x < width-1; x++)
+		{
+			int offsets[4][2] = { { 0, 0 },{ 1, 0 },{ 1, 1 },{ 0, 1 } };
 
 			if (valueAt(x, y, GREEN) > 0)
 			{
 				world->addLevelObject(new LevelObject(world->getObjectFromValue(valueAt(x, y, GREEN)).first, Vec3f(x, heightAt(x, y), y), Vec3f(0, 0, 0), 1, world->getObjectFromValue(valueAt(x, y, GREEN)).second));
 			}
 
-			Vec3f normal = ca.cross(ba);
-			normal.Normalize();
-
 			for (int i = 0; i < 4; i++)
 			{
-				float h = heightAt(x + offsets[i][0], y + offsets[i][1]);
-				vertices.push_back(Vertex{ (float)(x + offsets[i][0]), h, (float)(y + offsets[i][1]),
+				int xx = x + offsets[i][0];
+				int yy = y + offsets[i][1];
+
+				Vec3f normal(0, 0, 0);
+				if(xx < width-1 && yy < height-1)
+					normal = normal + faceNormals[xx][yy];
+				if(xx > 0 && yy < height-1)
+					normal = normal + faceNormals[xx-1][yy];
+				if (xx > 0 && yy > 0)
+					normal = normal + faceNormals[xx-1][yy-1];
+				if (yy > 0 && xx < width-1)
+					normal = normal + faceNormals[xx][yy-1];
+				normal.Normalize();
+
+				float h = heightAt(xx, yy);
+				vertices.push_back(Vertex{ (float)(xx), h, (float)(yy),
 									normal.x, normal.y, normal.z,
-									(x + offsets[i][0]) / (float)height, (y + offsets[i][1]) / (float)width } );
+									(xx) / (float)height, (yy) / (float)width } );
 			}
 		}
 	}

+ 4 - 4
Interface.cpp

@@ -60,8 +60,8 @@ void Interface::draw()
 	glVertex2f(250, 965);
 
 	glColor4f(1.0f, 0.5f, 0.5f, 1.0);
-	glVertex2f(250 + (player->health / 100 * 500), 965);
-	glVertex2f(250 + (player->health / 100 * 500), 980);
+	glVertex2f(250 + (player->health / player->maxHp * 500), 965);
+	glVertex2f(250 + (player->health / player->maxHp * 500), 980);
 	glEnd();
 
 	//XP bar
@@ -79,8 +79,8 @@ void Interface::draw()
 	glVertex2f(250, 935);
 
 	glColor4f(1.0f, 1.0f, 0.5f, 1.0);
-	glVertex2f(250 + (player->xp / 100 * 500), 935);
-	glVertex2f(250 + (player->xp / 100 * 500), 950);
+	glVertex2f(250 + (player->xp / player->maxXp * 500), 935);
+	glVertex2f(250 + (player->xp / player->maxXp * 500), 950);
 	glEnd();
 
 	//Text: level

+ 1 - 1
Menu.cpp

@@ -1,4 +1,4 @@
-#include <GL\freeglut.h>
+#include <GL/freeglut.h>
 #include "Menu.h"
 #include "CrystalPoint.h"
 

+ 1 - 0
Model.cpp

@@ -7,6 +7,7 @@
 #include <string>
 #include <algorithm>
 #include <cmath>
+#include <cstring>
 
 //Prototypes
 std::vector<std::string> split(std::string str, std::string sep);

+ 65 - 3
Player.cpp

@@ -8,10 +8,18 @@ Player* Player::instance = NULL;
 Player::Player()
 {
 	speed = 10;
-	health = 50;
-	xp = 75;
-	level = 10;
+	maxHp = 100;
+	health = maxHp;
+	xp = 0;
+	maxXp = 100;
+	level = 1;
 	crystals = 0;
+
+	leftWeapon = new Weapon("models/weapons/ZwaardMetTextures/TextureZwaard.obj", 1, position, rotation, Vec3f(4.5, -8, -1), Vec3f(-2.0f, 6.0f, -2.1f), Vec2f(170, 70), Vec2f(20, -80));
+	leftWeapon->rotateWeapon(Vec3f(150, 0, 60));
+
+	rightWeapon = new Weapon("models/weapons/ZwaardMetTextures/TextureZwaard.obj", 1, position, rotation, Vec3f(0.5, -8, -1), Vec3f(-2.0f, 6.0f, -2.1f), Vec2f(170, 70), Vec2f(20, -80));
+	rightWeapon->rotateWeapon(Vec3f(150, 0, 60));
 }
 
 Player* Player::getInstance()
@@ -42,6 +50,9 @@ void Player::setCamera()
 	glRotatef(rotation.y, 0, 1, 0);
 	glTranslatef(-position.x, -position.y, -position.z);
 
+	leftWeapon->rotate(rotation);
+	rightWeapon->rotate(rotation);
+
 }
 
 void Player::setPosition(float angle, float fac, bool height)
@@ -53,4 +64,55 @@ void Player::setPosition(float angle, float fac, bool height)
 		position.x -= (float)cos((rotation.y + angle) / 180 * M_PI) * fac;
 		position.z -= (float)sin((rotation.y + angle) / 180 * M_PI) * fac;
 	}
+
+	leftWeapon->move(position);
+	rightWeapon->move(position);
+}
+
+void Player::draw() {
+	leftWeapon->draw();
+	rightWeapon->draw();
+}
+
+void Player::HpDown(int damage)
+{
+	int newHealth = health - damage;
+	if (newHealth <= 0)
+	{
+		exit(0);
+	}
+	health = newHealth;
+}
+
+void Player::HpUp(int hp)
+{
+	if (health != maxHp)
+	{
+		int newHealth = health + hp;
+		if (newHealth >= maxHp)
+		{
+			newHealth = maxHp;
+		}
+		health = newHealth;
+	}	
+}
+
+void Player::XpUp(int xpUp)
+{
+	float newXp = xp + xpUp;
+	if (newXp >= maxXp)
+	{
+		newXp -= maxXp;		
+		levelUp();
+	}
+	xp = newXp;
+}
+
+
+void Player::levelUp()
+{
+	level++;
+	maxXp += 50;
+	maxHp += 10;
+	health = maxHp;
 }

+ 11 - 6
Player.h

@@ -1,18 +1,19 @@
 #pragma once
 #include "Vector.h"
-
-class Model;
+#include "Weapon.h"
 
 class Player
 {
 private:
 	static Player* instance;
+	void levelUp();
 public:
 	Player();
 	~Player();
 
 	void setCamera();
 	void setPosition(float angle, float fac, bool height);
+	void draw(void);
 
 	static Player* getInstance(void);
 	static void init(void);
@@ -20,13 +21,17 @@ public:
 	Vec3f position;
 	Vec2f rotation;
 
-	Model* leftWeapon;
-	Model* rightWeapon;
+	Weapon* leftWeapon;
+	Weapon* rightWeapon;
 
-	float health;
-	float xp;
+	float health, maxHp;
+	float xp, maxXp;
 	int level;
 	int crystals;
 
 	float speed;
+
+	void HpUp(int);
+	void HpDown(int);
+	void XpUp(int);
 };

+ 1 - 1
SoundSystem.cpp

@@ -54,5 +54,5 @@ void SoundSystem::UnloadSound(unsigned int inID)
 	if (inID > sounds.size())
 		return;
 	delete sounds[inID];
-//	sounds.erase[inID];
+	//sounds.erase(sounds.begin() + inID);
 }

+ 1 - 1
Util.h

@@ -1,6 +1,6 @@
 #pragma once
 #include <string>
-#include <GL\freeglut.h>
+#include <GL/freeglut.h>
 
 class Util
 {

+ 86 - 0
Weapon.cpp

@@ -0,0 +1,86 @@
+//
+// Created by janco on 25-5-16.
+//
+
+#include "Weapon.h"
+#include "Model.h"
+#include <iostream>
+#include <string>
+#include <cmath>
+
+
+Weapon::Weapon(std::string modelFilename, float scale, Vec3f location, Vec2f rotation,
+               Vec3f offsetPlayer, Vec3f ankerPoint,
+               Vec2f maxRotation, Vec2f minRotation){
+    weaponmodel = Model::load(modelFilename);
+    rotate(rotation);
+    move(location);
+    this->scale = scale;
+
+    this->offsetPlayer = offsetPlayer;
+    this->ankerPoint = ankerPoint;
+    this->maxRotation = maxRotation;
+    this->minRotation = minRotation;
+};
+
+Weapon::~Weapon(){
+
+}
+
+void Weapon::rotateWeapon(Vec3f rotation){
+    if(rotation.x < maxRotation.x && rotation.x > minRotation.x){
+        rotationWeapon.x = rotation.x;
+    }
+    if(rotation.z < maxRotation.y && rotation.z > minRotation.y){
+        rotationWeapon.z = rotation.z;
+    }
+    rotationWeapon.y = rotation.y;
+}
+
+void Weapon::rotate(Vec2f rotation){
+    this->rotation.y = -rotation.y;
+}
+
+void Weapon::move(Vec3f location){
+    position = location;
+}
+
+void Weapon::draw(){
+    if (weaponmodel != nullptr)
+    {
+        glPushMatrix();
+
+        //Player position and rotation
+        glTranslatef(position.x, position.y, position.z);
+        glRotatef(rotation.x, 1, 0, 0);
+        glRotatef(rotation.y, 0, 1, 0);
+        glRotatef(rotation.z, 0, 0, 1);
+
+        //offset from player
+        glTranslatef(offsetPlayer.x, offsetPlayer.y, offsetPlayer.z);
+
+        //Rotate weapon itself, from specific anker point
+        glTranslatef(ankerPoint.x, ankerPoint.y, ankerPoint.z);
+        glRotatef(rotationWeapon.z, 0, 0, 1);
+        glRotatef(rotationWeapon.y, 0, 1, 0);
+        glRotatef(rotationWeapon.x, 1, 0, 0);
+        glTranslatef(-ankerPoint.x, -ankerPoint.y, -ankerPoint.z);
+
+        glScalef(scale, scale, scale);
+
+        weaponmodel->draw();
+
+        //Test code for finding anker point
+        glColor3ub(255, 255, 0);
+        glTranslatef(ankerPoint.x, ankerPoint.y, ankerPoint.z);
+        glBegin(GL_LINES);
+        glVertex2f(0, 4);
+        glVertex2f(0, -4);
+        glVertex2f(4, 0);
+        glVertex2f(-4, 0);
+        glEnd();
+
+        glPopMatrix();
+
+    }
+}

+ 34 - 0
Weapon.h

@@ -0,0 +1,34 @@
+//
+// Created by janco on 25-5-16.
+//
+
+#ifndef CRYSTALPOINT_WEAPON_H
+#define CRYSTALPOINT_WEAPON_H
+
+#include "Vector.h"
+#include "Model.h"
+#include <string>
+
+class Weapon {
+public:
+    Weapon(std::string modelFilename, float scale, Vec3f location, Vec2f rotation,
+           Vec3f offsetPlayer, Vec3f ankerPoint,
+           Vec2f maxRotation, Vec2f minXRotation);
+    ~Weapon();
+
+    void draw();
+    void rotateWeapon(Vec3f rotation);
+    void rotate(Vec2f rotation);
+    void move(Vec3f location);
+
+    unsigned int damage;
+    Model* weaponmodel;
+
+    float scale;
+    Vec3f position, rotation, rotationWeapon;
+    Vec3f offsetPlayer, ankerPoint;
+    Vec2f maxRotation, minRotation;
+};
+
+
+#endif //CRYSTALPOINT_WEAPON_H

+ 6 - 2
World.cpp

@@ -240,7 +240,7 @@ float World::getHeight(float x, float y)
 
 void World::draw()
 {
-	player->setCamera();
+
 
 	float lightPosition[4] = { 0, 2, 1, 0 };
 	glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
@@ -252,6 +252,9 @@ void World::draw()
 
 	skybox->draw();
 
+	player->setCamera();
+	player->draw();
+
 	heightmap->Draw();
 
 	for (auto &enemy : enemies)
@@ -317,10 +320,11 @@ void World::update(float elapsedTime)
 	if (remove)
 	{
 		delete enemies[count];
+		player->XpUp(enemies[count]->xp);
 		enemies.erase(enemies.begin() + count);
+		player->HpUp(10);		
 	}
 
-
 	skybox->update(elapsedTime, maxEnemies - enemies.size(), maxEnemies);
 
 	if (portal->mayEnter)

+ 221 - 0
lib/serial/include/impl/unix.h

@@ -0,0 +1,221 @@
+/*!
+ * \file serial/impl/unix.h
+ * \author  William Woodall <wjwwood@gmail.com>
+ * \author  John Harrison <ash@greaterthaninfinity.com>
+ * \version 0.1
+ *
+ * \section LICENSE
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2012 William Woodall, John Harrison
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * \section DESCRIPTION
+ *
+ * This provides a unix based pimpl for the Serial class. This implementation is
+ * based off termios.h and uses select for multiplexing the IO ports.
+ *
+ */
+
+#if !defined(_WIN32)
+
+#ifndef SERIAL_IMPL_UNIX_H
+#define SERIAL_IMPL_UNIX_H
+
+
+#include <pthread.h>
+#include "../serial.h"
+
+namespace serial {
+
+using std::size_t;
+using std::string;
+using std::invalid_argument;
+
+using serial::SerialException;
+using serial::IOException;
+
+class MillisecondTimer {
+public:
+  MillisecondTimer(const uint32_t millis);         
+  int64_t remaining();
+
+private:
+  static timespec timespec_now();
+  timespec expiry;
+};
+
+class serial::Serial::SerialImpl {
+public:
+  SerialImpl (const string &port,
+              unsigned long baudrate,
+              bytesize_t bytesize,
+              parity_t parity,
+              stopbits_t stopbits,
+              flowcontrol_t flowcontrol);
+
+  virtual ~SerialImpl ();
+
+  void
+  open ();
+
+  void
+  close ();
+
+  bool
+  isOpen () const;
+
+  size_t
+  available ();
+
+  bool
+  waitReadable (uint32_t timeout);
+
+  void
+  waitByteTimes (size_t count);
+
+  size_t
+  read (uint8_t *buf, size_t size = 1);
+
+  size_t
+  write (const uint8_t *data, size_t length);
+
+  void
+  flush ();
+
+  void
+  flushInput ();
+
+  void
+  flushOutput ();
+
+  void
+  sendBreak (int duration);
+
+  void
+  setBreak (bool level);
+
+  void
+  setRTS (bool level);
+
+  void
+  setDTR (bool level);
+
+  bool
+  waitForChange ();
+
+  bool
+  getCTS ();
+
+  bool
+  getDSR ();
+
+  bool
+  getRI ();
+
+  bool
+  getCD ();
+
+  void
+  setPort (const string &port);
+
+  string
+  getPort () const;
+
+  void
+  setTimeout (Timeout &timeout);
+
+  Timeout
+  getTimeout () const;
+
+  void
+  setBaudrate (unsigned long baudrate);
+
+  unsigned long
+  getBaudrate () const;
+
+  void
+  setBytesize (bytesize_t bytesize);
+
+  bytesize_t
+  getBytesize () const;
+
+  void
+  setParity (parity_t parity);
+
+  parity_t
+  getParity () const;
+
+  void
+  setStopbits (stopbits_t stopbits);
+
+  stopbits_t
+  getStopbits () const;
+
+  void
+  setFlowcontrol (flowcontrol_t flowcontrol);
+
+  flowcontrol_t
+  getFlowcontrol () const;
+
+  void
+  readLock ();
+
+  void
+  readUnlock ();
+
+  void
+  writeLock ();
+
+  void
+  writeUnlock ();
+
+protected:
+  void reconfigurePort ();
+
+private:
+  string port_;               // Path to the file descriptor
+  int fd_;                    // The current file descriptor
+
+  bool is_open_;
+  bool xonxoff_;
+  bool rtscts_;
+
+  Timeout timeout_;           // Timeout for read operations
+  unsigned long baudrate_;    // Baudrate
+  uint32_t byte_time_ns_;     // Nanoseconds to transmit/receive a single byte
+
+  parity_t parity_;           // Parity
+  bytesize_t bytesize_;       // Size of the bytes
+  stopbits_t stopbits_;       // Stop Bits
+  flowcontrol_t flowcontrol_; // Flow Control
+
+  // Mutex used to lock the read functions
+  pthread_mutex_t read_mutex;
+  // Mutex used to lock the write functions
+  pthread_mutex_t write_mutex;
+};
+
+}
+
+#endif // SERIAL_IMPL_UNIX_H
+
+#endif // !defined(_WIN32)

+ 207 - 0
lib/serial/include/impl/win.h

@@ -0,0 +1,207 @@
+/*!
+ * \file serial/impl/windows.h
+ * \author  William Woodall <wjwwood@gmail.com>
+ * \author  John Harrison <ash@greaterthaninfinity.com>
+ * \version 0.1
+ *
+ * \section LICENSE
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2012 William Woodall, John Harrison
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * \section DESCRIPTION
+ *
+ * This provides a windows implementation of the Serial class interface.
+ *
+ */
+
+#if defined(_WIN32)
+
+#ifndef SERIAL_IMPL_WINDOWS_H
+#define SERIAL_IMPL_WINDOWS_H
+
+#include "include/serial.h"
+
+#include "windows.h"
+
+namespace serial {
+
+using std::string;
+using std::wstring;
+using std::invalid_argument;
+
+using serial::SerialException;
+using serial::IOException;
+
+class serial::Serial::SerialImpl {
+public:
+  SerialImpl (const string &port,
+              unsigned long baudrate,
+              bytesize_t bytesize,
+              parity_t parity,
+              stopbits_t stopbits,
+              flowcontrol_t flowcontrol);
+
+  virtual ~SerialImpl ();
+
+  void
+  open ();
+
+  void
+  close ();
+
+  bool
+  isOpen () const;
+
+  size_t
+  available ();
+  
+  bool
+  waitReadable (uint32_t timeout);
+
+  void
+  waitByteTimes (size_t count);
+
+  size_t
+  read (uint8_t *buf, size_t size = 1);
+
+  size_t
+  write (const uint8_t *data, size_t length);
+
+  void
+  flush ();
+
+  void
+  flushInput ();
+
+  void
+  flushOutput ();
+
+  void
+  sendBreak (int duration);
+
+  void
+  setBreak (bool level);
+
+  void
+  setRTS (bool level);
+
+  void
+  setDTR (bool level);
+
+  bool
+  waitForChange ();
+
+  bool
+  getCTS ();
+
+  bool
+  getDSR ();
+
+  bool
+  getRI ();
+
+  bool
+  getCD ();
+
+  void
+  setPort (const string &port);
+
+  string
+  getPort () const;
+
+  void
+  setTimeout (Timeout &timeout);
+
+  Timeout
+  getTimeout () const;
+
+  void
+  setBaudrate (unsigned long baudrate);
+
+  unsigned long
+  getBaudrate () const;
+
+  void
+  setBytesize (bytesize_t bytesize);
+
+  bytesize_t
+  getBytesize () const;
+
+  void
+  setParity (parity_t parity);
+
+  parity_t
+  getParity () const;
+
+  void
+  setStopbits (stopbits_t stopbits);
+
+  stopbits_t
+  getStopbits () const;
+
+  void
+  setFlowcontrol (flowcontrol_t flowcontrol);
+
+  flowcontrol_t
+  getFlowcontrol () const;
+
+  void
+  readLock ();
+
+  void
+  readUnlock ();
+
+  void
+  writeLock ();
+
+  void
+  writeUnlock ();
+
+protected:
+  void reconfigurePort ();
+
+private:
+  wstring port_;               // Path to the file descriptor
+  HANDLE fd_;
+
+  bool is_open_;
+
+  Timeout timeout_;           // Timeout for read operations
+  unsigned long baudrate_;    // Baudrate
+
+  parity_t parity_;           // Parity
+  bytesize_t bytesize_;       // Size of the bytes
+  stopbits_t stopbits_;       // Stop Bits
+  flowcontrol_t flowcontrol_; // Flow Control
+
+  // Mutex used to lock the read functions
+  HANDLE read_mutex;
+  // Mutex used to lock the write functions
+  HANDLE write_mutex;
+};
+
+}
+
+#endif // SERIAL_IMPL_WINDOWS_H
+
+#endif // if defined(_WIN32)

+ 772 - 0
lib/serial/include/serial.h

@@ -0,0 +1,772 @@
+/*!
+ * \file serial/serial.h
+ * \author  William Woodall <wjwwood@gmail.com>
+ * \author  John Harrison   <ash.gti@gmail.com>
+ * \version 0.1
+ *
+ * \section LICENSE
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2012 William Woodall
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * \section DESCRIPTION
+ *
+ * This provides a cross platform interface for interacting with Serial Ports.
+ */
+
+#ifndef SERIAL_H
+#define SERIAL_H
+
+#include <limits>
+#include <vector>
+#include <string>
+#include <cstring>
+#include <sstream>
+#include <exception>
+#include <stdexcept>
+
+#define THROW(exceptionClass, message) throw exceptionClass(__FILE__, \
+__LINE__, (message) )
+
+namespace serial {
+
+/*!
+ * Enumeration defines the possible bytesizes for the serial port.
+ */
+    typedef enum {
+        fivebits = 5,
+        sixbits = 6,
+        sevenbits = 7,
+        eightbits = 8
+    } bytesize_t;
+
+/*!
+ * Enumeration defines the possible parity types for the serial port.
+ */
+    typedef enum {
+        parity_none = 0,
+        parity_odd = 1,
+        parity_even = 2,
+        parity_mark = 3,
+        parity_space = 4
+    } parity_t;
+
+/*!
+ * Enumeration defines the possible stopbit types for the serial port.
+ */
+    typedef enum {
+        stopbits_one = 1,
+        stopbits_two = 2,
+        stopbits_one_point_five
+    } stopbits_t;
+
+/*!
+ * Enumeration defines the possible flowcontrol types for the serial port.
+ */
+    typedef enum {
+        flowcontrol_none = 0,
+        flowcontrol_software,
+        flowcontrol_hardware
+    } flowcontrol_t;
+
+/*!
+ * Structure for setting the timeout of the serial port, times are
+ * in milliseconds.
+ *
+ * In order to disable the interbyte timeout, set it to Timeout::max().
+ */
+    struct Timeout {
+#ifdef max
+# undef max
+#endif
+        static uint32_t max() {return std::numeric_limits<uint32_t>::max();}
+        /*!
+         * Convenience function to generate Timeout structs using a
+         * single absolute timeout.
+         *
+         * \param timeout A long that defines the time in milliseconds until a
+         * timeout occurs after a call to read or write is made.
+         *
+         * \return Timeout struct that represents this simple timeout provided.
+         */
+        static Timeout simpleTimeout(uint32_t timeout) {
+          return Timeout(max(), timeout, 0, timeout, 0);
+        }
+
+        /*! Number of milliseconds between bytes received to timeout on. */
+        uint32_t inter_byte_timeout;
+        /*! A constant number of milliseconds to wait after calling read. */
+        uint32_t read_timeout_constant;
+        /*! A multiplier against the number of requested bytes to wait after
+         *  calling read.
+         */
+        uint32_t read_timeout_multiplier;
+        /*! A constant number of milliseconds to wait after calling write. */
+        uint32_t write_timeout_constant;
+        /*! A multiplier against the number of requested bytes to wait after
+         *  calling write.
+         */
+        uint32_t write_timeout_multiplier;
+
+        explicit Timeout (uint32_t inter_byte_timeout_=0,
+                          uint32_t read_timeout_constant_=0,
+                          uint32_t read_timeout_multiplier_=0,
+                          uint32_t write_timeout_constant_=0,
+                          uint32_t write_timeout_multiplier_=0)
+                : inter_byte_timeout(inter_byte_timeout_),
+                  read_timeout_constant(read_timeout_constant_),
+                  read_timeout_multiplier(read_timeout_multiplier_),
+                  write_timeout_constant(write_timeout_constant_),
+                  write_timeout_multiplier(write_timeout_multiplier_)
+        {}
+    };
+
+/*!
+ * Class that provides a portable serial port interface.
+ */
+    class Serial {
+    public:
+        /*!
+         * Creates a Serial object and opens the port if a port is specified,
+         * otherwise it remains closed until serial::Serial::open is called.
+         *
+         * \param port A std::string containing the address of the serial port,
+         *        which would be something like 'COM1' on Windows and '/dev/ttyS0'
+         *        on Linux.
+         *
+         * \param baudrate An unsigned 32-bit integer that represents the baudrate
+         *
+         * \param timeout A serial::Timeout struct that defines the timeout
+         * conditions for the serial port. \see serial::Timeout
+         *
+         * \param bytesize Size of each byte in the serial transmission of data,
+         * default is eightbits, possible values are: fivebits, sixbits, sevenbits,
+         * eightbits
+         *
+         * \param parity Method of parity, default is parity_none, possible values
+         * are: parity_none, parity_odd, parity_even
+         *
+         * \param stopbits Number of stop bits used, default is stopbits_one,
+         * possible values are: stopbits_one, stopbits_one_point_five, stopbits_two
+         *
+         * \param flowcontrol Type of flowcontrol used, default is
+         * flowcontrol_none, possible values are: flowcontrol_none,
+         * flowcontrol_software, flowcontrol_hardware
+         *
+         * \throw serial::PortNotOpenedException
+         * \throw serial::IOException
+         * \throw std::invalid_argument
+         */
+        Serial (const std::string &port = "",
+                uint32_t baudrate = 9600,
+                Timeout timeout = Timeout(),
+                bytesize_t bytesize = eightbits,
+                parity_t parity = parity_none,
+                stopbits_t stopbits = stopbits_one,
+                flowcontrol_t flowcontrol = flowcontrol_none);
+
+        /*! Destructor */
+        virtual ~Serial ();
+
+        /*!
+         * Opens the serial port as long as the port is set and the port isn't
+         * already open.
+         *
+         * If the port is provided to the constructor then an explicit call to open
+         * is not needed.
+         *
+         * \see Serial::Serial
+         *
+         * \throw std::invalid_argument
+         * \throw serial::SerialException
+         * \throw serial::IOException
+         */
+        void
+        open ();
+
+        /*! Gets the open status of the serial port.
+         *
+         * \return Returns true if the port is open, false otherwise.
+         */
+        bool
+        isOpen () const;
+
+        /*! Closes the serial port. */
+        void
+        close ();
+
+        /*! Return the number of characters in the buffer. */
+        size_t
+        available ();
+
+        /*! Block until there is serial data to read or read_timeout_constant
+         * number of milliseconds have elapsed. The return value is true when
+         * the function exits with the port in a readable state, false otherwise
+         * (due to timeout or select interruption). */
+        bool
+        waitReadable ();
+
+        /*! Block for a period of time corresponding to the transmission time of
+         * count characters at present serial settings. This may be used in con-
+         * junction with waitReadable to read larger blocks of data from the
+         * port. */
+        void
+        waitByteTimes (size_t count);
+
+        /*! Read a given amount of bytes from the serial port into a given buffer.
+         *
+         * The read function will return in one of three cases:
+         *  * The number of requested bytes was read.
+         *    * In this case the number of bytes requested will match the size_t
+         *      returned by read.
+         *  * A timeout occurred, in this case the number of bytes read will not
+         *    match the amount requested, but no exception will be thrown.  One of
+         *    two possible timeouts occurred:
+         *    * The inter byte timeout expired, this means that number of
+         *      milliseconds elapsed between receiving bytes from the serial port
+         *      exceeded the inter byte timeout.
+         *    * The total timeout expired, which is calculated by multiplying the
+         *      read timeout multiplier by the number of requested bytes and then
+         *      added to the read timeout constant.  If that total number of
+         *      milliseconds elapses after the initial call to read a timeout will
+         *      occur.
+         *  * An exception occurred, in this case an actual exception will be thrown.
+         *
+         * \param buffer An uint8_t array of at least the requested size.
+         * \param size A size_t defining how many bytes to be read.
+         *
+         * \return A size_t representing the number of bytes read as a result of the
+         *         call to read.
+         *
+         * \throw serial::PortNotOpenedException
+         * \throw serial::SerialException
+         */
+        size_t
+        read (uint8_t *buffer, size_t size);
+
+        /*! Read a given amount of bytes from the serial port into a give buffer.
+         *
+         * \param buffer A reference to a std::vector of uint8_t.
+         * \param size A size_t defining how many bytes to be read.
+         *
+         * \return A size_t representing the number of bytes read as a result of the
+         *         call to read.
+         *
+         * \throw serial::PortNotOpenedException
+         * \throw serial::SerialException
+         */
+        size_t
+        read (std::vector<uint8_t> &buffer, size_t size = 1);
+
+        /*! Read a given amount of bytes from the serial port into a give buffer.
+         *
+         * \param buffer A reference to a std::string.
+         * \param size A size_t defining how many bytes to be read.
+         *
+         * \return A size_t representing the number of bytes read as a result of the
+         *         call to read.
+         *
+         * \throw serial::PortNotOpenedException
+         * \throw serial::SerialException
+         */
+        size_t
+        read (std::string &buffer, size_t size = 1);
+
+        /*! Read a given amount of bytes from the serial port and return a string
+         *  containing the data.
+         *
+         * \param size A size_t defining how many bytes to be read.
+         *
+         * \return A std::string containing the data read from the port.
+         *
+         * \throw serial::PortNotOpenedException
+         * \throw serial::SerialException
+         */
+        std::string
+        read (size_t size = 1);
+
+        /*! Reads in a line or until a given delimiter has been processed.
+         *
+         * Reads from the serial port until a single line has been read.
+         *
+         * \param buffer A std::string reference used to store the data.
+         * \param size A maximum length of a line, defaults to 65536 (2^16)
+         * \param eol A string to match against for the EOL.
+         *
+         * \return A size_t representing the number of bytes read.
+         *
+         * \throw serial::PortNotOpenedException
+         * \throw serial::SerialException
+         */
+        size_t
+        readline (std::string &buffer, size_t size = 65536, std::string eol = "\n");
+
+        /*! Reads in a line or until a given delimiter has been processed.
+         *
+         * Reads from the serial port until a single line has been read.
+         *
+         * \param size A maximum length of a line, defaults to 65536 (2^16)
+         * \param eol A string to match against for the EOL.
+         *
+         * \return A std::string containing the line.
+         *
+         * \throw serial::PortNotOpenedException
+         * \throw serial::SerialException
+         */
+        std::string
+        readline (size_t size = 65536, std::string eol = "\n");
+
+        /*! Reads in multiple lines until the serial port times out.
+         *
+         * This requires a timeout > 0 before it can be run. It will read until a
+         * timeout occurs and return a list of strings.
+         *
+         * \param size A maximum length of combined lines, defaults to 65536 (2^16)
+         *
+         * \param eol A string to match against for the EOL.
+         *
+         * \return A vector<string> containing the lines.
+         *
+         * \throw serial::PortNotOpenedException
+         * \throw serial::SerialException
+         */
+        std::vector<std::string>
+        readlines (size_t size = 65536, std::string eol = "\n");
+
+        /*! Write a string to the serial port.
+         *
+         * \param data A const reference containing the data to be written
+         * to the serial port.
+         *
+         * \param size A size_t that indicates how many bytes should be written from
+         * the given data buffer.
+         *
+         * \return A size_t representing the number of bytes actually written to
+         * the serial port.
+         *
+         * \throw serial::PortNotOpenedException
+         * \throw serial::SerialException
+         * \throw serial::IOException
+         */
+        size_t
+        write (const uint8_t *data, size_t size);
+
+        /*! Write a string to the serial port.
+         *
+         * \param data A const reference containing the data to be written
+         * to the serial port.
+         *
+         * \return A size_t representing the number of bytes actually written to
+         * the serial port.
+         *
+         * \throw serial::PortNotOpenedException
+         * \throw serial::SerialException
+         * \throw serial::IOException
+         */
+        size_t
+        write (const std::vector<uint8_t> &data);
+
+        /*! Write a string to the serial port.
+         *
+         * \param data A const reference containing the data to be written
+         * to the serial port.
+         *
+         * \return A size_t representing the number of bytes actually written to
+         * the serial port.
+         *
+         * \throw serial::PortNotOpenedException
+         * \throw serial::SerialException
+         * \throw serial::IOException
+         */
+        size_t
+        write (const std::string &data);
+
+        /*! Sets the serial port identifier.
+         *
+         * \param port A const std::string reference containing the address of the
+         * serial port, which would be something like 'COM1' on Windows and
+         * '/dev/ttyS0' on Linux.
+         *
+         * \throw std::invalid_argument
+         */
+        void
+        setPort (const std::string &port);
+
+        /*! Gets the serial port identifier.
+         *
+         * \see Serial::setPort
+         *
+         * \throw std::invalid_argument
+         */
+        std::string
+        getPort () const;
+
+        /*! Sets the timeout for reads and writes using the Timeout struct.
+         *
+         * There are two timeout conditions described here:
+         *  * The inter byte timeout:
+         *    * The inter_byte_timeout component of serial::Timeout defines the
+         *      maximum amount of time, in milliseconds, between receiving bytes on
+         *      the serial port that can pass before a timeout occurs.  Setting this
+         *      to zero will prevent inter byte timeouts from occurring.
+         *  * Total time timeout:
+         *    * The constant and multiplier component of this timeout condition,
+         *      for both read and write, are defined in serial::Timeout.  This
+         *      timeout occurs if the total time since the read or write call was
+         *      made exceeds the specified time in milliseconds.
+         *    * The limit is defined by multiplying the multiplier component by the
+         *      number of requested bytes and adding that product to the constant
+         *      component.  In this way if you want a read call, for example, to
+         *      timeout after exactly one second regardless of the number of bytes
+         *      you asked for then set the read_timeout_constant component of
+         *      serial::Timeout to 1000 and the read_timeout_multiplier to zero.
+         *      This timeout condition can be used in conjunction with the inter
+         *      byte timeout condition with out any problems, timeout will simply
+         *      occur when one of the two timeout conditions is met.  This allows
+         *      users to have maximum control over the trade-off between
+         *      responsiveness and efficiency.
+         *
+         * Read and write functions will return in one of three cases.  When the
+         * reading or writing is complete, when a timeout occurs, or when an
+         * exception occurs.
+         *
+         * \param timeout A serial::Timeout struct containing the inter byte
+         * timeout, and the read and write timeout constants and multipliers.
+         *
+         * \see serial::Timeout
+         */
+        void
+        setTimeout (Timeout &timeout);
+
+        /*! Sets the timeout for reads and writes. */
+        void
+        setTimeout (uint32_t inter_byte_timeout, uint32_t read_timeout_constant,
+                    uint32_t read_timeout_multiplier, uint32_t write_timeout_constant,
+                    uint32_t write_timeout_multiplier)
+        {
+          Timeout timeout(inter_byte_timeout, read_timeout_constant,
+                          read_timeout_multiplier, write_timeout_constant,
+                          write_timeout_multiplier);
+          return setTimeout(timeout);
+        }
+
+        /*! Gets the timeout for reads in seconds.
+         *
+         * \return A Timeout struct containing the inter_byte_timeout, and read
+         * and write timeout constants and multipliers.
+         *
+         * \see Serial::setTimeout
+         */
+        Timeout
+        getTimeout () const;
+
+        /*! Sets the baudrate for the serial port.
+         *
+         * Possible baudrates depends on the system but some safe baudrates include:
+         * 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 56000,
+         * 57600, 115200
+         * Some other baudrates that are supported by some comports:
+         * 128000, 153600, 230400, 256000, 460800, 921600
+         *
+         * \param baudrate An integer that sets the baud rate for the serial port.
+         *
+         * \throw std::invalid_argument
+         */
+        void
+        setBaudrate (uint32_t baudrate);
+
+        /*! Gets the baudrate for the serial port.
+         *
+         * \return An integer that sets the baud rate for the serial port.
+         *
+         * \see Serial::setBaudrate
+         *
+         * \throw std::invalid_argument
+         */
+        uint32_t
+        getBaudrate () const;
+
+        /*! Sets the bytesize for the serial port.
+         *
+         * \param bytesize Size of each byte in the serial transmission of data,
+         * default is eightbits, possible values are: fivebits, sixbits, sevenbits,
+         * eightbits
+         *
+         * \throw std::invalid_argument
+         */
+        void
+        setBytesize (bytesize_t bytesize);
+
+        /*! Gets the bytesize for the serial port.
+         *
+         * \see Serial::setBytesize
+         *
+         * \throw std::invalid_argument
+         */
+        bytesize_t
+        getBytesize () const;
+
+        /*! Sets the parity for the serial port.
+         *
+         * \param parity Method of parity, default is parity_none, possible values
+         * are: parity_none, parity_odd, parity_even
+         *
+         * \throw std::invalid_argument
+         */
+        void
+        setParity (parity_t parity);
+
+        /*! Gets the parity for the serial port.
+         *
+         * \see Serial::setParity
+         *
+         * \throw std::invalid_argument
+         */
+        parity_t
+        getParity () const;
+
+        /*! Sets the stopbits for the serial port.
+         *
+         * \param stopbits Number of stop bits used, default is stopbits_one,
+         * possible values are: stopbits_one, stopbits_one_point_five, stopbits_two
+         *
+         * \throw std::invalid_argument
+         */
+        void
+        setStopbits (stopbits_t stopbits);
+
+        /*! Gets the stopbits for the serial port.
+         *
+         * \see Serial::setStopbits
+         *
+         * \throw std::invalid_argument
+         */
+        stopbits_t
+        getStopbits () const;
+
+        /*! Sets the flow control for the serial port.
+         *
+         * \param flowcontrol Type of flowcontrol used, default is flowcontrol_none,
+         * possible values are: flowcontrol_none, flowcontrol_software,
+         * flowcontrol_hardware
+         *
+         * \throw std::invalid_argument
+         */
+        void
+        setFlowcontrol (flowcontrol_t flowcontrol);
+
+        /*! Gets the flow control for the serial port.
+         *
+         * \see Serial::setFlowcontrol
+         *
+         * \throw std::invalid_argument
+         */
+        flowcontrol_t
+        getFlowcontrol () const;
+
+        /*! Flush the input and output buffers */
+        void
+        flush ();
+
+        /*! Flush only the input buffer */
+        void
+        flushInput ();
+
+        /*! Flush only the output buffer */
+        void
+        flushOutput ();
+
+        /*! Sends the RS-232 break signal.  See tcsendbreak(3). */
+        void
+        sendBreak (int duration);
+
+        /*! Set the break condition to a given level.  Defaults to true. */
+        void
+        setBreak (bool level = true);
+
+        /*! Set the RTS handshaking line to the given level.  Defaults to true. */
+        void
+        setRTS (bool level = true);
+
+        /*! Set the DTR handshaking line to the given level.  Defaults to true. */
+        void
+        setDTR (bool level = true);
+
+        /*!
+         * Blocks until CTS, DSR, RI, CD changes or something interrupts it.
+         *
+         * Can throw an exception if an error occurs while waiting.
+         * You can check the status of CTS, DSR, RI, and CD once this returns.
+         * Uses TIOCMIWAIT via ioctl if available (mostly only on Linux) with a
+         * resolution of less than +-1ms and as good as +-0.2ms.  Otherwise a
+         * polling method is used which can give +-2ms.
+         *
+         * \return Returns true if one of the lines changed, false if something else
+         * occurred.
+         *
+         * \throw SerialException
+         */
+        bool
+        waitForChange ();
+
+        /*! Returns the current status of the CTS line. */
+        bool
+        getCTS ();
+
+        /*! Returns the current status of the DSR line. */
+        bool
+        getDSR ();
+
+        /*! Returns the current status of the RI line. */
+        bool
+        getRI ();
+
+        /*! Returns the current status of the CD line. */
+        bool
+        getCD ();
+
+    private:
+        // Disable copy constructors
+        Serial(const Serial&);
+        Serial& operator=(const Serial&);
+
+        // Pimpl idiom, d_pointer
+        class SerialImpl;
+        SerialImpl *pimpl_;
+
+        // Scoped Lock Classes
+        class ScopedReadLock;
+        class ScopedWriteLock;
+
+        // Read common function
+        size_t
+        read_ (uint8_t *buffer, size_t size);
+        // Write common function
+        size_t
+        write_ (const uint8_t *data, size_t length);
+
+    };
+
+    class SerialException : public std::exception
+    {
+        // Disable copy constructors
+        SerialException& operator=(const SerialException&);
+        std::string e_what_;
+    public:
+        SerialException (const char *description) {
+          std::stringstream ss;
+          ss << "SerialException " << description << " failed.";
+          e_what_ = ss.str();
+        }
+        SerialException (const SerialException& other) : e_what_(other.e_what_) {}
+        virtual ~SerialException() throw() {}
+        virtual const char* what () const throw () {
+          return e_what_.c_str();
+        }
+    };
+
+    class IOException : public std::exception
+    {
+        // Disable copy constructors
+        IOException& operator=(const IOException&);
+        std::string file_;
+        int line_;
+        std::string e_what_;
+        int errno_;
+    public:
+        explicit IOException (std::string file, int line, int errnum)
+                : file_(file), line_(line), errno_(errnum) {
+          std::stringstream ss;
+#if defined(_WIN32) && !defined(__MINGW32__)
+          char error_str [1024];
+      strerror_s(error_str, 1024, errnum);
+#else
+          char * error_str = strerror(errnum);
+#endif
+          ss << "IO Exception (" << errno_ << "): " << error_str;
+          ss << ", file " << file_ << ", line " << line_ << ".";
+          e_what_ = ss.str();
+        }
+        explicit IOException (std::string file, int line, const char * description)
+                : file_(file), line_(line), errno_(0) {
+          std::stringstream ss;
+          ss << "IO Exception: " << description;
+          ss << ", file " << file_ << ", line " << line_ << ".";
+          e_what_ = ss.str();
+        }
+        virtual ~IOException() throw() {}
+        IOException (const IOException& other) : line_(other.line_), e_what_(other.e_what_), errno_(other.errno_) {}
+
+        int getErrorNumber () { return errno_; }
+
+        virtual const char* what () const throw () {
+          return e_what_.c_str();
+        }
+    };
+
+    class PortNotOpenedException : public std::exception
+    {
+        // Disable copy constructors
+        const PortNotOpenedException& operator=(PortNotOpenedException);
+        std::string e_what_;
+    public:
+        PortNotOpenedException (const char * description)  {
+          std::stringstream ss;
+          ss << "PortNotOpenedException " << description << " failed.";
+          e_what_ = ss.str();
+        }
+        PortNotOpenedException (const PortNotOpenedException& other) : e_what_(other.e_what_) {}
+        virtual ~PortNotOpenedException() throw() {}
+        virtual const char* what () const throw () {
+          return e_what_.c_str();
+        }
+    };
+
+/*!
+ * Structure that describes a serial device.
+ */
+    struct PortInfo {
+
+        /*! Address of the serial port (this can be passed to the constructor of Serial). */
+        std::string port;
+
+        /*! Human readable description of serial device if available. */
+        std::string description;
+
+        /*! Hardware ID (e.g. VID:PID of USB serial devices) or "n/a" if not available. */
+        std::string hardware_id;
+
+    };
+
+/* Lists the serial ports available on the system
+ *
+ * Returns a vector of available serial ports, each represented
+ * by a serial::PortInfo data structure:
+ *
+ * \return vector of serial::PortInfo.
+ */
+    std::vector<PortInfo>
+    list_ports();
+
+} // namespace serial
+
+#endif

+ 57 - 0
lib/serial/include/v8stdint.h

@@ -0,0 +1,57 @@
+// This header is from the v8 google project:
+// http://code.google.com/p/v8/source/browse/trunk/include/v8stdint.h
+
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Load definitions of standard types.
+
+#ifndef V8STDINT_H_
+#define V8STDINT_H_
+
+#include <stddef.h>
+#include <stdio.h>
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef short int16_t;  // NOLINT
+typedef unsigned short uint16_t;  // NOLINT
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+// intptr_t and friends are defined in crtdefs.h through stdio.h.
+
+#else
+
+#include <stdint.h>
+
+#endif
+
+#endif  // V8STDINT_H_

+ 335 - 0
lib/serial/src/impl/list_ports/list_ports_linux.cc

@@ -0,0 +1,335 @@
+#if defined(__linux__)
+
+/*
+ * Copyright (c) 2014 Craig Lilley <cralilley@gmail.com>
+ * This software is made available under the terms of the MIT licence.
+ * A copy of the licence can be obtained from:
+ * http://opensource.org/licenses/MIT
+ */
+
+#include <vector>
+#include <string>
+#include <sstream>
+#include <stdexcept>
+#include <iostream>
+#include <fstream>
+#include <cstdio>
+#include <cstdarg>
+#include <cstdlib>
+
+#include <glob.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "../../../include/serial.h"
+
+
+using serial::PortInfo;
+using std::istringstream;
+using std::ifstream;
+using std::getline;
+using std::vector;
+using std::string;
+using std::cout;
+using std::endl;
+
+static vector<string> glob(const vector<string>& patterns);
+static string basename(const string& path);
+static string dirname(const string& path);
+static bool path_exists(const string& path);
+static string realpath(const string& path);
+static string usb_sysfs_friendly_name(const string& sys_usb_path);
+static vector<string> get_sysfs_info(const string& device_path);
+static string read_line(const string& file);
+static string usb_sysfs_hw_string(const string& sysfs_path);
+static string format(const char* format, ...);
+
+vector<string>
+glob(const vector<string>& patterns)
+{
+    vector<string> paths_found;
+
+	if(patterns.size() == 0)
+	    return paths_found;
+
+    glob_t glob_results;
+
+    int glob_retval = glob(patterns[0].c_str(), 0, NULL, &glob_results);
+
+    vector<string>::const_iterator iter = patterns.begin();
+
+    while(++iter != patterns.end())
+    {
+        glob_retval = glob(iter->c_str(), GLOB_APPEND, NULL, &glob_results);
+    }
+
+    for(int path_index = 0; path_index < glob_results.gl_pathc; path_index++)
+    {
+        paths_found.push_back(glob_results.gl_pathv[path_index]);
+    }
+
+    globfree(&glob_results);
+
+    return paths_found;
+}
+
+string
+basename(const string& path)
+{
+    size_t pos = path.rfind("/");
+
+    if(pos == std::string::npos)
+        return path;
+
+    return string(path, pos+1, string::npos);
+}
+
+string
+dirname(const string& path)
+{
+    size_t pos = path.rfind("/");
+
+    if(pos == std::string::npos)
+        return path;
+    else if(pos == 0)
+        return "/";
+
+    return string(path, 0, pos);
+}
+
+bool
+path_exists(const string& path)
+{
+    struct stat sb;
+
+    if( stat(path.c_str(), &sb ) == 0 )
+        return true;
+
+    return false;
+}
+
+string
+realpath(const string& path)
+{
+    char* real_path = realpath(path.c_str(), NULL);
+
+    string result;
+
+    if(real_path != NULL)
+    {
+        result = real_path;
+
+        free(real_path);
+    }
+
+    return result;
+}
+
+string
+usb_sysfs_friendly_name(const string& sys_usb_path)
+{
+    unsigned int device_number = 0;
+
+    istringstream( read_line(sys_usb_path + "/devnum") ) >> device_number;
+
+    string manufacturer = read_line( sys_usb_path + "/manufacturer" );
+
+    string product = read_line( sys_usb_path + "/product" );
+
+    string serial = read_line( sys_usb_path + "/serial" );
+
+    if( manufacturer.empty() && product.empty() && serial.empty() )
+        return "";
+
+    return format("%s %s %s", manufacturer.c_str(), product.c_str(), serial.c_str() );
+}
+
+vector<string>
+get_sysfs_info(const string& device_path)
+{
+    string device_name = basename( device_path );
+
+    string friendly_name;
+
+    string hardware_id;
+
+    string sys_device_path = format( "/sys/class/tty/%s/device", device_name.c_str() );
+
+    if( device_name.compare(0,6,"ttyUSB") == 0 )
+    {
+        sys_device_path = dirname( dirname( realpath( sys_device_path ) ) );
+
+        if( path_exists( sys_device_path ) )
+        {
+            friendly_name = usb_sysfs_friendly_name( sys_device_path );
+
+            hardware_id = usb_sysfs_hw_string( sys_device_path );
+        }
+    }
+    else if( device_name.compare(0,6,"ttyACM") == 0 )
+    {
+        sys_device_path = dirname( realpath( sys_device_path ) );
+
+        if( path_exists( sys_device_path ) )
+        {
+            friendly_name = usb_sysfs_friendly_name( sys_device_path );
+
+            hardware_id = usb_sysfs_hw_string( sys_device_path );
+        }
+    }
+    else
+    {
+        // Try to read ID string of PCI device
+
+        string sys_id_path = sys_device_path + "/id";
+
+        if( path_exists( sys_id_path ) )
+            hardware_id = read_line( sys_id_path );
+    }
+
+    if( friendly_name.empty() )
+        friendly_name = device_name;
+
+    if( hardware_id.empty() )
+        hardware_id = "n/a";
+
+    vector<string> result;
+    result.push_back(friendly_name);
+    result.push_back(hardware_id);
+
+    return result;
+}
+
+string
+read_line(const string& file)
+{
+    ifstream ifs(file.c_str(), ifstream::in);
+
+    string line;
+
+    if(ifs)
+    {
+        getline(ifs, line);
+    }
+
+    return line;
+}
+
+string
+format(const char* format, ...)
+{
+    va_list ap;
+
+    size_t buffer_size_bytes = 256;
+
+    string result;
+
+    char* buffer = (char*)malloc(buffer_size_bytes);
+
+    if( buffer == NULL )
+        return result;
+
+    bool done = false;
+
+    unsigned int loop_count = 0;
+
+    while(!done)
+    {
+        va_start(ap, format);
+
+        int return_value = vsnprintf(buffer, buffer_size_bytes, format, ap);
+
+        if( return_value < 0 )
+        {
+            done = true;
+        }
+        else if( return_value >= buffer_size_bytes )
+        {
+            // Realloc and try again.
+
+            buffer_size_bytes = return_value + 1;
+
+            char* new_buffer_ptr = (char*)realloc(buffer, buffer_size_bytes);
+
+            if( new_buffer_ptr == NULL )
+            {
+                done = true;
+            }
+            else
+            {
+                buffer = new_buffer_ptr;
+            }
+        }
+        else
+        {
+            result = buffer;
+            done = true;
+        }
+
+        va_end(ap);
+
+        if( ++loop_count > 5 )
+            done = true;
+    }
+
+    free(buffer);
+
+    return result;
+}
+
+string
+usb_sysfs_hw_string(const string& sysfs_path)
+{
+    string serial_number = read_line( sysfs_path + "/serial" );
+
+    if( serial_number.length() > 0 )
+    {
+        serial_number = format( "SNR=%s", serial_number.c_str() );
+    }
+
+    string vid = read_line( sysfs_path + "/idVendor" );
+
+    string pid = read_line( sysfs_path + "/idProduct" );
+
+    return format("USB VID:PID=%s:%s %s", vid.c_str(), pid.c_str(), serial_number.c_str() );
+}
+
+vector<PortInfo>
+serial::list_ports()
+{
+    vector<PortInfo> results;
+
+    vector<string> search_globs;
+    search_globs.push_back("/dev/ttyACM*");
+    search_globs.push_back("/dev/ttyS*");
+    search_globs.push_back("/dev/ttyUSB*");
+    search_globs.push_back("/dev/tty.*");
+    search_globs.push_back("/dev/cu.*");
+
+    vector<string> devices_found = glob( search_globs );
+
+    vector<string>::iterator iter = devices_found.begin();
+
+    while( iter != devices_found.end() )
+    {
+        string device = *iter++;
+
+        vector<string> sysfs_info = get_sysfs_info( device );
+
+        string friendly_name = sysfs_info[0];
+
+        string hardware_id = sysfs_info[1];
+
+        PortInfo device_entry;
+        device_entry.port = device;
+        device_entry.description = friendly_name;
+        device_entry.hardware_id = hardware_id;
+
+        results.push_back( device_entry );
+
+    }
+
+    return results;
+}
+
+#endif // defined(__linux__)

+ 286 - 0
lib/serial/src/impl/list_ports/list_ports_osx.cc

@@ -0,0 +1,286 @@
+#if defined(__APPLE__)
+
+#include <sys/param.h>
+#include <stdint.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/serial/IOSerialKeys.h>
+#include <IOKit/IOBSD.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "serial/serial.h"
+
+using serial::PortInfo;
+using std::string;
+using std::vector;
+
+#define HARDWARE_ID_STRING_LENGTH 128
+
+string cfstring_to_string( CFStringRef cfstring );
+string get_device_path( io_object_t& serial_port );
+string get_class_name( io_object_t& obj );
+io_registry_entry_t get_parent_iousb_device( io_object_t& serial_port );
+string get_string_property( io_object_t& device, const char* property );
+uint16_t get_int_property( io_object_t& device, const char* property );
+string rtrim(const string& str);
+
+string
+cfstring_to_string( CFStringRef cfstring )
+{
+    char cstring[MAXPATHLEN];
+    string result;
+
+    if( cfstring )
+    {
+        Boolean success = CFStringGetCString( cfstring,
+            cstring,
+            sizeof(cstring),
+            kCFStringEncodingASCII );
+
+        if( success )
+            result = cstring;
+    }
+
+    return result;
+}
+
+string
+get_device_path( io_object_t& serial_port )
+{
+    CFTypeRef callout_path;
+    string device_path;
+
+    callout_path = IORegistryEntryCreateCFProperty( serial_port,
+        CFSTR(kIOCalloutDeviceKey),
+        kCFAllocatorDefault,
+        0 );
+
+    if (callout_path)
+    {
+        if( CFGetTypeID(callout_path) == CFStringGetTypeID() )
+            device_path = cfstring_to_string( static_cast<CFStringRef>(callout_path) );
+
+        CFRelease(callout_path);
+    }
+
+    return device_path;
+}
+
+string
+get_class_name( io_object_t& obj )
+{
+    string result;
+    io_name_t class_name;
+    kern_return_t kern_result;
+
+    kern_result = IOObjectGetClass( obj, class_name );
+
+    if( kern_result == KERN_SUCCESS )
+        result = class_name;
+
+    return result;
+}
+
+io_registry_entry_t
+get_parent_iousb_device( io_object_t& serial_port )
+{
+    io_object_t device = serial_port;
+    io_registry_entry_t parent = 0;
+    io_registry_entry_t result = 0;
+    kern_return_t kern_result = KERN_FAILURE;
+    string name = get_class_name(device);
+
+    // Walk the IO Registry tree looking for this devices parent IOUSBDevice.
+    while( name != "IOUSBDevice" )
+    {
+        kern_result = IORegistryEntryGetParentEntry( device,
+        kIOServicePlane,
+        &parent );
+
+        if(kern_result != KERN_SUCCESS)
+        {
+            result = 0;
+            break;
+        }
+
+        device = parent;
+
+        name = get_class_name(device);
+    }
+
+    if(kern_result == KERN_SUCCESS)
+        result = device;
+
+    return result;
+}
+
+string
+get_string_property( io_object_t& device, const char* property )
+{
+    string property_name;
+
+    if( device )
+    {
+        CFStringRef property_as_cfstring = CFStringCreateWithCString (
+            kCFAllocatorDefault,
+            property,
+            kCFStringEncodingASCII );
+
+        CFTypeRef name_as_cfstring = IORegistryEntryCreateCFProperty(
+            device,
+            property_as_cfstring,
+            kCFAllocatorDefault,
+            0 );
+
+        if( name_as_cfstring )
+        {
+            if( CFGetTypeID(name_as_cfstring) == CFStringGetTypeID() )
+                property_name = cfstring_to_string( static_cast<CFStringRef>(name_as_cfstring) );
+
+            CFRelease(name_as_cfstring);
+        }
+
+        if(property_as_cfstring)
+            CFRelease(property_as_cfstring);
+    }
+
+    return property_name;
+}
+
+uint16_t
+get_int_property( io_object_t& device, const char* property )
+{
+    uint16_t result = 0;
+
+    if( device )
+    {
+        CFStringRef property_as_cfstring = CFStringCreateWithCString (
+            kCFAllocatorDefault,
+            property,
+            kCFStringEncodingASCII );
+
+        CFTypeRef number = IORegistryEntryCreateCFProperty( device,
+            property_as_cfstring,
+            kCFAllocatorDefault,
+            0 );
+
+        if(property_as_cfstring)
+            CFRelease(property_as_cfstring);
+
+        if( number )
+        {
+            if( CFGetTypeID(number) == CFNumberGetTypeID() )
+            {
+                bool success = CFNumberGetValue( static_cast<CFNumberRef>(number),
+                    kCFNumberSInt16Type,
+                    &result );
+
+                if( !success )
+                    result = 0;
+            }
+
+            CFRelease(number);
+        }
+
+    }
+
+    return result;
+}
+
+string rtrim(const string& str)
+{
+    string result = str;
+
+    string whitespace = " \t\f\v\n\r";
+
+    std::size_t found = result.find_last_not_of(whitespace);
+
+    if (found != std::string::npos)
+        result.erase(found+1);
+    else
+        result.clear();
+
+    return result;
+}
+
+vector<PortInfo>
+serial::list_ports(void)
+{
+    vector<PortInfo> devices_found;
+    CFMutableDictionaryRef classes_to_match;
+    io_iterator_t serial_port_iterator;
+    io_object_t serial_port;
+    mach_port_t master_port;
+    kern_return_t kern_result;
+
+    kern_result = IOMasterPort(MACH_PORT_NULL, &master_port);
+
+    if(kern_result != KERN_SUCCESS)
+        return devices_found;
+
+    classes_to_match = IOServiceMatching(kIOSerialBSDServiceValue);
+
+    if (classes_to_match == NULL)
+        return devices_found;
+
+    CFDictionarySetValue( classes_to_match,
+        CFSTR(kIOSerialBSDTypeKey),
+        CFSTR(kIOSerialBSDAllTypes) );
+
+    kern_result = IOServiceGetMatchingServices(master_port, classes_to_match, &serial_port_iterator);
+
+    if (KERN_SUCCESS != kern_result)
+        return devices_found;
+
+    while ( (serial_port = IOIteratorNext(serial_port_iterator)) )
+    {
+        string device_path = get_device_path( serial_port );
+        io_registry_entry_t parent = get_parent_iousb_device( serial_port );
+        IOObjectRelease(serial_port);
+
+        if( device_path.empty() )
+            continue;
+
+        PortInfo port_info;
+        port_info.port = device_path;
+        port_info.description = "n/a";
+        port_info.hardware_id = "n/a";
+
+        string device_name = rtrim( get_string_property( parent, "USB Product Name" ) );
+        string vendor_name = rtrim( get_string_property( parent, "USB Vendor Name") );
+        string description = rtrim( vendor_name + " " + device_name );
+        if( !description.empty() )
+            port_info.description = description;
+
+        string serial_number = rtrim(get_string_property( parent, "USB Serial Number" ) );
+        uint16_t vendor_id = get_int_property( parent, "idVendor" );
+        uint16_t product_id = get_int_property( parent, "idProduct" );
+
+        if( vendor_id && product_id )
+        {
+            char cstring[HARDWARE_ID_STRING_LENGTH];
+
+            if(serial_number.empty())
+                serial_number = "None";
+
+            int ret = snprintf( cstring, HARDWARE_ID_STRING_LENGTH, "USB VID:PID=%04x:%04x SNR=%s",
+                vendor_id,
+                product_id,
+                serial_number.c_str() );
+
+            if( (ret >= 0) && (ret < HARDWARE_ID_STRING_LENGTH) )
+                port_info.hardware_id = cstring;
+        }
+
+        devices_found.push_back(port_info);
+    }
+
+    IOObjectRelease(serial_port_iterator);
+    return devices_found;
+}
+
+#endif // defined(__APPLE__)

+ 152 - 0
lib/serial/src/impl/list_ports/list_ports_win.cc

@@ -0,0 +1,152 @@
+#if defined(_WIN32)
+
+/*
+ * Copyright (c) 2014 Craig Lilley <cralilley@gmail.com>
+ * This software is made available under the terms of the MIT licence.
+ * A copy of the licence can be obtained from:
+ * http://opensource.org/licenses/MIT
+ */
+
+#include "include/serial.h"
+#include <tchar.h>
+#include <windows.h>
+#include <setupapi.h>
+#include <initguid.h>
+#include <devguid.h>
+#include <cstring>
+
+using serial::PortInfo;
+using std::vector;
+using std::string;
+
+static const DWORD port_name_max_length = 256;
+static const DWORD friendly_name_max_length = 256;
+static const DWORD hardware_id_max_length = 256;
+
+// Convert a wide Unicode string to an UTF8 string
+std::string utf8_encode(const std::wstring &wstr)
+{
+	int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
+	std::string strTo( size_needed, 0 );
+	WideCharToMultiByte                  (CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
+	return strTo;
+}
+
+vector<PortInfo>
+serial::list_ports()
+{
+	vector<PortInfo> devices_found;
+
+	HDEVINFO device_info_set = SetupDiGetClassDevs(
+		(const GUID *) &GUID_DEVCLASS_PORTS,
+		NULL,
+		NULL,
+		DIGCF_PRESENT);
+
+	unsigned int device_info_set_index = 0;
+	SP_DEVINFO_DATA device_info_data;
+
+	device_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
+
+	while(SetupDiEnumDeviceInfo(device_info_set, device_info_set_index, &device_info_data))
+	{
+		device_info_set_index++;
+
+		// Get port name
+
+		HKEY hkey = SetupDiOpenDevRegKey(
+			device_info_set,
+			&device_info_data,
+			DICS_FLAG_GLOBAL,
+			0,
+			DIREG_DEV,
+			KEY_READ);
+
+		TCHAR port_name[port_name_max_length];
+		DWORD port_name_length = port_name_max_length;
+
+		LONG return_code = RegQueryValueEx(
+					hkey,
+					_T("PortName"),
+					NULL,
+					NULL,
+					(LPBYTE)port_name,
+					&port_name_length);
+
+		RegCloseKey(hkey);
+
+		if(return_code != EXIT_SUCCESS)
+			continue;
+
+		if(port_name_length > 0 && port_name_length <= port_name_max_length)
+			port_name[port_name_length-1] = '\0';
+		else
+			port_name[0] = '\0';
+
+		// Ignore parallel ports
+
+		if(_tcsstr(port_name, _T("LPT")) != NULL)
+			continue;
+
+		// Get port friendly name
+
+		TCHAR friendly_name[friendly_name_max_length];
+		DWORD friendly_name_actual_length = 0;
+
+		BOOL got_friendly_name = SetupDiGetDeviceRegistryProperty(
+					device_info_set,
+					&device_info_data,
+					SPDRP_FRIENDLYNAME,
+					NULL,
+					(PBYTE)friendly_name,
+					friendly_name_max_length,
+					&friendly_name_actual_length);
+
+		if(got_friendly_name == TRUE && friendly_name_actual_length > 0)
+			friendly_name[friendly_name_actual_length-1] = '\0';
+		else
+			friendly_name[0] = '\0';
+
+		// Get hardware ID
+
+		TCHAR hardware_id[hardware_id_max_length];
+		DWORD hardware_id_actual_length = 0;
+
+		BOOL got_hardware_id = SetupDiGetDeviceRegistryProperty(
+					device_info_set,
+					&device_info_data,
+					SPDRP_HARDWAREID,
+					NULL,
+					(PBYTE)hardware_id,
+					hardware_id_max_length,
+					&hardware_id_actual_length);
+
+		if(got_hardware_id == TRUE && hardware_id_actual_length > 0)
+			hardware_id[hardware_id_actual_length-1] = '\0';
+		else
+			hardware_id[0] = '\0';
+
+		#ifdef UNICODE
+			std::string portName = utf8_encode(port_name);
+			std::string friendlyName = utf8_encode(friendly_name);
+			std::string hardwareId = utf8_encode(hardware_id);
+		#else
+			std::string portName = port_name;
+			std::string friendlyName = friendly_name;
+			std::string hardwareId = hardware_id;
+		#endif
+
+		PortInfo port_entry;
+		port_entry.port = portName;
+		port_entry.description = friendlyName;
+		port_entry.hardware_id = hardwareId;
+
+		devices_found.push_back(port_entry);
+	}
+
+	SetupDiDestroyDeviceInfoList(device_info_set);
+
+	return devices_found;
+}
+
+#endif // #if defined(_WIN32)

+ 1058 - 0
lib/serial/src/impl/unix.cc

@@ -0,0 +1,1058 @@
+/* Copyright 2012 William Woodall and John Harrison
+ *
+ * Additional Contributors: Christopher Baker @bakercp
+ */
+
+#if !defined(_WIN32)
+
+#include <stdio.h>
+#include <string.h>
+#include <sstream>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/signal.h>
+#include <errno.h>
+#include <paths.h>
+#include <sysexits.h>
+#include <termios.h>
+#include <sys/param.h>
+#include <pthread.h>
+
+#if defined(__linux__)
+# include <linux/serial.h>
+#endif
+
+#include <sys/select.h>
+#include <sys/time.h>
+#include <time.h>
+#ifdef __MACH__
+#include <AvailabilityMacros.h>
+#include <mach/clock.h>
+#include <mach/mach.h>
+#endif
+
+#include "../../include/impl/unix.h"
+
+#ifndef TIOCINQ
+#ifdef FIONREAD
+#define TIOCINQ FIONREAD
+#else
+#define TIOCINQ 0x541B
+#endif
+#endif
+
+#if defined(MAC_OS_X_VERSION_10_3) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3)
+#include <IOKit/serial/ioss.h>
+#endif
+
+using std::string;
+using std::stringstream;
+using std::invalid_argument;
+using serial::MillisecondTimer;
+using serial::Serial;
+using serial::SerialException;
+using serial::PortNotOpenedException;
+using serial::IOException;
+
+
+MillisecondTimer::MillisecondTimer (const uint32_t millis)
+  : expiry(timespec_now())
+{
+  int64_t tv_nsec = expiry.tv_nsec + (millis * 1e6);
+  if (tv_nsec >= 1e9) {
+    int64_t sec_diff = tv_nsec / static_cast<int> (1e9);
+    expiry.tv_nsec = tv_nsec - static_cast<int> (1e9 * sec_diff);
+    expiry.tv_sec += sec_diff;
+  } else {
+    expiry.tv_nsec = tv_nsec;
+  }
+}
+
+int64_t
+MillisecondTimer::remaining ()
+{
+  timespec now(timespec_now());
+  int64_t millis = (expiry.tv_sec - now.tv_sec) * 1e3;
+  millis += (expiry.tv_nsec - now.tv_nsec) / 1e6;
+  return millis;
+}
+
+timespec
+MillisecondTimer::timespec_now ()
+{
+  timespec time;
+# ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time
+  clock_serv_t cclock;
+  mach_timespec_t mts;
+  host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
+  clock_get_time(cclock, &mts);
+  mach_port_deallocate(mach_task_self(), cclock);
+  time.tv_sec = mts.tv_sec;
+  time.tv_nsec = mts.tv_nsec;
+# else
+  clock_gettime(CLOCK_MONOTONIC, &time);
+# endif
+  return time;
+}
+
+timespec
+timespec_from_ms (const uint32_t millis)
+{
+  timespec time;
+  time.tv_sec = millis / 1e3;
+  time.tv_nsec = (millis - (time.tv_sec * 1e3)) * 1e6;
+  return time;
+}
+
+Serial::SerialImpl::SerialImpl (const string &port, unsigned long baudrate,
+                                bytesize_t bytesize,
+                                parity_t parity, stopbits_t stopbits,
+                                flowcontrol_t flowcontrol)
+  : port_ (port), fd_ (-1), is_open_ (false), xonxoff_ (false), rtscts_ (false),
+    baudrate_ (baudrate), parity_ (parity),
+    bytesize_ (bytesize), stopbits_ (stopbits), flowcontrol_ (flowcontrol)
+{
+  pthread_mutex_init(&this->read_mutex, NULL);
+  pthread_mutex_init(&this->write_mutex, NULL);
+  if (port_.empty () == false)
+    open ();
+}
+
+Serial::SerialImpl::~SerialImpl ()
+{
+  close();
+  pthread_mutex_destroy(&this->read_mutex);
+  pthread_mutex_destroy(&this->write_mutex);
+}
+
+void
+Serial::SerialImpl::open ()
+{
+  if (port_.empty ()) {
+    throw invalid_argument ("Empty port is invalid.");
+  }
+  if (is_open_ == true) {
+    throw SerialException ("Serial port already open.");
+  }
+
+  fd_ = ::open (port_.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
+
+  if (fd_ == -1) {
+    switch (errno) {
+    case EINTR:
+      // Recurse because this is a recoverable error.
+      open ();
+      return;
+    case ENFILE:
+    case EMFILE:
+      THROW (IOException, "Too many file handles open.");
+    default:
+      THROW (IOException, errno);
+    }
+  }
+
+  reconfigurePort();
+  is_open_ = true;
+}
+
+void
+Serial::SerialImpl::reconfigurePort ()
+{
+  if (fd_ == -1) {
+    // Can only operate on a valid file descriptor
+    THROW (IOException, "Invalid file descriptor, is the serial port open?");
+  }
+
+  struct termios options; // The options for the file descriptor
+
+  if (tcgetattr(fd_, &options) == -1) {
+    THROW (IOException, "::tcgetattr");
+  }
+
+  // set up raw mode / no echo / binary
+  options.c_cflag |= (tcflag_t)  (CLOCAL | CREAD);
+  options.c_lflag &= (tcflag_t) ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL |
+                                       ISIG | IEXTEN); //|ECHOPRT
+
+  options.c_oflag &= (tcflag_t) ~(OPOST);
+  options.c_iflag &= (tcflag_t) ~(INLCR | IGNCR | ICRNL | IGNBRK);
+#ifdef IUCLC
+  options.c_iflag &= (tcflag_t) ~IUCLC;
+#endif
+#ifdef PARMRK
+  options.c_iflag &= (tcflag_t) ~PARMRK;
+#endif
+
+  // setup baud rate
+  bool custom_baud = false;
+  speed_t baud;
+  switch (baudrate_) {
+#ifdef B0
+  case 0: baud = B0; break;
+#endif
+#ifdef B50
+  case 50: baud = B50; break;
+#endif
+#ifdef B75
+  case 75: baud = B75; break;
+#endif
+#ifdef B110
+  case 110: baud = B110; break;
+#endif
+#ifdef B134
+  case 134: baud = B134; break;
+#endif
+#ifdef B150
+  case 150: baud = B150; break;
+#endif
+#ifdef B200
+  case 200: baud = B200; break;
+#endif
+#ifdef B300
+  case 300: baud = B300; break;
+#endif
+#ifdef B600
+  case 600: baud = B600; break;
+#endif
+#ifdef B1200
+  case 1200: baud = B1200; break;
+#endif
+#ifdef B1800
+  case 1800: baud = B1800; break;
+#endif
+#ifdef B2400
+  case 2400: baud = B2400; break;
+#endif
+#ifdef B4800
+  case 4800: baud = B4800; break;
+#endif
+#ifdef B7200
+  case 7200: baud = B7200; break;
+#endif
+#ifdef B9600
+  case 9600: baud = B9600; break;
+#endif
+#ifdef B14400
+  case 14400: baud = B14400; break;
+#endif
+#ifdef B19200
+  case 19200: baud = B19200; break;
+#endif
+#ifdef B28800
+  case 28800: baud = B28800; break;
+#endif
+#ifdef B57600
+  case 57600: baud = B57600; break;
+#endif
+#ifdef B76800
+  case 76800: baud = B76800; break;
+#endif
+#ifdef B38400
+  case 38400: baud = B38400; break;
+#endif
+#ifdef B115200
+  case 115200: baud = B115200; break;
+#endif
+#ifdef B128000
+  case 128000: baud = B128000; break;
+#endif
+#ifdef B153600
+  case 153600: baud = B153600; break;
+#endif
+#ifdef B230400
+  case 230400: baud = B230400; break;
+#endif
+#ifdef B256000
+  case 256000: baud = B256000; break;
+#endif
+#ifdef B460800
+  case 460800: baud = B460800; break;
+#endif
+#ifdef B576000
+  case 576000: baud = B576000; break;
+#endif
+#ifdef B921600
+  case 921600: baud = B921600; break;
+#endif
+#ifdef B1000000
+  case 1000000: baud = B1000000; break;
+#endif
+#ifdef B1152000
+  case 1152000: baud = B1152000; break;
+#endif
+#ifdef B1500000
+  case 1500000: baud = B1500000; break;
+#endif
+#ifdef B2000000
+  case 2000000: baud = B2000000; break;
+#endif
+#ifdef B2500000
+  case 2500000: baud = B2500000; break;
+#endif
+#ifdef B3000000
+  case 3000000: baud = B3000000; break;
+#endif
+#ifdef B3500000
+  case 3500000: baud = B3500000; break;
+#endif
+#ifdef B4000000
+  case 4000000: baud = B4000000; break;
+#endif
+  default:
+    custom_baud = true;
+    // OS X support
+#if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
+    // Starting with Tiger, the IOSSIOSPEED ioctl can be used to set arbitrary baud rates
+    // other than those specified by POSIX. The driver for the underlying serial hardware
+    // ultimately determines which baud rates can be used. This ioctl sets both the input
+    // and output speed.
+    speed_t new_baud = static_cast<speed_t> (baudrate_);
+    if (-1 == ioctl (fd_, IOSSIOSPEED, &new_baud, 1)) {
+      THROW (IOException, errno);
+    }
+    // Linux Support
+#elif defined(__linux__) && defined (TIOCSSERIAL)
+    struct serial_struct ser;
+
+    if (-1 == ioctl (fd_, TIOCGSERIAL, &ser)) {
+      THROW (IOException, errno);
+    }
+
+    // set custom divisor
+    ser.custom_divisor = ser.baud_base / static_cast<int> (baudrate_);
+    // update flags
+    ser.flags &= ~ASYNC_SPD_MASK;
+    ser.flags |= ASYNC_SPD_CUST;
+
+    if (-1 == ioctl (fd_, TIOCSSERIAL, &ser)) {
+      THROW (IOException, errno);
+    }
+#else
+    throw invalid_argument ("OS does not currently support custom bauds");
+#endif
+  }
+  if (custom_baud == false) {
+#ifdef _BSD_SOURCE
+    ::cfsetspeed(&options, baud);
+#else
+    ::cfsetispeed(&options, baud);
+    ::cfsetospeed(&options, baud);
+#endif
+  }
+
+  // setup char len
+  options.c_cflag &= (tcflag_t) ~CSIZE;
+  if (bytesize_ == eightbits)
+    options.c_cflag |= CS8;
+  else if (bytesize_ == sevenbits)
+    options.c_cflag |= CS7;
+  else if (bytesize_ == sixbits)
+    options.c_cflag |= CS6;
+  else if (bytesize_ == fivebits)
+    options.c_cflag |= CS5;
+  else
+    throw invalid_argument ("invalid char len");
+  // setup stopbits
+  if (stopbits_ == stopbits_one)
+    options.c_cflag &= (tcflag_t) ~(CSTOPB);
+  else if (stopbits_ == stopbits_one_point_five)
+    // ONE POINT FIVE same as TWO.. there is no POSIX support for 1.5
+    options.c_cflag |=  (CSTOPB);
+  else if (stopbits_ == stopbits_two)
+    options.c_cflag |=  (CSTOPB);
+  else
+    throw invalid_argument ("invalid stop bit");
+  // setup parity
+  options.c_iflag &= (tcflag_t) ~(INPCK | ISTRIP);
+  if (parity_ == parity_none) {
+    options.c_cflag &= (tcflag_t) ~(PARENB | PARODD);
+  } else if (parity_ == parity_even) {
+    options.c_cflag &= (tcflag_t) ~(PARODD);
+    options.c_cflag |=  (PARENB);
+  } else if (parity_ == parity_odd) {
+    options.c_cflag |=  (PARENB | PARODD);
+  }
+#ifdef CMSPAR
+  else if (parity_ == parity_mark) {
+    options.c_cflag |=  (PARENB | CMSPAR | PARODD);
+  }
+  else if (parity_ == parity_space) {
+    options.c_cflag |=  (PARENB | CMSPAR);
+    options.c_cflag &= (tcflag_t) ~(PARODD);
+  }
+#else
+  // CMSPAR is not defined on OSX. So do not support mark or space parity.
+  else if (parity_ == parity_mark || parity_ == parity_space) {
+    throw invalid_argument ("OS does not support mark or space parity");
+  }
+#endif  // ifdef CMSPAR
+  else {
+    throw invalid_argument ("invalid parity");
+  }
+  // setup flow control
+  if (flowcontrol_ == flowcontrol_none) {
+    xonxoff_ = false;
+    rtscts_ = false;
+  }
+  if (flowcontrol_ == flowcontrol_software) {
+    xonxoff_ = true;
+    rtscts_ = false;
+  }
+  if (flowcontrol_ == flowcontrol_hardware) {
+    xonxoff_ = false;
+    rtscts_ = true;
+  }
+  // xonxoff
+#ifdef IXANY
+  if (xonxoff_)
+    options.c_iflag |=  (IXON | IXOFF); //|IXANY)
+  else
+    options.c_iflag &= (tcflag_t) ~(IXON | IXOFF | IXANY);
+#else
+  if (xonxoff_)
+    options.c_iflag |=  (IXON | IXOFF);
+  else
+    options.c_iflag &= (tcflag_t) ~(IXON | IXOFF);
+#endif
+  // rtscts
+#ifdef CRTSCTS
+  if (rtscts_)
+    options.c_cflag |=  (CRTSCTS);
+  else
+    options.c_cflag &= (unsigned long) ~(CRTSCTS);
+#elif defined CNEW_RTSCTS
+  if (rtscts_)
+    options.c_cflag |=  (CNEW_RTSCTS);
+  else
+    options.c_cflag &= (unsigned long) ~(CNEW_RTSCTS);
+#else
+#error "OS Support seems wrong."
+#endif
+
+  // http://www.unixwiz.net/techtips/termios-vmin-vtime.html
+  // this basically sets the read call up to be a polling read,
+  // but we are using select to ensure there is data available
+  // to read before each call, so we should never needlessly poll
+  options.c_cc[VMIN] = 0;
+  options.c_cc[VTIME] = 0;
+
+  // activate settings
+  ::tcsetattr (fd_, TCSANOW, &options);
+
+  // Update byte_time_ based on the new settings.
+  uint32_t bit_time_ns = 1e9 / baudrate_;
+  byte_time_ns_ = bit_time_ns * (1 + bytesize_ + parity_ + stopbits_);
+
+  // Compensate for the stopbits_one_point_five enum being equal to int 3,
+  // and not 1.5.
+  if (stopbits_ == stopbits_one_point_five) {
+    byte_time_ns_ += ((1.5 - stopbits_one_point_five) * bit_time_ns);
+  }
+}
+
+void
+Serial::SerialImpl::close ()
+{
+  if (is_open_ == true) {
+    if (fd_ != -1) {
+      int ret;
+      ret = ::close (fd_);
+      if (ret == 0) {
+        fd_ = -1;
+      } else {
+        THROW (IOException, errno);
+      }
+    }
+    is_open_ = false;
+  }
+}
+
+bool
+Serial::SerialImpl::isOpen () const
+{
+  return is_open_;
+}
+
+size_t
+Serial::SerialImpl::available ()
+{
+  if (!is_open_) {
+    return 0;
+  }
+  int count = 0;
+  if (-1 == ioctl (fd_, TIOCINQ, &count)) {
+      THROW (IOException, errno);
+  } else {
+      return static_cast<size_t> (count);
+  }
+}
+
+bool
+Serial::SerialImpl::waitReadable (uint32_t timeout)
+{
+  // Setup a select call to block for serial data or a timeout
+  fd_set readfds;
+  FD_ZERO (&readfds);
+  FD_SET (fd_, &readfds);
+  timespec timeout_ts (timespec_from_ms (timeout));
+  int r = pselect (fd_ + 1, &readfds, NULL, NULL, &timeout_ts, NULL);
+
+  if (r < 0) {
+    // Select was interrupted
+    if (errno == EINTR) {
+      return false;
+    }
+    // Otherwise there was some error
+    THROW (IOException, errno);
+  }
+  // Timeout occurred
+  if (r == 0) {
+    return false;
+  }
+  // This shouldn't happen, if r > 0 our fd has to be in the list!
+  if (!FD_ISSET (fd_, &readfds)) {
+    THROW (IOException, "select reports ready to read, but our fd isn't"
+           " in the list, this shouldn't happen!");
+  }
+  // Data available to read.
+  return true;
+}
+
+void
+Serial::SerialImpl::waitByteTimes (size_t count)
+{
+  timespec wait_time = { 0, static_cast<long>(byte_time_ns_ * count)};
+  pselect (0, NULL, NULL, NULL, &wait_time, NULL);
+}
+
+size_t
+Serial::SerialImpl::read (uint8_t *buf, size_t size)
+{
+  // If the port is not open, throw
+  if (!is_open_) {
+    throw PortNotOpenedException ("Serial::read");
+  }
+  size_t bytes_read = 0;
+
+  // Calculate total timeout in milliseconds t_c + (t_m * N)
+  long total_timeout_ms = timeout_.read_timeout_constant;
+  total_timeout_ms += timeout_.read_timeout_multiplier * static_cast<long> (size);
+  MillisecondTimer total_timeout(total_timeout_ms);
+
+  // Pre-fill buffer with available bytes
+  {
+    ssize_t bytes_read_now = ::read (fd_, buf, size);
+    if (bytes_read_now > 0) {
+      bytes_read = bytes_read_now;
+    }
+  }
+
+  while (bytes_read < size) {
+    int64_t timeout_remaining_ms = total_timeout.remaining();
+    if (timeout_remaining_ms <= 0) {
+      // Timed out
+      break;
+    }
+    // Timeout for the next select is whichever is less of the remaining
+    // total read timeout and the inter-byte timeout.
+    uint32_t timeout = std::min(static_cast<uint32_t> (timeout_remaining_ms),
+                                timeout_.inter_byte_timeout);
+    // Wait for the device to be readable, and then attempt to read.
+    if (waitReadable(timeout)) {
+      // If it's a fixed-length multi-byte read, insert a wait here so that
+      // we can attempt to grab the whole thing in a single IO call. Skip
+      // this wait if a non-max inter_byte_timeout is specified.
+      if (size > 1 && timeout_.inter_byte_timeout == Timeout::max()) {
+        size_t bytes_available = available();
+        if (bytes_available + bytes_read < size) {
+          waitByteTimes(size - (bytes_available + bytes_read));
+        }
+      }
+      // This should be non-blocking returning only what is available now
+      //  Then returning so that select can block again.
+      ssize_t bytes_read_now =
+        ::read (fd_, buf + bytes_read, size - bytes_read);
+      // read should always return some data as select reported it was
+      // ready to read when we get to this point.
+      if (bytes_read_now < 1) {
+        // Disconnected devices, at least on Linux, show the
+        // behavior that they are always ready to read immediately
+        // but reading returns nothing.
+        throw SerialException ("device reports readiness to read but "
+                               "returned no data (device disconnected?)");
+      }
+      // Update bytes_read
+      bytes_read += static_cast<size_t> (bytes_read_now);
+      // If bytes_read == size then we have read everything we need
+      if (bytes_read == size) {
+        break;
+      }
+      // If bytes_read < size then we have more to read
+      if (bytes_read < size) {
+        continue;
+      }
+      // If bytes_read > size then we have over read, which shouldn't happen
+      if (bytes_read > size) {
+        throw SerialException ("read over read, too many bytes where "
+                               "read, this shouldn't happen, might be "
+                               "a logical error!");
+      }
+    }
+  }
+  return bytes_read;
+}
+
+size_t
+Serial::SerialImpl::write (const uint8_t *data, size_t length)
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::write");
+  }
+  fd_set writefds;
+  size_t bytes_written = 0;
+
+  // Calculate total timeout in milliseconds t_c + (t_m * N)
+  long total_timeout_ms = timeout_.write_timeout_constant;
+  total_timeout_ms += timeout_.write_timeout_multiplier * static_cast<long> (length);
+  MillisecondTimer total_timeout(total_timeout_ms);
+
+  while (bytes_written < length) {
+    int64_t timeout_remaining_ms = total_timeout.remaining();
+    if (timeout_remaining_ms <= 0) {
+      // Timed out
+      break;
+    }
+    timespec timeout(timespec_from_ms(timeout_remaining_ms));
+
+    FD_ZERO (&writefds);
+    FD_SET (fd_, &writefds);
+
+    // Do the select
+    int r = pselect (fd_ + 1, NULL, &writefds, NULL, &timeout, NULL);
+
+    // Figure out what happened by looking at select's response 'r'
+    /** Error **/
+    if (r < 0) {
+      // Select was interrupted, try again
+      if (errno == EINTR) {
+        continue;
+      }
+      // Otherwise there was some error
+      THROW (IOException, errno);
+    }
+    /** Timeout **/
+    if (r == 0) {
+      break;
+    }
+    /** Port ready to write **/
+    if (r > 0) {
+      // Make sure our file descriptor is in the ready to write list
+      if (FD_ISSET (fd_, &writefds)) {
+        // This will write some
+        ssize_t bytes_written_now =
+          ::write (fd_, data + bytes_written, length - bytes_written);
+        // write should always return some data as select reported it was
+        // ready to write when we get to this point.
+        if (bytes_written_now < 1) {
+          // Disconnected devices, at least on Linux, show the
+          // behavior that they are always ready to write immediately
+          // but writing returns nothing.
+          throw SerialException ("device reports readiness to write but "
+                                 "returned no data (device disconnected?)");
+        }
+        // Update bytes_written
+        bytes_written += static_cast<size_t> (bytes_written_now);
+        // If bytes_written == size then we have written everything we need to
+        if (bytes_written == length) {
+          break;
+        }
+        // If bytes_written < size then we have more to write
+        if (bytes_written < length) {
+          continue;
+        }
+        // If bytes_written > size then we have over written, which shouldn't happen
+        if (bytes_written > length) {
+          throw SerialException ("write over wrote, too many bytes where "
+                                 "written, this shouldn't happen, might be "
+                                 "a logical error!");
+        }
+      }
+      // This shouldn't happen, if r > 0 our fd has to be in the list!
+      THROW (IOException, "select reports ready to write, but our fd isn't"
+                          " in the list, this shouldn't happen!");
+    }
+  }
+  return bytes_written;
+}
+
+void
+Serial::SerialImpl::setPort (const string &port)
+{
+  port_ = port;
+}
+
+string
+Serial::SerialImpl::getPort () const
+{
+  return port_;
+}
+
+void
+Serial::SerialImpl::setTimeout (serial::Timeout &timeout)
+{
+  timeout_ = timeout;
+}
+
+serial::Timeout
+Serial::SerialImpl::getTimeout () const
+{
+  return timeout_;
+}
+
+void
+Serial::SerialImpl::setBaudrate (unsigned long baudrate)
+{
+  baudrate_ = baudrate;
+  if (is_open_)
+    reconfigurePort ();
+}
+
+unsigned long
+Serial::SerialImpl::getBaudrate () const
+{
+  return baudrate_;
+}
+
+void
+Serial::SerialImpl::setBytesize (serial::bytesize_t bytesize)
+{
+  bytesize_ = bytesize;
+  if (is_open_)
+    reconfigurePort ();
+}
+
+serial::bytesize_t
+Serial::SerialImpl::getBytesize () const
+{
+  return bytesize_;
+}
+
+void
+Serial::SerialImpl::setParity (serial::parity_t parity)
+{
+  parity_ = parity;
+  if (is_open_)
+    reconfigurePort ();
+}
+
+serial::parity_t
+Serial::SerialImpl::getParity () const
+{
+  return parity_;
+}
+
+void
+Serial::SerialImpl::setStopbits (serial::stopbits_t stopbits)
+{
+  stopbits_ = stopbits;
+  if (is_open_)
+    reconfigurePort ();
+}
+
+serial::stopbits_t
+Serial::SerialImpl::getStopbits () const
+{
+  return stopbits_;
+}
+
+void
+Serial::SerialImpl::setFlowcontrol (serial::flowcontrol_t flowcontrol)
+{
+  flowcontrol_ = flowcontrol;
+  if (is_open_)
+    reconfigurePort ();
+}
+
+serial::flowcontrol_t
+Serial::SerialImpl::getFlowcontrol () const
+{
+  return flowcontrol_;
+}
+
+void
+Serial::SerialImpl::flush ()
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::flush");
+  }
+  tcdrain (fd_);
+}
+
+void
+Serial::SerialImpl::flushInput ()
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::flushInput");
+  }
+  tcflush (fd_, TCIFLUSH);
+}
+
+void
+Serial::SerialImpl::flushOutput ()
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::flushOutput");
+  }
+  tcflush (fd_, TCOFLUSH);
+}
+
+void
+Serial::SerialImpl::sendBreak (int duration)
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::sendBreak");
+  }
+  tcsendbreak (fd_, static_cast<int> (duration / 4));
+}
+
+void
+Serial::SerialImpl::setBreak (bool level)
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::setBreak");
+  }
+
+  if (level) {
+    if (-1 == ioctl (fd_, TIOCSBRK))
+    {
+        stringstream ss;
+        ss << "setBreak failed on a call to ioctl(TIOCSBRK): " << errno << " " << strerror(errno);
+        throw(SerialException(ss.str().c_str()));
+    }
+  } else {
+    if (-1 == ioctl (fd_, TIOCCBRK))
+    {
+        stringstream ss;
+        ss << "setBreak failed on a call to ioctl(TIOCCBRK): " << errno << " " << strerror(errno);
+        throw(SerialException(ss.str().c_str()));
+    }
+  }
+}
+
+void
+Serial::SerialImpl::setRTS (bool level)
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::setRTS");
+  }
+
+  int command = TIOCM_RTS;
+
+  if (level) {
+    if (-1 == ioctl (fd_, TIOCMBIS, &command))
+    {
+      stringstream ss;
+      ss << "setRTS failed on a call to ioctl(TIOCMBIS): " << errno << " " << strerror(errno);
+      throw(SerialException(ss.str().c_str()));
+    }
+  } else {
+    if (-1 == ioctl (fd_, TIOCMBIC, &command))
+    {
+      stringstream ss;
+      ss << "setRTS failed on a call to ioctl(TIOCMBIC): " << errno << " " << strerror(errno);
+      throw(SerialException(ss.str().c_str()));
+    }
+  }
+}
+
+void
+Serial::SerialImpl::setDTR (bool level)
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::setDTR");
+  }
+
+  int command = TIOCM_DTR;
+
+  if (level) {
+    if (-1 == ioctl (fd_, TIOCMBIS, &command))
+    {
+      stringstream ss;
+      ss << "setDTR failed on a call to ioctl(TIOCMBIS): " << errno << " " << strerror(errno);
+      throw(SerialException(ss.str().c_str()));
+    }
+  } else {
+    if (-1 == ioctl (fd_, TIOCMBIC, &command))
+    {
+      stringstream ss;
+      ss << "setDTR failed on a call to ioctl(TIOCMBIC): " << errno << " " << strerror(errno);
+      throw(SerialException(ss.str().c_str()));
+    }
+  }
+}
+
+bool
+Serial::SerialImpl::waitForChange ()
+{
+#ifndef TIOCMIWAIT
+
+while (is_open_ == true) {
+
+    int status;
+
+    if (-1 == ioctl (fd_, TIOCMGET, &status))
+    {
+        stringstream ss;
+        ss << "waitForChange failed on a call to ioctl(TIOCMGET): " << errno << " " << strerror(errno);
+        throw(SerialException(ss.str().c_str()));
+    }
+    else
+    {
+        if (0 != (status & TIOCM_CTS)
+         || 0 != (status & TIOCM_DSR)
+         || 0 != (status & TIOCM_RI)
+         || 0 != (status & TIOCM_CD))
+        {
+          return true;
+        }
+    }
+
+    usleep(1000);
+  }
+
+  return false;
+#else
+  int command = (TIOCM_CD|TIOCM_DSR|TIOCM_RI|TIOCM_CTS);
+
+  if (-1 == ioctl (fd_, TIOCMIWAIT, &command)) {
+    stringstream ss;
+    ss << "waitForDSR failed on a call to ioctl(TIOCMIWAIT): "
+       << errno << " " << strerror(errno);
+    throw(SerialException(ss.str().c_str()));
+  }
+  return true;
+#endif
+}
+
+bool
+Serial::SerialImpl::getCTS ()
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::getCTS");
+  }
+
+  int status;
+
+  if (-1 == ioctl (fd_, TIOCMGET, &status))
+  {
+    stringstream ss;
+    ss << "getCTS failed on a call to ioctl(TIOCMGET): " << errno << " " << strerror(errno);
+    throw(SerialException(ss.str().c_str()));
+  }
+  else
+  {
+    return 0 != (status & TIOCM_CTS);
+  }
+}
+
+bool
+Serial::SerialImpl::getDSR ()
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::getDSR");
+  }
+
+  int status;
+
+  if (-1 == ioctl (fd_, TIOCMGET, &status))
+  {
+      stringstream ss;
+      ss << "getDSR failed on a call to ioctl(TIOCMGET): " << errno << " " << strerror(errno);
+      throw(SerialException(ss.str().c_str()));
+  }
+  else
+  {
+      return 0 != (status & TIOCM_DSR);
+  }
+}
+
+bool
+Serial::SerialImpl::getRI ()
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::getRI");
+  }
+
+  int status;
+
+  if (-1 == ioctl (fd_, TIOCMGET, &status))
+  {
+    stringstream ss;
+    ss << "getRI failed on a call to ioctl(TIOCMGET): " << errno << " " << strerror(errno);
+    throw(SerialException(ss.str().c_str()));
+  }
+  else
+  {
+    return 0 != (status & TIOCM_RI);
+  }
+}
+
+bool
+Serial::SerialImpl::getCD ()
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::getCD");
+  }
+
+  int status;
+
+  if (-1 == ioctl (fd_, TIOCMGET, &status))
+  {
+    stringstream ss;
+    ss << "getCD failed on a call to ioctl(TIOCMGET): " << errno << " " << strerror(errno);
+    throw(SerialException(ss.str().c_str()));
+  }
+  else
+  {
+    return 0 != (status & TIOCM_CD);
+  }
+}
+
+void
+Serial::SerialImpl::readLock ()
+{
+  int result = pthread_mutex_lock(&this->read_mutex);
+  if (result) {
+    THROW (IOException, result);
+  }
+}
+
+void
+Serial::SerialImpl::readUnlock ()
+{
+  int result = pthread_mutex_unlock(&this->read_mutex);
+  if (result) {
+    THROW (IOException, result);
+  }
+}
+
+void
+Serial::SerialImpl::writeLock ()
+{
+  int result = pthread_mutex_lock(&this->write_mutex);
+  if (result) {
+    THROW (IOException, result);
+  }
+}
+
+void
+Serial::SerialImpl::writeUnlock ()
+{
+  int result = pthread_mutex_unlock(&this->write_mutex);
+  if (result) {
+    THROW (IOException, result);
+  }
+}
+
+#endif // !defined(_WIN32)

+ 640 - 0
lib/serial/src/impl/win.cc

@@ -0,0 +1,640 @@
+#if defined(_WIN32)
+
+/* Copyright 2012 William Woodall and John Harrison */
+
+#include <sstream>
+
+#include "include/impl/win.h"
+
+using std::string;
+using std::wstring;
+using std::stringstream;
+using std::invalid_argument;
+using serial::Serial;
+using serial::Timeout;
+using serial::bytesize_t;
+using serial::parity_t;
+using serial::stopbits_t;
+using serial::flowcontrol_t;
+using serial::SerialException;
+using serial::PortNotOpenedException;
+using serial::IOException;
+
+inline wstring
+_prefix_port_if_needed(const wstring &input)
+{
+  static wstring windows_com_port_prefix = L"\\\\.\\";
+  if (input.compare(windows_com_port_prefix) != 0)
+  {
+    return windows_com_port_prefix + input;
+  }
+  return input;
+}
+
+Serial::SerialImpl::SerialImpl (const string &port, unsigned long baudrate,
+                                bytesize_t bytesize,
+                                parity_t parity, stopbits_t stopbits,
+                                flowcontrol_t flowcontrol)
+  : port_ (port.begin(), port.end()), fd_ (INVALID_HANDLE_VALUE), is_open_ (false),
+    baudrate_ (baudrate), parity_ (parity),
+    bytesize_ (bytesize), stopbits_ (stopbits), flowcontrol_ (flowcontrol)
+{
+  read_mutex = CreateMutex(NULL, false, NULL);
+  write_mutex = CreateMutex(NULL, false, NULL);
+  if (port_.empty () == false)
+    open ();
+}
+
+Serial::SerialImpl::~SerialImpl ()
+{
+  this->close();
+  CloseHandle(read_mutex);
+  CloseHandle(write_mutex);
+}
+
+void
+Serial::SerialImpl::open ()
+{
+  if (port_.empty ()) {
+    throw invalid_argument ("Empty port is invalid.");
+  }
+  if (is_open_ == true) {
+    throw SerialException ("Serial port already open.");
+  }
+
+  // See: https://github.com/wjwwood/serial/issues/84
+  wstring port_with_prefix = _prefix_port_if_needed(port_);
+  LPCWSTR lp_port = port_with_prefix.c_str();
+  fd_ = CreateFileW(lp_port,
+                    GENERIC_READ | GENERIC_WRITE,
+                    0,
+                    0,
+                    OPEN_EXISTING,
+                    FILE_ATTRIBUTE_NORMAL,
+                    0);
+
+  if (fd_ == INVALID_HANDLE_VALUE) {
+    DWORD errno_ = GetLastError();
+	stringstream ss;
+    switch (errno_) {
+    case ERROR_FILE_NOT_FOUND:
+      // Use this->getPort to convert to a std::string
+      ss << "Specified port, " << this->getPort() << ", does not exist.";
+      THROW (IOException, ss.str().c_str());
+    default:
+      ss << "Unknown error opening the serial port: " << errno;
+      THROW (IOException, ss.str().c_str());
+    }
+  }
+
+  reconfigurePort();
+  is_open_ = true;
+}
+
+void
+Serial::SerialImpl::reconfigurePort ()
+{
+  if (fd_ == INVALID_HANDLE_VALUE) {
+    // Can only operate on a valid file descriptor
+    THROW (IOException, "Invalid file descriptor, is the serial port open?");
+  }
+
+  DCB dcbSerialParams = {0};
+
+  dcbSerialParams.DCBlength=sizeof(dcbSerialParams);
+
+  if (!GetCommState(fd_, &dcbSerialParams)) {
+    //error getting state
+    THROW (IOException, "Error getting the serial port state.");
+  }
+
+  // setup baud rate
+  switch (baudrate_) {
+#ifdef CBR_0
+  case 0: dcbSerialParams.BaudRate = CBR_0; break;
+#endif
+#ifdef CBR_50
+  case 50: dcbSerialParams.BaudRate = CBR_50; break;
+#endif
+#ifdef CBR_75
+  case 75: dcbSerialParams.BaudRate = CBR_75; break;
+#endif
+#ifdef CBR_110
+  case 110: dcbSerialParams.BaudRate = CBR_110; break;
+#endif
+#ifdef CBR_134
+  case 134: dcbSerialParams.BaudRate = CBR_134; break;
+#endif
+#ifdef CBR_150
+  case 150: dcbSerialParams.BaudRate = CBR_150; break;
+#endif
+#ifdef CBR_200
+  case 200: dcbSerialParams.BaudRate = CBR_200; break;
+#endif
+#ifdef CBR_300
+  case 300: dcbSerialParams.BaudRate = CBR_300; break;
+#endif
+#ifdef CBR_600
+  case 600: dcbSerialParams.BaudRate = CBR_600; break;
+#endif
+#ifdef CBR_1200
+  case 1200: dcbSerialParams.BaudRate = CBR_1200; break;
+#endif
+#ifdef CBR_1800
+  case 1800: dcbSerialParams.BaudRate = CBR_1800; break;
+#endif
+#ifdef CBR_2400
+  case 2400: dcbSerialParams.BaudRate = CBR_2400; break;
+#endif
+#ifdef CBR_4800
+  case 4800: dcbSerialParams.BaudRate = CBR_4800; break;
+#endif
+#ifdef CBR_7200
+  case 7200: dcbSerialParams.BaudRate = CBR_7200; break;
+#endif
+#ifdef CBR_9600
+  case 9600: dcbSerialParams.BaudRate = CBR_9600; break;
+#endif
+#ifdef CBR_14400
+  case 14400: dcbSerialParams.BaudRate = CBR_14400; break;
+#endif
+#ifdef CBR_19200
+  case 19200: dcbSerialParams.BaudRate = CBR_19200; break;
+#endif
+#ifdef CBR_28800
+  case 28800: dcbSerialParams.BaudRate = CBR_28800; break;
+#endif
+#ifdef CBR_57600
+  case 57600: dcbSerialParams.BaudRate = CBR_57600; break;
+#endif
+#ifdef CBR_76800
+  case 76800: dcbSerialParams.BaudRate = CBR_76800; break;
+#endif
+#ifdef CBR_38400
+  case 38400: dcbSerialParams.BaudRate = CBR_38400; break;
+#endif
+#ifdef CBR_115200
+  case 115200: dcbSerialParams.BaudRate = CBR_115200; break;
+#endif
+#ifdef CBR_128000
+  case 128000: dcbSerialParams.BaudRate = CBR_128000; break;
+#endif
+#ifdef CBR_153600
+  case 153600: dcbSerialParams.BaudRate = CBR_153600; break;
+#endif
+#ifdef CBR_230400
+  case 230400: dcbSerialParams.BaudRate = CBR_230400; break;
+#endif
+#ifdef CBR_256000
+  case 256000: dcbSerialParams.BaudRate = CBR_256000; break;
+#endif
+#ifdef CBR_460800
+  case 460800: dcbSerialParams.BaudRate = CBR_460800; break;
+#endif
+#ifdef CBR_921600
+  case 921600: dcbSerialParams.BaudRate = CBR_921600; break;
+#endif
+  default:
+    // Try to blindly assign it
+    dcbSerialParams.BaudRate = baudrate_;
+  }
+
+  // setup char len
+  if (bytesize_ == eightbits)
+    dcbSerialParams.ByteSize = 8;
+  else if (bytesize_ == sevenbits)
+    dcbSerialParams.ByteSize = 7;
+  else if (bytesize_ == sixbits)
+    dcbSerialParams.ByteSize = 6;
+  else if (bytesize_ == fivebits)
+    dcbSerialParams.ByteSize = 5;
+  else
+    throw invalid_argument ("invalid char len");
+
+  // setup stopbits
+  if (stopbits_ == stopbits_one)
+    dcbSerialParams.StopBits = ONESTOPBIT;
+  else if (stopbits_ == stopbits_one_point_five)
+    dcbSerialParams.StopBits = ONE5STOPBITS;
+  else if (stopbits_ == stopbits_two)
+    dcbSerialParams.StopBits = TWOSTOPBITS;
+  else
+    throw invalid_argument ("invalid stop bit");
+
+  // setup parity
+  if (parity_ == parity_none) {
+    dcbSerialParams.Parity = NOPARITY;
+  } else if (parity_ == parity_even) {
+    dcbSerialParams.Parity = EVENPARITY;
+  } else if (parity_ == parity_odd) {
+    dcbSerialParams.Parity = ODDPARITY;
+  } else if (parity_ == parity_mark) {
+    dcbSerialParams.Parity = MARKPARITY;
+  } else if (parity_ == parity_space) {
+    dcbSerialParams.Parity = SPACEPARITY;
+  } else {
+    throw invalid_argument ("invalid parity");
+  }
+
+  // setup flowcontrol
+  if (flowcontrol_ == flowcontrol_none) {
+    dcbSerialParams.fOutxCtsFlow = false;
+    dcbSerialParams.fRtsControl = 0x00;
+    dcbSerialParams.fOutX = false;
+    dcbSerialParams.fInX = false;
+  }
+  if (flowcontrol_ == flowcontrol_software) {
+    dcbSerialParams.fOutxCtsFlow = false;
+    dcbSerialParams.fRtsControl = 0x00;
+    dcbSerialParams.fOutX = true;
+    dcbSerialParams.fInX = true;
+  }
+  if (flowcontrol_ == flowcontrol_hardware) {
+    dcbSerialParams.fOutxCtsFlow = true;
+    dcbSerialParams.fRtsControl = 0x03;
+    dcbSerialParams.fOutX = false;
+    dcbSerialParams.fInX = false;
+  }
+
+  // activate settings
+  if (!SetCommState(fd_, &dcbSerialParams)){
+    CloseHandle(fd_);
+    THROW (IOException, "Error setting serial port settings.");
+  }
+
+  // Setup timeouts
+  COMMTIMEOUTS timeouts = {0};
+  timeouts.ReadIntervalTimeout = timeout_.inter_byte_timeout;
+  timeouts.ReadTotalTimeoutConstant = timeout_.read_timeout_constant;
+  timeouts.ReadTotalTimeoutMultiplier = timeout_.read_timeout_multiplier;
+  timeouts.WriteTotalTimeoutConstant = timeout_.write_timeout_constant;
+  timeouts.WriteTotalTimeoutMultiplier = timeout_.write_timeout_multiplier;
+  if (!SetCommTimeouts(fd_, &timeouts)) {
+    THROW (IOException, "Error setting timeouts.");
+  }
+}
+
+void
+Serial::SerialImpl::close ()
+{
+  if (is_open_ == true) {
+    if (fd_ != INVALID_HANDLE_VALUE) {
+      int ret;
+      ret = CloseHandle(fd_);
+      if (ret == 0) {
+        stringstream ss;
+        ss << "Error while closing serial port: " << GetLastError();
+        THROW (IOException, ss.str().c_str());
+      } else {
+        fd_ = INVALID_HANDLE_VALUE;
+      }
+    }
+    is_open_ = false;
+  }
+}
+
+bool
+Serial::SerialImpl::isOpen () const
+{
+  return is_open_;
+}
+
+size_t
+Serial::SerialImpl::available ()
+{
+  if (!is_open_) {
+    return 0;
+  }
+  COMSTAT cs;
+  if (!ClearCommError(fd_, NULL, &cs)) {
+    stringstream ss;
+    ss << "Error while checking status of the serial port: " << GetLastError();
+    THROW (IOException, ss.str().c_str());
+  }
+  return static_cast<size_t>(cs.cbInQue);
+}
+
+bool
+Serial::SerialImpl::waitReadable (uint32_t /*timeout*/)
+{
+  THROW (IOException, "waitReadable is not implemented on Windows.");
+  return false;
+}
+
+void
+Serial::SerialImpl::waitByteTimes (size_t /*count*/)
+{
+  THROW (IOException, "waitByteTimes is not implemented on Windows.");
+}
+
+size_t
+Serial::SerialImpl::read (uint8_t *buf, size_t size)
+{
+  if (!is_open_) {
+    throw PortNotOpenedException ("Serial::read");
+  }
+  DWORD bytes_read;
+  if (!ReadFile(fd_, buf, static_cast<DWORD>(size), &bytes_read, NULL)) {
+    stringstream ss;
+    ss << "Error while reading from the serial port: " << GetLastError();
+    THROW (IOException, ss.str().c_str());
+  }
+  return (size_t) (bytes_read);
+}
+
+size_t
+Serial::SerialImpl::write (const uint8_t *data, size_t length)
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::write");
+  }
+  DWORD bytes_written;
+  if (!WriteFile(fd_, data, static_cast<DWORD>(length), &bytes_written, NULL)) {
+    stringstream ss;
+    ss << "Error while writing to the serial port: " << GetLastError();
+    THROW (IOException, ss.str().c_str());
+  }
+  return (size_t) (bytes_written);
+}
+
+void
+Serial::SerialImpl::setPort (const string &port)
+{
+  port_ = wstring(port.begin(), port.end());
+}
+
+string
+Serial::SerialImpl::getPort () const
+{
+  return string(port_.begin(), port_.end());
+}
+
+void
+Serial::SerialImpl::setTimeout (serial::Timeout &timeout)
+{
+  timeout_ = timeout;
+  if (is_open_) {
+    reconfigurePort ();
+  }
+}
+
+serial::Timeout
+Serial::SerialImpl::getTimeout () const
+{
+  return timeout_;
+}
+
+void
+Serial::SerialImpl::setBaudrate (unsigned long baudrate)
+{
+  baudrate_ = baudrate;
+  if (is_open_) {
+    reconfigurePort ();
+  }
+}
+
+unsigned long
+Serial::SerialImpl::getBaudrate () const
+{
+  return baudrate_;
+}
+
+void
+Serial::SerialImpl::setBytesize (serial::bytesize_t bytesize)
+{
+  bytesize_ = bytesize;
+  if (is_open_) {
+    reconfigurePort ();
+  }
+}
+
+serial::bytesize_t
+Serial::SerialImpl::getBytesize () const
+{
+  return bytesize_;
+}
+
+void
+Serial::SerialImpl::setParity (serial::parity_t parity)
+{
+  parity_ = parity;
+  if (is_open_) {
+    reconfigurePort ();
+  }
+}
+
+serial::parity_t
+Serial::SerialImpl::getParity () const
+{
+  return parity_;
+}
+
+void
+Serial::SerialImpl::setStopbits (serial::stopbits_t stopbits)
+{
+  stopbits_ = stopbits;
+  if (is_open_) {
+    reconfigurePort ();
+  }
+}
+
+serial::stopbits_t
+Serial::SerialImpl::getStopbits () const
+{
+  return stopbits_;
+}
+
+void
+Serial::SerialImpl::setFlowcontrol (serial::flowcontrol_t flowcontrol)
+{
+  flowcontrol_ = flowcontrol;
+  if (is_open_) {
+    reconfigurePort ();
+  }
+}
+
+serial::flowcontrol_t
+Serial::SerialImpl::getFlowcontrol () const
+{
+  return flowcontrol_;
+}
+
+void
+Serial::SerialImpl::flush ()
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::flush");
+  }
+  FlushFileBuffers (fd_);
+}
+
+void
+Serial::SerialImpl::flushInput ()
+{
+  THROW (IOException, "flushInput is not supported on Windows.");
+}
+
+void
+Serial::SerialImpl::flushOutput ()
+{
+  THROW (IOException, "flushOutput is not supported on Windows.");
+}
+
+void
+Serial::SerialImpl::sendBreak (int /*duration*/)
+{
+  THROW (IOException, "sendBreak is not supported on Windows.");
+}
+
+void
+Serial::SerialImpl::setBreak (bool level)
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::setBreak");
+  }
+  if (level) {
+    EscapeCommFunction (fd_, SETBREAK);
+  } else {
+    EscapeCommFunction (fd_, CLRBREAK);
+  }
+}
+
+void
+Serial::SerialImpl::setRTS (bool level)
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::setRTS");
+  }
+  if (level) {
+    EscapeCommFunction (fd_, SETRTS);
+  } else {
+    EscapeCommFunction (fd_, CLRRTS);
+  }
+}
+
+void
+Serial::SerialImpl::setDTR (bool level)
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::setDTR");
+  }
+  if (level) {
+    EscapeCommFunction (fd_, SETDTR);
+  } else {
+    EscapeCommFunction (fd_, CLRDTR);
+  }
+}
+
+bool
+Serial::SerialImpl::waitForChange ()
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::waitForChange");
+  }
+  DWORD dwCommEvent;
+
+  if (!SetCommMask(fd_, EV_CTS | EV_DSR | EV_RING | EV_RLSD)) {
+    // Error setting communications mask
+    return false;
+  }
+
+  if (!WaitCommEvent(fd_, &dwCommEvent, NULL)) {
+    // An error occurred waiting for the event.
+    return false;
+  } else {
+    // Event has occurred.
+    return true;
+  }
+}
+
+bool
+Serial::SerialImpl::getCTS ()
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::getCTS");
+  }
+  DWORD dwModemStatus;
+  if (!GetCommModemStatus(fd_, &dwModemStatus)) {
+    THROW (IOException, "Error getting the status of the CTS line.");
+  }
+
+  return (MS_CTS_ON & dwModemStatus) != 0;
+}
+
+bool
+Serial::SerialImpl::getDSR ()
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::getDSR");
+  }
+  DWORD dwModemStatus;
+  if (!GetCommModemStatus(fd_, &dwModemStatus)) {
+    THROW (IOException, "Error getting the status of the DSR line.");
+  }
+
+  return (MS_DSR_ON & dwModemStatus) != 0;
+}
+
+bool
+Serial::SerialImpl::getRI()
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::getRI");
+  }
+  DWORD dwModemStatus;
+  if (!GetCommModemStatus(fd_, &dwModemStatus)) {
+    THROW (IOException, "Error getting the status of the RI line.");
+  }
+
+  return (MS_RING_ON & dwModemStatus) != 0;
+}
+
+bool
+Serial::SerialImpl::getCD()
+{
+  if (is_open_ == false) {
+    throw PortNotOpenedException ("Serial::getCD");
+  }
+  DWORD dwModemStatus;
+  if (!GetCommModemStatus(fd_, &dwModemStatus)) {
+    // Error in GetCommModemStatus;
+    THROW (IOException, "Error getting the status of the CD line.");
+  }
+
+  return (MS_RLSD_ON & dwModemStatus) != 0;
+}
+
+void
+Serial::SerialImpl::readLock()
+{
+  if (WaitForSingleObject(read_mutex, INFINITE) != WAIT_OBJECT_0) {
+    THROW (IOException, "Error claiming read mutex.");
+  }
+}
+
+void
+Serial::SerialImpl::readUnlock()
+{
+  if (!ReleaseMutex(read_mutex)) {
+    THROW (IOException, "Error releasing read mutex.");
+  }
+}
+
+void
+Serial::SerialImpl::writeLock()
+{
+  if (WaitForSingleObject(write_mutex, INFINITE) != WAIT_OBJECT_0) {
+    THROW (IOException, "Error claiming write mutex.");
+  }
+}
+
+void
+Serial::SerialImpl::writeUnlock()
+{
+  if (!ReleaseMutex(write_mutex)) {
+    THROW (IOException, "Error releasing write mutex.");
+  }
+}
+
+#endif // #if defined(_WIN32)
+

+ 415 - 0
lib/serial/src/serial.cc

@@ -0,0 +1,415 @@
+/* Copyright 2012 William Woodall and John Harrison */
+#include <algorithm>
+
+#if !defined(_WIN32) && !defined(__OpenBSD__) && !defined(__FreeBSD__)
+# include <alloca.h>
+#endif
+
+#if defined (__MINGW32__)
+# define alloca __builtin_alloca
+#endif
+
+
+#ifdef _WIN32
+#include "include/serial.h"
+#include "include/impl/win.h"
+#else
+#include "../include/serial.h"
+#include "../include/impl/unix.h"
+#endif
+
+using std::invalid_argument;
+using std::min;
+using std::numeric_limits;
+using std::vector;
+using std::size_t;
+using std::string;
+
+using serial::Serial;
+using serial::SerialException;
+using serial::IOException;
+using serial::bytesize_t;
+using serial::parity_t;
+using serial::stopbits_t;
+using serial::flowcontrol_t;
+
+class Serial::ScopedReadLock {
+public:
+    ScopedReadLock(SerialImpl *pimpl) : pimpl_(pimpl) {
+      this->pimpl_->readLock();
+    }
+    ~ScopedReadLock() {
+      this->pimpl_->readUnlock();
+    }
+private:
+    // Disable copy constructors
+    ScopedReadLock(const ScopedReadLock&);
+    const ScopedReadLock& operator=(ScopedReadLock);
+
+    SerialImpl *pimpl_;
+};
+
+class Serial::ScopedWriteLock {
+public:
+    ScopedWriteLock(SerialImpl *pimpl) : pimpl_(pimpl) {
+      this->pimpl_->writeLock();
+    }
+    ~ScopedWriteLock() {
+      this->pimpl_->writeUnlock();
+    }
+private:
+    // Disable copy constructors
+    ScopedWriteLock(const ScopedWriteLock&);
+    const ScopedWriteLock& operator=(ScopedWriteLock);
+    SerialImpl *pimpl_;
+};
+
+Serial::Serial (const string &port, uint32_t baudrate, serial::Timeout timeout,
+                bytesize_t bytesize, parity_t parity, stopbits_t stopbits,
+                flowcontrol_t flowcontrol)
+        : pimpl_(new SerialImpl (port, baudrate, bytesize, parity,
+                                 stopbits, flowcontrol))
+{
+  pimpl_->setTimeout(timeout);
+}
+
+Serial::~Serial ()
+{
+  delete pimpl_;
+}
+
+void
+Serial::open ()
+{
+  pimpl_->open ();
+}
+
+void
+Serial::close ()
+{
+  pimpl_->close ();
+}
+
+bool
+Serial::isOpen () const
+{
+  return pimpl_->isOpen ();
+}
+
+size_t
+Serial::available ()
+{
+  return pimpl_->available ();
+}
+
+bool
+Serial::waitReadable ()
+{
+  serial::Timeout timeout(pimpl_->getTimeout ());
+  return pimpl_->waitReadable(timeout.read_timeout_constant);
+}
+
+void
+Serial::waitByteTimes (size_t count)
+{
+  pimpl_->waitByteTimes(count);
+}
+
+size_t
+Serial::read_ (uint8_t *buffer, size_t size)
+{
+  return this->pimpl_->read (buffer, size);
+}
+
+size_t
+Serial::read (uint8_t *buffer, size_t size)
+{
+  ScopedReadLock lock(this->pimpl_);
+  return this->pimpl_->read (buffer, size);
+}
+
+size_t
+Serial::read (std::vector<uint8_t> &buffer, size_t size)
+{
+  ScopedReadLock lock(this->pimpl_);
+  uint8_t *buffer_ = new uint8_t[size];
+  size_t bytes_read = this->pimpl_->read (buffer_, size);
+  buffer.insert (buffer.end (), buffer_, buffer_+bytes_read);
+  delete[] buffer_;
+  return bytes_read;
+}
+
+size_t
+Serial::read (std::string &buffer, size_t size)
+{
+  ScopedReadLock lock(this->pimpl_);
+  uint8_t *buffer_ = new uint8_t[size];
+  size_t bytes_read = this->pimpl_->read (buffer_, size);
+  buffer.append (reinterpret_cast<const char*>(buffer_), bytes_read);
+  delete[] buffer_;
+  return bytes_read;
+}
+
+string
+Serial::read (size_t size)
+{
+  std::string buffer;
+  this->read (buffer, size);
+  return buffer;
+}
+
+size_t
+Serial::readline (string &buffer, size_t size, string eol)
+{
+  ScopedReadLock lock(this->pimpl_);
+  size_t eol_len = eol.length ();
+  uint8_t *buffer_ = static_cast<uint8_t*>
+  (alloca (size * sizeof (uint8_t)));
+  size_t read_so_far = 0;
+  while (true)
+  {
+    size_t bytes_read = this->read_ (buffer_ + read_so_far, 1);
+    read_so_far += bytes_read;
+    if (bytes_read == 0) {
+      break; // Timeout occured on reading 1 byte
+    }
+    if (string (reinterpret_cast<const char*>
+                (buffer_ + read_so_far - eol_len), eol_len) == eol) {
+      break; // EOL found
+    }
+    if (read_so_far == size) {
+      break; // Reached the maximum read length
+    }
+  }
+  buffer.append(reinterpret_cast<const char*> (buffer_), read_so_far);
+  return read_so_far;
+}
+
+string
+Serial::readline (size_t size, string eol)
+{
+  std::string buffer;
+  this->readline (buffer, size, eol);
+  return buffer;
+}
+
+vector<string>
+Serial::readlines (size_t size, string eol)
+{
+  ScopedReadLock lock(this->pimpl_);
+  std::vector<std::string> lines;
+  size_t eol_len = eol.length ();
+  uint8_t *buffer_ = static_cast<uint8_t*>
+  (alloca (size * sizeof (uint8_t)));
+  size_t read_so_far = 0;
+  size_t start_of_line = 0;
+  while (read_so_far < size) {
+    size_t bytes_read = this->read_ (buffer_+read_so_far, 1);
+    read_so_far += bytes_read;
+    if (bytes_read == 0) {
+      if (start_of_line != read_so_far) {
+        lines.push_back (
+                string (reinterpret_cast<const char*> (buffer_ + start_of_line),
+                        read_so_far - start_of_line));
+      }
+      break; // Timeout occured on reading 1 byte
+    }
+    if (string (reinterpret_cast<const char*>
+                (buffer_ + read_so_far - eol_len), eol_len) == eol) {
+      // EOL found
+      lines.push_back(
+              string(reinterpret_cast<const char*> (buffer_ + start_of_line),
+                     read_so_far - start_of_line));
+      start_of_line = read_so_far;
+    }
+    if (read_so_far == size) {
+      if (start_of_line != read_so_far) {
+        lines.push_back(
+                string(reinterpret_cast<const char*> (buffer_ + start_of_line),
+                       read_so_far - start_of_line));
+      }
+      break; // Reached the maximum read length
+    }
+  }
+  return lines;
+}
+
+size_t
+Serial::write (const string &data)
+{
+  ScopedWriteLock lock(this->pimpl_);
+  return this->write_ (reinterpret_cast<const uint8_t*>(data.c_str()),
+                       data.length());
+}
+
+size_t
+Serial::write (const std::vector<uint8_t> &data)
+{
+  ScopedWriteLock lock(this->pimpl_);
+  return this->write_ (&data[0], data.size());
+}
+
+size_t
+Serial::write (const uint8_t *data, size_t size)
+{
+  ScopedWriteLock lock(this->pimpl_);
+  return this->write_(data, size);
+}
+
+size_t
+Serial::write_ (const uint8_t *data, size_t length)
+{
+  return pimpl_->write (data, length);
+}
+
+void
+Serial::setPort (const string &port)
+{
+  ScopedReadLock rlock(this->pimpl_);
+  ScopedWriteLock wlock(this->pimpl_);
+  bool was_open = pimpl_->isOpen ();
+  if (was_open) close();
+  pimpl_->setPort (port);
+  if (was_open) open ();
+}
+
+string
+Serial::getPort () const
+{
+  return pimpl_->getPort ();
+}
+
+void
+Serial::setTimeout (serial::Timeout &timeout)
+{
+  pimpl_->setTimeout (timeout);
+}
+
+serial::Timeout
+Serial::getTimeout () const {
+  return pimpl_->getTimeout ();
+}
+
+void
+Serial::setBaudrate (uint32_t baudrate)
+{
+  pimpl_->setBaudrate (baudrate);
+}
+
+uint32_t
+Serial::getBaudrate () const
+{
+  return uint32_t(pimpl_->getBaudrate ());
+}
+
+void
+Serial::setBytesize (bytesize_t bytesize)
+{
+  pimpl_->setBytesize (bytesize);
+}
+
+bytesize_t
+Serial::getBytesize () const
+{
+  return pimpl_->getBytesize ();
+}
+
+void
+Serial::setParity (parity_t parity)
+{
+  pimpl_->setParity (parity);
+}
+
+parity_t
+Serial::getParity () const
+{
+  return pimpl_->getParity ();
+}
+
+void
+Serial::setStopbits (stopbits_t stopbits)
+{
+  pimpl_->setStopbits (stopbits);
+}
+
+stopbits_t
+Serial::getStopbits () const
+{
+  return pimpl_->getStopbits ();
+}
+
+void
+Serial::setFlowcontrol (flowcontrol_t flowcontrol)
+{
+  pimpl_->setFlowcontrol (flowcontrol);
+}
+
+flowcontrol_t
+Serial::getFlowcontrol () const
+{
+  return pimpl_->getFlowcontrol ();
+}
+
+void Serial::flush ()
+{
+  ScopedReadLock rlock(this->pimpl_);
+  ScopedWriteLock wlock(this->pimpl_);
+  pimpl_->flush ();
+}
+
+void Serial::flushInput ()
+{
+  ScopedReadLock lock(this->pimpl_);
+  pimpl_->flushInput ();
+}
+
+void Serial::flushOutput ()
+{
+  ScopedWriteLock lock(this->pimpl_);
+  pimpl_->flushOutput ();
+}
+
+void Serial::sendBreak (int duration)
+{
+  pimpl_->sendBreak (duration);
+}
+
+void Serial::setBreak (bool level)
+{
+  pimpl_->setBreak (level);
+}
+
+void Serial::setRTS (bool level)
+{
+  pimpl_->setRTS (level);
+}
+
+void Serial::setDTR (bool level)
+{
+  pimpl_->setDTR (level);
+}
+
+bool Serial::waitForChange()
+{
+  return pimpl_->waitForChange();
+}
+
+bool Serial::getCTS ()
+{
+  return pimpl_->getCTS ();
+}
+
+bool Serial::getDSR ()
+{
+  return pimpl_->getDSR ();
+}
+
+bool Serial::getRI ()
+{
+  return pimpl_->getRI ();
+}
+
+bool Serial::getCD ()
+{
+  return pimpl_->getCD ();
+}