www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Access to serial ports in Windows

reply Don Clugston <dac nospam.com.au> writes:
Has anyone accessed serial (COM) ports using D (under Windows)?
Does Mango support them?

If there is currently nothing in D, does anyone know of a candidate that 
could be ported to D?
It ought to be possible to create something that would work on both 
Windows and Linux PCs, since the hardware is the same.
Oct 24 2005
next sibling parent reply =?ISO-8859-1?Q?Jari-Matti_M=E4kel=E4?= <jmjmak invalid_utu.fi> writes:
Don Clugston wrote:
 Has anyone accessed serial (COM) ports using D (under Windows)?
 Does Mango support them?

Windows and Linux behave a bit differently here. On Win9x/DOS you can access hardware ports directly. On WinNT+ and Linux you have to use system calls. I think D is able to call these system API functions directly, you just need correct header files.
 If there is currently nothing in D, does anyone know of a candidate that 
 could be ported to D?
 It ought to be possible to create something that would work on both 
 Windows and Linux PCs, since the hardware is the same.

I'm not really sure this interoperability will be of much motivation - nowadays serial ports are pretty ancient technology.
Oct 24 2005
parent reply Don Clugston <dac nospam.com.au> writes:
Jari-Matti Mäkelä wrote:
 Don Clugston wrote:
 
 Has anyone accessed serial (COM) ports using D (under Windows)?
 Does Mango support them?

Windows and Linux behave a bit differently here. On Win9x/DOS you can access hardware ports directly. On WinNT+ and Linux you have to use system calls. I think D is able to call these system API functions directly, you just need correct header files.

Yes, it is, but serial ports aren't quite that simple. There are subtle handshaking and concurrency issues, it's extremely easy to get it wrong. It's actually quite similar to a slow internet connection. You have to consider the possibility that the connection is lost in mid-transmission; you cannot freeze the machine while you wait for the next bytes to arrive; etc. The Windows API gives you very little help with this; I'm not sure what the situation is for Linux.
 If there is currently nothing in D, does anyone know of a candidate 
 that could be ported to D?
 It ought to be possible to create something that would work on both 
 Windows and Linux PCs, since the hardware is the same.

I'm not really sure this interoperability will be of much motivation - nowadays serial ports are pretty ancient technology.

It's ancient, but very common for engineering tools, where a cheap, simple interface is required. COM ports probably won't die for another decade. An ancient library would be adequate. If I could switch my code between Windows + Linux machines, it would be a real bonus for me.
Oct 24 2005
parent reply Sean Kelly <sean f4.ca> writes:
In article <djikpt$q52$1 digitaldaemon.com>, Don Clugston says...
Jari-Matti Mäkelä wrote:
 
 I'm not really sure this interoperability will be of much motivation - 
 nowadays serial ports are pretty ancient technology.

It's ancient, but very common for engineering tools, where a cheap, simple interface is required. COM ports probably won't die for another decade. An ancient library would be adequate.

So long as you're using an old PC at any rate. If I remember correctly, the latest mainboard format does away with serial ports. Sean
Oct 24 2005
parent antonio <antonio abrevia.net> writes:
Sean Kelly wrote:
 In article <djikpt$q52$1 digitaldaemon.com>, Don Clugston says...
 Jari-Matti Mäkelä wrote:
 I'm not really sure this interoperability will be of much motivation - 
 nowadays serial ports are pretty ancient technology.

simple interface is required. COM ports probably won't die for another decade. An ancient library would be adequate.

So long as you're using an old PC at any rate. If I remember correctly, the latest mainboard format does away with serial ports. Sean

COM ports are very common on industrial environments actually and in the future. I agree that Office/playing machines can forget this technology... but there is other worlds in this world... and industrial environments and instrumentation is one of this worlds... Antonio
Oct 28 2005
prev sibling next sibling parent Florian Sonnenberger <nairolf online.de> writes:
Don Clugston schrieb:
 Has anyone accessed serial (COM) ports using D (under Windows)?
 Does Mango support them?
 
 If there is currently nothing in D, does anyone know of a candidate that 
 could be ported to D?
 It ought to be possible to create something that would work on both 
 Windows and Linux PCs, since the hardware is the same.

http://sourceforge.net/projects/dframework/ There is a file called "serialstream.d". I don't know if this is what you want, I haven't looked at it yet. But I think, it's Windows-only. Florian
Oct 24 2005
prev sibling next sibling parent reply "Kris" <fu bar.com> writes:
Mango does not currently support serial or parallel port I/O. Part of the 
reasoning behind this is that user-level code does not have access to the 
hardware: on Win32 you need a kernel-mode device driver to jump across the 
ring barrier. I don't know what the scoop is on Linux, but I imagine it's 
somewhat similar (the hardware ports are not likely to be exposed at the 
user-mode level).

In the past I've used 'UserPort' on Win32 to gain access to the hardware 
(via Java, actually). It's easy to apply, but would really need 
interrupt-callback support added for serious usage (currently you have to 
poll incoming data: a big no-no, IMO).

If it were deemed generally useful to treat the 'legacy' ports as a 
non-sharable entity, and a (interrupt supporting) driver were made available 
for both Win32 and Linux, I'd be happy to see wrappers for that within Mango 
:-)

In the meantime, if you're on Win32 and can put up with polling (or don't 
need to read data; just send it), then UserPort will be your friend. Let us 
know how you get on?

- Kris


"Don Clugston" <dac nospam.com.au> wrote in message 
news:djif1s$lfr$1 digitaldaemon.com...
 Has anyone accessed serial (COM) ports using D (under Windows)?
 Does Mango support them?

 If there is currently nothing in D, does anyone know of a candidate that 
 could be ported to D?
 It ought to be possible to create something that would work on both 
 Windows and Linux PCs, since the hardware is the same. 

Oct 24 2005
parent "Kris" <fu bar.com> writes:
Well I'll be. Win32 does expose access to the com ports, and supports 
asynchronous reading of them also: 
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/devio/base/monitoring_communications_events.asp

WaitCommEvent
If the overlapped operation cannot be completed immediately, the function 
returns FALSE and the GetLastError function returns ERROR_IO_PENDING, 
indicating that the operation is executing in the background. When this 
happens, the system sets the hEvent member of the OVERLAPPED structure to 
the not-signaled state before WaitCommEvent returns, and then it sets it to 
the signaled state when one of the specified events or an error occurs. The 
calling process can use one of the wait functions to determine the event 
object's state and then use the GetOverlappedResult function to determine 
the results of the WaitCommEvent operation.


My apologies for the earlier misguidance.

- Kris



"Kris" <fu bar.com> wrote in message news:djj7e7$196e$1 digitaldaemon.com...
 Mango does not currently support serial or parallel port I/O. Part of the 
 reasoning behind this is that user-level code does not have access to the 
 hardware: on Win32 you need a kernel-mode device driver to jump across the 
 ring barrier. I don't know what the scoop is on Linux, but I imagine it's 
 somewhat similar (the hardware ports are not likely to be exposed at the 
 user-mode level).

 In the past I've used 'UserPort' on Win32 to gain access to the hardware 
 (via Java, actually). It's easy to apply, but would really need 
 interrupt-callback support added for serious usage (currently you have to 
 poll incoming data: a big no-no, IMO).

 If it were deemed generally useful to treat the 'legacy' ports as a 
 non-sharable entity, and a (interrupt supporting) driver were made 
 available for both Win32 and Linux, I'd be happy to see wrappers for that 
 within Mango :-)

 In the meantime, if you're on Win32 and can put up with polling (or don't 
 need to read data; just send it), then UserPort will be your friend. Let 
 us know how you get on?

 - Kris


 "Don Clugston" <dac nospam.com.au> wrote in message 
 news:djif1s$lfr$1 digitaldaemon.com...
 Has anyone accessed serial (COM) ports using D (under Windows)?
 Does Mango support them?

 If there is currently nothing in D, does anyone know of a candidate that 
 could be ported to D?
 It ought to be possible to create something that would work on both 
 Windows and Linux PCs, since the hardware is the same.


Oct 24 2005
prev sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
------------Nf6FVE3JdOPfLFySk0O8y4
Content-Type: text/plain; format=flowed; delsp=yes; charset=iso-8859-15
Content-Transfer-Encoding: 8bit

On Mon, 24 Oct 2005 13:03:16 +0200, Don Clugston <dac nospam.com.au> wrote:
 Has anyone accessed serial (COM) ports using D (under Windows)?

Yes.
 Does Mango support them?
 If there is currently nothing in D, does anyone know of a candidate that  
 could be ported to D?
 It ought to be possible to create something that would work on both  
 Windows and Linux PCs, since the hardware is the same.

The attached source is a COM stream for windows only. I have also written COM code on linux and freebsd and could probably add support for these two to the attached source, let me know if you're interested. Regan ------------Nf6FVE3JdOPfLFySk0O8y4 Content-Disposition: attachment; filename=com.d Content-Type: application/octet-stream; name=com.d Content-Transfer-Encoding: 8bit module com; import std.c.windows.windows; import std.windows.syserror; import std.intrinsic; import std.stream; extern(Windows) { struct COMMTIMEOUTS { DWORD ReadIntervalTimeout; /* Maximum time between read chars. */ DWORD ReadTotalTimeoutMultiplier; /* Multiplier of characters. */ DWORD ReadTotalTimeoutConstant; /* Constant in milliseconds. */ DWORD WriteTotalTimeoutMultiplier; /* Multiplier of characters. */ DWORD WriteTotalTimeoutConstant; /* Constant in milliseconds. */ } alias COMMTIMEOUTS* LPCOMMTIMEOUTS; struct DCB { DWORD DCBlength; /* sizeof(DCB) */ DWORD BaudRate; /* Baudrate at which running */ uint BITS; //bit fBinary; /* Binary Mode (skip EOF check) */ //bit fParity; /* Enable parity checking */ //bit fOutxCtsFlow; /* CTS handshaking on output */ //bit fOutxDsrFlow; /* DSR handshaking on output */ //bit[2] fDtrControl; /* DTR Flow control */ //bit fDsrSensitivity; /* DSR Sensitivity */ //bit fTXContinueOnXoff; /* Continue TX when Xoff sent */ //bit fOutX; /* Enable output X-ON/X-OFF */ //bit fInX; /* Enable input X-ON/X-OFF */ //bit fErrorChar; /* Enable Err Replacement */ //bit fNull; /* Enable Null stripping */ //bit[2] fRtsControl; /* Rts Flow control */ //bit fAbortOnError; /* Abort all reads and writes on Error */ //bit[17] fDummy2; /* Reserved */ WORD wReserved; /* Not currently used */ WORD XonLim; /* Transmit X-ON threshold */ WORD XoffLim; /* Transmit X-OFF threshold */ BYTE ByteSize; /* Number of bits/byte, 4-8 */ BYTE Parity; /* 0-4=None,Odd,Even,Mark,Space */ BYTE StopBits; /* 0,1,2 = 1, 1.5, 2 */ char XonChar; /* Tx and Rx X-ON character */ char XoffChar; /* Tx and Rx X-OFF character */ char ErrorChar; /* Error replacement char */ char EofChar; /* End of Input character */ char EvtChar; /* Received Event character */ WORD wReserved1; /* Fill for now. */ } alias DCB* LPDCB; void fBinary(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,0) : btr(&dcb.BITS,0); } void fParity(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,1) : btr(&dcb.BITS,1); } void fOutxCtsFlow(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,2) : btr(&dcb.BITS,2); } void fOutxDsrFlow(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,3) : btr(&dcb.BITS,3); } void fDtrControl(DCB* dcb, uint b) { (bt(&b,0)) ? bts(&dcb.BITS,4) : btr(&dcb.BITS,4); (bt(&b,1)) ? bts(&dcb.BITS,5) : btr(&dcb.BITS,5); } void fDsrSensitivity(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,6) : btr(&dcb.BITS,6); } void fTXContinueOnXoff(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,7) : btr(&dcb.BITS,7); } void fOutX(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,8) : btr(&dcb.BITS,8); } void fInX(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,9) : btr(&dcb.BITS,9); } void fErrorChar(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,10) : btr(&dcb.BITS,10); } void fNull(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,11) : btr(&dcb.BITS,11); } void fRtsControl(DCB* dcb, uint b) { (bt(&b,0)) ? bts(&dcb.BITS,12) : btr(&dcb.BITS,12); (bt(&b,1)) ? bts(&dcb.BITS,13) : btr(&dcb.BITS,13); } void fAbortOnError(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,14) : btr(&dcb.BITS,14); } const int CBR_110 = 110; const int CBR_300 = 300; const int CBR_600 = 600; const int CBR_1200 = 1200; const int CBR_2400 = 2400; const int CBR_4800 = 4800; const int CBR_9600 = 9600; const int CBR_14400 = 14400; const int CBR_19200 = 19200; const int CBR_38400 = 38400; const int CBR_56000 = 56000; const int CBR_57600 = 57600; const int CBR_115200 = 115200; const int CBR_128000 = 128000; const int CBR_256000 = 256000; const int MAXDWORD = 0xffffffff; int GetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts); int SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts); int GetCommState(HANDLE hFile, LPDCB lpDCB); int SetCommState(HANDLE hFile, LPDCB lpDCB); const BYTE ONESTOPBIT = 0; const BYTE ONE5STOPBITS = 1; const BYTE TWOSTOPBITS = 2; const BYTE NOPARITY = 0; const BYTE ODDPARITY = 1; const BYTE EVENPARITY = 2; const BYTE MARKPARITY = 3; const BYTE SPACEPARITY = 4; const ubyte DTR_CONTROL_DISABLE = 0x00; const ubyte DTR_CONTROL_ENABLE = 0x01; const ubyte DTR_CONTROL_HANDSHAKE = 0x02; const ubyte RTS_CONTROL_DISABLE = 0x00; const ubyte RTS_CONTROL_ENABLE = 0x01; const ubyte RTS_CONTROL_HANDSHAKE = 0x02; const ubyte RTS_CONTROL_TOGGLE = 0x03; } enum BaudRate { B110, B300, B600, B1200, B2400, B4800, B9600, B19200, B38400, B57600, B115200 } private static int BaudRates[BaudRate.B115200+1] = [ CBR_110, CBR_300, CBR_600, CBR_1200, CBR_2400, CBR_4800, CBR_9600, CBR_19200, CBR_38400,CBR_57600,CBR_115200 ]; class ComException : Exception { this(char[] msg) { super(msg ~ " {" ~ sysErrorString(GetLastError()) ~ "}"); } } class ComStream : Stream { this() { setState(false); } void open(char[] port, BaudRate baud) { handle = CreateFileA(toStringz(port),GENERIC_READ|GENERIC_WRITE,0,null,OPEN_EXISTING,0,null); if (handle == INVALID_HANDLE_VALUE) throw new ComException("Failed to open"); if (!GetCommTimeouts(handle,&curTimeouts)) throw new ComException("Failed to get timeouts"); if (!SetCommTimeouts(handle,&reqTimeouts)) throw new ComException("Failed to set timeouts"); if (!GetCommState(handle,&dcb)) throw new ComException("Failed to get state"); fParity(&dcb,0); dcb.Parity = NOPARITY; dcb.BaudRate = BaudRates[baud]; dcb.StopBits = ONESTOPBIT; dcb.ByteSize = 8; fDtrControl(&dcb,DTR_CONTROL_ENABLE); fRtsControl(&dcb,RTS_CONTROL_ENABLE); fTXContinueOnXoff(&dcb,1); fAbortOnError(&dcb,1); dcb.XonLim = 2048; dcb.XoffLim = 512; dcb.XonChar = 17; dcb.XoffChar = 19; fDsrSensitivity(&dcb,0); fOutxCtsFlow(&dcb,0); fOutxDsrFlow(&dcb,0); fOutX(&dcb,0); if (!SetCommState(handle,&dcb)) throw new ComException("Failed to set state"); } void close() { if (handle == INVALID_HANDLE_VALUE) return ; SetCommTimeouts(handle,&curTimeouts); CloseHandle(handle); handle = INVALID_HANDLE_VALUE; } override size_t readBlock(void* result, size_t len) { size_t bytes = 0; while(true) { if (ReadFile(handle,result,len,&bytes,null) == 0) throw new ComException("Failed to read"); if (bytes) break; Sleep(100); } return bytes; } override size_t writeBlock(void* result, size_t len) { size_t bytes = 0; size_t done = 0; for(; done < len; done += bytes) { if (WriteFile(handle,result+done,len-done,&bytes,null) == 0) throw new ComException("Failed to write"); } return done; } override ulong seek(long offset, SeekPos whence) { assertSeekable(); } private: static COMMTIMEOUTS reqTimeouts = {MAXDWORD,0,0,0,0}; HANDLE handle = INVALID_HANDLE_VALUE; COMMTIMEOUTS curTimeouts; DCB dcb; void setState(bool open) { isopen = open; readable = open; writeable = open; seekable = false; readEOF = false; prevCr = false; } } ------------Nf6FVE3JdOPfLFySk0O8y4--
Oct 24 2005
next sibling parent reply Tiago Gasiba <tiago.gasiba gmail.com> writes:
Hi Regan,


