Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / lang / LangPrimSource / PyrSerialPrim.cpp
blobd58c9959e2f77a8121def1ead6024095b0baa7d1
1 /*
2 Serial port support.
3 Copyright (c) 2006 stefan kersten.
5 ====================================================================
7 SuperCollider real time audio synthesis system
8 Copyright (c) 2002 James McCartney. All rights reserved.
9 http://www.audiosynth.com
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <stdint.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/ioctl.h>
33 #include <sys/select.h>
34 #include <termios.h>
35 #include <unistd.h>
36 #include <boost/atomic.hpp>
38 #include <stdexcept>
39 #include <sstream>
41 #include "GC.h"
42 #include "PyrKernel.h"
43 #include "PyrPrimitive.h"
44 #include "SC_LanguageClient.h"
45 #include "SCBase.h"
47 #include "SC_FIFO.h"
49 class SerialPort
51 public:
52 enum Parity
54 kNoParity,
55 kEvenParity,
56 kOddParity
59 struct Options
61 Options()
62 : exclusive(false),
63 baudrate(9600),
64 databits(8),
65 stopbit(true),
66 parity(kNoParity),
67 crtscts(false),
68 xonxoff(false)
69 { }
71 bool exclusive;
72 size_t baudrate;
73 size_t databits;
74 bool stopbit;
75 Parity parity;
76 bool crtscts;
77 bool xonxoff;
80 static const int kNumOptions = 7;
81 static const int kBufferSize = 8192;
82 static const int kReadTimeoutMs = 1000;
84 typedef SC_FIFO<uint8_t,kBufferSize> FIFO;
86 struct Error : std::runtime_error
88 explicit Error(const char* what)
89 : std::runtime_error(what)
90 { }
93 struct SysError : public Error
95 explicit SysError(int e=errno)
96 : Error(strerror(e))
97 { }
100 static PyrSymbol* s_dataAvailable;
101 static PyrSymbol* s_doneAction;
103 public:
104 SerialPort(PyrObject* obj, const char* serialport, const Options& options);
105 ~SerialPort();
107 bool isRunning() const { return m_running; }
108 int fd() const { return m_fd; }
109 const Options& options() const { return m_options; }
111 bool put(uint8_t byte);
112 bool get(uint8_t* byte);
113 int rxErrors();
115 void stop();
116 void cleanup();
118 protected:
119 static void* threadFunc(void*);
120 void threadLoop();
122 void dataAvailable();
123 void doneAction();
125 private:
126 // language interface
127 PyrObject* m_obj;
129 boost::atomic<bool> m_dodone;
131 // serial interface
132 Options m_options;
133 int m_fd;
134 boost::atomic<bool> m_open;
135 struct termios m_termio;
136 struct termios m_oldtermio;
138 // rx buffers
139 int m_rxErrors[2];
140 FIFO m_rxfifo;
141 uint8_t m_rxbuffer[kBufferSize];
143 // rx thread
144 boost::atomic<bool> m_running;
145 pthread_t m_thread;
148 PyrSymbol* SerialPort::s_dataAvailable = 0;
149 PyrSymbol* SerialPort::s_doneAction = 0;
151 SerialPort::SerialPort(PyrObject* obj, const char* serialport, const Options& options)
152 : m_obj(obj),
153 m_options(options),
154 m_fd(-1)
156 // open non blocking
157 m_fd = open(serialport, O_RDWR | O_NOCTTY | O_NDELAY);
158 if (m_fd == -1) {
159 throw SysError(errno);
162 // exclusiveness
163 #if defined(TIOCEXCL)
164 if (m_options.exclusive) {
165 if (ioctl(m_fd, TIOCEXCL) == -1) {
166 throw SysError(errno);
169 #endif // TIOCEXCL
171 if (fcntl(m_fd, F_SETFL, O_NONBLOCK) == -1) {
172 int e = errno;
173 close(m_fd);
174 throw SysError(e);
177 // initialize serial connection
179 // get current settings and remember them
180 struct termios toptions;
181 if (tcgetattr(m_fd, &toptions) < 0) {
182 int e = errno;
183 close(m_fd);
184 throw SysError(e);
186 memcpy(&m_oldtermio, &toptions, sizeof(toptions));
188 // baudrate
189 speed_t brate;
190 switch (m_options.baudrate) {
191 case 1200:
192 brate = B1200;
193 break;
194 case 1800:
195 brate = B1800;
196 break;
197 case 2400:
198 brate = B2400;
199 break;
200 case 4800:
201 brate = B4800;
202 break;
203 case 9600:
204 brate = B9600;
205 break;
206 case 19200:
207 brate = B19200;
208 break;
209 case 38400:
210 brate = B38400;
211 break;
212 // #ifndef _POSIX_C_SOURCE
213 #if defined(B7200)
214 case 7200:
215 brate = B7200;
216 break;
217 #endif
218 #if defined(B7200)
219 case 14400:
220 brate = B14400;
221 break;
222 #endif
223 #if defined(B28800)
224 case 28800:
225 brate = B28800;
226 break;
227 #endif
228 #if defined(B57600)
229 case 57600:
230 brate = B57600;
231 break;
232 #endif
233 #if defined(B76800)
234 case 76800:
235 brate = B76800;
236 break;
237 #endif
238 #if defined(B115200)
239 case 115200:
240 brate = B115200;
241 break;
242 #endif
243 #if defined(B230400)
244 case 230400:
245 brate = B230400;
246 break;
247 #endif
248 // #endif // !_POSIX_C_SOURCE
249 default:
250 close(m_fd);
251 throw Error("unsupported baudrate");
254 cfsetispeed(&toptions, brate);
255 cfsetospeed(&toptions, brate);
257 // data bits
258 toptions.c_cflag &= ~CSIZE;
259 switch (m_options.databits)
261 case 5:
262 toptions.c_cflag |= CS5;
263 break;
264 case 6:
265 toptions.c_cflag |= CS6;
266 break;
267 case 7:
268 toptions.c_cflag |= CS7;
269 break;
270 default:
271 m_options.databits = 8;
272 toptions.c_cflag |= CS8;
273 break;
276 // stop bit
277 if (m_options.stopbit) {
278 toptions.c_cflag |= CSTOPB;
279 } else {
280 toptions.c_cflag &= ~CSTOPB;
283 // parity
284 switch (m_options.parity)
286 case kNoParity:
287 toptions.c_cflag &= ~PARENB;
288 break;
289 case kEvenParity:
290 toptions.c_cflag |= PARENB;
291 toptions.c_cflag &= ~PARODD;
292 break;
293 case kOddParity:
294 toptions.c_cflag |= (PARENB | PARODD);
295 break;
298 // h/w flow control
299 #if !defined(_POSIX_C_SOURCE) || defined(__USE_MISC)
300 if (m_options.crtscts) {
301 toptions.c_cflag &= ~CRTSCTS;
302 } else {
303 toptions.c_cflag |= CRTSCTS;
305 #endif // !_POSIX_C_SOURCE || __USE_MISC
307 // s/w flow control
308 if (m_options.xonxoff) {
309 toptions.c_iflag |= (IXON | IXOFF | IXANY);
310 } else {
311 toptions.c_iflag &= ~(IXON | IXOFF | IXANY);
314 // (nescivi) by default carriage returns are translated to line feeds,
315 // we don't want that
316 toptions.c_iflag &= ~ICRNL;
318 // enable READ & ignore ctrl lines
319 toptions.c_cflag |= (CREAD | CLOCAL);
320 // non-canonical (raw) i/o
321 toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
322 // disable post processing
323 toptions.c_oflag &= ~OPOST;
325 // see: http://unixwiz.net/techtips/termios-vmin-vtime.html
326 // NOTE: unused for non-blocking reads
327 // toptions.c_cc[VMIN] = 0;
328 // toptions.c_cc[VTIME] = 20;
330 if (tcsetattr(m_fd, TCSAFLUSH, &toptions) < 0) {
331 int e = errno;
332 close(m_fd);
333 throw SysError(e);
335 memcpy(&m_termio, &toptions, sizeof(toptions));
337 m_rxErrors[0] = m_rxErrors[1] = 0;
339 int e = pthread_create(&m_thread, 0, threadFunc, this);
340 if (e != 0) {
341 close(m_fd);
342 throw SysError(e);
345 m_open = true;
346 m_dodone = true;
349 SerialPort::~SerialPort()
351 m_running = false;
352 // m_open = false;
353 if ( m_open ){
354 tcflush(m_fd, TCIOFLUSH);
355 tcsetattr(m_fd, TCSANOW, &m_oldtermio);
356 close(m_fd);
357 m_open = false;
359 pthread_join(m_thread, 0);
362 void SerialPort::stop(){
363 m_running = false;
366 void SerialPort::cleanup(){
367 m_running = false;
368 m_dodone = false;
369 if ( m_open ){
370 tcflush(m_fd, TCIOFLUSH);
371 tcsetattr(m_fd, TCSANOW, &m_oldtermio);
372 close(m_fd);
373 m_open = false;
377 bool SerialPort::put(uint8_t byte)
379 return write(m_fd, &byte, sizeof(byte)) == sizeof(byte);
382 bool SerialPort::get(uint8_t* byte)
384 if (m_rxfifo.IsEmpty())
385 return false;
386 *byte = m_rxfifo.Get() & 0xFF;
387 return true;
390 int SerialPort::rxErrors()
392 // errors since last query
393 int x = m_rxErrors[1];
394 int res = x-m_rxErrors[0];
395 m_rxErrors[0] = x;
396 return res;
399 void* SerialPort::threadFunc(void* self)
401 ((SerialPort*)self)->threadLoop();
402 return 0;
405 void SerialPort::dataAvailable()
407 int status = lockLanguageOrQuit(m_running);
408 if (status == EINTR)
409 return;
410 if (status) {
411 postfl("error when locking language (%d)\n", status);
412 return;
415 PyrSymbol *method = s_dataAvailable;
416 if (m_obj) {
417 VMGlobals *g = gMainVMGlobals;
418 g->canCallOS = true;
419 ++g->sp; SetObject(g->sp, m_obj);
420 runInterpreter(g, method, 1);
421 g->canCallOS = false;
423 pthread_mutex_unlock (&gLangMutex);
426 void SerialPort::doneAction()
428 int status = lockLanguageOrQuit(m_running);
429 if (status == EINTR)
430 return;
431 if (status) {
432 postfl("error when locking language (%d)\n", status);
433 return;
436 PyrSymbol *method = s_doneAction;
437 if (m_obj) {
438 VMGlobals *g = gMainVMGlobals;
439 g->canCallOS = true;
440 ++g->sp; SetObject(g->sp, m_obj);
441 runInterpreter(g, method, 1);
442 g->canCallOS = false;
444 pthread_mutex_unlock (&gLangMutex);
447 void SerialPort::threadLoop()
449 const int fd = m_fd;
450 const int max_fd = fd+1;
452 m_running = true;
453 m_rxErrors[1] = 0;
455 while (true) {
456 fd_set rfds;
458 FD_ZERO( &rfds);
459 FD_SET(fd, &rfds);
461 struct timeval timeout;
462 timeout.tv_sec = kReadTimeoutMs/1000;
463 timeout.tv_usec = (kReadTimeoutMs%1000)*1000;
465 int n = select(max_fd, &rfds, 0, 0, &timeout);
466 // int fdset = FD_ISSET(fd, &rfds);
467 // printf( "fdset %i, n %i, errno %i\n", fdset, n, errno );
468 if ( m_open ){
469 if ((n > 0) && FD_ISSET(fd, &rfds)) {
470 // printf("poll input\n");
471 int nr = 0;
472 // while (true) {
473 if ( m_open ){
474 int n2 = read(fd, m_rxbuffer, kBufferSize);
475 // printf("read %d, errno %i, errbadf %i, %i, %i\n", n2, errno, EBADF, EAGAIN, EIO);
476 if (n2 > 0) {
477 // write data to ringbuffer
478 for (int i=0; i < n2; ++i) {
479 if (!m_rxfifo.Put(m_rxbuffer[i])) {
480 m_rxErrors[1]++;
481 break;
484 nr += n2;
485 } else if ((n2 == 0) && (n == 1) ) { // added by nescivi, to check for disconnected device. In this case the read is 0 all the time and otherwise eats up the CPU
486 // printf( "done\n" );
487 goto done;
488 } else if ((n2 == 0) || ((n2 == -1) && (errno == EAGAIN))) {
489 // printf( "break\n");
490 break;
491 } else {
492 #ifndef NDEBUG
493 printf("SerialPort HUP\n");
494 #endif
495 goto done;
499 if (!m_running) {
500 // close and cleanup
501 goto done;
503 if (nr > 0) {
504 dataAvailable();
506 } else if (n == -1) {
507 goto done;
510 if (!m_running) {
511 // close and cleanup
512 goto done;
516 done:
517 // doneAction();
518 if ( m_open ){
519 tcflush(fd, TCIOFLUSH);
520 tcsetattr(fd, TCSANOW, &m_oldtermio);
521 close(fd);
523 m_open = false;
524 m_running = false;
525 if ( m_dodone )
526 { doneAction(); }
527 #ifndef NDEBUG
528 printf("SerialPort closed\n");
529 #endif
532 // =====================================================================
533 // primitives
535 static SerialPort* getSerialPort(PyrSlot* slot)
537 if (NotPtr(&slotRawObject(slot)->slots[0]))
538 return NULL;
539 return (SerialPort*)slotRawPtr(&slotRawObject(slot)->slots[0]);
542 static int prSerialPort_Open(struct VMGlobals *g, int numArgsPushed)
544 PyrSlot *args = g->sp - 1 - SerialPort::kNumOptions;
546 int err;
548 PyrSlot* self = args+0;
550 if (getSerialPort(self) != 0)
551 return errFailed;
553 char portName[PATH_MAX];
554 err = slotStrVal(args+1, portName, sizeof(portName));
555 printf("portName %s\n", portName);
556 if (err) return err;
558 SerialPort::Options options;
559 SerialPort* port = 0;
561 options.exclusive = IsTrue(args+2);
563 int baudrate;
564 err = slotIntVal(args+3, &baudrate);
565 if (err) return err;
566 options.baudrate = baudrate;
568 int databits;
569 err = slotIntVal(args+4, &databits);
570 if (err) return err;
571 options.databits = databits;
573 options.stopbit = IsTrue(args+5);
575 int parity;
576 err = slotIntVal(args+6, &parity);
577 if (err) return err;
578 options.parity = (SerialPort::Parity)parity;
580 options.crtscts = IsTrue(args+7);
581 options.xonxoff = IsTrue(args+8);
583 try {
584 port = new SerialPort(slotRawObject(self), portName, options);
585 } catch (SerialPort::Error& e) {
586 std::ostringstream os;
587 os << "SerialPort Error: " << e.what();
588 post(os.str().c_str());
589 return errFailed;
592 SetPtr(slotRawObject(self)->slots+0, port);
594 return errNone;
597 static int prSerialPort_Close(struct VMGlobals *g, int numArgsPushed)
599 PyrSlot* self = g->sp;
600 SerialPort* port = (SerialPort*)getSerialPort(self);
601 if (port == 0) return errFailed;
602 port->stop();
603 return errNone;
606 static int prSerialPort_Cleanup(struct VMGlobals *g, int numArgsPushed)
608 PyrSlot* self = g->sp;
609 SerialPort* port = (SerialPort*)getSerialPort(self);
611 if (port == 0) return errFailed;
613 port->cleanup();
615 post("SerialPort Cleanup\n");
617 delete port;
618 SetNil(slotRawObject(self)->slots+0);
619 return errNone;
622 static int prSerialPort_Next(struct VMGlobals *g, int numArgsPushed)
624 PyrSlot* self = g->sp;
625 SerialPort* port = (SerialPort*)getSerialPort(self);
626 // printf( "port %i", port );
627 if (port == 0) return errFailed;
629 uint8_t byte;
630 if (port->get(&byte)) {
631 SetInt(self, byte);
632 } else {
633 SetNil(self);
636 return errNone;
639 static int prSerialPort_Put(struct VMGlobals *g, int numArgsPushed)
641 PyrSlot *args = g->sp - 1;
643 PyrSlot* self = args+0;
644 SerialPort* port = (SerialPort*)getSerialPort(self);
645 if (port == 0) return errFailed;
647 PyrSlot* src = args+1;
649 int val;
650 if (IsChar(src)) {
651 val = slotRawChar(src);
652 } else {
653 int err = slotIntVal(src, &val);
654 if (err) return err;
657 bool res = port->put(val & 0xFF);
658 SetBool(self, res);
660 return errNone;
663 static int prSerialPort_RXErrors(struct VMGlobals *g, int numArgsPushed)
665 PyrSlot* self = g->sp;
666 SerialPort* port = (SerialPort*)getSerialPort(self);
667 if (port == 0) return errFailed;
668 SetInt(self, port->rxErrors());
669 return errNone;
672 void initSerialPrimitives()
674 int base, index;
676 base = nextPrimitiveIndex();
677 index = 0;
679 definePrimitive(base, index++, "_SerialPort_Open", prSerialPort_Open, 2+SerialPort::kNumOptions, 0);
680 definePrimitive(base, index++, "_SerialPort_Close", prSerialPort_Close, 1, 0);
681 definePrimitive(base, index++, "_SerialPort_Next", prSerialPort_Next, 1, 0);
682 definePrimitive(base, index++, "_SerialPort_Put", prSerialPort_Put, 2, 0);
683 definePrimitive(base, index++, "_SerialPort_RXErrors", prSerialPort_RXErrors, 1, 0);
684 definePrimitive(base, index++, "_SerialPort_Cleanup", prSerialPort_Cleanup, 1, 0);
686 SerialPort::s_dataAvailable = getsym("prDataAvailable");
687 SerialPort::s_doneAction = getsym("prDoneAction");
690 // EOF