/*************************************************************************** CSerialPort.c Network component (c) 2003-2004 Daniel Campos Fernández This program 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 1, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ***************************************************************************/ #define __CSERIALPORT_C #include #include #include #include #include #include #ifdef __CYGWIN__ /* Cygwin defines FIONREAD in . */ #include #endif /* __CYGWIN__ */ #include "main.h" #include "tools.h" #include "CSerialPort.h" #define MAX_SERIAL_BUFFER_SIZE 65536 long *ser_objwatch=NULL; long *ser_portwatch=NULL; long ser_numwatch=0; GB_STREAM_DESC SerialStream = { open: CSerialPort_stream_open, close: CSerialPort_stream_close, read: CSerialPort_stream_read, write: CSerialPort_stream_write, seek: CSerialPort_stream_seek, tell: CSerialPort_stream_tell, flush: CSerialPort_stream_flush, eof: CSerialPort_stream_eof, lof: CSerialPort_stream_lof, handle: CSerialPort_stream_handle }; DECLARE_EVENT (Serial_Read); DECLARE_EVENT (Serial_DTR); DECLARE_EVENT (Serial_DSR); DECLARE_EVENT (Serial_RTS); DECLARE_EVENT (Serial_CTS); DECLARE_EVENT (Serial_DCD); DECLARE_EVENT (Serial_RNG); void Serial_Signal_Status(serialsignal *sdata,int iPort) { int ist; sdata->s_DSR=0; sdata->s_DTR=0; sdata->s_RTS=0; sdata->s_CTS=0; sdata->s_DCD=0; sdata->s_RNG=0; ioctl(iPort,TIOCMGET,&ist); if ( ist & TIOCM_DSR ) sdata->s_DSR=1; if ( ist & TIOCM_DTR ) sdata->s_DTR=1; if ( ist & TIOCM_RTS ) sdata->s_RTS=1; if ( ist & TIOCM_CTS ) sdata->s_CTS=1; if ( ist & TIOCM_CAR ) sdata->s_DCD=1; if ( ist & TIOCM_RNG ) sdata->s_RNG=1; } void CSerialPort_Event(long param) { serialevent *eparam; eparam=(serialevent*)param; switch(eparam->nevent) { case 0: GB.Raise(eparam->obj,Serial_DTR,1,GB_T_BOOLEAN,eparam->value); break; case 1: GB.Raise(eparam->obj,Serial_DSR,1,GB_T_BOOLEAN,eparam->value); break; case 2: GB.Raise(eparam->obj,Serial_RTS,1,GB_T_BOOLEAN,eparam->value); break; case 3: GB.Raise(eparam->obj,Serial_CTS,1,GB_T_BOOLEAN,eparam->value); break; case 4: GB.Raise(eparam->obj,Serial_DCD,1,GB_T_BOOLEAN,eparam->value); break; case 5: GB.Raise(eparam->obj,Serial_RNG,1,GB_T_BOOLEAN,eparam->value); break; } GB.Unref((void**)&eparam->obj); } void CSerialPort_CallBack(long lParam) { int position=0; serialsignal newstatus; struct pollfd mypoll; int numpoll; CSERIALPORT *mythis; /* Just sleeping a bit to reduce CPU waste */ struct timespec mywait; mywait.tv_sec=0; mywait.tv_nsec=1000000; nanosleep(&mywait,NULL); if ((position=search_by_integer(ser_portwatch,ser_numwatch,(int)lParam))==-1) return; mythis=(CSERIALPORT*)ser_objwatch[position]; /* Serial port signals status */ Serial_Signal_Status(&newstatus,lParam); mythis->e_DTR.value=newstatus.s_DTR; mythis->e_DSR.value=newstatus.s_DSR; mythis->e_RTS.value=newstatus.s_RTS; mythis->e_CTS.value=newstatus.s_CTS; mythis->e_DCD.value=newstatus.s_DCD; mythis->e_RNG.value=newstatus.s_RNG; if (mythis->ser_status.s_DTR != newstatus.s_DTR ) { mythis->ser_status.s_DTR = newstatus.s_DTR; GB.Ref(mythis); GB.Post(CSerialPort_Event,(long)&mythis->e_DTR); } if (mythis->ser_status.s_DSR != newstatus.s_DSR ) { mythis->ser_status.s_DSR = newstatus.s_DSR; GB.Ref(mythis); GB.Post(CSerialPort_Event,(long)&mythis->e_DSR); } if (mythis->ser_status.s_RTS != newstatus.s_RTS ) { mythis->ser_status.s_RTS = newstatus.s_RTS; GB.Ref(mythis); GB.Post(CSerialPort_Event,(long)&mythis->e_RTS); } if (mythis->ser_status.s_CTS != newstatus.s_CTS ) { mythis->ser_status.s_CTS = newstatus.s_CTS; GB.Ref(mythis); GB.Post(CSerialPort_Event,(long)&mythis->e_CTS); } if (mythis->ser_status.s_DCD != newstatus.s_DCD ) { mythis->ser_status.s_DCD = newstatus.s_DCD; GB.Ref(mythis); GB.Post(CSerialPort_Event,(long)&mythis->e_DCD); } if (mythis->ser_status.s_RNG != newstatus.s_RNG ) { mythis->ser_status.s_RNG = newstatus.s_RNG; GB.Ref(mythis); GB.Post(CSerialPort_Event,(long)&mythis->e_RNG); } /* Data Available */ mypoll.fd=lParam; mypoll.events=POLLIN; mypoll.revents=0; numpoll=poll(&mypoll,1,0); if (numpoll) { GB.Raise((void*)mythis,Serial_Read,0); } } void CSerialPort_AssignCallBack(long t_obj,int t_port) { int position=0; CSERIALPORT *mythis; mythis=(CSERIALPORT*)t_obj; position=search_by_integer(ser_objwatch,ser_numwatch,t_obj); if (position>=0) GB.Watch (t_port , GB_WATCH_NONE , (void *)CSerialPort_CallBack,0); if (position<0) { position=ser_numwatch++; Alloc_CallBack_Pointers(ser_numwatch,&ser_objwatch,&ser_portwatch); } ser_objwatch[position]=t_obj; ser_portwatch[position]=t_port; GB.Watch (t_port , GB_WATCH_READ , (void *)CSerialPort_CallBack,0); } void CSerialPort_FreeCallBack(long t_obj) { int myloop; int position; position=search_by_integer(ser_objwatch,ser_numwatch,t_obj); if ( position==-1 ) return; GB.Watch (ser_portwatch[position] , GB_WATCH_NONE , (void *)CSerialPort_CallBack,0); for ( myloop=position;myloop < (ser_numwatch-1);myloop++ ) { ser_objwatch[myloop]=ser_objwatch[myloop+1]; ser_portwatch[myloop]=ser_portwatch[myloop+1]; } ser_numwatch--; Alloc_CallBack_Pointers(ser_numwatch,&ser_objwatch,&ser_portwatch); } //////////////////////////////////////////////////////////////////////////////////// // // //********************************************************************************** //=================================================================================== //##################STREAM RELATED FUNCTIONS######################################### //=================================================================================== //***********************************************************************************/ // //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // ///////////////////////////////////////////////////////////////////////////////////// int CSerialPort_stream_open(GB_STREAM *stream, const char *path, int mode, void *data) { return -1; /* not allowed */ } int CSerialPort_stream_seek(GB_STREAM *stream, int64_t pos, int whence) { return -1; /* not allowed */ } int CSerialPort_stream_tell(GB_STREAM *stream, int64_t *pos) { return -1; /* not allowed */ } int CSerialPort_stream_flush(GB_STREAM *stream) { return 0; /* OK */ } int CSerialPort_stream_handle(GB_STREAM *stream) { void *_object = stream->tag; return THIS->Port; } int CSerialPort_stream_close(GB_STREAM *stream) { void *_object = stream->tag; if (!_object) return -1; if (THIS->iStatus) { CSerialPort_FreeCallBack((long)THIS); THIS->stream.desc=NULL; CloseSerialPort(THIS->Port,&THIS->oldtio); THIS->iStatus=0; } return 0; } int CSerialPort_stream_lof(GB_STREAM *stream, int64_t *len) { void *_object = stream->tag; int bytes; *len=0; if (!_object) return -1; if (ioctl(THIS->Port,FIONREAD,&bytes)) return -1; *len=bytes; return 0; } int CSerialPort_stream_eof(GB_STREAM *stream) { void *_object = stream->tag; int bytes; if (!_object) return -1; if (ioctl(THIS->Port,FIONREAD,&bytes)) return -1; if (!bytes) return -1; return 0; } int CSerialPort_stream_read(GB_STREAM *stream, char *buffer, int len) { void *_object = stream->tag; int npos=-1; int NoBlock=0; int bytes; if (!_object) return -1; if (ioctl(THIS->Port,FIONREAD,&bytes)) return -1; if (bytes < len) return -1; ioctl(THIS->Port,FIONBIO,&NoBlock); npos=read(THIS->Port,(void*)buffer,len); NoBlock++; ioctl(THIS->Port,FIONBIO,&NoBlock); if (npos==len) return 0; return -1; } int CSerialPort_stream_write(GB_STREAM *stream, char *buffer, int len) { void *_object = stream->tag; int npos=-1; int NoBlock=0; if (!_object) return -1; ioctl(THIS->Port,FIONBIO,&NoBlock); npos=write(THIS->Port,(void*)buffer,len); NoBlock++; ioctl(THIS->Port,FIONBIO,&NoBlock); if (npos>=0) return 0; return -1; } /************************************************************************ ########################################################################## GAMBAS OBJECT PUBLIC INTERFACE ########################################################################## **************************************************************************/ /******************************************************************** Returns current Status of the port (0->Closed, 1 --> Opened) *********************************************************************/ BEGIN_PROPERTY ( CSERIALPORT_Status ) GB.ReturnInteger(THIS->iStatus); END_PROPERTY /************************************* Data Set Ready *************************************/ BEGIN_PROPERTY ( CSERIALPORT_DSR ) if ( !THIS->iStatus ) { GB.ReturnBoolean(0); return; } Serial_Signal_Status(&THIS->ser_status,THIS->Port); GB.ReturnBoolean(THIS->ser_status.s_DSR); END_PROPERTY /*************************************** Data Transmission Ready ***************************************/ BEGIN_PROPERTY ( CSERIALPORT_DTR ) int ist; if (READ_PROPERTY) { if ( !THIS->iStatus ) { GB.ReturnBoolean(0); return; } Serial_Signal_Status(&THIS->ser_status,THIS->Port); GB.ReturnBoolean(THIS->ser_status.s_DTR); return; } if (!THIS->iStatus ) { GB.Error ("Port is closed"); return; } ioctl(THIS->Port,TIOCMGET,&ist); if (!VPROP(GB_BOOLEAN)) ist &= ~TIOCM_DTR; else ist = ist | TIOCM_DTR; ioctl(THIS->Port,TIOCMSET,&ist); END_PROPERTY /**************************************** Ready to send ****************************************/ BEGIN_PROPERTY ( CSERIALPORT_RTS ) int ist; if (READ_PROPERTY) { if ( !THIS->iStatus ) { GB.ReturnBoolean(0); return; } Serial_Signal_Status(&THIS->ser_status,THIS->Port); GB.ReturnBoolean(THIS->ser_status.s_RTS); return; } if (!THIS->iStatus ) { GB.Error ("Port is closed"); return; } ioctl(THIS->Port,TIOCMGET,&ist); if(!VPROP(GB_BOOLEAN)) ist &= ~TIOCM_RTS; else ist = ist | TIOCM_RTS; ioctl(THIS->Port,TIOCMSET,&ist); END_PROPERTY /**************************************** Clear to send ****************************************/ BEGIN_PROPERTY ( CSERIALPORT_CTS ) if ( !THIS->iStatus ) { GB.ReturnBoolean(0); return; } Serial_Signal_Status(&THIS->ser_status,THIS->Port); GB.ReturnBoolean(THIS->ser_status.s_CTS); END_PROPERTY /*************************************** Data Carrier Detect ***************************************/ BEGIN_PROPERTY ( CSERIALPORT_DCD ) if ( !THIS->iStatus ) { GB.ReturnBoolean(0); return; } Serial_Signal_Status(&THIS->ser_status,THIS->Port); GB.ReturnBoolean(THIS->ser_status.s_DCD); END_PROPERTY /*************************************** Ring ***************************************/ BEGIN_PROPERTY ( CSERIALPORT_RNG ) if ( !THIS->iStatus ) { GB.ReturnBoolean(0); return; } Serial_Signal_Status(&THIS->ser_status,THIS->Port); GB.ReturnBoolean(THIS->ser_status.s_RNG); END_PROPERTY /********************************************************************* Gets / Sets serial port name (on Linux /dev/ttyS0, etc) **********************************************************************/ BEGIN_PROPERTY ( CSERIALPORT_Port ) if (READ_PROPERTY) { GB.ReturnString(THIS->sPort); return; } if ( THIS->iStatus ) { GB.Error("Current port must be closed first"); return; } GB.StoreString(PROP(GB_STRING), &THIS->sPort); END_PROPERTY /******************************************************************************* FlowControl : 1->CRTSCTS , 2-> XON/XOFF , 3-> XON/OFF plus CRTSCTS, 4 --> NONE ********************************************************************************/ BEGIN_PROPERTY ( CSERIALPORT_FlowControl ) if (READ_PROPERTY) { GB.ReturnInteger(THIS->iFlow); return; } if ( THIS->iStatus ) { GB.Error("Current port must be closed first"); return; } if ( (VPROP(GB_INTEGER)<0) || (VPROP(GB_INTEGER)>3) ) { GB.Error("Invalid flow control value"); return; } THIS->iFlow=VPROP(GB_INTEGER); END_PROPERTY /********************************************************************* Gets / Sets serial parity (E,O,N) **********************************************************************/ BEGIN_PROPERTY ( CSERIALPORT_Parity ) int parity; if (READ_PROPERTY) { GB.ReturnInteger(THIS->Parity); } else { if ( THIS->iStatus ) GB.Error("Current port must be closed first"); else { parity = VPROP(GB_INTEGER); if (parity < 0 || parity > 2) GB.Error("Invalid parity"); else THIS->Parity = parity; /* GB.StoreString(PROP(GB_STRING), &tmpstring); if ( GB.StringLength (tmpstring) != 1 ) { myok=0; } else { if (ConvertParity(tmpstring[0]) == -1) myok=0; } GB.FreeString(&tmpstring); if (!myok) GB.Error("Invalid parity value."); else GB.StoreString(PROP(GB_STRING), &THIS->Parity); */ } } END_PROPERTY /********************************************************************* Gets / Sets serial port Speed **********************************************************************/ BEGIN_PROPERTY ( CSERIALPORT_Speed ) int myok=1; if (READ_PROPERTY) { GB.ReturnInteger(THIS->Speed); return; } if ( THIS->iStatus ){ GB.Error("Current port must be closed first");return; } if ( !VPROP(GB_INTEGER) ) myok=0; if ( ConvertBaudRate(VPROP(GB_INTEGER)) == -1) myok=0; if (!myok) GB.Error("Invalid speed value"); else THIS->Speed=VPROP(GB_INTEGER); END_PROPERTY /********************************************************************* Gets / Sets serial port Data Bits **********************************************************************/ BEGIN_PROPERTY ( CSERIALPORT_DataBits ) int myok=1; if (READ_PROPERTY) { GB.ReturnInteger(THIS->DataBits); return; } if ( THIS->iStatus ) { GB.Error("Current port must be closed first"); return; } if ( ConvertDataBits(VPROP(GB_INTEGER)) == -1) myok=0; if (!myok) GB.Error("Invalid data bits value"); else THIS->DataBits=VPROP(GB_INTEGER); END_PROPERTY /********************************************************************* Gets / Sets serial port Stop Bits **********************************************************************/ BEGIN_PROPERTY ( CSERIALPORT_StopBits ) int myok=1; if (READ_PROPERTY) { GB.ReturnInteger(THIS->StopBits); return; } if ( THIS->iStatus ){GB.Error("Current port must be closed first");return;} if ( ConvertStopBits(VPROP(GB_INTEGER)) == -1) myok=0; if (!myok) GB.Error("Invalid stop bits value"); else THIS->StopBits=VPROP(GB_INTEGER); END_PROPERTY /************************************************* Gambas object "Constructor" *************************************************/ BEGIN_METHOD_VOID(CSERIALPORT_new) THIS->Port=0; THIS->iStatus=0; THIS->sPort=NULL; GB.NewString(&THIS->sPort,"/dev/ttyS0",10); THIS->Speed=19200; THIS->Parity=0; THIS->DataBits=8; THIS->StopBits=1; THIS->iFlow=1; END_METHOD /************************************************* Gambas object "Destructor" *************************************************/ BEGIN_METHOD_VOID(CSERIALPORT_free) if (THIS->iStatus) { CSerialPort_FreeCallBack((long)THIS); //GB.Stream.Init(&THIS->stream,-1); CloseSerialPort(THIS->Port,&THIS->oldtio); THIS->iStatus=0; } GB.FreeString(&THIS->sPort); END_METHOD /************************************************* To open the port *************************************************/ BEGIN_METHOD_VOID(CSERIALPORT_Open) int err; char buffer[8]; if (THIS->iStatus) { GB.Error("Port is already opened"); return; } if ((err = OpenSerialPort(&THIS->Port, THIS->iFlow, &THIS->oldtio, THIS->sPort, THIS->Speed, THIS->Parity, THIS->DataBits, THIS->StopBits))) { sprintf(buffer, "#%d", err); GB.Error("Cannot open serial port (&1)", buffer); return; } THIS->e_DTR.nevent=0; THIS->e_DSR.nevent=1; THIS->e_RTS.nevent=2; THIS->e_CTS.nevent=3; THIS->e_DCD.nevent=4; THIS->e_RNG.nevent=5; THIS->e_DTR.obj=THIS; THIS->e_DSR.obj=THIS; THIS->e_RTS.obj=THIS; THIS->e_CTS.obj=THIS; THIS->e_DCD.obj=THIS; THIS->e_RNG.obj=THIS; Serial_Signal_Status(&THIS->ser_status,THIS->Port); CSerialPort_AssignCallBack((long)THIS,THIS->Port); //CSerialPort_stream_init(&THIS->stream,THIS->Port); THIS->stream.desc=&SerialStream; THIS->iStatus=1; THIS->stream.tag = THIS; END_METHOD /*************************************************************** Here we declare the public interface of SerialPort class ***************************************************************/ GB_DESC CSerialPortDesc[] = { GB_DECLARE("SerialPort", sizeof(CSERIALPORT)), GB_INHERITS("Stream"), GB_CONSTANT("None", "i", 0), GB_CONSTANT("Hardware", "i", 1), GB_CONSTANT("Software", "i", 2), GB_CONSTANT("Both", "i", 3), GB_CONSTANT("Even", "i", 1), GB_CONSTANT("Odd", "i", 2), GB_CONSTANT("Bits1", "i", 1), GB_CONSTANT("Bits2", "i", 2), GB_CONSTANT("Bits5", "i", 5), GB_CONSTANT("Bits6", "i", 6), GB_CONSTANT("Bits7", "i", 7), GB_CONSTANT("Bits8", "i", 8), GB_EVENT("Read", NULL, NULL, &Serial_Read), GB_EVENT("DTRChange", NULL, "(CurrentValue)b", &Serial_DTR), GB_EVENT("DSRChange", NULL, "(CurrentValue)b", &Serial_DSR), GB_EVENT("RTSChange", NULL, "(CurrentValue)b", &Serial_RTS), GB_EVENT("CTSChange", NULL, "(CurrentValue)b", &Serial_CTS), GB_EVENT("DCDChange", NULL, "(CurrentValue)b", &Serial_DCD), GB_EVENT("RNGChange", NULL, "(CurrentValue)b", &Serial_RNG), GB_METHOD("_new", NULL, CSERIALPORT_new, NULL), GB_METHOD("_free", NULL, CSERIALPORT_free, NULL), GB_METHOD("Open", NULL, CSERIALPORT_Open, NULL), //GB_METHOD("Close", NULL, CSERIALPORT_Close, NULL), GB_PROPERTY("FlowControl","i",CSERIALPORT_FlowControl), GB_PROPERTY("PortName", "s", CSERIALPORT_Port), GB_PROPERTY("Parity", "i", CSERIALPORT_Parity), GB_PROPERTY("Speed", "i", CSERIALPORT_Speed), GB_PROPERTY("DataBits", "i", CSERIALPORT_DataBits), GB_PROPERTY("StopBits", "i", CSERIALPORT_StopBits), GB_PROPERTY("DTR", "b", CSERIALPORT_DTR), GB_PROPERTY("RTS", "b", CSERIALPORT_RTS), GB_PROPERTY_READ("Status", "i", CSERIALPORT_Status), GB_PROPERTY_READ("DSR", "b", CSERIALPORT_DSR), GB_PROPERTY_READ("CTS", "b", CSERIALPORT_CTS), GB_PROPERTY_READ("DCD", "b", CSERIALPORT_DCD), GB_PROPERTY_READ("RNG", "b", CSERIALPORT_RNG), GB_CONSTANT("_Properties", "s", "FlowControl{SerialPort.None;Hardware;Software;Both}=Hardware,PortName,Parity{SerialPort.None;Even;Odd}=None,Speed=19200,DataBits{SerialPort.Bits5;Bits6;Bits7;Bits8}=Bits8,StopBits{SerialPort.Bits1;Bits2}=Bits1"), GB_CONSTANT("_DefaultEvent", "s", "Read"), GB_END_DECLARE };