Regan Heath schrieb:

 I have also written COM code on linux and freebsd and could probably add
 support for these two to the attached source, let me know if you're
 interested.

I'm following this thread and, although I'm not imidiatly interested in COM support for Linux, maybe in the future it will come in handy... Could you post the code for Linux also? Thanks! Tiago -- Tiago Gasiba (MSc.) - http://www.gasiba.de Everything should be made as simple as possible, but not simpler.
Oct 26 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
------------giX2XrjLXjG0aCx8HYXGnn
Content-Type: text/plain; format=flowed; delsp=yes; charset=iso-8859-15
Content-Transfer-Encoding: 8bit

On Wed, 26 Oct 2005 09:30:19 +0200, Tiago Gasiba <tiago.gasiba gmail.com>  
wrote:
 I have also written COM code on linux and freebsd and could probably add
 support for these two to the attached source, let me know if you're
 interested.

I'm following this thread and, although I'm not imidiatly interested in COM support for Linux, maybe in the future it will come in handy... Could you post the code for Linux also? Thanks!

All right, here goes! I have modified the previously posted com.d file, it now contains an 'else' block which should hopefully work for linux, freebsd, and even macosx (maybe, not sure if I ever got it working there). I haven't got any of those machines to compile upon and chances are I have missed some symbol or import that is required. I wasn't really sure what to do with "#if defined(M_ELF)" so I just put it in a version block. Same with "#if defined(_POSIX_SOURCE) || defined(_XOPEN_SOURCE)" (note: this is a pain to translate into D version blocks, when are we going to get version statements that allow || ?!?) I added a 'canRead' function to both implementations, the linux (etc) one may need some error checking added for the 'select' call. The basic idea with read/write in this class is that it will block until it completes, but the underlying device is placed in a non-blocking mode. What this means is that the 'read' and 'ReadFile' functions return immediately whether they read or not, thus the 'while' loop to keep trying till you get something. This source is intended for public domain. Regan ------------giX2XrjLXjG0aCx8HYXGnn Content-Disposition: attachment; filename=com.d Content-Type: application/octet-stream; name=com.d Content-Transfer-Encoding: 8bit module com; private import std.c.time; private import std.intrinsic; import std.stream; enum BaudRate { B110, B300, B600, B1200, B2400, B4800, B9600, B19200, B38400, B57600, B115200 } class ComStreamBase : Stream { this() { setState(false); } override ulong seek(long offset, SeekPos whence) { assertSeekable(); } protected: void setState(bool open) { isopen = open; readable = open; writeable = open; seekable = false; readEOF = false; prevCr = false; } } version(Windows) { private import std.c.windows.windows; private import std.windows.syserror; extern(Windows) { struct COMMTIMEOUTS { DWORD ReadIntervalTimeout; /* Maximum time between read chars. */ DWORD ReadTotalTimeoutMultiplier; /* Multiplier of characters. */ DWORD ReadTotalTimeoutConstant; /* Constant in milliseconds. */ DWORD WriteTotalTimeoutMultiplier; /* Multiplier of characters. */ DWORD WriteTotalTimeoutConstant; /* Constant in milliseconds. */ } alias COMMTIMEOUTS* LPCOMMTIMEOUTS; struct DCB { DWORD DCBlength; /* sizeof(DCB) */ DWORD BaudRate; /* Baudrate at which running */ uint BITS; //bit fBinary; /* Binary Mode (skip EOF check) */ //bit fParity; /* Enable parity checking */ //bit fOutxCtsFlow; /* CTS handshaking on output */ //bit fOutxDsrFlow; /* DSR handshaking on output */ //bit[2] fDtrControl; /* DTR Flow control */ //bit fDsrSensitivity; /* DSR Sensitivity */ //bit fTXContinueOnXoff; /* Continue TX when Xoff sent */ //bit fOutX; /* Enable output X-ON/X-OFF */ //bit fInX; /* Enable input X-ON/X-OFF */ //bit fErrorChar; /* Enable Err Replacement */ //bit fNull; /* Enable Null stripping */ //bit[2] fRtsControl; /* Rts Flow control */ //bit fAbortOnError; /* Abort all reads and writes on Error */ //bit[17] fDummy2; /* Reserved */ WORD wReserved; /* Not currently used */ WORD XonLim; /* Transmit X-ON threshold */ WORD XoffLim; /* Transmit X-OFF threshold */ BYTE ByteSize; /* Number of bits/byte, 4-8 */ BYTE Parity; /* 0-4=None,Odd,Even,Mark,Space */ BYTE StopBits; /* 0,1,2 = 1, 1.5, 2 */ char XonChar; /* Tx and Rx X-ON character */ char XoffChar; /* Tx and Rx X-OFF character */ char ErrorChar; /* Error replacement char */ char EofChar; /* End of Input character */ char EvtChar; /* Received Event character */ WORD wReserved1; /* Fill for now. */ } alias DCB* LPDCB; void fBinary(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,0) : btr(&dcb.BITS,0); } void fParity(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,1) : btr(&dcb.BITS,1); } void fOutxCtsFlow(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,2) : btr(&dcb.BITS,2); } void fOutxDsrFlow(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,3) : btr(&dcb.BITS,3); } void fDtrControl(DCB* dcb, uint b) { (bt(&b,0)) ? bts(&dcb.BITS,4) : btr(&dcb.BITS,4); (bt(&b,1)) ? bts(&dcb.BITS,5) : btr(&dcb.BITS,5); } void fDsrSensitivity(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,6) : btr(&dcb.BITS,6); } void fTXContinueOnXoff(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,7) : btr(&dcb.BITS,7); } void fOutX(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,8) : btr(&dcb.BITS,8); } void fInX(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,9) : btr(&dcb.BITS,9); } void fErrorChar(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,10) : btr(&dcb.BITS,10); } void fNull(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,11) : btr(&dcb.BITS,11); } void fRtsControl(DCB* dcb, uint b) { (bt(&b,0)) ? bts(&dcb.BITS,12) : btr(&dcb.BITS,12); (bt(&b,1)) ? bts(&dcb.BITS,13) : btr(&dcb.BITS,13); } void fAbortOnError(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,14) : btr(&dcb.BITS,14); } const int CBR_110 = 110; const int CBR_300 = 300; const int CBR_600 = 600; const int CBR_1200 = 1200; const int CBR_2400 = 2400; const int CBR_4800 = 4800; const int CBR_9600 = 9600; const int CBR_14400 = 14400; const int CBR_19200 = 19200; const int CBR_38400 = 38400; const int CBR_56000 = 56000; const int CBR_57600 = 57600; const int CBR_115200 = 115200; const int CBR_128000 = 128000; const int CBR_256000 = 256000; const int MAXDWORD = 0xffffffff; int GetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts); int SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts); int GetCommState(HANDLE hFile, LPDCB lpDCB); int SetCommState(HANDLE hFile, LPDCB lpDCB); int WaitCommEvent(HANDLE hFile, LPDWORD lpEvtMask, OVERLAPPED *lpOverlapped); const BYTE ONESTOPBIT = 0; const BYTE ONE5STOPBITS = 1; const BYTE TWOSTOPBITS = 2; const BYTE NOPARITY = 0; const BYTE ODDPARITY = 1; const BYTE EVENPARITY = 2; const BYTE MARKPARITY = 3; const BYTE SPACEPARITY = 4; const ubyte DTR_CONTROL_DISABLE = 0x00; const ubyte DTR_CONTROL_ENABLE = 0x01; const ubyte DTR_CONTROL_HANDSHAKE = 0x02; const ubyte RTS_CONTROL_DISABLE = 0x00; const ubyte RTS_CONTROL_ENABLE = 0x01; const ubyte RTS_CONTROL_HANDSHAKE = 0x02; const ubyte RTS_CONTROL_TOGGLE = 0x03; } private static int BaudRates[BaudRate.B115200+1] = [ CBR_110, CBR_300, CBR_600, CBR_1200, CBR_2400, CBR_4800, CBR_9600, CBR_19200, CBR_38400,CBR_57600,CBR_115200 ]; class ComException : Exception { this(char[] msg) { super(msg ~ " {" ~ sysErrorString(GetLastError()) ~ "}"); } } class ComStream : ComStreamBase { void open(char[] port, BaudRate baud) { handle = CreateFileA(toStringz(port),GENERIC_READ|GENERIC_WRITE,0,null,OPEN_EXISTING,0,null); if (handle == INVALID_HANDLE_VALUE) throw new ComException("Failed to open"); if (!GetCommTimeouts(handle,&curTimeouts)) throw new ComException("Failed to get timeouts"); if (!SetCommTimeouts(handle,&reqTimeouts)) throw new ComException("Failed to set timeouts"); if (!GetCommState(handle,&dcb)) throw new ComException("Failed to get state"); fParity(&dcb,0); dcb.Parity = NOPARITY; dcb.BaudRate = BaudRates[baud]; dcb.StopBits = ONESTOPBIT; dcb.ByteSize = 8; fDtrControl(&dcb,DTR_CONTROL_ENABLE); fRtsControl(&dcb,RTS_CONTROL_ENABLE); fTXContinueOnXoff(&dcb,1); fAbortOnError(&dcb,1); dcb.XonLim = 2048; dcb.XoffLim = 512; dcb.XonChar = 17; dcb.XoffChar = 19; fDsrSensitivity(&dcb,0); fOutxCtsFlow(&dcb,0); fOutxDsrFlow(&dcb,0); fOutX(&dcb,0); if (!SetCommState(handle,&dcb)) throw new ComException("Failed to set state"); setState(true); } void close() { if (handle == INVALID_HANDLE_VALUE) return ; SetCommTimeouts(handle,&curTimeouts); CloseHandle(handle); handle = INVALID_HANDLE_VALUE; setState(false); } bool canRead() { DWORD Mask; assertReadable(); if (!WaitCommEvent(handle,&Mask,null)) return false; return true; } override size_t readBlock(void* result, size_t len) { size_t bytes = 0; assertReadable(); while(true) { if (ReadFile(handle,result,len,&bytes,null) == 0) throw new ComException("Failed to read"); if (bytes) break; msleep(100); } return bytes; } override size_t writeBlock(void* result, size_t len) { size_t bytes = 0; size_t done = 0; assertWriteable(); for(; done < len; done += bytes) { if (WriteFile(handle,result+done,len-done,&bytes,null) == 0) throw new ComException("Failed to write"); } return done; } private: static COMMTIMEOUTS reqTimeouts = {MAXDWORD,0,0,0,0}; HANDLE handle = INVALID_HANDLE_VALUE; COMMTIMEOUTS curTimeouts; DCB dcb; } } else { private import std.c.linux.linux; private import std.c.linux.socket; private import std.c.stdlib; private import std.socket; extern(C) { const int NCCS = 19; typedef ubyte cc_t; typedef uint speed_t; //#if defined(M_ELF) version(M_ELF) { typedef uint tcflag_t; } else { typedef ushort tcflag_t; } struct termios { tcflag_t c_iflag; tcflag_t c_oflag; tcflag_t c_cflag; tcflag_t c_lflag; cc_t c_cc[NCCS]; }; //#if defined(_POSIX_SOURCE) || defined(_XOPEN_SOURCE) version(posix) version = posix_and_open; version(open_source) version = posix_and_open; version(posix_and_open) { int XIOC = (('i'<<24) | ('X'<<16)); } else { int XIOC = ('x'<<8); } int XCGETA = (XIOC|1); int XCSETA = (XIOC|2); int XCSETAW = (XIOC|3); int XCSETAF = (XIOC|4); version(M_ELF) { int TCSANOW = (14|('T'<<8)); int TCSADRAIN = (15|('T'<<8)); int TCSAFLUSH = (16|('T'<<8)); } else { alias TCSANOW XCSETA; alias TCSADRAIN XCSETAW; alias TCSAFLUSH XCSETAF; alias TCSADFLUSH XCSETAF; } const int EAGAIN = 11; int CS5 = 0; int CS6 = 0x10; int CS7 = 0x20; int CS8 = 0x30; int CSIZE = 0x30; int CSTOPB = 0x40; int CREAD = 0x80; int PARENB = 0x100; int PARODD = 0x200; int HUPCL = 0x400; int CLOCAL = 0x800; } private static int BaudRates[BaudRate.B115200+1] = [ B110, B300, B600, B1200, B2400, B4800, B9600, B19200, B38400,B57600,B115200 ]; class ComException : Exception { this(char[] msg) { super(msg ~ " {" ~ strerror(getErrno()) ~ "}"); } } version(freebsd) version = freebsd_and_osx; version(osx) version = freebsd_and_osx; class ComStream : ComStreamBase { void open(char[] port, BaudRate baud) { file = open(port,O_RDWR|O_NONBLOCK); if(file == -1) throw new ComException("Failed to open"); if (tcgetattr(file,&attr) != 0) throw new ComException("Failed to get attributes"); version(freebsd_and_osx) { if (cfsetispeed(&attr,BaudRates[baud]) != 0) throw new ComException("Failed to set output speed"); if (cfsetospeed(&attr,BaudRates[baud]) != 0) throw new ComException("Failed to set input speed"); attr.c_cflag = (CS8 | CREAD | CLOCAL); } else { attr.c_cflag = (CS8 | CREAD | CLOCAL | BaudRates[baud]); } attr.c_iflag = 0; attr.c_oflag = 0; attr.c_lflag = 0; if (tcsetattr(file,TCSANOW,&attr) != 0) throw new ComException("Failed to set attributes"); setState(true); } void close() { if (file == -1) return ; close(file); file = -1; setState(false); } bool canRead() { fd_set readfd; assertReadable(); FD_ZERO(&readfd); FD_SET(file,&readfd); select(file,&readfd,null,null,null); return FD_ISSET(file,&readfd); } override size_t readBlock(void* result, size_t len) { int bytes; assertReadable(); while(true) { bytes = read(file,result,len); if (bytes == -1) { if (getErrno() == EAGAIN) bytes = 0; else throw new ComException("Failed to read"); } if (bytes) break; msleep(100); } return bytes; } override size_t writeBlock(void* result, size_t len) { int done = 0; int bytes; assertWriteable(); for(; done < len; done += bytes) { bytes = write(file,result,len); if (bytes == -1) { if (getErrno() == EAGAIN) bytes = 0; else throw new ComException("Failed to write"); } } return done; } private: int file = -1; termios attr; } } ------------giX2XrjLXjG0aCx8HYXGnn--
Oct 26 2005
parent reply Tiago Gasiba <tiago.gasiba gmail.com> writes:
Regan Heath schrieb:

 
 I haven't got any of those machines to compile upon and chances are I have
 missed some symbol or import that is required.

I also think so... :) Lets see, compiling with "dmd -c com.d" I get: ----- com.d(295): alias com.XCSETA conflicts with com.XCSETA at com.d(285) com.d(296): alias com.XCSETAW conflicts with com.XCSETAW at com.d(286) com.d(297): alias com.XCSETAF conflicts with com.XCSETAF at com.d(287) com.d(298): alias com.XCSETAF conflicts with com.XCSETAF at com.d(287) /opt/dmd/src/phobos/std/stream.d(2593): TArrayStream!(ubyte[]) is used as a type /opt/dmd/src/phobos/std/stream.d(2593): class std.stream.MemoryStream base type must be class or interface, not void /opt/dmd/src/phobos/std/stream.d(2613): function std.stream.MemoryStream.writeBlock function writeBlock does not override any /opt/dmd/src/phobos/std/stream.d(2670): TArrayStream!(MmFile) is used as a type /opt/dmd/src/phobos/std/stream.d(2670): class std.stream.MmFileStream base type must be class or interface, not void /opt/dmd/src/phobos/std/stream.d(2679): function std.stream.MmFileStream.flush function flush does not override any /opt/dmd/src/phobos/std/stream.d(2686): function std.stream.MmFileStream.close function close does not override any com.d(295): identifier 'TCSANOW' is not defined com.d(295): TCSANOW is used as a type com.d(296): identifier 'TCSADRAIN' is not defined com.d(296): TCSADRAIN is used as a type com.d(297): identifier 'TCSAFLUSH' is not defined com.d(297): TCSAFLUSH is used as a type com.d(298): identifier 'TCSADFLUSH' is not defined com.d(298): TCSADFLUSH is used as a type ----- And compiling with "dmd -c -version=M_ELF com.d" I get: ----- com.d(318): undefined identifier B110 com.d(318): undefined identifier B300 com.d(318): undefined identifier B600 com.d(318): undefined identifier B1200 com.d(319): undefined identifier B2400 com.d(319): undefined identifier B4800 com.d(319): undefined identifier B9600 com.d(319): undefined identifier B19200 com.d(320): undefined identifier B38400 com.d(320): undefined identifier B57600 com.d(320): undefined identifier B115200 ----- I rewrite part of your code like: (approx line 318) private static int BaudRates[BaudRate.B115200+1] = [ BaudRate.B110, BaudRate.B300, BaudRate.B600, BaudRate.B1200, BaudRate.B2400, BaudRate.B4800, BaudRate.B9600, BaudRate.B19200, BaudRate.B38400,BaudRate.B57600,BaudRate.B115200 ]; Compiling with "dmd -c -version=M_ELF com.d" I then get: ----- com.d(327): undefined identifier strerror com.d(327): function expected before (), not strerror of type int com.d(327): incompatible types for ((msg ~ " {") ~ (strerror(getErrno()))): 'char[]' and 'int' com.d(327): Can only concatenate arrays, not (char[] ~ int) com.d(327): incompatible types for ((msg ~ " {" ~ strerror(getErrno())) ~ ("}")): 'int' and 'char[1]' com.d(327): Can only concatenate arrays, not (int ~ char[1]) com.d(327): constructor object.Exception.this (char[]) does not match argument types (int) com.d(327): cannot implicitly convert expression (msg ~ " {" ~ strerror(getErrno()) ~ "}") of type int to char[] com.d(338): voids have no value com.d(338): cannot implicitly convert expression (this.open(port,cast(BaudRate)2050)) of type void to int com.d(341): undefined identifier tcgetattr com.d(341): function expected before (), not tcgetattr of type int com.d(350): cannot implicitly convert expression (CS8 | CREAD | CLOCAL | BaudRates[baud]) of type int to tcflag_t com.d(357): undefined identifier tcsetattr com.d(357): function expected before (), not tcsetattr of type int com.d(365): function com.ComStream.close () does not match argument types (int) com.d(365): Error: expected 0 arguments, not 1 com.d(378): cannot implicitly convert expression (FD_ISSET(this.file,&readfd)) of type int to bit com.d(387): function std.stream.Stream.read (ubyte[]) does not match argument types (int,void*,uint) com.d(387): Error: expected 1 arguments, not 3 com.d(387): cannot implicitly convert expression (this.file) of type int to wchar[] com.d(387): cast(wchar[])(this.file) is not an lvalue com.d(387): voids have no value com.d(387): cannot implicitly convert expression (cast(Stream)(this).read(cast(wchar[])(this.file),result,len)) of type void to int com.d(406): function std.stream.Stream.write (ubyte[]) does not match argument types (int,void*,uint) com.d(406): Error: expected 1 arguments, not 3 com.d(406): cannot implicitly convert expression (this.file) of type int to wchar[] com.d(406): voids have no value com.d(406): cannot implicitly convert expression (cast(Stream)(this).write(cast(wchar[])(this.file),result,len)) of type void to int ----- Oops... something is very wrong here :( Tried to quickly fix the code but no success... Also did not find "tcgetattr" defined in D anywhere. Best, Tiago -- Tiago Gasiba (MSc.) - http://www.gasiba.de Everything should be made as simple as possible, but not simpler.
Oct 26 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 26 Oct 2005 13:22:52 +0200, Tiago Gasiba <tiago.gasiba gmail.com>  
wrote:
 Regan Heath schrieb:

 I haven't got any of those machines to compile upon and chances are I  
 have
 missed some symbol or import that is required.

I also think so... :) Lets see, compiling with "dmd -c com.d" I get: ----- com.d(295): alias com.XCSETA conflicts with com.XCSETA at com.d(285) com.d(296): alias com.XCSETAW conflicts with com.XCSETAW at com.d(286) com.d(297): alias com.XCSETAF conflicts with com.XCSETAF at com.d(287) com.d(298): alias com.XCSETAF conflicts with com.XCSETAF at com.d(287)

Oops. alias lines are backwards, try: alias XCSETA TCSANOW; alias XCSETAW TCSADRAIN; alias XCSETAF TCSAFLUSH; alias XCSETAF TCSADFLUSH;
 /opt/dmd/src/phobos/std/stream.d(2593): TArrayStream!(ubyte[]) is used  
 as a type
 /opt/dmd/src/phobos/std/stream.d(2593): class std.stream.MemoryStream  
 base type must be class or interface, not void
 /opt/dmd/src/phobos/std/stream.d(2613): function  
 std.stream.MemoryStream.writeBlock function writeBlock does not override  
 any
 /opt/dmd/src/phobos/std/stream.d(2670): TArrayStream!(MmFile) is used as  
 a type
 /opt/dmd/src/phobos/std/stream.d(2670): class std.stream.MmFileStream  
 base type must be class or interface, not void
 /opt/dmd/src/phobos/std/stream.d(2679): function  
 std.stream.MmFileStream.flush function flush does not override any
 /opt/dmd/src/phobos/std/stream.d(2686): function  
 std.stream.MmFileStream.close function close does not override any

Not sure what that's on about.
 com.d(295): identifier 'TCSANOW' is not defined
 com.d(295): TCSANOW is used as a type
 com.d(296): identifier 'TCSADRAIN' is not defined
 com.d(296): TCSADRAIN is used as a type
 com.d(297): identifier 'TCSAFLUSH' is not defined
 com.d(297): TCSAFLUSH is used as a type
 com.d(298): identifier 'TCSADFLUSH' is not defined
 com.d(298): TCSADFLUSH is used as a type

should be fixed by alias lines.
 -----

 And compiling with "dmd -c -version=M_ELF com.d" I get:
 -----
 com.d(318): undefined identifier B110
 com.d(318): undefined identifier B300
 com.d(318): undefined identifier B600
 com.d(318): undefined identifier B1200
 com.d(319): undefined identifier B2400
 com.d(319): undefined identifier B4800
 com.d(319): undefined identifier B9600
 com.d(319): undefined identifier B19200
 com.d(320): undefined identifier B38400
 com.d(320): undefined identifier B57600
 com.d(320): undefined identifier B115200
 -----

 I rewrite part of your code like:

 (approx   line 318)
  private static int BaudRates[BaudRate.B115200+1] = [
     BaudRate.B110,  BaudRate.B300,  BaudRate.B600,  BaudRate.B1200,
     BaudRate.B2400, BaudRate.B4800, BaudRate.B9600, BaudRate.B19200,
     BaudRate.B38400,BaudRate.B57600,BaudRate.B115200
   ];

This is wrong. This array translates BaudRate values into the OS values for baud rate, i.e. B110 etc. They should be defined in a header somewhere... (I'll repost when I have fixed it)
 Compiling with "dmd -c -version=M_ELF com.d" I then get:
 -----
 com.d(327): undefined identifier strerror

Add "char* strerror(int);" to extern C block. <snip>
 Oops... something is very wrong here :(
 Tried to quickly fix the code but no success...

I'll fix these and repost.
 Also did not find "tcgetattr" defined in D anywhere.

Yep. Need to add that def too. Regan
Oct 26 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
------------gJZNCZ1ESHaZaiazcBvRDb
Content-Type: text/plain; format=flowed; delsp=yes; charset=iso-8859-15
Content-Transfer-Encoding: 8bit

Ok, here is my latest version.

I can compile it on a ubuntu linux vmware virtual machine but it wont run,  
it errors on the "tcsetattr" call. How about you?

Regan
------------gJZNCZ1ESHaZaiazcBvRDb
Content-Disposition: attachment; filename=com.d
Content-Type: application/octet-stream; name=com.d
Content-Transfer-Encoding: 8bit

module com;

private import std.c.time;
private import std.intrinsic;
import std.stream;

enum BaudRate {
	B110, B300, B600, B1200, B2400, B4800, B9600, B19200, B38400
	//, B57600, B115200
}

class ComStreamBase : Stream
{
	this()
	{
		setState(false);
	}
	
	override ulong seek(long offset, SeekPos whence)
	{
		assertSeekable();
	}
protected:
	void setState(bool open)
	{
		isopen = open;
		readable = open;
		writeable = open;		

		seekable = false;
		readEOF = false;
		prevCr = false;		
	}	
}

version(Windows) {
	private import std.c.windows.windows;
	private import std.windows.syserror;

	extern(Windows) {
		struct COMMTIMEOUTS {
		    DWORD ReadIntervalTimeout;          /* Maximum time between read chars. */
		    DWORD ReadTotalTimeoutMultiplier;   /* Multiplier of characters.        */
		    DWORD ReadTotalTimeoutConstant;     /* Constant in milliseconds.        */
		    DWORD WriteTotalTimeoutMultiplier;  /* Multiplier of characters.        */
		    DWORD WriteTotalTimeoutConstant;    /* Constant in milliseconds.        */
		}
		alias COMMTIMEOUTS* LPCOMMTIMEOUTS;

		struct DCB {
		    DWORD DCBlength;       /* sizeof(DCB)                     */
		    DWORD BaudRate;        /* Baudrate at which running       */
		    uint BITS;
		    //bit fBinary;           /* Binary Mode (skip EOF check)    */
		    //bit fParity;           /* Enable parity checking          */
		    //bit fOutxCtsFlow;      /* CTS handshaking on output       */
		    //bit fOutxDsrFlow;      /* DSR handshaking on output       */
		    //bit[2] fDtrControl;    /* DTR Flow control                */
		    //bit fDsrSensitivity;   /* DSR Sensitivity              */
		    //bit fTXContinueOnXoff; /* Continue TX when Xoff sent */
		    //bit fOutX;             /* Enable output X-ON/X-OFF        */
		    //bit fInX;              /* Enable input X-ON/X-OFF         */
		    //bit fErrorChar;        /* Enable Err Replacement          */
		    //bit fNull;             /* Enable Null stripping           */
		    //bit[2] fRtsControl;    /* Rts Flow control                */
		    //bit fAbortOnError;     /* Abort all reads and writes on Error */
		    //bit[17] fDummy2;       /* Reserved                        */
		    WORD wReserved;        /* Not currently used              */
		    WORD XonLim;           /* Transmit X-ON threshold         */
		    WORD XoffLim;          /* Transmit X-OFF threshold        */
		    BYTE ByteSize;         /* Number of bits/byte, 4-8        */
		    BYTE Parity;           /* 0-4=None,Odd,Even,Mark,Space    */
		    BYTE StopBits;         /* 0,1,2 = 1, 1.5, 2               */
		    char XonChar;          /* Tx and Rx X-ON character        */
		    char XoffChar;         /* Tx and Rx X-OFF character       */
		    char ErrorChar;        /* Error replacement char          */
		    char EofChar;          /* End of Input character          */
		    char EvtChar;          /* Received Event character        */
		    WORD wReserved1;       /* Fill for now.                   */
		}
		alias DCB* LPDCB;

		void fBinary(DCB* dcb, uint b)           { (b) ? bts(&dcb.BITS,0) :
btr(&dcb.BITS,0); }	
		void fParity(DCB* dcb, uint b)           { (b) ? bts(&dcb.BITS,1) :
btr(&dcb.BITS,1); }
		void fOutxCtsFlow(DCB* dcb, uint b)      { (b) ? bts(&dcb.BITS,2) :
btr(&dcb.BITS,2); }
		void fOutxDsrFlow(DCB* dcb, uint b)      { (b) ? bts(&dcb.BITS,3) :
btr(&dcb.BITS,3); }
		void fDtrControl(DCB* dcb, uint b)       { (bt(&b,0)) ? bts(&dcb.BITS,4) :
btr(&dcb.BITS,4); (bt(&b,1)) ? bts(&dcb.BITS,5) : btr(&dcb.BITS,5); }
		void fDsrSensitivity(DCB* dcb, uint b)   { (b) ? bts(&dcb.BITS,6) :
btr(&dcb.BITS,6); }
		void fTXContinueOnXoff(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,7) :
btr(&dcb.BITS,7); }
		void fOutX(DCB* dcb, uint b)             { (b) ? bts(&dcb.BITS,8) :
btr(&dcb.BITS,8); }
		void fInX(DCB* dcb, uint b)              { (b) ? bts(&dcb.BITS,9) :
btr(&dcb.BITS,9); }
		void fErrorChar(DCB* dcb, uint b)        { (b) ? bts(&dcb.BITS,10) :
btr(&dcb.BITS,10); }
		void fNull(DCB* dcb, uint b)             { (b) ? bts(&dcb.BITS,11) :
btr(&dcb.BITS,11); }
		void fRtsControl(DCB* dcb, uint b)       { (bt(&b,0)) ? bts(&dcb.BITS,12) :
btr(&dcb.BITS,12); (bt(&b,1)) ? bts(&dcb.BITS,13) : btr(&dcb.BITS,13); }
		void fAbortOnError(DCB* dcb, uint b)     { (b) ? bts(&dcb.BITS,14) :
btr(&dcb.BITS,14); }

		const int CBR_110             = 110;
		const int CBR_300             = 300;
		const int CBR_600             = 600;
		const int CBR_1200            = 1200;
		const int CBR_2400            = 2400;
		const int CBR_4800            = 4800;
		const int CBR_9600            = 9600;
		const int CBR_14400           = 14400;
		const int CBR_19200           = 19200;
		const int CBR_38400           = 38400;
		const int CBR_56000           = 56000;
		const int CBR_57600           = 57600;
		const int CBR_115200          = 115200;
		const int CBR_128000          = 128000;
		const int CBR_256000          = 256000;

		const int MAXDWORD    = 0xffffffff;

		int GetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts);
		int SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts);

		int GetCommState(HANDLE hFile, LPDCB lpDCB);
		int SetCommState(HANDLE hFile, LPDCB lpDCB);
		
		int WaitCommEvent(HANDLE hFile, LPDWORD lpEvtMask, OVERLAPPED *lpOverlapped);

		const BYTE ONESTOPBIT          = 0;
		const BYTE ONE5STOPBITS        = 1;
		const BYTE TWOSTOPBITS         = 2;

		const BYTE NOPARITY            = 0;
		const BYTE ODDPARITY           = 1;
		const BYTE EVENPARITY          = 2;
		const BYTE MARKPARITY          = 3;
		const BYTE SPACEPARITY         = 4;

		const ubyte DTR_CONTROL_DISABLE    = 0x00;
		const ubyte DTR_CONTROL_ENABLE     = 0x01;
		const ubyte DTR_CONTROL_HANDSHAKE  = 0x02;

		const ubyte RTS_CONTROL_DISABLE    = 0x00;
		const ubyte RTS_CONTROL_ENABLE     = 0x01;
		const ubyte RTS_CONTROL_HANDSHAKE  = 0x02;
		const ubyte RTS_CONTROL_TOGGLE     = 0x03;
	}

	private static int BaudRates[BaudRate.B38400+1] = [
		CBR_110,  CBR_300,  CBR_600,  CBR_1200,
		CBR_2400, CBR_4800, CBR_9600, CBR_19200,
		CBR_38400
		//,CBR_57600,CBR_115200
	];

	class ComException : Exception
	{
		this(char[] msg)
		{
			super(msg ~ " {" ~ sysErrorString(GetLastError()) ~ "}");
		}
	}

	class ComStream : ComStreamBase
	{
		void open(char[] port, BaudRate baud)
		{
			handle = CreateFileA(toStringz(port),GENERIC_READ|GENERIC_WRITE,0,null,OPEN_EXISTING,0,null);
			if (handle == INVALID_HANDLE_VALUE) throw new ComException("Failed to open");

			if (!GetCommTimeouts(handle,&curTimeouts)) throw new ComException("Failed to
get timeouts");
			if (!SetCommTimeouts(handle,&reqTimeouts)) throw new ComException("Failed to
set timeouts");

			if (!GetCommState(handle,&dcb)) throw new ComException("Failed to get
state");

			fParity(&dcb,0);
			dcb.Parity = NOPARITY;

			dcb.BaudRate = BaudRates[baud];
			dcb.StopBits = ONESTOPBIT;
			dcb.ByteSize = 8;

			fDtrControl(&dcb,DTR_CONTROL_ENABLE);
			fRtsControl(&dcb,RTS_CONTROL_ENABLE);		

			fTXContinueOnXoff(&dcb,1);		
			fAbortOnError(&dcb,1);

			dcb.XonLim = 2048;
			dcb.XoffLim = 512;
			dcb.XonChar = 17;
			dcb.XoffChar = 19;

			fDsrSensitivity(&dcb,0);

			fOutxCtsFlow(&dcb,0);
			fOutxDsrFlow(&dcb,0);
			fOutX(&dcb,0);

			if (!SetCommState(handle,&dcb)) throw new ComException("Failed to set
state");
			
			setState(true);
		}

		void close()
		{
			if (handle == INVALID_HANDLE_VALUE) return ;
			SetCommTimeouts(handle,&curTimeouts);
			CloseHandle(handle);
			handle = INVALID_HANDLE_VALUE;
			setState(false);
		}
		
		bool canRead()
		{
			DWORD Mask;
			assertReadable();
			if (!WaitCommEvent(handle,&Mask,null)) return false;
			return true;
		}

		override size_t readBlock(void* result, size_t len)
		{
			size_t bytes = 0;

			assertReadable();
			while(true) {
				if (ReadFile(handle,result,len,&bytes,null) == 0)
					throw new ComException("Failed to read");
				if (bytes) break;
				sleep(1);
			}
			return bytes;
		}

		override size_t writeBlock(void* result, size_t len)
		{
			size_t bytes = 0;
			size_t done = 0;

			assertWriteable();
			for(; done < len; done += bytes) {
				if (WriteFile(handle,result+done,len-done,&bytes,null) == 0)
					throw new ComException("Failed to write");
			}
			return done;
		}
	private:
		static COMMTIMEOUTS reqTimeouts = {MAXDWORD,0,0,0,0};
		HANDLE handle = INVALID_HANDLE_VALUE;
		COMMTIMEOUTS curTimeouts;
		DCB dcb;
	}

}
else {
	private import std.c.linux.linux;
	private import std.c.linux.socket;
	private import std.c.stdlib;
	private import std.socket;
	private import std.string;
	
	extern(C) {
		char* strerror(int);
		int ioctl(int, int, ...);
	
		const int NCCS = 19;
		
		typedef ubyte cc_t;
		typedef uint speed_t;
		
		//#if defined(M_ELF)
		version(M_ELF) { typedef uint tcflag_t; }
		else { typedef ushort tcflag_t; }
		
		struct termios
		{
			tcflag_t c_iflag;
			tcflag_t c_oflag;
			tcflag_t c_cflag;
			tcflag_t c_lflag;
			cc_t c_cc[NCCS];
		};
		
		//#if defined(_POSIX_SOURCE) || defined(_XOPEN_SOURCE)
		version(posix) version = posix_and_open;
		version(open_source) version = posix_and_open;		
		version(posix_and_open) {
			const uint XIOC = (('i'<<24) | ('X'<<16));
		}
		else {
			const uint XIOC = ('x'<<8);
		}

		const uint XCGETA  = (XIOC|1);
		const uint XCSETA  = (XIOC|2);
		const uint XCSETAW = (XIOC|3);
		const uint XCSETAF = (XIOC|4);
		
		version(M_ELF) {
			int TCSANOW   = (14|('T'<<8));
			int TCSADRAIN = (15|('T'<<8));
			int TCSAFLUSH = (16|('T'<<8));
		}
		else {
			alias XCSETA  TCSANOW;
			alias XCSETAW TCSADRAIN;
			alias XCSETAF TCSAFLUSH;
			alias XCSETAF TCSADFLUSH;
		}
		
		const uint TIOC		= ('T'<<8);
		const uint TCGETA	= (1|TIOC);
		const uint TCSETA	= (2|TIOC);
		const uint TCSETAW	= (3|TIOC);
		const uint TCSETAF	= (4|TIOC);
		const uint TCSBRK	= (5|TIOC);
		const uint TCXONC	= (6|TIOC);
		const uint TCFLSH	= (7|TIOC);
		
		const int EAGAIN = 11;
		
		const uint CS5    = 0;
		const uint CS6    = 0x10;
		const uint CS7    = 0x20;
		const uint CS8    = 0x30;
		const uint CSIZE  = 0x30;
		const uint CSTOPB = 0x40;
		const uint CREAD  = 0x80;
		const uint PARENB = 0x100;
		const uint PARODD = 0x200;
		const uint HUPCL  = 0x400;
		const uint CLOCAL = 0x800;
		
		//#if !__STDC__
		version(STDC) {}
		else {
			speed_t cfgetispeed (termios *);
			speed_t cfgetospeed (termios *);
			int cfsetispeed (termios *, speed_t);
			int cfsetospeed (termios *, speed_t);

			//#if !defined(_XOPEN_SOURCE) && !defined(_POSIX_SOURCE)
			version(posix_and_open) {}
			else {
				version(M_ELF) {
					const uint TCGETS  = (13|TIOC);
					const uint TCSETS  = (14|TIOC);

					int tcgetattr(int fd, termios *termios_p) { return ioctl(fd, TCGETS,
termios_p); }
				}
				else {
					int tcgetattr(int fd, termios *termios_p) { return ioctl(fd, XCGETA,
termios_p); }
				}

				int tcsetattr(int fd, int opt, termios *termios_p)	{ return ioctl(fd, opt,
termios_p); }
				int tcflow(int fd, int action)				{ return ioctl(fd, TCXONC, action); }
				int tcflush(int fd, int queue_select)			{ return ioctl(fd, TCFLSH,
queue_select); }
				int tcdrain(int fd)					{ return ioctl(fd, TCSBRK, 1); }
				int tcsendbreak(int fd, int duration)			{ return ioctl(fd, TCSBRK, 0,
duration); }
			}
		}
		
		const uint B110   = 0x3;
		const uint B300   = 0x7;
		const uint B600   = 0x8;
		const uint B1200  = 0x9;
		const uint B2400  = 0xB;
		const uint B4800  = 0xC;
		const uint B9600  = 0xD;
		const uint B19200 = 0xE;
		const uint B38400 = 0xF;
		//cant find the other 2
	}

	private static uint BaudRates[BaudRate.B38400+1] = [
		B110,  B300,  B600,  B1200,
		B2400, B4800, B9600, B19200,
		B38400
		//,B57600,B115200
	];
	
	class ComException : Exception
	{
		this(char[] msg)
		{
			char* e = strerror(getErrno());			
			super(msg ~ " {" ~ e[0..strlen(e)] ~ "}");
		}
	}

	class ComStream : ComStreamBase
	{
		void open(char[] port, BaudRate baud)
		{
			file = .open(toStringz(port),O_RDWR|O_NONBLOCK);
			if(file == -1) throw new ComException("Failed to open");			
			
			if (tcgetattr(file,&attr) != 0) throw new ComException("Failed to get
attributes");			
			
			if (cfsetispeed(&attr,cast(speed_t)BaudRates[baud]) != 0) throw new
ComException("Failed to set output speed");
			if (cfsetospeed(&attr,cast(speed_t)BaudRates[baud]) != 0) throw new
ComException("Failed to set input speed");
			attr.c_cflag = cast(tcflag_t)(CS8 | CREAD | CLOCAL);
			
			attr.c_iflag = 0;
			attr.c_oflag = 0;
			attr.c_lflag = 0;
			
			if (tcsetattr(file,TCSANOW,&attr) != 0) throw new ComException("Failed to
set attributes");
			
			setState(true);
		}
		
		void close()
		{
			if (file == -1) return ;
			.close(file);
			file = -1;
			setState(false);
		}
		
		bool canRead()
		{
			fd_set readfd;
			
			assertReadable();
			FD_ZERO(&readfd);
			FD_SET(file,&readfd);
			if (select(file,&readfd,null,null,null) == -1) throw new
ComException("Failed to select");
			return FD_ISSET(file,&readfd) == 1;
		}
		
		override size_t readBlock(void* result, size_t len)
		{
			int bytes;
			
			assertReadable();
			while(true) {
				bytes = .read(file,result,len);
				if (bytes == -1) {
					if (getErrno() == EAGAIN) bytes = 0;
					else throw new ComException("Failed to read");
				}
				if (bytes) break;
				sleep(1);
			}
					
			return bytes;
		}
		
		override size_t writeBlock(void* result, size_t len)
		{
			int done = 0;
			int bytes;			
			
			assertWriteable();			
			for(; done < len; done += bytes) {
				bytes = .write(file,result,len);
				if (bytes == -1) {
					if (getErrno() == EAGAIN) bytes = 0;
					else throw new ComException("Failed to write");
				}
			}
			
			return done;
		
		}
	private:
		int file = -1;
		termios attr;		
	}
}

------------gJZNCZ1ESHaZaiazcBvRDb--
Oct 27 2005
next sibling parent reply Tiago Gasiba <tiago.gasiba gmail.com> writes:
Regan Heath schrieb:

 Ok, here is my latest version.
 
 I can compile it on a ubuntu linux vmware virtual machine but it wont run,
 it errors on the "tcsetattr" call. How about you?
 
 Regan

Same here. The B57600,B115200 that you can not find are declared in termbits.h under /usr/include/asm. I give you a small <snip> of the file: <snip> #define B57600 0010001 #define B115200 0010002 #define B230400 0010003 #define B460800 0010004 #define B500000 0010005 #define B576000 0010006 #define B921600 0010007 #define B1000000 0010010 #define B1152000 0010011 #define B1500000 0010012 #define B2000000 0010013 #define B2500000 0010014 #define B3000000 0010015 #define B3500000 0010016 #define B4000000 0010017 <snip> Furthermore, I think that there is something wrong with your IOCTL. Lets see - I have downloaded and compiled the following program: http://gpsbots.com/tutorials/tut_linux_serial.php Compiled with g++ and ran the program with "strace", i.e. "strace tut_linux_serial". The program runs perfectly and I get the following (relevant) system calls: <snip> ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 -opost -isig -icanon -echo ...}) = 0 ioctl(3, TCFLSH, 0) = 0 ioctl(3, SNDCTL_TMR_START or TCSETS, {B38400 -opost -isig -icanon -echo ...}) = 0 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 -opost -isig -icanon -echo ...}) = 0 <snip> With your D module, I have written the following program: <snip - test.d> import com; int main(){ ComStream COM = new ComStream; COM.open("/dev/ttyS0",B9600); return 0; } <snip - test.d> And I get the following (relevant) system calls: ioctl(3, 0x7801, 0x40191fa4) = -1 EINVAL (Invalid argument) ioctl(3, 0x7801, 0x40191fa4) = -1 EINVAL (Invalid argument) Conclusion: I think that you are calling the IOCTL wrongly. I have further investigated which number TCGETS should be. It appears in /usr/include/asm/ioctls.h and its value is 0x5401. I have replaced the IOCTL call with: int tcgetattr(int fd, termios *termios_p) { return ioctl(fd, 0x5401, termios_p); } and it works! now, the system call trace looks like: ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 -opost -isig -icanon -echo ...}) = 0 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 -opost -isig -icanon -echo ...}) = 0 BUT, the next error is: Error: ArrayBoundsError com(396) ie the line if (cfsetispeed(&attr,cast(speed_t)BaudRates[baud]) != 0) throw new ComException("Failed to set output speed"); For now, that's all... Best regards, Tiago -- Tiago Gasiba (MSc.) - http://www.gasiba.de Everything should be made as simple as possible, but not simpler.
Oct 27 2005
next sibling parent reply Tiago Gasiba <tiago.gasiba gmail.com> writes:
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8Bit

Tiago Gasiba schrieb:


 BUT, the next error is:
 Error: ArrayBoundsError com(396)
 ie the line
 if (cfsetispeed(&attr,cast(speed_t)BaudRates[baud]) != 0) throw new
ComException("Failed to set output speed");

I should have known... in test.d I have written COM.open("/dev/ttyS0",B9600); instead of COM.open("/dev/ttyS0",BaudRate.B9600); This is a serious source of bugs and I think that you should remove completely the BaudRate.B9600. Just declare the consts and use them/pass them as parameters! If necessary, use different definitions for Windows/Linux. After correcting this small bug, the next one pops out here: open("/dev/ttyS0", O_RDWR|O_NONBLOCK) = 3 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 opost isig icanon echo ...}) = 0 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 opost isig icanon echo ...}) = 0 ioctl(3, 0x7802, 0x40191fa4) = -1 EINVAL (Invalid argument) All the IOCTL's have to be reviewed: The better solution is to rewrite the ioctls's consts. You can find a first attempt in attachment. Best, Tiago -- Tiago Gasiba (MSc.) - http://www.gasiba.de Everything should be made as simple as possible, but not simpler.
Oct 27 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Thu, 27 Oct 2005 18:02:40 +0200, Tiago Gasiba <tiago.gasiba gmail.com>  
wrote:
 BUT, the next error is:
 Error: ArrayBoundsError com(396)
 ie the line
 if (cfsetispeed(&attr,cast(speed_t)BaudRates[baud]) != 0) throw new  
 ComException("Failed to set output speed");

I should have known... in test.d I have written COM.open("/dev/ttyS0",B9600); instead of COM.open("/dev/ttyS0",BaudRate.B9600); This is a serious source of bugs and I think that you should remove completely the BaudRate.B9600. Just declare the consts and use them/pass them as parameters! If necessary, use different definitions for Windows/Linux.

No. In order to make it a cross-platform we have to use the same definitions, that is why BaudRate was created. There should be a way to make it error when you fail to pass a BaudRate enum value. Regan
Oct 27 2005
parent reply Tiago Gasiba <tiago.gasiba gmail.com> writes:
 
 No. In order to make it a cross-platform we have to use the same
 definitions, that is why BaudRate was created. There should be a way to
 make it error when you fail to pass a BaudRate enum value.

Not really - You can use a construct similar to this: <snip> import std.c.stdlib; version(linux){ const int A = 0; } version(windows){ const int A = 1; } int main( ){ printf("%d\n",A); return 0; } <snip> If you compile it under Linux you see a "0", in Windows you see a "1" ;) The, in your routines you can just simply pass the argument transparently without the need of a translating array! Best, Tiago -- Tiago Gasiba (M.Sc.) - http://www.gasiba.de Everything should be made as simple as possible, but not simpler.
Oct 28 2005
parent "Regan Heath" <regan netwin.co.nz> writes:
On Fri, 28 Oct 2005 10:53:42 +0200, Tiago Gasiba <tiago.gasiba gmail.com>  
wrote:
 No. In order to make it a cross-platform we have to use the same
 definitions, that is why BaudRate was created. There should be a way to
 make it error when you fail to pass a BaudRate enum value.

Not really - You can use a construct similar to this: <snip> import std.c.stdlib; version(linux){ const int A = 0; } version(windows){ const int A = 1; } int main( ){ printf("%d\n",A); return 0; } <snip> If you compile it under Linux you see a "0", in Windows you see a "1" ;) The, in your routines you can just simply pass the argument transparently without the need of a translating array!

You still have to choose either: - use the windows names - use the linux names - use new names Either way you're going to get someone (as you yourself did) typing the wrong thing. It would be better if you could catch the error at compile time. I was hoping something like this would work: typedef uint baud_t; version(Windows) { enum BaudRate : baud_t { B110 = 110, B300 = 300 } } version(linux) { enum BaudRate : baud_t { B110 = 0x3, B300 = 0x7 } } void foo(BaudRate b) {} void main() { uint a = 5; int b = 5; foo(5); //no error! foo(a); //error foo(b); //error foo(BaudRate.B110); //no error } But it seems that an integer literal is implicitly converted to a baud_t and is thus acceptable as a BaudRate. So, then I tried: version(Windows) { enum BaudRate : uint { B110 = 110, B300 = 300 } } version(linux) { enum BaudRate : uint { B110 = 0x3, B300 = 0x7 } } typedef BaudRate baud_t; void foo(baud_t b) {} and got the same results. Does anyone know how to do this? (restrict the input of a function to one of a bunch of defined values i.e. an Enum or similar) Regan
Oct 28 2005
prev sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Thu, 27 Oct 2005 15:04:22 +0200, Tiago Gasiba <tiago.gasiba gmail.com>  
wrote:
 Ok, here is my latest version.

 I can compile it on a ubuntu linux vmware virtual machine but it wont  
 run,
 it errors on the "tcsetattr" call. How about you?

 Regan

Same here. The B57600,B115200 that you can not find are declared in termbits.h under /usr/include/asm. I give you a small <snip> of the file:

I found them in the headers on my ubuntu system, but, they're not defined in the Digital Mars (DMC) headers. As DMD links with the DMC libraries (it does on Windows) I thought we could only use stuff that was present there. Am I wrong? is it linking with the ubuntu (or other) libraries?
 Furthermore, I think that there is something wrong with your IOCTL. Lets  
 see - I have downloaded and compiled the following program:  
 http://gpsbots.com/tutorials/tut_linux_serial.php
 Compiled with g++ and ran the program with "strace", i.e. "strace  
 tut_linux_serial".
 The program runs perfectly and I get the following (relevant) system  
 calls:

 <snip>
 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 -opost -isig -icanon  
 -echo ...}) = 0
 ioctl(3, TCFLSH, 0)                     = 0
 ioctl(3, SNDCTL_TMR_START or TCSETS, {B38400 -opost -isig -icanon -echo  
 ...}) = 0
 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 -opost -isig -icanon  
 -echo ...}) = 0
 <snip>

 With your D module, I have written the following program:

 <snip - test.d>
 import com;

 int main(){
   ComStream COM = new ComStream;

   COM.open("/dev/ttyS0",B9600);

   return 0;
 }
 <snip - test.d>

 And I get the following (relevant) system calls:
 ioctl(3, 0x7801, 0x40191fa4)            = -1 EINVAL (Invalid argument)
 ioctl(3, 0x7801, 0x40191fa4)            = -1 EINVAL (Invalid argument)


 Conclusion:
 I think that you are calling the IOCTL wrongly. I have further  
 investigated which number TCGETS should be.
 It appears in /usr/include/asm/ioctls.h and its value is 0x5401.
 I have replaced the IOCTL call with:

 int tcgetattr(int fd, termios *termios_p) { return ioctl(fd, 0x5401,  
 termios_p); }
 and it works! now, the system call trace looks like:

 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 -opost -isig -icanon  
 -echo ...}) = 0
 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 -opost -isig -icanon  
 -echo ...}) = 0

