//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / OVOPS++
//
//File: device.cpp
//
//Version: $Revision: 1.17 $
//
//State: $State: Exp $
//
//Date: $Date: 1998/10/19 08:46:02 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
//
//Authors:
//      Juhana Räsänen
//      Timo Kokkonen
//
//Description:
//      See the corresponding header file
//
//Copyright:
//      Copyright 1999 Helsinki University of Technology
//      ALL RIGHTS RESERVED BETWEEN JANUARY 1996 AND JUNE 1999.
//
//Licence:
//
//
//History: 
//
//

#include "device.h"
#include <unistd.h>	// read and write
#include <stdio.h>	// perror
#include "debug.h"
#include "system.h"

//
//Function: pfDevice constructor
//
//Description:
//    The constructor initializes the device and leaves it to
//    an idle state waiting for the call of open member function,
//    which finally opens the file. 
//    Since pfDevice is an abstract base class, this constructor
//    must be called from the derived actual devices.
//    Initially the device is set into non-polling state.
//

pfDevice :: pfDevice(pfUlong bufferSize_)
    : pfAdapter(),
      _writeFrame(),
      _readPending(0),
      _writePending(0),
      _bufferSize(bufferSize_),
      _readBuffer(0),
      _writeBuffer(0),
      _readBufferLength(0),
      _writeBufferLength(0),
      _fd(NO_FD),
      _status(IDLE),
      _defTimeout(0)
{
    pfSystem::instance()->registerDevice(this);
    return;
}

//
//Function: pfDevice destructor
//
//Description:
//    Does nothing. This is because base class destructor is called
//    *after* the derived class destructor, and you can't access any
//    derived class structures or methods from the base class destructor
//    because they (supposedly) are already deleted. This means that
//    eg. freeBuffers() must be called from the derived class destructor
//    and because buffers can't be freed until device is closed, also
//    closeDevice() must be called from the derived class destructor.
//    So, be careful when implementing devices!
//

pfDevice :: ~pfDevice(void)
{
    return;
}

//
//Function: closeDevice
//
//Description:
//    Closes the file.
//    Returns zero if the closing was successful. Otherwise, returns
//    non-zero value.
//

bool pfDevice :: closeDevice(void)
{
    bool failure = 0;
    if (_fd != NO_FD)
    {
        if (::close(_fd) == -1)
        {
            debugUser("pfDevice: Error in closeDevice()");
            perror("close");
            failure = 1;
        }
        else
        {
            setFD(NO_FD);
            _readPending = 0;
            _writePending = 0;
        }
    }

    return failure;
}

//
//Function: readDevice
//
//Description:
//    Takes in a request to read data from the device. Only one
//    request can be active at time. When the request has been 
//    served the Device::READ bit in the status field is cleared 
//    and the readAction function is called. If an error happens
//    during reading the Device::ERROR bit is set in the status
//    field. 
//
//    Returns zero if the request to read is accepted. If there is 
//    already a request to read it is error and non-zero value is
//    returned.
//

bool pfDevice :: readDevice(void)
{
    bool failure = 0;
    if (_readPending != 0)
    {
	// Previous read wasn't completed. We cannot queue requests.
	failure = 1;
    }
    else
    {
        // If the device is set to poll the FD, callbackRead is called
        // directly. Otherwise status read bit is set to make OVOPS
        // include this FD into ioHandler's select() call.
        if (isPolling() != 0)
        {
            readCallback();
        }
        else
        {
            setStatusRead();
            _readPending = 1;
            requestRead(_fd, _defTimeout);
        }
    }
    return failure;
}

//
//Function: writeDevice
//
//Description:
//    Sets up the device for writing. Only one write request can be
//    pending at any time, so the host class (pfDeviceHost) must handle
//    queuing of write requests. If write request cannot be registered
//    (there is already a request pending) a non-zero value is returned.
//

bool pfDevice :: writeDevice(pfFrame &frame_)
{
    bool failure = 0;
    if (_writePending != 0)
    {
	// previous write wasn't completed. we cannot queue requests
	failure = 1;
    }
    else
    {
        // Save the frame to be written into local space. As the pfFrame
        // class has copy-on-write functionality, no unnecessary memory
        // copies will be made.
        _writeFrame = frame_;

        // If the device is set to poll the FD, callbackWrite is called
        // directly. Otherwise status write bit is set to make OVOPS
        // include this FD into ioHandler's select() call.
        if (isPolling() != 0)
        {
            writeCallback();
        }
        else
        {
            setStatusWrite();
            _writePending = 1;
            requestWrite(_fd, _defTimeout);
        }
    }
    return failure;
}

//
//Function: setStatusRead
//
//Description:
//    Sets the device READ status bit. This and the following set/clear
//    methods hide OVOPS Device::status word from pfDevice users.
//

void pfDevice :: setStatusRead(void)
{
    _status |= READ;
    return;
}

//
//Function: setStatusWrite
//
//Description:
//    Sets the device WRITE status bit.
//

void pfDevice :: setStatusWrite(void)
{
    _status |= WRITE;
    return;
}

//
//Function: setStatusPolling
//
//Description:
//    Sets the device POLLING status bit.
//

