/**************************************************************************** * This file is part of the project AqHome. * AqHome (c) by 2023 Martin Preuss, all rights reserved. * * The license for this file can be found in the file COPYING which you * should have received along with this file. ****************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include "aqhome/msgendpointtty_p.h" #include #include #include #include #include #include #include #include #define AQH_MSG_ENDPOINT_TTY_BAUDRATE B19200 #define AQH_MSG_ENDPOINT_TTY_BYTE_MICROSECS 520 GWEN_INHERIT(AQH_MSG_ENDPOINT, AQH_MSG_ENDPOINT_TTY) static void GWENHYWFAR_CB _freeData(void *bp, void *p); static int _startMsg(AQH_MSG_ENDPOINT *ep); static int _endMsg(AQH_MSG_ENDPOINT *ep); static int _checkMsg(AQH_MSG_ENDPOINT *ep); static int _setupDevice(AQH_MSG_ENDPOINT *ep); static int _attnLow(AQH_MSG_ENDPOINT *ep); static int _attnHigh(AQH_MSG_ENDPOINT *ep); static int _isAttnLow(AQH_MSG_ENDPOINT *ep); AQH_MSG_ENDPOINT *AQH_MsgEndpointTty_new(const char *deviceName) { AQH_MSG_ENDPOINT *ep; AQH_MSG_ENDPOINT_TTY *xep; int fd; int rv; fd=open(deviceName, O_NOCTTY | O_NDELAY | O_RDWR); if (fd<0) { DBG_ERROR(NULL, "Error on open(%s): %s (%d)", deviceName, strerror(errno), errno); return NULL; } ep=AQH_MsgEndpoint_new(fd, AQH_MSG_ENDPOINT_ENDPOINTGROUP_BUS); AQH_MsgEndpoint_SetAcceptedEndpointGroups(ep, AQH_MSG_ENDPOINT_ENDPOINTGROUP_NET); GWEN_NEW_OBJECT(AQH_MSG_ENDPOINT_TTY, xep); GWEN_INHERIT_SETDATA(AQH_MSG_ENDPOINT, AQH_MSG_ENDPOINT_TTY, ep, xep, _freeData); xep->deviceName=strdup(deviceName); _attnHigh(ep); AQH_MsgEndpoint_SetStartMsgFn(ep, _startMsg); AQH_MsgEndpoint_SetEndMsgFn(ep, _endMsg); AQH_MsgEndpoint_SetCheckMsgFn(ep, _checkMsg); rv=_setupDevice(ep); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); AQH_MsgEndpoint_free(ep); return NULL; } return ep; } void _freeData(void *bp, void *p) { AQH_MSG_ENDPOINT *ep; AQH_MSG_ENDPOINT_TTY *xep; int fd; ep=(AQH_MSG_ENDPOINT*) bp; xep=(AQH_MSG_ENDPOINT_TTY*) p; fd=AQH_MsgEndpoint_GetFd(ep); if (fd>=0) tcsetattr(fd, TCSANOW, &xep->previousOptions); free(xep->deviceName); GWEN_FREE_OBJECT(xep); } int _startMsg(AQH_MSG_ENDPOINT *ep) { AQH_MSG_ENDPOINT_TTY *xep; xep=GWEN_INHERIT_GETDATA(AQH_MSG_ENDPOINT, AQH_MSG_ENDPOINT_TTY, ep); if (xep->intendedAttnState==1) { int rv; rv=_attnLow(ep); if (rv<0) { DBG_ERROR(NULL, "here (%d)", rv); return rv; } usleep(AQH_MSG_ENDPOINT_TTY_BYTE_MICROSECS/5); } return 0; } int _endMsg(AQH_MSG_ENDPOINT *ep) { _attnHigh(ep); } int _checkMsg(AQH_MSG_ENDPOINT *ep) { return _isAttnLow(ep); } int _setupDevice(AQH_MSG_ENDPOINT *ep) { AQH_MSG_ENDPOINT_TTY *xep; int fd; int status; int i; struct termios options; int rv; int m; xep=GWEN_INHERIT_GETDATA(AQH_MSG_ENDPOINT, AQH_MSG_ENDPOINT_TTY, ep); fd=AQH_MsgEndpoint_GetFd(ep); rv=tcgetattr(fd, &(xep->previousOptions)); if (rv<0) { DBG_ERROR(NULL, "Error on tcgetattr(%s): %s (%d)", xep->deviceName, strerror(errno), errno); return GWEN_ERROR_IO; } memset(&options, 0, sizeof(options)); /* preset */ options.c_cflag=CLOCAL | CREAD | CS8; options.c_iflag=IGNPAR | IGNBRK; options.c_oflag=0; options.c_lflag=0; cfmakeraw(&options); options.c_cc[VTIME]=0; /* read timeout in deciseconds */ options.c_cc[VMIN]=0; /* no minimum number of receive bytes */ rv=cfsetispeed(&options, AQH_MSG_ENDPOINT_TTY_BAUDRATE); if (rv<0) { DBG_ERROR(NULL, "Error on cfsetispeed(%s): %s (%d)", xep->deviceName, strerror(errno), errno); return GWEN_ERROR_IO; } rv=cfsetospeed(&options, AQH_MSG_ENDPOINT_TTY_BAUDRATE); if (rv<0) { DBG_ERROR(NULL, "Error on cfsetospeed(%s): %s (%d)", xep->deviceName, strerror(errno), errno); return GWEN_ERROR_IO; } rv=tcflush(fd, TCIOFLUSH); if (rv<0) { DBG_ERROR(NULL, "Error on tcflush(%s): %s (%d)", xep->deviceName, strerror(errno), errno); return GWEN_ERROR_IO; } rv=tcsetattr(fd, TCSANOW, &options); if (rv<0) { DBG_ERROR(NULL, "Error on tcsetattr(%s): %s (%d)", xep->deviceName, strerror(errno), errno); return GWEN_ERROR_IO; } _attnHigh(ep); return 0; } int _attnLow(AQH_MSG_ENDPOINT *ep) { AQH_MSG_ENDPOINT_TTY *xep; int status; int rv; int fd; xep=GWEN_INHERIT_GETDATA(AQH_MSG_ENDPOINT, AQH_MSG_ENDPOINT_TTY, ep); fd=AQH_MsgEndpoint_GetFd(ep); rv=ioctl(fd, TIOCMGET, &status); /* GET the State of MODEM bits in Status */ if (rv<0) { DBG_ERROR(NULL, "Error on ioctl(%s): %s (%d)", xep->deviceName, strerror(errno), errno); return GWEN_ERROR_IO; } status |= TIOCM_DTR | TIOCM_RTS; /* clear the DTR pin (cave: signals inverted!) */ rv=ioctl(fd, TIOCMSET, &status); if (rv<0) { DBG_ERROR(NULL, "Error on ioctl(%s): %s (%d)", xep->deviceName, strerror(errno), errno); return GWEN_ERROR_IO; } xep->intendedAttnState=0; return 0; } int _attnHigh(AQH_MSG_ENDPOINT *ep) { AQH_MSG_ENDPOINT_TTY *xep; int status; int rv; int fd; xep=GWEN_INHERIT_GETDATA(AQH_MSG_ENDPOINT, AQH_MSG_ENDPOINT_TTY, ep); fd=AQH_MsgEndpoint_GetFd(ep); rv=ioctl(fd, TIOCMGET, &status); if (rv<0) { DBG_ERROR(NULL, "Error on ioctl(%s): %s (%d)", xep->deviceName, strerror(errno), errno); return GWEN_ERROR_IO; } status |= TIOCM_DTR; /* Set the DTR pin */ status &= ~ (TIOCM_DTR | TIOCM_RTS); /* clear the DTR pin (cave: signals inverted!) */ rv=ioctl(fd, TIOCMSET, &status); if (rv<0) { DBG_ERROR(NULL, "Error on ioctl(%s): %s (%d)", xep->deviceName, strerror(errno), errno); return GWEN_ERROR_IO; } xep->intendedAttnState=1; return 0; } int _isAttnLow(AQH_MSG_ENDPOINT *ep) { AQH_MSG_ENDPOINT_TTY *xep; int status; int rv; int fd; xep=GWEN_INHERIT_GETDATA(AQH_MSG_ENDPOINT, AQH_MSG_ENDPOINT_TTY, ep); fd=AQH_MsgEndpoint_GetFd(ep); rv=ioctl(fd, TIOCMGET, &status); if (rv<0) { DBG_ERROR(NULL, "Error on ioctl(%s): %s (%d)", xep->deviceName, strerror(errno), errno); return GWEN_ERROR_IO; } //return (status & TIOCM_CTS)?1:0; return (status & TIOCM_CTS)?1:0; }