Giter Site home page Giter Site logo

veeso / termiwin Goto Github PK

View Code? Open in Web Editor NEW
60.0 6.0 23.0 169 KB

termiWin: a termios porting for Windows

License: GNU General Public License v3.0

C 94.51% CMake 5.49%
termios windows serial-ports termios-structure struct-termios termios-compatibilty serial-communication serial-port termios-for-windows windows-termios

termiwin's Introduction

termiWin

License: GPL v3 Stars Issues Build

~ A termios porting for Windows ~

Current Version 1.2.1 (16/11/2020)


Introduction

termiWin is a library which purpose is to allow you to use on a Windows system, the same code used in Linux to communicate with a device through a serial port. This is possible because termios’s functions have been rewritten to be compatible with Windows’s COM functions.


Project status ⚠️

I'm no longer working on this project, so this should be considered as an abandoned project. Feel free to fork it, but please, consider that even forking this project should be considered wrong, and here's why:

First of all, I think this project is just wrong. I've implemented this in 2017, when I had just started my career as a dev, and many things have changed since then, even the way I see software. At the time I saw no issue with something like termiwin, but now, personally, I think termiwin is a huge mistake, I even feel ashamed for implementing it. It is so far from my idea of how software should work, that I just feel bad every time someone stars this project. Portings are a cool thing, but termiwin is not a porting. It's just a copy-paste of some linux headers and "pray it works and be thankful to the creator of the world if it runs on your machine".

why didn't you fix it to make it good, then?

Well, I tried at least, but it's just not possible. If there is not a porting of termios out there for windows, it's because there cannot be one. I/O on linux is so much different from how I/O is managed on Windows, that this thing cannot just work. I felt so smart when I implemented this like

I'm so cool yo, I'm the only one who wrote a porting of termios for Windows

Do you think MinGW devs are stupid? No, they're F not.

In addition to this, I have just no interest in this project:

  1. First of all, I've moved away from C/C++ development, thanks, god. You should do it too.
  2. I've developed software for Windows once in my lifetime and I promised myself that I would never do this. Do you know what Windows is good for? Playing videogames. And not because games run fine there, but just because the game producers, won't develop games for other platforms. If I didn't play games, I wouldn't even use Windows. When I develop on Windows, I'm on a terminal running WSL all the time. So I don't even think of developing on this damned operating system again.
  3. I've got many other projects that received much more interest from the community.

TL;DR I won't work on this project anymore. Stop asking me to solve your issue, because I won't be able to. I hate Windows.


Implementation

It's enough to include termios.h, termiWin.h and termiWin.c to build the project. You don't have to change anything else in your code

Supported Build Platform

  • Visual C++ (> 12.0)
    • Build and works fine without any restriction
  • Visual C++ (< 12.0)
    • Not tested, if you want you can report a working build on an older compiler. I will appreciate your contribution.
  • MinGW
    • Requires -D TERMIWIN_DONOTREDEFINE to build (which means that open, close, select etc must be replaced with openSerial, closeSerial, ...), but works fine afterall and it's used on my travis configuration

Build with cmake

Once you are in the project root, run:

cmake .. -G "MinGW Makefiles" -DTERMIWIN_DONOTREDEFINE=yes -DCMAKE_SH="CMAKE_SH-NOTFOUND"
mingw32-make

Library Architecture

The termios Structure

This is the main structure of the library and it’s often passed as argument to the functions, it has the following members:

tcflag_t c_iflag; /*input modes*/
tcflag_t c_oflag; /*output modes*/
tcflag_t c_cflag; /*control modes*/
tcflag_t c_lflag; /*local modes*/
cc_t c_cc[NCCS]; /* special character */

where tcflag_t is defined as an unsigned integer.

The members of termios structure are used to set and retrieve the serial port configuration parameters.
There five types of flags, sorted by mode; they are implemented in the same way as they are in termios, except for some, which are not used.

