/*********************************************************************/
/*                                                                   */
/*  This Program Written by Paul Edwards                             */
/*  Released to the Public Domain                                    */
/*                                                                   */
/*********************************************************************/
/*********************************************************************/
/*                                                                   */
/*  pdcommo - Com routines for OS/2                                  */
/*                                                                   */
/*  1999-08-14: Fixed bug with DosDevIOCtl as suggested by           */
/*              Eric Handbury.                                       */
/*                                                                   */
/*********************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#include "error.h"
#include "pdcommo.h"

#if 0                          
static void pdcommQueryLine(PDCOMM *pdcomm, 
                            int *parity,
                            int *data,
                            int *stop);
static void pdcommDisableXON(PDCOMM *pdcomm);
#endif                            
                            
void pdcommDefaults(PDCOMM *pdcomm)
{
    (void)pdcomm;
    return;
}

/* Supports the following syntax (e.g. to open port 3):
   3
   COM3
   COM3:
   COM3:19200
*/

void pdcommInit(PDCOMM *pdcomm, char *name)
{
    APIRET rc;
    ULONG  action;
    ULONG  newsize = 0;
    ULONG  fileAttr = 0;
    ULONG  openAction = OPEN_ACTION_OPEN_IF_EXISTS;
    ULONG  openMode = OPEN_SHARE_DENYREADWRITE | OPEN_ACCESS_READWRITE;
    char   fileName[FILENAME_MAX];
    unsigned long speed;
    char *p;
    
    strcpy(fileName, name);
    p = strchr(fileName, ':');
    if (p != NULL)
    {
        speed = atol(p + 1);
        *p = '\0';
    }
    else
    {
        speed = 0;
        if (isdigit(*fileName))
        {
            sprintf(fileName, "COM%c", *fileName);
        }
    }
    rc = DosOpen(fileName,
                 &pdcomm->hfile, 
                 &action, 
                 newsize, 
                 fileAttr,
                 openAction, 
                 openMode, 
                 NULL);
    if (rc != 0)
    {
        errorSet(COMERROR);
        printf("rc from DosOpen is %lu\n", (unsigned long)rc);
    }
    else
    {
        if (speed != 0)
        {
            pdcommSetSpeed(pdcomm, speed);
        }
        pdcommSetParms(pdcomm, PDCOMM_NO_PARITY, 8, 1);
        if (ALLOK)
        {
            pdcommRaiseDTR(pdcomm);
        }
    }
    return;
}                 

void pdcommSetSpeed(PDCOMM *pdcomm, unsigned long speed)
{
    APIRET rc;
    
    rc = DosDevIOCtl(pdcomm->hfile, 
                     IOCTL_ASYNC,
                     0x43, /* set extended bit rate */
                     &speed,
                     sizeof speed,
                     NULL,
                     NULL,
                     0,
                     NULL);
    if (rc != 0)
    {
        printf("rc from DosDevIoCtl is %lu\n", (unsigned long)rc);
        errorSet(COMERROR);
    }
    return;
}

void pdcommSetParms(PDCOMM *pdcomm, int parity, int data, int stop)
{
    APIRET rc;
    struct {
        BYTE data;
        BYTE parity;
        BYTE stop;
    } lineCharacter;

    lineCharacter.data = (BYTE)data;
    lineCharacter.parity = (BYTE)parity;
    if (stop == 1)
    {
        stop = 0;
    }
    lineCharacter.stop = (BYTE)stop;  
    rc = DosDevIOCtl(pdcomm->hfile, 
                     IOCTL_ASYNC,
                     0x42,
                     &lineCharacter,
                     sizeof lineCharacter,
                     NULL,
                     NULL,
                     0,
                     NULL);
    if (rc != 0)
    {
        printf("rc from DosDevIoCtl is %lu\n", (unsigned long)rc);
        errorSet(COMERROR);
    }
    return;
}

#if 0
static void pdcommQueryLine(PDCOMM *pdcomm, 
                            int *parity,
                            int *data,
                            int *stop)
{
    APIRET rc;
    struct {
        BYTE data;
        BYTE parity;
        BYTE stop;
        BYTE brk;
    } lineCharacter;
    
    rc = DosDevIOCtl(pdcomm->hfile, 
                     IOCTL_ASYNC,
                     0x62, /* set extended bit rate */
                     NULL,
                     0,
                     NULL,
                     &lineCharacter,
                     sizeof lineCharacter,
                     NULL);
    if (rc != 0)
    {
        printf("rc from DosDevIoCtl is %lu\n", (unsigned long)rc);
        errorSet(COMERROR);
    }
    else
    {
        *parity = (int)lineCharacter.parity;
        *data = (int)lineCharacter.data;
        *stop = (int)lineCharacter.stop;
    }    
    return;
}
#endif

void pdcommRaiseDTR(PDCOMM *pdcomm)
{
    APIRET rc;
    struct {
        BYTE on;
        BYTE off;
    } parm;
    unsigned short controlWord;
    
    parm.on = (BYTE)0x01;
    parm.off = (BYTE)0xff;
    rc = DosDevIOCtl(pdcomm->hfile, 
                     IOCTL_ASYNC,
                     0x46, 
                     &parm,
                     sizeof parm,
                     NULL,
                     &controlWord,
                     sizeof controlWord,
                     NULL);
    if (rc != 0)
    {
        printf("rc from DosDevIoCtl is %lu\n", (unsigned long)rc);
        errorSet(COMERROR);
    }
    return;
}

