libcm is a C development framework with an emphasis on audio signal processing applications.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

cmSerialPort.c 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmRpt.h"
  4. #include "cmErr.h"
  5. #include "cmCtx.h"
  6. #include "cmMem.h"
  7. #include "cmMallocDebug.h"
  8. #include "cmSerialPort.h"
  9. #include "cmThread.h"
  10. #include <poll.h>
  11. #include <termios.h>
  12. #include <unistd.h> // close()
  13. #include <fcntl.h> // O_RDWR
  14. #include <sys/ioctl.h> // TIOCEXCL
  15. typedef struct cmSerialPort_str
  16. {
  17. cmErr_t _err;
  18. cmThreadH_t _thH;
  19. const char* _deviceStr;
  20. int _deviceH;
  21. unsigned _baudRate;
  22. unsigned _cfgFlags;
  23. cmSeCallbackFunc_t _cbFunc;
  24. void* _cbArg;
  25. struct termios _ttyAttrs;
  26. struct pollfd _pollfd;
  27. unsigned _pollPeriodMs;
  28. } cmSerialPort_t;
  29. cmSerialPort_t* _cmSePtrFromHandle( cmSeH_t h )
  30. {
  31. cmSerialPort_t* p = (cmSerialPort_t*)h.h;
  32. assert(p!=NULL);
  33. return p;
  34. }
  35. void _cmSeSetClosedState( cmSerialPort_t* p )
  36. {
  37. if( p->_deviceStr != NULL )
  38. cmMemFree((char*)(p->_deviceStr));
  39. p->_deviceH = -1;
  40. p->_deviceStr = NULL;
  41. p->_baudRate = 0;
  42. p->_cfgFlags = 0;
  43. p->_cbFunc = NULL;
  44. p->_cbArg = NULL;
  45. }
  46. cmSeRC_t _cmSeGetAttributes( cmSerialPort_t* p, struct termios* attr )
  47. {
  48. if( tcgetattr(p->_deviceH, attr) == -1 )
  49. return cmErrSysMsg(&p->_err,kGetAttrFailSeRC,errno,"Error getting tty attributes from %s.",p->_deviceStr);
  50. return kOkSeRC;
  51. }
  52. cmSeRC_t _cmSePoll( cmSerialPort_t* p, unsigned timeOutMs )
  53. {
  54. cmSeRC_t rc = kOkSeRC;
  55. int sysRC;
  56. if((sysRC = poll(&p->_pollfd,1,timeOutMs)) == 0)
  57. rc = kTimeOutSeRC;
  58. else
  59. {
  60. if( sysRC < 0 )
  61. rc = cmErrSysMsg(&p->_err,kReadFailSeRC,errno,"Poll failed on serial port.");
  62. }
  63. return rc;
  64. }
  65. bool _cmSeThreadFunc(void* param)
  66. {
  67. cmSerialPort_t* p = (cmSerialPort_t*)param;
  68. cmSeH_t h;
  69. h.h = p;
  70. unsigned readN;
  71. if( cmSeIsOpen(h) )
  72. cmSeReceiveCbTimeOut(h,p->_pollPeriodMs,&readN);
  73. return true;
  74. }
  75. cmSeRC_t _cmSeDestroy( cmSerialPort_t* p )
  76. {
  77. cmSeRC_t rc = kOkSeRC;
  78. // stop the thread first
  79. if( cmThreadDestroy(&p->_thH) != kOkThRC )
  80. {
  81. rc = cmErrMsg(&p->_err,kThreadErrSeRC,"Thread destroy failed.");
  82. goto errLabel;
  83. }
  84. // Block until all written output has been sent from the device.
  85. // Note that this call is simply passed on to the serial device driver.
  86. // See tcsendbreak(3) ("man 3 tcsendbreak") for details.
  87. if (tcdrain(p->_deviceH) == -1)
  88. {
  89. rc = cmErrSysMsg(&p->_err,kFlushFailSeRC,errno,"Error waiting for serial device '%s' to drain.", p->_deviceStr );
  90. goto errLabel;
  91. }
  92. // It is good practice to reset a serial port back to the state in
  93. // which you found it. This is why we saved the original termios struct
  94. // The constant TCSANOW (defined in termios.h) indicates that
  95. // the change should take effect immediately.
  96. if (tcsetattr(p->_deviceH, TCSANOW, &p->_ttyAttrs) == -1)
  97. {
  98. rc = cmErrSysMsg(&p->_err,kSetAttrFailSeRC,errno,"Error resetting tty attributes on serial device '%s'.",p->_deviceStr);
  99. goto errLabel;
  100. }
  101. if( p->_deviceH != -1 )
  102. {
  103. if( close(p->_deviceH ) != 0 )
  104. {
  105. rc = cmErrSysMsg(&p->_err,kCloseFailSeRC,errno,"Port close failed on serial dvice '%s'.", p->_deviceStr);
  106. goto errLabel;
  107. }
  108. _cmSeSetClosedState(p);
  109. }
  110. cmMemPtrFree(&p);
  111. errLabel:
  112. return rc;
  113. }
  114. cmSeH_t cmSeCreate( cmCtx_t* ctx, cmSeH_t* hp, const char* deviceStr, unsigned baudRate, unsigned cfgFlags, cmSeCallbackFunc_t cbFunc, void* cbArg, unsigned pollPeriodMs )
  115. {
  116. cmSeRC_t rc = kOkSeRC;
  117. struct termios options;
  118. cmSeH_t h;
  119. // if the port is already open then close it
  120. if((rc = cmSeDestroy(hp)) != kOkSeRC )
  121. return *hp;
  122. cmSerialPort_t* p = cmMemAllocZ(cmSerialPort_t,1);
  123. cmErrSetup(&p->_err,&ctx->rpt,"Serial Port");
  124. p->_deviceH = -1;
  125. // open the port
  126. if( (p->_deviceH = open(deviceStr, O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1 )
  127. {
  128. rc = cmErrSysMsg(&p->_err,kOpenFailSeRC,errno,"Error opening serial '%s'",cmStringNullGuard(deviceStr));
  129. goto errLabel;;
  130. }
  131. // Note that open() follows POSIX semantics: multiple open() calls to
  132. // the same file will succeed unless the TIOCEXCL ioctl is issued.
  133. // This will prevent additional opens except by root-owned processes.
  134. // See tty(4) ("man 4 tty") and ioctl(2) ("man 2 ioctl") for details.
  135. if( ioctl(p->_deviceH, TIOCEXCL) == -1 )
  136. {
  137. rc = cmErrSysMsg(&p->_err,kResourceNotAvailableSeRC,errno,"The serial device '%s' is already in use.", cmStringNullGuard(deviceStr));
  138. goto errLabel;
  139. }
  140. // Now that the device is open, clear the O_NONBLOCK flag so
  141. // subsequent I/O will block.
  142. // See fcntl(2) ("man 2 fcntl") for details.
  143. /*
  144. if (fcntl(_deviceH, F_SETFL, 0) == -1)
  145. {
  146. _error("Error clearing O_NONBLOCK %s - %s(%d).", pr.devFilePath.c_str(), strerror(errno), errno);
  147. goto errLabel;
  148. }
  149. */
  150. // Get the current options and save them so we can restore the
  151. // default settings later.
  152. if (tcgetattr(p->_deviceH, &p->_ttyAttrs) == -1)
  153. {
  154. rc = cmErrSysMsg(&p->_err,kGetAttrFailSeRC,errno,"Error getting tty attributes from the device '%s'.",deviceStr);
  155. goto errLabel;
  156. }
  157. // The serial port attributes such as timeouts and baud rate are set by
  158. // modifying the termios structure and then calling tcsetattr to
  159. // cause the changes to take effect. Note that the
  160. // changes will not take effect without the tcsetattr() call.
  161. // See tcsetattr(4) ("man 4 tcsetattr") for details.
  162. options = p->_ttyAttrs;
  163. // Set raw input (non-canonical) mode, with reads blocking until either
  164. // a single character has been received or a 100ms timeout expires.
  165. // See tcsetattr(4) ("man 4 tcsetattr") and termios(4) ("man 4 termios")
  166. // for details.
  167. cfmakeraw(&options);
  168. options.c_cc[VMIN] = 1;
  169. options.c_cc[VTIME] = 1;
  170. // The baud rate, word length, and handshake options can be set as follows:
  171. // set baud rate
  172. cfsetspeed(&options, baudRate);
  173. options.c_cflag |= CREAD | CLOCAL; // ignore modem controls
  174. // set data word size
  175. cmClrBits(options.c_cflag, CSIZE); // clear the word size bits
  176. cmEnaBits(options.c_cflag, CS5, cmIsFlag(cfgFlags, kDataBits5SeFl));
  177. cmEnaBits(options.c_cflag, CS6, cmIsFlag(cfgFlags, kDataBits6SeFl));
  178. cmEnaBits(options.c_cflag, CS7, cmIsFlag(cfgFlags, kDataBits7SeFl));
  179. cmEnaBits(options.c_cflag, CS8, cmIsFlag(cfgFlags, kDataBits8SeFl));
  180. cmClrBits(options.c_cflag, PARENB); // assume no-parity
  181. // if the odd or even parity flag is set
  182. if( cmIsFlag( cfgFlags, kEvenParitySeFl) || cmIsFlag( cfgFlags, kOddParitySeFl ) )
  183. {
  184. cmSetBits(options.c_cflag, PARENB);
  185. if( cmIsFlag(cfgFlags, kOddParitySeFl ) )
  186. cmSetBits( options.c_cflag, PARODD);
  187. }
  188. // set two stop bits
  189. cmEnaBits( options.c_cflag, CSTOPB, cmIsFlag(cfgFlags, k2StopBitSeFl));
  190. // set hardware flow control
  191. //cmEnaBits(options.c_cflag, CCTS_OFLOW, cmIsFlag(cfgFlags, kCTS_OutFlowCtlFl));
  192. //cmEnaBits(options.c_cflag, CRTS_IFLOW, cmIsFlag(cfgFlags, kRTS_InFlowCtlFl));
  193. //cmEnaBits(options.c_cflag, CDTR_IFLOW, cmIsFlag(cfgFlags, kDTR_InFlowCtlFl));
  194. //cmEnaBits(options.c_cflag, CDSR_OFLOW, cmIsFlag(cfgFlags, kDSR_OutFlowCtlFl));
  195. //cmEnaBits(options.c_cflag, CCAR_OFLOW, cmIsFlag(cfgFlags, kDCD_OutFlowCtlFl));
  196. cmClrBits(options.c_cflag,CRTSCTS); // turn-off hardware flow control
  197. // 7 bit words, enable even parity, CTS out ctl flow, RTS in ctl flow
  198. // note: set PARODD and PARENB to enable odd parity)
  199. //options.c_cflag |= (CS7 | PARENB | CCTS_OFLOW | CRTS_IFLOW );
  200. // Cause the new options to take effect immediately.
  201. if (tcsetattr(p->_deviceH, TCSANOW, &options) == -1)
  202. {
  203. rc = cmErrSysMsg(&p->_err,kSetAttrFailSeRC,errno,"Error setting tty attributes on serial device %.", deviceStr);
  204. goto errLabel;
  205. }
  206. memset(&p->_pollfd,0,sizeof(p->_pollfd));
  207. p->_pollfd.fd = p->_deviceH;
  208. p->_pollfd.events = POLLIN;
  209. p->_deviceStr = cmMemAllocStr( deviceStr );
  210. p->_baudRate = baudRate;
  211. p->_cfgFlags = cfgFlags;
  212. p->_cbFunc = cbFunc;
  213. p->_cbArg = cbArg;
  214. p->_pollPeriodMs = pollPeriodMs;
  215. // create the listening thread
  216. if( cmThreadCreate( &p->_thH, _cmSeThreadFunc, p, &ctx->rpt) != kOkThRC )
  217. {
  218. rc = cmErrMsg(&p->_err,kThreadErrSeRC,"Thread initialization failed.");
  219. goto errLabel;
  220. }
  221. if( cmThreadPause(p->_thH,0) != kOkThRC )
  222. {
  223. rc = cmErrMsg(&p->_err,kThreadErrSeRC,0,"Thread start failed.");
  224. goto errLabel;
  225. }
  226. if( hp != NULL )
  227. hp->h = p;
  228. else
  229. h.h = p;
  230. errLabel:
  231. if( rc != kOkSeRC )
  232. {
  233. _cmSeDestroy(p);
  234. h.h = NULL;
  235. }
  236. return hp != NULL ? *hp : h;
  237. }
  238. cmSeRC_t cmSeDestroy(cmSeH_t* hp )
  239. {
  240. cmSeRC_t rc = kOkSeRC;
  241. if( hp==NULL || !cmSeIsOpen(*hp) )
  242. return kOkSeRC;
  243. cmSerialPort_t* p = _cmSePtrFromHandle(*hp);
  244. if((rc = _cmSeDestroy(p)) != kOkSeRC )
  245. return rc;
  246. hp->h = NULL;
  247. return rc;
  248. }
  249. bool cmSeIsOpen( cmSeH_t h)
  250. {
  251. if( h.h == NULL )
  252. return false;
  253. cmSerialPort_t* p = _cmSePtrFromHandle(h);
  254. return p->_deviceH != -1;
  255. }
  256. cmSeRC_t cmSeSend( cmSeH_t h, const void* byteA, unsigned byteN )
  257. {
  258. cmSeRC_t rc = kOkSeRC;
  259. cmSerialPort_t* p = _cmSePtrFromHandle(h);
  260. if( !cmSeIsOpen(h) )
  261. return cmErrWarnMsg( &p->_err, kResourceNotAvailableSeRC, "An attempt was made to transmit from a closed serial port.");
  262. if( byteN == 0 )
  263. return rc;
  264. // implement a non blocking write - if less than all the bytes were written then iterate
  265. unsigned i = 0;
  266. do
  267. {
  268. int n = 0;
  269. if((n = write( p->_deviceH, ((char*)byteA)+i, byteN-i )) == -1 )
  270. {
  271. rc = cmErrSysMsg(&p->_err,kWriteFailSeRC,errno,"Write failed on serial port '%s'.", p->_deviceStr );
  272. break;
  273. }
  274. i += n;
  275. }while( i<byteN );
  276. return rc;
  277. }
  278. cmSeRC_t cmSeReceiveCbNb( cmSeH_t h, unsigned* readN_Ref)
  279. {
  280. cmSeRC_t rc = kOkSeRC;
  281. cmSerialPort_t* p = _cmSePtrFromHandle(h);
  282. const unsigned bufN = 512;
  283. char buf[ bufN ];
  284. if( readN_Ref != NULL)
  285. *readN_Ref = 0;
  286. if((rc = cmSeReceiveNb(h,buf,bufN,readN_Ref)) == kOkSeRC )
  287. if( readN_Ref > 0 && p->_cbFunc != NULL )
  288. p->_cbFunc( p->_cbArg, buf, *readN_Ref );
  289. return rc;
  290. }
  291. cmSeRC_t cmSeReceiveCbTimeOut( cmSeH_t h, unsigned timeOutMs, unsigned* readN_Ref)
  292. {
  293. cmSeRC_t rc;
  294. cmSerialPort_t* p = _cmSePtrFromHandle(h);
  295. if((rc = _cmSePoll(p,timeOutMs)) == kOkSeRC )
  296. rc = cmSeReceiveCbNb(h,readN_Ref);
  297. return rc;
  298. }
  299. cmSeRC_t cmSeReceiveNb( cmSeH_t h, void* buf, unsigned bufN, unsigned* readN_Ref)
  300. {
  301. cmSeRC_t rc = kOkSeRC;
  302. cmSerialPort_t* p = _cmSePtrFromHandle(h);
  303. if( readN_Ref != NULL )
  304. *readN_Ref = 0;
  305. if( !cmSeIsOpen(h) )
  306. return cmErrWarnMsg(&p->_err, kResourceNotAvailableSeRC, "An attempt was made to read from a closed serial port.");
  307. int n = 0;
  308. // if attempt to read the port succeeded ...
  309. if((n =read( p->_deviceH, buf, bufN )) != -1 )
  310. *readN_Ref = n;
  311. else
  312. {
  313. // ... or failed and it wasn't because the port was empty
  314. if( errno != EAGAIN)
  315. rc = cmErrSysMsg(&p->_err,kReadFailSeRC,errno,"An attempt to read the serial port '%s' failed.", p->_deviceStr );
  316. }
  317. return rc;
  318. }
  319. cmSeRC_t cmSeReceive( cmSeH_t h, void* buf, unsigned bufByteN, unsigned timeOutMs, unsigned* readN_Ref )
  320. {
  321. cmSeRC_t rc = kOkSeRC;
  322. cmSerialPort_t* p = _cmSePtrFromHandle(h);
  323. if((rc = _cmSePoll(p,timeOutMs)) == kOkSeRC )
  324. rc = cmSeReceiveNb(h,buf,bufByteN,readN_Ref);
  325. return rc;
  326. }
  327. const char* cmSeDevice( cmSeH_t h)
  328. {
  329. cmSerialPort_t* p = _cmSePtrFromHandle(h);
  330. return p->_deviceStr;
  331. }
  332. unsigned cmSeBaudRate( cmSeH_t h)
  333. {
  334. cmSerialPort_t* p = _cmSePtrFromHandle(h);
  335. return p->_baudRate;
  336. }
  337. unsigned cmSeCfgFlags( cmSeH_t h)
  338. {
  339. cmSerialPort_t* p = _cmSePtrFromHandle(h);
  340. return p->_cfgFlags;
  341. }
  342. unsigned cmSeReadInBaudRate( cmSeH_t h )
  343. {
  344. struct termios attr;
  345. cmSerialPort_t* p = _cmSePtrFromHandle(h);
  346. if((_cmSeGetAttributes(p,&attr)) != kOkSeRC )
  347. return 0;
  348. return cfgetispeed(&attr);
  349. }
  350. unsigned cmSeReadOutBaudRate( cmSeH_t h)
  351. {
  352. struct termios attr;
  353. cmSerialPort_t* p = _cmSePtrFromHandle(h);
  354. if((_cmSeGetAttributes(p,&attr)) != kOkSeRC )
  355. return 0;
  356. return cfgetospeed(&attr);
  357. }
  358. unsigned cmSeReadCfgFlags( cmSeH_t h)
  359. {
  360. struct termios attr;
  361. unsigned result = 0;
  362. cmSerialPort_t* p = _cmSePtrFromHandle(h);
  363. if((_cmSeGetAttributes(p,&attr)) == false )
  364. return 0;
  365. switch( attr.c_cflag & CSIZE )
  366. {
  367. case CS5:
  368. cmSetBits( result, kDataBits5SeFl);
  369. break;
  370. case CS6:
  371. cmSetBits( result, kDataBits6SeFl );
  372. break;
  373. case CS7:
  374. cmSetBits( result, kDataBits7SeFl);
  375. break;
  376. case CS8:
  377. cmSetBits( result, kDataBits8SeFl);
  378. break;
  379. }
  380. cmEnaBits( result, k2StopBitSeFl, cmIsFlag( attr.c_cflag, CSTOPB ));
  381. cmEnaBits( result, k1StopBitSeFl, !cmIsFlag( attr.c_cflag, CSTOPB ));
  382. if( cmIsFlag( attr.c_cflag, PARENB ) )
  383. {
  384. cmEnaBits( result, kOddParitySeFl, cmIsFlag( attr.c_cflag, PARODD ));
  385. cmEnaBits( result, kEvenParitySeFl, !cmIsFlag( attr.c_cflag, PARODD ));
  386. }
  387. return result;
  388. }
  389. //====================================================================================================
  390. //
  391. //
  392. void _cmSePortTestCb( void* arg, const void* byteA, unsigned byteN )
  393. {
  394. const char* text = (const char*)byteA;
  395. for(unsigned i=0; i<byteN; ++i)
  396. printf("%c:%i ",text[i],(int)text[i]);
  397. if( byteN )
  398. fflush(stdout);
  399. }
  400. cmSeRC_t cmSePortTest(cmCtx_t* ctx)
  401. {
  402. // Use this test an Arduino running study/serial/arduino_xmt_rcv/main.c
  403. cmSeRC_t rc = kOkSeRC;
  404. const char* device = "/dev/ttyACM0";
  405. unsigned baud = 38400;
  406. unsigned serialCfgFlags = kDefaultCfgSeFlags;
  407. unsigned pollPeriodMs = 50;
  408. cmSeH_t h;
  409. h.h = NULL;
  410. h = cmSeCreate(ctx,&h,device,baud,serialCfgFlags,_cmSePortTestCb,NULL,pollPeriodMs);
  411. bool quitFl = false;
  412. printf("q=quit\n");
  413. while(!quitFl)
  414. {
  415. char c = getchar();
  416. if( c == 'q')
  417. quitFl = true;
  418. else
  419. if( '0' <= c && c <= 'z' )
  420. cmSeSend(h,&c,1);
  421. }
  422. cmSeDestroy(&h);
  423. return rc;
  424. }