Input modes flags

  • INPCK – Not implemented, use PARENB instead.
  • IGNPAR – Not implemented, disable PARENB instead.
  • PARMRK – Not implemented, use PARENB instead.
  • ISTRIP – Not implemented, use CS7 instead.
  • IGNBRK – Not implemented.
  • IGNCR – Not implemented.
  • ICRNL – Not implemented.
  • INLCR – Not implemented.
  • IXOFF - If this bit is set, start/stop control on input is enabled. In other words, the computer sends STOP and START characters as necessary to prevent input from coming in faster than programs are reading it. The idea is that the actual terminal hardware that is generating the input data responds to a STOP character by suspending transmission, and to a START character by resuming transmission.
  • IXON - If this bit is set, start/stop control on output is enabled. In other words, if the computer receives a STOP character, it suspends output until a START character is received. In this case, the STOP and START characters are never passed to the application program. If this bit is not set, then START and STOP can be read as ordinary characters.

Local modes flags

Since there’s no way to implement them in Windows, they have asbolutely no effect using termiWin, but you can keep the same you use in Linux for compatibilty.

Control modes flags

  • CSTOPB - If this bit is set, two stop bits are used. Otherwise, only one stop bit is used.
  • PARENB - If this bit is set, generation and detection of a parity bit are enabled.
  • PARODD - This bit is only useful if PARENB is set. If PARODD is set, odd parity is used, otherwise even parity is used.
  • CSIZE - This is a mask for the number of bits per character.
  • CS5 - This specifies five bits per byte.
  • CS6 – This specifies six bits per byte.
  • CS7 – This specifies seven bits per byte.
  • CS8 – This specifies eight bits per byte.
  • CLOCAL – Is used in termios for ignoring the data carrier detected, but in Windows it can't be implemented
  • CREAD – In termios, if not set, no character will be received, but in Windows it can't be implemented

Output modes flags

Since there’s no way to implement them in Windows, they have absolutely no effect using termiWin, but you can keep the same you use in Linux for compatibilty.

Special character array

  • VEOF – Is the EOF character to be used during the communication.
  • VEOL – Not implemented.
  • VERASE – Not implemented.
  • VINTR – Interrupt character.
  • VKILL – Not implemented.
  • VMIN – If set to 0, the port is set to not-blocking, otherwise to blocking.
  • VQUIT – Not implemented.
  • VSTART – Not implemented.
  • VSTOP – Not implemented.
  • VSUSP – Not implemented.
  • VTIME – Timeout for reading operations when COM is set to not-blocking.

Serial configuration functions

tcgetattr

int tcgetattr(int fd, struct termios *termios_p)

Sets in the internal DCB structures the current serial port parameters, it always has to be invoked before using tcsetattr.
Returns 0 if succeded, otherwise -1.

tcsetattr

int tcsetattr(int fd, int optional_actions, struct termios *termios_p)

Reads the flags set in the termios structure and sets the properly parameters in the DCB structure and eventually it applies the parameters to the serial port.
Returns 0 if succeded, otherwise -1.

tcsendbreak

int tcsendbreak(int fd, int duration)

Sends a break character to the serial port; duration is not implemented.
Returns 0 if succeded, otherwise -1.

tcdrain

int tcdrain(int fd)

Waits until all output written to the serial port has been transmitted.
Returns 0 if succeded, otherwise -1.

tcflush

int tcflush(int fd, int queue_selector)

Discards data on serial port. queue_selector can assume the following values:

  • TCIFLUSH (discards data received but still not read).
  • TCOFLUSH (discards data written but still not transmitted),
  • TCIOFLUSH (discards both data received but still not read and data written but still not transmitted).

Returns 0 if succeded, otherwise -1.

tcflow

int tcflow(int fd, int action)

Suspends transmission or receptions of data on serial port based on action. Action can assume the following values:

  • TCOON restarts suspended output.
  • TCIOFF transmits a STOP character.
  • TCION transmits a START character.
  • TCOOF suspends output.

Returns 0 if succeded, otherwise -1.

cfmakeraw

void cfmakeraw(struct termios *termios_p)

Sets but doesn’t commit the following options for the serial port:

  • Charset: 8 bits
  • StopBits: one stop bit
  • Parity: no parity

