/* * This file holds the functions necessary for the generic layer * of the DP serial channel driver. Platform-specific functions * in %OS%/dpSerial.c do most of the work since there really is no * standard serial port API. * * DP knows a serial port by two names: the OS specific name (i.e * COM1 or /dev/ttya) and DP's own naming scheme that can be used * on every platform for portable Tcl code: serialX where X is a * number. Please see %OS%/dpSerial.c for more details. * * On Win32, serial2 cooresponds to COM2. On Unix, serial2 * cooresponds to the second serial port. * * YOU CAN ONLY USE ONE NAME TO OPEN A CONNECTION WITH DP_CONNECT: * dp_connect serial -device serialX * */ #include "generic/dpPort.h" #include "generic/dpInt.h" static unsigned int serialCount = 0; static int SerialBlock _ANSI_ARGS_((ClientData instanceData, int mode)); static int SerialInput _ANSI_ARGS_((ClientData instanceData, char *bufPtr, int bufSize, int *errorPtr)); static int SerialOutput _ANSI_ARGS_((ClientData instanceData, char *bufPtr, int toWrite, int *errorPtr)); static int SerialClose _ANSI_ARGS_((ClientData instanceData, Tcl_Interp *interp)); static int SerialSetOption _ANSI_ARGS_((ClientData instanceData, Tcl_Interp *interp, char *nameStr, char *val)); #ifndef _TCL76 static int SerialGetOption _ANSI_ARGS_((ClientData instanceData, Tcl_Interp *interp, char *optionName, Tcl_DString *dsPtr)); #else static int SerialGetOption _ANSI_ARGS_((ClientData instanceData, char *optionName, Tcl_DString *dsPtr)); #endif #ifndef _TCL76 static int SerialGetFile _ANSI_ARGS_((ClientData instanceData, int direction, FileHandle *handlePtr)); #else static Tcl_File SerialGetFile _ANSI_ARGS_((ClientData instanceData, int direction)); #endif #ifdef _TCL76 static int SerialReady _ANSI_ARGS_((ClientData instanceData, int direction)); #endif static void SerialWatch _ANSI_ARGS_((ClientData instanceData, int mask)); static Tcl_ChannelType serialChannelType = { "serial", SerialBlock, SerialClose, SerialInput, SerialOutput, NULL, /* Can't seek! */ SerialSetOption, SerialGetOption, SerialWatch, #ifdef _TCL76 SerialReady, #endif SerialGetFile }; /* -------------------------------------------------- * * DpOpenSerialChannel -- * * Generic routine to open a serial channel * * Returns * * A Tcl_Channel. * * Side Effects * * Opens the serial port and allocates memory. * * --------------------------------------------------- */ Tcl_Channel DpOpenSerialChannel(interp, argc, argv) Tcl_Interp *interp; int argc; char **argv; { Tcl_Channel chan; SerialState *ssPtr; char devStr[15]; char channelNameStr[10]; int block, ro; int i, flags, mode; block = 0; ro = 0; flags = 0; strcpy(devStr, ""); for (i=0; itheFile = Tcl_GetFile((ClientData)ssPtr->fd, SERIAL_HANDLE); #else ssPtr->theFile = (FileHandle)ssPtr->fd; #endif /* * Setup the serial channel to flush every line */ sprintf(channelNameStr, "serial%d", serialCount++); chan = Tcl_CreateChannel(&serialChannelType, channelNameStr, (ClientData) ssPtr, mode); Tcl_RegisterChannel(interp, chan); if (Tcl_SetChannelOption(interp, chan, "-buffering", "line") != TCL_OK) { DpClose(interp, chan); return NULL; } ssPtr->channel = chan; return chan; arg_missing: Tcl_AppendResult(interp, "Value for \"", argv[i], "\" missing", NULL); return NULL; } /* -------------------------------------------------- * * SerialBlock -- * * Generic routine to set I/O to blocking or * non-blocking. * * Returns * * TCL_OK or TCL_ERROR. * * Side Effects * * None. * * --------------------------------------------------- */ static int SerialBlock(instanceData, mode) ClientData instanceData; int mode; /* (in) Block or not */ { return DppSerialBlock(instanceData, mode); } /* -------------------------------------------------- * * SerialInput -- * * Generic read routine for serial ports * * Returns * * Amount read or -1 with errorcode in errorPtr. * * Side Effects * * Buffer is updated. * * --------------------------------------------------- */ static int SerialInput(instanceData, bufPtr, bufSize, errorPtr) ClientData instanceData; char *bufPtr; /* (in) Ptr to buffer */ int bufSize; /* (in) sizeof buffer */ int *errorPtr; /* (out) error code */ { return DppSerialInput(instanceData, bufPtr, bufSize, errorPtr); } /* -------------------------------------------------- * * SerialOutput -- * * Generic write routine for serial ports * * Returns * * Amount written or -1 with errorcode in errorPtr * * Side Effects * * None. * * --------------------------------------------------- */ static int SerialOutput(instanceData, bufPtr, toWrite, errorPtr) ClientData instanceData; char *bufPtr; /* (in) Ptr to buffer */ int toWrite; /* (in) amount to write */ int *errorPtr; /* (out) error code */ { return DppSerialOutput(instanceData, bufPtr, toWrite, errorPtr); } /* -------------------------------------------------- * * SerialClose -- * * Generic routine to close the serial port * * Returns * * 0 if successful or a POSIX errorcode with * interp updated. * * Side Effects * * Channel is deleted. * * --------------------------------------------------- */ static int SerialClose(instanceData, interp) ClientData instanceData; Tcl_Interp *interp; { SerialState *ssPtr = (SerialState *) instanceData; int rc = TCL_OK; rc = DppSerialClose(instanceData); if ((rc != 0) && (interp != NULL)) { Tcl_SetErrno(rc); Tcl_SetResult(interp, Tcl_PosixError(interp), TCL_VOLATILE); } ckfree((char *)ssPtr); return rc; } /* -------------------------------------------------- * * SerialSetOptions -- * * Sets "name" to "val". Possible * options with valid arguments are: * * -parity [odd|even|none] * -charsize [7|8] * -stopbits [1|2] * -baudrate [rate] * -sendBuffer [size] * -recvBuffer [size] * * Returns * * TCL_OK or TCL_ERROR with interp->result updated * * Side Effects * * Changes parameters for this channel * * --------------------------------------------------- */ static int SerialSetOption(instanceData, interp, nameStr, valStr) ClientData instanceData; Tcl_Interp *interp; char *nameStr; /* (in) Name of option */ char *valStr; /* (in) New value of option */ { SerialState *ssPtr = (SerialState *) instanceData; int optVal, option; char errorStr[80]; int optBool; if (nameStr[0] != '-') { option = -1; } else { option = DpTranslateOption(nameStr+1); } switch (option) { case DP_PARITY: if (!strcmp(valStr, "none")) { optVal = PARITY_NONE; } else if (!strcmp(valStr, "even")) { optVal = PARITY_EVEN; } else if (!strcmp(valStr, "odd")) { optVal = PARITY_ODD; } else { sprintf(errorStr, "Parity must be \"even\", \"odd\" or \"none\""); goto argError; } return DppSerialSetOption(ssPtr, DP_PARITY, optVal); case DP_CHARSIZE: if (Tcl_GetInt(interp, valStr, &optVal) == TCL_ERROR) { return TCL_ERROR; } if (optVal != 7 && optVal != 8) { sprintf(errorStr, "Charsize must be 7 or 8"); goto argError; } return DppSerialSetOption(ssPtr, DP_CHARSIZE, optVal); case DP_STOPBITS: if (Tcl_GetInt(interp, valStr, &optVal) == TCL_ERROR) { return TCL_ERROR; } if (optVal != 1 && optVal != 2) { sprintf(errorStr, "Stopbits must be 1 or 2"); goto argError; } return DppSerialSetOption(ssPtr, DP_STOPBITS, optVal); case DP_BAUDRATE: if (Tcl_GetInt(interp, valStr, &optVal) == TCL_ERROR) { return TCL_ERROR; } return DppSerialSetOption(ssPtr, DP_BAUDRATE, optVal); case DP_BLOCK: if (Tcl_GetBoolean(interp, valStr, &optBool) == TCL_ERROR) { return TCL_ERROR; } return DppSerialSetOption(ssPtr, DP_BLOCK, optBool); default: Tcl_AppendResult (interp, "Illegal option \"", nameStr, "\" -- must be charsize, stopbits, parity, baudrate, \ or a standard channel option", NULL); return TCL_ERROR; } argError: Tcl_AppendResult(interp, errorStr, (char *) NULL); return TCL_ERROR; } /* ---------------------------------------------------- * * SerialGetOption -- * * Queries serial channel for the current value of * the given option. * * Returns * * TCL_OK and dsPtr updated with the value or * TCL_ERROR. * * Side Effects * * None. * * ----------------------------------------------------- */ static int SerialGetOption(instanceData, #ifdef _TCL80 interp, #endif optionName, dsPtr) ClientData instanceData; #ifdef _TCL80 Tcl_Interp *interp; #endif char *optionName; /* (in) Name of option to retrieve */ Tcl_DString *dsPtr; /* (in) String to place value */ { SerialState *ssPtr = (SerialState *) instanceData; int option; if (optionName != NULL) { if (optionName[0] != '-') { option = -1; } else { option = DpTranslateOption(optionName+1); } } else { Tcl_DStringAppend(dsPtr, " -charsize ", -1); DppSerialGetOption(ssPtr, DP_CHARSIZE, dsPtr); Tcl_DStringAppend(dsPtr, " -stopbits ", -1); DppSerialGetOption(ssPtr, DP_STOPBITS, dsPtr); Tcl_DStringAppend(dsPtr, " -baudrate ", -1); DppSerialGetOption(ssPtr, DP_BAUDRATE, dsPtr); Tcl_DStringAppend(dsPtr, " -parity ", -1); DppSerialGetOption(ssPtr, DP_PARITY, dsPtr); Tcl_DStringAppend(dsPtr, " -device ", -1); DppSerialGetOption(ssPtr, DP_DEVICENAME, dsPtr); return TCL_OK; } if (option == -1) { #ifndef _TCL76 Tcl_AppendResult(interp, "bad option \"", optionName,"\": must be -blocking,", " -buffering, -buffersize, -eofchar, -translation,", " or a channel type specific option", NULL); #else char errStr[128]; sprintf(errStr, "bad option \"%s\": must be -blocking," "-buffering, -buffersize, -eofchar, -translation," " or a channel type specific option", optionName); Tcl_DStringAppend(dsPtr, errStr, -1); #endif Tcl_SetErrno(EINVAL); return TCL_ERROR; } return DppSerialGetOption(ssPtr, option, optionName, dsPtr); } /* ---------------------------------------------------- * * SerialGetFile -- * * See below * * Returns * * TCL_OK * * Side Effects * * None. * * ----------------------------------------------------- */ #ifndef _TCL76 static int SerialGetFile(instanceData, direction, handlePtr) ClientData instanceData; int direction; FileHandle *handlePtr; { SerialState *statePtr = (SerialState *)instanceData; *handlePtr = statePtr->theFile; return TCL_OK; } #else static Tcl_File SerialGetFile(instanceData, direction) ClientData instanceData; int direction; { SerialState *statePtr = (SerialState *)instanceData; return statePtr->theFile; } #endif /* ---------------------------------------------------- * * SerialReady -- * * Determines whether serial port has data to be * read or is OK for writing. * * Returns * * A bitmask of the events that were found (i.e. * TCL_FILE_READABLE | TCL_FILE_WRITABLE). * * Side Effects * * None. * * ----------------------------------------------------- */ #ifdef _TCL76 static int SerialReady(instanceData, direction) ClientData instanceData; int direction; { return DppSerialFileReady(instanceData, direction); } #endif /* ---------------------------------------------------- * * SerialWatch -- * * Sets up event handling on a serial port Tcl_Channel * * Returns * * Nothing * * Side Effects * * None. * * ----------------------------------------------------- */ static void SerialWatch(instanceData, mask) ClientData instanceData; int mask; { DppSerialWatchFile(instanceData, mask); }