Did you compile with -version=M_ELF? tcgetattr works for me, it was tcsetattr that was failing. The TCGets value I am using is from the DMC headers (not the ubuntu ones). How do you use strace. I have tried using it on my D executable and all it doesn't seem to show anything of my own code, i.e. I cannot see the ioctl calls. I see some open calls (I assume this is the code DMD adds to the start of every executable).
 BUT, the next error is:
 Error: ArrayBoundsError com(396)
 ie the line
 if (cfsetispeed(&attr,cast(speed_t)BaudRates[baud]) != 0) throw new  
 ComException("Failed to set output speed");

This was due to you using the wrong baud value. Regan.
Oct 27 2005
parent reply Tiago Gasiba <tiago.gasiba gmail.com> writes:
 
 Did you compile with -version=M_ELF?
 tcgetattr works for me, it was tcsetattr that was failing.
 The TCGets value I am using is from the DMC headers (not the ubuntu ones).

No. I just compiled it without any -version stuff... What does M_ELF stand for should it be compiled like this on Linux?
 How do you use strace. I have tried using it on my D executable and all it
 doesn't seem to show anything of my own code, i.e. I cannot see the ioctl
 calls. I see some open calls (I assume this is the code DMD adds to the
 start of every executable).
 

strace outputs many lines of information and you have to search for the IOCTLs. For example, the lines I get while executing (as root) the D code for testing com.d are the following: licoslp0:/home/gasiba/downloads/D/Utils # strace ./test_com execve("./test_com", ["./test_com"], [/* 75 vars */]) = 0 uname({sys="Linux", node="licoslp0", ...}) = 0 brk(0) = 0x806a000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/root/GNUstep/Library/Libraries/tls/i686/sse2/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/root/GNUstep/Library/Libraries/tls/i686/sse2", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/root/GNUstep/Library/Libraries/tls/i686/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/root/GNUstep/Library/Libraries/tls/i686", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/root/GNUstep/Library/Libraries/tls/sse2/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/root/GNUstep/Library/Libraries/tls/sse2", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/root/GNUstep/Library/Libraries/tls/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/root/GNUstep/Library/Libraries/tls", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/root/GNUstep/Library/Libraries/i686/sse2/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/root/GNUstep/Library/Libraries/i686/sse2", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/root/GNUstep/Library/Libraries/i686/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/root/GNUstep/Library/Libraries/i686", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/root/GNUstep/Library/Libraries/sse2/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/root/GNUstep/Library/Libraries/sse2", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/root/GNUstep/Library/Libraries/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/root/GNUstep/Library/Libraries", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/usr/GNUstep/Local/Library/Libraries/tls/i686/sse2/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/usr/GNUstep/Local/Library/Libraries/tls/i686/sse2", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/usr/GNUstep/Local/Library/Libraries/tls/i686/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/usr/GNUstep/Local/Library/Libraries/tls/i686", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/usr/GNUstep/Local/Library/Libraries/tls/sse2/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/usr/GNUstep/Local/Library/Libraries/tls/sse2", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/usr/GNUstep/Local/Library/Libraries/tls/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/usr/GNUstep/Local/Library/Libraries/tls", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/usr/GNUstep/Local/Library/Libraries/i686/sse2/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/usr/GNUstep/Local/Library/Libraries/i686/sse2", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/usr/GNUstep/Local/Library/Libraries/i686/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/usr/GNUstep/Local/Library/Libraries/i686", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/usr/GNUstep/Local/Library/Libraries/sse2/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/usr/GNUstep/Local/Library/Libraries/sse2", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/usr/GNUstep/Local/Library/Libraries/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/usr/GNUstep/Local/Library/Libraries", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/usr/GNUstep/System/Library/Libraries/tls/i686/sse2/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/usr/GNUstep/System/Library/Libraries/tls/i686/sse2", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/usr/GNUstep/System/Library/Libraries/tls/i686/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/usr/GNUstep/System/Library/Libraries/tls/i686", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/usr/GNUstep/System/Library/Libraries/tls/sse2/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/usr/GNUstep/System/Library/Libraries/tls/sse2", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/usr/GNUstep/System/Library/Libraries/tls/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/usr/GNUstep/System/Library/Libraries/tls", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/usr/GNUstep/System/Library/Libraries/i686/sse2/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/usr/GNUstep/System/Library/Libraries/i686/sse2", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/usr/GNUstep/System/Library/Libraries/i686/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/usr/GNUstep/System/Library/Libraries/i686", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/usr/GNUstep/System/Library/Libraries/sse2/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/usr/GNUstep/System/Library/Libraries/sse2", 0xbff2950c) = -1 ENOENT (No such file or directory) open("/usr/GNUstep/System/Library/Libraries/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory) stat64("/usr/GNUstep/System/Library/Libraries", {st_mode=S_IFDIR|0755, st_size=328, ...}) = 0 open("/etc/ld.so.cache", O_RDONLY) = 3 fstat64(3, {st_mode=S_IFREG|0644, st_size=131025, ...}) = 0 old_mmap(NULL, 131025, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40017000 close(3) = 0 open("/lib/tls/libpthread.so.0", O_RDONLY) = 3 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0pH\0\000"..., 512) = 512 fstat64(3, {st_mode=S_IFREG|0755, st_size=93266, ...}) = 0 old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40037000 old_mmap(NULL, 70104, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x40038000 madvise(0x40038000, 70104, MADV_SEQUENTIAL|0x1) = 0 old_mmap(0x40046000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xd000) = 0x40046000 old_mmap(0x40048000, 4568, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x40048000 close(3) = 0 open("/usr/GNUstep/System/Library/Libraries/libm.so.6", O_RDONLY) = -1 ENOENT (No such file or directory) open("/lib/tls/libm.so.6", O_RDONLY) = 3 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\2203\0"..., 512) = 512 fstat64(3, {st_mode=S_IFREG|0755, st_size=191109, ...}) = 0 old_mmap(NULL, 151712, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x4004a000 madvise(0x4004a000, 151712, MADV_SEQUENTIAL|0x1) = 0 old_mmap(0x4006e000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x23000) = 0x4006e000 close(3) = 0 open("/usr/GNUstep/System/Library/Libraries/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory) open("/lib/tls/libc.so.6", O_RDONLY) = 3 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0`O\1\000"..., 512) = 512 fstat64(3, {st_mode=S_IFREG|0755, st_size=1417063, ...}) = 0 old_mmap(NULL, 1174492, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x40070000 madvise(0x40070000, 1174492, MADV_SEQUENTIAL|0x1) = 0 old_mmap(0x40189000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x119000) = 0x40189000 old_mmap(0x4018d000, 7132, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x4018d000 close(3) = 0 old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x4018f000 mprotect(0x40189000, 4096, PROT_READ) = 0 set_thread_area({entry_number:-1 -> 6, base_addr:0x4018f6c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0 munmap(0x40017000, 131025) = 0 set_tid_address(0x4018f708) = 6267 rt_sigaction(SIGRTMIN, {0x4003c3c0, [], SA_SIGINFO}, NULL, 8) = 0 rt_sigaction(SIGRT_1, {0x4003c440, [], SA_RESTART|SA_SIGINFO}, NULL, 8) = 0 rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0 getrlimit(RLIMIT_STACK, {rlim_cur=RLIM_INFINITY, rlim_max=RLIM_INFINITY}) = 0 _sysctl({{CTL_KERN, KERN_VERSION, 0, 20d91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 2, 0xbff29c30, 30, (nil), 0}) = 0 brk(0) = 0x806a000 brk(0x808b000) = 0x808b000 mmap2(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40190000 rt_sigaction(SIGUSR1, {0x8057c50, ~[RTMIN RT_1], 0}, NULL, 8) = 0 rt_sigaction(SIGUSR2, {0x8057cf0, ~[RTMIN RT_1], 0}, NULL, 8) = 0 open("/dev/ttyS0", O_RDWR|O_NONBLOCK) = 3 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 opost isig icanon echo ...}) = 0 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 opost isig icanon echo ...}) = 0 ioctl(3, 0x7802, 0x40191fa4) = -1 EINVAL (Invalid argument) fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40017000 write(1, "Error: Failed to set attributes "..., 51Error: Failed to set attributes {Invalid argument} ) = 51 munmap(0x40017000, 4096) = 0 exit_group(1) Notice the IOCTLs in the last lines... ;) In this case, the last IOCTL, namely ioctl(3, 0x7802, 0x40191fa4) is not recognized by the OS! (it results Invalid argument) Best, Tiago PS: I'm using a SuSE 10.0 with kernel 2.6.13-8-default, but this does not make any difference for the strace! -- Tiago Gasiba (M.Sc.) - http://www.gasiba.de Everything should be made as simple as possible, but not simpler.
Oct 28 2005
parent "Regan Heath" <regan netwin.co.nz> writes:
On Fri, 28 Oct 2005 10:47:50 +0200, Tiago Gasiba <tiago.gasiba gmail.com>  
wrote:
 Did you compile with -version=M_ELF?
 tcgetattr works for me, it was tcsetattr that was failing.
 The TCGets value I am using is from the DMC headers (not the ubuntu  
 ones).

