/***************************************************************************
* Copyright (C) 2013, 2014 by Terraneo Federico *
* *
* 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 2 of the License, 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. *
* *
* As a special exception, if other files instantiate templates or use *
* macros or inline functions from this file, or you compile this file *
* and link it with other works to produce a work based on this file, *
* this file does not by itself cause the resulting work to be covered *
* by the GNU General Public License. However the source code for this *
* file must still be made available in accordance with the GNU General *
* Public License. This exception does not invalidate any other reasons *
* why a work based on this file might be covered by the GNU General *
* Public License. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, see *
***************************************************************************/
#include "console_device.h"
#include "filesystem/ioctl.h"
#include
#include
using namespace std;
namespace miosix {
//
// class TerminalDevice
//
TerminalDevice::TerminalDevice(intrusive_ref_ptr device)
: FileBase(intrusive_ref_ptr()), device(device),
mutex(), echo(true), binary(false), skipNewline(false) {}
ssize_t TerminalDevice::write(const void *data, size_t length)
{
if(binary) return device->writeBlock(data,length,0);
//No mutex here to avoid blocking writes while reads are in progress
const char *buffer=static_cast(data);
const char *start=buffer;
//Try to write data in chunks, stop at every \n to replace with \r\n
//Although it may be tempting to call echoBack() from here since it performs
//a similar task, it is not possible, as echoBack() uses a class field,
//chunkStart, and write is not mutexed to allow concurrent writing
for(size_t i=0;istart)
{
ssize_t r=device->writeBlock(start,buffer-start,0);
if(r<=0) return r;
}
ssize_t r=device->writeBlock("\r\n",2,0);//Add \r\n
if(r<=0) return r;
start=buffer+1;
}
if(buffer>start)
{
ssize_t r=device->writeBlock(start,buffer-start,0);
if(r<=0) return r;
}
return length;
}
ssize_t TerminalDevice::read(void *data, size_t length)
{
if(binary)
{
ssize_t result=device->readBlock(data,length,0);
if(echo && result>0) device->writeBlock(data,result,0);//Ignore write errors
return result;
}
Lock l(mutex); //Reads are serialized
char *buffer=static_cast(data);
size_t readBytes=0;
for(;;)
{
ssize_t r=device->readBlock(buffer+readBytes,length-readBytes,0);
if(r<0) return r;
pair result=normalize(buffer,readBytes,readBytes+r);
readBytes=result.first;
if(readBytes==length || result.second) return readBytes;
}
}
#ifdef WITH_FILESYSTEM
off_t TerminalDevice::lseek(off_t pos, int whence)
{
return -EBADF;
}
int TerminalDevice::fstat(struct stat *pstat) const
{
return device->fstat(pstat);
}
int TerminalDevice::isatty() const { return device->isatty(); }
#endif //WITH_FILESYSTEM
int TerminalDevice::ioctl(int cmd, void *arg)
{
if(int result=device->ioctl(cmd,arg)!=0) return result;
termios *t=reinterpret_cast(arg);
switch(cmd)
{
case IOCTL_TCGETATTR:
if(echo) t->c_lflag |= ECHO; else t->c_lflag &= ~ECHO;
if(binary==false) t->c_lflag |= ICANON; else t->c_lflag &= ~ICANON;
break;
case IOCTL_TCSETATTR_NOW:
case IOCTL_TCSETATTR_DRAIN:
case IOCTL_TCSETATTR_FLUSH:
echo=(t->c_lflag & ECHO) ? true : false;
binary=(t->c_lflag & ICANON) ? false : true;
break;
default:
break;
}
return 0;
}
pair TerminalDevice::normalize(char *buffer, ssize_t begin,
ssize_t end)
{
bool newlineFound=false;
buffer+=begin;
chunkStart=buffer;
for(ssize_t i=begin;ichunkStart) device->writeBlock(chunkStart,chunkEnd-chunkStart,0);
chunkStart=chunkEnd+1;
if(sep) device->writeBlock(sep,sepLen,0); //Ignore write errors
}
//
// class DefaultConsole
//
DefaultConsole& DefaultConsole::instance()
{
static DefaultConsole singleton;
return singleton;
}
void DefaultConsole::IRQset(intrusive_ref_ptr console)
{
//Note: should be safe to be called also outside of IRQ as set() calls
//IRQset()
atomic_store(&this->console,console);
#ifndef WITH_FILESYSTEM
atomic_store(&terminal,
intrusive_ref_ptr(new TerminalDevice(console)));
#endif //WITH_FILESYSTEM
}
DefaultConsole::DefaultConsole() : console(new Device(Device::STREAM))
#ifndef WITH_FILESYSTEM
, terminal(new TerminalDevice(console))
#endif //WITH_FILESYSTEM
{}
} //namespace miosix