void pdcommDropDTR(PDCOMM *pdcomm)
{
    APIRET rc;
    struct {
        BYTE on;
        BYTE off;
    } parm;
    unsigned short controlWord;
    
    parm.on = 0x00;
    parm.off = 0xfe;
    rc = DosDevIOCtl(pdcomm->hfile, 
                     IOCTL_ASYNC,
                     0x46, 
                     &parm,
                     sizeof parm,
                     NULL,
                     &controlWord,
                     sizeof controlWord,
                     NULL);
    if (rc != 0)
    {
        printf("rc from DosDevIoCtl is %lu\n", (unsigned long)rc);
        errorSet(COMERROR);
    }
    return;
}

#if 0
static void pdcommDisableXON(PDCOMM *pdcomm)
{
    APIRET rc;
    struct {
        unsigned short write;
        unsigned short read;
        BYTE flags1;
        BYTE flags2;
        BYTE flags3;
        BYTE err;
        BYTE brk;
        BYTE xon;
        BYTE xoff;
    } parm;
    
    rc = DosDevIOCtl(pdcomm->hfile, 
                     IOCTL_ASYNC,
                     0x73, 
                     NULL,
                     0,
                     NULL,
                     &parm,
                     sizeof parm,
                     NULL);
    if (rc != 0)
    {
        printf("rc from DosDevIoCtl is %lu\n", (unsigned long)rc);
        errorSet(COMERROR);
    }
    else
    {
        parm.flags2 &= ~0x03;
        rc = DosDevIOCtl(pdcomm->hfile, 
                         IOCTL_ASYNC,
                         0x53, 
                         &parm,
                         sizeof parm,
                         NULL,
                         NULL,
                         0,
                         NULL);
        if (rc != 0)
        {
            printf("rc from DosDevIoCtl is %lu\n", (unsigned long)rc);
            errorSet(COMERROR);
        }
    }
    return;
}
#endif

int pdcommWriteCh(PDCOMM *pdcomm, int ch)
{
    ULONG actualWritten;
    int rc;
    int ret;
    unsigned char buf[1];
    
    buf[0] = (unsigned char)ch;
    rc = DosWrite(pdcomm->hfile, buf, 1, &actualWritten);
    if (rc != 0)
    {
        printf("rc from DosWrite is %lu\n", (unsigned long)rc);
        ret = EOF;
        errorSet(COMERROR);
    }
    else
    {
        ret = ch;
    }
    return (ret);
}    

size_t pdcommWriteBuf(PDCOMM *pdcomm, void *buf, size_t num)
{
    ULONG actualWritten;
    int rc;
    
    rc = DosWrite(pdcomm->hfile, buf, num, &actualWritten);
    if (rc != 0)
    {
        printf("rc from DosWrite is %lu\n", (unsigned long)rc);
        errorSet(COMERROR);
        actualWritten = 0;
    }
    else if (actualWritten != num)
    {
        printf("only wrote %d bytes instead of %d\n", actualWritten, num);
        errorSet(COMERROR);
    }
    return ((size_t)actualWritten);
}

int pdcommReadCh(PDCOMM *pdcomm)
{
    ULONG actualRead;
    unsigned char buf[1];
    int rc;
    int ret = EOF;
    RXQUEUE InQue;

    rc = DosDevIOCtl(pdcomm->hfile, 
                     IOCTL_ASYNC,
                     0x68, /* query # characters in receive queue */
                     NULL,
                     0,
                     NULL,
                     &InQue,
                     sizeof InQue,
                     NULL);

    if (rc != 0)
    {
        printf("rc from DosDevIOCtl is %lu\n", (unsigned long)rc);
        return (EOF);
    }
    if (InQue.cch == 0)
    {
        return (EOF);
    }
    rc = DosRead(pdcomm->hfile, buf, 1, &actualRead);
    if (rc != 0)
    {
        printf("rc from DosRead is %lu\n", (unsigned long)rc);
    }
    else
    {
        if (actualRead == 1)
        {
            ret = buf[0];
        }
    }
    return (ret);
}    

size_t pdcommReadBuf(PDCOMM *pdcomm, void *buf, size_t num)
{
    ULONG  actualRead;
    int    rc;
    RXQUEUE InQue;

    rc = DosDevIOCtl(pdcomm->hfile, 
                     IOCTL_ASYNC,
                     0x68, /* query # characters in receive queue */
                     NULL,
                     0,
                     NULL,
                     &InQue,
                     sizeof InQue,
                     NULL);

    if (rc != 0)
    {
        printf("rc from DosDevIOCtl is %lu\n", (unsigned long)rc);
        return (0);
    }
    if (InQue.cch == 0)
    {
        return (0);
    }
    if (num > InQue.cch)
    {
        num = InQue.cch;
    }
    rc = DosRead(pdcomm->hfile, buf, num, &actualRead);
    if (rc != 0)
    {
        printf("rc from DosRead is %lu\n", (unsigned long)rc);
        return (0);
    }
    return ((size_t)actualRead);
}    

void pdcommTerm(PDCOMM *pdcomm)
{
    int rc;
    
    pdcommDropDTR(pdcomm);
    rc = DosClose(pdcomm->hfile);
    if (rc != 0)
    {
        printf("rc from DosClose is %lu\n", (unsigned long)rc);
        errorSet(COMERROR);
    }
    return;
}