No. I just compiled it without any -version stuff... What does M_ELF stand for should it be compiled like this on Linux?

No idea. All I know is that on ubuntu the tcgetattr in the M_ELF block works whereas the other does not.
 How do you use strace. I have tried using it on my D executable and all  
 it
 doesn't seem to show anything of my own code, i.e. I cannot see the  
 ioctl
 calls. I see some open calls (I assume this is the code DMD adds to the
 start of every executable).

strace outputs many lines of information and you have to search for the IOCTLs.

I realise this. I didn't get these lines:
 open("/dev/ttyS0", O_RDWR|O_NONBLOCK)   = 3
 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 opost isig icanon echo  
 ...}) = 0
 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 opost isig icanon echo  
 ...}) = 0
 ioctl(3, 0x7802, 0x40191fa4)            = -1 EINVAL (Invalid argument)

I was looking for them, especially the open containing "/dev/ttyS0". I didn't get anywhere near as many lines as you did, mine was much shorter. Can anyone tell me whether DMD on linux links with it's own C library (as it does on windows) or whether it links with the system libraries. If it's linking with the system libraries then I'll use the system headers to define the com stuff (as opposed to using the DMC ones). Regan
Oct 28 2005
prev sibling parent "Regan Heath" <regan netwin.co.nz> writes:
------------ffmK5HiPwRcqIVFHxWqqRZ
Content-Type: text/plain; format=flowed; delsp=yes; charset=iso-8859-15
Content-Transfer-Encoding: 8bit

