Use a 16384-byte buffer for send and another for receive. Read and write occur during comport_tick(), the clock callback, so any data written since the previous tick will be sent in a single write. Callback rate is now adjustable via the [pollintervall( message which defaults to 1ms (usually that's faster than the dsp block rate so the dsp rate takes precedence.)

svn path=/trunk/externals/iem/comport/; revision=13128
This commit is contained in:
Martin Peach 2010-02-01 21:13:58 +00:00
parent 7e89880e44
commit 0c9dd479c6

View file

@ -18,6 +18,7 @@ MP 20070719 added "ports" method to output list of available ports on status out
MP 20071011 added comport_list and write_serials for list processing based on code by Thomas O Fredericks <tof@danslchamp.org>
MP 20071113 modified non-windows open_serial to set the index of the port when it's opened by name
MP 20080916 fixed Windows version stop bits to set and display 1, 1.5 or 2 for "stopbits" input of 1, 1.5 or 2
MP 20100201 use a buffer for writes, write takes place during clock callback comport_tick()
*/
#include "m_pd.h"
@ -77,6 +78,11 @@ typedef struct comport
int verbose;
t_outlet *x_data_outlet;
t_outlet *x_status_outlet;
unsigned char *x_inbuf; /* read incoming serial to here */
unsigned char *x_outbuf; /* write outgoing serial from here */
int x_inbuf_len; /* length of inbuf */
int x_outbuf_len; /* length of outbuf */
int x_outbuf_wr_index; /* offset to next free location in x_outbuf */
} t_comport;
#ifndef TRUE
@ -98,7 +104,7 @@ typedef struct comport
#define COMPORT_MAX 99
#define USE_DEVICENAME 9999 /* use the device name instead of the number */
#define MAX_LIST 1000 /* arbitrary maximum list length for comport_list */
#define COMPORT_BUF_SIZE 16384 /* this should be the largest possible packet size for a USB com port */
#ifdef _WIN32
/* we don't use the table for windos cos we can set the number directly. */
@ -235,7 +241,7 @@ static int set_rts(t_comport *x, int nr);
static int set_xonxoff(t_comport *x, int nr);
static int set_serial(t_comport *x);
static int write_serial(t_comport *x, unsigned char serial_byte);
static int write_serials(t_comport *x, unsigned char *serial_buf, size_t buf_length);
static int write_serials(t_comport *x, unsigned char *serial_buf, int buf_length);
static int comport_get_dsr(t_comport *x);
static int comport_get_cts(t_comport *x);
#ifdef _WIN32
@ -461,7 +467,7 @@ static HANDLE open_serial(unsigned int com_num, t_comport *x)
x->serial_device = gensym(buffer);
}
post("Opening %s", &x->serial_device->s_name[4]);/* skip slashes and dot */
fd = CreateFile( x->serial_device->s_name,
fd = CreateFileA( x->serial_device->s_name,
GENERIC_READ | GENERIC_WRITE,
0,
0,
@ -608,73 +614,6 @@ static HANDLE close_serial(t_comport *x)
return INVALID_HANDLE_VALUE;
}
static int write_serial(t_comport *x, unsigned char serial_byte)
{
OVERLAPPED osWrite = {0};
DWORD dwWritten;
DWORD dwToWrite = 1L;
DWORD dwErr;
DWORD numTransferred = 0L;
osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (osWrite.hEvent == NULL)
{
post("Couldn't create event. Transmission aborted.");
return 0;
}
if (!WriteFile(x->comhandle, &serial_byte, dwToWrite, &dwWritten, &osWrite))
{
dwErr = GetLastError();
if (dwErr != ERROR_IO_PENDING)
{
post("WriteFile error: %d", (int)dwErr);
return 0;
}
}
if (!GetOverlappedResult(x->comhandle, &osWrite, &numTransferred, TRUE))
{/* wait for the character to be sent */
dwErr = GetLastError();
post("WriteFile:GetOverlappedResult error: %d", (int)dwErr);
}
CloseHandle(osWrite.hEvent);
return 1;
}
static int write_serials(t_comport *x, unsigned char *serial_buf, size_t buf_length)
{
OVERLAPPED osWrite = {0};
DWORD dwWritten;
DWORD dwToWrite = (DWORD)buf_length;
DWORD dwErr;
DWORD numTransferred = 0L;
osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (osWrite.hEvent == NULL)
{
post("Couldn't create event. Transmission aborted.");
return 0;
}
if (!WriteFile(x->comhandle, serial_buf, dwToWrite, &dwWritten, &osWrite))
{
dwErr = GetLastError();
if (dwErr != ERROR_IO_PENDING)
{
post("WriteFile error: %d", (int)dwErr);
return 0;
}
}
if (!GetOverlappedResult(x->comhandle, &osWrite, &numTransferred, TRUE))
{/* wait for the character(s) to be sent */
dwErr = GetLastError();
post("WriteFile:GetOverlappedResult error: %d", (int)dwErr);
}
CloseHandle(osWrite.hEvent);
return 1;
}
static int comport_get_dsr(t_comport *x)
{
short dsr_state = 0;
@ -694,6 +633,7 @@ static int comport_get_dsr(t_comport *x)
}
return dsr_state;
}
int comport_get_cts(t_comport *x)
{
short cts_state = 0;
@ -1026,29 +966,6 @@ static int set_serial(t_comport *x)
return 1;
}
static int write_serial(t_comport *x, unsigned char serial_byte)
{
int result = write(x->comhandle,(char *) &serial_byte,1);
if (result != 1)
post ("[comport] write returned %d, errno is %d", result, errno);
return result;
/* flush pending I/O chars */
/* but nowadays discards them ;-(
else
{
ioctl(x->comhandle,TCFLSH,TCOFLUSH);
}
*/
}
static int write_serials(t_comport *x, unsigned char *serial_buf, size_t buf_length)
{
int result = write(x->comhandle,(char *)serial_buf, buf_length);
if (result != (int)buf_length)
post ("[comport] write returned %d, errno is %d", result, errno);
return result;
}
static int comport_get_dsr(t_comport *x)
{
short dsr_state = 0;
@ -1100,7 +1017,6 @@ static void comport_tick(t_comport *x)
if(fd != INVALID_HANDLE_VALUE)
{ /* while there are bytes, read them and send them out, ignore errors (!??) */
#ifdef _WIN32
unsigned char serial_byte[1000];
DWORD dwRead;
OVERLAPPED osReader = {0};
DWORD dwX;
@ -1109,13 +1025,13 @@ static void comport_tick(t_comport *x)
err = 0;
osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if(ReadFile(x->comhandle, serial_byte, 1000, &dwRead, &osReader))
if(ReadFile(x->comhandle, x->x_inbuf, x->x_inbuf_len, &dwRead, &osReader))
{
if(dwRead > 0)
{
for(dwX = 0; dwX < dwRead ;dwX++)
{
outlet_float(x->x_data_outlet, (t_float) serial_byte[dwX]);
outlet_float(x->x_data_outlet, (t_float) x->x_inbuf[dwX]);
}
}
}
@ -1126,7 +1042,6 @@ static void comport_tick(t_comport *x)
}
CloseHandle(osReader.hEvent);
#else
unsigned char serial_byte[1000];
fd_set com_rfds;
int count = 0;
int i;
@ -1136,12 +1051,16 @@ static void comport_tick(t_comport *x)
FD_SET(fd,&com_rfds);
while((err = select(fd+1, &com_rfds, NULL, NULL, &null_tv)) > 0)
{
ioctl(fd, FIONREAD, &count); /* load count with the number of bytes in the receive buffer */
ioctl(fd, FIONREAD, &count); /* load count with the number of bytes in the receive buffer... */
if (count > x->x_inbuf_len) count = x->x_inbuf_len; /* ...but no more than the buffer can hold */
/*err = read(fd,(char *) &serial_byte,1);*/
err = read(fd,(char *) serial_byte, count);/* try to read count bytes */
err = read(fd,(char *)x->x_inbuf, count);/* try to read count bytes */
if (err >= 0)
{
for (i = 0; i < err; ++i ) outlet_float(x->x_data_outlet, (t_float) serial_byte[i]);
for (i = 0; i < err; ++i )
{
outlet_float(x->x_data_outlet, (t_float) x->x_inbuf[i]);
}
}
else whicherr = errno;
}
@ -1152,8 +1071,67 @@ static void comport_tick(t_comport *x)
post("RXERRORS on serial line (%d)\n", whicherr);
x->rxerrors++; /* remember */
}
if (!x->x_hit) clock_delay(x->x_clock, 1);
/* now if anything to send, send the output buffer */
if (0 != x->x_outbuf_wr_index)
{
#ifdef _WIN32
OVERLAPPED osWrite = {0};
DWORD dwWritten;
DWORD dwToWrite = (DWORD)x->x_outbuf_wr_index;
DWORD dwErr;
DWORD numTransferred = 0L;
osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (osWrite.hEvent == NULL)
{
post("[comport]: Couldn't create event. Transmission aborted.");
goto endsendevent;
}
else if (!WriteFile(x->comhandle, x->x_outbuf, dwToWrite, &dwWritten, &osWrite))
{
dwErr = GetLastError();
if (dwErr != ERROR_IO_PENDING)
{
post("[comport]: WriteFile error: %d", (int)dwErr);
goto endsendevent;
}
}
if (!GetOverlappedResult(x->comhandle, &osWrite, &numTransferred, TRUE))
{/* wait for the character(s) to be sent */
dwErr = GetLastError();
post("[comport]: WriteFile:GetOverlappedResult error: %d", (int)dwErr);
}
endsendevent:
CloseHandle(osWrite.hEvent);
#else
err = write(x->comhandle,(char *)x->x_outbuf, x->x_outbuf_wr_index);
if (err != x->x_outbuf_wr_index)
post ("[comport]: Write returned %d, errno is %d", err, errno);
#endif /*_WIN32*/
x->x_outbuf_wr_index = 0; /* for now we just drop anything that didn't send */
}
if (!x->x_hit) clock_delay(x->x_clock, x->x_deltime); /* default 1 ms */
}
}
static int write_serial(t_comport *x, unsigned char serial_byte)
{
if(x->x_outbuf_wr_index < x->x_outbuf_len)
{
x->x_outbuf[x->x_outbuf_wr_index++] = serial_byte;
return 1;
}
/* handle overrun error */
else return 0;
}
static int write_serials(t_comport *x, unsigned char *serial_buf, int buf_length)
{
int i;
for (i = 0; ((i < buf_length) && (x->x_outbuf_wr_index < x->x_outbuf_len)); ++x->x_outbuf_wr_index, ++i)
x->x_outbuf[x->x_outbuf_wr_index] = serial_buf[i];
return i;
}
static void comport_float(t_comport *x, t_float f)
@ -1168,21 +1146,19 @@ static void comport_float(t_comport *x, t_float f)
static void comport_list(t_comport *x, t_symbol *s, int argc, t_atom *argv)
{
unsigned char temp_array[MAX_LIST];/* arbitrary maximum list length */
unsigned char temp_array[COMPORT_BUF_SIZE];/* arbitrary maximum list length */
int i, count;
int result;
count = argc;
if (argc > MAX_LIST)
if (argc > COMPORT_BUF_SIZE)
{
post ("[comport] truncated list of %d elements to %d", argc, count);
count = MAX_LIST;
count = COMPORT_BUF_SIZE;
}
for(i = 0; i < count; i++)
temp_array[i] = ((unsigned char)atom_getint(argv+i))&0xFF; /* brutal conv */
result = write_serials(x, temp_array, count);
if (result < 0)
post ("[comport] write returned %d, errno is %d", result, errno);
}
static void *comport_new(t_symbol *s, int argc, t_atom *argv)
@ -1265,6 +1241,23 @@ allows COM port numbers to be specified. */
#endif
}
/* allocate memory for in and out buffers */
x->x_inbuf = getbytes(COMPORT_BUF_SIZE);
if (NULL == x->x_inbuf)
{
pd_error(x, "[comport] unable to allocate input buffer");
return 0;
}
x->x_inbuf_len = COMPORT_BUF_SIZE;
x->x_outbuf = getbytes(COMPORT_BUF_SIZE);
if (NULL == x->x_outbuf)
{
pd_error(x, "[comport] unable to allocate output buffer");
return 0;
}
x->x_outbuf_len = COMPORT_BUF_SIZE;
x->x_outbuf_wr_index = 0;
x->rxerrors = 0; /* holds the rx line errors */
x->x_data_outlet = outlet_new(&x->x_obj, &s_float);
@ -1288,6 +1281,8 @@ static void comport_free(t_comport *x)
clock_unset(x->x_clock);
clock_free(x->x_clock);
x->comhandle = close_serial(x);
freebytes(x->x_inbuf, x->x_inbuf_len);
freebytes(x->x_outbuf, x->x_outbuf_len);
}
/* ---------------- use serial settings ------------- */
@ -1573,7 +1568,7 @@ static void comport_enum(t_comport *x)
for(i = 1; i < COMPORT_MAX; i++)
{
sprintf(device_name, "%s%d", x->serial_device_prefix, i);
fd = CreateFile( device_name,
fd = CreateFileA( device_name,
GENERIC_READ | GENERIC_WRITE,
0,
0,
@ -1637,7 +1632,7 @@ static void comport_ports(t_comport *x)
for(i = 1; i < COMPORT_MAX; i++)
{
sprintf(device_name, "%s%d", x->serial_device_prefix, i);
fd = CreateFile( device_name,
fd = CreateFileA( device_name,
GENERIC_READ | GENERIC_WRITE,
0,
0,