Use tcsetattr to commit them.

cfgetispeed

speed_t cfgetispeed(const struct termios *termios_p)

Returns the input speed, speed can assume the same values of termios (B9600, B115200, …).

cfgetospeed

speed_t cfgetospeed(const struct termios *termios_p)

returns the output speed, speed can assume the same values of termios (B9600, B115200, …).

cfsetispeed

int cfsetispeed(struct termios *termios_p, speed_t speed)

Sets, but doesn’t commits the parameter of speed for the serial port (in Windows there’s no distinction between input/output /control), speed can assume the same values of termios (B9600, B115200, …).
Returns 0 if succeded, otherwise -1.

cfsetospeed

int cfsetospeed(struct termios *termios_p, speed_t speed)

Sets, but doesn’t commits the parameter of speed for the serial port (in Windows there’s no distinction between input/output /control), speed can assume the same values of termios (B9600, B115200, ...).
Returns 0 if succeded, otherwise -1.

cfsetsspeed

int cfsetsspeed(struct termios \*termios_p, speed_t speed)

Sets, but doesn’t commits the parameter of speed for the serial port (in Windows there’s no distinction between input/output /control), speed can assume the same values of termios (B9600, B115200, ...).
Returns 0 if succeded, otherwise -1.

Supported speed

The supported speeds are the the following (not all speeds could be implemented since some of them can't be used in a Windows environment)

  • B110
  • B300
  • B600
  • B1200
  • B2400
  • B4800
  • B9600
  • B19200
  • B38400
  • B57600
  • B115200

Extra functions

You can use open/close/write/read instead of these names by default. If it causes to you conflicts with another library you can deactivate these definitions defining in your project:

#define TERMIWIN_DONOTREDEFINE  

open_serial

int open_serial(const char* portname, int opt)

Open the serial port which name is portname with opt set for the port to be read only, write only or both read/write (O_RDONLY, O_WRONLY, O_RDWR). Returns the file descriptor (fd is actually useless in Windows with serial ports, but is set for compatibilty). The function can be called using open instead of openSerial (for termios compatibilty).
The portname must be in the format "COMnumber" (e.g. COM2, COM11).

close_serial

int close_serial(int fd)

Closes the serial port. Returns 0 if succeded, otherwise -1. The function can be called using close instead of closeSerial. (for termios compatibilty).

write_serial

ssize_t write_serial(int fd, const void* buffer, size_t count)

Writes to serial “count” characters contained in buffer. Returns the number of transmitted characters or -1 if transmission failed. The function can be called using write instead of writeToSerial (for termios compatibilty).

read_serial

ssize_t read_serial(int fd, void* buffer, size_t count)

Reads “count” bytes from serial port and put them into buffer. Returns the number of read bytes or -1 if read failed. The function can be called using read instead of readFromSerial (for termios compatibilty).

select_serial

int select_serial(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)

It behaves as termios select. Returns the file descriptor ready for the chosen operation or -1 if failed. The function can be called using select instead of selectSerial (for termios compatibility).

getHandle

HANDLE getHandle()

Returns the HANDLE from the COM structure.

Multiple serial communications

At the moment is not possible to have more than one serial communication at the same time. I have never implemented this feature and probably I won't.


What is termiWin intended for

termiWin is just a simple library which executes termios functions using Windows libsock functions, because of that, any particular behaviour in the communication with the serial device, could not work properly. termiWin is intended just for simple communications.

When termiWin can be a good choice? ✅

  • ✅ - When you have to do a quick project without particular requirements.
  • ✅ - When you don't need performance and high reliability.
  • ✅ - When the communication between you and the serial device doesn't require a particular configuration.

When termiWin is NOT a good choice? ❌

  • ❌ - When you need multiple device communications. termiWin just doesn't support this feature at the moment.
  • ❌ - When you need performance - since termiWin has to convert termios to windows instructions, it won't be faster than a program with windows instructions wrote by the developer.
  • ❌ - When you require particular configuration and to use particular functions (such as tcdrain, tcflush etc) - they could work, but I can't assure

Contributions

Everybody can contribute to this project, indeed any improvement will be appreciated.

Changelog

View CHANGELOG

License

termiWin is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. termiWin is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with termiWin.
If not, see http://www.gnu.org/licenses/.

termiwin's People

Contributors

kolanich avatar phoebe-leong avatar veeso avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

termiwin's Issues

Missing defines CREAD, CLOCAL and B600

I currently try to get libmbus to compile on windows and use your termiWin.

I miss:

  • B600 ... ok this one was easy to add :-)
  • CREAD
  • CLOCAL

