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