/*************************************************************************** * 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