Do you have any idea how to add them?

Serial port name init seems wrong/strange

Hi again :-)

I found one last thing which adding testing for my library.

Your code in "OpenSerial" to init ihe port name is:

com.port = calloc(1,sizeof(char)*(strlen("\\\\.\\")+4));
strncat(com.port, portname, 4);

So you only accept the first 4 characters of the portname that is given to the function, but reserve then 4 bytes more without writing them somewhere.

From specs:

  • You can use COM1..9 directly
  • for others (like COM10 or other names) you need to use \.\COM10 as example.

So I think you initially wanted to limit to 4 character ports (but with this you do not allow COM10 or such) and wanted to always add "\." in front ... but you did not done that :-)

I would propose a change to:

	com.port = calloc(1,sizeof(char)*(strlen(portname)+1));
	strncat(com.port, portname, strlen(portname));

so simply copy the portname as it was as it came in (plus 1 for the \0 as string terminator) and have people send correct portnames ?!
Alternative would be to use portname as is (to also allow longer portnmes then 4 characters) and always prepend "\.". What do you think?

tcdrain is always blocking ...

... can it be because of the fact that the CreateFile is done in "non overlapping" mode ?!

When I understood it correctly "non overlapping" means that the WriteFile call returns after everything is written. So when you call a drain after the Write you will wait for an Event that never comes (because already was there?)

Could this be possible?

I also tried to set mode to overlapping, but then I got no successfull connect (or I forgot something).

For now I removed the drain, but if my above "idea" is true then the tcdrain should always return directly, or ?! Or you need to move to overlapping mode.

What fo you think?

Any way to use with a Python packages?

I'm attempting to run a Python package called asciinema (records terminal sessions) but every attempts throws up errors due to missing module "termios". So my question is whether or not (and if so, how) this can be integrated into a Python package, and/or how (if possible) it can be used in place of the missing module.

Control/Charset option handling is not working

I struggled around a very long time now, and finally noted that the "getCharSet" and "getControlOptions" methods are not working and return crap (or better: only 0").

Because of this wrong options are set.

My cflag is sset as follows:

memset(term, 0, sizeof(*term));
    term->c_cflag |= (CS8/*|CREAD|CLOCAL*/);
    term->c_cflag |= PARENB;

resulting in

c_cflag = 4c00

This struct is then used to call

 tcsetattr(handle->fd, TCSANOW, term);

Now after nothing worked I added debug and I found out that the"getByte" calls (at least) in getCharSet and getControlOptions return 0 in this case, leading to wrong results.

Could you please check that?!
I will also try to get in the bianry comare stuff, but ... maybe your are faster :-)

Thank you for all your support!

Returned handle cannot be used with "isatty"

The libmbus code uses "isatty" with the handle returned from "connect" to check if the connection is correct and to a serial device.

Because the handle you return is not really a handle, but the real handle is somewhere in your struct only this method returns an error.

Any idea how to fix this? Also overwrite isatty?

hard defines for read/write/select/open/close also affects other files

Hey,

I currently try to get libmbus to compile on windows and sofound your great termiWin as replacement for termios.h ...
Bute when I use this the redefines for "read/write/select/open/close" in termiWin.h also affects other methods called "open/close" in a complete different file (definition of a struct in libmbus) ends up in weired error messages.

Here is the struct from libmbus:
https://github.com/Apollon77/libmbus/blob/build-windows/mbus/mbus-protocol-aux.h#L92

and this generates error ;