New version. This one works for me on ubuntu linux. However, the timeout  
on read isn't as instant as I'd like, each call to read takes approx 1 sec  
to timeout.

Regan
------------ffmK5HiPwRcqIVFHxWqqRZ
Content-Disposition: attachment; filename=com.d
Content-Type: application/octet-stream; name=com.d
Content-Transfer-Encoding: 8bit

module com;

private import std.c.time;
private import std.intrinsic;
import std.stream;

class ComStreamBase : Stream
{
	this()
	{
		setState(false);
	}
	
	override ulong seek(long offset, SeekPos whence)
	{
		assertSeekable();
	}
protected:
	void setState(bool open)
	{
		isopen = open;
		readable = open;
		writeable = open;		

		seekable = false;
		readEOF = false;
		prevCr = false;		
	}	
}

version(Windows) {
	private import std.c.windows.windows;
	private import std.windows.syserror;

	extern(Windows) {
		struct COMMTIMEOUTS {
		    DWORD ReadIntervalTimeout;          /* Maximum time between read chars. */
		    DWORD ReadTotalTimeoutMultiplier;   /* Multiplier of characters.        */
		    DWORD ReadTotalTimeoutConstant;     /* Constant in milliseconds.        */
		    DWORD WriteTotalTimeoutMultiplier;  /* Multiplier of characters.        */
		    DWORD WriteTotalTimeoutConstant;    /* Constant in milliseconds.        */
		}
		alias COMMTIMEOUTS* LPCOMMTIMEOUTS;

		struct DCB {
		    DWORD DCBlength;       /* sizeof(DCB)                     */
		    DWORD BaudRate;        /* Baudrate at which running       */
		    uint BITS;
		    //bit fBinary;           /* Binary Mode (skip EOF check)    */
		    //bit fParity;           /* Enable parity checking          */
		    //bit fOutxCtsFlow;      /* CTS handshaking on output       */
		    //bit fOutxDsrFlow;      /* DSR handshaking on output       */
		    //bit[2] fDtrControl;    /* DTR Flow control                */
		    //bit fDsrSensitivity;   /* DSR Sensitivity              */
		    //bit fTXContinueOnXoff; /* Continue TX when Xoff sent */
		    //bit fOutX;             /* Enable output X-ON/X-OFF        */
		    //bit fInX;              /* Enable input X-ON/X-OFF         */
		    //bit fErrorChar;        /* Enable Err Replacement          */
		    //bit fNull;             /* Enable Null stripping           */
		    //bit[2] fRtsControl;    /* Rts Flow control                */
		    //bit fAbortOnError;     /* Abort all reads and writes on Error */
		    //bit[17] fDummy2;       /* Reserved                        */
		    WORD wReserved;        /* Not currently used              */
		    WORD XonLim;           /* Transmit X-ON threshold         */
		    WORD XoffLim;          /* Transmit X-OFF threshold        */
		    BYTE ByteSize;         /* Number of bits/byte, 4-8        */
		    BYTE Parity;           /* 0-4=None,Odd,Even,Mark,Space    */
		    BYTE StopBits;         /* 0,1,2 = 1, 1.5, 2               */
		    char XonChar;          /* Tx and Rx X-ON character        */
		    char XoffChar;         /* Tx and Rx X-OFF character       */
		    char ErrorChar;        /* Error replacement char          */
		    char EofChar;          /* End of Input character          */
		    char EvtChar;          /* Received Event character        */
		    WORD wReserved1;       /* Fill for now.                   */
		}
		alias DCB* LPDCB;

		void fBinary(DCB* dcb, uint b)           { (b) ? bts(&dcb.BITS,0) :
btr(&dcb.BITS,0); }	
		void fParity(DCB* dcb, uint b)           { (b) ? bts(&dcb.BITS,1) :
btr(&dcb.BITS,1); }
		void fOutxCtsFlow(DCB* dcb, uint b)      { (b) ? bts(&dcb.BITS,2) :
btr(&dcb.BITS,2); }
		void fOutxDsrFlow(DCB* dcb, uint b)      { (b) ? bts(&dcb.BITS,3) :
btr(&dcb.BITS,3); }
		void fDtrControl(DCB* dcb, uint b)       { (bt(&b,0)) ? bts(&dcb.BITS,4) :
btr(&dcb.BITS,4); (bt(&b,1)) ? bts(&dcb.BITS,5) : btr(&dcb.BITS,5); }
		void fDsrSensitivity(DCB* dcb, uint b)   { (b) ? bts(&dcb.BITS,6) :
btr(&dcb.BITS,6); }
		void fTXContinueOnXoff(DCB* dcb, uint b) { (b) ? bts(&dcb.BITS,7) :
btr(&dcb.BITS,7); }
		void fOutX(DCB* dcb, uint b)             { (b) ? bts(&dcb.BITS,8) :
btr(&dcb.BITS,8); }
		void fInX(DCB* dcb, uint b)              { (b) ? bts(&dcb.BITS,9) :
btr(&dcb.BITS,9); }
		void fErrorChar(DCB* dcb, uint b)        { (b) ? bts(&dcb.BITS,10) :
btr(&dcb.BITS,10); }
		void fNull(DCB* dcb, uint b)             { (b) ? bts(&dcb.BITS,11) :
btr(&dcb.BITS,11); }
		void fRtsControl(DCB* dcb, uint b)       { (bt(&b,0)) ? bts(&dcb.BITS,12) :
btr(&dcb.BITS,12); (bt(&b,1)) ? bts(&dcb.BITS,13) : btr(&dcb.BITS,13); }
		void fAbortOnError(DCB* dcb, uint b)     { (b) ? bts(&dcb.BITS,14) :
btr(&dcb.BITS,14); }

		const int CBR_110       = 110;
		const int CBR_300       = 300;
		const int CBR_600       = 600;
		const int CBR_1200      = 1200;
		const int CBR_2400      = 2400;
		const int CBR_4800      = 4800;
		const int CBR_9600      = 9600;
		const int CBR_14400     = 14400;
		const int CBR_19200     = 19200;
		const int CBR_38400     = 38400;
		const int CBR_56000     = 56000;
		const int CBR_57600     = 57600;
		const int CBR_115200    = 115200;
		const int CBR_128000    = 128000;
		const int CBR_256000    = 256000;
		
		alias CBR_110		B110;
		alias CBR_300		B300;
		alias CBR_600		B600;
		alias CBR_1200		B1200;
		alias CBR_2400		B2400;
		alias CBR_4800		B4800;
		alias CBR_9600		B9600;
		alias CBR_14400		B14400;
		alias CBR_19200		B19200;
		alias CBR_38400		B38400;
		alias CBR_56000		B56000;
		alias CBR_57600		B57600;
		alias CBR_115200	B115200;
		alias CBR_128000	B128000;
		alias CBR_256000	B256000;
		
		const int MAXDWORD    = 0xffffffff;

		int GetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts);
		int SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts);

		int GetCommState(HANDLE hFile, LPDCB lpDCB);
		int SetCommState(HANDLE hFile, LPDCB lpDCB);
		
		int WaitCommEvent(HANDLE hFile, LPDWORD lpEvtMask, OVERLAPPED *lpOverlapped);

		const BYTE ONESTOPBIT          = 0;
		const BYTE ONE5STOPBITS        = 1;
		const BYTE TWOSTOPBITS         = 2;

		const BYTE NOPARITY            = 0;
		const BYTE ODDPARITY           = 1;
		const BYTE EVENPARITY          = 2;
		const BYTE MARKPARITY          = 3;
		const BYTE SPACEPARITY         = 4;

		const ubyte DTR_CONTROL_DISABLE    = 0x00;
		const ubyte DTR_CONTROL_ENABLE     = 0x01;
		const ubyte DTR_CONTROL_HANDSHAKE  = 0x02;

		const ubyte RTS_CONTROL_DISABLE    = 0x00;
		const ubyte RTS_CONTROL_ENABLE     = 0x01;
		const ubyte RTS_CONTROL_HANDSHAKE  = 0x02;
		const ubyte RTS_CONTROL_TOGGLE     = 0x03;
	}

	class ComException : Exception
	{
		this(char[] msg)
		{
			super(msg ~ " {" ~ sysErrorString(GetLastError()) ~ "}");
		}
	}

	class ComStream : ComStreamBase
	{
		void open(char[] port, int baud)
		{
			handle = CreateFileA(toStringz(port),GENERIC_READ|GENERIC_WRITE,0,null,OPEN_EXISTING,0,null);
			if (handle == INVALID_HANDLE_VALUE) throw new ComException("Failed to open");

			if (!GetCommTimeouts(handle,&curTimeouts)) throw new ComException("Failed to
get timeouts");
			if (!SetCommTimeouts(handle,&reqTimeouts)) throw new ComException("Failed to
set timeouts");

			if (!GetCommState(handle,&dcb)) throw new ComException("Failed to get
state");

			fParity(&dcb,0);
			dcb.Parity = NOPARITY;

			dcb.BaudRate = baud;
			dcb.StopBits = ONESTOPBIT;
			dcb.ByteSize = 8;

			fDtrControl(&dcb,DTR_CONTROL_ENABLE);
			fRtsControl(&dcb,RTS_CONTROL_ENABLE);		

			fTXContinueOnXoff(&dcb,1);		
			fAbortOnError(&dcb,1);

			dcb.XonLim = 2048;
			dcb.XoffLim = 512;
			dcb.XonChar = 17;
			dcb.XoffChar = 19;

			fDsrSensitivity(&dcb,0);

			fOutxCtsFlow(&dcb,0);
			fOutxDsrFlow(&dcb,0);
			fOutX(&dcb,0);

			if (!SetCommState(handle,&dcb)) throw new ComException("Failed to set
state");
			
			setState(true);
		}

		void close()
		{
			if (handle == INVALID_HANDLE_VALUE) return ;
			SetCommTimeouts(handle,&curTimeouts);
			CloseHandle(handle);
			handle = INVALID_HANDLE_VALUE;
			setState(false);
		}
		
		bool canRead()
		{
			DWORD Mask;
			assertReadable();
			if (!WaitCommEvent(handle,&Mask,null)) return false;
			return true;
		}

		override size_t readBlock(void* result, size_t len)
		{
			size_t bytes = 0;

			assertReadable();
			while(true) {
				if (ReadFile(handle,result,len,&bytes,null) == 0)
					throw new ComException("Failed to read");
				if (bytes) break;
				sleep(1);
			}
			return bytes;
		}

		override size_t writeBlock(void* result, size_t len)
		{
			size_t bytes = 0;
			size_t done = 0;

			assertWriteable();
			for(; done < len; done += bytes) {
				if (WriteFile(handle,result+done,len-done,&bytes,null) == 0)
					throw new ComException("Failed to write");
			}
			return done;
		}
	private:
		static COMMTIMEOUTS reqTimeouts = {MAXDWORD,0,0,0,0};
		HANDLE handle = INVALID_HANDLE_VALUE;
		COMMTIMEOUTS curTimeouts;
		DCB dcb;
	}

}

version(linux) {
	private import std.c.linux.linux;
	private import std.c.linux.socket;
	private import std.c.stdlib;
	private import std.string;
	
	private import termios;	
	extern(C) {
		const int EAGAIN = 11;
		char *strerror(int);		
	}
	
	class ComException : Exception
	{
		this(char[] msg)
		{
			char* e = strerror(getErrno());
			super(msg ~ " {" ~ e[0..strlen(e)] ~ "}");
		}
	}

	class ComStream : ComStreamBase
	{
		void open(char[] port, uint baud)
		{
			file = std.c.linux.linux.open(toStringz(port),O_RDWR|O_NONBLOCK);
			if(file == -1) throw new ComException("Failed to open");			
			
			if (tcgetattr(file,&attr) != 0) throw new ComException("Failed to get
attributes");			
			
			if (cfsetispeed(&attr,cast(speed_t)baud) != 0) throw new
ComException("Failed to set output speed");
			if (cfsetospeed(&attr,cast(speed_t)baud) != 0) throw new
ComException("Failed to set input speed");
			attr.c_cflag = cast(tcflag_t)(CS8 | CREAD | CLOCAL);
			
			attr.c_iflag = 0;
			attr.c_oflag = 0;
			attr.c_lflag = 0;
			
			if (tcsetattr(file,TCSANOW,&attr) != 0) throw new ComException("Failed to
set attributes");
			
			setState(true);
		}
		
		void close()
		{
			if (file == -1) return ;
			std.c.linux.linux.close(file);
			file = -1;
			setState(false);
		}
		
		bool canRead()
		{
			fd_set readfd;
			
			assertReadable();
			FD_ZERO(&readfd);
			FD_SET(file,&readfd);
			if (select(file,&readfd,null,null,null) == -1) throw new
ComException("Failed to select");
			return FD_ISSET(file,&readfd) == 1;
		}
		
		override size_t readBlock(void* result, size_t len)
		{
			int bytes;
			
			assertReadable();
			while(true) {
				bytes = std.c.linux.linux.read(file,result,len);
				if (bytes == -1) {
					if (getErrno() == EAGAIN) bytes = 0;
					else throw new ComException("Failed to read");
				}
				if (bytes) break;
				sleep(1);
			}
					
			return bytes;
		}
		
		override size_t writeBlock(void* result, size_t len)
		{
			int done = 0;
			int bytes;			
			
			assertWriteable();			
			for(; done < len; done += bytes) {
				bytes = std.c.linux.linux.write(file,result,len);
				if (bytes == -1) {
					if (getErrno() == EAGAIN) bytes = 0;
					else throw new ComException("Failed to write");
				}
			}
			
			return done;
		
		}
	private:
		int file = -1;
		termios attr;		
	}
}

------------ffmK5HiPwRcqIVFHxWqqRZ
Content-Disposition: attachment; filename=termios.d
Content-Type: application/octet-stream; name=termios.d
Content-Transfer-Encoding: 8bit

module termios;

extern(C) {
	typedef ubyte cc_t;
	typedef uint speed_t;
	typedef uint tcflag_t;

	const uint NCCS = 19;

	struct termios {
		tcflag_t c_iflag;
		tcflag_t c_oflag;
		tcflag_t c_cflag;
		tcflag_t c_lflag;
		cc_t c_line;
		cc_t c_cc[NCCS];
	}
	
	/* c_cc characters */
	const uint VINTR 	= 0;
	const uint VQUIT 	= 1;
	const uint VERASE 	= 2;
	const uint VKILL 	= 3;
	const uint VEOF 	= 4;
	const uint VTIME 	= 5;
	const uint VMIN 	= 6;
	const uint VSWTC 	= 7;
	const uint VSTART 	= 8;
	const uint VSTOP 	= 9;
	const uint VSUSP 	= 10;
	const uint VEOL 	= 11;
	const uint VREPRINT 	= 12;
	const uint VDISCARD 	= 13;
	const uint VWERASE 	= 14;
	const uint VLNEXT 	= 15;
	const uint VEOL2 	= 16;
	
	/* c_iflag bits */
	const uint IGNBRK  	= 0000001;
	const uint BRKINT  	= 0000002;
	const uint IGNPAR  	= 0000004;
	const uint PARMRK  	= 0000010;
	const uint INPCK   	= 0000020;
	const uint ISTRIP  	= 0000040;
	const uint INLCR   	= 0000100;
	const uint IGNCR   	= 0000200;
	const uint ICRNL   	= 0000400;
	const uint IUCLC   	= 0001000;
	const uint IXON    	= 0002000;
	const uint IXANY   	= 0004000;
	const uint IXOFF   	= 0010000;
	const uint IMAXBEL 	= 0020000;

	/* c_oflag bits */
	const uint OPOST   	= 0000001;
	const uint OLCUC   	= 0000002;
	const uint ONLCR   	= 0000004;
	const uint OCRNL   	= 0000010;
	const uint ONOCR   	= 0000020;
	const uint ONLRET  	= 0000040;
	const uint OFILL   	= 0000100;
	const uint OFDEL   	= 0000200;
	const uint NLDLY   	= 0000400;
	const uint   NL0   	= 0000000;
	const uint   NL1   	= 0000400;
	const uint CRDLY   	= 0003000;
	const uint   CR0   	= 0000000;
	const uint   CR1   	= 0001000;
	const uint   CR2   	= 0002000;
	const uint   CR3   	= 0003000;
	const uint TABDLY  	= 0014000;
	const uint   TAB0  	= 0000000;
	const uint   TAB1  	= 0004000;
	const uint   TAB2  	= 0010000;
	const uint   TAB3  	= 0014000;
	const uint   XTABS 	= 0014000;
	const uint BSDLY   	= 0020000;
	const uint   BS0   	= 0000000;
	const uint   BS1   	= 0020000;
	const uint VTDLY   	= 0040000;
	const uint   VT0   	= 0000000;
	const uint   VT1   	= 0040000;
	const uint FFDLY   	= 0100000;
	const uint   FF0   	= 0000000;
	const uint   FF1   	= 0100000;

	/* c_cflag bit meaning */
	const uint CBAUD   	= 0010017;
	const uint  B0     	= 0000000;
	const uint  B50    	= 0000001;
	const uint  B75    	= 0000002;
	const uint  B110   	= 0000003;
	const uint  B134   	= 0000004;
	const uint  B150   	= 0000005;
	const uint  B200   	= 0000006;
	const uint  B300   	= 0000007;
	const uint  B600   	= 0000010;
	const uint  B1200  	= 0000011;
	const uint  B1800  	= 0000012;
	const uint  B2400  	= 0000013;
	const uint  B4800  	= 0000014;
	const uint  B9600  	= 0000015;
	const uint  B19200 	= 0000016;
	const uint  B38400 	= 0000017;
	const uint EXTA 	= B19200;
	const uint EXTB 	= B38400;
	const uint CSIZE   	= 0000060;
	const uint   CS5   	= 0000000;
	const uint   CS6   	= 0000020;
	const uint   CS7   	= 0000040;
	const uint   CS8   	= 0000060;
	const uint CSTOPB  	= 0000100;
	const uint CREAD   	= 0000200;
	const uint PARENB  	= 0000400;
	const uint PARODD  	= 0001000;
	const uint HUPCL   	= 0002000;
	const uint CLOCAL  	= 0004000;
	const uint CBAUDEX 	= 0010000;
	const uint    B57600 	= 0010001;
	const uint   B115200 	= 0010002;
	const uint   B230400 	= 0010003;
	const uint   B460800 	= 0010004;
	const uint   B500000 	= 0010005;
	const uint   B576000 	= 0010006;
	const uint   B921600 	= 0010007;
	const uint  B1000000 	= 0010010;
	const uint  B1152000 	= 0010011;
	const uint  B1500000 	= 0010012;
	const uint  B2000000 	= 0010013;
	const uint  B2500000 	= 0010014;
	const uint  B3000000 	= 0010015;
	const uint  B3500000 	= 0010016;
	const uint  B4000000 	= 0010017;
	const uint CIBAUD    	= 002003600000;  /* input baud rate (not used) */
	const uint CMSPAR    	= 010000000000;  /* mark or space (stick) parity */
	const uint CRTSCTS   	= 020000000000;  /* flow control */
	
	/* c_lflag bits */
	const uint ISIG    	= 0000001;
	const uint ICANON  	= 0000002;
	const uint XCASE   	= 0000004;
	const uint ECHO    	= 0000010;
	const uint ECHOE   	= 0000020;
	const uint ECHOK   	= 0000040;
	const uint ECHONL  	= 0000100;
	const uint NOFLSH  	= 0000200;
	const uint TOSTOP  	= 0000400;
	const uint ECHOCTL 	= 0001000;
	const uint ECHOPRT 	= 0002000;
	const uint ECHOKE  	= 0004000;
	const uint FLUSHO  	= 0010000;
	const uint PENDIN  	= 0040000;
	const uint IEXTEN  	= 0100000;

	/* tcflow() and TCXONC use these */
	const uint TCOOFF       = 0;
	const uint TCOON        = 1;
	const uint TCIOFF       = 2;
	const uint TCION        = 3;
	
	/* tcflush() and TCFLSH use these */
	const uint TCIFLUSH     = 0;
	const uint TCOFLUSH     = 1;
	const uint TCIOFLUSH    = 2;
	
	/* tcsetattr uses these */
	const uint TCSANOW      = 0;
	const uint TCSADRAIN    = 1;
	const uint TCSAFLUSH    = 2;
	
	int tcgetattr(int,termios *);
	int tcsetattr(int,int,termios *);
	
	int cfsetispeed(termios *,speed_t);
	int cfsetospeed(termios *,speed_t);
	
	speed_t cfgetispeed(termios *);
	speed_t cfgetospeed(termios *);
	
	int tcsendbreak(int,int);
	int tcdrain(int);
	int tcflush(int,int);
	int tcflow(int,int);
	
	int cfsetspeed(termios *,speed_t);
	void cfmakeraw(termios *);	
}

------------ffmK5HiPwRcqIVFHxWqqRZ--
Oct 28 2005
prev sibling parent reply Don Clugston <dac nospam.com.au> writes:
Thanks Regan.

Unfortunately, the timeouts don't work in that code. Not for me, anyway. 
(In fact, I've never been able to get COM timeouts to work in Windows, 
except when I open the port in OVERLAPPED mode). For my application, 
timeouts are extremely important (a timeout occurs about once every five 
minutes). For every application I've used, a 0.5 second timeout has 
worked well. I modified your code to get this to work, but it's too 
hacky and specific to post here at the moment.

Thanks again, you've saved me quite a bit of time.
-Don



Regan Heath wrote:
 On Mon, 24 Oct 2005 13:03:16 +0200, Don Clugston <dac nospam.com.au> wrote:
 
 Has anyone accessed serial (COM) ports using D (under Windows)?

Yes.
 Does Mango support them?
 If there is currently nothing in D, does anyone know of a candidate 
 that  could be ported to D?
 It ought to be possible to create something that would work on both  
 Windows and Linux PCs, since the hardware is the same.

The attached source is a COM stream for windows only. I have also written COM code on linux and freebsd and could probably add support for these two to the attached source, let me know if you're interested. Regan

Oct 26 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Thu, 27 Oct 2005 08:30:24 +0200, Don Clugston <dac nospam.com.au> wrote:
 Thanks Regan.

 Unfortunately, the timeouts don't work in that code. Not for me, anyway.  
 (In fact, I've never been able to get COM timeouts to work in Windows,  
 except when I open the port in OVERLAPPED mode). For my application,  
 timeouts are extremely important (a timeout occurs about once every five  
 minutes). For every application I've used, a 0.5 second timeout has  
 worked well. I modified your code to get this to work, but it's too  
 hacky and specific to post here at the moment.

So... you want it to timeout after 0.5 seconds on a read? What sort of timeout occurs once every 5 mins? ReadFile should return immediately .. and you could use msleep to create a timeout (though it may not be 100% acurate). Is that what you did? Opening using OVERLAPPED then using WaitCommEvent should allow you to do a "wait for data then read" sort of thing.
 Thanks again, you've saved me quite a bit of time.

Glad I could help. Regan
 Regan Heath wrote:
 On Mon, 24 Oct 2005 13:03:16 +0200, Don Clugston <dac nospam.com.au>  
 wrote:

 Has anyone accessed serial (COM) ports using D (under Windows)?

 Does Mango support them?
 If there is currently nothing in D, does anyone know of a candidate  
 that  could be ported to D?
 It ought to be possible to create something that would work on both   
 Windows and Linux PCs, since the hardware is the same.

I have also written COM code on linux and freebsd and could probably add support for these two to the attached source, let me know if you're interested. Regan


Oct 27 2005
parent Don Clugston <dac nospam.com.au> writes:
Regan Heath wrote:
 On Thu, 27 Oct 2005 08:30:24 +0200, Don Clugston <dac nospam.com.au> wrote:
 
 Thanks Regan.

 Unfortunately, the timeouts don't work in that code. Not for me, 
 anyway.  (In fact, I've never been able to get COM timeouts to work in 
 Windows,  except when I open the port in OVERLAPPED mode). For my 
 application,  timeouts are extremely important (a timeout occurs about 
 once every five  minutes). For every application I've used, a 0.5 
 second timeout has  worked well. I modified your code to get this to 
 work, but it's too  hacky and specific to post here at the moment.

So... you want it to timeout after 0.5 seconds on a read? What sort of timeout occurs once every 5 mins?

The item of measurement equipment I'm currently working on has occasional glitches where it ignores commands. Such glitches occur about ten times per hour on this machine. It can happen when the PLC inside the equipment is momentarily overworked, and dumps its command buffer because it can't keep up. (Poor design, but it seems to be relatively common behaviour amongst engineering tools). The other case is when the power to the machine has been cut temporarily so that the PLC has reset, again clearing the command buffer, or ignoring commands for the few milliseconds it takes to restart. It's an important issue when your program needs to run 24/7.
 ReadFile should return immediately .. and you could use msleep to create 
 a  timeout (though it may not be 100% acurate). Is that what you did?

In this case, yes, and it's been enough to get it going. The length of the timeout period is generally not very critical, it's just important that the system not hang.
 Opening using OVERLAPPED then using WaitCommEvent should allow you to do 
 a  "wait for data then read" sort of thing.

This is what I plan to do in the longer term. I've done it in C++, but in D there are delegates and better multithreading support, so it can potentially be much better. It really isn't much different to a web client, you cannot guarantee that you'll get a response to all packets you send, this is why I thought it has commonality with Mango.
Oct 27 2005