void pfDevice :: setStatusPolling(void)
{
    _status |= POLLING;
    return;
}

//
//Function: setStatusError
//
//Description:
//    Sets the device ERROR status bit.
//

void pfDevice :: setStatusError(void)
{
    _status |= ERROR;
    return;
}

//
//Function: clearStatusRead
//
//Description:
//    Clears the device READ status bit.
//

void pfDevice :: clearStatusRead(void)
{
    _status &= ~READ;
    return;
}

//
//Function: clearStatusWrite
//
//Description:
//    Clears the device WRITE status bit.
//

void pfDevice :: clearStatusWrite(void)
{
    _status &= ~WRITE;
    return;
}

//
//Function: clearStatusPolling
//
//Description:
//    Clears the device POLLING status bit.
//

void pfDevice :: clearStatusPolling(void)
{
    _status &= ~POLLING;
    return;
}

//
//Function: clearStatusError
//
//Description:
//    Clears the device ERROR status bit.
//

void pfDevice :: clearStatusError(void)
{
    _status &= ~ERROR;
    return;
}


//
//Function: isPolling
//
//Description:
//    Returns the polling status of the device.
//

bool pfDevice :: isPolling(void) const
{
    bool result;
    result = _status & POLLING;
    return result;
}


//
//Function: readCallback
//
//Description:
//    Callback routine called by the i/o handler when it notices
//    that there is something to read for this device. The read
//    request will be satisfied when there is even only one byte
//    read.
//    After something is read the readAction function is called and
//    the READ bit in the status field is cleared. If an error
//    occured during read, device ERROR status bit will be set and
//    readAction is called with an error code.
//

void pfDevice :: readCallback(void)
{
    pfFrame frame(_bufferSize);
    if (_readBuffer != 0)
    {
        _readBufferLength = readFromSocket();
        if (_readBufferLength > 0)
        {
            clearStatusRead();
            _readPending = 0;
            frame.putLast((pfByte *) _readBuffer, _readBufferLength);
            readAction(frame, 0);
        }
        else
        {
            setStatusError();
            _readPending = 0;
            debugUser("pfDevice: Error in readCallback:");
            perror("read");
            readAction(frame, errno);
        }
    }
    else
    {
        setStatusError();
        debugUser("pfDevice: Error in readCallback: No buffer");

        // Call readAction with nonzero value to indicate error
        readAction(frame, 1);
    }
    return;
}

//
//Function: writeCallback
//
//Description:
//    The callback routine is called by the i/o handler when it
//    notices that writing could be done. When the whole frame
//    is written the writeAction function is called and the
//    WRITE bit in the status field is cleared. When an 
//    error has occurred during writing the ERROR bit 
//    is set and the frame is disposed. Writing can be done
//    in many parts due to the operation of the operating system.
//

void pfDevice :: writeCallback(void)
{
    int nbytes;

    if (_writeBuffer != 0)
    {
        //assert(_writeFrame.length() <= _bufferSize);
        _writeFrame.copyData((pfByte *) _writeBuffer);
        _writeBufferLength = _writeFrame.length();
        char *start = _writeBuffer;
        while (_writeBufferLength) {
            nbytes = writeToSocket(start);
            if (nbytes < 0)
            {
                setStatusError();
                debugUser("pfDevice: Error in writeCallback");
                perror("write");
                writeAction(errno);
                return;
            }
            start += nbytes;
            _writeBufferLength -= nbytes;
        }

        // write completed
        writeAction(0);
        clearStatusWrite();
        _writePending = 0;
    }
    else
    {
        setStatusError();
        _writePending = 0;
        debugUser("pfDevice: Error in writeCallback: No buffer");

        // Call writeAction with nonzero value to indicate error
        writeAction(1);
    }
    return;
}

//
//Function: readFromSocket
//
//Description:
//    Reads data from socket to _readBuffer and returns readed data's length.
//

int pfDevice :: readFromSocket(void)
{
    int readBufferLength;
    readBufferLength = ::read(_fd, _readBuffer, _bufferSize);
    return readBufferLength;
}

//
//Function: writeToSocket
//
//Description:
//    Writes data from _writeBuffer to socket and returns writed data's length.
//

int pfDevice :: writeToSocket(char *start_)
{
    int nbytes;
    nbytes = ::write(_fd, start_, _writeBufferLength);
    return nbytes;
}

//
//Function: setFD
//
//Description:
//    Sets the file descriptor to given value. The OVOPS FD is also set
//    to the same value, because OVOPS iohandler uses it in its select
//    call. The point of having to separate fds is to hide OVOPS completely
//    from the pfDevice users - thus the user defined classes must not
//    access OVOPS's fd (or other structures) directly in any way.
//

void pfDevice :: setFD(int fd_)
{
    _fd = fd_;    // Set pfDevice fd
    return;
}

//
//Function: setAddr
//
//Description:
//      Method to set address. This is empty in base-class.
//
void pfDevice :: setAddr(const struct sockaddr_in &)
{
    return;
}
//
//Function: getAddr
//
//Description:
//      Method to get address. This return empty address in base-class.
//
const struct sockaddr_in &pfDevice :: getAddr(void) const
{
    static struct sockaddr_in addr = {0};
    return addr;
}