..\libmbus\mbus\mbus-protocol-aux.c(1529): error C2039: 'openSerial': is not a member of '_mbus_handle'

as soon as the code wants to assign to "open" property:
https://github.com/Apollon77/libmbus/blob/build-windows/mbus/mbus-protocol-aux.c#L1529

Is there any way to solve this?!

Ingo

Function openSerial() changes the portname parameter

The function openSerial() changes the portname parameter at the end were fd is established:

if (com.hComm == INVALID_HANDLE_VALUE) return -1;
strncpy(portname, portname + 3, 1);
com.fd = atoi(portname); //COMx
SerialParams.DCBlength = sizeof(SerialParams);
return com.fd;

This can easily be fixed as follows:

if (com.hComm == INVALID_HANDLE_VALUE) return -1;
com.fd = atoi(portname + 3);							// COMx and COMxx
SerialParams.DCBlength = sizeof(SerialParams);
return com.fd;

There is a type at the beginning as well:

//COMx
else {
	com.port = calloc(1, sizeof(char) * 5));
}

There is one bracket to many on right side. It should be:

//COMx
else {
	com.port = calloc(1, sizeof(char) * 5);

Sending appears to be working correctly, but I have trouble receiving data.
Is there something where i can define a receive buffer size?

Tons of Errors

A:\Projects\cProjects>gcc -c termiWin.c
termiWin.h:39:14: error: conflicting types for 'openSerial'
#define open openSerial
^~~~~~~~~~
termiWin.h:180:5: note: previous declaration of 'openSerial' was here
int openSerial(char* portname, int opt);
^~~~~~~~~~
termiWin.h:36:14: error: conflicting types for 'readFromSerial'
#define read readFromSerial
^~~~~~~~~~~~~~
termiWin.h:178:5: note: previous declaration of 'readFromSerial' was here
int readFromSerial(int fd, char* buffer, int count);
^~~~~~~~~~~~~~
termiWin.h:37:15: error: conflicting types for 'writeToSerial'
#define write writeToSerial
^~~~~~~~~~~~~
termiWin.h:179:5: note: previous declaration of 'writeToSerial' was here
int writeToSerial(int fd, char* buffer, int count);
^~~~~~~~~~~~~
termiWin.c: In function 'readFromSerial':
termiWin.c:454:44: warning: passing argument 4 of 'ReadFile' from incompatible pointer type [-Wincompatible-pointer-types]
ret = ReadFile(com.hComm, buffer, count, &rc, NULL);
^~~
In file included from C:/Mingw-w64/mingw64/x86_64-w64-mingw32/include/winbase.h:18,
from C:/Mingw-w64/mingw64/x86_64-w64-mingw32/include/windows.h:70,
from termiWin.h:29:
C:/Mingw-w64/mingw64/x86_64-w64-mingw32/include/fileapi.h:167:106: note: expected 'LPDWORD' {aka 'long unsigned int *'} but argument is of type 'int *'
WINBASEAPI WINBOOL WINAPI ReadFile (HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped);
~~~~~~~~^~~~~~~~~~~~~~~~~~~
termiWin.c: In function 'writeToSerial':
termiWin.c:468:45: warning: passing argument 4 of 'WriteFile' from incompatible pointer type [-Wincompatible-pointer-types]
ret = WriteFile(com.hComm, buffer, count, &rc, NULL);
^~~
In file included from C:/Mingw-w64/mingw64/x86_64-w64-mingw32/include/winbase.h:18,
from C:/Mingw-w64/mingw64/x86_64-w64-mingw32/include/windows.h:70,
from termiWin.h:29:
C:/Mingw-w64/mingw64/x86_64-w64-mingw32/include/fileapi.h:175:109: note: expected 'LPDWORD' {aka 'long unsigned int *'} but argument is of type 'int *'
WINBASEAPI WINBOOL WINAPI WriteFile (HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped);
~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~
termiWin.c: In function 'openSerial':
termiWin.c:483:5: warning: 'strncat' specified bound 4 equals source length [-Wstringop-overflow=]
strncat(com.port, "\\.\", strlen("\\.\"));

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.