1 /*
2  * CRRCsim - the Charles River Radio Control Club Flight Simulator Project
3  *   Copyright (C) 2008 - Martin Herrmann (original author)
4  *   Copyright (C) 2008, 2009 - Jan Reucker
5  *   Copyright (C) 2008 - Jens Wilhelm Wulf
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2
9  * as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330,
19  * Boston, MA 02111-1307, USA.
20  *
21  */
22 #include <crrc_config.h>
23 #include <cstdio>
24 #include <plib/ul.h>
25 
26 #include "inputdev_serial.h"
27 #include "../../mod_misc/lib_conversions.h"
28 #include "../../global.h"
29 #include <sstream>
30 //#include "../../mod_chardevice/chardevicebase.h"
31 
32 #ifdef WIN32
33 #include <tchar.h>
34 #endif
35 
36 // Remaining problems:
37 //   - crash when non-existent port is opened
38 //   - connect USB, choose USB, save, quit, disconnect USB, start ==> crash?
39 
40 // TODO Kalibrierproblem
41 
42 // TODO 110 Baud eingestellt nach Fehler
43 
44 #ifdef WIN32
45 #define DEFAULT_PORT_NAME "COM1"
46 #elif defined (__FreeBSD__)
47 #define DEFAULT_PORT_NAME "/dev/cuad0"
48 #else
49 #define DEFAULT_PORT_NAME "/dev/ttyS0"
50 #endif
51 
52 #if DEBUG_TX_INTERFACE > 0
53 #define DEBUG(...) printf (__VA_ARGS__)
54 #else
55 #define DEBUG()
56 #endif
57 
58 const int T_TX_InterfaceSerial::nNumBaudrates = 11;
59 const char *T_TX_InterfaceSerial::baudRates[] =
60   {    "110",  "300",   "600",  "1200",  "2400",
61        "4800", "9600", "19200", "38400", "57600",
62        "115200",   NULL
63   };
64 const int T_TX_InterfaceSerial::anBaudRates[] =
65   {      110,    300,     600,    1200,    2400,
66          4800,   9600,   19200,   38400,   57600,
67          115200,     -1
68   };
69 
70 
71 // ******************
72 // ** Construction **
73 // ******************
74 
T_TX_InterfaceSerial()75 T_TX_InterfaceSerial::T_TX_InterfaceSerial ()/*{{{*/
76     :charDevice (NULL), portName (""), baudRate (0)
77 {
78   DEBUG ("T_TX_InterfaceSerial::T_TX_InterfaceSerial ()\n");
79 
80   mixer=new T_TX_Mixer (this);
81   map=new T_AxisMapper (this);
82   calib=new T_Calibration (this);
83 
84 #if DEBUG_TX_INTERFACE>0
85   dbgbufferidx=0;
86 #endif
87 }
88 /*}}}*/
89 
~T_TX_InterfaceSerial()90 T_TX_InterfaceSerial::~T_TX_InterfaceSerial ()/*{{{*/
91 {
92   DEBUG("T_TX_InterfaceSerial::~T_TX_InterfaceSerial()\n");
93 
94   delete mixer;
95   delete map;
96   delete calib;
97   delete charDevice;
98 }
99 /*}}}*/
100 
101 // *******************
102 // ** Configuration **
103 // *******************
104 
init(SimpleXMLTransfer * config)105 int T_TX_InterfaceSerial::init (SimpleXMLTransfer *config)/*{{{*/
106 {
107   DEBUG ("int T_TX_InterfaceSerial::init (SimpleXMLTransfer *config)\n");
108 
109   // Initialize the port settings
110   SimpleXMLTransfer *port=config->getChild (getXmlChildName ()+".port", true);
111   portName = port->getString ("name", DEFAULT_PORT_NAME);
112   baudRate = port->getInt ("baudrate", getDefaultBaudRate ());
113   // Initialize the subsystems
114   if (mixer->init (config, getXmlChildName ())!=0)
115   {
116     setErrMsg("Mixer initialization failed.");
117     return 1;
118   }
119   calib->init (config, getXmlChildName ());
120   map->init (config, getXmlChildName ());
121 
122   try
123   {
124     // Try-Methode?
125     ostringstream options;
126     options << portName << "," << baudRate;
127     string optionString=options.str ();
128     cout << "Opening the serial port with option string " << optionString << endl;
129     charDevice=new SerialCharDevice (optionString.c_str (), false);
130   }
131   catch (CharDevice::ConfigureDeviceException e)
132   {
133     setErrMsg ("The device could not be configured.");
134     cerr << "Serial interface initialization: " << getErrMsg () << endl;
135     return 1;
136   }
137 
138   cout << "Serial interface initialization: OK" << endl;
139   return 0;
140 }
141 /*}}}*/
142 
putBackIntoCfg(SimpleXMLTransfer * config)143 void T_TX_InterfaceSerial::putBackIntoCfg (SimpleXMLTransfer *config)/*{{{*/
144 {
145   DEBUG("int T_TX_InterfaceSerial::putBackIntoCfg (SimpleXMLTransfer *config)\n");
146 
147   // Store the port settings
148   SimpleXMLTransfer *port=config->getChild (getXmlChildName ()+".port", true);
149   port->setAttributeOverwrite ("name", portName);
150   port->setAttributeOverwrite ("baudrate", baudRate);
151 
152   // Store the subsystems
153   if (mixer) mixer->putBackIntoCfg (config);
154   if (map)   map  ->putBackIntoCfg (config);
155   if (calib) calib->putBackIntoCfg (config);
156 }
157 /*}}}*/
158 
getDeviceName()159 std::string T_TX_InterfaceSerial::getDeviceName ()/*{{{*/
160 {
161   return portName;
162 }
163 /*}}}*/
164 
setDeviceSpeed(int speed)165 void T_TX_InterfaceSerial::setDeviceSpeed (int speed)/*{{{*/
166 {
167   if ((speed >= 0) && (speed < nNumBaudrates))
168   {
169     baudRate = anBaudRates[speed];
170   }
171   else
172   {
173     baudRate = getDefaultBaudRate();
174   }
175 }
176 /*}}}*/
177 
baudRateToSpeed(int br)178 int T_TX_InterfaceSerial::baudRateToSpeed (int br)/*{{{*/
179 {
180   int nSpeed;
181 
182   switch (br)
183   {
184   case 110:
185     nSpeed = 0;
186     break;
187 
188   case 300:
189     nSpeed = 1;
190     break;
191 
192   case 600:
193     nSpeed = 2;
194     break;
195 
196   case 1200:
197     nSpeed = 3;
198     break;
199 
200   case 2400:
201     nSpeed = 4;
202     break;
203 
204   case 4800:
205     nSpeed = 5;
206     break;
207 
208   case 9600:
209     nSpeed = 6;
210     break;
211 
212   case 19200:
213     nSpeed = 7;
214     break;
215 
216   case 38400:
217     nSpeed = 8;
218     break;
219 
220   case 57600:
221     nSpeed = 9;
222     break;
223 
224   case 115200:
225     nSpeed = 10;
226     break;
227 
228   default:
229     nSpeed = -1;
230     break;
231   }
232 
233   return nSpeed;
234 }
235 /*}}}*/
236 
getDeviceSpeed()237 int T_TX_InterfaceSerial::getDeviceSpeed ()/*{{{*/
238 {
239   int speed = baudRateToSpeed (baudRate);
240   if (speed < 0)
241     speed = baudRateToSpeed (getDefaultBaudRate ());
242   return speed;
243 }
244 /*}}}*/
245 
setDeviceName(std::string devname)246 void T_TX_InterfaceSerial::setDeviceName (std::string devname)/*{{{*/
247 {
248   portName = devname;
249 }
250 /*}}}*/
251 
setDtr(bool dtr)252 void T_TX_InterfaceSerial::setDtr (bool dtr)/*{{{*/
253 {
254   if (dtr)
255     charDevice->write_output_pins (SerialCharDevice::WriteOutputPinUnchanged, SerialCharDevice::WriteOutputPinHigh); // RTS, DTR
256   else
257     charDevice->write_output_pins (SerialCharDevice::WriteOutputPinUnchanged, SerialCharDevice::WriteOutputPinLow); // RTS, DTR
258 }
259 /*}}}*/
260 
setRts(bool rts)261 void T_TX_InterfaceSerial::setRts (bool rts)/*{{{*/
262 {
263   if (rts)
264     charDevice->write_output_pins (SerialCharDevice::WriteOutputPinHigh, SerialCharDevice::WriteOutputPinUnchanged); // RTS, DTR
265   else
266     charDevice->write_output_pins (SerialCharDevice::WriteOutputPinLow, SerialCharDevice::WriteOutputPinUnchanged); // RTS, DTR
267 }
268 /*}}}*/
269 
270 
271 // *****************
272 // ** Data access **
273 // *****************
274 
getInputData(TSimInputs * inputs)275 void T_TX_InterfaceSerial::getInputData (TSimInputs *inputs)/*{{{*/
276 {
277   // Read serial data and update rawData[]
278   readSerialData ();
279 
280   CalibMixMapValues(inputs, rawData);
281 }
282 /*}}}*/
283 
getRawData(float * dest)284 void T_TX_InterfaceSerial::getRawData (float *dest)/*{{{*/
285 {
286   // Read serial data and update rawData[]
287   readSerialData ();
288 
289   int numAxes=getNumAxes ();
290   if (numAxes > TX_MAXAXIS)
291   {
292     numAxes = TX_MAXAXIS;
293   }
294   for (int i = 0; i < numAxes; ++i)
295   {
296     *(dest + i) = rawData[i];
297   }
298 }
299 /*}}}*/
300 
301 
302 // *********
303 // ** I/O **
304 // *********
305 
306 /**
307  * Reads one byte from the serial interface. Only this method should be used
308  * for reading serial data.
309  * \return -1 if no more bytes available, the byte else
310  */
getSerialByte()311 int T_TX_InterfaceSerial::getSerialByte ()/*{{{*/
312 {
313   unsigned char buffer;
314 
315   if (charDevice->read (&buffer, 1)<1)
316     return -1;
317 
318 #if DEBUG_TX_INTERFACE > 0
319   debugHandler(buffer);
320 #endif
321 
322   return buffer;
323 }
324 /*}}}*/
325 
readSerialData()326 void T_TX_InterfaceSerial::readSerialData ()/*{{{*/
327 {
328   while (true)
329   {
330     int data=getSerialByte ();
331     if (data<0) return;
332 
333     processDataByte (data);
334   }
335 }
336 ///*}}}*/
337 
setRawData(int num,float data)338 void T_TX_InterfaceSerial::setRawData (int num, float data)/*{{{*/
339 {
340 // DEBUG ("setting %d to %f\n", num, data);
341 
342   if (num>=0 && num<TX_MAXAXIS)
343     rawData[num]=data;
344 }
345 /*}}}*/
346 
347 
348 #if DEBUG_TX_INTERFACE > 0
349 /**
350  *  Generate some interesting debugging output.
351  *
352  *  Dump the byte stream received from the serial port to stdout.
353  *  If DEBUG_TX_INTERFACE is set to 1, DBGBUFFERSIZE bytes are
354  *  recorded once at startup and then dumped to stdout. If
355  *  DEBUG_TX_INTERFACE is set to 2, the byte stream is continously
356  *  dumped to stdout in chunks of DBGBUFFERSIZE bytes.
357  *
358  *  \param ucByte   the byte to be logged
359  */
debugHandler(unsigned char ucByte)360 void T_TX_InterfaceSerial::debugHandler(unsigned char ucByte)
361 {
362   if (dbgbufferidx < DBGBUFFERSIZE)
363   {
364     dbgbuffer[dbgbufferidx++] = ucByte;
365   }
366   else if (dbgbufferidx == DBGBUFFERSIZE)
367   {
368 #if DEBUG_TX_INTERFACE == 1
369     dbgbufferidx++;
370 #elif DEBUG_TX_INTERFACE == 2
371     dbgbufferidx = 0;
372 #endif
373     for (int i = 0; i < DBGBUFFERSIZE; i++)
374     {
375       if (i % 32 == 0)
376       {
377         printf("\n");
378       }
379       printf("%02X ", dbgbuffer[i]);
380     }
381     printf("\n");
382   }
383 }
384 #endif // DEBUG_TX_INTERFACE > 0
385 
386 /**
387  * \brief Generate a list of serial ports
388  *
389  * This method generates a list of serial ports. Depending on the
390  * OS, this can be done by querying the registry (on Windows) or
391  * looking at the /dev hierarchy (Linux, MacOSX).
392  *
393  * \param   SerialPorts   Vector to be filled with the list of port names
394  * \return  number of ports in the list (equivalent to SerialPorts.size())
395  */
getDeviceList(std::vector<std::string> & SerialPorts)396 int T_TX_InterfaceSerial::getDeviceList(std::vector<std::string>& SerialPorts)
397 {
398   // clear any possible previous content of SerialPorts
399   SerialPorts.erase(SerialPorts.begin(), SerialPorts.end());
400 
401 #ifdef WIN32
402   // --- Windows implementation -----------------------------------------------
403   TCHAR acValue[MAX_PATH];
404   BYTE  abData[MAX_PATH];
405 
406   HKEY  hKey        = NULL;
407   DWORD dwDataType  = 0;
408   DWORD dwIndex     = 0;
409 
410 
411   // open the registry key containing all active serial ports
412   LONG lRet = ::RegOpenKeyEx( HKEY_LOCAL_MACHINE,
413                               _T("HARDWARE\\DEVICEMAP\\SERIALCOMM"),
414                               0,
415                               KEY_READ,
416                               &hKey);
417   if(ERROR_SUCCESS != lRet)
418   {
419     // Error handling
420     std::cerr << "Unable to open registry key HKEY_LOCAL_MACHINE\\HARDWARE\\DEVICEMAP\\SERIALCOMM" << std::endl;
421   }
422   else
423   {
424     // Get a list of all subkeys
425     do
426     {
427       DWORD dwValueSize = MAX_PATH;
428       DWORD dwDataSize = MAX_PATH;
429       lRet = ::RegEnumValue(hKey,
430                             dwIndex,
431                             acValue,
432                             &dwValueSize,
433                             NULL,
434                             &dwDataType,
435                             abData,
436                             &dwDataSize);
437       if ((lRet == ERROR_SUCCESS) && (dwDataType == REG_SZ))
438       {
439         SerialPorts.push_back(std::string((char *)abData));
440       }
441       dwIndex++;
442     } while (lRet == ERROR_SUCCESS);
443     ::RegCloseKey(hKey);
444   }
445 #else
446   // --- implementation for other OSes ----------------------------------------
447   #ifdef OLD_WAY
448 #if defined (__FreeBSD__)
449   const char* serialDevs[] = {"/dev/cuad0", "/dev/cuad1",
450                               "/dev/cuad2", "/dev/cuad3",
451                               "/dev/cuaU0", "/dev/cuaU1",
452                               "/dev/cuaU2", "/dev/cuaU3",
453                               NULL};
454 #else
455   const char* serialDevs[] = {"/dev/ttyS0", "/dev/ttyS1",
456                               "/dev/ttyS2", "/dev/ttyS3",
457                               "/dev/ttyUSB0", "/dev/ttyUSB1",
458                               "/dev/ttyUSB2", "/dev/ttyUSB3",
459                               NULL};
460 #endif
461   const char** ptr;
462 
463   for (ptr = serialDevs; *ptr != NULL; ptr++)
464   {
465     SerialPorts.push_back(std::string(*ptr));
466   }
467   #else
468   // look for some well-known names in /dev/
469   std::vector<std::string> serialDevs;
470   std::string sPath = "/dev";
471 #if defined (__FreeBSD__)
472   serialDevs.push_back("cuad");
473   serialDevs.push_back("cuaU");
474 #else
475   serialDevs.push_back("ttyS");
476   serialDevs.push_back("ttyUSB");
477 #endif
478 
479   ulDir *dir = ulOpenDir(sPath.c_str());
480   if (dir != NULL)
481   {
482     ulDirEnt *entry;
483 
484     do
485     {
486       entry = ulReadDir(dir);
487       if (entry != NULL)
488       {
489         if (!entry->d_isdir)
490         {
491           std::vector<std::string>::iterator it;
492           std::string dev = entry->d_name;
493           for (it = serialDevs.begin(); it != serialDevs.end(); it++)
494           {
495             if (0 == dev.compare(0, (*it).size(), *it))
496             {
497               SerialPorts.push_back(sPath + '/' + dev);
498               break;
499             }
500           }
501         }
502       }
503     } while (entry != NULL);
504 
505     ulCloseDir(dir);
506   }
507 
508   #endif
509 #endif
510 
511   return SerialPorts.size();
512 }
513 
514 
515