Compiling miosix kernel from sources instead of linking against a pre-build image
This commit is contained in:
parent
1b8106d607
commit
b861beb0e6
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
This repository contains an adapted version of the miosix kernel, tailored for the usage with the OpenRTX targets equipped with an ARM cortex M4 microcontroller.
|
||||
|
||||
The original repository can be found here: [https://github.com/fedetft/miosix-kernel](https://github.com/fedetft/miosix-kernel)
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
miosix_inc = ['lib/miosix-kernel/miosix',
|
||||
'lib/miosix-kernel/miosix/config',
|
||||
'lib/miosix-kernel/miosix/arch/common',
|
||||
'lib/miosix-kernel/miosix/util']
|
||||
|
||||
miosix_src = ['lib/miosix-kernel/miosix/kernel/scheduler/priority/priority_scheduler.cpp',
|
||||
'lib/miosix-kernel/miosix/kernel/error.cpp',
|
||||
'lib/miosix-kernel/miosix/kernel/kernel.cpp',
|
||||
'lib/miosix-kernel/miosix/kernel/pthread.cpp',
|
||||
'lib/miosix-kernel/miosix/kernel/stage_2_boot.cpp',
|
||||
'lib/miosix-kernel/miosix/kernel/sync.cpp',
|
||||
'lib/miosix-kernel/miosix/kernel/timeconversion.cpp',
|
||||
'lib/miosix-kernel/miosix/util/util.cpp',
|
||||
'lib/miosix-kernel/miosix/stdlib_integration/libc_integration.cpp',
|
||||
'lib/miosix-kernel/miosix/stdlib_integration/libstdcpp_integration.cpp']
|
||||
|
||||
miosix_def = {'DONT_USE_CMSIS_INIT' : '',
|
||||
'COMPILING_MIOSIX' : '',
|
||||
'_POSIX_PRIORITY_SCHEDULING': ''}
|
||||
|
||||
##
|
||||
## ARM Cortex M4
|
||||
##
|
||||
miosix_cm4f_inc = miosix_inc + ['lib/miosix-kernel/miosix/arch/cortexM4F']
|
||||
miosix_cm4f_src = miosix_src + ['lib/miosix-kernel/miosix/arch/cortexM4F/portability.cpp',
|
||||
'lib/miosix-kernel/miosix/arch/common/core/interrupts_cortexMx.cpp']
|
||||
miosix_cm4f_def = miosix_def + {'_ARCH_CORTEXM4' : ''}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013, 2014, 2015 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ATOMIC_OPS_IMPL_H
|
||||
#define ATOMIC_OPS_IMPL_H
|
||||
|
||||
namespace miosix {
|
||||
|
||||
inline int atomicSwap(volatile int *p, int v)
|
||||
{
|
||||
//This is the only atomic operation in the ARM7 assembler
|
||||
register int result;
|
||||
asm volatile("swp %0, %1, [%2]" : "=&r"(result) : "r"(v),"r"(p) : "memory");
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void atomicAdd(volatile int *p, int incr)
|
||||
{
|
||||
register int a,b; //Temporaries used by ASM code
|
||||
asm volatile(" mrs %0, cpsr \n"
|
||||
" tst %0, #0x80 \n"
|
||||
" orreq %1, %0, #0x80 \n"
|
||||
" msreq cpsr_c, %1 \n"
|
||||
" ldr %1, [%3] \n"
|
||||
" add %1, %1, %2 \n"
|
||||
" str %1, [%3] \n"
|
||||
" tst %0, #0x80 \n"
|
||||
" msreq cpsr_c, %0 \n"
|
||||
: "=&r"(a),"=&r"(b)
|
||||
: "r"(incr),"r"(p)
|
||||
: "cc","memory");
|
||||
}
|
||||
|
||||
inline int atomicAddExchange(volatile int *p, int incr)
|
||||
{
|
||||
register int a; //Temporaries used by ASM code
|
||||
register int result;
|
||||
asm volatile(" mrs %0, cpsr \n"
|
||||
" tst %0, #0x80 \n"
|
||||
" orreq %1, %0, #0x80 \n"
|
||||
" msreq cpsr_c, %1 \n"
|
||||
" ldr %1, [%3] \n"
|
||||
" add %2, %2, %1 \n"
|
||||
" str %2, [%3] \n"
|
||||
" tst %0, #0x80 \n"
|
||||
" msreq cpsr_c, %0 \n"
|
||||
: "=&r"(a),"=&r"(result),"+&r"(incr)//Incr is read and clobbered
|
||||
: "r"(p)
|
||||
: "cc","memory");
|
||||
return result;
|
||||
}
|
||||
|
||||
inline int atomicCompareAndSwap(volatile int *p, int prev, int next)
|
||||
{
|
||||
register int a; //Temporaries used by ASM code
|
||||
register int result;
|
||||
asm volatile(" mrs %0, cpsr \n"
|
||||
" tst %0, #0x80 \n"
|
||||
" orreq %1, %0, #0x80 \n"
|
||||
" msreq cpsr_c, %1 \n"
|
||||
" ldr %1, [%2] \n"
|
||||
" cmp %1, %3 \n"
|
||||
" streq %4, [%2] \n"
|
||||
" tst %0, #0x80 \n"
|
||||
" msreq cpsr_c, %0 \n"
|
||||
: "=&r"(a),"=&r"(result)
|
||||
: "r"(p),"r"(prev),"r"(next)
|
||||
: "cc","memory");
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void *atomicFetchAndIncrement(void * const volatile * p, int offset,
|
||||
int incr)
|
||||
{
|
||||
register int a,b; //Temporaries used by ASM code
|
||||
register void *result;
|
||||
asm volatile(" mrs %0, cpsr \n"
|
||||
" tst %0, #0x80 \n"
|
||||
" orreq %1, %0, #0x80 \n"
|
||||
" msreq cpsr_c, %1 \n"
|
||||
" ldr %2, [%3] \n"
|
||||
" ldr %1, [%2, %4, asl #2] \n"
|
||||
" add %1, %1, %5 \n"
|
||||
" str %1, [%2, %4, asl #2] \n"
|
||||
" tst %0, #0x80 \n"
|
||||
" msreq cpsr_c, %0 \n"
|
||||
: "=&r"(a),"=&r"(b),"=&r"(result)
|
||||
: "r"(p),"r"(offset),"r"(incr)
|
||||
: "cc","memory");
|
||||
return result;
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //ATOMIC_OPS_IMPL_H
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013, 2014, 2015 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ATOMIC_OPS_IMPL_M0_H
|
||||
#define ATOMIC_OPS_IMPL_M0_H
|
||||
|
||||
/**
|
||||
* Cortex M0/M0+ architectures does not support __LDREXW, __STREXW and __CLREX
|
||||
* instructions, so we have to redefine the atomic operations using functions
|
||||
* that disable the interrupts.
|
||||
*
|
||||
* TODO: actually this implementation is not very efficient
|
||||
*
|
||||
*/
|
||||
|
||||
#include "interfaces/arch_registers.h"
|
||||
#include <kernel/kernel.h>
|
||||
|
||||
namespace miosix {
|
||||
|
||||
|
||||
inline int atomicSwap(volatile int *p, int v)
|
||||
{
|
||||
InterruptDisableLock dLock;
|
||||
|
||||
int result = *p;
|
||||
*p = v;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void atomicAdd(volatile int *p, int incr)
|
||||
{
|
||||
InterruptDisableLock dLock;
|
||||
|
||||
*p += incr;
|
||||
}
|
||||
|
||||
inline int atomicAddExchange(volatile int *p, int incr)
|
||||
{
|
||||
InterruptDisableLock dLock;
|
||||
|
||||
int result = *p;
|
||||
*p += incr;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline int atomicCompareAndSwap(volatile int *p, int prev, int next)
|
||||
{
|
||||
InterruptDisableLock dLock;
|
||||
|
||||
int result = *p;
|
||||
if(*p == prev) *p = next;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void *atomicFetchAndIncrement(void * const volatile * p, int offset,
|
||||
int incr)
|
||||
{
|
||||
InterruptDisableLock dLock;
|
||||
|
||||
volatile uint32_t *pt;
|
||||
|
||||
void *result = *p;
|
||||
if(result == 0) return 0;
|
||||
pt = reinterpret_cast<uint32_t*>(result) + offset;
|
||||
*pt += incr;
|
||||
return result;
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //ATOMIC_OPS_IMPL_M0_H
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013, 2014, 2015 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ATOMIC_OPS_IMPL_H
|
||||
#define ATOMIC_OPS_IMPL_H
|
||||
|
||||
#include "interfaces/arch_registers.h"
|
||||
|
||||
namespace miosix {
|
||||
|
||||
inline int atomicSwap(volatile int *p, int v)
|
||||
{
|
||||
int result;
|
||||
volatile uint32_t *ptr=reinterpret_cast<volatile uint32_t*>(p);
|
||||
do {
|
||||
result=__LDREXW(ptr);
|
||||
} while(__STREXW(v,ptr));
|
||||
asm volatile("":::"memory");
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void atomicAdd(volatile int *p, int incr)
|
||||
{
|
||||
int value;
|
||||
volatile uint32_t *ptr=reinterpret_cast<volatile uint32_t*>(p);
|
||||
do {
|
||||
value=__LDREXW(ptr);
|
||||
} while(__STREXW(value+incr,ptr));
|
||||
asm volatile("":::"memory");
|
||||
}
|
||||
|
||||
inline int atomicAddExchange(volatile int *p, int incr)
|
||||
{
|
||||
int result;
|
||||
volatile uint32_t *ptr=reinterpret_cast<volatile uint32_t*>(p);
|
||||
do {
|
||||
result=__LDREXW(ptr);
|
||||
} while(__STREXW(result+incr,ptr));
|
||||
asm volatile("":::"memory");
|
||||
return result;
|
||||
}
|
||||
|
||||
inline int atomicCompareAndSwap(volatile int *p, int prev, int next)
|
||||
{
|
||||
int result;
|
||||
volatile uint32_t *ptr=reinterpret_cast<volatile uint32_t*>(p);
|
||||
do {
|
||||
result=__LDREXW(ptr);
|
||||
if(result!=prev)
|
||||
{
|
||||
__CLREX();
|
||||
return result;
|
||||
}
|
||||
} while(__STREXW(next,ptr));
|
||||
asm volatile("":::"memory");
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void *atomicFetchAndIncrement(void * const volatile * p, int offset,
|
||||
int incr)
|
||||
{
|
||||
void *result;
|
||||
volatile uint32_t *rcp;
|
||||
int rc;
|
||||
do {
|
||||
for(;;)
|
||||
{
|
||||
result=*p;
|
||||
if(result==0) return 0;
|
||||
rcp=reinterpret_cast<uint32_t*>(result)+offset;
|
||||
rc=__LDREXW(rcp);
|
||||
asm volatile("":::"memory");
|
||||
if(result==*p) break;
|
||||
__CLREX();
|
||||
}
|
||||
} while(__STREXW(rc+incr,rcp));
|
||||
asm volatile("":::"memory");
|
||||
return result;
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //ATOMIC_OPS_IMPL_H
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2018 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include "cache_cortexMx.h"
|
||||
#include "mpu_cortexMx.h"
|
||||
#include <utility>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace miosix {
|
||||
|
||||
static const unsigned int cacheLine=32; //Cortex-M7 cache line size
|
||||
|
||||
/**
|
||||
* Using the MPU, configure a region of the memory space as
|
||||
* - write-through cacheable
|
||||
* - non-shareable
|
||||
* - readable/writable/executable only by privileged code (for compatibility
|
||||
* with the way processes use the MPU)
|
||||
* \param region MPU region. Note that region 6 and 7 are used by processes, and
|
||||
* should be avoided here
|
||||
* \param base base address, aligned to a 32Byte cache line
|
||||
* \param size size, must be at least 32 and a power of 2, or it is rounded to
|
||||
* the next power of 2
|
||||
*/
|
||||
static void IRQconfigureCacheability(unsigned int region, unsigned int base,
|
||||
unsigned int size)
|
||||
{
|
||||
// NOTE: The ARM documentation is unclear about the effect of the shareable
|
||||
// bit on a single core architecture. Experimental evidence on an STM32F476
|
||||
// shows that setting it in IRQconfigureCache for the internal RAM region
|
||||
// causes the boot to fail.
|
||||
// For this reason, all regions are marked as not shareable
|
||||
MPU->RBAR=(base & (~(cacheLine-1))) | MPU_RBAR_VALID_Msk | region;
|
||||
MPU->RASR=1<<MPU_RASR_AP_Pos //Privileged: RW, unprivileged: no access
|
||||
| MPU_RASR_C_Msk //Cacheable, write through
|
||||
| 1 //Enable bit
|
||||
| sizeToMpu(size)<<1;
|
||||
}
|
||||
|
||||
void IRQconfigureCache(const unsigned int *xramBase, unsigned int xramSize)
|
||||
{
|
||||
IRQconfigureCacheability(0,0x00000000,0x20000000);
|
||||
IRQconfigureCacheability(1,0x20000000,0x20000000);
|
||||
if(xramSize)
|
||||
IRQconfigureCacheability(2,reinterpret_cast<unsigned int>(xramBase),xramSize);
|
||||
IRQenableMPUatBoot();
|
||||
|
||||
SCB_EnableICache();
|
||||
SCB_EnableDCache();
|
||||
}
|
||||
|
||||
#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT==1)
|
||||
/**
|
||||
* Align a generic buffer to another one that contains the first one, but the
|
||||
* start size is aligned to a cache line
|
||||
*/
|
||||
static pair<uint32_t*, int32_t> alignBuffer(void *buffer, int size)
|
||||
{
|
||||
auto bufferAddr=reinterpret_cast<unsigned int>(buffer);
|
||||
|
||||
auto base=bufferAddr & (~(cacheLine-1));
|
||||
size+=bufferAddr-base;
|
||||
|
||||
return make_pair(reinterpret_cast<uint32_t*>(base),size);
|
||||
}
|
||||
|
||||
void markBufferAfterDmaRead(void *buffer, int size)
|
||||
{
|
||||
//Since the current cache policy is write-through, we just invalidate the
|
||||
//cache lines corresponding to the buffer. No need to flush (clean) the cache.
|
||||
auto result=alignBuffer(buffer,size);
|
||||
SCB_InvalidateDCache_by_Addr(result.first,result.second);
|
||||
}
|
||||
#endif
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2018 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef CACHE_CORTEX_MX_H
|
||||
#define CACHE_CORTEX_MX_H
|
||||
|
||||
/*
|
||||
* README: Essentials about how cache support is implemented.
|
||||
*
|
||||
* Caches in the Cortex M7 are transparent to software, except when using
|
||||
* the DMA. As the DMA reads and writes directly to memory, explicit management
|
||||
* is required. The default cache policy is write-back, but this has been deemed
|
||||
* unsuitable for Miosix, so for the time being only write-through is supported.
|
||||
*
|
||||
* The IRQconfigureCache() configures the cache as write-through and enables it.
|
||||
* It should be called early at boot, in stage_1_boot.cpp
|
||||
*
|
||||
* When writing DMA drivers, before passing a buffer to the DMA for it to be
|
||||
* written to a peripheral, call markBufferBeforeDmaWrite().
|
||||
* After a DMA read from a peripheral to a memory buffer has completed,
|
||||
* call markBufferAfterDmaRead(). These take care of keeping the DMA operations
|
||||
* in sync with the cache. These become no-ops for other architectures, so you
|
||||
* can freely put the in any driver.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Some more info about caches. Why not supporting write-back?
|
||||
* When writing data from memory to a peripheral using DMA, things are easy
|
||||
* also with write-back. You just flush (clean) the relevant cache lines, and
|
||||
* the DMA has access to the correct values. So it looks like it's ok.
|
||||
* When instead the DMA has to write to a memory location things become
|
||||
* complicated. Assume that a buffer not aligned to a cache line is passed to
|
||||
* a DMA read routine. After that a context switch happens and another thread
|
||||
* writes to a memory location that is on the same cache line as the (beginning
|
||||
* or end of) the buffer passed to the DMA. At the same time the DMA is writing
|
||||
* to the buffer.
|
||||
* At the end the situation looks like this, where the thread has written to
|
||||
* location X in the cache, while the DMA has written Y to the buffer.
|
||||
* <-- outside buffer --x-- buffer -->
|
||||
* +----------------------------------+
|
||||
* | X | | cache
|
||||
* +----------------------------------+
|
||||
* | |YYYYYYYYYYYYY| memory
|
||||
* +----------------------------------+
|
||||
* What are you suppose to do? If you flush (clean) the cache line, X will be
|
||||
* committed to memory, but the Y data written by the DMA will be lost. If you
|
||||
* invalidate the cache, Y is preserved, but X is lost.
|
||||
* If you're just thinking that the problem can be solved by making sure buffers
|
||||
* are aligned to the cache line (and their size is a multiple of the cache
|
||||
* line), well, there's a problem.
|
||||
* Miosix is a real-time OS, and for performance and safety, most drivers are
|
||||
* zero copy. Applications routinely pass to DMA drivers such as the SDIO large
|
||||
* buffers (think 100+KB). Of course allocating an aligned buffer inside the
|
||||
* DMA driver as large as the user-passed buffer and copying the data there
|
||||
* isn't just slow, it wouldn't be safe, as you risk to exceed the free heap
|
||||
* memory or fragment the heap. Allocating a small buffer and splitting the
|
||||
* large DMA transfer in many small ones where the user passed buffer is copyied
|
||||
* one chunk at a time would be feasible, but even slower, and even more so
|
||||
* considering that some peripherals such as SD cards are optimized for large
|
||||
* sequential writes, not for small chunks.
|
||||
* But what if we make sure all buffers passed to DMA drivers are aligned?
|
||||
* That is a joke, as it the burden of doing so is unmaintainable. Buffers
|
||||
* passed to DMA memory are everywhere, in the C/C++ standard library
|
||||
* (think the buffer used for IO formatting inside printf/fprintf), and
|
||||
* everywherein application code. Something like
|
||||
* char s[128]="Hello world";
|
||||
* puts(s);
|
||||
* may cause s to be passed to a DMA driver. We would spend our lives chasing
|
||||
* unaligned buffers, and the risk of this causing difficult to reproduce memory
|
||||
* corruptions is too high. For this reason, for the time being, Miosix only
|
||||
* supports write-through caching on the Cortex-M7.
|
||||
*
|
||||
* A note about performance. Using the testsuite benchmark, when caches are
|
||||
* disabled the STM32F746 @ 216MHz is less than half the speed of the
|
||||
* STM32F407 @ 168MHz. By enabling the ICACHE things get better, but it is
|
||||
* still slower, and achieves a speedup of 1.53 only when both ICACHE and
|
||||
* DCACHE are enabled. The speedup also includes the gains due to the faster
|
||||
* clock frequency. So if you want speed you have to use caches.
|
||||
*/
|
||||
|
||||
#include "interfaces/arch_registers.h"
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* To be called in stage_1_boot.cpp to configure caches.
|
||||
* Only call this function if the board has caches.
|
||||
* \param xramBase base address of external memory, if present, otherwise nullptr
|
||||
* \param xramSize size of external memory, if present, otherwise 0
|
||||
*/
|
||||
void IRQconfigureCache(const unsigned int *xramBase=nullptr, unsigned int xramSize=0);
|
||||
|
||||
/**
|
||||
* Call this function to mark a buffer before starting a DMA transfer where
|
||||
* the DMA will read the buffer.
|
||||
* \param buffer buffer
|
||||
* \param size buffer size
|
||||
*/
|
||||
inline void markBufferBeforeDmaWrite(const void *buffer, int size)
|
||||
{
|
||||
#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT==1)
|
||||
// You may think that since the cache is configured as write-through,
|
||||
// there's nothing to do before the DMA can read a memory buffer just
|
||||
// written by the CPU, right? Wrong! Other than the cache, there's the
|
||||
// write buffer to worry about. My hypothesis is that once a memory region
|
||||
// is marked as cacheable, the write buffer becomes more lax in
|
||||
// automatically flushing as soon as possible. In the stm32 serial port
|
||||
// driver writing just a few characters causes garbage to be printed if
|
||||
// this __DSB() is removed. Apparently, the characters remian in the write
|
||||
// buffer.
|
||||
__DSB();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this function after having completed a DMA transfer where the DMA has
|
||||
* written to the buffer.
|
||||
* \param buffer buffer
|
||||
* \param size buffer size
|
||||
*/
|
||||
#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT==1)
|
||||
void markBufferAfterDmaRead(void *buffer, int size);
|
||||
#else
|
||||
inline void markBufferAfterDmaRead(void *buffer, int size) {}
|
||||
#endif
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //CACHE_CORTEX_MX_H
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2011 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ENDIANNESS_IMPL_H
|
||||
#define ENDIANNESS_IMPL_H
|
||||
|
||||
#ifndef MIOSIX_BIG_ENDIAN
|
||||
//This target is little endian
|
||||
#define MIOSIX_LITTLE_ENDIAN
|
||||
#endif //MIOSIX_BIG_ENDIAN
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define __MIOSIX_INLINE inline
|
||||
#else //__cplusplus
|
||||
#define __MIOSIX_INLINE static inline
|
||||
#endif //__cplusplus
|
||||
|
||||
|
||||
__MIOSIX_INLINE unsigned short swapBytes16(unsigned short x)
|
||||
{
|
||||
return (x>>8) | (x<<8);
|
||||
}
|
||||
|
||||
__MIOSIX_INLINE unsigned int swapBytes32(unsigned int x)
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
return __builtin_bswap32(x);
|
||||
#else //__GNUC__
|
||||
return ( x>>24) |
|
||||
((x<< 8) & 0x00ff0000) |
|
||||
((x>> 8) & 0x0000ff00) |
|
||||
( x<<24);
|
||||
#endif //__GNUC__
|
||||
}
|
||||
|
||||
__MIOSIX_INLINE unsigned long long swapBytes64(unsigned long long x)
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
return __builtin_bswap64(x);
|
||||
#else //__GNUC__
|
||||
return ( x>>56) |
|
||||
((x<<40) & 0x00ff000000000000ull) |
|
||||
((x<<24) & 0x0000ff0000000000ull) |
|
||||
((x<< 8) & 0x000000ff00000000ull) |
|
||||
((x>> 8) & 0x00000000ff000000ull) |
|
||||
((x>>24) & 0x0000000000ff0000ull) |
|
||||
((x>>40) & 0x000000000000ff00ull) |
|
||||
( x<<56);
|
||||
#endif //__GNUC__
|
||||
}
|
||||
|
||||
#undef __MIOSIX_INLINE
|
||||
|
||||
#endif //ENDIANNESS_IMPL_H
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2011, 2012, 2013, 2014, 2015 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ENDIANNESS_IMPL_H
|
||||
#define ENDIANNESS_IMPL_H
|
||||
|
||||
#ifndef MIOSIX_BIG_ENDIAN
|
||||
//This target is little endian
|
||||
#define MIOSIX_LITTLE_ENDIAN
|
||||
#endif //MIOSIX_BIG_ENDIAN
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define __MIOSIX_INLINE inline
|
||||
#else //__cplusplus
|
||||
#define __MIOSIX_INLINE static inline
|
||||
#endif //__cplusplus
|
||||
|
||||
__MIOSIX_INLINE unsigned short swapBytes16(unsigned short x)
|
||||
{
|
||||
//It's kind of a shame that GCC can't automatically make use of
|
||||
//instructions like rev and rev16 to do byte swapping.
|
||||
//Moreover, while for 32 and 64 bit integers it has builtins, for 16 bit
|
||||
//we're forced to use inline asm.
|
||||
#ifdef __GNUC__
|
||||
if(!__builtin_constant_p(x))
|
||||
{
|
||||
unsigned short y;
|
||||
asm("rev16 %0, %1":"=r"(y):"r"(x));
|
||||
return y;
|
||||
} else {
|
||||
//It gets worse: if value is constant inlining assembler disables
|
||||
//contant folding, wtf...
|
||||
return (x>>8) | (x<<8);
|
||||
}
|
||||
#else
|
||||
return (x>>8) | (x<<8);
|
||||
#endif
|
||||
}
|
||||
|
||||
__MIOSIX_INLINE unsigned int swapBytes32(unsigned int x)
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
return __builtin_bswap32(x);
|
||||
#else
|
||||
return ( x>>24) |
|
||||
((x<< 8) & 0x00ff0000) |
|
||||
((x>> 8) & 0x0000ff00) |
|
||||
( x<<24);
|
||||
#endif
|
||||
}
|
||||
|
||||
__MIOSIX_INLINE unsigned long long swapBytes64(unsigned long long x)
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
return __builtin_bswap64(x);
|
||||
#else
|
||||
return ( x>>56) |
|
||||
((x<<40) & 0x00ff000000000000ull) |
|
||||
((x<<24) & 0x0000ff0000000000ull) |
|
||||
((x<< 8) & 0x000000ff00000000ull) |
|
||||
((x>> 8) & 0x00000000ff000000ull) |
|
||||
((x>>24) & 0x0000000000ff0000ull) |
|
||||
((x>>40) & 0x000000000000ff00ull) |
|
||||
( x<<56);
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef __MIOSIX_INLINE
|
||||
|
||||
#endif //ENDIANNESS_IMPL_H
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
//Interrupt code is common for all the cortex M cores, so it has been put here
|
||||
|
||||
#ifdef _ARCH_ARM7_LPC2000
|
||||
#include "interrupts_arm7.h"
|
||||
#elif defined(_ARCH_CORTEXM0) || defined(_ARCH_CORTEXM3) \
|
||||
|| defined(_ARCH_CORTEXM4) || defined(_ARCH_CORTEXM7)
|
||||
#include "interrupts_cortexMx.h"
|
||||
#else
|
||||
#error "Unknown arch"
|
||||
#endif
|
||||
|
||||
// Cortex M0 and M0+ does not have some SCB registers, in order to avoid
|
||||
// compilation issues a flag is defined to disable code that accesses to
|
||||
// registers not present in these families
|
||||
|
||||
#if defined(_ARCH_CORTEXM0_STM32)
|
||||
#define _ARCH_CORTEXM0
|
||||
#endif
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2008, 2009, 2010 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include "kernel/logging.h"
|
||||
#include "interfaces/portability.h"
|
||||
#include "config/miosix_settings.h"
|
||||
#include "interrupts.h"
|
||||
|
||||
using namespace miosix;
|
||||
|
||||
//Look up table used by printUnsignedInt()
|
||||
static const char hexdigits[]="0123456789abcdef";
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Used to print an unsigned int in hexadecimal format, and to reboot the system
|
||||
* Note that printf/iprintf cannot be used inside an IRQ, so that's why there's
|
||||
* this function.
|
||||
* \param x number to print
|
||||
*/
|
||||
static void printUnsignedInt(unsigned int x)
|
||||
{
|
||||
char result[]="0x........\r\n";
|
||||
for(int i=9;i>=2;i--)
|
||||
{
|
||||
result[i]=hexdigits[x & 0xf];
|
||||
x>>=4;
|
||||
}
|
||||
IRQerrorLog(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Spurious interrupt handler.
|
||||
* The LPC2138 datasheet says that spurious interrups can occur, but until now
|
||||
* it never happened. If and when spurious interruts will occur, this code will
|
||||
* be modified to deal with them. Until then, this code just reboots the system.
|
||||
*/
|
||||
void default_IRQ_Routine()
|
||||
{
|
||||
IRQerrorLog("\r\n***Unexpected IRQ\r\n");
|
||||
miosix_private::IRQsystemReboot();
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* FIQ is currently not used.
|
||||
* Prints an error message, and reboots the system.
|
||||
* Stack usage is 24 Bytes (measured with watermarking and stack dump)
|
||||
* so a 32 byte stack is used (to leave some guard space).
|
||||
* If the user wants to use FIQ, it is important to remember to increase the
|
||||
* FIQ's stack size, which is defined in miosix.ld
|
||||
*/
|
||||
extern "C" void FIQ_Routine()
|
||||
{
|
||||
IRQerrorLog("\r\n***Unexpected FIQ\r\n");
|
||||
miosix_private::IRQsystemReboot();
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* This ISR handles Undefined instruction.
|
||||
* Prints an error message, showing an address near the instruction that caused
|
||||
* the exception. This address together with the map file allows finding the
|
||||
* function that caused the exception.
|
||||
* Please note that when compiling with some level of optimization, the compiler
|
||||
* can inline functions so the address is no longer accurate.
|
||||
* Stack usage is 47 Bytes (measured with watermarking and stack dump)
|
||||
* so a 48 byte stack is used (stak must be word-aligned).
|
||||
*/
|
||||
extern "C" void UNDEF_Routine()
|
||||
{
|
||||
//These two instructions MUST be the first two instructions of the interrupt
|
||||
//routine. They store in return_address the pc of the instruction that
|
||||
//caused the interrupt.
|
||||
register int returnAddress;
|
||||
asm volatile("mov %0, lr" : "=r"(returnAddress));
|
||||
|
||||
IRQerrorLog("\r\n***Unexpected UNDEF @ ");
|
||||
printUnsignedInt(returnAddress);
|
||||
miosix_private::IRQsystemReboot();
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* This ISR handles data abort.
|
||||
* Prints an error message, showing an address near the instruction that caused
|
||||
* the exception. This address together with the map file allows finding the
|
||||
* function that caused the exception.
|
||||
* Please note that when compiling with some level of optimization, the compiler
|
||||
* can inline functions so the address is no longer accurate.
|
||||
* Stack usage is 47 Bytes (measured with watermarking and stack dump)
|
||||
* so a 48 byte stack is used (stak must be word-aligned).
|
||||
*/
|
||||
extern "C" void DABT_Routine()
|
||||
{
|
||||
//These two instructions MUST be the first two instructions of the interrupt
|
||||
//routine. They store in return_address the pc of the instruction that
|
||||
//caused the interrupt. (lr has an offset of 8 during a data abort)
|
||||
register int returnAddress;
|
||||
asm volatile("sub %0, lr, #8" : "=r"(returnAddress));
|
||||
|
||||
IRQerrorLog("\r\n***Unexpected data abort @ ");
|
||||
printUnsignedInt(returnAddress);
|
||||
miosix_private::IRQsystemReboot();
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* This ISR handles prefetch abort.
|
||||
* Prints an error message, showing an address near the instruction that caused
|
||||
* the exception. This address together with the map file allows finding the
|
||||
* function that caused the exception.
|
||||
* Please note that when compiling with some level of optimization, the compiler
|
||||
* can inline functions so the address is no longer accurate.
|
||||
* Stack usage is 47 Bytes (measured with watermarking and stack dump)
|
||||
* so a 48 byte stack is used (stak must be word-aligned).
|
||||
*/
|
||||
extern "C" void PABT_Routine()
|
||||
{
|
||||
//These two instructions MUST be the first two instructions of the interrupt
|
||||
//routine. They store in return_address the pc of the instruction that
|
||||
//caused the interrupt. (lr has an offset of 4 during a data abort)
|
||||
register int returnAddress;
|
||||
asm volatile("sub %0, lr, #4" : "=r"(returnAddress));
|
||||
|
||||
IRQerrorLog("\r\n***Unexpected prefetch abort @ ");
|
||||
printUnsignedInt(returnAddress);
|
||||
miosix_private::IRQsystemReboot();
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2008 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
* interrupts.h Part of the Miosix Embedded OS.
|
||||
* Implementation of "Generic" interrupts, not related to a particular
|
||||
* hardware driver.
|
||||
************************************************************************/
|
||||
|
||||
#ifndef INTERRUPTS_H
|
||||
#define INTERRUPTS_H
|
||||
|
||||
void default_IRQ_Routine() __attribute__ ((interrupt("IRQ")));
|
||||
extern "C" void FIQ_Routine() __attribute__ ((interrupt("FIQ")));
|
||||
extern "C" void UNDEF_Routine() __attribute__ ((interrupt("UNDEF")));
|
||||
extern "C" void DABT_Routine() __attribute__ ((interrupt("DABT")));
|
||||
extern "C" void PABT_Routine() __attribute__ ((interrupt("PABT")));
|
||||
|
||||
#endif //INTERRUPTS_H
|
||||
|
|
@ -0,0 +1,282 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010, 2011, 2012, 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include "kernel/logging.h"
|
||||
#include "kernel/kernel.h"
|
||||
#include "config/miosix_settings.h"
|
||||
#include "interfaces/portability.h"
|
||||
#include "interfaces/arch_registers.h"
|
||||
#include "interrupts.h"
|
||||
|
||||
using namespace miosix;
|
||||
|
||||
#ifdef WITH_ERRLOG
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Used to print an unsigned int in hexadecimal format, and to reboot the system
|
||||
* Note that printf/iprintf cannot be used inside an IRQ, so that's why there's
|
||||
* this function.
|
||||
* \param x number to print
|
||||
*/
|
||||
static void printUnsignedInt(unsigned int x)
|
||||
{
|
||||
static const char hexdigits[]="0123456789abcdef";
|
||||
char result[]="0x........\r\n";
|
||||
for(int i=9;i>=2;i--)
|
||||
{
|
||||
result[i]=hexdigits[x & 0xf];
|
||||
x>>=4;
|
||||
}
|
||||
IRQerrorLog(result);
|
||||
}
|
||||
|
||||
#endif //WITH_ERRLOG
|
||||
|
||||
#if defined(WITH_PROCESSES) || defined(WITH_ERRLOG)
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \return the program counter of the thread that was running when the exception
|
||||
* occurred.
|
||||
*/
|
||||
static unsigned int getProgramCounter()
|
||||
{
|
||||
register unsigned int result;
|
||||
// Get program counter when the exception was thrown from stack frame
|
||||
asm volatile("mrs %0, psp \n\t"
|
||||
"add %0, %0, #24 \n\t"
|
||||
"ldr %0, [%0] \n\t":"=r"(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif //WITH_PROCESSES || WITH_ERRLOG
|
||||
|
||||
void NMI_Handler()
|
||||
{
|
||||
IRQerrorLog("\r\n***Unexpected NMI\r\n");
|
||||
miosix_private::IRQsystemReboot();
|
||||
}
|
||||
|
||||
void __attribute__((naked)) HardFault_Handler()
|
||||
{
|
||||
saveContext();
|
||||
//Call HardFault_impl(). Name is a C++ mangled name.
|
||||
asm volatile("bl _Z14HardFault_implv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
void __attribute__((noinline)) HardFault_impl()
|
||||
{
|
||||
#ifdef WITH_PROCESSES
|
||||
if(miosix::Thread::IRQreportFault(miosix_private::FaultData(
|
||||
HARDFAULT,getProgramCounter()))) return;
|
||||
#endif //WITH_PROCESSES
|
||||
#ifdef WITH_ERRLOG
|
||||
IRQerrorLog("\r\n***Unexpected HardFault @ ");
|
||||
printUnsignedInt(getProgramCounter());
|
||||
#ifndef _ARCH_CORTEXM0
|
||||
unsigned int hfsr=SCB->HFSR;
|
||||
if(hfsr & 0x40000000) //SCB_HFSR_FORCED
|
||||
IRQerrorLog("Fault escalation occurred\r\n");
|
||||
if(hfsr & 0x00000002) //SCB_HFSR_VECTTBL
|
||||
IRQerrorLog("A BusFault occurred during a vector table read\r\n");
|
||||
#endif //_ARCH_CORTEXM0
|
||||
#endif //WITH_ERRLOG
|
||||
miosix_private::IRQsystemReboot();
|
||||
}
|
||||
|
||||
// Cortex M0/M0+ architecture does not have the interrupts handled by code
|
||||
// below this point
|
||||
#ifndef _ARCH_CORTEXM0
|
||||
|
||||
void __attribute__((naked)) MemManage_Handler()
|
||||
{
|
||||
saveContext();
|
||||
//Call MemManage_impl(). Name is a C++ mangled name.
|
||||
asm volatile("bl _Z14MemManage_implv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
void __attribute__((noinline)) MemManage_impl()
|
||||
{
|
||||
#if defined(WITH_PROCESSES) || defined(WITH_ERRLOG)
|
||||
unsigned int cfsr=SCB->CFSR;
|
||||
#endif //WITH_PROCESSES || WITH_ERRLOG
|
||||
#ifdef WITH_PROCESSES
|
||||
int id, arg=0;
|
||||
if(cfsr & 0x00000001) id=MP_XN;
|
||||
else if(cfsr & 0x00000080) { id=MP; arg=SCB->MMFAR; }
|
||||
else id=MP_NOADDR;
|
||||
if(miosix::Thread::IRQreportFault(miosix_private::FaultData(
|
||||
id,getProgramCounter(),arg)))
|
||||
{
|
||||
SCB->SHCSR &= ~(1<<13); //Clear MEMFAULTPENDED bit
|
||||
return;
|
||||
}
|
||||
#endif //WITH_PROCESSES
|
||||
#ifdef WITH_ERRLOG
|
||||
IRQerrorLog("\r\n***Unexpected MemManage @ ");
|
||||
printUnsignedInt(getProgramCounter());
|
||||
if(cfsr & 0x00000080) //SCB_CFSR_MMARVALID
|
||||
{
|
||||
IRQerrorLog("Fault caused by attempted access to ");
|
||||
printUnsignedInt(SCB->MMFAR);
|
||||
} else IRQerrorLog("The address that caused the fault is missing\r\n");
|
||||
if(cfsr & 0x00000010) //SCB_CFSR_MSTKERR
|
||||
IRQerrorLog("Fault occurred during exception stacking\r\n");
|
||||
if(cfsr & 0x00000008) //SCB_CFSR_MUNSTKERR
|
||||
IRQerrorLog("Fault occurred during exception unstacking\r\n");
|
||||
if(cfsr & 0x00000002) //SCB_CFSR_DACCVIOL
|
||||
IRQerrorLog("Fault was caused by invalid PC\r\n");
|
||||
if(cfsr & 0x00000001) //SCB_CFSR_IACCVIOL
|
||||
IRQerrorLog("Fault was caused by attempted execution from XN area\r\n");
|
||||
#endif //WITH_ERRLOG
|
||||
miosix_private::IRQsystemReboot();
|
||||
}
|
||||
|
||||
void __attribute__((naked)) BusFault_Handler()
|
||||
{
|
||||
saveContext();
|
||||
//Call BusFault_impl(). Name is a C++ mangled name.
|
||||
asm volatile("bl _Z13BusFault_implv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
void __attribute__((noinline)) BusFault_impl()
|
||||
{
|
||||
#if defined(WITH_PROCESSES) || defined(WITH_ERRLOG)
|
||||
unsigned int cfsr=SCB->CFSR;
|
||||
#endif //WITH_PROCESSES || WITH_ERRLOG
|
||||
#ifdef WITH_PROCESSES
|
||||
int id, arg=0;
|
||||
if(cfsr & 0x00008000) { id=BF; arg=SCB->BFAR; }
|
||||
else id=BF_NOADDR;
|
||||
if(miosix::Thread::IRQreportFault(miosix_private::FaultData(
|
||||
id,getProgramCounter(),arg)))
|
||||
{
|
||||
SCB->SHCSR &= ~(1<<14); //Clear BUSFAULTPENDED bit
|
||||
return;
|
||||
}
|
||||
#endif //WITH_PROCESSES
|
||||
#ifdef WITH_ERRLOG
|
||||
IRQerrorLog("\r\n***Unexpected BusFault @ ");
|
||||
printUnsignedInt(getProgramCounter());
|
||||
if(cfsr & 0x00008000) //SCB_CFSR_BFARVALID
|
||||
{
|
||||
IRQerrorLog("Fault caused by attempted access to ");
|
||||
printUnsignedInt(SCB->BFAR);
|
||||
} else IRQerrorLog("The address that caused the fault is missing\r\n");
|
||||
if(cfsr & 0x00001000) //SCB_CFSR_STKERR
|
||||
IRQerrorLog("Fault occurred during exception stacking\r\n");
|
||||
if(cfsr & 0x00000800) //SCB_CFSR_UNSTKERR
|
||||
IRQerrorLog("Fault occurred during exception unstacking\r\n");
|
||||
if(cfsr & 0x00000400) //SCB_CFSR_IMPRECISERR
|
||||
IRQerrorLog("Fault is imprecise\r\n");
|
||||
if(cfsr & 0x00000200) //SCB_CFSR_PRECISERR
|
||||
IRQerrorLog("Fault is precise\r\n");
|
||||
if(cfsr & 0x00000100) //SCB_CFSR_IBUSERR
|
||||
IRQerrorLog("Fault happened during instruction fetch\r\n");
|
||||
#endif //WITH_ERRLOG
|
||||
miosix_private::IRQsystemReboot();
|
||||
}
|
||||
|
||||
void __attribute__((naked)) UsageFault_Handler()
|
||||
{
|
||||
saveContext();
|
||||
//Call UsageFault_impl(). Name is a C++ mangled name.
|
||||
asm volatile("bl _Z15UsageFault_implv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
void __attribute__((noinline)) UsageFault_impl()
|
||||
{
|
||||
#if defined(WITH_PROCESSES) || defined(WITH_ERRLOG)
|
||||
unsigned int cfsr=SCB->CFSR;
|
||||
#endif //WITH_PROCESSES || WITH_ERRLOG
|
||||
#ifdef WITH_PROCESSES
|
||||
int id;
|
||||
if(cfsr & 0x02000000) id=UF_DIVZERO;
|
||||
else if(cfsr & 0x01000000) id=UF_UNALIGNED;
|
||||
else if(cfsr & 0x00080000) id=UF_COPROC;
|
||||
else if(cfsr & 0x00040000) id=UF_EXCRET;
|
||||
else if(cfsr & 0x00020000) id=UF_EPSR;
|
||||
else if(cfsr & 0x00010000) id=UF_UNDEF;
|
||||
else id=UF_UNEXP;
|
||||
if(miosix::Thread::IRQreportFault(miosix_private::FaultData(
|
||||
id,getProgramCounter())))
|
||||
{
|
||||
SCB->SHCSR &= ~(1<<12); //Clear USGFAULTPENDED bit
|
||||
return;
|
||||
}
|
||||
#endif //WITH_PROCESSES
|
||||
#ifdef WITH_ERRLOG
|
||||
IRQerrorLog("\r\n***Unexpected UsageFault @ ");
|
||||
printUnsignedInt(getProgramCounter());
|
||||
if(cfsr & 0x02000000) //SCB_CFSR_DIVBYZERO
|
||||
IRQerrorLog("Divide by zero\r\n");
|
||||
if(cfsr & 0x01000000) //SCB_CFSR_UNALIGNED
|
||||
IRQerrorLog("Unaligned memory access\r\n");
|
||||
if(cfsr & 0x00080000) //SCB_CFSR_NOCP
|
||||
IRQerrorLog("Attempted coprocessor access\r\n");
|
||||
if(cfsr & 0x00040000) //SCB_CFSR_INVPC
|
||||
IRQerrorLog("EXC_RETURN not expected now\r\n");
|
||||
if(cfsr & 0x00020000) //SCB_CFSR_INVSTATE
|
||||
IRQerrorLog("Invalid EPSR usage\r\n");
|
||||
if(cfsr & 0x00010000) //SCB_CFSR_UNDEFINSTR
|
||||
IRQerrorLog("Undefined instruction\r\n");
|
||||
#endif //WITH_ERRLOG
|
||||
miosix_private::IRQsystemReboot();
|
||||
}
|
||||
|
||||
void DebugMon_Handler()
|
||||
{
|
||||
#ifdef WITH_ERRLOG
|
||||
IRQerrorLog("\r\n***Unexpected DebugMon @ ");
|
||||
printUnsignedInt(getProgramCounter());
|
||||
#endif //WITH_ERRLOG
|
||||
miosix_private::IRQsystemReboot();
|
||||
}
|
||||
|
||||
#endif //_ARCH_CORTEXM0
|
||||
|
||||
void PendSV_Handler()
|
||||
{
|
||||
#ifdef WITH_ERRLOG
|
||||
IRQerrorLog("\r\n***Unexpected PendSV @ ");
|
||||
printUnsignedInt(getProgramCounter());
|
||||
#endif //WITH_ERRLOG
|
||||
miosix_private::IRQsystemReboot();
|
||||
}
|
||||
|
||||
void unexpectedInterrupt()
|
||||
{
|
||||
#ifdef WITH_ERRLOG
|
||||
IRQerrorLog("\r\n***Unexpected Peripheral interrupt\r\n");
|
||||
#endif //WITH_ERRLOG
|
||||
miosix_private::IRQsystemReboot();
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010, 2011, 2012, 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef INTERRUPTS_H
|
||||
#define INTERRUPTS_H
|
||||
|
||||
/**
|
||||
* Called when an unexpected interrupt occurs.
|
||||
* It is called by stage_1_boot.cpp for all weak interrupts not defined.
|
||||
*/
|
||||
void unexpectedInterrupt();
|
||||
|
||||
/**
|
||||
* Possible kind of faults that the Cortex-M3 can report.
|
||||
* They are used to print debug information if a process causes a fault
|
||||
*/
|
||||
enum FaultType
|
||||
{
|
||||
MP=1, //Process attempted data access outside its memory
|
||||
MP_NOADDR=2, //Process attempted data access outside its memory (missing addr)
|
||||
MP_XN=3, //Process attempted code access outside its memory
|
||||
UF_DIVZERO=4, //Process attempted to divide by zero
|
||||
UF_UNALIGNED=5,//Process attempted unaligned memory access
|
||||
UF_COPROC=6, //Process attempted a coprocessor access
|
||||
UF_EXCRET=7, //Process attempted an exception return
|
||||
UF_EPSR=8, //Process attempted to access the EPSR
|
||||
UF_UNDEF=9, //Process attempted to execute an invalid instruction
|
||||
UF_UNEXP=10, //Unexpected usage fault
|
||||
HARDFAULT=11, //Hardfault (for example process executed a BKPT instruction)
|
||||
BF=12, //Busfault
|
||||
BF_NOADDR=13 //Busfault (missing addr)
|
||||
};
|
||||
|
||||
#endif //INTERRUPTS_H
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
//MPU code is common for all the cortex M cores, so it has been put here
|
||||
|
||||
#ifndef MEMORY_PROTECTION_H
|
||||
#define MEMORY_PROTECTION_H
|
||||
|
||||
#if defined(_ARCH_CORTEXM3_STM32F2) || defined(_ARCH_CORTEXM4_STM32F4) \
|
||||
|| defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7) \
|
||||
|| defined(_ARCH_CORTEXM3_EFM32GG) || defined(_ARCH_CORTEXM4_STM32L4)
|
||||
#include "mpu_cortexMx.h"
|
||||
#endif
|
||||
|
||||
#endif //MEMORY_PROTECTION_H
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2018 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include "mpu_cortexMx.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
|
||||
namespace miosix {
|
||||
|
||||
unsigned int sizeToMpu(unsigned int size)
|
||||
{
|
||||
assert(size>=32);
|
||||
unsigned int result=30-__builtin_clz(size);
|
||||
if(size & (size-1)) result++;
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef WITH_PROCESSES
|
||||
|
||||
//
|
||||
// class MPUConfiguration
|
||||
//
|
||||
|
||||
MPUConfiguration::MPUConfiguration(unsigned int *elfBase, unsigned int elfSize,
|
||||
unsigned int *imageBase, unsigned int imageSize)
|
||||
{
|
||||
// NOTE: The ARM documentation is unclear about the effect of the shareable
|
||||
// bit on a single core architecture. Experimental evidence on an STM32F476
|
||||
// shows that setting it in IRQconfigureCache for the internal RAM region
|
||||
// causes the boot to fail.
|
||||
// For this reason, all regions are marked as not shareable
|
||||
regValues[0]=(reinterpret_cast<unsigned int>(elfBase) & (~0x1f))
|
||||
| MPU_RBAR_VALID_Msk | 6; //Region 6
|
||||
regValues[2]=(reinterpret_cast<unsigned int>(imageBase) & (~0x1f))
|
||||
| MPU_RBAR_VALID_Msk | 7; //Region 7
|
||||
regValues[1]=2<<MPU_RASR_AP_Pos //Privileged: RW, unprivileged: RO
|
||||
| MPU_RASR_C_Msk
|
||||
| 1 //Enable bit
|
||||
| sizeToMpu(elfSize)<<1;
|
||||
regValues[3]=3<<MPU_RASR_AP_Pos //Privileged: RW, unprivileged: RW
|
||||
| MPU_RASR_XN_Msk
|
||||
| MPU_RASR_C_Msk
|
||||
| 1 //Enable bit
|
||||
| sizeToMpu(imageSize)<<1;
|
||||
}
|
||||
|
||||
void MPUConfiguration::dumpConfiguration()
|
||||
{
|
||||
for(int i=0;i<2;i++)
|
||||
{
|
||||
unsigned int base=regValues[2*i] & (~0x1f);
|
||||
unsigned int end=base+(1<<(((regValues[2*i+1]>>1) & 31)+1));
|
||||
char w=regValues[2*i+1] & (1<<MPU_RASR_AP_Pos) ? 'w' : '-';
|
||||
char x=regValues[2*i+1] & MPU_RASR_XN_Msk ? '-' : 'x';
|
||||
iprintf("* MPU region %d 0x%08x-0x%08x r%c%c\n",i+6,base,end,w,x);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int MPUConfiguration::roundSizeForMPU(unsigned int size)
|
||||
{
|
||||
return 1<<(sizeToMpu(size)+1);
|
||||
}
|
||||
|
||||
bool MPUConfiguration::withinForReading(const void *ptr, size_t size) const
|
||||
{
|
||||
size_t codeStart=regValues[0] & (~0x1f);
|
||||
size_t codeEnd=codeStart+(1<<(((regValues[1]>>1) & 31)+1));
|
||||
size_t dataStart=regValues[2] & (~0x1f);
|
||||
size_t dataEnd=dataStart+(1<<(((regValues[3]>>1) & 31)+1));
|
||||
size_t base=reinterpret_cast<size_t>(ptr);
|
||||
//The last check is to prevent a wraparound to be considered valid
|
||||
return ( (base>=codeStart && base+size<codeEnd)
|
||||
|| (base>=dataStart && base+size<dataEnd)) && base+size>=base;
|
||||
}
|
||||
|
||||
bool MPUConfiguration::withinForWriting(const void *ptr, size_t size) const
|
||||
{
|
||||
size_t dataStart=regValues[2] & (~0x1f);
|
||||
size_t dataEnd=dataStart+(1<<(((regValues[3]>>1) & 31)+1));
|
||||
size_t base=reinterpret_cast<size_t>(ptr);
|
||||
//The last check is to prevent a wraparound to be considered valid
|
||||
return base>=dataStart && base+size<dataEnd && base+size>=base;
|
||||
}
|
||||
|
||||
bool MPUConfiguration::withinForReading(const char* str) const
|
||||
{
|
||||
size_t codeStart=regValues[0] & (~0x1f);
|
||||
size_t codeEnd=codeStart+(1<<(((regValues[1]>>1) & 31)+1));
|
||||
size_t dataStart=regValues[2] & (~0x1f);
|
||||
size_t dataEnd=dataStart+(1<<(((regValues[3]>>1) & 31)+1));
|
||||
size_t base=reinterpret_cast<size_t>(str);
|
||||
if((base>=codeStart) && (base<codeEnd))
|
||||
return strnlen(str,codeEnd-base)<codeEnd-base;
|
||||
if((base>=dataStart) && (base<dataEnd))
|
||||
return strnlen(str,dataEnd-base)<dataEnd-base;
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif //WITH_PROCESSES
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2018 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef MPU_CORTEX_MX_H
|
||||
#define MPU_CORTEX_MX_H
|
||||
|
||||
#include "config/miosix_settings.h"
|
||||
#include "interfaces/arch_registers.h"
|
||||
#include <cstddef>
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* \param size in bytes >32
|
||||
* \return a value that can be written to MPU->RASR to represent that size
|
||||
*/
|
||||
unsigned int sizeToMpu(unsigned int size);
|
||||
|
||||
/**
|
||||
* To be called at boot to enable the MPU.
|
||||
* Without calling this function, the MPU will not work even if regions are
|
||||
* configured in MPUConfiguration
|
||||
*/
|
||||
inline void IRQenableMPUatBoot()
|
||||
{
|
||||
MPU->CTRL=MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_ENABLE_Msk;
|
||||
}
|
||||
|
||||
#ifdef WITH_PROCESSES
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* This class is used to manage the MemoryProtectionUnit
|
||||
*/
|
||||
class MPUConfiguration
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Default constructor, leaves the MPU regions unconfigured
|
||||
*/
|
||||
MPUConfiguration() {}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \param elfBase base address of the ELF file
|
||||
* \param elfSize size of the ELF file
|
||||
* \param imageBase base address of the Process RAM image
|
||||
* \param imageSize size of the Process RAM image
|
||||
*/
|
||||
MPUConfiguration(unsigned int *elfBase, unsigned int elfSize,
|
||||
unsigned int *imageBase, unsigned int imageSize);
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* This method is used to configure the Memoy Protection region for a
|
||||
* Process during a context-switch to a userspace thread.
|
||||
* Can only be called inside an IRQ, not even with interrupts disabled
|
||||
*/
|
||||
void IRQenable()
|
||||
{
|
||||
MPU->RBAR=regValues[0];
|
||||
MPU->RASR=regValues[1];
|
||||
MPU->RBAR=regValues[2];
|
||||
MPU->RASR=regValues[3];
|
||||
__set_CONTROL(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* This method is used to disable the MPU during a context-switch to a
|
||||
* kernelspace thread.
|
||||
* Can only be called inside an IRQ, not even with interrupts disabled
|
||||
*/
|
||||
static void IRQdisable()
|
||||
{
|
||||
__set_CONTROL(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the MPU configuration for debugging purposes
|
||||
*/
|
||||
void dumpConfiguration();
|
||||
|
||||
/**
|
||||
* Some MPU implementations may not allow regions of arbitrary size,
|
||||
* this function allows to round a size up to the minimum value that
|
||||
* the MPU support.
|
||||
* \param size the size of a memory area to be configured as an MPU
|
||||
* region
|
||||
* \return the size rounded to the minimum MPU region allowed that is
|
||||
* greater or equal to the given size
|
||||
*/
|
||||
static unsigned int roundSizeForMPU(unsigned int size);
|
||||
|
||||
/**
|
||||
* Check if a buffer is within a readable segment of the process
|
||||
* \param ptr base pointer of the buffer to check
|
||||
* \param size buffer size
|
||||
* \return true if the buffer is correctly within the process
|
||||
*/
|
||||
bool withinForReading(const void *ptr, size_t size) const;
|
||||
|
||||
/**
|
||||
* Check if a buffer is within a writable segment of the process
|
||||
* \param ptr base pointer of the buffer to check
|
||||
* \param size buffer size
|
||||
* \return true if the buffer is correctly within the process
|
||||
*/
|
||||
bool withinForWriting(const void *ptr, size_t size) const;
|
||||
|
||||
/**
|
||||
* Check if a nul terminated string is entirely contained in the process,
|
||||
* \param str a pointer to a nul terminated string
|
||||
* \return true if the buffer is correctly within the process
|
||||
*/
|
||||
bool withinForReading(const char *str) const;
|
||||
|
||||
//Uses default copy constructor and operator=
|
||||
private:
|
||||
///These value are copied into the MPU registers to configure them
|
||||
unsigned int regValues[4];
|
||||
};
|
||||
|
||||
#endif //WITH_PROCESSES
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //MPU_CORTEX_MX_H
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2011, 2012, 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
/*
|
||||
* DCC support inspired by dcc_stdio.c in OpenOCD
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include "filesystem/ioctl.h"
|
||||
#include "dcc.h"
|
||||
|
||||
namespace miosix {
|
||||
|
||||
ssize_t ARMDCC::readBlock(void* buffer, size_t size, off_t where)
|
||||
{
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
ssize_t ARMDCC::writeBlock(const void* buffer, size_t size, off_t where)
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
debugStr(reinterpret_cast<const char*>(buffer),size);
|
||||
return size;
|
||||
}
|
||||
|
||||
void ARMDCC::IRQwrite(const char* str)
|
||||
{
|
||||
//Actually, there is a race condition if IRQdebugWrite is called from an
|
||||
//interrupt while the main code is printing with debugWrite, but it is
|
||||
//not such an issue since IRQdebugWrite is called only
|
||||
//- at boot before the kernel is started, so there are no other threads
|
||||
//- in case of a serious error, to print what went wrong before rebooting
|
||||
//In the second case, which is rare, the data may not be printed correctly
|
||||
debugStr(str,-1);
|
||||
}
|
||||
|
||||
int ARMDCC::ioctl(int cmd, void* arg)
|
||||
{
|
||||
if(cmd==IOCTL_SYNC) return 0; //Nothing to do, but say we did somaething
|
||||
return -ENOTTY; //Means the operation does not apply to this descriptor
|
||||
}
|
||||
|
||||
void ARMDCC::send(unsigned char c)
|
||||
{
|
||||
const unsigned int busy=1;
|
||||
while(CoreDebug->DCRDR & busy) ;
|
||||
CoreDebug->DCRDR=(static_cast<unsigned int>(c)<<8) | busy;
|
||||
}
|
||||
|
||||
void ARMDCC::debugStr(const char *str, int length)
|
||||
{
|
||||
//If not being debugged, don't print anything
|
||||
if((CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk)==0) return;
|
||||
|
||||
if(length<0) length=strlen(str);
|
||||
if(length>0 && str[0]=='\r') str++, length--; //TODO: better \r\n removal
|
||||
if(length>0 && str[0]=='\n') str++, length--;
|
||||
if(length==0) return;
|
||||
send(1); //1=sending a string
|
||||
send(0);
|
||||
send(length & 0xff);
|
||||
send(length>>8);
|
||||
for(int i=0;i<length;i++) send(*str++);
|
||||
//OpenOCD expects data in 4 byte blocks, so send trailing zeros to align
|
||||
int remaining=4-(length & 3);
|
||||
if(remaining<4) for(int i=0;i<remaining;i++) send(0);
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2011, 2012, 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef DCC_H
|
||||
#define DCC_H
|
||||
|
||||
#include "filesystem/console/console_device.h"
|
||||
#include "kernel/sync.h"
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* This class exposes the ARM debug communication channel through the Device
|
||||
* interface, to allow redirecting stdin and stdout to the DCC for boards that
|
||||
* have JTAG/SWD but no serial port
|
||||
*/
|
||||
class ARMDCC : public Device
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
ARMDCC() : Device(Device::TTY) {}
|
||||
|
||||
/**
|
||||
* Read a block of data. As the DCC is write only, this function returns an
|
||||
* error
|
||||
* \param buffer buffer where read data will be stored
|
||||
* \param size buffer size
|
||||
* \param where where to read from
|
||||
* \return number of bytes read or a negative number on failure
|
||||
*/
|
||||
ssize_t readBlock(void *buffer, size_t size, off_t where);
|
||||
|
||||
/**
|
||||
* Write a block of data
|
||||
* \param buffer buffer where take data to write
|
||||
* \param size buffer size
|
||||
* \param where where to write to
|
||||
* \return number of bytes written or a negative number on failure
|
||||
*/
|
||||
ssize_t writeBlock(const void *buffer, size_t size, off_t where);
|
||||
|
||||
/**
|
||||
* Write a string.
|
||||
* An extension to the Device interface that adds a new member function,
|
||||
* which is used by the kernel on console devices to write debug information
|
||||
* before the kernel is started or in case of serious errors, right before
|
||||
* rebooting.
|
||||
* Can ONLY be called when the kernel is not yet started, paused or within
|
||||
* an interrupt. This default implementation ignores writes.
|
||||
* \param str the string to write. The string must be NUL terminated.
|
||||
*/
|
||||
void IRQwrite(const char *str);
|
||||
|
||||
/**
|
||||
* Performs device-specific operations
|
||||
* \param cmd specifies the operation to perform
|
||||
* \param arg optional argument that some operation require
|
||||
* \return the exact return value depends on CMD, -1 is returned on error
|
||||
*/
|
||||
int ioctl(int cmd, void *arg);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Send data to the host
|
||||
*/
|
||||
static void send(unsigned char c);
|
||||
|
||||
/**
|
||||
* Send a line of text to the host.
|
||||
* OpenOCD will insert a \n after each line, unfortunately,
|
||||
* as this complicates things.
|
||||
*/
|
||||
static void debugStr(const char *str, int length);
|
||||
|
||||
FastMutex mutex;
|
||||
};
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //DCC_H
|
||||
|
|
@ -0,0 +1,587 @@
|
|||
/*
|
||||
* Integration in Miosix by Terraneo Federico.
|
||||
* Based on code by Martin Thomas to initialize SD cards from LPC2000
|
||||
*/
|
||||
|
||||
#include "sd_lpc2000.h"
|
||||
#include "interfaces/bsp.h"
|
||||
#include "LPC213x.h"
|
||||
#include "board_settings.h" //For sdVoltage
|
||||
#include <cstdio>
|
||||
#include <errno.h>
|
||||
|
||||
//Note: enabling debugging might cause deadlock when using sleep() or reboot()
|
||||
//The bug won't be fixed because debugging is only useful for driver development
|
||||
///\internal Debug macro, for normal conditions
|
||||
//#define DBG iprintf
|
||||
#define DBG(x,...) do {} while(0)
|
||||
///\internal Debug macro, for errors only
|
||||
//#define DBGERR iprintf
|
||||
#define DBGERR(x,...) do {} while(0)
|
||||
|
||||
namespace miosix {
|
||||
|
||||
///\internal Type of card (1<<0)=MMC (1<<1)=SDv1 (1<<2)=SDv2 (1<<2)|(1<<3)=SDHC
|
||||
static unsigned char cardType=0;
|
||||
|
||||
/*
|
||||
* Definitions for MMC/SDC command.
|
||||
* A command has the following format:
|
||||
* - 1 bit @ 0 (start bit)
|
||||
* - 1 bit @ 1 (transmission bit)
|
||||
* - 6 bit which identify command index (CMD0..CMD63)
|
||||
* - 32 bit command argument
|
||||
* - 7 bit CRC
|
||||
* - 1 bit @ 1 (end bit)
|
||||
* In addition, ACMDxx are the sequence of two commands, CMD55 and CMDxx
|
||||
* These constants have the following meaninig:
|
||||
* - bit #7 @ 1 indicates that it is an ACMD. send_cmd() will send CMD55, then
|
||||
* clear this bit and send the command with this bit @ 0 (which is start bit)
|
||||
* - bit #6 always @ 1, because it is the transmission bit
|
||||
* - remaining 6 bit represent command index
|
||||
*/
|
||||
#define CMD0 (0x40+0) /* GO_IDLE_STATE */
|
||||
#define CMD1 (0x40+1) /* SEND_OP_COND (MMC) */
|
||||
#define ACMD41 (0xC0+41) /* SEND_OP_COND (SDC) */
|
||||
#define CMD8 (0x40+8) /* SEND_IF_COND */
|
||||
#define CMD9 (0x40+9) /* SEND_CSD */
|
||||
#define CMD10 (0x40+10) /* SEND_CID */
|
||||
#define CMD12 (0x40+12) /* STOP_TRANSMISSION */
|
||||
#define CMD13 (0x40+13) /* SEND_STATUS */
|
||||
#define ACMD13 (0xC0+13) /* SD_STATUS (SDC) */
|
||||
#define CMD16 (0x40+16) /* SET_BLOCKLEN */
|
||||
#define CMD17 (0x40+17) /* READ_SINGLE_BLOCK */
|
||||
#define CMD18 (0x40+18) /* READ_MULTIPLE_BLOCK */
|
||||
#define CMD23 (0x40+23) /* SET_BLOCK_COUNT (MMC) */
|
||||
#define ACMD23 (0xC0+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */
|
||||
#define CMD24 (0x40+24) /* WRITE_BLOCK */
|
||||
#define CMD25 (0x40+25) /* WRITE_MULTIPLE_BLOCK */
|
||||
#define CMD42 (0x40+42) /* LOCK_UNLOCK */
|
||||
#define CMD55 (0x40+55) /* APP_CMD */
|
||||
#define CMD58 (0x40+58) /* READ_OCR */
|
||||
|
||||
// SSPCR0 Bit-Definitions
|
||||
#define CPOL 6
|
||||
#define CPHA 7
|
||||
// SSPCR1 Bit-Defintions
|
||||
#define SSE 1
|
||||
#define MS 2
|
||||
#define SCR 8
|
||||
// SSPSR Bit-Definitions
|
||||
#define TNF 1
|
||||
#define RNE 2
|
||||
#define BSY 4
|
||||
|
||||
#define SPI_SCK_PIN 17 // SCK P0.17 out
|
||||
#define SPI_MISO_PIN 18 // MISO P0.18 in
|
||||
#define SPI_MOSI_PIN 19 // MOSI P0.19 out
|
||||
#define SPI_SS_PIN 20 // CS p0.20 out
|
||||
|
||||
#define SPI_SCK_FUNCBIT 2
|
||||
#define SPI_MISO_FUNCBIT 4
|
||||
#define SPI_MOSI_FUNCBIT 6
|
||||
#define SPI_SS_FUNCBIT 8
|
||||
|
||||
///\internal Maximum speed 14745600/2=7372800
|
||||
#define SPI_PRESCALE_MIN 2
|
||||
|
||||
///\internal Select/unselect card
|
||||
#define CS_LOW() IOCLR0 = (1<<SPI_SS_PIN)
|
||||
#define CS_HIGH() IOSET0 = (1<<SPI_SS_PIN)
|
||||
|
||||
//Function prototypes
|
||||
static unsigned char send_cmd(unsigned char cmd, unsigned int arg);
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Initialize SPI
|
||||
*/
|
||||
static void spi_1_init()
|
||||
{
|
||||
unsigned char incoming;
|
||||
PCONP|=(1<<10);//Enable SPI1
|
||||
// setup GPIO
|
||||
IODIR0 |= (1<<SPI_SCK_PIN)|(1<<SPI_MOSI_PIN)|(1<<SPI_SS_PIN);
|
||||
IODIR0 &= ~(1<<SPI_MISO_PIN);
|
||||
// Unselect card
|
||||
CS_HIGH();
|
||||
// Set GPIO mode
|
||||
PINSEL1 &= ~( (3<<SPI_SCK_FUNCBIT) | (3<<SPI_MISO_FUNCBIT) |
|
||||
(3<<SPI_MOSI_FUNCBIT) | (3<<SPI_SS_FUNCBIT) );
|
||||
|
||||
// setup Pin-Functions - keep automatic CS disabled during init
|
||||
PINSEL1 |= ( (2<<SPI_SCK_FUNCBIT) | (2<<SPI_MISO_FUNCBIT) |
|
||||
(2<<SPI_MOSI_FUNCBIT) );
|
||||
// enable SPI-Master - slowest speed
|
||||
SSPCR0 = ((8-1)<<0) | (0<<CPOL) | (0x20<<SCR);
|
||||
SSPCR1 = (1<<SSE);
|
||||
// low speed during init
|
||||
SSPCPSR=254;
|
||||
|
||||
// Send 20 spi commands with card not selected
|
||||
for(int i=0;i<20;i++)
|
||||
{
|
||||
while( !(SSPSR & (1<<TNF)) ) ; //Wait
|
||||
SSPDR=0xff;
|
||||
while( !(SSPSR & (1<<RNE)) ) ; //Wait
|
||||
incoming=SSPDR;
|
||||
(void)incoming;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Send and receive one byte through SPI
|
||||
*/
|
||||
static unsigned char spi_1_send(unsigned char outgoing)
|
||||
{
|
||||
while(!(SSPSR & (1<<TNF))) ;
|
||||
SSPDR=outgoing;
|
||||
while(!(SSPSR & (1<<RNE))) ;
|
||||
return SSPDR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Used for debugging, print 8 bit error code from SD card
|
||||
*/
|
||||
static void print_error_code(unsigned char value)
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
case 0x40:
|
||||
DBGERR("Parameter error\n");
|
||||
break;
|
||||
case 0x20:
|
||||
DBGERR("Address error\n");
|
||||
break;
|
||||
case 0x10:
|
||||
DBGERR("Erase sequence error\n");
|
||||
break;
|
||||
case 0x08:
|
||||
DBGERR("CRC error\n");
|
||||
break;
|
||||
case 0x04:
|
||||
DBGERR("Illegal command\n");
|
||||
break;
|
||||
case 0x02:
|
||||
DBGERR("Erase reset\n");
|
||||
break;
|
||||
case 0x01:
|
||||
DBGERR("Card is initializing\n");
|
||||
break;
|
||||
default:
|
||||
DBGERR("Unknown error 0x%x\n",value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Return 1 if card is OK, otherwise print 16 bit error code from SD card
|
||||
*/
|
||||
static char sd_status()
|
||||
{
|
||||
short value=send_cmd(CMD13,0);
|
||||
value<<=8;
|
||||
value|=spi_1_send(0xff);
|
||||
|
||||
switch(value)
|
||||
{
|
||||
case 0x0000:
|
||||
return 1;
|
||||
case 0x0001:
|
||||
DBGERR("Card is Locked\n");
|
||||
/*//Try to fix the problem by erasing all the SD card.
|
||||
char e=send_cmd(CMD16,1);
|
||||
if(e!=0) print_error_code(e);
|
||||
e=send_cmd(CMD42,0);
|
||||
if(e!=0) print_error_code(e);
|
||||
spi_1_send(0xfe); // Start block
|
||||
spi_1_send(1<<3); //Erase all disk command
|
||||
spi_1_send(0xff); // Checksum part 1
|
||||
spi_1_send(0xff); // Checksum part 2
|
||||
e=spi_1_send(0xff);
|
||||
iprintf("Reached here 0x%x\n",e);//Should return 0xe5
|
||||
while(spi_1_send(0xff)!=0xff);*/
|
||||
break;
|
||||
case 0x0002:
|
||||
DBGERR("WP erase skip or lock/unlock cmd failed\n");
|
||||
break;
|
||||
case 0x0004:
|
||||
DBGERR("General error\n");
|
||||
break;
|
||||
case 0x0008:
|
||||
DBGERR("Internal card controller error\n");
|
||||
break;
|
||||
case 0x0010:
|
||||
DBGERR("Card ECC failed\n");
|
||||
break;
|
||||
case 0x0020:
|
||||
DBGERR("Write protect violation\n");
|
||||
break;
|
||||
case 0x0040:
|
||||
DBGERR("Invalid selection for erase\n");
|
||||
break;
|
||||
case 0x0080:
|
||||
DBGERR("Out of range or CSD overwrite\n");
|
||||
break;
|
||||
default:
|
||||
if(value>0x00FF)
|
||||
print_error_code((unsigned char)(value>>8));
|
||||
else
|
||||
DBGERR("Unknown error 0x%x\n",value);
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Wait until card is ready
|
||||
*/
|
||||
static unsigned char wait_ready()
|
||||
{
|
||||
unsigned char result;
|
||||
spi_1_send(0xff);
|
||||
for(int i=0;i<460800;i++)//Timeout ~500ms
|
||||
{
|
||||
result=spi_1_send(0xff);
|
||||
if(result==0xff) return 0xff;
|
||||
if(i%500==0) DBG("*\n");
|
||||
}
|
||||
DBGERR("Error: wait_ready()\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Send a command to the SD card
|
||||
* \param cmd one among the #define'd commands
|
||||
* \param arg command's argument
|
||||
* \return command's r1 response. If command returns a longer response, the user
|
||||
* can continue reading the response with spi_1_send(0xff)
|
||||
*/
|
||||
static unsigned char send_cmd(unsigned char cmd, unsigned int arg)
|
||||
{
|
||||
unsigned char n, res;
|
||||
if(cmd & 0x80)
|
||||
{ // ACMD<n> is the command sequence of CMD55-CMD<n>
|
||||
cmd&=0x7f;
|
||||
res=send_cmd(CMD55,0);
|
||||
if(res>1) return res;
|
||||
}
|
||||
|
||||
// Select the card and wait for ready
|
||||
CS_HIGH();
|
||||
CS_LOW();
|
||||
|
||||
if(cmd==CMD0)
|
||||
{
|
||||
//wait_ready on CMD0 seems to cause infinite loop
|
||||
spi_1_send(0xff);
|
||||
} else {
|
||||
if(wait_ready()!=0xff) return 0xff;
|
||||
}
|
||||
// Send command
|
||||
spi_1_send(cmd); // Start + Command index
|
||||
spi_1_send((unsigned char)(arg >> 24)); // Argument[31..24]
|
||||
spi_1_send((unsigned char)(arg >> 16)); // Argument[23..16]
|
||||
spi_1_send((unsigned char)(arg >> 8)); // Argument[15..8]
|
||||
spi_1_send((unsigned char)arg); // Argument[7..0]
|
||||
n=0x01; // Dummy CRC + Stop
|
||||
if (cmd==CMD0) n=0x95; // Valid CRC for CMD0(0)
|
||||
if (cmd==CMD8) n=0x87; // Valid CRC for CMD8(0x1AA)
|
||||
spi_1_send(n);
|
||||
// Receive response
|
||||
if (cmd==CMD12) spi_1_send(0xff); // Skip a stuff byte when stop reading
|
||||
n=10; // Wait response, try 10 times
|
||||
do
|
||||
res=spi_1_send(0xff);
|
||||
while ((res & 0x80) && --n);
|
||||
return res; // Return with the response value
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Receive a data packet from the SD card
|
||||
* \param buf data buffer to store received data
|
||||
* \param byte count (must be multiple of 4)
|
||||
* \return true on success, false on failure
|
||||
*/
|
||||
static bool rx_datablock(unsigned char *buf, unsigned int btr)
|
||||
{
|
||||
unsigned char token;
|
||||
for(int i=0;i<0xffff;i++)
|
||||
{
|
||||
token=spi_1_send(0xff);
|
||||
if(token!=0xff) break;
|
||||
}
|
||||
if(token!=0xfe) return false; // If not valid data token, retutn error
|
||||
|
||||
do { // Receive the data block into buffer
|
||||
*buf=spi_1_send(0xff); buf++;
|
||||
*buf=spi_1_send(0xff); buf++;
|
||||
*buf=spi_1_send(0xff); buf++;
|
||||
*buf=spi_1_send(0xff); buf++;
|
||||
} while(btr-=4);
|
||||
spi_1_send(0xff); // Discard CRC
|
||||
spi_1_send(0xff);
|
||||
|
||||
return true; // Return success
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Send a data packet to the SD card
|
||||
* \param buf 512 byte data block to be transmitted
|
||||
* \param token data start/stop token
|
||||
* \return true on success, false on failure
|
||||
*/
|
||||
static bool tx_datablock (const unsigned char *buf, unsigned char token)
|
||||
{
|
||||
unsigned char resp;
|
||||
if(wait_ready()!=0xff) return false;
|
||||
|
||||
spi_1_send(token); // Xmit data token
|
||||
if (token!=0xfd)
|
||||
{ // Is data token
|
||||
for(int i=0;i<256;i++)
|
||||
{ // Xmit the 512 byte data block
|
||||
spi_1_send(*buf); buf++;
|
||||
spi_1_send(*buf); buf++;
|
||||
}
|
||||
spi_1_send(0xff); // CRC (Dummy)
|
||||
spi_1_send(0xff);
|
||||
resp=spi_1_send(0xff); // Receive data response
|
||||
if((resp & 0x1f)!=0x05) // If not accepted, return error
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// class SPISDDriver
|
||||
//
|
||||
|
||||
intrusive_ref_ptr<SPISDDriver> SPISDDriver::instance()
|
||||
{
|
||||
static FastMutex m;
|
||||
static intrusive_ref_ptr<SPISDDriver> instance;
|
||||
Lock<FastMutex> l(m);
|
||||
if(!instance) instance=new SPISDDriver();
|
||||
return instance;
|
||||
}
|
||||
|
||||
ssize_t SPISDDriver::readBlock(void* buffer, size_t size, off_t where)
|
||||
{
|
||||
if(where % 512 || size % 512) return -EFAULT;
|
||||
unsigned int lba=where/512;
|
||||
unsigned int nSectors=size/512;
|
||||
unsigned char *buf=reinterpret_cast<unsigned char*>(buffer);
|
||||
Lock<FastMutex> l(mutex);
|
||||
DBG("SPISDDriver::readBlock(): nSectors=%d\n",nSectors);
|
||||
if(!(cardType & 8)) lba*=512; // Convert to byte address if needed
|
||||
unsigned char result;
|
||||
if(nSectors==1)
|
||||
{ // Single block read
|
||||
result=send_cmd(CMD17,lba); // READ_SINGLE_BLOCK
|
||||
if(result!=0)
|
||||
{
|
||||
print_error_code(result);
|
||||
CS_HIGH();
|
||||
return -EBADF;
|
||||
}
|
||||
if(rx_datablock(buf,512)==false)
|
||||
{
|
||||
DBGERR("Block read error\n");
|
||||
CS_HIGH();
|
||||
return -EBADF;
|
||||
}
|
||||
} else { // Multiple block read
|
||||
//DBGERR("Mbr\n");//debug only
|
||||
result=send_cmd(CMD18,lba); // READ_MULTIPLE_BLOCK
|
||||
if(result!=0)
|
||||
{
|
||||
print_error_code(result);
|
||||
CS_HIGH();
|
||||
return -EBADF;
|
||||
}
|
||||
do {
|
||||
if(!rx_datablock(buf,512)) break;
|
||||
buf+=512;
|
||||
} while(--nSectors);
|
||||
send_cmd(CMD12,0); // STOP_TRANSMISSION
|
||||
if(nSectors!=0)
|
||||
{
|
||||
DBGERR("Multiple block read error\n");
|
||||
CS_HIGH();
|
||||
return -EBADF;
|
||||
}
|
||||
}
|
||||
CS_HIGH();
|
||||
return size;
|
||||
}
|
||||
|
||||
ssize_t SPISDDriver::writeBlock(const void* buffer, size_t size, off_t where)
|
||||
{
|
||||
if(where % 512 || size % 512) return -EFAULT;
|
||||
unsigned int lba=where/512;
|
||||
unsigned int nSectors=size/512;
|
||||
const unsigned char *buf=reinterpret_cast<const unsigned char*>(buffer);
|
||||
Lock<FastMutex> l(mutex);
|
||||
DBG("SPISDDriver::writeBlock(): nSectors=%d\n",nSectors);
|
||||
if(!(cardType & 8)) lba*=512; // Convert to byte address if needed
|
||||
unsigned char result;
|
||||
if(nSectors==1)
|
||||
{ // Single block write
|
||||
result=send_cmd(CMD24,lba); // WRITE_BLOCK
|
||||
if(result!=0)
|
||||
{
|
||||
print_error_code(result);
|
||||
CS_HIGH();
|
||||
return -EBADF;
|
||||
}
|
||||
if(tx_datablock(buf,0xfe)==false) // WRITE_BLOCK
|
||||
{
|
||||
DBGERR("Block write error\n");
|
||||
CS_HIGH();
|
||||
return -EBADF;
|
||||
}
|
||||
} else { // Multiple block write
|
||||
//DBGERR("Mbw\n");//debug only
|
||||
if(cardType & 6) send_cmd(ACMD23,nSectors);//Only if it is SD card
|
||||
result=send_cmd(CMD25,lba); // WRITE_MULTIPLE_BLOCK
|
||||
if(result!=0)
|
||||
{
|
||||
print_error_code(result);
|
||||
CS_HIGH();
|
||||
return -EBADF;
|
||||
}
|
||||
do {
|
||||
if(!tx_datablock(buf,0xfc)) break;
|
||||
buf+=512;
|
||||
} while(--nSectors);
|
||||
if(!tx_datablock(0,0xfd)) // STOP_TRAN token
|
||||
{
|
||||
DBGERR("Multiple block write error\n");
|
||||
CS_HIGH();
|
||||
return -EBADF;
|
||||
}
|
||||
}
|
||||
CS_HIGH();
|
||||
return size;
|
||||
}
|
||||
|
||||
int SPISDDriver::ioctl(int cmd, void* arg)
|
||||
{
|
||||
DBG("SPISDDriver::ioctl()\n");
|
||||
if(cmd!=IOCTL_SYNC) return -ENOTTY;
|
||||
Lock<FastMutex> l(mutex);
|
||||
CS_LOW();
|
||||
unsigned char result=wait_ready();
|
||||
CS_HIGH();
|
||||
if(result==0xff) return 0;
|
||||
else return -EFAULT;
|
||||
}
|
||||
|
||||
SPISDDriver::SPISDDriver() : Device(Device::BLOCK)
|
||||
{
|
||||
const int MAX_RETRY=20;//Maximum command retry before failing
|
||||
spi_1_init(); /* init at low speed */
|
||||
unsigned char resp;
|
||||
int i;
|
||||
for(i=0;i<MAX_RETRY;i++)
|
||||
{
|
||||
resp=send_cmd(CMD0,0);
|
||||
if(resp==1) break;
|
||||
}
|
||||
DBG("CMD0 required %d commands\n",i+1);
|
||||
|
||||
if(resp!=1)
|
||||
{
|
||||
print_error_code(resp);
|
||||
DBGERR("Init failed\n");
|
||||
CS_HIGH();
|
||||
return; //Error
|
||||
}
|
||||
unsigned char n, cmd, ty=0, ocr[4];
|
||||
// Enter Idle state
|
||||
if(send_cmd(CMD8,0x1aa)==1)
|
||||
{ /* SDHC */
|
||||
for(n=0;n<4;n++) ocr[n]=spi_1_send(0xff);// Get return value of R7 resp
|
||||
if((ocr[2]==0x01)&&(ocr[3]==0xaa))
|
||||
{ // The card can work at vdd range of 2.7-3.6V
|
||||
for(i=0;i<MAX_RETRY;i++)
|
||||
{
|
||||
resp=send_cmd(ACMD41, 1UL << 30);
|
||||
if(resp==0)
|
||||
{
|
||||
if(send_cmd(CMD58,0)==0)
|
||||
{ // Check CCS bit in the OCR
|
||||
for(n=0;n<4;n++) ocr[n]=spi_1_send(0xff);
|
||||
if(ocr[0] & 0x40)
|
||||
{
|
||||
ty=12;
|
||||
DBG("SDHC, block addressing supported\n");
|
||||
} else {
|
||||
ty=4;
|
||||
DBG("SDHC, no block addressing\n");
|
||||
}
|
||||
} else DBGERR("CMD58 failed\n");
|
||||
|
||||
break; //Exit from for
|
||||
} else print_error_code(resp);
|
||||
}
|
||||
DBG("ACMD41 required %d commands\n",i+1);
|
||||
} else DBGERR("CMD8 failed\n");
|
||||
} else { /* SDSC or MMC */
|
||||
if(send_cmd(ACMD41,0)<=1)
|
||||
{
|
||||
ty=2;
|
||||
cmd=ACMD41; /* SDSC */
|
||||
DBG("SD card\n");
|
||||
} else {
|
||||
ty=1;
|
||||
cmd=CMD1; /* MMC */
|
||||
DBG("MMC card\n");
|
||||
}
|
||||
for(i=0;i<MAX_RETRY;i++)
|
||||
{
|
||||
resp=send_cmd(cmd,0);
|
||||
if(resp==0)
|
||||
{
|
||||
if(send_cmd(CMD16,512)!=0)
|
||||
{
|
||||
ty=0;
|
||||
DBGERR("CMD16 failed\n");
|
||||
}
|
||||
break; //Exit from for
|
||||
} else print_error_code(resp);
|
||||
}
|
||||
DBG("CMD required %d commands\n",i+1);
|
||||
}
|
||||
|
||||
if(ty==0)
|
||||
{
|
||||
CS_HIGH();
|
||||
return; //Error
|
||||
}
|
||||
cardType=ty;
|
||||
|
||||
if(sd_status()<0)
|
||||
{
|
||||
DBGERR("Status error\n");
|
||||
CS_HIGH();
|
||||
return; //Error
|
||||
}
|
||||
|
||||
CS_HIGH();
|
||||
//Configure the SPI interface to use the 7.4MHz high speed mode
|
||||
SSPCR0=((8-1)<<0) | (0<<CPOL);
|
||||
SSPCPSR=SPI_PRESCALE_MIN;
|
||||
|
||||
DBG("Init done...\n");
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef SD_LPC2000_H
|
||||
#define SD_LPC2000_H
|
||||
|
||||
#include "kernel/sync.h"
|
||||
#include "filesystem/devfs/devfs.h"
|
||||
#include "filesystem/ioctl.h"
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* Driver for interfacing to an SD card through SPI
|
||||
*/
|
||||
class SPISDDriver : public Device
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \return an instance to this class, singleton
|
||||
*/
|
||||
static intrusive_ref_ptr<SPISDDriver> instance();
|
||||
|
||||
virtual ssize_t readBlock(void *buffer, size_t size, off_t where);
|
||||
|
||||
virtual ssize_t writeBlock(const void *buffer, size_t size, off_t where);
|
||||
|
||||
virtual int ioctl(int cmd, void *arg);
|
||||
private:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
SPISDDriver();
|
||||
|
||||
FastMutex mutex;
|
||||
};
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //SD_LPC2000_H
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,64 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef SD_STM32F1_H
|
||||
#define SD_STM32F1_H
|
||||
|
||||
#include "kernel/sync.h"
|
||||
#include "filesystem/devfs/devfs.h"
|
||||
#include "filesystem/ioctl.h"
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* Driver for the SDIO peripheral in STM32F1 microcontrollers
|
||||
*/
|
||||
class SDIODriver : public Device
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \return an instance to this class, singleton
|
||||
*/
|
||||
static intrusive_ref_ptr<SDIODriver> instance();
|
||||
|
||||
virtual ssize_t readBlock(void *buffer, size_t size, off_t where);
|
||||
|
||||
virtual ssize_t writeBlock(const void *buffer, size_t size, off_t where);
|
||||
|
||||
virtual int ioctl(int cmd, void *arg);
|
||||
private:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
SDIODriver();
|
||||
|
||||
FastMutex mutex;
|
||||
};
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //SD_STM32F1_H
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,64 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef SD_STM32F2_F4_H
|
||||
#define SD_STM32F2_F4_H
|
||||
|
||||
#include "kernel/sync.h"
|
||||
#include "filesystem/devfs/devfs.h"
|
||||
#include "filesystem/ioctl.h"
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* Driver for the SDIO peripheral in STM32F2 and F4 microcontrollers
|
||||
*/
|
||||
class SDIODriver : public Device
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \return an instance to this class, singleton
|
||||
*/
|
||||
static intrusive_ref_ptr<SDIODriver> instance();
|
||||
|
||||
virtual ssize_t readBlock(void *buffer, size_t size, off_t where);
|
||||
|
||||
virtual ssize_t writeBlock(const void *buffer, size_t size, off_t where);
|
||||
|
||||
virtual int ioctl(int cmd, void *arg);
|
||||
private:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
SDIODriver();
|
||||
|
||||
FastMutex mutex;
|
||||
};
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //SD_STM32F2_F4_H
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
//Serial code is common for all the cortex M cores, so it has been put here
|
||||
|
||||
#ifdef _ARCH_ARM7_LPC2000
|
||||
#include "serial_lpc2000.h"
|
||||
#elif defined(_ARCH_CORTEXM0_STM32) || defined(_ARCH_CORTEXM3_STM32) \
|
||||
|| defined(_ARCH_CORTEXM4_STM32F4) || defined(_ARCH_CORTEXM3_STM32F2) \
|
||||
|| defined(_ARCH_CORTEXM3_STM32L1) || defined(_ARCH_CORTEXM7_STM32F7) \
|
||||
|| defined(_ARCH_CORTEXM7_STM32H7) || defined(_ARCH_CORTEXM4_STM32F3) \
|
||||
|| defined(_ARCH_CORTEXM4_STM32L4)
|
||||
#include "serial_stm32.h"
|
||||
#elif defined(_ARCH_CORTEXM3_EFM32GG)
|
||||
#include "serial_efm32.h"
|
||||
#elif defined(_ARCH_CORTEXM4_ATSAM4L)
|
||||
#include "serial_atsam4l.h"
|
||||
#else
|
||||
#error "Unknown arch"
|
||||
#endif
|
||||
|
|
@ -0,0 +1,245 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2020 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include <limits>
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <termios.h>
|
||||
#include "serial_atsam4l.h"
|
||||
#include "kernel/sync.h"
|
||||
#include "kernel/scheduler/scheduler.h"
|
||||
#include "interfaces/portability.h"
|
||||
#include "filesystem/ioctl.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace miosix;
|
||||
|
||||
static const int numPorts=4; //USART0 to USART3
|
||||
|
||||
/// Pointer to serial port classes to let interrupts access the classes
|
||||
static ATSAMSerial *ports[numPorts]={0};
|
||||
|
||||
/**
|
||||
* \internal interrupt routine for usart2 rx actual implementation
|
||||
*/
|
||||
void __attribute__((noinline)) usart2rxIrqImpl()
|
||||
{
|
||||
if(ports[2]) ports[2]->IRQhandleInterrupt();
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal interrupt routine for usart2 rx
|
||||
*/
|
||||
void __attribute__((naked)) USART2_Handler()
|
||||
{
|
||||
saveContext();
|
||||
asm volatile("bl _Z15usart2rxIrqImplv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
namespace miosix {
|
||||
|
||||
//
|
||||
// class ATSAMSerial
|
||||
//
|
||||
|
||||
// A note on the baudrate/500: the buffer is selected so as to withstand
|
||||
// 20ms of full data rate. In the 8N1 format one char is made of 10 bits.
|
||||
// So (baudrate/10)*0.02=baudrate/500
|
||||
ATSAMSerial::ATSAMSerial(int id, int baudrate)
|
||||
: Device(Device::TTY), rxQueue(rxQueueMin+baudrate/500), rxWaiting(0),
|
||||
idle(true), portId(id)
|
||||
{
|
||||
if(id!=2 || ports[portId]) errorHandler(UNEXPECTED);
|
||||
|
||||
{
|
||||
InterruptDisableLock dLock;
|
||||
ports[portId]=this;
|
||||
|
||||
port=USART2;
|
||||
|
||||
//TODO: USART2 hardcoded
|
||||
PM->PM_UNLOCK=0xaa<<24 | PM_PBAMASK_OFFSET;
|
||||
PM->PM_PBAMASK |= PM_PBAMASK_USART2;
|
||||
NVIC_SetPriority(USART2_IRQn,15);//Lowest priority for serial
|
||||
NVIC_EnableIRQ(USART2_IRQn);
|
||||
}
|
||||
|
||||
port->US_BRGR = ((SystemCoreClock/baudrate/4)+1)/2; //TODO: fractional part
|
||||
port->US_MR = US_MR_FILTER // Filter input with majority of 3 samples
|
||||
| US_MR_OVER // 8 cycles oversample
|
||||
| US_MR_PAR_NONE // No parity
|
||||
| US_MR_CHRL_8 // 8 bit char
|
||||
| US_MR_USCLKS_MCK // CLK_USART is clock source
|
||||
| US_MR_MODE_NORMAL;// Just a plain usart, please
|
||||
|
||||
port->US_RTOR=10; //Timeout 10 bits (one char time)
|
||||
port->US_IER = US_IER_RXRDY | US_IER_TIMEOUT;
|
||||
|
||||
port->US_CR = US_CR_TXEN | US_CR_RXEN;
|
||||
}
|
||||
|
||||
ssize_t ATSAMSerial::readBlock(void *buffer, size_t size, off_t where)
|
||||
{
|
||||
Lock<FastMutex> l(rxMutex);
|
||||
char *buf=reinterpret_cast<char*>(buffer);
|
||||
size_t result=0;
|
||||
FastInterruptDisableLock dLock;
|
||||
for(;;)
|
||||
{
|
||||
//Try to get data from the queue
|
||||
for(;result<size;result++)
|
||||
{
|
||||
if(rxQueue.tryGet(buf[result])==false) break;
|
||||
//This is here just not to keep IRQ disabled for the whole loop
|
||||
FastInterruptEnableLock eLock(dLock);
|
||||
}
|
||||
if(idle && result>0) break;
|
||||
if(result==size) break;
|
||||
//Wait for data in the queue
|
||||
do {
|
||||
rxWaiting=Thread::IRQgetCurrentThread();
|
||||
Thread::IRQwait();
|
||||
{
|
||||
FastInterruptEnableLock eLock(dLock);
|
||||
Thread::yield();
|
||||
}
|
||||
} while(rxWaiting);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t ATSAMSerial::writeBlock(const void *buffer, size_t size, off_t where)
|
||||
{
|
||||
Lock<FastMutex> l(txMutex);
|
||||
const char *buf=reinterpret_cast<const char*>(buffer);
|
||||
for(size_t i=0;i<size;i++)
|
||||
{
|
||||
while((port->US_CSR & US_CSR_TXRDY) == 0) ;
|
||||
port->US_THR =*buf++;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void ATSAMSerial::IRQwrite(const char *str)
|
||||
{
|
||||
// We can reach here also with only kernel paused, so make sure
|
||||
// interrupts are disabled.
|
||||
bool interrupts=areInterruptsEnabled();
|
||||
if(interrupts) fastDisableInterrupts();
|
||||
while(*str)
|
||||
{
|
||||
while((port->US_CSR & US_CSR_TXRDY) == 0) ;
|
||||
port->US_THR = *str++;
|
||||
}
|
||||
waitSerialTxFifoEmpty();
|
||||
if(interrupts) fastEnableInterrupts();
|
||||
}
|
||||
|
||||
int ATSAMSerial::ioctl(int cmd, void *arg)
|
||||
{
|
||||
if(reinterpret_cast<unsigned>(arg) & 0b11) return -EFAULT; //Unaligned
|
||||
termios *t=reinterpret_cast<termios*>(arg);
|
||||
switch(cmd)
|
||||
{
|
||||
case IOCTL_SYNC:
|
||||
waitSerialTxFifoEmpty();
|
||||
return 0;
|
||||
case IOCTL_TCGETATTR:
|
||||
t->c_iflag=IGNBRK | IGNPAR;
|
||||
t->c_oflag=0;
|
||||
t->c_cflag=CS8;
|
||||
t->c_lflag=0;
|
||||
return 0;
|
||||
case IOCTL_TCSETATTR_NOW:
|
||||
case IOCTL_TCSETATTR_DRAIN:
|
||||
case IOCTL_TCSETATTR_FLUSH:
|
||||
//Changing things at runtime unsupported, so do nothing, but don't
|
||||
//return error as console_device.h implements some attribute changes
|
||||
return 0;
|
||||
default:
|
||||
return -ENOTTY; //Means the operation does not apply to this descriptor
|
||||
}
|
||||
}
|
||||
|
||||
void ATSAMSerial::IRQhandleInterrupt()
|
||||
{
|
||||
bool wake=false;
|
||||
unsigned int status=port->US_CSR;
|
||||
|
||||
if(status & US_CSR_RXRDY)
|
||||
{
|
||||
wake=true;
|
||||
//Always read the char (resets flags), but put it in the queue only if
|
||||
//no framing error
|
||||
char c=port->US_RHR;
|
||||
if((status & US_CSR_FRAME) == 0)
|
||||
{
|
||||
if(rxQueue.tryPut(c & 0xff)==false) /*fifo overflow*/;
|
||||
idle=false;
|
||||
}
|
||||
}
|
||||
if(status & US_CSR_TIMEOUT)
|
||||
{
|
||||
wake=true;
|
||||
port->US_CR=US_CR_STTTO;
|
||||
idle=true;
|
||||
}
|
||||
|
||||
if(wake && rxWaiting)
|
||||
{
|
||||
rxWaiting->IRQwakeup();
|
||||
if(rxWaiting->IRQgetPriority()>
|
||||
Thread::IRQgetCurrentThread()->IRQgetPriority())
|
||||
Scheduler::IRQfindNextThread();
|
||||
rxWaiting=0;
|
||||
}
|
||||
}
|
||||
|
||||
ATSAMSerial::~ATSAMSerial()
|
||||
{
|
||||
waitSerialTxFifoEmpty();
|
||||
|
||||
port->US_CR = US_CR_TXDIS | US_CR_RXDIS;
|
||||
port->US_IDR = US_IDR_RXRDY | US_IDR_TIMEOUT;
|
||||
|
||||
InterruptDisableLock dLock;
|
||||
ports[portId]=nullptr;
|
||||
|
||||
//TODO: USART2 hardcoded
|
||||
NVIC_DisableIRQ(USART2_IRQn);
|
||||
NVIC_ClearPendingIRQ(USART2_IRQn);
|
||||
PM->PM_UNLOCK=0xaa<<24 | PM_PBAMASK_OFFSET;
|
||||
PM->PM_PBAMASK &= ~PM_PBAMASK_USART2;
|
||||
}
|
||||
|
||||
void ATSAMSerial::waitSerialTxFifoEmpty()
|
||||
{
|
||||
while((port->US_CSR & US_CSR_TXEMPTY) == 0) ;
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2020 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "filesystem/console/console_device.h"
|
||||
#include "kernel/sync.h"
|
||||
#include "kernel/queue.h"
|
||||
#include "board_settings.h"
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* Serial port class for ATSAM4L microcontrollers.
|
||||
*
|
||||
* This is a simple implementation with the following limitations:
|
||||
* -only supports USART2
|
||||
* -no DMA, TX uses polling, while RX is interrupt-based
|
||||
*
|
||||
* Classes of this type are reference counted, must be allocated on the heap
|
||||
* and managed through intrusive_ref_ptr<FileBase>
|
||||
*/
|
||||
class ATSAMSerial : public Device
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor, initializes the serial port.
|
||||
* Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when
|
||||
* attempting to construct multiple objects with the same id. That is,
|
||||
* it is possible to instantiate only one instance of this class for each
|
||||
* hardware USART.
|
||||
* \param id a number to select the USART
|
||||
* \param baudrate serial port baudrate
|
||||
*/
|
||||
ATSAMSerial(int id, int baudrate);
|
||||
|
||||
/**
|
||||
* Read a block of data
|
||||
* \param buffer buffer where read data will be stored
|
||||
* \param size buffer size
|
||||
* \param where where to read from
|
||||
* \return number of bytes read or a negative number on failure. Note that
|
||||
* it is normal for this function to return less character than the amount
|
||||
* asked
|
||||
*/
|
||||
ssize_t readBlock(void *buffer, size_t size, off_t where);
|
||||
|
||||
/**
|
||||
* Write a block of data
|
||||
* \param buffer buffer where take data to write
|
||||
* \param size buffer size
|
||||
* \param where where to write to
|
||||
* \return number of bytes written or a negative number on failure
|
||||
*/
|
||||
ssize_t writeBlock(const void *buffer, size_t size, off_t where);
|
||||
|
||||
/**
|
||||
* Write a string.
|
||||
* An extension to the Device interface that adds a new member function,
|
||||
* which is used by the kernel on console devices to write debug information
|
||||
* before the kernel is started or in case of serious errors, right before
|
||||
* rebooting.
|
||||
* Can ONLY be called when the kernel is not yet started, paused or within
|
||||
* an interrupt. This default implementation ignores writes.
|
||||
* \param str the string to write. The string must be NUL terminated.
|
||||
*/
|
||||
void IRQwrite(const char *str);
|
||||
|
||||
/**
|
||||
* Performs device-specific operations
|
||||
* \param cmd specifies the operation to perform
|
||||
* \param arg optional argument that some operation require
|
||||
* \return the exact return value depends on CMD, -1 is returned on error
|
||||
*/
|
||||
int ioctl(int cmd, void *arg);
|
||||
|
||||
/**
|
||||
* \internal the serial port interrupts call this member function.
|
||||
* Never call this from user code.
|
||||
*/
|
||||
void IRQhandleInterrupt();
|
||||
|
||||
/**
|
||||
* \return port id, 0 for USART0, ...
|
||||
*/
|
||||
int getId() const { return portId; }
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~ATSAMSerial();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Wait until all characters have been written to the serial port
|
||||
*/
|
||||
void waitSerialTxFifoEmpty();
|
||||
|
||||
FastMutex txMutex; ///< Mutex locked during transmission
|
||||
FastMutex rxMutex; ///< Mutex locked during reception
|
||||
|
||||
DynUnsyncQueue<char> rxQueue; ///< Receiving queue
|
||||
static const unsigned int rxQueueMin=1; ///< Minimum queue size
|
||||
Thread *rxWaiting; ///< Thread waiting for rx, or 0
|
||||
bool idle; ///< Receiver idle
|
||||
|
||||
Usart *port; ///< Pointer to USART peripheral
|
||||
|
||||
const unsigned char portId; ///< 0 for USART0, ...
|
||||
};
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,279 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2015 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include <limits>
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <termios.h>
|
||||
#include "serial_efm32.h"
|
||||
#include "kernel/sync.h"
|
||||
#include "kernel/scheduler/scheduler.h"
|
||||
#include "interfaces/portability.h"
|
||||
#include "interfaces/gpio.h"
|
||||
#include "filesystem/ioctl.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace miosix;
|
||||
|
||||
static const int numPorts=1; //Supporting only USART0 for now
|
||||
|
||||
//Hope GPIO mapping doesn't change among EFM32 microcontrollers, otherwise
|
||||
//we'll fix that with #ifdefs
|
||||
typedef Gpio<GPIOE_BASE,10> u0tx;
|
||||
typedef Gpio<GPIOE_BASE,11> u0rx;
|
||||
|
||||
/// Pointer to serial port classes to let interrupts access the classes
|
||||
static EFM32Serial *ports[numPorts]={0};
|
||||
|
||||
/**
|
||||
* \internal interrupt routine for usart0 rx actual implementation
|
||||
*/
|
||||
void __attribute__((noinline)) usart0rxIrqImpl()
|
||||
{
|
||||
if(ports[0]) ports[0]->IRQhandleInterrupt();
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal interrupt routine for usart0 rx
|
||||
*/
|
||||
void __attribute__((naked)) USART0_RX_IRQHandler()
|
||||
{
|
||||
saveContext();
|
||||
asm volatile("bl _Z15usart0rxIrqImplv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
namespace miosix {
|
||||
|
||||
//
|
||||
// class EFM32Serial
|
||||
//
|
||||
|
||||
// A note on the baudrate/500: the buffer is selected so as to withstand
|
||||
// 20ms of full data rate. In the 8N1 format one char is made of 10 bits.
|
||||
// So (baudrate/10)*0.02=baudrate/500
|
||||
EFM32Serial::EFM32Serial(int id, int baudrate)
|
||||
: Device(Device::TTY), rxQueue(rxQueueMin+baudrate/500), rxWaiting(0),
|
||||
portId(id), baudrate(baudrate)
|
||||
{
|
||||
if(id<0 || id>=numPorts || ports[id]!=0) errorHandler(UNEXPECTED);
|
||||
|
||||
{
|
||||
InterruptDisableLock dLock;
|
||||
ports[id]=this;
|
||||
|
||||
switch(id)
|
||||
{
|
||||
case 0:
|
||||
u0tx::mode(Mode::OUTPUT_HIGH);
|
||||
u0rx::mode(Mode::INPUT_PULL_UP_FILTER);
|
||||
|
||||
CMU->HFPERCLKEN0|=CMU_HFPERCLKEN0_USART0;
|
||||
port=USART0;
|
||||
|
||||
port->IEN=USART_IEN_RXDATAV;
|
||||
port->IRCTRL=0; //USART0 also has IrDA mode
|
||||
|
||||
NVIC_SetPriority(USART0_RX_IRQn,15);//Lowest priority for serial
|
||||
NVIC_EnableIRQ(USART0_RX_IRQn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
port->CTRL=USART_CTRL_TXBIL_HALFFULL; //Use the buffer more efficiently
|
||||
port->FRAME=USART_FRAME_STOPBITS_ONE
|
||||
| USART_FRAME_PARITY_NONE
|
||||
| USART_FRAME_DATABITS_EIGHT;
|
||||
port->TRIGCTRL=0;
|
||||
port->INPUT=0;
|
||||
port->I2SCTRL=0;
|
||||
port->ROUTE=USART_ROUTE_LOCATION_LOC0 //Default location
|
||||
| USART_ROUTE_TXPEN //Enable TX pin
|
||||
| USART_ROUTE_RXPEN; //Enable RX pin
|
||||
unsigned int periphClock=SystemHFClockGet()/(1<<(CMU->HFPERCLKDIV & 0xf));
|
||||
//The number we need is periphClock/baudrate/16-1, but with two bits of
|
||||
//fractional part. We divide by 2 instead of 16 to have 3 bit of fractional
|
||||
//part. We use the additional fractional bit to add one to round towards
|
||||
//the nearest. This gets us a little more precision. Then we subtract 8
|
||||
//which is one with three fractional bits. Then we shift to fit the integer
|
||||
//part in bits 20:8 and the fractional part in bits 7:6, masking away the
|
||||
//third fractional bit. Easy, isn't it? Not quite.
|
||||
port->CLKDIV=((((periphClock/baudrate/2)+1)-8)<<5) & 0x1fffc0;
|
||||
port->CMD=USART_CMD_CLEARRX
|
||||
| USART_CMD_CLEARTX
|
||||
| USART_CMD_TXTRIDIS
|
||||
| USART_CMD_RXBLOCKDIS
|
||||
| USART_CMD_MASTEREN
|
||||
| USART_CMD_TXEN
|
||||
| USART_CMD_RXEN;
|
||||
}
|
||||
|
||||
ssize_t EFM32Serial::readBlock(void *buffer, size_t size, off_t where)
|
||||
{
|
||||
Lock<FastMutex> l(rxMutex);
|
||||
char *buf=reinterpret_cast<char*>(buffer);
|
||||
size_t result=0;
|
||||
FastInterruptDisableLock dLock;
|
||||
for(;;)
|
||||
{
|
||||
//Try to get data from the queue
|
||||
for(;result<size;result++)
|
||||
{
|
||||
if(rxQueue.tryGet(buf[result])==false) break;
|
||||
//This is here just not to keep IRQ disabled for the whole loop
|
||||
FastInterruptEnableLock eLock(dLock);
|
||||
}
|
||||
//Don't block if we have at least one char
|
||||
//This is required for \n detection
|
||||
if(result>0) break;
|
||||
//Wait for data in the queue
|
||||
do {
|
||||
rxWaiting=Thread::IRQgetCurrentThread();
|
||||
Thread::IRQwait();
|
||||
{
|
||||
FastInterruptEnableLock eLock(dLock);
|
||||
Thread::yield();
|
||||
}
|
||||
} while(rxWaiting);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t EFM32Serial::writeBlock(const void *buffer, size_t size, off_t where)
|
||||
{
|
||||
Lock<FastMutex> l(txMutex);
|
||||
const char *buf=reinterpret_cast<const char*>(buffer);
|
||||
for(size_t i=0;i<size;i++)
|
||||
{
|
||||
while((port->STATUS & USART_STATUS_TXBL)==0) ;
|
||||
port->TXDATA=*buf++;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void EFM32Serial::IRQwrite(const char *str)
|
||||
{
|
||||
// We can reach here also with only kernel paused, so make sure
|
||||
// interrupts are disabled.
|
||||
bool interrupts=areInterruptsEnabled();
|
||||
if(interrupts) fastDisableInterrupts();
|
||||
while(*str)
|
||||
{
|
||||
while((port->STATUS & USART_STATUS_TXBL)==0) ;
|
||||
port->TXDATA=*str++;
|
||||
}
|
||||
waitSerialTxFifoEmpty();
|
||||
if(interrupts) fastEnableInterrupts();
|
||||
}
|
||||
|
||||
int EFM32Serial::ioctl(int cmd, void* arg)
|
||||
{
|
||||
if(reinterpret_cast<unsigned>(arg) & 0b11) return -EFAULT; //Unaligned
|
||||
termios *t=reinterpret_cast<termios*>(arg);
|
||||
switch(cmd)
|
||||
{
|
||||
case IOCTL_SYNC:
|
||||
waitSerialTxFifoEmpty();
|
||||
return 0;
|
||||
case IOCTL_TCGETATTR:
|
||||
t->c_iflag=IGNBRK | IGNPAR;
|
||||
t->c_oflag=0;
|
||||
t->c_cflag=CS8;
|
||||
t->c_lflag=0;
|
||||
return 0;
|
||||
case IOCTL_TCSETATTR_NOW:
|
||||
case IOCTL_TCSETATTR_DRAIN:
|
||||
case IOCTL_TCSETATTR_FLUSH:
|
||||
//Changing things at runtime unsupported, so do nothing, but don't
|
||||
//return error as console_device.h implements some attribute changes
|
||||
return 0;
|
||||
default:
|
||||
return -ENOTTY; //Means the operation does not apply to this descriptor
|
||||
}
|
||||
}
|
||||
|
||||
void EFM32Serial::IRQhandleInterrupt()
|
||||
{
|
||||
bool atLeastOne=false;
|
||||
while(port->STATUS & USART_STATUS_RXDATAV)
|
||||
{
|
||||
unsigned int c=port->RXDATAX;
|
||||
if((c & (USART_RXDATAX_FERR | USART_RXDATAX_PERR))==0)
|
||||
{
|
||||
atLeastOne=true;
|
||||
if(rxQueue.tryPut(c & 0xff)==false) /*fifo overflow*/;
|
||||
}
|
||||
}
|
||||
if(atLeastOne && rxWaiting)
|
||||
{
|
||||
rxWaiting->IRQwakeup();
|
||||
if(rxWaiting->IRQgetPriority()>
|
||||
Thread::IRQgetCurrentThread()->IRQgetPriority())
|
||||
Scheduler::IRQfindNextThread();
|
||||
rxWaiting=0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EFM32Serial::~EFM32Serial()
|
||||
{
|
||||
waitSerialTxFifoEmpty();
|
||||
|
||||
port->CMD=USART_CMD_TXDIS
|
||||
| USART_CMD_RXDIS;
|
||||
port->ROUTE=0;
|
||||
|
||||
InterruptDisableLock dLock;
|
||||
ports[portId]=0;
|
||||
switch(portId)
|
||||
{
|
||||
case 0:
|
||||
NVIC_DisableIRQ(USART0_RX_IRQn);
|
||||
NVIC_ClearPendingIRQ(USART0_RX_IRQn);
|
||||
|
||||
u0tx::mode(Mode::DISABLED);
|
||||
u0rx::mode(Mode::DISABLED);
|
||||
|
||||
CMU->HFPERCLKEN0 &= ~CMU_HFPERCLKEN0_USART0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EFM32Serial::waitSerialTxFifoEmpty()
|
||||
{
|
||||
//The documentation states that the TXC bit goes to one as soon as a
|
||||
//transmission is complete. However, this bit is initially zero, so if we
|
||||
//call this function before transmitting, the loop will wait forever. As a
|
||||
//solution, add a timeout having as value the time needed to send three
|
||||
//bytes (the current one in the shift register plus the two in the buffer).
|
||||
//The +1 is to produce rounding on the safe side, the 30 is the time to send
|
||||
//three char through the port, including start and stop bits.
|
||||
int timeout=(SystemCoreClock/baudrate+1)*30;
|
||||
while(timeout-->0 && (port->STATUS & USART_STATUS_TXC)==0) ;
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2015 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef SERIAL_EFM32_H
|
||||
#define SERIAL_EFM32_H
|
||||
|
||||
#include "filesystem/console/console_device.h"
|
||||
#include "kernel/sync.h"
|
||||
#include "kernel/queue.h"
|
||||
#include "board_settings.h"
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* Serial port class for EFM32 microcontrollers.
|
||||
*
|
||||
* This is a simple implementation with the following limitations:
|
||||
* -only supports USART0
|
||||
* -no DMA, TX uses polling, while RX is interrupt-based
|
||||
*
|
||||
* Classes of this type are reference counted, must be allocated on the heap
|
||||
* and managed through intrusive_ref_ptr<FileBase>
|
||||
*/
|
||||
class EFM32Serial : public Device
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor, initializes the serial port.
|
||||
* Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when
|
||||
* attempting to construct multiple objects with the same id. That is,
|
||||
* it is possible to instantiate only one instance of this class for each
|
||||
* hardware USART.
|
||||
* \param id a number to select the USART
|
||||
* \param baudrate serial port baudrate
|
||||
*/
|
||||
EFM32Serial(int id, int baudrate);
|
||||
|
||||
/**
|
||||
* Read a block of data
|
||||
* \param buffer buffer where read data will be stored
|
||||
* \param size buffer size
|
||||
* \param where where to read from
|
||||
* \return number of bytes read or a negative number on failure. Note that
|
||||
* it is normal for this function to return less character than the amount
|
||||
* asked
|
||||
*/
|
||||
ssize_t readBlock(void *buffer, size_t size, off_t where);
|
||||
|
||||
/**
|
||||
* Write a block of data
|
||||
* \param buffer buffer where take data to write
|
||||
* \param size buffer size
|
||||
* \param where where to write to
|
||||
* \return number of bytes written or a negative number on failure
|
||||
*/
|
||||
ssize_t writeBlock(const void *buffer, size_t size, off_t where);
|
||||
|
||||
/**
|
||||
* Write a string.
|
||||
* An extension to the Device interface that adds a new member function,
|
||||
* which is used by the kernel on console devices to write debug information
|
||||
* before the kernel is started or in case of serious errors, right before
|
||||
* rebooting.
|
||||
* Can ONLY be called when the kernel is not yet started, paused or within
|
||||
* an interrupt. This default implementation ignores writes.
|
||||
* \param str the string to write. The string must be NUL terminated.
|
||||
*/
|
||||
void IRQwrite(const char *str);
|
||||
|
||||
/**
|
||||
* Performs device-specific operations
|
||||
* \param cmd specifies the operation to perform
|
||||
* \param arg optional argument that some operation require
|
||||
* \return the exact return value depends on CMD, -1 is returned on error
|
||||
*/
|
||||
int ioctl(int cmd, void *arg);
|
||||
|
||||
/**
|
||||
* \internal the serial port interrupts call this member function.
|
||||
* Never call this from user code.
|
||||
*/
|
||||
void IRQhandleInterrupt();
|
||||
|
||||
/**
|
||||
* \return port id, 0 for USART0, ...
|
||||
*/
|
||||
int getId() const { return portId; }
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~EFM32Serial();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Wait until all characters have been written to the serial port
|
||||
*/
|
||||
void waitSerialTxFifoEmpty();
|
||||
|
||||
FastMutex txMutex; ///< Mutex locked during transmission
|
||||
FastMutex rxMutex; ///< Mutex locked during reception
|
||||
|
||||
DynUnsyncQueue<char> rxQueue; ///< Receiving queue
|
||||
static const unsigned int rxQueueMin=1; ///< Minimum queue size
|
||||
Thread *rxWaiting; ///< Thread waiting for rx, or 0
|
||||
|
||||
USART_TypeDef *port; ///< Pointer to USART peripheral
|
||||
|
||||
const unsigned char portId; ///< 0 for USART0, ...
|
||||
int baudrate; ///< Baudrate
|
||||
};
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //SERIAL_EFM32_H
|
||||
|
|
@ -0,0 +1,304 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2008, 2009, 2010, 2011, 2012, 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <termios.h>
|
||||
#include "serial_lpc2000.h"
|
||||
#include "kernel/sync.h"
|
||||
#include "kernel/scheduler/scheduler.h"
|
||||
#include "interfaces/portability.h"
|
||||
#include "filesystem/ioctl.h"
|
||||
#include "LPC213x.h"
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/// Pointer to serial port classes to let interrupts access the classes
|
||||
static LPC2000Serial *ports[2]={0};
|
||||
|
||||
/**
|
||||
* \internal interrupt routine for usart0 actual implementation
|
||||
*/
|
||||
void __attribute__((noinline)) usart0irqImpl()
|
||||
{
|
||||
if(ports[0]) ports[0]->IRQhandleInterrupt();
|
||||
VICVectAddr=0xff;//Restart VIC
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal interrupt routine for usart0
|
||||
*/
|
||||
void __attribute__((interrupt("IRQ"),naked)) usart0irq()
|
||||
{
|
||||
saveContextFromIrq();
|
||||
asm volatile("bl _ZN6miosix13usart0irqImplEv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal interrupt routine for usart1 actual implementation
|
||||
*/
|
||||
void __attribute__((noinline)) usart1irqImpl()
|
||||
{
|
||||
if(ports[1]) ports[1]->IRQhandleInterrupt();
|
||||
VICVectAddr=0xff;//Restart VIC
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal interrupt routine for usart1
|
||||
*/
|
||||
void __attribute__ ((interrupt("IRQ"),naked)) usart1irq()
|
||||
{
|
||||
saveContextFromIrq();
|
||||
asm volatile("bl _ZN6miosix13usart1irqImplEv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
//
|
||||
// class LPC2000Serial
|
||||
//
|
||||
|
||||
// A note on the baudrate/500: the buffer is selected so as to withstand
|
||||
// 20ms of full data rate. In the 8N1 format one char is made of 10 bits.
|
||||
// So (baudrate/10)*0.02=baudrate/500
|
||||
LPC2000Serial::LPC2000Serial(int id, int baudrate) : Device(Device::TTY),
|
||||
rxQueue(hwRxQueueLen+baudrate/500), rxWaiting(0), idle(true)
|
||||
{
|
||||
InterruptDisableLock dLock;
|
||||
if(id<0 || id>1 || ports[id]!=0) errorHandler(UNEXPECTED);
|
||||
ports[id]=this;
|
||||
if(id==0)
|
||||
{
|
||||
serial=reinterpret_cast<Usart16550*>(0xe000c000);
|
||||
PCONP|=(1<<3);//Enable UART0 peripheral
|
||||
PINSEL0&=~(0xf);//Clear bits 0 to 3 of PINSEL0
|
||||
PINSEL0|=0x5;//Set p0.0 as TXD and p0.1 as RXD
|
||||
//Init VIC
|
||||
VICSoftIntClr=(1<<6);//Clear uart0 interrupt flag (if previously set)
|
||||
VICIntSelect&=~(1<<6);//uart0=IRQ
|
||||
VICIntEnable=(1<<6);//uart0 interrupt ON
|
||||
VICVectCntl2=0x20 | 0x6;//Slot 2 of VIC used by uart0
|
||||
VICVectAddr2=reinterpret_cast<unsigned int>(&usart0irq);
|
||||
} else {
|
||||
serial=reinterpret_cast<Usart16550*>(0xe0010000);
|
||||
PCONP|=(1<<4);//Enable UART1 peripheral
|
||||
PINSEL0&=~(0xf0000);//Clear bits 16 to 19 of PINSEL0
|
||||
PINSEL0|=0x50000;//Set p0.8 as TXD and p0.9 as RXD
|
||||
//Init VIC
|
||||
VICSoftIntClr=(1<<7);//Clear uart1 interrupt flag (if previously set)
|
||||
VICIntSelect&=~(1<<7);//uart1=IRQ
|
||||
VICIntEnable=(1<<7);//uart1 interrupt ON
|
||||
VICVectCntl3=0x20 | 0x7;//Slot 3 of VIC used by uart1
|
||||
VICVectAddr3=reinterpret_cast<unsigned int>(&usart1irq);
|
||||
}
|
||||
serial->LCR=0x3;//DLAB disabled
|
||||
//0x07= fifo enabled, reset tx and rx hardware fifos
|
||||
//0x80= uart rx fifo trigger level 8 characters
|
||||
serial->FCR=0x07 | 0x80;
|
||||
serial->LCR=0x83;//8data bit, 1stop, no parity, DLAB enabled
|
||||
int div=TIMER_CLOCK/16/baudrate;
|
||||
serial->DLL=div & 0xff;
|
||||
serial->DLM=(div>>8) & 0xff;
|
||||
serial->LCR=0x3;//DLAB disabled
|
||||
serial->IER=0x7;//Enable RLS, RDA, CTI and THRE interrupts
|
||||
}
|
||||
|
||||
ssize_t LPC2000Serial::readBlock(void *buffer, size_t size, off_t where)
|
||||
{
|
||||
Lock<FastMutex> l(rxMutex);
|
||||
char *buf=reinterpret_cast<char*>(buffer);
|
||||
size_t result=0;
|
||||
FastInterruptDisableLock dLock;
|
||||
for(;;)
|
||||
{
|
||||
//Try to get data from the queue
|
||||
for(;result<size;result++)
|
||||
{
|
||||
if(rxQueue.tryGet(buf[result])==false) break;
|
||||
//This is here just not to keep IRQ disabled for the whole loop
|
||||
FastInterruptEnableLock eLock(dLock);
|
||||
}
|
||||
if(idle && result>0) break;
|
||||
if(result==size) break;
|
||||
//Wait for data in the queue
|
||||
do {
|
||||
rxWaiting=Thread::IRQgetCurrentThread();
|
||||
Thread::IRQwait();
|
||||
{
|
||||
FastInterruptEnableLock eLock(dLock);
|
||||
Thread::yield();
|
||||
}
|
||||
} while(rxWaiting);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t LPC2000Serial::writeBlock(const void *buffer, size_t size, off_t where)
|
||||
{
|
||||
Lock<FastMutex> l(txMutex);
|
||||
FastInterruptDisableLock dLock;
|
||||
size_t len=size;
|
||||
const char *buf=reinterpret_cast<const char*>(buffer);
|
||||
while(len>0)
|
||||
{
|
||||
//If no data in software and hardware queue
|
||||
if((serial->LSR & (1<<5)) && (txQueue.isEmpty()))
|
||||
{
|
||||
//Fill hardware queue first
|
||||
for(int i=0;i<hwTxQueueLen;i++)
|
||||
{
|
||||
serial->THR=*buf++;
|
||||
len--;
|
||||
if(len==0) break;
|
||||
}
|
||||
} else {
|
||||
if(txQueue.IRQput(*buf)==true)
|
||||
{
|
||||
buf++;
|
||||
len--;
|
||||
} else {
|
||||
FastInterruptEnableLock eLock(dLock);
|
||||
txQueue.waitUntilNotFull();
|
||||
}
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void LPC2000Serial::IRQwrite(const char *str)
|
||||
{
|
||||
while((*str)!='\0')
|
||||
{
|
||||
//Wait until the hardware fifo is ready to accept one char
|
||||
while(!(serial->LSR & (1<<5))) ; //wait
|
||||
serial->THR=*str++;
|
||||
}
|
||||
waitSerialTxFifoEmpty();
|
||||
}
|
||||
|
||||
int LPC2000Serial::ioctl(int cmd, void* arg)
|
||||
{
|
||||
if(reinterpret_cast<unsigned>(arg) & 0b11) return -EFAULT; //Unaligned
|
||||
termios *t=reinterpret_cast<termios*>(arg);
|
||||
switch(cmd)
|
||||
{
|
||||
case IOCTL_SYNC:
|
||||
waitSerialTxFifoEmpty();
|
||||
return 0;
|
||||
case IOCTL_TCGETATTR:
|
||||
t->c_iflag=IGNBRK | IGNPAR;
|
||||
t->c_oflag=0;
|
||||
t->c_cflag=CS8;
|
||||
t->c_lflag=0;
|
||||
return 0;
|
||||
case IOCTL_TCSETATTR_NOW:
|
||||
case IOCTL_TCSETATTR_DRAIN:
|
||||
case IOCTL_TCSETATTR_FLUSH:
|
||||
//Changing things at runtime unsupported, so do nothing, but don't
|
||||
//return error as console_device.h implements some attribute changes
|
||||
return 0;
|
||||
default:
|
||||
return -ENOTTY; //Means the operation does not apply to this descriptor
|
||||
}
|
||||
}
|
||||
|
||||
void LPC2000Serial::IRQhandleInterrupt()
|
||||
{
|
||||
char c;
|
||||
bool hppw=false;
|
||||
bool wakeup=false;
|
||||
switch(serial->IIR & 0xf)
|
||||
{
|
||||
case 0x6: //RLS
|
||||
c=serial->LSR;//Read LSR to clear interrupt
|
||||
c=serial->RBR;//Read RBR to discard char that caused error
|
||||
break;
|
||||
case 0x4: //RDA
|
||||
//Note: read one less char than HARDWARE_RX_QUEUE_LENGTH as the
|
||||
//CTI interrupt only occurs if there's at least one character in
|
||||
//the buffer, and we always want the CTI interrupt
|
||||
for(int i=0;i<hwRxQueueLen-1;i++)
|
||||
if(rxQueue.tryPut(serial->RBR)==false) /*fifo overflow*/;
|
||||
wakeup=true;
|
||||
idle=false;
|
||||
break;
|
||||
case 0xc: //CTI
|
||||
while(serial->LSR & (1<<0))
|
||||
if(rxQueue.tryPut(serial->RBR)==false) /*fifo overflow*/;
|
||||
wakeup=true;
|
||||
idle=true;
|
||||
break;
|
||||
case 0x2: //THRE
|
||||
for(int i=0;i<hwTxQueueLen;i++)
|
||||
{
|
||||
//If software queue empty, stop
|
||||
if(txQueue.IRQget(c,hppw)==false) break;
|
||||
serial->THR=c;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(wakeup && rxWaiting)
|
||||
{
|
||||
rxWaiting->IRQwakeup();
|
||||
if(rxWaiting->IRQgetPriority()>
|
||||
Thread::IRQgetCurrentThread()->IRQgetPriority()) hppw=true;
|
||||
rxWaiting=0;
|
||||
}
|
||||
if(hppw) Scheduler::IRQfindNextThread();
|
||||
}
|
||||
|
||||
LPC2000Serial::~LPC2000Serial()
|
||||
{
|
||||
waitSerialTxFifoEmpty();
|
||||
|
||||
InterruptDisableLock dLock;
|
||||
//Disable UART0
|
||||
serial->LCR=0;//DLAB disabled
|
||||
serial->FCR=0;
|
||||
|
||||
int id=0;
|
||||
if(ports[0]==this) id=0;
|
||||
else if(ports[1]==this) id=1;
|
||||
else errorHandler(UNEXPECTED);
|
||||
ports[id]=0;
|
||||
|
||||
if(id==0)
|
||||
{
|
||||
//Disable VIC
|
||||
VICIntEnClr=(1<<6);
|
||||
//Disable PIN
|
||||
PINSEL0&=~(0xf);//Clear bits 0 to 3 of PINSEL0
|
||||
} else {
|
||||
//Disable VIC
|
||||
VICIntEnClr=(1<<7);
|
||||
//Disable PIN
|
||||
PINSEL0&=~(0xf0000);//Clear bits 16 to 19 of PINSEL0
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2008, 2009, 2010, 2011, 2012, 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef SERIAL_LPC2000_H
|
||||
#define SERIAL_LPC2000_H
|
||||
|
||||
#include "filesystem/console/console_device.h"
|
||||
#include "kernel/sync.h"
|
||||
#include "kernel/queue.h"
|
||||
#include "interfaces/delays.h"
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* Serial port class for LPC2000 microcontrollers.
|
||||
*
|
||||
* Classes of this type are reference counted, must be allocated on the heap
|
||||
* and managed through intrusive_ref_ptr<FileBase>
|
||||
*/
|
||||
class LPC2000Serial : public Device
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor, initializes the serial port.
|
||||
* Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when
|
||||
* attempting to construct multiple objects with the same id. That is,
|
||||
* it is possible to instantiate only one instance of this class for each
|
||||
* hardware USART.
|
||||
* \param id 0=USART0, 1=USART1
|
||||
* \param baudrate serial port baudrate.
|
||||
*/
|
||||
LPC2000Serial(int id, int baudrate);
|
||||
|
||||
/**
|
||||
* Read a block of data
|
||||
* \param buffer buffer where read data will be stored
|
||||
* \param size buffer size
|
||||
* \param where where to read from
|
||||
* \return number of bytes read or a negative number on failure
|
||||
*/
|
||||
ssize_t readBlock(void *buffer, size_t size, off_t where);
|
||||
|
||||
/**
|
||||
* Write a block of data
|
||||
* \param buffer buffer where take data to write
|
||||
* \param size buffer size
|
||||
* \param where where to write to
|
||||
* \return number of bytes written or a negative number on failure
|
||||
*/
|
||||
ssize_t writeBlock(const void *buffer, size_t size, off_t where);
|
||||
|
||||
/**
|
||||
* Write a string.
|
||||
* An extension to the Device interface that adds a new member function,
|
||||
* which is used by the kernel on console devices to write debug information
|
||||
* before the kernel is started or in case of serious errors, right before
|
||||
* rebooting.
|
||||
* Can ONLY be called when the kernel is not yet started, paused or within
|
||||
* an interrupt. This default implementation ignores writes.
|
||||
* \param str the string to write. The string must be NUL terminated.
|
||||
*/
|
||||
void IRQwrite(const char *str);
|
||||
|
||||
/**
|
||||
* Performs device-specific operations
|
||||
* \param cmd specifies the operation to perform
|
||||
* \param arg optional argument that some operation require
|
||||
* \return the exact return value depends on CMD, -1 is returned on error
|
||||
*/
|
||||
int ioctl(int cmd, void *arg);
|
||||
|
||||
/**
|
||||
* \internal the serial port interrupts call this member function.
|
||||
* Never call this from user code.
|
||||
*/
|
||||
void IRQhandleInterrupt();
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~LPC2000Serial();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Wait until all characters have been written to the serial port
|
||||
*/
|
||||
void waitSerialTxFifoEmpty()
|
||||
{
|
||||
while((serial->LSR & (1<<6))==0) ;
|
||||
|
||||
//This delay has been added to fix a quirk on the Miosix board. When
|
||||
//writing a message to the console and rebooting, if the reboot happens
|
||||
//too fast with respect to the last character sent out of the serial
|
||||
//port, the FT232 gets confused and the last charcters are lost,
|
||||
//probably from the FT232 buffer. Using delayMs() to be callable from IRQ
|
||||
delayMs(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* The registers of the USART in struct form, to make a generic driver
|
||||
*/
|
||||
struct Usart16550
|
||||
{
|
||||
//Offset 0x00
|
||||
union {
|
||||
volatile unsigned char RBR;
|
||||
volatile unsigned char THR;
|
||||
volatile unsigned char DLL;
|
||||
};
|
||||
char padding0[3];
|
||||
//Offset 0x04
|
||||
union {
|
||||
volatile unsigned char IER;
|
||||
volatile unsigned char DLM;
|
||||
};
|
||||
char padding1[3];
|
||||
//Offset 0x08
|
||||
union {
|
||||
volatile unsigned char IIR;
|
||||
volatile unsigned char FCR;
|
||||
};
|
||||
char padding2[3];
|
||||
//Offset 0x0c
|
||||
volatile unsigned char LCR;
|
||||
char padding3[3];
|
||||
//Offset 0x10
|
||||
volatile unsigned char MCR; //Only USART1 has this
|
||||
char padding4[3];
|
||||
//Offset 0x14
|
||||
volatile unsigned char LSR;
|
||||
char padding5[7];
|
||||
//Offset 0x1c
|
||||
volatile unsigned char SCR;
|
||||
char padding6[19];
|
||||
//Offset 0x30
|
||||
volatile unsigned char TER;
|
||||
};
|
||||
|
||||
//Configure the software queue here
|
||||
static const int swTxQueue=32;///< Size of tx software queue
|
||||
|
||||
//The hardware queues cannot be modified, since their length is hardware-specific
|
||||
static const int hwTxQueueLen=16;
|
||||
static const int hwRxQueueLen=8;
|
||||
|
||||
FastMutex txMutex;///< Mutex used to guard the tx queue
|
||||
FastMutex rxMutex;///< Mutex used to guard the rx queue
|
||||
|
||||
Queue<char,swTxQueue> txQueue;///< Tx software queue
|
||||
DynUnsyncQueue<char> rxQueue;///< Rx software queue
|
||||
Thread *rxWaiting; ///< Thread waiting on rx queue
|
||||
bool idle; ///< Receiver idle
|
||||
|
||||
Usart16550 *serial; ///< Serial port registers
|
||||
};
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //SERIAL_LPC2000_H
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,295 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010-2018 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef SERIAL_STM32_H
|
||||
#define SERIAL_STM32_H
|
||||
|
||||
#include "filesystem/console/console_device.h"
|
||||
#include "kernel/sync.h"
|
||||
#include "kernel/queue.h"
|
||||
#include "interfaces/gpio.h"
|
||||
#include "board_settings.h"
|
||||
|
||||
#if defined(_ARCH_CORTEXM3_STM32) && defined(__ENABLE_XRAM)
|
||||
//Quirk: concurrent access to the FSMC from both core and DMA is broken in
|
||||
//the stm32f1, so disable DMA mode if XRAM is enabled.
|
||||
#undef SERIAL_1_DMA
|
||||
#undef SERIAL_2_DMA
|
||||
#undef SERIAL_3_DMA
|
||||
#endif
|
||||
|
||||
#if defined(SERIAL_1_DMA) || defined(SERIAL_2_DMA) || defined(SERIAL_3_DMA)
|
||||
#define SERIAL_DMA
|
||||
#endif
|
||||
|
||||
#if defined(SERIAL_DMA) && defined(_ARCH_CORTEXM0_STM32)
|
||||
#undef SERIAL_1_DMA
|
||||
#undef SERIAL_2_DMA
|
||||
#undef SERIAL_3_DMA
|
||||
#undef SERIAL_DMA
|
||||
#warning "DMA not yet implemented for STM32F0 family"
|
||||
#endif
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* Serial port class for stm32 microcontrollers.
|
||||
* Only supports USART1, USART2 and USART3
|
||||
* Additionally, USARTx can use DMA if SERIAL_x_DMA is defined in
|
||||
* board_settings.h, while the other serial use polling for transmission,
|
||||
* and interrupt for reception.
|
||||
*
|
||||
* Classes of this type are reference counted, must be allocated on the heap
|
||||
* and managed through intrusive_ref_ptr<FileBase>
|
||||
*/
|
||||
class STM32Serial : public Device
|
||||
{
|
||||
public:
|
||||
enum FlowCtrl
|
||||
{
|
||||
NOFLOWCTRL, ///< No hardware flow control
|
||||
RTSCTS ///< RTS/CTS hardware flow control
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor, initializes the serial port using the default pins, which
|
||||
* are:
|
||||
* USART1: tx=PA9 rx=PA10 cts=PA11 rts=PA12
|
||||
* USART2: tx=PA2 rx=PA3 cts=PA0 rts=PA1
|
||||
* USART3: tx=PB10 rx=PB11 cts=PB13 rts=PB14
|
||||
* If you board has a different mapping, use one of the other constructors.
|
||||
*
|
||||
* Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when
|
||||
* attempting to construct multiple objects with the same id. That is,
|
||||
* it is possible to instantiate only one instance of this class for each
|
||||
* hardware USART.
|
||||
* \param id a number 1 to 3 to select which USART
|
||||
* \param baudrate serial port baudrate
|
||||
* \param flowControl to enable hardware flow control on this port
|
||||
*/
|
||||
STM32Serial(int id, int baudrate, FlowCtrl flowControl=NOFLOWCTRL);
|
||||
|
||||
/**
|
||||
* Constructor, initializes the serial port using remapped pins and disables
|
||||
* flow control.
|
||||
*
|
||||
* NOTE: for stm32f2, f4, f7 and h7 you have to set the correct alternate
|
||||
* function to the pins in order to connect then to the USART peripheral
|
||||
* before passing them to this class.
|
||||
*
|
||||
* Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when
|
||||
* attempting to construct multiple objects with the same id. That is,
|
||||
* it is possible to instantiate only one instance of this class for each
|
||||
* hardware USART.
|
||||
* \param id a number 1 to 3 to select which USART
|
||||
* \param baudrate serial port baudrate
|
||||
* \param tx tx pin
|
||||
* \param rx rx pin
|
||||
*/
|
||||
STM32Serial(int id, int baudrate, miosix::GpioPin tx, miosix::GpioPin rx);
|
||||
|
||||
/**
|
||||
* Constructor, initializes the serial port using remapped pins and enables
|
||||
* flow control.
|
||||
*
|
||||
* NOTE: for stm32f2, f4, f7 and h7 you have to set the correct alternate
|
||||
* function to the pins in order to connect then to the USART peripheral
|
||||
* before passing them to this class.
|
||||
*
|
||||
* Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when
|
||||
* attempting to construct multiple objects with the same id. That is,
|
||||
* it is possible to instantiate only one instance of this class for each
|
||||
* hardware USART.
|
||||
* \param id a number 1 to 3 to select which USART
|
||||
* \param tx tx pin
|
||||
* \param rx rx pin
|
||||
* \param rts rts pin
|
||||
* \param cts cts pin
|
||||
*/
|
||||
STM32Serial(int id, int baudrate, miosix::GpioPin tx, miosix::GpioPin rx,
|
||||
miosix::GpioPin rts, miosix::GpioPin cts);
|
||||
|
||||
/**
|
||||
* Read a block of data
|
||||
* \param buffer buffer where read data will be stored
|
||||
* \param size buffer size
|
||||
* \param where where to read from
|
||||
* \return number of bytes read or a negative number on failure. Note that
|
||||
* it is normal for this function to return less character than the amount
|
||||
* asked
|
||||
*/
|
||||
ssize_t readBlock(void *buffer, size_t size, off_t where);
|
||||
|
||||
/**
|
||||
* Write a block of data
|
||||
* \param buffer buffer where take data to write
|
||||
* \param size buffer size
|
||||
* \param where where to write to
|
||||
* \return number of bytes written or a negative number on failure
|
||||
*/
|
||||
ssize_t writeBlock(const void *buffer, size_t size, off_t where);
|
||||
|
||||
/**
|
||||
* Write a string.
|
||||
* An extension to the Device interface that adds a new member function,
|
||||
* which is used by the kernel on console devices to write debug information
|
||||
* before the kernel is started or in case of serious errors, right before
|
||||
* rebooting.
|
||||
* Can ONLY be called when the kernel is not yet started, paused or within
|
||||
* an interrupt. This default implementation ignores writes.
|
||||
* \param str the string to write. The string must be NUL terminated.
|
||||
*/
|
||||
void IRQwrite(const char *str);
|
||||
|
||||
/**
|
||||
* Performs device-specific operations
|
||||
* \param cmd specifies the operation to perform
|
||||
* \param arg optional argument that some operation require
|
||||
* \return the exact return value depends on CMD, -1 is returned on error
|
||||
*/
|
||||
int ioctl(int cmd, void *arg);
|
||||
|
||||
/**
|
||||
* \internal the serial port interrupts call this member function.
|
||||
* Never call this from user code.
|
||||
*/
|
||||
void IRQhandleInterrupt();
|
||||
|
||||
#ifdef SERIAL_DMA
|
||||
/**
|
||||
* \internal the serial port DMA tx interrupts call this member function.
|
||||
* Never call this from user code.
|
||||
*/
|
||||
void IRQhandleDMAtx();
|
||||
|
||||
/**
|
||||
* \internal the serial port DMA rx interrupts call this member function.
|
||||
* Never call this from user code.
|
||||
*/
|
||||
void IRQhandleDMArx();
|
||||
#endif //SERIAL_DMA
|
||||
|
||||
/**
|
||||
* \return port id, 1 for USART1, 2 for USART2, ...
|
||||
*/
|
||||
int getId() const { return portId; }
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~STM32Serial();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Code common for all constructors
|
||||
*/
|
||||
void commonInit(int id, int baudrate, miosix::GpioPin tx, miosix::GpioPin rx,
|
||||
miosix::GpioPin rts, miosix::GpioPin cts);
|
||||
|
||||
#ifdef SERIAL_DMA
|
||||
/**
|
||||
* Wait until a pending DMA TX completes, if any
|
||||
*/
|
||||
void waitDmaTxCompletion();
|
||||
|
||||
/**
|
||||
* Write to the serial port using DMA. When the function returns, the DMA
|
||||
* transfer is still in progress.
|
||||
* \param buffer buffer to write
|
||||
* \param size size of buffer to write
|
||||
*/
|
||||
void writeDma(const char *buffer, size_t size);
|
||||
|
||||
/**
|
||||
* Read from DMA buffer and write data to queue
|
||||
*/
|
||||
void IRQreadDma();
|
||||
|
||||
/**
|
||||
* Start DMA read
|
||||
*/
|
||||
void IRQdmaReadStart();
|
||||
|
||||
/**
|
||||
* Stop DMA read
|
||||
* \return the number of characters in rxBuffer
|
||||
*/
|
||||
int IRQdmaReadStop();
|
||||
#endif //SERIAL_DMA
|
||||
|
||||
/**
|
||||
* Wait until all characters have been written to the serial port.
|
||||
* Needs to be callable from interrupts disabled (it is used in IRQwrite)
|
||||
*/
|
||||
void waitSerialTxFifoEmpty()
|
||||
{
|
||||
#if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
|
||||
&& !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \
|
||||
&& !defined(_ARCH_CORTEXM4_STM32L4)
|
||||
while((port->SR & USART_SR_TC)==0) ;
|
||||
#else //_ARCH_CORTEXM7_STM32F7/H7
|
||||
while((port->ISR & USART_ISR_TC)==0) ;
|
||||
#endif //_ARCH_CORTEXM7_STM32F7/H7
|
||||
}
|
||||
|
||||
FastMutex txMutex; ///< Mutex locked during transmission
|
||||
FastMutex rxMutex; ///< Mutex locked during reception
|
||||
|
||||
DynUnsyncQueue<char> rxQueue; ///< Receiving queue
|
||||
static const unsigned int rxQueueMin=16; ///< Minimum queue size
|
||||
Thread *rxWaiting=0; ///< Thread waiting for rx, or 0
|
||||
|
||||
USART_TypeDef *port; ///< Pointer to USART peripheral
|
||||
#ifdef SERIAL_DMA
|
||||
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \
|
||||
|| defined(_ARCH_CORTEXM4_STM32L4)
|
||||
DMA_Channel_TypeDef *dmaTx; ///< Pointer to DMA TX peripheral
|
||||
DMA_Channel_TypeDef *dmaRx; ///< Pointer to DMA RX peripheral
|
||||
#else //_ARCH_CORTEXM3_STM32 and _ARCH_CORTEXM4_STM32F3
|
||||
DMA_Stream_TypeDef *dmaTx; ///< Pointer to DMA TX peripheral
|
||||
DMA_Stream_TypeDef *dmaRx; ///< Pointer to DMA RX peripheral
|
||||
#endif //_ARCH_CORTEXM3_STM32 and _ARCH_CORTEXM4_STM32F3
|
||||
Thread *txWaiting; ///< Thread waiting for tx, or 0
|
||||
static const unsigned int txBufferSize=16; ///< Size of tx buffer, for tx speedup
|
||||
/// Tx buffer, for tx speedup. This buffer must not end up in the CCM of the
|
||||
/// STM32F4, as it is used to perform DMA operations. This is guaranteed by
|
||||
/// the fact that this class must be allocated on the heap as it derives
|
||||
/// from Device, and the Miosix linker scripts never put the heap in CCM
|
||||
char txBuffer[txBufferSize];
|
||||
/// This buffer emulates the behaviour of a 16550. It is filled using DMA
|
||||
/// and an interrupt is fired as soon as it is half full
|
||||
char rxBuffer[rxQueueMin];
|
||||
bool dmaTxInProgress; ///< True if a DMA tx is in progress
|
||||
#endif //SERIAL_DMA
|
||||
bool idle=true; ///< Receiver idle
|
||||
const bool flowControl; ///< True if flow control GPIOs enabled
|
||||
const unsigned char portId; ///< 1 for USART1, 2 for USART2, ...
|
||||
};
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //SERIAL_STM32_H
|
||||
|
|
@ -0,0 +1,356 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include "servo_stm32.h"
|
||||
#include "kernel/scheduler/scheduler.h"
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cmath>
|
||||
|
||||
using namespace std;
|
||||
using namespace miosix;
|
||||
|
||||
typedef Gpio<GPIOB_BASE,6> servo1out;
|
||||
typedef Gpio<GPIOB_BASE,7> servo2out;
|
||||
typedef Gpio<GPIOB_BASE,8> servo3out;
|
||||
typedef Gpio<GPIOB_BASE,9> servo4out;
|
||||
static Thread *waiting=0;
|
||||
|
||||
/**
|
||||
* Timer 4 interrupt handler actual implementation
|
||||
*/
|
||||
void __attribute__((used)) tim4impl()
|
||||
{
|
||||
TIM4->SR=0; //Clear interrupt flag
|
||||
if(waiting==0) return;
|
||||
waiting->IRQwakeup();
|
||||
if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
|
||||
Scheduler::IRQfindNextThread();
|
||||
waiting=0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Timer 4 interrupt handler
|
||||
*/
|
||||
void __attribute__((naked)) TIM4_IRQHandler()
|
||||
{
|
||||
saveContext();
|
||||
asm volatile("bl _Z8tim4implv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/* TODO: find a better place for this */
|
||||
unsigned int divideRoundToNearest(unsigned int a, unsigned int b)
|
||||
{
|
||||
const unsigned int quot=2*a/b;
|
||||
return quot/2 + (quot & 1);
|
||||
}
|
||||
|
||||
//
|
||||
// class SynchronizedServo
|
||||
//
|
||||
|
||||
SynchronizedServo& SynchronizedServo::instance()
|
||||
{
|
||||
static SynchronizedServo singleton;
|
||||
return singleton;
|
||||
}
|
||||
|
||||
void SynchronizedServo::enable(int channel)
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(status!=STOPPED) return; // If timer enabled ignore the call
|
||||
|
||||
{
|
||||
FastInterruptDisableLock dLock;
|
||||
// Calling the mode() function on a GPIO is subject to race conditions
|
||||
// between threads on the STM32, so we disable interrupts
|
||||
switch(channel)
|
||||
{
|
||||
case 0:
|
||||
TIM4->CCMR1 |= TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE;
|
||||
TIM4->CCER |= TIM_CCER_CC1E;
|
||||
#ifndef _ARCH_CORTEXM3_STM32 //Only stm32f2 and stm32f4 have it
|
||||
servo1out::alternateFunction(2);
|
||||
#endif //_ARCH_CORTEXM3_STM32
|
||||
servo1out::mode(Mode::ALTERNATE);
|
||||
break;
|
||||
case 1:
|
||||
TIM4->CCMR1 |= TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE;
|
||||
TIM4->CCER |= TIM_CCER_CC2E;
|
||||
#ifndef _ARCH_CORTEXM3_STM32 //Only stm32f2 and stm32f4 have it
|
||||
servo2out::alternateFunction(2);
|
||||
#endif //_ARCH_CORTEXM3_STM32
|
||||
servo2out::mode(Mode::ALTERNATE);
|
||||
break;
|
||||
case 2:
|
||||
TIM4->CCMR2 |= TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE;
|
||||
TIM4->CCER |= TIM_CCER_CC3E;
|
||||
#ifndef _ARCH_CORTEXM3_STM32 //Only stm32f2 and stm32f4 have it
|
||||
servo3out::alternateFunction(2);
|
||||
#endif //_ARCH_CORTEXM3_STM32
|
||||
servo3out::mode(Mode::ALTERNATE);
|
||||
break;
|
||||
case 3:
|
||||
TIM4->CCMR2 |= TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE;
|
||||
TIM4->CCER |= TIM_CCER_CC4E;
|
||||
#ifndef _ARCH_CORTEXM3_STM32 //Only stm32f2 and stm32f4 have it
|
||||
servo4out::alternateFunction(2);
|
||||
#endif //_ARCH_CORTEXM3_STM32
|
||||
servo4out::mode(Mode::ALTERNATE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SynchronizedServo::disable(int channel)
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(status!=STOPPED) return; // If timer enabled ignore the call
|
||||
|
||||
{
|
||||
FastInterruptDisableLock dLock;
|
||||
// Calling the mode() function on a GPIO is subject to race conditions
|
||||
// between threads on the STM32, so we disable interrupts
|
||||
switch(channel)
|
||||
{
|
||||
case 0:
|
||||
servo1out::mode(Mode::INPUT);
|
||||
TIM4->CCER &= ~TIM_CCER_CC1E;
|
||||
break;
|
||||
case 1:
|
||||
servo2out::mode(Mode::INPUT);
|
||||
TIM4->CCER &= ~TIM_CCER_CC2E;
|
||||
break;
|
||||
case 2:
|
||||
servo3out::mode(Mode::INPUT);
|
||||
TIM4->CCER &= ~TIM_CCER_CC3E;
|
||||
break;
|
||||
case 3:
|
||||
servo4out::mode(Mode::INPUT);
|
||||
TIM4->CCER &= ~TIM_CCER_CC4E;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SynchronizedServo::setPosition(int channel, float value)
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(status!=STARTED) return; // If timer disabled ignore the call
|
||||
|
||||
value=min(1.0f,max(0.0f,value));
|
||||
switch(channel)
|
||||
{
|
||||
case 0:
|
||||
TIM4->CCR1=a*value+b;
|
||||
break;
|
||||
case 1:
|
||||
TIM4->CCR2=a*value+b;
|
||||
break;
|
||||
case 2:
|
||||
TIM4->CCR3=a*value+b;
|
||||
break;
|
||||
case 3:
|
||||
TIM4->CCR4=a*value+b;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float SynchronizedServo::getPosition(int channel)
|
||||
{
|
||||
switch(channel)
|
||||
{
|
||||
case 0:
|
||||
return TIM4->CCR1==0 ? NAN : TIM4->CCR1/a-b;
|
||||
case 1:
|
||||
return TIM4->CCR2==0 ? NAN : TIM4->CCR2/a-b;
|
||||
case 2:
|
||||
return TIM4->CCR3==0 ? NAN : TIM4->CCR3/a-b;
|
||||
case 3:
|
||||
return TIM4->CCR4==0 ? NAN : TIM4->CCR4/a-b;
|
||||
default:
|
||||
return NAN;
|
||||
}
|
||||
}
|
||||
|
||||
void SynchronizedServo::start()
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(status!=STOPPED) return; // If timer enabled ignore the call
|
||||
|
||||
// While status is starting neither memeber function callable with timer
|
||||
// started nor stopped are allowed
|
||||
status=STARTED;
|
||||
TIM4->CNT=0;
|
||||
TIM4->EGR=TIM_EGR_UG;
|
||||
TIM4->CR1=TIM_CR1_CEN;
|
||||
}
|
||||
|
||||
void SynchronizedServo::stop()
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(status!=STARTED) return; // If timer disabled ignore the call
|
||||
|
||||
status=STOPPED;
|
||||
// Stopping the timer is a bit difficult because we don't want to
|
||||
// accidentally create glitches on the outputs which may turn the servos
|
||||
// to random positions. So, we set all PWM outputs to 0, then wait until the
|
||||
// end of the period, and then disable the timer
|
||||
TIM4->CCR1=0;
|
||||
TIM4->CCR2=0;
|
||||
TIM4->CCR3=0;
|
||||
TIM4->CCR4=0;
|
||||
{
|
||||
FastInterruptDisableLock dLock;
|
||||
// Wakeup an eventual thread waiting on waitForCycleBegin()
|
||||
if(waiting) waiting->IRQwakeup();
|
||||
IRQwaitForTimerOverflow(dLock);
|
||||
}
|
||||
TIM4->CR1=0;
|
||||
}
|
||||
|
||||
bool SynchronizedServo::waitForCycleBegin()
|
||||
{
|
||||
// No need to lock the mutex because disabling interrupts is enough to avoid
|
||||
// race conditions. Also, locking the mutex here would prevent other threads
|
||||
// from calling other member functions of this class
|
||||
FastInterruptDisableLock dLock;
|
||||
if(status!=STARTED) return true;
|
||||
IRQwaitForTimerOverflow(dLock);
|
||||
return status!=STARTED;
|
||||
}
|
||||
|
||||
void SynchronizedServo::setFrequency(unsigned int frequency)
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(status!=STOPPED) return; // If timer enabled ignore the call
|
||||
|
||||
TIM4->PSC=divideRoundToNearest(getPrescalerInputFrequency(),frequency*65536)-1;
|
||||
precomputeCoefficients();
|
||||
}
|
||||
|
||||
float SynchronizedServo::getFrequency() const
|
||||
{
|
||||
float prescalerFreq=getPrescalerInputFrequency();
|
||||
return prescalerFreq/((TIM4->PSC+1)*65536);
|
||||
}
|
||||
|
||||
void SynchronizedServo::setMinPulseWidth(float minPulse)
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(status!=STOPPED) return; // If timer enabled ignore the call
|
||||
|
||||
minWidth=1e-6f*min(1300.0f,max(500.0f,minPulse));
|
||||
precomputeCoefficients();
|
||||
}
|
||||
|
||||
void SynchronizedServo::setMaxPulseWidth(float maxPulse)
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(status!=STOPPED) return; // If timer enabled ignore the call
|
||||
|
||||
maxWidth=1e-6f*min(2500.0f,max(1700.0f,maxPulse));
|
||||
precomputeCoefficients();
|
||||
}
|
||||
|
||||
SynchronizedServo::SynchronizedServo() : status(STOPPED)
|
||||
{
|
||||
{
|
||||
FastInterruptDisableLock dLock;
|
||||
// The RCC register should be written with interrupts disabled to
|
||||
// prevent race conditions with other threads.
|
||||
RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;
|
||||
RCC_SYNC();
|
||||
}
|
||||
|
||||
// Configure timer
|
||||
TIM4->CR1=0;
|
||||
TIM4->ARR=0xffff;
|
||||
TIM4->CCR1=0;
|
||||
TIM4->CCR2=0;
|
||||
TIM4->CCR3=0;
|
||||
TIM4->CCR4=0;
|
||||
// Configure interrupt on timer overflow
|
||||
TIM4->DIER=TIM_DIER_UIE;
|
||||
NVIC_SetPriority(TIM4_IRQn,13); //Low priority for timer IRQ
|
||||
NVIC_EnableIRQ(TIM4_IRQn);
|
||||
// Set default parameters
|
||||
setFrequency(50);
|
||||
setMinPulseWidth(1000);
|
||||
setMaxPulseWidth(2000);
|
||||
}
|
||||
|
||||
void SynchronizedServo::precomputeCoefficients()
|
||||
{
|
||||
float realPeriod=1.0f/getFrequency();
|
||||
a=65536.0f*(maxWidth-minWidth)/realPeriod;
|
||||
b=65536.0f*minWidth/realPeriod;
|
||||
}
|
||||
|
||||
unsigned int SynchronizedServo::getPrescalerInputFrequency()
|
||||
{
|
||||
// The global variable SystemCoreClock from ARM's CMSIS allows to know
|
||||
// the CPU frequency.
|
||||
unsigned int freq=SystemCoreClock;
|
||||
|
||||
//The position of the PPRE1 bit in RCC->CFGR is different in some stm32
|
||||
#ifdef _ARCH_CORTEXM3_STM32
|
||||
const unsigned int ppre1=8;
|
||||
#else //stm32f2 and f4
|
||||
const unsigned int ppre1=10;
|
||||
#endif
|
||||
|
||||
// The timer frequency may however be a submultiple of the CPU frequency,
|
||||
// due to the bus at whch the periheral is connected being slower. The
|
||||
// RCC->CFGR register tells us how slower the APB1 bus is running.
|
||||
// This formula takes into account that if the APB1 clock is divided by a
|
||||
// factor of two or greater, the timer is clocked at twice the bus
|
||||
// interface. After this, the freq variable contains the frequency in Hz
|
||||
// at which the timer prescaler is clocked.
|
||||
if(RCC->CFGR & RCC_CFGR_PPRE1_2) freq/=1<<((RCC->CFGR>>ppre1) & 0x3);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
void SynchronizedServo::IRQwaitForTimerOverflow(FastInterruptDisableLock& dLock)
|
||||
{
|
||||
waiting=Thread::IRQgetCurrentThread();
|
||||
do {
|
||||
Thread::IRQwait();
|
||||
{
|
||||
FastInterruptEnableLock eLock(dLock);
|
||||
Thread::yield();
|
||||
}
|
||||
} while(waiting);
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef SERVO_STM32_H
|
||||
#define SERVO_STM32_H
|
||||
|
||||
#include "miosix.h"
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* This class is designed to drive up to 4 servomotors. It generates
|
||||
* four square waves that are synchronized with respect to each other,
|
||||
* and allows the execution of code that is too synchronized with the
|
||||
* waveform generation, to ease the development of closed loop control
|
||||
* code using the servos as actuators.
|
||||
* This class can be safely accessed by multiple threads, except the
|
||||
* waitForCycleBegin() member function.
|
||||
*/
|
||||
class SynchronizedServo
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \return an instance of the SynchronizedServo class (singleton)
|
||||
* When first returned, the servo waveform generation is stopped. You must
|
||||
* enable at least one channel call start() and setPosition() before the
|
||||
* servo driving waveforms will be generated.
|
||||
*/
|
||||
static SynchronizedServo& instance();
|
||||
|
||||
/**
|
||||
* Enable a channel. Can only be called with the outputs stopped. Even if
|
||||
* the channel is enabled, when it is started it will not produce any
|
||||
* waveform until the first call to setPosition()
|
||||
* \param channel which channel to enable, must be between 0 and 3.
|
||||
*/
|
||||
void enable(int channel);
|
||||
|
||||
/**
|
||||
* Disable a channel. Can only be called with the outputs stopped.
|
||||
* \param channel which channel to disable, must be between 0 and 3.
|
||||
*/
|
||||
void disable(int channel);
|
||||
|
||||
/**
|
||||
* Set the servo position. Can only be called with the outputs started.
|
||||
* The written value takes effect at the next waveform generation cycle.
|
||||
* \param channel channel whose output need to be changed, between 0 and 3
|
||||
* \param value a float value from 0.0 to 1.0. Due to the limited timer
|
||||
* resolution, the actual set value is approximated. With the default values
|
||||
* of waveform frequency, min and max width the range between 0.0 and 1.0
|
||||
* is covered by around 3200 points.
|
||||
*/
|
||||
void setPosition(int channel, float value);
|
||||
|
||||
/**
|
||||
* \param channel channel whose output need to be read, between 0 and 3
|
||||
* \return the exact servo position, considering the approximations.
|
||||
* For this reason, the returned value may differ from the last call to
|
||||
* setPosition(). NAN is returned if no setValue() was called on the channel
|
||||
* since the last call to start()
|
||||
*/
|
||||
float getPosition(int channel);
|
||||
|
||||
/**
|
||||
* Start producing the output waveforms.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* Stop producing the output waveforms. If a thread is waiting in
|
||||
* waitForCycleBegin() it will be forecefully woken up.
|
||||
* As a side effect, the position of all the channels will be set to NAN.
|
||||
* When you restart the timer, you must call setPosition() on each enabled
|
||||
* channel before the channel will actually produce a waveform.
|
||||
* This function waits until the end of a waveform generation cycle in order
|
||||
* to not produce glitches. For this reason, it may take up to
|
||||
* 1/getFrequency() to complete, which with the default value of 50Hz is 20ms
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* Wait until the begin of a waveform generation cycle
|
||||
* \return false if a new cycle of waveform generation has begun, or true
|
||||
* if another thread called stop(). Only one thread at a time can call this
|
||||
* member function. If more than one thread calls this deadlock will occur
|
||||
* so don't do it!
|
||||
*/
|
||||
bool waitForCycleBegin();
|
||||
|
||||
/**
|
||||
* Set the frequency of the generated waveform. Can only be called
|
||||
* with the outputs stopped. The default is 50Hz. Note that due to prescaler
|
||||
* resolution, the actual frequency is set to the closest possible value.
|
||||
* To know the actual frequency, call getFrequency()
|
||||
* \param frequency desired servo update frequency in Hz
|
||||
* Must be between 10 and 100Hz
|
||||
*/
|
||||
void setFrequency(unsigned int frequency);
|
||||
|
||||
/**
|
||||
* \return the actual servo update frequency in Hz. Note that the
|
||||
* returned value is floating point as the returned frequency takes into
|
||||
* account approximations due to the prescaler resolution
|
||||
*/
|
||||
float getFrequency() const;
|
||||
|
||||
/**
|
||||
* Set the minimum width of the generated pulses, that is, the pulse width
|
||||
* generated when an output is set to zero with setPosition(x,0).
|
||||
* The default is 1000us. Can only be called with the outputs stopped.
|
||||
* \param minPulse minimum pulse width in microseconds.
|
||||
* Must be between 500 and 1300.
|
||||
*/
|
||||
void setMinPulseWidth(float minPulse);
|
||||
|
||||
/**
|
||||
* \return minimum pulse width in microseconds
|
||||
*/
|
||||
float getMinPulseWidth() const { return minWidth*1e6f; }
|
||||
|
||||
/**
|
||||
* Set the maximum width of the generated pulses, that is, the pulse width
|
||||
* generated when an output is set to one with setPosition(x,1).
|
||||
* The default is 2000us. Can only be called with the outputs stopped.
|
||||
* \param maxPulse maximum pulse width in microseconds.
|
||||
* Must be between 1700 and 2500.
|
||||
*/
|
||||
void setMaxPulseWidth(float maxPulse);
|
||||
|
||||
/**
|
||||
* \return maximum pulse width in microseconds
|
||||
*/
|
||||
float getMaxPulseWidth() const { return maxWidth*1e6f; }
|
||||
|
||||
private:
|
||||
SynchronizedServo(const SynchronizedServo&);
|
||||
SynchronizedServo& operator= (const SynchronizedServo&);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
SynchronizedServo();
|
||||
|
||||
/**
|
||||
* Precompute a and b coefficient to make setPosition() faster
|
||||
*/
|
||||
void precomputeCoefficients();
|
||||
|
||||
/**
|
||||
* \return the input frequency of the timer prescaler
|
||||
*/
|
||||
static unsigned int getPrescalerInputFrequency();
|
||||
|
||||
/**
|
||||
* Wait until the timer overflows from 0xffff to 0. Can only be called with
|
||||
* interrupts disabled
|
||||
*/
|
||||
static void IRQwaitForTimerOverflow(FastInterruptDisableLock& dLock);
|
||||
|
||||
float minWidth, maxWidth; ///< Minimum and maximum pulse widths
|
||||
float a, b; ///< Precomputed coefficients
|
||||
FastMutex mutex; ///< Mutex to protect from concurrent access
|
||||
enum {
|
||||
STOPPED, ///< Timer is stopped
|
||||
STARTED ///< Timer is started
|
||||
} status;
|
||||
};
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //SERVO_STM32_H
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include <stdexcept>
|
||||
#include "interfaces/delays.h"
|
||||
#include "stm32_hardware_rng.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace miosix {
|
||||
|
||||
//
|
||||
// class HardwareRng
|
||||
//
|
||||
|
||||
HardwareRng& HardwareRng::instance()
|
||||
{
|
||||
static HardwareRng singleton;
|
||||
return singleton;
|
||||
}
|
||||
|
||||
unsigned int HardwareRng::get()
|
||||
{
|
||||
miosix::Lock<miosix::FastMutex> l(mutex);
|
||||
PeripheralEnable pe;
|
||||
return getImpl();
|
||||
}
|
||||
|
||||
void HardwareRng::get(void* buf, unsigned int size)
|
||||
{
|
||||
unsigned char *buffer=reinterpret_cast<unsigned char*>(buf);
|
||||
Lock<FastMutex> l(mutex);
|
||||
PeripheralEnable pe;
|
||||
union Cast
|
||||
{
|
||||
unsigned int theInt;
|
||||
unsigned char theChar[4];
|
||||
};
|
||||
|
||||
if(reinterpret_cast<unsigned int>(buffer) & 0x3)
|
||||
{
|
||||
Cast cast;
|
||||
cast.theInt=getImpl();
|
||||
int i=0;
|
||||
while(reinterpret_cast<unsigned int>(buffer) & 0x3)
|
||||
{
|
||||
if(size--==0) return; //May happen if buffer is small and unaligned
|
||||
*buffer++=cast.theChar[i++];
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int *aligned=reinterpret_cast<unsigned int*>(buffer);
|
||||
for(unsigned int i=0;i<size/4;i++) aligned[i]=getImpl();
|
||||
|
||||
if(size & 0x3)
|
||||
{
|
||||
buffer+=(size/4)*4;
|
||||
Cast cast;
|
||||
cast.theInt=getImpl();
|
||||
int i=0;
|
||||
while(size & 0x3)
|
||||
{
|
||||
size--;
|
||||
*buffer++=cast.theChar[i++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int HardwareRng::getImpl()
|
||||
{
|
||||
#ifndef __NO_EXCEPTIONS
|
||||
for(int i=0;i<16;i++) //Try up to a reasonable # of times, then throw
|
||||
#else
|
||||
for(;;) //Can't return an error, keep retrying
|
||||
#endif
|
||||
{
|
||||
int timeout=1000000;
|
||||
unsigned int sr;
|
||||
while(--timeout>0) { sr=RNG->SR; if(sr & RNG_SR_DRDY) break; }
|
||||
if((sr & RNG_SR_SECS) || (sr & RNG_SR_CECS) || timeout<=0)
|
||||
{
|
||||
RNG->CR=0;
|
||||
delayUs(1);
|
||||
RNG->CR=RNG_CR_RNGEN;
|
||||
continue;
|
||||
}
|
||||
unsigned int result=RNG->DR;
|
||||
if(result==old) continue;
|
||||
old=result;
|
||||
return result;
|
||||
}
|
||||
#ifndef __NO_EXCEPTIONS
|
||||
throw runtime_error("RNG Fault detected");
|
||||
#endif //__NO_EXCEPTIONS
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef RNG_H
|
||||
#define RNG_H
|
||||
|
||||
#include "interfaces/arch_registers.h"
|
||||
#include "kernel/sync.h"
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* Class to access the hardware random number generator in Miosix
|
||||
* Works with the hardware RNG in stm32f2 and stm32f4
|
||||
*/
|
||||
class HardwareRng
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \return an instance of this class (singleton)
|
||||
*/
|
||||
static HardwareRng& instance();
|
||||
|
||||
/**
|
||||
* \return a 32 bit random number
|
||||
* \throws runtime_error if the self test is not passed
|
||||
*/
|
||||
unsigned int get();
|
||||
|
||||
/**
|
||||
* Fill a buffer with random data
|
||||
* \param buf buffer to be filled
|
||||
* \param size buffer size
|
||||
* \throws runtime_error if the self test is not passed
|
||||
*/
|
||||
void get(void *buf, unsigned int size);
|
||||
|
||||
private:
|
||||
HardwareRng(const HardwareRng&);
|
||||
HardwareRng& operator=(const HardwareRng&);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
HardwareRng() : old(0)
|
||||
{
|
||||
miosix::FastInterruptDisableLock dLock;
|
||||
RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN;
|
||||
RCC_SYNC();
|
||||
}
|
||||
|
||||
/**
|
||||
* \return a 32 bit random number
|
||||
* \throws runtime_error if the self test is not passed
|
||||
*/
|
||||
unsigned int getImpl();
|
||||
|
||||
/**
|
||||
* To save power, enable the peripheral ony when requested
|
||||
*/
|
||||
class PeripheralEnable
|
||||
{
|
||||
public:
|
||||
PeripheralEnable() { RNG->CR=RNG_CR_RNGEN; }
|
||||
~PeripheralEnable() { RNG->CR=0; }
|
||||
};
|
||||
|
||||
miosix::FastMutex mutex; ///< To protect against concurrent access
|
||||
unsigned int old; ///< Previously read value
|
||||
};
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //RNG_H
|
||||
|
|
@ -0,0 +1,322 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2017 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include "stm32_rtc.h"
|
||||
#include <miosix.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <kernel/scheduler/scheduler.h>
|
||||
|
||||
using namespace miosix;
|
||||
|
||||
namespace {
|
||||
|
||||
//
|
||||
// class ScopedCnf, RAII to allow writing to the RTC alarm and prescaler.
|
||||
// NOTE: datasheet says that after CNF bit has been cleared, no write is allowed
|
||||
// to *any* of the RTC registers, not just PRL,CNT,ALR until RTOFF goes to 1.
|
||||
// We do not wait until RTOFF is 1 in the destructor for performance reasons,
|
||||
// so the rest of the code must be careful.
|
||||
//
|
||||
class ScopedCnf
|
||||
{
|
||||
public:
|
||||
ScopedCnf()
|
||||
{
|
||||
while((RTC->CRL & RTC_CRL_RTOFF)==0) ;
|
||||
RTC->CRL=0b11111;
|
||||
}
|
||||
|
||||
~ScopedCnf()
|
||||
{
|
||||
RTC->CRL=0b01111;
|
||||
}
|
||||
};
|
||||
|
||||
long long swTime=0; //64bit software extension of current time in ticks
|
||||
long long irqTime=0; //64bit software extension of scheduled irq time in ticks
|
||||
Thread *waiting=nullptr; //waiting thread
|
||||
|
||||
/**
|
||||
* \return the hardware counter
|
||||
*/
|
||||
inline unsigned int IRQgetHwTick()
|
||||
{
|
||||
unsigned int h1=RTC->CNTH;
|
||||
unsigned int l1=RTC->CNTL;
|
||||
unsigned int h2=RTC->CNTH;
|
||||
if(h1==h2) return (h1<<16) | l1;
|
||||
return (h2<<16) | RTC->CNTL;
|
||||
}
|
||||
|
||||
/**
|
||||
* \return the time in ticks (hardware part + software extension to 64bits)
|
||||
*/
|
||||
long long IRQgetTick()
|
||||
{
|
||||
//Pending bit trick
|
||||
unsigned int hwTime=IRQgetHwTick();
|
||||
if((RTC->CRL & RTC_CRL_OWF) && IRQgetHwTick()>=hwTime)
|
||||
return (swTime + static_cast<long long>(hwTime)) + (1LL<<32);
|
||||
return swTime + static_cast<long long>(hwTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep the current thread till the specified time
|
||||
* \param tick absolute time in ticks
|
||||
* \param dLock used to reenable interrupts while sleeping
|
||||
* \return true if the wait time was in the past
|
||||
*/
|
||||
bool IRQabsoluteWaitTick(long long tick, FastInterruptDisableLock& dLock)
|
||||
{
|
||||
irqTime=tick;
|
||||
unsigned int hwAlarm=(tick & 0xffffffffULL) - (swTime & 0xffffffffULL);
|
||||
{
|
||||
ScopedCnf cnf;
|
||||
RTC->ALRL=hwAlarm;
|
||||
RTC->ALRH=hwAlarm>>16;
|
||||
}
|
||||
//NOTE: We do not wait for the alarm register to be written back to the low
|
||||
//frequency domain for performance reasons. The datasheet says it takes
|
||||
//at least 3 cycles of the 32KHz clock, but experiments show that it takes
|
||||
//from 2 to 3, so perhaps they meant "at most 3". Because of this we
|
||||
//consider the time in the past if we are more than 2 ticks of the 16KHz
|
||||
//clock (4 ticks of the 32KHz one) in advance. Sleeps less than 122us are
|
||||
//thus not supported.
|
||||
if(IRQgetTick()>=tick-2) return true;
|
||||
waiting=Thread::IRQgetCurrentThread();
|
||||
do {
|
||||
Thread::IRQwait();
|
||||
{
|
||||
FastInterruptEnableLock eLock(dLock);
|
||||
Thread::yield();
|
||||
}
|
||||
} while(waiting);
|
||||
return false;
|
||||
}
|
||||
|
||||
} //anon namespace
|
||||
|
||||
/**
|
||||
* RTC interrupt
|
||||
*/
|
||||
void __attribute__((naked)) RTC_IRQHandler()
|
||||
{
|
||||
saveContext();
|
||||
asm volatile("bl _Z10RTCIrqImplv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* RTC interrupt actual implementation
|
||||
*/
|
||||
void __attribute__((used)) RTCIrqImpl()
|
||||
{
|
||||
unsigned int crl=RTC->CRL;
|
||||
if(crl & RTC_CRL_OWF)
|
||||
{
|
||||
RTC->CRL=(RTC->CRL | 0xf) & ~RTC_CRL_OWF;
|
||||
swTime+=1LL<<32;
|
||||
} else if(crl & RTC_CRL_ALRF) {
|
||||
RTC->CRL=(RTC->CRL | 0xf) & ~RTC_CRL_ALRF;
|
||||
if(waiting && IRQgetTick()>=irqTime)
|
||||
{
|
||||
waiting->IRQwakeup();
|
||||
if(waiting->IRQgetPriority()>
|
||||
Thread::IRQgetCurrentThread()->IRQgetPriority())
|
||||
Scheduler::IRQfindNextThread();
|
||||
waiting=nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace miosix {
|
||||
|
||||
void absoluteDeepSleep(long long int value)
|
||||
{
|
||||
#ifdef RUN_WITH_HSI
|
||||
const int wakeupAdvance=3; //waking up takes time
|
||||
#else //RUN_WITH_HSI
|
||||
const int wakeupAdvance=33; //HSE starup time is 2ms
|
||||
#endif //RUN_WITH_HSI
|
||||
|
||||
Rtc& rtc=Rtc::instance();
|
||||
ioctl(STDOUT_FILENO,IOCTL_SYNC,0);
|
||||
|
||||
FastInterruptDisableLock dLock;
|
||||
|
||||
//Set alarm and enable EXTI
|
||||
long long wkupTick=rtc.tc.ns2tick(value)-wakeupAdvance;
|
||||
unsigned int hwAlarm=(wkupTick & 0xffffffffULL) - (swTime & 0xffffffffULL);
|
||||
{
|
||||
ScopedCnf cnf;
|
||||
RTC->ALRL=hwAlarm;
|
||||
RTC->ALRH=hwAlarm>>16;
|
||||
}
|
||||
while((RTC->CRL & RTC_CRL_RTOFF)==0) ;
|
||||
|
||||
|
||||
EXTI->RTSR |= EXTI_RTSR_TR17;
|
||||
EXTI->EMR |= EXTI_EMR_MR17; //enable event for wakeup
|
||||
|
||||
long long tick=IRQgetTick();
|
||||
while(tick<wkupTick)
|
||||
{
|
||||
PWR->CR |= PWR_CR_LPDS;
|
||||
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
|
||||
// Using WFE instead of WFI because if while we are with interrupts
|
||||
// disabled an interrupt (such as the tick interrupt) occurs, it
|
||||
// remains pending and the WFI becomes a nop, and the device never goes
|
||||
// in sleep mode. WFE events are latched in a separate pending register
|
||||
// so interrupts do not interfere with them
|
||||
__WFE();
|
||||
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
|
||||
PWR->CR &= ~PWR_CR_LPDS;
|
||||
|
||||
#ifndef SYSCLK_FREQ_24MHz
|
||||
#error TODO: support more PLL frequencies
|
||||
#endif
|
||||
//STOP mode resets the clock to the HSI 8MHz, so restore the 24MHz clock
|
||||
#ifndef RUN_WITH_HSI
|
||||
RCC->CR |= RCC_CR_HSEON;
|
||||
while((RCC->CR & RCC_CR_HSERDY)==0) ;
|
||||
//PLL = (HSE / 2) * 6 = 24 MHz
|
||||
RCC->CFGR &= ~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL);
|
||||
RCC->CFGR |= RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL6;
|
||||
#else //RUN_WITH_HSI
|
||||
//PLL = (HSI / 2) * 6 = 24 MHz
|
||||
RCC->CFGR &= ~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL);
|
||||
RCC->CFGR |= RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL6;
|
||||
#endif //RUN_WITH_HSI
|
||||
RCC->CR |= RCC_CR_PLLON;
|
||||
while((RCC->CR & RCC_CR_PLLRDY)==0) ;
|
||||
RCC->CFGR &= ~RCC_CFGR_SW;
|
||||
RCC->CFGR |= RCC_CFGR_SW_PLL;
|
||||
while((RCC->CFGR & RCC_CFGR_SWS)!=0x08) ;
|
||||
|
||||
//Wait RSF
|
||||
RTC->CRL=(RTC->CRL | 0xf) & ~RTC_CRL_RSF;
|
||||
while((RTC->CRL & RTC_CRL_RSF)==0) ;
|
||||
|
||||
//Clear IRQ
|
||||
unsigned int crl=RTC->CRL;
|
||||
//NOTE: ST docs say OWF, ALRF are not updated in deep sleep. So, to
|
||||
//detect counter ovreflow we check for oldTick>newTick. The check for
|
||||
//flags is still there in case overflow/alarm occurs before/after sleep
|
||||
if(crl & RTC_CRL_OWF || tick>IRQgetTick())
|
||||
{
|
||||
RTC->CRL=(RTC->CRL | 0xf) & ~RTC_CRL_OWF;
|
||||
swTime+=1LL<<32;
|
||||
} else if(crl & RTC_CRL_ALRF) {
|
||||
RTC->CRL=(RTC->CRL | 0xf) & ~RTC_CRL_ALRF;
|
||||
}
|
||||
|
||||
//Update tick, done after checking IRQ flags to avoid race condition
|
||||
tick=IRQgetTick();
|
||||
}
|
||||
|
||||
EXTI->EMR &=~ EXTI_EMR_MR17; //disable event for wakeup
|
||||
|
||||
wkupTick+=wakeupAdvance;
|
||||
|
||||
//NOTE: if we use the HSE we may spin for a while, but adding a
|
||||
//IRQabsoluteWaitTick here makes this function wakeup too late
|
||||
while(IRQgetTick()<wkupTick) ;
|
||||
}
|
||||
|
||||
//
|
||||
// class Rtc
|
||||
//
|
||||
|
||||
Rtc& Rtc::instance()
|
||||
{
|
||||
static Rtc singleton;
|
||||
return singleton;
|
||||
}
|
||||
|
||||
long long Rtc::getValue() const
|
||||
{
|
||||
//Function takes ~170 clock cycles ~60 cycles IRQgetTick, ~96 cycles tick2ns
|
||||
long long tick;
|
||||
{
|
||||
FastInterruptDisableLock dLock;
|
||||
tick=IRQgetTick();
|
||||
}
|
||||
//tick2ns is reentrant, so can be called with interrupt enabled
|
||||
return tc.tick2ns(tick);
|
||||
}
|
||||
|
||||
long long Rtc::IRQgetValue() const
|
||||
{
|
||||
return tc.tick2ns(IRQgetTick());
|
||||
}
|
||||
|
||||
// May not work due to the way hwAlarm is computed in sleep functions
|
||||
// void Rtc::setValue(long long value)
|
||||
// {
|
||||
// FastInterruptDisableLock dLock;
|
||||
// swTime=tc.ns2tick(value)-IRQgetHwTick();
|
||||
// }
|
||||
|
||||
void Rtc::wait(long long value)
|
||||
{
|
||||
FastInterruptDisableLock dLock;
|
||||
IRQabsoluteWaitTick(IRQgetTick()+tc.ns2tick(value),dLock);
|
||||
}
|
||||
|
||||
bool Rtc::absoluteWait(long long value)
|
||||
{
|
||||
FastInterruptDisableLock dLock;
|
||||
return IRQabsoluteWaitTick(tc.ns2tick(value),dLock);
|
||||
}
|
||||
|
||||
Rtc::Rtc() : tc(getTickFrequency())
|
||||
{
|
||||
{
|
||||
FastInterruptDisableLock dLock;
|
||||
RCC->APB1ENR |= RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN;
|
||||
PWR->CR |= PWR_CR_DBP;
|
||||
RCC->BDCR=RCC_BDCR_RTCEN //RTC enabled
|
||||
| RCC_BDCR_LSEON //External 32KHz oscillator enabled
|
||||
| RCC_BDCR_RTCSEL_0; //Select LSE as clock source for RTC
|
||||
RCC_SYNC();
|
||||
#ifdef RTC_CLKOUT_ENABLE
|
||||
BKP->RTCCR=BKP_RTCCR_CCO; //Output RTC clock/64 on pin
|
||||
#endif
|
||||
}
|
||||
while((RCC->BDCR & RCC_BDCR_LSERDY)==0) ; //Wait for LSE to start
|
||||
|
||||
RTC->CRH=RTC_CRH_OWIE | RTC_CRH_ALRIE;
|
||||
{
|
||||
ScopedCnf cnf;
|
||||
RTC->PRLH=0;
|
||||
RTC->PRLL=1;
|
||||
}
|
||||
NVIC_SetPriority(RTC_IRQn,5);
|
||||
NVIC_EnableIRQ(RTC_IRQn);
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2017 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef RTC_H
|
||||
#define RTC_H
|
||||
|
||||
#include <kernel/timeconversion.h>
|
||||
|
||||
namespace miosix {
|
||||
|
||||
///
|
||||
/// Uncomment this directive to enable the RTC clock output functionality
|
||||
///
|
||||
|
||||
//#define RTC_CLKOUT_ENABLE
|
||||
|
||||
/**
|
||||
* Puts the MCU in deep sleep until the specified absolute time.
|
||||
* \param value absolute wait time in nanoseconds
|
||||
* If value of absolute time is in the past no waiting will be set
|
||||
* and function return immediately.
|
||||
*/
|
||||
void absoluteDeepSleep(long long value);
|
||||
|
||||
/**
|
||||
* Driver for the stm32 RTC.
|
||||
* All the wait and deepSleep functions cannot be called concurrently by
|
||||
* multiple threads.
|
||||
*/
|
||||
class Rtc
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \return an instance of this class
|
||||
*/
|
||||
static Rtc& instance();
|
||||
|
||||
/**
|
||||
* \return the timer counter value in nanoseconds
|
||||
*/
|
||||
long long getValue() const;
|
||||
|
||||
/**
|
||||
* \return the timer counter value in nanoseconds
|
||||
*
|
||||
* Can be called with interrupt disabled, or inside an interrupt
|
||||
*/
|
||||
long long IRQgetValue() const;
|
||||
|
||||
/**
|
||||
* Set the timer counter value
|
||||
* \param value new timer value in nanoseconds
|
||||
*
|
||||
* NOTE: if alarm is set wakeup time is not updated
|
||||
*/
|
||||
// void setValue(long long value);
|
||||
|
||||
/**
|
||||
* Put thread in wait for the specified relative time.
|
||||
* This function wait for a relative time passed as parameter.
|
||||
* \param value relative time to wait, expressed in nanoseconds
|
||||
*/
|
||||
void wait(long long value);
|
||||
|
||||
/**
|
||||
* Puts the thread in wait until the specified absolute time.
|
||||
* \param value absolute wait time in nanoseconds
|
||||
* If value of absolute time is in the past no waiting will be set
|
||||
* and function return immediately.
|
||||
* \return true if the wait time was in the past
|
||||
*/
|
||||
bool absoluteWait(long long value);
|
||||
|
||||
/**
|
||||
* \return the timer frequency in Hz
|
||||
*/
|
||||
unsigned int getTickFrequency() const { return 16384; }
|
||||
|
||||
private:
|
||||
Rtc();
|
||||
Rtc(const Rtc&)=delete;
|
||||
Rtc& operator= (const Rtc&)=delete;
|
||||
|
||||
TimeConversion tc;
|
||||
|
||||
friend void absoluteDeepSleep(long long value);
|
||||
};
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //RTC_H
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2017 by Matteo Michele Piazzolla *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include "board_settings.h"
|
||||
#include "stm32_sgm.h"
|
||||
#include <string.h>
|
||||
#include "miosix.h"
|
||||
|
||||
namespace miosix {
|
||||
|
||||
extern unsigned char _preserve_start asm("_preserve_start");
|
||||
extern unsigned char _preserve_end asm("_preserve_end");
|
||||
|
||||
static unsigned char *preserve_start=&_preserve_start;
|
||||
static unsigned char *preserve_end=&_preserve_end;
|
||||
|
||||
SGM& SGM::instance()
|
||||
{
|
||||
static SGM singleton;
|
||||
return singleton;
|
||||
}
|
||||
|
||||
SGM::SGM()
|
||||
{
|
||||
/* Enable PWR clock */
|
||||
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
|
||||
|
||||
/* Enable backup SRAM Clock */
|
||||
RCC->AHB1ENR |= RCC_AHB1ENR_BKPSRAMEN;
|
||||
|
||||
enableWrite();
|
||||
|
||||
/* Enable Backup regulator */
|
||||
PWR->CSR |= PWR_CSR_BRE;
|
||||
|
||||
/* Enable the Backup SRAM low power Regulator */
|
||||
PWR->CSR |= PWR_CSR_BRE;
|
||||
|
||||
/* Wait for backup regulator */
|
||||
while (!(PWR->CSR & (PWR_CSR_BRR)));
|
||||
|
||||
/* Retrive last reset reason and clear the pending flag */
|
||||
readResetRegister();
|
||||
|
||||
/*
|
||||
* If the reset wasn't caused by software failure we cannot trust
|
||||
* the content of the backup memory and we need to clear it.
|
||||
*/
|
||||
if(lastReset != RST_SW)
|
||||
{
|
||||
memset(preserve_start, 0, preserve_end-preserve_start);
|
||||
}
|
||||
}
|
||||
|
||||
void SGM::disableWrite()
|
||||
{
|
||||
/* Enable Backup Domain write protection */
|
||||
PWR->CR &= ~PWR_CR_DBP;
|
||||
}
|
||||
|
||||
void SGM::enableWrite()
|
||||
{
|
||||
/* Disable Backup Domain write protection */
|
||||
PWR->CR |= PWR_CR_DBP;
|
||||
}
|
||||
|
||||
void SGM::clearResetFlag()
|
||||
{
|
||||
RCC->CSR |= RCC_CSR_RMVF;
|
||||
}
|
||||
|
||||
void SGM::readResetRegister()
|
||||
{
|
||||
uint32_t resetReg = RCC->CSR;
|
||||
clearResetFlag();
|
||||
if(resetReg & RCC_CSR_LPWRRSTF)
|
||||
{
|
||||
lastReset = RST_LOW_PWR;
|
||||
}
|
||||
else if( resetReg & RCC_CSR_WWDGRSTF)
|
||||
{
|
||||
lastReset = RST_WINDOW_WDG;
|
||||
}
|
||||
else if( resetReg & RCC_CSR_WDGRSTF)
|
||||
{
|
||||
lastReset = RST_INDEPENDENT_WDG;
|
||||
}
|
||||
else if( resetReg & RCC_CSR_SFTRSTF)
|
||||
{
|
||||
lastReset = RST_SW;
|
||||
}
|
||||
else if( resetReg & RCC_CSR_PORRSTF)
|
||||
{
|
||||
lastReset = RST_POWER_ON;
|
||||
}
|
||||
else if( resetReg & RCC_CSR_PADRSTF)
|
||||
{
|
||||
lastReset = RST_PIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastReset = RST_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace miosix
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2017 by Matteo Michele Piazzolla *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include "board_settings.h"
|
||||
|
||||
#define PRESERVE __attribute__((section(".preserve")))
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* Possible causes for an STM32 reset
|
||||
*/
|
||||
enum ResetReason
|
||||
{
|
||||
RST_LOW_PWR=0,
|
||||
RST_WINDOW_WDG=1,
|
||||
RST_INDEPENDENT_WDG=2,
|
||||
RST_SW=3,
|
||||
RST_POWER_ON=4,
|
||||
RST_PIN=5,
|
||||
RST_UNKNOWN=6,
|
||||
};
|
||||
|
||||
/**
|
||||
* Driver for the STM32F2 and STM32F4 backup SRAM, here used as
|
||||
* SafeGuard Memory, that is, a memory whose value is preseved across resets.
|
||||
*/
|
||||
class SGM
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \return an instance of this class (singleton)
|
||||
*/
|
||||
static SGM& instance();
|
||||
|
||||
/**
|
||||
* Temporarily disable writing to the safeguard memory.
|
||||
* By deafult, from reset to when the contrsuctor of this class is called
|
||||
* the safeguard memory is not writable. After the constructor is called,
|
||||
* the safeguard memory is writable.
|
||||
*/
|
||||
void disableWrite();
|
||||
|
||||
/**
|
||||
* Make the safeguard memory writable again, after a call to disableWrite()
|
||||
*/
|
||||
void enableWrite();
|
||||
|
||||
/**
|
||||
* Return the cause of the last reset of the microcontroller
|
||||
*/
|
||||
ResetReason lastResetReason() { return lastReset; }
|
||||
|
||||
private:
|
||||
ResetReason lastReset;
|
||||
|
||||
SGM(const SGM&)=delete;
|
||||
SGM& operator=(const SGM&)=delete;
|
||||
|
||||
SGM();
|
||||
void readResetRegister();
|
||||
void clearResetFlag();
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2017 by Matteo Michele Piazzolla *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include "stm32_wd.h"
|
||||
#include "miosix.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace miosix {
|
||||
|
||||
IWatchDog& IWatchDog::instance()
|
||||
{
|
||||
static IWatchDog singleton;
|
||||
return singleton;
|
||||
}
|
||||
|
||||
void IWatchDog::enable(int ms)
|
||||
{
|
||||
/* disable WD configuration protection */
|
||||
IWDG->KR = 0x5555;
|
||||
|
||||
/* set prescaler divider to /32 */
|
||||
IWDG->PR = 0x3;
|
||||
|
||||
/* set reload register */
|
||||
IWDG->RLR = max(1,min(4096,ms))-1;
|
||||
|
||||
/* start the watchdog */
|
||||
IWDG->KR = 0xCCCC;
|
||||
}
|
||||
|
||||
void IWatchDog::refresh()
|
||||
{
|
||||
IWDG->KR=0xAAAA;
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2017 by Matteo Michele Piazzolla *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* Driver for the STM32 independent watchdog
|
||||
*/
|
||||
class IWatchDog
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \return an instance of this class (singleton)
|
||||
*/
|
||||
static IWatchDog& instance();
|
||||
|
||||
/**
|
||||
* Enable the watchdog
|
||||
* \param ms reload period, from 1 (1 millisecond) to 4096 (4.096 seconds)
|
||||
*/
|
||||
void enable(int ms);
|
||||
|
||||
/**
|
||||
* If the watchdog is not periodically reloaded at least within the
|
||||
* period selected by enable, the microcontroller is reset.
|
||||
* The datsheet says that the oscillator clocking the watchdog can be
|
||||
* up to twice as fast, so it is recomended to reload the watchdog three
|
||||
* to four times faster to prevent spurious resets.
|
||||
*/
|
||||
void refresh();
|
||||
|
||||
private:
|
||||
IWatchDog(const IWatchDog&)=delete;
|
||||
IWatchDog& operator=(const IWatchDog&)=delete;
|
||||
IWatchDog() {}
|
||||
};
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,476 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 by Terraneo Federico and Silvano Seva *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include "stm32f2_f4_i2c.h"
|
||||
#include <miosix.h>
|
||||
#include <kernel/scheduler/scheduler.h>
|
||||
|
||||
using namespace miosix;
|
||||
|
||||
static volatile bool error; ///< Set to true by IRQ on error
|
||||
static Thread *waiting=0; ///< Thread waiting for an operation to complete
|
||||
|
||||
/* In non-DMA mode the variables below are used to
|
||||
* handle the reception of 2 or more bytes through
|
||||
* an interrupt, avoiding the thread that calls recv
|
||||
* to be locked in polling
|
||||
*/
|
||||
|
||||
#ifndef I2C_WITH_DMA
|
||||
static uint8_t *rxBuf = 0;
|
||||
static unsigned int rxBufCnt = 0;
|
||||
static unsigned int rxBufSize = 0;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef I2C_WITH_DMA
|
||||
/**
|
||||
* DMA I2C rx end of transfer
|
||||
*/
|
||||
void __attribute__((naked)) DMA1_Stream0_IRQHandler()
|
||||
{
|
||||
saveContext();
|
||||
asm volatile("bl _Z20I2C1rxDmaHandlerImplv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* DMA I2C rx end of transfer actual implementation
|
||||
*/
|
||||
void __attribute__((used)) I2C1rxDmaHandlerImpl()
|
||||
{
|
||||
DMA1->LIFCR=DMA_LIFCR_CTCIF0
|
||||
| DMA_LIFCR_CTEIF0
|
||||
| DMA_LIFCR_CDMEIF0
|
||||
| DMA_LIFCR_CFEIF0;
|
||||
I2C1->CR1 |= I2C_CR1_STOP;
|
||||
if(waiting==0) return;
|
||||
waiting->IRQwakeup();
|
||||
if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
|
||||
Scheduler::IRQfindNextThread();
|
||||
waiting=0;
|
||||
}
|
||||
|
||||
/**
|
||||
* DMA I2C tx end of transfer
|
||||
*/
|
||||
void DMA1_Stream7_IRQHandler()
|
||||
{
|
||||
DMA1->HIFCR=DMA_HIFCR_CTCIF7
|
||||
| DMA_HIFCR_CTEIF7
|
||||
| DMA_HIFCR_CDMEIF7
|
||||
| DMA_HIFCR_CFEIF7;
|
||||
|
||||
//We can't just wake the thread because the I2C is double buffered, and this
|
||||
//interrupt is fired at the same time as the second last byte is starting
|
||||
//to be sent out of the bus. If we return now, the main code would send a
|
||||
//stop condiotion too soon, and the last byte would never be sent. Instead,
|
||||
//we change from DMA mode to IRQ mode, so when the second last byte is sent,
|
||||
//that interrupt is fired and the last byte is sent out.
|
||||
//Note that since no thread is awakened from this IRQ, there's no need for
|
||||
//the saveContext(), restoreContext() and __attribute__((naked))
|
||||
I2C1->CR2 &= ~I2C_CR2_DMAEN;
|
||||
I2C1->CR2 |= I2C_CR2_ITBUFEN | I2C_CR2_ITEVTEN;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* I2C address sent interrupt
|
||||
*/
|
||||
void __attribute__((naked)) I2C1_EV_IRQHandler()
|
||||
{
|
||||
saveContext();
|
||||
asm volatile("bl _Z15I2C1HandlerImplv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* I2C address sent interrupt actual implementation
|
||||
*/
|
||||
void __attribute__((used)) I2C1HandlerImpl()
|
||||
{
|
||||
#ifdef I2C_WITH_DMA
|
||||
//When called to resolve the last byte not sent issue, clearing
|
||||
//I2C_CR2_ITBUFEN prevents this interrupt being re-entered forever, as
|
||||
//it does not send another byte to the I2C, so the interrupt would remain
|
||||
//pending. When called after the start bit has been sent, clearing
|
||||
//I2C_CR2_ITEVTEN prevents the same infinite re-enter as this interrupt
|
||||
//does not start an address transmission, which is necessary to stop
|
||||
//this interrupt from being pending
|
||||
I2C1->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITBUFEN);
|
||||
if(waiting==0) return;
|
||||
#else
|
||||
|
||||
bool rxFinished = false;
|
||||
|
||||
/* If rxBuf is equal to zero means that we are sending the slave
|
||||
address and this ISR is used to manage the address sent interrupt */
|
||||
|
||||
if(rxBuf == 0)
|
||||
{
|
||||
I2C1->CR2 &= ~I2C_CR2_ITEVTEN;
|
||||
rxFinished = true;
|
||||
}
|
||||
|
||||
if(I2C1->SR1 & I2C_SR1_RXNE)
|
||||
{
|
||||
rxBuf[rxBufCnt++] = I2C1->DR;
|
||||
if(rxBufCnt >= rxBufSize)
|
||||
{
|
||||
I2C1->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITBUFEN);
|
||||
rxFinished = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(waiting==0 || !rxFinished) return;
|
||||
#endif
|
||||
waiting->IRQwakeup();
|
||||
if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
|
||||
Scheduler::IRQfindNextThread();
|
||||
waiting=0;
|
||||
}
|
||||
|
||||
/**
|
||||
* I2C error interrupt
|
||||
*/
|
||||
void __attribute__((naked)) I2C1_ER_IRQHandler()
|
||||
{
|
||||
saveContext();
|
||||
asm volatile("bl _Z18I2C1errHandlerImplv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* I2C error interrupt actual implementation
|
||||
*/
|
||||
void __attribute__((used)) I2C1errHandlerImpl()
|
||||
{
|
||||
I2C1->SR1=0; //Clear error flags
|
||||
error=true;
|
||||
if(waiting==0) return;
|
||||
waiting->IRQwakeup();
|
||||
if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
|
||||
Scheduler::IRQfindNextThread();
|
||||
waiting=0;
|
||||
}
|
||||
|
||||
namespace miosix {
|
||||
|
||||
//
|
||||
// class I2C
|
||||
//
|
||||
|
||||
I2C1Driver& I2C1Driver::instance()
|
||||
{
|
||||
static I2C1Driver singleton;
|
||||
return singleton;
|
||||
}
|
||||
|
||||
void I2C1Driver::init()
|
||||
{
|
||||
//I2C devices are connected to APB1, whose frequency is the system clock
|
||||
//divided by a value set in the PPRE1 bits of RCC->CFGR
|
||||
const int ppre1=(RCC->CFGR & RCC_CFGR_PPRE1)>>10;
|
||||
const int divFactor= (ppre1 & 1<<2) ? (2<<(ppre1 & 0x3)) : 1;
|
||||
const int fpclk1=SystemCoreClock/divFactor;
|
||||
//iprintf("fpclk1=%d\n",fpclk1);
|
||||
|
||||
{
|
||||
FastInterruptDisableLock dLock;
|
||||
|
||||
#ifdef I2C_WITH_DMA
|
||||
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
|
||||
RCC_SYNC();
|
||||
#endif
|
||||
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; //Enable clock gating
|
||||
RCC_SYNC();
|
||||
}
|
||||
|
||||
#ifdef I2C_WITH_DMA
|
||||
NVIC_SetPriority(DMA1_Stream7_IRQn,10);//Low priority for DMA
|
||||
NVIC_ClearPendingIRQ(DMA1_Stream7_IRQn);//DMA1 stream 7 channel 1 = I2C1 TX
|
||||
NVIC_EnableIRQ(DMA1_Stream7_IRQn);
|
||||
|
||||
NVIC_SetPriority(DMA1_Stream0_IRQn,10);//Low priority for DMA
|
||||
NVIC_ClearPendingIRQ(DMA1_Stream0_IRQn);//DMA1 stream 0 channel 1 = I2C1 RX
|
||||
NVIC_EnableIRQ(DMA1_Stream0_IRQn);
|
||||
#endif
|
||||
|
||||
NVIC_SetPriority(I2C1_EV_IRQn,10);//Low priority for I2C
|
||||
NVIC_ClearPendingIRQ(I2C1_EV_IRQn);
|
||||
NVIC_EnableIRQ(I2C1_EV_IRQn);
|
||||
|
||||
NVIC_SetPriority(I2C1_ER_IRQn,10);
|
||||
NVIC_ClearPendingIRQ(I2C1_ER_IRQn);
|
||||
NVIC_EnableIRQ(I2C1_ER_IRQn);
|
||||
|
||||
I2C1->CR1=I2C_CR1_SWRST;
|
||||
I2C1->CR1=0;
|
||||
I2C1->CR2=fpclk1/1000000; //Set pclk frequency in MHz
|
||||
//This sets the duration of both Thigh and Tlow (master mode))
|
||||
const int i2cSpeed=100000; //100KHz
|
||||
I2C1->CCR=std::max(4,fpclk1/(2*i2cSpeed)); //Duty=2, standard mode (100KHz)
|
||||
//Datasheet says with I2C @ 100KHz, maximum SCL rise time is 1000ns
|
||||
//Need to change formula if I2C needs to run @ 400kHz
|
||||
I2C1->TRISE=fpclk1/1000000+1;
|
||||
I2C1->CR1=I2C_CR1_PE; //Enable peripheral
|
||||
}
|
||||
|
||||
bool I2C1Driver::send(unsigned char address,
|
||||
const void *data, int len, bool sendStop)
|
||||
{
|
||||
address &= 0xfe; //Mask bit 0, as we are writing
|
||||
if(start(address)==false || (I2C1->SR2 & I2C_SR2_TRA)==0)
|
||||
{
|
||||
I2C1->CR1 |= I2C_CR1_STOP;
|
||||
return false;
|
||||
}
|
||||
|
||||
error=false;
|
||||
|
||||
#ifdef I2C_WITH_DMA
|
||||
waiting=Thread::getCurrentThread();
|
||||
DMA1_Stream7->CR=0;
|
||||
DMA1_Stream7->PAR=reinterpret_cast<unsigned int>(&I2C1->DR);
|
||||
DMA1_Stream7->M0AR=reinterpret_cast<unsigned int>(data);
|
||||
DMA1_Stream7->NDTR=len;
|
||||
DMA1_Stream7->FCR=DMA_SxFCR_FEIE
|
||||
| DMA_SxFCR_DMDIS;
|
||||
DMA1_Stream7->CR=DMA_SxCR_CHSEL_0 //Channel 1
|
||||
| DMA_SxCR_MINC //Increment memory pointer
|
||||
| DMA_SxCR_DIR_0 //Memory to peripheral
|
||||
| DMA_SxCR_TCIE //Interrupt on transfer complete
|
||||
| DMA_SxCR_TEIE //Interrupt on transfer error
|
||||
| DMA_SxCR_DMEIE //Interrupt on direct mode error
|
||||
| DMA_SxCR_EN; //Start DMA
|
||||
|
||||
//Enable DMA in the I2C peripheral *after* having configured the DMA
|
||||
//peripheral, or a spurious interrupt is triggered
|
||||
I2C1->CR2 |= I2C_CR2_DMAEN | I2C_CR2_ITERREN;
|
||||
|
||||
{
|
||||
FastInterruptDisableLock dLock;
|
||||
while(waiting)
|
||||
{
|
||||
waiting->IRQwait();
|
||||
{
|
||||
FastInterruptEnableLock eLock(dLock);
|
||||
Thread::yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DMA1_Stream7->CR=0;
|
||||
|
||||
//The DMA interrupt routine changes the interrupt flags!
|
||||
I2C1->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITERREN);
|
||||
#else
|
||||
|
||||
I2C1->CR2 |= I2C_CR2_ITERREN;
|
||||
|
||||
const uint8_t *txData = reinterpret_cast<const uint8_t*>(data);
|
||||
for(int i=0; i<len && !error; i++)
|
||||
{
|
||||
I2C1->DR = txData[i];
|
||||
while(!(I2C1->SR1 & I2C_SR1_TXE)) ;
|
||||
}
|
||||
|
||||
I2C1->CR2 &= ~I2C_CR2_ITERREN;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The main idea of this driver is to avoid having the processor spinning
|
||||
* waiting on some status flag. Why? Because I2C is slow compared to a
|
||||
* modern processor. A 120MHz core does 1200 clock cycles in the time it
|
||||
* takes to transfer a single bit through an I2C clocked at 100KHz.
|
||||
* This time could be better spent doing a context switch and letting
|
||||
* another thread do useful work, or (and Miosix does it automatically if
|
||||
* there are no ready threads) sleeping the processor core. However,
|
||||
* I'm quite disappointed by the STM32 I2C peripheral, as it seems overly
|
||||
* complicated to use. To come close to achieving this goal I had to
|
||||
* orchestrate among *four* interrupt handlers, two of the DMA, and two
|
||||
* of the I2C itself. And in the end, what's even more disappointing, is
|
||||
* that I haven't found a way to completely avoid spinning. Why?
|
||||
* There's no interrupt that's fired when the stop bit is sent!
|
||||
* And what's worse, the documentation says that after you set the stop
|
||||
* bit in the CR2 register you can't write to it again (for example, to send
|
||||
* a start bit because two i2c api calls are made back to back) until the
|
||||
* MSL bit is cleared. But there's no interrupt tied to that event!
|
||||
* What's worse, is that the closest interrupt flag I've found when doing
|
||||
* an I2C send is fired when the last byte is *beginning* to be sent.
|
||||
* Maybe I haven't searched well enough, but the fact is I found nothing,
|
||||
* so this code below spins for 8 data bits of the last byte plus the ack
|
||||
* bit, plus the stop bit. That's 12000 wasted CPU cycles. Thanks, ST...
|
||||
*/
|
||||
|
||||
if(sendStop)
|
||||
{
|
||||
I2C1->CR1 |= I2C_CR1_STOP;
|
||||
while(I2C1->SR2 & I2C_SR2_MSL) ; //Wait for stop bit sent
|
||||
} else {
|
||||
// Dummy write, is the only way to clear
|
||||
// the TxE flag if stop bit is not sent...
|
||||
I2C1->DR = 0x00;
|
||||
}
|
||||
return !error;
|
||||
}
|
||||
|
||||
bool I2C1Driver::recv(unsigned char address, void *data, int len)
|
||||
{
|
||||
address |= 0x01;
|
||||
if(start(address,len==1)==false || I2C1->SR2 & I2C_SR2_TRA)
|
||||
{
|
||||
I2C1->CR1 |= I2C_CR1_STOP;
|
||||
return false;
|
||||
}
|
||||
|
||||
error=false;
|
||||
waiting=Thread::getCurrentThread();
|
||||
|
||||
#ifdef I2C_WITH_DMA
|
||||
I2C1->CR2 |= I2C_CR2_DMAEN | I2C_CR2_LAST | I2C_CR2_ITERREN;
|
||||
|
||||
DMA1_Stream0->CR=0;
|
||||
DMA1_Stream0->PAR=reinterpret_cast<unsigned int>(&I2C1->DR);
|
||||
DMA1_Stream0->M0AR=reinterpret_cast<unsigned int>(data);
|
||||
DMA1_Stream0->NDTR=len;
|
||||
DMA1_Stream0->FCR=DMA_SxFCR_FEIE
|
||||
| DMA_SxFCR_DMDIS;
|
||||
DMA1_Stream0->CR=DMA_SxCR_CHSEL_0 //Channel 1
|
||||
| DMA_SxCR_MINC //Increment memory pointer
|
||||
| DMA_SxCR_TCIE //Interrupt on transfer complete
|
||||
| DMA_SxCR_TEIE //Interrupt on transfer error
|
||||
| DMA_SxCR_DMEIE //Interrupt on direct mode error
|
||||
| DMA_SxCR_EN; //Start DMA
|
||||
|
||||
{
|
||||
FastInterruptDisableLock dLock;
|
||||
while(waiting)
|
||||
{
|
||||
waiting->IRQwait();
|
||||
{
|
||||
FastInterruptEnableLock eLock(dLock);
|
||||
Thread::yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DMA1_Stream7->CR=0;
|
||||
|
||||
I2C1->CR2 &= ~(I2C_CR2_DMAEN | I2C_CR2_LAST | I2C_CR2_ITERREN);
|
||||
|
||||
#else
|
||||
|
||||
/* Since i2c data reception is a bit tricky (see ST's reference manual for
|
||||
* further details), the thread that calls recv is yelded and reception is
|
||||
* handled using interrupts only if the number of bytes to be received is
|
||||
* greater than one.
|
||||
*/
|
||||
|
||||
rxBuf = reinterpret_cast<uint8_t*>(data);
|
||||
|
||||
if(len > 1)
|
||||
{
|
||||
I2C1->CR2 |= I2C_CR2_ITERREN | I2C_CR2_ITEVTEN | I2C_CR2_ITBUFEN;
|
||||
|
||||
rxBufCnt = 0;
|
||||
rxBufSize = len-2;
|
||||
|
||||
{
|
||||
FastInterruptDisableLock dLock;
|
||||
while(waiting)
|
||||
{
|
||||
waiting->IRQwait();
|
||||
{
|
||||
FastInterruptEnableLock eLock(dLock);
|
||||
Thread::yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
I2C1->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITBUFEN);
|
||||
}
|
||||
|
||||
I2C1->CR1 &= ~I2C_CR1_ACK;
|
||||
I2C1->CR1 |= I2C_CR1_STOP;
|
||||
|
||||
while(!(I2C1->SR1 & I2C_SR1_RXNE)) ;
|
||||
rxBuf[len-1] = I2C1->DR;
|
||||
|
||||
//set pointer to rx buffer to zero after having used it, see i2c event ISR
|
||||
rxBuf = 0;
|
||||
|
||||
I2C1->CR2 &= ~I2C_CR2_ITERREN;
|
||||
#endif
|
||||
|
||||
while(I2C1->SR2 & I2C_SR2_MSL) ; //Wait for stop bit sent
|
||||
return !error;
|
||||
}
|
||||
|
||||
bool I2C1Driver::start(unsigned char address, bool immediateNak)
|
||||
{
|
||||
/* Because the only way to send a restart is having the send function not
|
||||
* sending a stop condition after the data transfer, here we have to manage
|
||||
* a couple of things in SR1:
|
||||
* - the BTF flag is set, cleared by a dummy read of DR
|
||||
* - The Berr flag is set, this because the I2C harware detects the start
|
||||
* condition sent without a stop before it as a misplaced start and
|
||||
* rises an error
|
||||
*/
|
||||
|
||||
I2C1->CR1 |= I2C_CR1_START | I2C_CR1_ACK;
|
||||
if(!waitStatus1()) return false;
|
||||
if((I2C1->SR1 & I2C_SR1_SB)==0) return false; //Must read SR1 to clear flag
|
||||
I2C1->DR=address;
|
||||
if(immediateNak) I2C1->CR1 &= ~I2C_CR1_ACK;
|
||||
if(!waitStatus1()) return false;
|
||||
if(I2C1->SR1 & I2C_SR1_AF) return false; //Must read SR1 and SR2
|
||||
if((I2C1->SR2 & I2C_SR2_MSL)==0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool I2C1Driver::waitStatus1()
|
||||
{
|
||||
error=false;
|
||||
waiting=Thread::getCurrentThread();
|
||||
I2C1->CR2 |= I2C_CR2_ITEVTEN | I2C_CR2_ITERREN;
|
||||
{
|
||||
FastInterruptDisableLock dLock;
|
||||
while(waiting)
|
||||
{
|
||||
waiting->IRQwait();
|
||||
{
|
||||
FastInterruptEnableLock eLock(dLock);
|
||||
Thread::yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
I2C1->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITERREN);
|
||||
return !error;
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 by Terraneo Federico and Silvano Seva *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef STM32F2_I2C_H
|
||||
#define STM32F2_I2C_H
|
||||
|
||||
#include <interfaces/arch_registers.h>
|
||||
#include "board_settings.h"
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* Driver for the I2C1 peripheral in STM32F2 and STM32F4 under Miosix
|
||||
*/
|
||||
class I2C1Driver
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \return an instance of this class (singleton)
|
||||
*/
|
||||
static I2C1Driver& instance();
|
||||
|
||||
/**
|
||||
* Initializes the peripheral. The only supported mode is 100KHz, master,
|
||||
* 7bit address. Note that there is no need to manually call this member
|
||||
* function as the constructor already inizializes the I2C peripheral.
|
||||
* The only use of this member function is to reinitialize the peripheral
|
||||
* if the microcontroller clock frequency or the APB prescaler is changed.
|
||||
*/
|
||||
void init();
|
||||
|
||||
/**
|
||||
* Send data to a device connected to the I2C bus
|
||||
* \param address device address (bit 0 is forced at 0)
|
||||
* \param data pointer with data to send
|
||||
* \param len length of data to send
|
||||
* \param sendStop if set to false disables the sending of a stop condition
|
||||
* after data transmission has finished
|
||||
* \return true on success, false on failure
|
||||
*/
|
||||
bool send(unsigned char address,
|
||||
const void *data, int len, bool sendStop = true);
|
||||
|
||||
/**
|
||||
* Receive data from a device connected to the I2C bus
|
||||
* \param address device address (bit 0 is forced at 1)
|
||||
* \param data pointer to a buffer where data will be received
|
||||
* \param len length of data to receive
|
||||
* \return true on success, false on failure
|
||||
*/
|
||||
bool recv(unsigned char address, void *data, int len);
|
||||
|
||||
private:
|
||||
I2C1Driver(const I2C1Driver&);
|
||||
I2C1Driver& operator=(const I2C1Driver&);
|
||||
|
||||
/**
|
||||
* Constructor. Initializes the peripheral except the GPIOs, that must be
|
||||
* set by the caller to the appropriate alternate function mode prior to
|
||||
* creating an instance of this class.
|
||||
* \param i2c pinter to the desired I2C peripheral, such as I2C1, I2C2, ...
|
||||
*/
|
||||
I2C1Driver() { init(); }
|
||||
|
||||
/**
|
||||
* Send a start condition
|
||||
* \param address
|
||||
* \param immediateNak
|
||||
* \return
|
||||
*/
|
||||
bool start(unsigned char address, bool immediateNak=false);
|
||||
|
||||
/**
|
||||
* Wait until until an interrupt occurs during the send start bit and
|
||||
* send address phases of the i2c communication.
|
||||
* \return true if the operation was successful, false on error
|
||||
*/
|
||||
bool waitStatus1();
|
||||
};
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //STM32F2_I2C_H
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
#ifndef ARCH_REGISTERS_IMPL_H
|
||||
#define ARCH_REGISTERS_IMPL_H
|
||||
|
||||
//Always include stm32f0xx.h before core_cm0.h, there's some nasty dependency
|
||||
#include "CMSIS/Device/ST/STM32F0xx/Include/stm32f0xx.h"
|
||||
#include "CMSIS/Include/core_cm0.h"
|
||||
#include "CMSIS/Device/ST/STM32F0xx/Include/system_stm32f0xx.h"
|
||||
|
||||
#define RCC_SYNC() //Workaround for a bug in stm32f42x
|
||||
|
||||
#endif //ARCH_REGISTERS_IMPL_H
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ARCH_SETTINGS_H
|
||||
#define ARCH_SETTINGS_H
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* \addtogroup Settings
|
||||
* \{
|
||||
*/
|
||||
|
||||
/// \internal Size of vector to store registers during ctx switch (9*4=36Bytes)
|
||||
/// Only sp and r4-r11 are saved here, since r0-r3,r12,lr,pc,xPSR and
|
||||
/// old sp are saved by hardware on the process stack on Cortex M3 CPUs.
|
||||
const unsigned char CTXSAVE_SIZE=9;
|
||||
|
||||
/// \internal some architectures save part of the context on their stack.
|
||||
/// This constant is used to increase the stack size by the size of context
|
||||
/// save frame. If zero, this architecture does not save anything on stack
|
||||
/// during context save. Size is in bytes, not words.
|
||||
/// MUST be divisible by 4.
|
||||
const unsigned int CTXSAVE_ON_STACK=32;
|
||||
|
||||
/// \internal stack alignment for this specific architecture
|
||||
const unsigned int CTXSAVE_STACK_ALIGNMENT=8;
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif /* ARCH_SETTINGS_H */
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010, 2011, 2012 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
//Miosix kernel
|
||||
|
||||
#include "interfaces/portability.h"
|
||||
#include "kernel/kernel.h"
|
||||
#include "kernel/error.h"
|
||||
#include "interfaces/bsp.h"
|
||||
#include "kernel/scheduler/scheduler.h"
|
||||
#include "kernel/scheduler/tick_interrupt.h"
|
||||
#include <algorithm>
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* timer interrupt routine.
|
||||
* Since inside naked functions only assembler code is allowed, this function
|
||||
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||
* the implementation code in ISR_preempt()
|
||||
*/
|
||||
void SysTick_Handler() __attribute__((naked));
|
||||
void SysTick_Handler()
|
||||
{
|
||||
saveContext();
|
||||
//Call ISR_preempt(). Name is a C++ mangled name.
|
||||
asm volatile("bl _ZN14miosix_private11ISR_preemptEv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* software interrupt routine.
|
||||
* Since inside naked functions only assembler code is allowed, this function
|
||||
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||
* the implementation code in ISR_yield()
|
||||
*/
|
||||
void SVC_Handler() __attribute__((naked));
|
||||
void SVC_Handler()
|
||||
{
|
||||
saveContext();
|
||||
//Call ISR_yield(). Name is a C++ mangled name.
|
||||
asm volatile("bl _ZN14miosix_private9ISR_yieldEv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||
/**
|
||||
* \internal
|
||||
* Auxiliary timer interupt routine.
|
||||
* Used for variable lenght bursts in control based scheduler.
|
||||
* Since inside naked functions only assembler code is allowed, this function
|
||||
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||
* the implementation code in ISR_yield()
|
||||
*/
|
||||
void XXX_IRQHandler() __attribute__((naked));
|
||||
void XXX_IRQHandler()
|
||||
{
|
||||
saveContext();
|
||||
//Call ISR_auxTimer(). Name is a C++ mangled name.
|
||||
asm volatile("bl _ZN14miosix_private12ISR_auxTimerEv");
|
||||
restoreContext();
|
||||
}
|
||||
#endif //SCHED_TYPE_CONTROL_BASED
|
||||
|
||||
namespace miosix_private {
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Called by the timer interrupt, preempt to next thread
|
||||
* Declared noinline to avoid the compiler trying to inline it into the caller,
|
||||
* which would violate the requirement on naked functions. Function is not
|
||||
* static because otherwise the compiler optimizes it out...
|
||||
*/
|
||||
void ISR_preempt() __attribute__((noinline));
|
||||
void ISR_preempt()
|
||||
{
|
||||
IRQstackOverflowCheck();
|
||||
miosix::IRQtickInterrupt();
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Called by the software interrupt, yield to next thread
|
||||
* Declared noinline to avoid the compiler trying to inline it into the caller,
|
||||
* which would violate the requirement on naked functions. Function is not
|
||||
* static because otherwise the compiler optimizes it out...
|
||||
*/
|
||||
void ISR_yield() __attribute__((noinline));
|
||||
void ISR_yield()
|
||||
{
|
||||
IRQstackOverflowCheck();
|
||||
miosix::Scheduler::IRQfindNextThread();
|
||||
}
|
||||
|
||||
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||
/**
|
||||
* \internal
|
||||
* Auxiliary timer interupt routine.
|
||||
* Used for variable lenght bursts in control based scheduler.
|
||||
*/
|
||||
void ISR_auxTimer() __attribute__((noinline));
|
||||
void ISR_auxTimer()
|
||||
{
|
||||
IRQstackOverflowCheck();
|
||||
miosix::Scheduler::IRQfindNextThread();//If the kernel is running, preempt
|
||||
if(miosix::kernel_running!=0) miosix::tick_skew=true;
|
||||
TIM2->SR=0;
|
||||
}
|
||||
#endif //SCHED_TYPE_CONTROL_BASED
|
||||
|
||||
void IRQstackOverflowCheck()
|
||||
{
|
||||
const unsigned int watermarkSize=miosix::WATERMARK_LEN/sizeof(unsigned int);
|
||||
for(unsigned int i=0;i<watermarkSize;i++)
|
||||
{
|
||||
if(miosix::cur->watermark[i]!=miosix::WATERMARK_FILL)
|
||||
miosix::errorHandler(miosix::STACK_OVERFLOW);
|
||||
}
|
||||
if(miosix::cur->ctxsave[0] < reinterpret_cast<unsigned int>(
|
||||
miosix::cur->watermark+watermarkSize))
|
||||
miosix::errorHandler(miosix::STACK_OVERFLOW);
|
||||
}
|
||||
|
||||
void IRQsystemReboot()
|
||||
{
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp,
|
||||
void *argv)
|
||||
{
|
||||
unsigned int *stackPtr=sp;
|
||||
stackPtr--; //Stack is full descending, so decrement first
|
||||
*stackPtr=0x01000000; stackPtr--; //--> xPSR
|
||||
*stackPtr=reinterpret_cast<unsigned long>(
|
||||
&miosix::Thread::threadLauncher); stackPtr--; //--> pc
|
||||
*stackPtr=0xffffffff; stackPtr--; //--> lr
|
||||
*stackPtr=0; stackPtr--; //--> r12
|
||||
*stackPtr=0; stackPtr--; //--> r3
|
||||
*stackPtr=0; stackPtr--; //--> r2
|
||||
*stackPtr=reinterpret_cast<unsigned long >(argv); stackPtr--; //--> r1
|
||||
*stackPtr=reinterpret_cast<unsigned long >(pc); //--> r0
|
||||
|
||||
ctxsave[0]=reinterpret_cast<unsigned long>(stackPtr); //--> psp
|
||||
//leaving the content of r4-r11 uninitialized
|
||||
}
|
||||
|
||||
void IRQportableStartKernel()
|
||||
{
|
||||
NVIC_SetPriority(SVC_IRQn,3);//High priority for SVC (Max=0, min=15)
|
||||
NVIC_SetPriority(SysTick_IRQn,3);//High priority for SysTick (Max=0, min=15)
|
||||
SysTick->LOAD=SystemCoreClock/miosix::TICK_FREQ;
|
||||
//Start SysTick, set to generate interrupts
|
||||
SysTick->CTRL=SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk |
|
||||
SysTick_CTRL_CLKSOURCE_Msk;
|
||||
|
||||
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||
AuxiliaryTimer::IRQinit();
|
||||
#endif //SCHED_TYPE_CONTROL_BASED
|
||||
|
||||
//create a temporary space to save current registers. This data is useless
|
||||
//since there's no way to stop the sheduler, but we need to save it anyway.
|
||||
unsigned int s_ctxsave[miosix::CTXSAVE_SIZE];
|
||||
ctxsave=s_ctxsave;//make global ctxsave point to it
|
||||
//Note, we can't use enableInterrupts() now since the call is not mathced
|
||||
//by a call to disableInterrupts()
|
||||
__enable_irq();
|
||||
miosix::Thread::yield();
|
||||
//Never reaches here
|
||||
}
|
||||
|
||||
void sleepCpu()
|
||||
{
|
||||
__WFI();
|
||||
}
|
||||
|
||||
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||
#error "AUX_TIMER not yet implemented"
|
||||
void AuxiliaryTimer::IRQinit()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int AuxiliaryTimer::IRQgetValue()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AuxiliaryTimer::IRQsetValue(int x)
|
||||
{
|
||||
|
||||
}
|
||||
#endif //SCHED_TYPE_CONTROL_BASED
|
||||
|
||||
} //namespace miosix_private
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010, 2011, 2012 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
//Miosix kernel
|
||||
|
||||
#ifndef PORTABILITY_IMPL_H
|
||||
#define PORTABILITY_IMPL_H
|
||||
|
||||
#include "interfaces/arch_registers.h"
|
||||
#include "config/miosix_settings.h"
|
||||
|
||||
/**
|
||||
* \addtogroup Drivers
|
||||
* \{
|
||||
*/
|
||||
|
||||
/*
|
||||
* This pointer is used by the kernel, and should not be used by end users.
|
||||
* this is a pointer to a location where to store the thread's registers during
|
||||
* context switch. It requires C linkage to be used inside asm statement.
|
||||
* Registers are saved in the following order:
|
||||
* *ctxsave+32 --> r11
|
||||
* *ctxsave+28 --> r10
|
||||
* *ctxsave+24 --> r9
|
||||
* *ctxsave+20 --> r8
|
||||
* *ctxsave+16 --> r7
|
||||
* *ctxsave+12 --> r6
|
||||
* *ctxsave+8 --> r5
|
||||
* *ctxsave+4 --> r4
|
||||
* *ctxsave+0 --> psp
|
||||
*/
|
||||
extern "C" {
|
||||
extern volatile unsigned int *ctxsave;
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \def saveContext()
|
||||
* Save context from an interrupt<br>
|
||||
* Must be the first line of an IRQ where a context switch can happen.
|
||||
* The IRQ must be "naked" to prevent the compiler from generating context save.
|
||||
*
|
||||
* A note on the dmb instruction, without it a race condition was observed
|
||||
* between pauseKernel() and IRQfindNextThread(). pauseKernel() uses an strex
|
||||
* instruction to store a value in the global variable kernel_running which is
|
||||
* tested by the context switch code in IRQfindNextThread(). Without the memory
|
||||
* barrier IRQfindNextThread() would occasionally read the previous value and
|
||||
* perform a context switch while the kernel was paused, leading to deadlock.
|
||||
* The failure was only observed within the exception_test() in the testsuite
|
||||
* running on the stm32f429zi_stm32f4discovery.
|
||||
*/
|
||||
|
||||
#define saveContext() \
|
||||
{ \
|
||||
asm volatile("push {lr} \n\t" /*save lr on MAIN stack*/ \
|
||||
"mrs r1, psp \n\t" /*get PROCESS stack pointer*/ \
|
||||
"ldr r0, =ctxsave \n\t" /*get current context*/ \
|
||||
"ldr r0, [r0] \n\t" \
|
||||
"stmia r0, {r1,r4-r7} \n\t" /*save PROCESS sp + r4-r7*/ \
|
||||
"mov r4, r8 \n\t" \
|
||||
"mov r5, r9 \n\t" \
|
||||
"mov r6, r10 \n\t" \
|
||||
"mov r7, r11 \n\t" \
|
||||
"stmia r0, {r4-r7} \n\t" \
|
||||
"dmb \n\t" \
|
||||
); \
|
||||
}
|
||||
|
||||
/**
|
||||
* \def restoreContext()
|
||||
* Restore context in an IRQ where saveContext() is used. Must be the last line
|
||||
* of an IRQ where a context switch can happen. The IRQ must be "naked" to
|
||||
* prevent the compiler from generating context restore.
|
||||
*/
|
||||
|
||||
#define restoreContext() \
|
||||
{ \
|
||||
asm volatile("ldr r0, =ctxsave \n\t" /*get current context*/ \
|
||||
"ldr r0, [r0] \n\t" \
|
||||
"ldmia r0, {r1,r4-r7} \n\t" /*pop r8-r11 saving in r4-r7*/ \
|
||||
"msr psp, r1 \n\t" /*restore PROCESS sp*/ \
|
||||
"ldmia r0!, {r0-r3} \n\t" \
|
||||
"mov r9, r0 \n\t" \
|
||||
"mov r10, r1 \n\t" \
|
||||
"mov r11, r2 \n\t" \
|
||||
"mov r11, r3 \n\t" \
|
||||
"pop {pc} \n\t" /*return*/ \
|
||||
); \
|
||||
}
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
namespace miosix_private {
|
||||
|
||||
/**
|
||||
* \addtogroup Drivers
|
||||
* \{
|
||||
*/
|
||||
|
||||
inline void doYield()
|
||||
{
|
||||
asm volatile("movs r3, #0\n\t"
|
||||
"svc 0"
|
||||
:::"r3");
|
||||
}
|
||||
|
||||
inline void doDisableInterrupts()
|
||||
{
|
||||
// Documentation says __disable_irq() disables all interrupts with
|
||||
// configurable priority, so also SysTick and SVC.
|
||||
// No need to disable faults with __disable_fault_irq()
|
||||
__disable_irq();
|
||||
//The new fastDisableInterrupts/fastEnableInterrupts are inline, so there's
|
||||
//the need for a memory barrier to avoid aggressive reordering
|
||||
asm volatile("":::"memory");
|
||||
}
|
||||
|
||||
inline void doEnableInterrupts()
|
||||
{
|
||||
__enable_irq();
|
||||
//The new fastDisableInterrupts/fastEnableInterrupts are inline, so there's
|
||||
//the need for a memory barrier to avoid aggressive reordering
|
||||
asm volatile("":::"memory");
|
||||
}
|
||||
|
||||
inline bool checkAreInterruptsEnabled()
|
||||
{
|
||||
register int i;
|
||||
asm volatile("mrs %0, primask \n\t":"=r"(i));
|
||||
if(i!=0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
} //namespace miosix_private
|
||||
|
||||
#endif //PORTABILITY_IMPL_H
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
#ifndef ARCH_REGISTERS_IMPL_H
|
||||
#define ARCH_REGISTERS_IMPL_H
|
||||
|
||||
//Always include stm32f10x.h before core_cm3.h, there's some nasty dependency
|
||||
#include "CMSIS/Device/ST/STM32F10x/Include/stm32f10x.h"
|
||||
#include "CMSIS/Include/core_cm3.h"
|
||||
#include "CMSIS/Device/ST/STM32F10x/Include/system_stm32f10x.h"
|
||||
|
||||
#define RCC_SYNC() //Workaround for a bug in stm32f42x
|
||||
|
||||
#endif //ARCH_REGISTERS_IMPL_H
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ARCH_SETTINGS_H
|
||||
#define ARCH_SETTINGS_H
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* \addtogroup Settings
|
||||
* \{
|
||||
*/
|
||||
|
||||
/// \internal Size of vector to store registers during ctx switch (9*4=36Bytes)
|
||||
/// Only sp and r4-r11 are saved here, since r0-r3,r12,lr,pc,xPSR and
|
||||
/// old sp are saved by hardware on the process stack on Cortex M3 CPUs.
|
||||
const unsigned char CTXSAVE_SIZE=9;
|
||||
|
||||
/// \internal some architectures save part of the context on their stack.
|
||||
/// This constant is used to increase the stack size by the size of context
|
||||
/// save frame. If zero, this architecture does not save anything on stack
|
||||
/// during context save. Size is in bytes, not words.
|
||||
/// MUST be divisible by 4.
|
||||
const unsigned int CTXSAVE_ON_STACK=32;
|
||||
|
||||
/// \internal stack alignment for this specific architecture
|
||||
const unsigned int CTXSAVE_STACK_ALIGNMENT=8;
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif /* ARCH_SETTINGS_H */
|
||||
|
|
@ -0,0 +1,273 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010, 2011, 2012 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
//Miosix kernel
|
||||
|
||||
#include "interfaces/portability.h"
|
||||
#include "kernel/kernel.h"
|
||||
#include "kernel/error.h"
|
||||
#include "interfaces/bsp.h"
|
||||
#include "kernel/scheduler/scheduler.h"
|
||||
#include "kernel/scheduler/tick_interrupt.h"
|
||||
#include <algorithm>
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* timer interrupt routine.
|
||||
* Since inside naked functions only assembler code is allowed, this function
|
||||
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||
* the implementation code in ISR_preempt()
|
||||
*/
|
||||
void SysTick_Handler() __attribute__((naked));
|
||||
void SysTick_Handler()
|
||||
{
|
||||
saveContext();
|
||||
//Call ISR_preempt(). Name is a C++ mangled name.
|
||||
asm volatile("bl _ZN14miosix_private11ISR_preemptEv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* software interrupt routine.
|
||||
* Since inside naked functions only assembler code is allowed, this function
|
||||
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||
* the implementation code in ISR_yield()
|
||||
*/
|
||||
void SVC_Handler() __attribute__((naked));
|
||||
void SVC_Handler()
|
||||
{
|
||||
saveContext();
|
||||
//Call ISR_yield(). Name is a C++ mangled name.
|
||||
asm volatile("bl _ZN14miosix_private9ISR_yieldEv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||
/**
|
||||
* \internal
|
||||
* Auxiliary timer interupt routine.
|
||||
* Used for variable lenght bursts in control based scheduler.
|
||||
* Since inside naked functions only assembler code is allowed, this function
|
||||
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||
* the implementation code in ISR_yield()
|
||||
*/
|
||||
void TIM2_IRQHandler() __attribute__((naked));
|
||||
void TIM2_IRQHandler()
|
||||
{
|
||||
saveContext();
|
||||
//Call ISR_auxTimer(). Name is a C++ mangled name.
|
||||
asm volatile("bl _ZN14miosix_private12ISR_auxTimerEv");
|
||||
restoreContext();
|
||||
}
|
||||
#endif //SCHED_TYPE_CONTROL_BASED
|
||||
|
||||
namespace miosix_private {
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Called by the timer interrupt, preempt to next thread
|
||||
* Declared noinline to avoid the compiler trying to inline it into the caller,
|
||||
* which would violate the requirement on naked functions. Function is not
|
||||
* static because otherwise the compiler optimizes it out...
|
||||
*/
|
||||
void ISR_preempt() __attribute__((noinline));
|
||||
void ISR_preempt()
|
||||
{
|
||||
IRQstackOverflowCheck();
|
||||
miosix::IRQtickInterrupt();
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Called by the software interrupt, yield to next thread
|
||||
* Declared noinline to avoid the compiler trying to inline it into the caller,
|
||||
* which would violate the requirement on naked functions. Function is not
|
||||
* static because otherwise the compiler optimizes it out...
|
||||
*/
|
||||
void ISR_yield() __attribute__((noinline));
|
||||
void ISR_yield()
|
||||
{
|
||||
IRQstackOverflowCheck();
|
||||
miosix::Scheduler::IRQfindNextThread();
|
||||
}
|
||||
|
||||
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||
/**
|
||||
* \internal
|
||||
* Auxiliary timer interupt routine.
|
||||
* Used for variable lenght bursts in control based scheduler.
|
||||
*/
|
||||
void ISR_auxTimer() __attribute__((noinline));
|
||||
void ISR_auxTimer()
|
||||
{
|
||||
IRQstackOverflowCheck();
|
||||
miosix::Scheduler::IRQfindNextThread();//If the kernel is running, preempt
|
||||
if(miosix::kernel_running!=0) miosix::tick_skew=true;
|
||||
TIM2->SR=0;
|
||||
}
|
||||
#endif //SCHED_TYPE_CONTROL_BASED
|
||||
|
||||
void IRQstackOverflowCheck()
|
||||
{
|
||||
const unsigned int watermarkSize=miosix::WATERMARK_LEN/sizeof(unsigned int);
|
||||
for(unsigned int i=0;i<watermarkSize;i++)
|
||||
{
|
||||
if(miosix::cur->watermark[i]!=miosix::WATERMARK_FILL)
|
||||
miosix::errorHandler(miosix::STACK_OVERFLOW);
|
||||
}
|
||||
if(miosix::cur->ctxsave[0] < reinterpret_cast<unsigned int>(
|
||||
miosix::cur->watermark+watermarkSize))
|
||||
miosix::errorHandler(miosix::STACK_OVERFLOW);
|
||||
}
|
||||
|
||||
void IRQsystemReboot()
|
||||
{
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp,
|
||||
void *argv)
|
||||
{
|
||||
unsigned int *stackPtr=sp;
|
||||
stackPtr--; //Stack is full descending, so decrement first
|
||||
*stackPtr=0x01000000; stackPtr--; //--> xPSR
|
||||
*stackPtr=reinterpret_cast<unsigned long>(
|
||||
&miosix::Thread::threadLauncher); stackPtr--; //--> pc
|
||||
*stackPtr=0xffffffff; stackPtr--; //--> lr
|
||||
*stackPtr=0; stackPtr--; //--> r12
|
||||
*stackPtr=0; stackPtr--; //--> r3
|
||||
*stackPtr=0; stackPtr--; //--> r2
|
||||
*stackPtr=reinterpret_cast<unsigned long >(argv); stackPtr--; //--> r1
|
||||
*stackPtr=reinterpret_cast<unsigned long >(pc); //--> r0
|
||||
|
||||
ctxsave[0]=reinterpret_cast<unsigned long>(stackPtr); //--> psp
|
||||
//leaving the content of r4-r11 uninitialized
|
||||
}
|
||||
|
||||
#ifdef WITH_PROCESSES
|
||||
|
||||
void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp,
|
||||
void *argv, unsigned int *gotBase)
|
||||
{
|
||||
unsigned int *stackPtr=sp;
|
||||
stackPtr--; //Stack is full descending, so decrement first
|
||||
*stackPtr=0x01000000; stackPtr--; //--> xPSR
|
||||
*stackPtr=reinterpret_cast<unsigned long>(pc); stackPtr--; //--> pc
|
||||
*stackPtr=0xffffffff; stackPtr--; //--> lr
|
||||
*stackPtr=0; stackPtr--; //--> r12
|
||||
*stackPtr=0; stackPtr--; //--> r3
|
||||
*stackPtr=0; stackPtr--; //--> r2
|
||||
*stackPtr=0; stackPtr--; //--> r1
|
||||
*stackPtr=reinterpret_cast<unsigned long >(argv); //--> r0
|
||||
|
||||
ctxsave[0]=reinterpret_cast<unsigned long>(stackPtr); //--> psp
|
||||
ctxsave[6]=reinterpret_cast<unsigned long>(gotBase); //--> r9
|
||||
//leaving the content of r4-r8,r10-r11 uninitialized
|
||||
}
|
||||
|
||||
#endif //WITH_PROCESSES
|
||||
|
||||
void IRQportableStartKernel()
|
||||
{
|
||||
//Enable fault handlers
|
||||
SCB->SHCSR |= SCB_SHCSR_USGFAULTENA | SCB_SHCSR_BUSFAULTENA
|
||||
| SCB_SHCSR_MEMFAULTENA;
|
||||
//Enable traps for division by zero. Trap for unaligned memory access
|
||||
//was removed as gcc starting from 4.7.2 generates unaligned accesses by
|
||||
//default (https://www.gnu.org/software/gcc/gcc-4.7/changes.html)
|
||||
SCB->CCR |= SCB_CCR_DIV_0_TRP;
|
||||
NVIC_SetPriorityGrouping(7);//This should disable interrupt nesting
|
||||
NVIC_SetPriority(SVCall_IRQn,3);//High priority for SVC (Max=0, min=15)
|
||||
NVIC_SetPriority(SysTick_IRQn,3);//High priority for SysTick (Max=0, min=15)
|
||||
SysTick->LOAD=SystemCoreClock/miosix::TICK_FREQ;
|
||||
//Start SysTick, set to generate interrupts
|
||||
SysTick->CTRL=SysTick_CTRL_ENABLE | SysTick_CTRL_TICKINT |
|
||||
SysTick_CTRL_CLKSOURCE;
|
||||
|
||||
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||
AuxiliaryTimer::IRQinit();
|
||||
#endif //SCHED_TYPE_CONTROL_BASED
|
||||
|
||||
//create a temporary space to save current registers. This data is useless
|
||||
//since there's no way to stop the sheduler, but we need to save it anyway.
|
||||
unsigned int s_ctxsave[miosix::CTXSAVE_SIZE];
|
||||
ctxsave=s_ctxsave;//make global ctxsave point to it
|
||||
//Note, we can't use enableInterrupts() now since the call is not mathced
|
||||
//by a call to disableInterrupts()
|
||||
__enable_fault_irq();
|
||||
__enable_irq();
|
||||
miosix::Thread::yield();
|
||||
//Never reaches here
|
||||
}
|
||||
|
||||
void sleepCpu()
|
||||
{
|
||||
__WFI();
|
||||
}
|
||||
|
||||
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||
void AuxiliaryTimer::IRQinit()
|
||||
{
|
||||
RCC->APB1ENR|=RCC_APB1ENR_TIM2EN;
|
||||
RCC_SYNC();
|
||||
DBGMCU->CR|=DBGMCU_CR_DBG_TIM2_STOP; //Tim2 stops while debugging
|
||||
TIM2->CR1=0; //Upcounter, not started, no special options
|
||||
TIM2->CR2=0; //No special options
|
||||
TIM2->SMCR=0; //No external trigger
|
||||
TIM2->CNT=0; //Clear timer
|
||||
TIM2->PSC=(SystemCoreClock/miosix::AUX_TIMER_CLOCK)-1;
|
||||
TIM2->ARR=0xffff; //Count from zero to 0xffff
|
||||
TIM2->DIER=TIM_DIER_CC1IE; //Enable interrupt on compare
|
||||
TIM2->CCR1=0xffff; //This will be initialized later with setValue
|
||||
NVIC_SetPriority(TIM2_IRQn,3);//High priority for TIM2 (Max=0, min=15)
|
||||
NVIC_EnableIRQ(TIM2_IRQn);
|
||||
TIM2->CR1=TIM_CR1_CEN; //Start timer
|
||||
//This is very important: without this the prescaler shadow register may
|
||||
//not be updated
|
||||
TIM2->EGR=TIM_EGR_UG;
|
||||
}
|
||||
|
||||
int AuxiliaryTimer::IRQgetValue()
|
||||
{
|
||||
return static_cast<int>(TIM2->CNT);
|
||||
}
|
||||
|
||||
void AuxiliaryTimer::IRQsetValue(int x)
|
||||
{
|
||||
TIM2->CR1=0; //Stop timer since changing CNT or CCR1 while running fails
|
||||
TIM2->CNT=0;
|
||||
TIM2->CCR1=static_cast<unsigned short>(std::min(x,0xffff));
|
||||
TIM2->CR1=TIM_CR1_CEN; //Start timer again
|
||||
//The above instructions cause a spurious if not called within the
|
||||
//timer 2 IRQ (This happens if called from an SVC).
|
||||
//Clearing the pending bit prevents this spurious interrupt
|
||||
TIM2->SR=0;
|
||||
NVIC_ClearPendingIRQ(TIM2_IRQn);
|
||||
}
|
||||
#endif //SCHED_TYPE_CONTROL_BASED
|
||||
|
||||
} //namespace miosix_private
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010, 2011, 2012 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
//Miosix kernel
|
||||
|
||||
#ifndef PORTABILITY_IMPL_H
|
||||
#define PORTABILITY_IMPL_H
|
||||
|
||||
#include "interfaces/arch_registers.h"
|
||||
#include "config/miosix_settings.h"
|
||||
|
||||
/**
|
||||
* \addtogroup Drivers
|
||||
* \{
|
||||
*/
|
||||
|
||||
/*
|
||||
* This pointer is used by the kernel, and should not be used by end users.
|
||||
* this is a pointer to a location where to store the thread's registers during
|
||||
* context switch. It requires C linkage to be used inside asm statement.
|
||||
* Registers are saved in the following order:
|
||||
* *ctxsave+32 --> r11
|
||||
* *ctxsave+28 --> r10
|
||||
* *ctxsave+24 --> r9
|
||||
* *ctxsave+20 --> r8
|
||||
* *ctxsave+16 --> r7
|
||||
* *ctxsave+12 --> r6
|
||||
* *ctxsave+8 --> r5
|
||||
* *ctxsave+4 --> r4
|
||||
* *ctxsave+0 --> psp
|
||||
*/
|
||||
extern "C" {
|
||||
extern volatile unsigned int *ctxsave;
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \def saveContext()
|
||||
* Save context from an interrupt<br>
|
||||
* Must be the first line of an IRQ where a context switch can happen.
|
||||
* The IRQ must be "naked" to prevent the compiler from generating context save.
|
||||
*
|
||||
* A note on the dmb instruction, without it a race condition was observed
|
||||
* between pauseKernel() and IRQfindNextThread(). pauseKernel() uses an strex
|
||||
* instruction to store a value in the global variable kernel_running which is
|
||||
* tested by the context switch code in IRQfindNextThread(). Without the memory
|
||||
* barrier IRQfindNextThread() would occasionally read the previous value and
|
||||
* perform a context switch while the kernel was paused, leading to deadlock.
|
||||
* The failure was only observed within the exception_test() in the testsuite
|
||||
* running on the stm32f429zi_stm32f4discovery.
|
||||
*/
|
||||
#define saveContext() \
|
||||
{ \
|
||||
asm volatile("stmdb sp!, {lr} \n\t" /*save lr on MAIN stack*/ \
|
||||
"mrs r1, psp \n\t" /*get PROCESS stack pointer*/ \
|
||||
"ldr r0, =ctxsave \n\t" /*get current context*/ \
|
||||
"ldr r0, [r0] \n\t" \
|
||||
"stmia r0, {r1,r4-r11} \n\t" /*save PROCESS sp + r4-r11*/ \
|
||||
"dmb \n\t" \
|
||||
); \
|
||||
}
|
||||
|
||||
/**
|
||||
* \def restoreContext()
|
||||
* Restore context in an IRQ where saveContext() is used. Must be the last line
|
||||
* of an IRQ where a context switch can happen. The IRQ must be "naked" to
|
||||
* prevent the compiler from generating context restore.
|
||||
*/
|
||||
#define restoreContext() \
|
||||
{ \
|
||||
asm volatile("ldr r0, =ctxsave \n\t" /*get current context*/ \
|
||||
"ldr r0, [r0] \n\t" \
|
||||
"ldmia r0, {r1,r4-r11} \n\t" /*restore r4-r11 + r1=psp*/ \
|
||||
"msr psp, r1 \n\t" /*restore PROCESS sp*/ \
|
||||
"ldmia sp!, {pc} \n\t" /*return*/ \
|
||||
); \
|
||||
}
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
namespace miosix_private {
|
||||
|
||||
/**
|
||||
* \addtogroup Drivers
|
||||
* \{
|
||||
*/
|
||||
|
||||
inline void doYield()
|
||||
{
|
||||
asm volatile("movs r3, #0\n\t"
|
||||
"svc 0"
|
||||
:::"r3");
|
||||
}
|
||||
|
||||
inline void doDisableInterrupts()
|
||||
{
|
||||
// Documentation says __disable_irq() disables all interrupts with
|
||||
// configurable priority, so also SysTick and SVC.
|
||||
// No need to disable faults with __disable_fault_irq()
|
||||
__disable_irq();
|
||||
//The new fastDisableInterrupts/fastEnableInterrupts are inline, so there's
|
||||
//the need for a memory barrier to avoid aggressive reordering
|
||||
asm volatile("":::"memory");
|
||||
}
|
||||
|
||||
inline void doEnableInterrupts()
|
||||
{
|
||||
__enable_irq();
|
||||
//The new fastDisableInterrupts/fastEnableInterrupts are inline, so there's
|
||||
//the need for a memory barrier to avoid aggressive reordering
|
||||
asm volatile("":::"memory");
|
||||
}
|
||||
|
||||
inline bool checkAreInterruptsEnabled()
|
||||
{
|
||||
register int i;
|
||||
asm volatile("mrs %0, primask \n\t":"=r"(i));
|
||||
if(i!=0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
} //namespace miosix_private
|
||||
|
||||
#endif //PORTABILITY_IMPL_H
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* \addtogroup Settings
|
||||
* \{
|
||||
*/
|
||||
|
||||
/// \internal Size of vector to store registers during ctx switch (9*4=36Bytes)
|
||||
/// Only sp and r4-r11 are saved here, since r0-r3,r12,lr,pc,xPSR and
|
||||
/// old sp are saved by hardware on the process stack on Cortex CPUs.
|
||||
const unsigned char CTXSAVE_SIZE=9;
|
||||
|
||||
/// \internal some architectures save part of the context on their stack.
|
||||
/// This constant is used to increase the stack size by the size of context
|
||||
/// save frame. If zero, this architecture does not save anything on stack
|
||||
/// during context save. Size is in bytes, not words.
|
||||
/// MUST be divisible by 4.
|
||||
const unsigned int CTXSAVE_ON_STACK=32;
|
||||
|
||||
/// \internal stack alignment for this specific architecture
|
||||
const unsigned int CTXSAVE_STACK_ALIGNMENT=8;
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010-2020 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
//Miosix kernel
|
||||
|
||||
#include "interfaces/portability.h"
|
||||
#include "kernel/kernel.h"
|
||||
#include "kernel/error.h"
|
||||
#include "interfaces/bsp.h"
|
||||
#include "kernel/scheduler/scheduler.h"
|
||||
#include "kernel/scheduler/tick_interrupt.h"
|
||||
#include <algorithm>
|
||||
|
||||
extern void (* const __Vectors[])();
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* timer interrupt routine.
|
||||
* Since inside naked functions only assembler code is allowed, this function
|
||||
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||
* the implementation code in ISR_preempt()
|
||||
*/
|
||||
void SysTick_Handler() __attribute__((naked));
|
||||
void SysTick_Handler()
|
||||
{
|
||||
saveContext();
|
||||
//Call ISR_preempt(). Name is a C++ mangled name.
|
||||
asm volatile("bl _ZN14miosix_private11ISR_preemptEv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* software interrupt routine.
|
||||
* Since inside naked functions only assembler code is allowed, this function
|
||||
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||
* the implementation code in ISR_yield()
|
||||
*/
|
||||
void SVC_Handler() __attribute__((naked));
|
||||
void SVC_Handler()
|
||||
{
|
||||
saveContext();
|
||||
//Call ISR_yield(). Name is a C++ mangled name.
|
||||
asm volatile("bl _ZN14miosix_private9ISR_yieldEv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||
/**
|
||||
* \internal
|
||||
* Auxiliary timer interupt routine.
|
||||
* Used for variable lenght bursts in control based scheduler.
|
||||
* Since inside naked functions only assembler code is allowed, this function
|
||||
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||
* the implementation code in ISR_yield()
|
||||
*/
|
||||
void XXX_IRQHandler() __attribute__((naked));
|
||||
void XXX_IRQHandler()
|
||||
{
|
||||
saveContext();
|
||||
//Call ISR_auxTimer(). Name is a C++ mangled name.
|
||||
asm volatile("bl _ZN14miosix_private12ISR_auxTimerEv");
|
||||
restoreContext();
|
||||
}
|
||||
#endif //SCHED_TYPE_CONTROL_BASED
|
||||
|
||||
namespace miosix_private {
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Called by the timer interrupt, preempt to next thread
|
||||
* Declared noinline to avoid the compiler trying to inline it into the caller,
|
||||
* which would violate the requirement on naked functions. Function is not
|
||||
* static because otherwise the compiler optimizes it out...
|
||||
*/
|
||||
void ISR_preempt() __attribute__((noinline));
|
||||
void ISR_preempt()
|
||||
{
|
||||
IRQstackOverflowCheck();
|
||||
miosix::IRQtickInterrupt();
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Called by the software interrupt, yield to next thread
|
||||
* Declared noinline to avoid the compiler trying to inline it into the caller,
|
||||
* which would violate the requirement on naked functions. Function is not
|
||||
* static because otherwise the compiler optimizes it out...
|
||||
*/
|
||||
void ISR_yield() __attribute__((noinline));
|
||||
void ISR_yield()
|
||||
{
|
||||
IRQstackOverflowCheck();
|
||||
miosix::Scheduler::IRQfindNextThread();
|
||||
}
|
||||
|
||||
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||
/**
|
||||
* \internal
|
||||
* Auxiliary timer interupt routine.
|
||||
* Used for variable lenght bursts in control based scheduler.
|
||||
*/
|
||||
void ISR_auxTimer() __attribute__((noinline));
|
||||
void ISR_auxTimer()
|
||||
{
|
||||
IRQstackOverflowCheck();
|
||||
miosix::Scheduler::IRQfindNextThread();//If the kernel is running, preempt
|
||||
if(miosix::kernel_running!=0) miosix::tick_skew=true;
|
||||
}
|
||||
#endif //SCHED_TYPE_CONTROL_BASED
|
||||
|
||||
void IRQstackOverflowCheck()
|
||||
{
|
||||
const unsigned int watermarkSize=miosix::WATERMARK_LEN/sizeof(unsigned int);
|
||||
for(unsigned int i=0;i<watermarkSize;i++)
|
||||
{
|
||||
if(miosix::cur->watermark[i]!=miosix::WATERMARK_FILL)
|
||||
miosix::errorHandler(miosix::STACK_OVERFLOW);
|
||||
}
|
||||
if(miosix::cur->ctxsave[0] < reinterpret_cast<unsigned int>(
|
||||
miosix::cur->watermark+watermarkSize))
|
||||
miosix::errorHandler(miosix::STACK_OVERFLOW);
|
||||
}
|
||||
|
||||
void IRQsystemReboot()
|
||||
{
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp,
|
||||
void *argv)
|
||||
{
|
||||
unsigned int *stackPtr=sp;
|
||||
stackPtr--; //Stack is full descending, so decrement first
|
||||
*stackPtr=0x01000000; stackPtr--; //--> xPSR
|
||||
*stackPtr=reinterpret_cast<unsigned long>(
|
||||
&miosix::Thread::threadLauncher); stackPtr--; //--> pc
|
||||
*stackPtr=0xffffffff; stackPtr--; //--> lr
|
||||
*stackPtr=0; stackPtr--; //--> r12
|
||||
*stackPtr=0; stackPtr--; //--> r3
|
||||
*stackPtr=0; stackPtr--; //--> r2
|
||||
*stackPtr=reinterpret_cast<unsigned long >(argv); stackPtr--; //--> r1
|
||||
*stackPtr=reinterpret_cast<unsigned long >(pc); //--> r0
|
||||
|
||||
ctxsave[0]=reinterpret_cast<unsigned long>(stackPtr); //--> psp
|
||||
//leaving the content of r4-r11 uninitialized
|
||||
}
|
||||
|
||||
void IRQportableStartKernel()
|
||||
{
|
||||
//NOTE: the SAM-BA bootloader does not relocate the vector table offset,
|
||||
//so any interrupt would call the SAM-BA IRQ handler, not the application
|
||||
//ones.
|
||||
SCB->VTOR = reinterpret_cast<unsigned int>(&__Vectors);
|
||||
|
||||
//Enable fault handlers
|
||||
SCB->SHCSR |= SCB_SHCSR_USGFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk
|
||||
| SCB_SHCSR_MEMFAULTENA_Msk;
|
||||
//Enable traps for division by zero. Trap for unaligned memory access
|
||||
//was removed as gcc starting from 4.7.2 generates unaligned accesses by
|
||||
//default (https://www.gnu.org/software/gcc/gcc-4.7/changes.html)
|
||||
SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk;
|
||||
NVIC_SetPriorityGrouping(7);//This should disable interrupt nesting
|
||||
NVIC_SetPriority(SVCall_IRQn,3);//High priority for SVC (Max=0, min=15)
|
||||
NVIC_SetPriority(SysTick_IRQn,3);//High priority for SysTick (Max=0, min=15)
|
||||
SysTick->LOAD=SystemCoreClock/miosix::TICK_FREQ-1;
|
||||
//Start SysTick, set to generate interrupts
|
||||
SysTick->CTRL=SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk |
|
||||
SysTick_CTRL_CLKSOURCE_Msk;
|
||||
|
||||
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||
AuxiliaryTimer::IRQinit();
|
||||
#endif //SCHED_TYPE_CONTROL_BASED
|
||||
|
||||
//create a temporary space to save current registers. This data is useless
|
||||
//since there's no way to stop the sheduler, but we need to save it anyway.
|
||||
unsigned int s_ctxsave[miosix::CTXSAVE_SIZE];
|
||||
ctxsave=s_ctxsave;//make global ctxsave point to it
|
||||
//Note, we can't use enableInterrupts() now since the call is not mathced
|
||||
//by a call to disableInterrupts()
|
||||
__enable_fault_irq();
|
||||
__enable_irq();
|
||||
miosix::Thread::yield();
|
||||
//Never reaches here
|
||||
}
|
||||
|
||||
void sleepCpu()
|
||||
{
|
||||
__WFI();
|
||||
}
|
||||
|
||||
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||
#error "AUX_TIMER not yet implemented"
|
||||
void AuxiliaryTimer::IRQinit()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int AuxiliaryTimer::IRQgetValue()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AuxiliaryTimer::IRQsetValue(int x)
|
||||
{
|
||||
|
||||
}
|
||||
#endif //SCHED_TYPE_CONTROL_BASED
|
||||
|
||||
} //namespace miosix_private
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010-2020 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "interfaces/arch_registers.h"
|
||||
#include "interfaces/portability.h"
|
||||
#include "config/miosix_settings.h"
|
||||
|
||||
/**
|
||||
* \addtogroup Drivers
|
||||
* \{
|
||||
*/
|
||||
|
||||
/*
|
||||
* This pointer is used by the kernel, and should not be used by end users.
|
||||
* this is a pointer to a location where to store the thread's registers during
|
||||
* context switch. It requires C linkage to be used inside asm statement.
|
||||
* Registers are saved in the following order:
|
||||
* *ctxsave+32 --> r11
|
||||
* *ctxsave+28 --> r10
|
||||
* *ctxsave+24 --> r9
|
||||
* *ctxsave+20 --> r8
|
||||
* *ctxsave+16 --> r7
|
||||
* *ctxsave+12 --> r6
|
||||
* *ctxsave+8 --> r5
|
||||
* *ctxsave+4 --> r4
|
||||
* *ctxsave+0 --> psp
|
||||
*/
|
||||
extern "C" {
|
||||
extern volatile unsigned int *ctxsave;
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \def saveContext()
|
||||
* Save context from an interrupt<br>
|
||||
* Must be the first line of an IRQ where a context switch can happen.
|
||||
* The IRQ must be "naked" to prevent the compiler from generating context save.
|
||||
*
|
||||
* A note on the dmb instruction, without it a race condition was observed
|
||||
* between pauseKernel() and IRQfindNextThread(). pauseKernel() uses an strex
|
||||
* instruction to store a value in the global variable kernel_running which is
|
||||
* tested by the context switch code in IRQfindNextThread(). Without the memory
|
||||
* barrier IRQfindNextThread() would occasionally read the previous value and
|
||||
* perform a context switch while the kernel was paused, leading to deadlock.
|
||||
* The failure was only observed within the exception_test() in the testsuite
|
||||
* running on the stm32f429zi_stm32f4discovery.
|
||||
*/
|
||||
#define saveContext() \
|
||||
{ \
|
||||
asm volatile("stmdb sp!, {lr} \n\t" /*save lr on MAIN stack*/ \
|
||||
"mrs r1, psp \n\t" /*get PROCESS stack pointer*/ \
|
||||
"ldr r0, =ctxsave \n\t" /*get current context*/ \
|
||||
"ldr r0, [r0] \n\t" \
|
||||
"stmia r0, {r1,r4-r11} \n\t" /*save PROCESS sp + r4-r11*/ \
|
||||
"dmb \n\t" \
|
||||
); \
|
||||
}
|
||||
|
||||
/**
|
||||
* \def restoreContext()
|
||||
* Restore context in an IRQ where saveContext() is used. Must be the last line
|
||||
* of an IRQ where a context switch can happen. The IRQ must be "naked" to
|
||||
* prevent the compiler from generating context restore.
|
||||
*/
|
||||
#define restoreContext() \
|
||||
{ \
|
||||
asm volatile("ldr r0, =ctxsave \n\t" /*get current context*/ \
|
||||
"ldr r0, [r0] \n\t" \
|
||||
"ldmia r0, {r1,r4-r11} \n\t" /*restore r4-r11 + r1=psp*/ \
|
||||
"msr psp, r1 \n\t" /*restore PROCESS sp*/ \
|
||||
"ldmia sp!, {pc} \n\t" /*return*/ \
|
||||
); \
|
||||
}
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
namespace miosix_private {
|
||||
|
||||
/**
|
||||
* \addtogroup Drivers
|
||||
* \{
|
||||
*/
|
||||
|
||||
inline void doYield()
|
||||
{
|
||||
asm volatile("movs r3, #0\n\t"
|
||||
"svc 0"
|
||||
:::"r3");
|
||||
}
|
||||
|
||||
|
||||
inline void doDisableInterrupts()
|
||||
{
|
||||
// Documentation says __disable_irq() disables all interrupts with
|
||||
// configurable priority, so also SysTick and SVC.
|
||||
// No need to disable faults with __disable_fault_irq()
|
||||
__disable_irq();
|
||||
//The new fastDisableInterrupts/fastEnableInterrupts are inline, so there's
|
||||
//the need for a memory barrier to avoid aggressive reordering
|
||||
asm volatile("":::"memory");
|
||||
}
|
||||
|
||||
inline void doEnableInterrupts()
|
||||
{
|
||||
__enable_irq();
|
||||
//The new fastDisableInterrupts/fastEnableInterrupts are inline, so there's
|
||||
//the need for a memory barrier to avoid aggressive reordering
|
||||
asm volatile("":::"memory");
|
||||
}
|
||||
|
||||
inline bool checkAreInterruptsEnabled()
|
||||
{
|
||||
register int i;
|
||||
asm volatile("mrs %0, primask \n\t":"=r"(i));
|
||||
if(i!=0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
} //namespace miosix_private
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2012 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ARCH_SETTINGS_H
|
||||
#define ARCH_SETTINGS_H
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* \addtogroup Settings
|
||||
* \{
|
||||
*/
|
||||
|
||||
/// \internal size of vector to store registers during ctx switch
|
||||
/// ((10+16)*4=104Bytes). Only sp, r4-r11, EXC_RETURN and s16-s31 are saved
|
||||
/// here, since r0-r3,r12,lr,pc,xPSR, old sp and s0-s15,fpscr are saved by
|
||||
/// hardware on the process stack on Cortex M4F CPUs. EXC_RETURN, or the lr,
|
||||
/// value to use to return from the exception is necessary to know if the
|
||||
/// thread has used fp regs, as an extension specific to Cortex-M4F CPUs.
|
||||
const unsigned char CTXSAVE_SIZE=10+16;
|
||||
|
||||
/// \internal some architectures save part of the context on their stack.
|
||||
/// ((8+17)*4=100Bytes). This constant is used to increase the stack size by
|
||||
/// the size of context save frame. If zero, this architecture does not save
|
||||
/// anything on stack during context save. Size is in bytes, not words.
|
||||
/// 8 registers=r0-r3,r12,lr,pc,xPSR
|
||||
/// 17 registers=s0-s15,fpscr
|
||||
/// MUST be divisible by 4.
|
||||
const unsigned int CTXSAVE_ON_STACK=(8+17)*4;
|
||||
|
||||
/// \internal stack alignment for this specific architecture
|
||||
const unsigned int CTXSAVE_STACK_ALIGNMENT=8;
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif /* ARCH_SETTINGS_H */
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010, 2011, 2012 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
//Miosix kernel
|
||||
|
||||
#include "interfaces/portability.h"
|
||||
#include "kernel/kernel.h"
|
||||
#include "kernel/error.h"
|
||||
#include "interfaces/bsp.h"
|
||||
#include "kernel/scheduler/scheduler.h"
|
||||
#include "kernel/scheduler/tick_interrupt.h"
|
||||
#include "core/interrupts.h"
|
||||
#include "kernel/process.h"
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* timer interrupt routine.
|
||||
* Since inside naked functions only assembler code is allowed, this function
|
||||
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||
* the implementation code in ISR_preempt()
|
||||
*/
|
||||
void SysTick_Handler() __attribute__((naked));
|
||||
void SysTick_Handler()
|
||||
{
|
||||
saveContext();
|
||||
//Call ISR_preempt(). Name is a C++ mangled name.
|
||||
asm volatile("bl _ZN14miosix_private11ISR_preemptEv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* software interrupt routine.
|
||||
* Since inside naked functions only assembler code is allowed, this function
|
||||
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||
* the implementation code in ISR_yield()
|
||||
*/
|
||||
void SVC_Handler() __attribute__((naked));
|
||||
void SVC_Handler()
|
||||
{
|
||||
saveContext();
|
||||
//Call ISR_yield(). Name is a C++ mangled name.
|
||||
asm volatile("bl _ZN14miosix_private9ISR_yieldEv");
|
||||
restoreContext();
|
||||
}
|
||||
|
||||
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||
/**
|
||||
* \internal
|
||||
* Auxiliary timer interupt routine.
|
||||
* Used for variable lenght bursts in control based scheduler.
|
||||
* Since inside naked functions only assembler code is allowed, this function
|
||||
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||
* the implementation code in ISR_yield()
|
||||
*/
|
||||
void TIM3_IRQHandler() __attribute__((naked));
|
||||
void TIM3_IRQHandler()
|
||||
{
|
||||
saveContext();
|
||||
//Call ISR_auxTimer(). Name is a C++ mangled name.
|
||||
asm volatile("bl _ZN14miosix_private12ISR_auxTimerEv");
|
||||
restoreContext();
|
||||
}
|
||||
#endif //SCHED_TYPE_CONTROL_BASED
|
||||
|
||||
namespace miosix_private {
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Called by the timer interrupt, preempt to next thread
|
||||
* Declared noinline to avoid the compiler trying to inline it into the caller,
|
||||
* which would violate the requirement on naked functions. Function is not
|
||||
* static because otherwise the compiler optimizes it out...
|
||||
*/
|
||||
void ISR_preempt() __attribute__((noinline));
|
||||
void ISR_preempt()
|
||||
{
|
||||
IRQstackOverflowCheck();
|
||||
miosix::IRQtickInterrupt();
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Called by the software interrupt, yield to next thread
|
||||
* Declared noinline to avoid the compiler trying to inline it into the caller,
|
||||
* which would violate the requirement on naked functions. Function is not
|
||||
* static because otherwise the compiler optimizes it out...
|
||||
*/
|
||||
void ISR_yield() __attribute__((noinline));
|
||||
void ISR_yield()
|
||||
{
|
||||
IRQstackOverflowCheck();
|
||||
miosix::Scheduler::IRQfindNextThread();
|
||||
}
|
||||
|
||||
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||
/**
|
||||
* \internal
|
||||
* Auxiliary timer interupt routine.
|
||||
* Used for variable lenght bursts in control based scheduler.
|
||||
*/
|
||||
void ISR_auxTimer() __attribute__((noinline));
|
||||
void ISR_auxTimer()
|
||||
{
|
||||
IRQstackOverflowCheck();
|
||||
miosix::Scheduler::IRQfindNextThread();//If the kernel is running, preempt
|
||||
if(miosix::kernel_running!=0) miosix::tick_skew=true;
|
||||
TIM3->SR=0;
|
||||
}
|
||||
#endif //SCHED_TYPE_CONTROL_BASED
|
||||
|
||||
void IRQstackOverflowCheck()
|
||||
{
|
||||
const unsigned int watermarkSize=miosix::WATERMARK_LEN/sizeof(unsigned int);
|
||||
for(unsigned int i=0;i<watermarkSize;i++)
|
||||
{
|
||||
if(miosix::cur->watermark[i]!=miosix::WATERMARK_FILL)
|
||||
miosix::errorHandler(miosix::STACK_OVERFLOW);
|
||||
}
|
||||
if(miosix::cur->ctxsave[0] < reinterpret_cast<unsigned int>(
|
||||
miosix::cur->watermark+watermarkSize))
|
||||
miosix::errorHandler(miosix::STACK_OVERFLOW);
|
||||
}
|
||||
|
||||
void IRQsystemReboot()
|
||||
{
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp,
|
||||
void *argv)
|
||||
{
|
||||
unsigned int *stackPtr=sp;
|
||||
stackPtr--; //Stack is full descending, so decrement first
|
||||
*stackPtr=0x01000000; stackPtr--; //--> xPSR
|
||||
*stackPtr=reinterpret_cast<unsigned long>(
|
||||
&miosix::Thread::threadLauncher); stackPtr--; //--> pc
|
||||
*stackPtr=0xffffffff; stackPtr--; //--> lr
|
||||
*stackPtr=0; stackPtr--; //--> r12
|
||||
*stackPtr=0; stackPtr--; //--> r3
|
||||
*stackPtr=0; stackPtr--; //--> r2
|
||||
*stackPtr=reinterpret_cast<unsigned long >(argv); stackPtr--; //--> r1
|
||||
*stackPtr=reinterpret_cast<unsigned long >(pc); //--> r0
|
||||
|
||||
ctxsave[0]=reinterpret_cast<unsigned long>(stackPtr); //--> psp
|
||||
//leaving the content of r4-r11 uninitialized
|
||||
ctxsave[9]=0xfffffffd; //EXC_RETURN=thread mode, use psp, no floating ops
|
||||
//leaving the content of s16-s31 uninitialized
|
||||
}
|
||||
|
||||
void IRQportableStartKernel()
|
||||
{
|
||||
//Enable fault handlers
|
||||
SCB->SHCSR |= SCB_SHCSR_USGFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk
|
||||
| SCB_SHCSR_MEMFAULTENA_Msk;
|
||||
//Enable traps for division by zero. Trap for unaligned memory access
|
||||
//was removed as gcc starting from 4.7.2 generates unaligned accesses by
|
||||
//default (https://www.gnu.org/software/gcc/gcc-4.7/changes.html)
|
||||
SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk;
|
||||
NVIC_SetPriorityGrouping(7);//This should disable interrupt nesting
|
||||
NVIC_SetPriority(SVCall_IRQn,3);//High priority for SVC (Max=0, min=15)
|
||||
NVIC_SetPriority(SysTick_IRQn,3);//High priority for SysTick (Max=0, min=15)
|
||||
NVIC_SetPriority(MemoryManagement_IRQn,2);//Higher priority for MemoryManagement (Max=0, min=15)
|
||||
SysTick->LOAD=SystemCoreClock/miosix::TICK_FREQ;
|
||||
//Start SysTick, set to generate interrupts
|
||||
SysTick->CTRL=SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk |
|
||||
SysTick_CTRL_CLKSOURCE_Msk;
|
||||
|
||||
//create a temporary space to save current registers. This data is useless
|
||||
//since there's no way to stop the sheduler, but we need to save it anyway.
|
||||
unsigned int s_ctxsave[miosix::CTXSAVE_SIZE];
|
||||
ctxsave=s_ctxsave;//make global ctxsave point to it
|
||||
//Note, we can't use enableInterrupts() now since the call is not mathced
|
||||
//by a call to disableInterrupts()
|
||||
__enable_fault_irq();
|
||||
__enable_irq();
|
||||
miosix::Thread::yield();
|
||||
//Never reaches here
|
||||
}
|
||||
|
||||
void sleepCpu()
|
||||
{
|
||||
__WFI();
|
||||
}
|
||||
|
||||
} //namespace miosix_private
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010, 2011, 2012 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
//Miosix kernel
|
||||
|
||||
#ifndef PORTABILITY_IMPL_H
|
||||
#define PORTABILITY_IMPL_H
|
||||
|
||||
#include "interfaces/arch_registers.h"
|
||||
#include "interfaces/portability.h"
|
||||
#include "config/miosix_settings.h"
|
||||
|
||||
/**
|
||||
* \addtogroup Drivers
|
||||
* \{
|
||||
*/
|
||||
|
||||
/*
|
||||
* This pointer is used by the kernel, and should not be used by end users.
|
||||
* this is a pointer to a location where to store the thread's registers during
|
||||
* context switch. It requires C linkage to be used inside asm statement.
|
||||
* Registers are saved in the following order:
|
||||
* *ctxsave+100 --> s31
|
||||
* ...
|
||||
* *ctxsave+40 --> s16
|
||||
* *ctxsave+36 --> lr (contains EXC_RETURN whose bit #4 tells if fpu is used)
|
||||
* *ctxsave+32 --> r11
|
||||
* *ctxsave+28 --> r10
|
||||
* *ctxsave+24 --> r9
|
||||
* *ctxsave+20 --> r8
|
||||
* *ctxsave+16 --> r7
|
||||
* *ctxsave+12 --> r6
|
||||
* *ctxsave+8 --> r5
|
||||
* *ctxsave+4 --> r4
|
||||
* *ctxsave+0 --> psp
|
||||
*/
|
||||
extern "C" {
|
||||
extern volatile unsigned int *ctxsave;
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \def saveContext()
|
||||
* Save context from an interrupt<br>
|
||||
* Must be the first line of an IRQ where a context switch can happen.
|
||||
* The IRQ must be "naked" to prevent the compiler from generating context save.
|
||||
*
|
||||
* A note on the dmb instruction, without it a race condition was observed
|
||||
* between pauseKernel() and IRQfindNextThread(). pauseKernel() uses an strex
|
||||
* instruction to store a value in the global variable kernel_running which is
|
||||
* tested by the context switch code in IRQfindNextThread(). Without the memory
|
||||
* barrier IRQfindNextThread() would occasionally read the previous value and
|
||||
* perform a context switch while the kernel was paused, leading to deadlock.
|
||||
* The failure was only observed within the exception_test() in the testsuite
|
||||
* running on the stm32f429zi_stm32f4discovery.
|
||||
*/
|
||||
#define saveContext() \
|
||||
{ \
|
||||
asm volatile(" mrs r1, psp \n"/*get PROCESS stack ptr */ \
|
||||
" ldr r0, =ctxsave \n"/*get current context */ \
|
||||
" ldr r0, [r0] \n" \
|
||||
" stmia r0!, {r1,r4-r11,lr} \n"/*save r1(psp),r4-r11,lr */ \
|
||||
" lsls r2, lr, #27 \n"/*check if bit #4 is set */ \
|
||||
" bmi 0f \n" \
|
||||
" vstmia.32 r0, {s16-s31} \n"/*save s16-s31 if we need*/ \
|
||||
"0: dmb \n" \
|
||||
); \
|
||||
}
|
||||
|
||||
/**
|
||||
* \def restoreContext()
|
||||
* Restore context in an IRQ where saveContext() is used. Must be the last line
|
||||
* of an IRQ where a context switch can happen. The IRQ must be "naked" to
|
||||
* prevent the compiler from generating context restore.
|
||||
*/
|
||||
#define restoreContext() \
|
||||
{ \
|
||||
asm volatile(" ldr r0, =ctxsave \n"/*get current context */ \
|
||||
" ldr r0, [r0] \n" \
|
||||
" ldmia r0!, {r1,r4-r11,lr} \n"/*load r1(psp),r4-r11,lr */ \
|
||||
" lsls r2, lr, #27 \n"/*check if bit #4 is set */ \
|
||||
" bmi 0f \n" \
|
||||
" vldmia.32 r0, {s16-s31} \n"/*restore s16-s31 if need*/ \
|
||||
"0: msr psp, r1 \n"/*restore PROCESS sp*/ \
|
||||
" bx lr \n"/*return*/ \
|
||||
); \
|
||||
}
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
namespace miosix_private {
|
||||
|
||||
/**
|
||||
* \addtogroup Drivers
|
||||
* \{
|
||||
*/
|
||||
|
||||
inline void doYield()
|
||||
{
|
||||
asm volatile("movs r3, #0\n\t"
|
||||
"svc 0"
|
||||
:::"r3");
|
||||
}
|
||||
|
||||
inline void doDisableInterrupts()
|
||||
{
|
||||
// Documentation says __disable_irq() disables all interrupts with
|
||||
// configurable priority, so also SysTick and SVC.
|
||||
// No need to disable faults with __disable_fault_irq()
|
||||
__disable_irq();
|
||||
//The new fastDisableInterrupts/fastEnableInterrupts are inline, so there's
|
||||
//the need for a memory barrier to avoid aggressive reordering
|
||||
asm volatile("":::"memory");
|
||||
}
|
||||
|
||||
inline void doEnableInterrupts()
|
||||
{
|
||||
__enable_irq();
|
||||
//The new fastDisableInterrupts/fastEnableInterrupts are inline, so there's
|
||||
//the need for a memory barrier to avoid aggressive reordering
|
||||
asm volatile("":::"memory");
|
||||
}
|
||||
|
||||
inline bool checkAreInterruptsEnabled()
|
||||
{
|
||||
register int i;
|
||||
asm volatile("mrs %0, primask \n\t":"=r"(i));
|
||||
if(i!=0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
} //namespace miosix_private
|
||||
|
||||
#endif //PORTABILITY_IMPL_H
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2012, 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef BOARD_SETTINGS_H
|
||||
#define BOARD_SETTINGS_H
|
||||
|
||||
#include "util/version.h"
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Versioning for board_settings.h for out of git tree projects
|
||||
*/
|
||||
#define BOARD_SETTINGS_VERSION 100
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* \addtogroup Settings
|
||||
* \{
|
||||
*/
|
||||
|
||||
/// Size of stack for main().
|
||||
/// The C standard library is stack-heavy (iprintf requires 1KB) but the
|
||||
/// STM32F407VG only has 192KB of RAM so there is room for a big 4K stack.
|
||||
const unsigned int MAIN_STACK_SIZE=4*1024;
|
||||
|
||||
/// Frequency of tick (in Hz). The frequency of the STM32F100RB timer in the
|
||||
/// stm32vldiscovery board can be divided by 1000. This allows to use a 1KHz
|
||||
/// tick and the minimun Thread::sleep value is 1ms
|
||||
/// For the priority scheduler this is also the context switch frequency
|
||||
const unsigned int TICK_FREQ=1000;
|
||||
|
||||
///\internal Aux timer run @ 100KHz
|
||||
///Note that since the timer is only 16 bits this imposes a limit on the
|
||||
///burst measurement of 655ms. If due to a pause_kernel() or
|
||||
///disable_interrupts() section a thread runs for more than that time, a wrong
|
||||
///burst value will be measured
|
||||
const unsigned int AUX_TIMER_CLOCK=100000;
|
||||
const unsigned int AUX_TIMER_MAX=0xffff; ///<\internal Aux timer is 16 bits
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif /* BOARD_SETTINGS_H */
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 *
|
||||
* 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef MIOSIX_SETTINGS_H
|
||||
#define MIOSIX_SETTINGS_H
|
||||
|
||||
// Before you can compile the kernel you have to configure it by editing this
|
||||
// file. After that, comment out this line to disable the reminder error.
|
||||
// The PARSING_FROM_IDE is because Netbeans gets confused by this, it is never
|
||||
// defined when compiling the code.
|
||||
#ifndef PARSING_FROM_IDE
|
||||
// #error This error is a reminder that you have not edited miosix_settings.h yet.
|
||||
#endif //PARSING_FROM_IDE
|
||||
|
||||
/**
|
||||
* \file miosix_settings.h
|
||||
* NOTE: this file contains ONLY configuration options that are not dependent
|
||||
* on architecture specific details. The other options are in the following
|
||||
* files which are included here:
|
||||
* miosix/arch/architecture name/common/arch_settings.h
|
||||
* miosix/arch/architecture name/board name/board_settings.h
|
||||
*/
|
||||
#include "arch_settings.h"
|
||||
#include "board_settings.h"
|
||||
#include "util/version.h"
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Versioning for miosix_settings.h for out of git tree projects
|
||||
*/
|
||||
#define MIOSIX_SETTINGS_VERSION 100
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* \addtogroup Settings
|
||||
* \{
|
||||
*/
|
||||
|
||||
//
|
||||
// Scheduler options
|
||||
//
|
||||
|
||||
/// \def SCHED_TYPE_PRIORITY
|
||||
/// If uncommented selects the priority scheduler
|
||||
/// \def SCHED_TYPE_CONTROL_BASED
|
||||
/// If uncommented selects the control based scheduler
|
||||
/// \def SCHED_TYPE_EDF
|
||||
///If uncommented selects the EDF scheduler
|
||||
//Uncomment only *one* of those
|
||||
|
||||
#define SCHED_TYPE_PRIORITY
|
||||
//#define SCHED_TYPE_CONTROL_BASED
|
||||
//#define SCHED_TYPE_EDF
|
||||
|
||||
//
|
||||
// Filesystem options
|
||||
//
|
||||
|
||||
/// \def WITH_FILESYSTEM
|
||||
/// Allows to enable/disable filesystem support to save code size
|
||||
/// By default it is defined (filesystem support is enabled)
|
||||
// #define WITH_FILESYSTEM
|
||||
|
||||
/// \def WITH_DEVFS
|
||||
/// Allows to enable/disable DevFs support to save code size
|
||||
/// By default it is defined (DevFs is enabled)
|
||||
// #define WITH_DEVFS
|
||||
|
||||
/// \def SYNC_AFTER_WRITE
|
||||
/// Increases filesystem write robustness. After each write operation the
|
||||
/// filesystem is synced so that a power failure happens data is not lost
|
||||
/// (unless power failure happens exactly between the write and the sync)
|
||||
/// Unfortunately write latency and throughput becomes twice as worse
|
||||
/// By default it is defined (slow but safe)
|
||||
// #define SYNC_AFTER_WRITE
|
||||
|
||||
/// Maximum number of open files. Trying to open more will fail.
|
||||
/// Cannot be lower than 3, as the first three are stdin, stdout, stderr
|
||||
const unsigned char MAX_OPEN_FILES=8;
|
||||
|
||||
/// \def WITH_PROCESSES
|
||||
/// If uncommented enables support for processes as well as threads.
|
||||
/// This enables the dynamic loader to load elf programs, the extended system
|
||||
/// call service and, if the hardware supports it, the MPU to provide memory
|
||||
/// isolation of processes
|
||||
//#define WITH_PROCESSES
|
||||
|
||||
#if defined(WITH_PROCESSES) && defined(__NO_EXCEPTIONS)
|
||||
#error Processes require C++ exception support
|
||||
#endif //defined(WITH_PROCESSES) && defined(__NO_EXCEPTIONS)
|
||||
|
||||
#if defined(WITH_PROCESSES) && !defined(WITH_FILESYSTEM)
|
||||
#error Processes require filesystem support
|
||||
#endif //defined(WITH_PROCESSES) && !defined(WITH_FILESYSTEM)
|
||||
|
||||
#if defined(WITH_PROCESSES) && !defined(WITH_DEVFS)
|
||||
#error Processes require devfs support
|
||||
#endif //defined(WITH_PROCESSES) && !defined(WITH_DEVFS)
|
||||
|
||||
//
|
||||
// C/C++ standard library I/O (stdin, stdout and stderr related)
|
||||
//
|
||||
|
||||
/// \def WITH_BOOTLOG
|
||||
/// Uncomment to print bootlogs on stdout.
|
||||
/// By default it is defined (bootlogs are printed)
|
||||
// #define WITH_BOOTLOG
|
||||
|
||||
/// \def WITH_ERRLOG
|
||||
/// Uncomment for debug information on stdout.
|
||||
/// By default it is defined (error information is printed)
|
||||
// #define WITH_ERRLOG
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Kernel related options (stack sizes, priorities)
|
||||
//
|
||||
|
||||
/**
|
||||
* \def JTAG_DISABLE_SLEEP
|
||||
* JTAG debuggers lose communication with the device if it enters sleep
|
||||
* mode, so to use debugging it is necessary to disable sleep in the idle thread.
|
||||
* By default it is not defined (idle thread calls sleep).
|
||||
*/
|
||||
//#define JTAG_DISABLE_SLEEP
|
||||
|
||||
/// Minimum stack size (MUST be divisible by 4)
|
||||
const unsigned int STACK_MIN=256;
|
||||
|
||||
/// \internal Size of idle thread stack.
|
||||
/// Should be >=STACK_MIN (MUST be divisible by 4)
|
||||
const unsigned int STACK_IDLE=256;
|
||||
|
||||
/// Default stack size for pthread_create.
|
||||
/// The chosen value is enough to call C standard library functions
|
||||
/// such as printf/fopen which are stack-heavy
|
||||
const unsigned int STACK_DEFAULT_FOR_PTHREAD=2048;
|
||||
|
||||
/// Maximum size of the RAM image of a process. If a program requires more
|
||||
/// the kernel will not run it (MUST be divisible by 4)
|
||||
const unsigned int MAX_PROCESS_IMAGE_SIZE=64*1024;
|
||||
|
||||
/// Minimum size of the stack for a process. If a program specifies a lower
|
||||
/// size the kernel will not run it (MUST be divisible by 4)
|
||||
const unsigned int MIN_PROCESS_STACK_SIZE=STACK_MIN;
|
||||
|
||||
/// Every userspace thread has two stacks, one for when it is running in
|
||||
/// userspace and one for when it is running in kernelspace (that is, while it
|
||||
/// is executing system calls). This is the size of the stack for when the
|
||||
/// thread is running in kernelspace (MUST be divisible by 4)
|
||||
const unsigned int SYSTEM_MODE_PROCESS_STACK_SIZE=2*1024;
|
||||
|
||||
/// Number of priorities (MUST be >1)
|
||||
/// PRIORITY_MAX-1 is the highest priority, 0 is the lowest. -1 is reserved as
|
||||
/// the priority of the idle thread.
|
||||
/// The meaning of a thread's priority depends on the chosen scheduler.
|
||||
#ifdef SCHED_TYPE_PRIORITY
|
||||
//Can be modified, but a high value makes context switches more expensive
|
||||
const short int PRIORITY_MAX=4;
|
||||
#elif defined(SCHED_TYPE_CONTROL_BASED)
|
||||
//Don't touch, the limit is due to the fixed point implementation
|
||||
//It's not needed for if floating point is selected, but kept for consistency
|
||||
const short int PRIORITY_MAX=64;
|
||||
#else //SCHED_TYPE_EDF
|
||||
//Doesn't exist for this kind of scheduler
|
||||
#endif
|
||||
|
||||
/// Priority of main()
|
||||
/// The meaning of a thread's priority depends on the chosen scheduler.
|
||||
const unsigned char MAIN_PRIORITY=1;
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Other low level kernel options. There is usually no need to modify these.
|
||||
//
|
||||
|
||||
/// \internal Length of wartermark (in bytes) to check stack overflow.
|
||||
/// MUST be divisible by 4 and can also be zero.
|
||||
/// A high value increases context switch time.
|
||||
const unsigned int WATERMARK_LEN=16;
|
||||
|
||||
/// \internal Used to fill watermark
|
||||
const unsigned int WATERMARK_FILL=0xaaaaaaaa;
|
||||
|
||||
/// \internal Used to fill stack (for checking stack usage)
|
||||
const unsigned int STACK_FILL=0xbbbbbbbb;
|
||||
|
||||
// Compiler version checks
|
||||
#if _MIOSIX_GCC_PATCH_MAJOR > 3
|
||||
#warning "You are using a too new compiler, which may not be supported"
|
||||
#elif _MIOSIX_GCC_PATCH_MAJOR == 2
|
||||
#error "The compiler you are using has known incomplete patches and is not supported. Get the latest one from https://miosix.org/wiki/index.php?title=Miosix_Toolchain"
|
||||
#elif _MIOSIX_GCC_PATCH_VERSION == 1
|
||||
#warning "You are using an unsupported compiler. Get the latest one from https://miosix.org/wiki/index.php?title=Miosix_Toolchain"
|
||||
#endif
|
||||
#if !defined(_MIOSIX_GCC_PATCH_MAJOR) && \
|
||||
(!defined(_MIOSIX_GCC_PATCH_VERSION) || _MIOSIX_GCC_PATCH_VERSION < 1)
|
||||
#error "You are using an unsupported compiler. Get the latest one from https://miosix.org/wiki/index.php?title=Miosix_Toolchain"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //MIOSIX_SETTINGS_H
|
||||
|
|
@ -0,0 +1,243 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2012, 2013, 2014, 2105, 2106 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef CALLBACK_H
|
||||
#define CALLBACK_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* This class is to extract from Callback code that
|
||||
* does not depend on the template parameter N.
|
||||
*/
|
||||
class CallbackBase
|
||||
{
|
||||
protected:
|
||||
/**
|
||||
* Possible operations performed by TypeDependentOperation::operation()
|
||||
*/
|
||||
enum Op
|
||||
{
|
||||
CALL,
|
||||
ASSIGN,
|
||||
DESTROY
|
||||
};
|
||||
/**
|
||||
* This class is part of the any idiom used by Callback.
|
||||
*/
|
||||
template<typename T>
|
||||
class TypeDependentOperation
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Perform the type-dependent operations
|
||||
* \param a storage for the any object, stores the function object
|
||||
* \param b storage for the source object for the copy constructor
|
||||
* \param op operation
|
||||
*/
|
||||
static void operation(int32_t *a, const int32_t *b, Op op)
|
||||
{
|
||||
T *o1=reinterpret_cast<T*>(a);
|
||||
const T *o2=reinterpret_cast<const T*>(b);
|
||||
switch(op)
|
||||
{
|
||||
case CALL:
|
||||
(*o1)();
|
||||
break;
|
||||
case ASSIGN:
|
||||
//This used to be simply *o1=*o2 when we were using
|
||||
//tr1/functional, but in C++11 the type returned by bind
|
||||
//due to having a move constructor doesn't like being
|
||||
//assigned, only copy construction works so we have to
|
||||
//use placement new
|
||||
new (o1) T(*o2);
|
||||
break;
|
||||
case DESTROY:
|
||||
o1->~T();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* A Callback works just like an std::function, but has some additional
|
||||
* <b>limitations</b>. First, it can only accept function objects that take void
|
||||
* as a parameter and return void, and second if the size of the
|
||||
* implementation-defined type returned by bind is larger than N a
|
||||
* compile-time error is generated. Also, calling an empty Callback does
|
||||
* nothing, while doing the same on a function results in an exception
|
||||
* being thrown.
|
||||
*
|
||||
* The reason why one would want to use this class is because, other than the
|
||||
* limitations, this class also offers a guarantee: it will never allocate
|
||||
* data on the heap. It is not just a matter of code speed: in Miosix calling
|
||||
* new/delete/malloc/free from an interrupt routine produces undefined
|
||||
* behaviour, so this class enables binding function calls form an interrupt
|
||||
* safely.
|
||||
*
|
||||
* \param N the size in bytes that an instance of this class reserves to
|
||||
* store the function objects. If the line starting with 'typedef char check1'
|
||||
* starts failing it means it is time to increase this number. The size
|
||||
* of an instance of this object is N+sizeof(void (*)()), but with N rounded
|
||||
* by excess to four byte boundaries.
|
||||
*/
|
||||
template<unsigned N>
|
||||
class Callback : private CallbackBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Default constructor. Produces an empty callback.
|
||||
*/
|
||||
Callback() : operation(0) {}
|
||||
|
||||
/**
|
||||
* Constructor. Not explicit by design.
|
||||
* \param functor function object a copy of which is stored internally
|
||||
*/
|
||||
template<typename T>
|
||||
Callback(T functor) : operation(0)
|
||||
{
|
||||
*this=functor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
* \param rhs object to copy
|
||||
*/
|
||||
Callback(const Callback& rhs)
|
||||
{
|
||||
operation=rhs.operation;
|
||||
if(operation) operation(any,rhs.any,ASSIGN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Operator =
|
||||
* \param rhs object to copy
|
||||
* \return *this
|
||||
*/
|
||||
Callback& operator= (const Callback& rhs);
|
||||
|
||||
/**
|
||||
* Assignment operation, assigns a function object to this callback.
|
||||
* \param funtor function object a copy of which is stored internally
|
||||
*/
|
||||
template<typename T>
|
||||
Callback& operator= (T functor);
|
||||
|
||||
/**
|
||||
* Removes any function object stored in this class
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
if(operation) operation(any,0,DESTROY);
|
||||
operation=0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the callback, or do nothing if no callback is set
|
||||
*/
|
||||
void operator() ()
|
||||
{
|
||||
if(operation) operation(any,0,CALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the callback, generating undefined behaviour if no callback is set
|
||||
*/
|
||||
void call()
|
||||
{
|
||||
operation(any,0,CALL);
|
||||
}
|
||||
|
||||
//Safe bool idiom
|
||||
struct SafeBoolStruct { void* b; };
|
||||
typedef void* SafeBoolStruct::* SafeBool;
|
||||
|
||||
/**
|
||||
* \return true if the object contains a callback
|
||||
*/
|
||||
operator SafeBool() const
|
||||
{
|
||||
return operation==0 ? 0 : &SafeBoolStruct::b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Callback()
|
||||
{
|
||||
if(operation) operation(any,0,DESTROY);
|
||||
}
|
||||
|
||||
private:
|
||||
/// This declaration is done like that to save memory. On 32 bit systems
|
||||
/// the size of a pointer is 4 bytes, but the strictest alignment is 8
|
||||
/// which is that of double and long long. Using an array of doubles would
|
||||
/// have guaranteed alignment but the array size would have been a multiple
|
||||
/// of 8 bytes, and by summing the 4 bytes of the operation pointer there
|
||||
/// would have been 4 bytes of slack space left unused when declaring arrays
|
||||
/// of callbacks. Therefore any is declared as an array of ints but aligned
|
||||
/// to 8 bytes. This allows i.e. declaring Callback<20> with 20 bytes of
|
||||
/// useful storage and 4 bytes of pointer, despite 20 is not a multiple of 8
|
||||
int32_t any[(N+3)/4] __attribute__((aligned(8)));
|
||||
void (*operation)(int32_t *a, const int32_t *b, Op op);
|
||||
};
|
||||
|
||||
template<unsigned N>
|
||||
Callback<N>& Callback<N>::operator= (const Callback<N>& rhs)
|
||||
{
|
||||
if(this==&rhs) return *this; //Handle assignmento to self
|
||||
if(operation) operation(any,0,DESTROY);
|
||||
operation=rhs.operation;
|
||||
if(operation) operation(any,rhs.any,ASSIGN);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<unsigned N>
|
||||
template<typename T>
|
||||
Callback<N>& Callback<N>::operator= (T functor)
|
||||
{
|
||||
//If an error is reported about this line an attempt to store a too large
|
||||
//object is made. Increase N.
|
||||
static_assert(sizeof(any)>=sizeof(T),"");
|
||||
|
||||
//This should not fail unless something has a stricter alignment than double
|
||||
static_assert(__alignof__(any)>=__alignof__(T),"");
|
||||
|
||||
if(operation) operation(any,0,DESTROY);
|
||||
|
||||
new (reinterpret_cast<T*>(any)) T(functor);
|
||||
operation=TypeDependentOperation<T>::operation;
|
||||
return *this;
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //CALLBACK_H
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2012, 2013, 2014, 2015, 2016 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include "e20.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace miosix {
|
||||
|
||||
//
|
||||
// Class EventQueue
|
||||
//
|
||||
|
||||
void EventQueue::post(function<void ()> event)
|
||||
{
|
||||
Lock<FastMutex> l(m);
|
||||
events.push_back(event);
|
||||
cv.signal();
|
||||
}
|
||||
|
||||
void EventQueue::run()
|
||||
{
|
||||
Lock<FastMutex> l(m);
|
||||
for(;;)
|
||||
{
|
||||
while(events.empty()) cv.wait(l);
|
||||
function<void ()> f=events.front();
|
||||
events.pop_front();
|
||||
{
|
||||
Unlock<FastMutex> u(l);
|
||||
f();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EventQueue::runOne()
|
||||
{
|
||||
function<void ()> f;
|
||||
{
|
||||
Lock<FastMutex> l(m);
|
||||
if(events.empty()) return;
|
||||
f=events.front();
|
||||
events.pop_front();
|
||||
}
|
||||
f();
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,460 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2012, 2013, 2014, 2015, 2016 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
//Miosix event based API
|
||||
|
||||
#ifndef E20_H
|
||||
#define E20_H
|
||||
|
||||
#include <list>
|
||||
#include <functional>
|
||||
#include <miosix.h>
|
||||
#include "callback.h"
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* A variable sized event queue.
|
||||
*
|
||||
* Makes use of heap allocations and as such it is not possible to post events
|
||||
* from within interrupt service routines. For this, use FixedEventQueue.
|
||||
*
|
||||
* This class acts as a synchronization point, multiple threads can post
|
||||
* events, and multiple threads can call run() or runOne() (thread pooling).
|
||||
*
|
||||
* Events are function that are posted by a thread through post() but executed
|
||||
* in the context of the thread that calls run() or runOne()
|
||||
*/
|
||||
class EventQueue
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
EventQueue() {}
|
||||
|
||||
/**
|
||||
* Post an event to the queue. This function never blocks.
|
||||
*
|
||||
* \param event function function to be called in the thread that calls
|
||||
* run() or runOne(). Bind can be used to bind parameters to the function.
|
||||
* \throws std::bad_alloc if there is not enough heap memory
|
||||
*/
|
||||
void post(std::function<void ()> event);
|
||||
|
||||
/**
|
||||
* This function blocks waiting for events being posted, and when available
|
||||
* it calls the event function. To return from this event loop an event
|
||||
* function must throw an exception.
|
||||
*
|
||||
* \throws any exception that is thrown by the event functions
|
||||
*/
|
||||
void run();
|
||||
|
||||
/**
|
||||
* Run at most one event. This function does not block.
|
||||
*
|
||||
* \throws any exception that is thrown by the event functions
|
||||
*/
|
||||
void runOne();
|
||||
|
||||
/**
|
||||
* \return the number of events in the queue
|
||||
*/
|
||||
unsigned int size() const
|
||||
{
|
||||
Lock<FastMutex> l(m);
|
||||
return events.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* \return true if the queue has no events
|
||||
*/
|
||||
bool empty() const
|
||||
{
|
||||
Lock<FastMutex> l(m);
|
||||
return events.empty();
|
||||
}
|
||||
|
||||
private:
|
||||
EventQueue(const EventQueue&);
|
||||
EventQueue& operator= (const EventQueue&);
|
||||
|
||||
std::list<std::function<void ()> > events; ///< Event queue
|
||||
mutable FastMutex m; ///< Mutex for synchronisation
|
||||
ConditionVariable cv; ///< Condition variable for synchronisation
|
||||
};
|
||||
|
||||
/**
|
||||
* This class is to extract from FixedEventQueue code that
|
||||
* does not depend on the NumSlots template parameters.
|
||||
*/
|
||||
template<unsigned SlotSize>
|
||||
class FixedEventQueueBase
|
||||
{
|
||||
protected:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
FixedEventQueueBase() : put(0), get(0), n(0), waitingGet(0), waitingPut(0)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Post an event. Blocks if event queue is full.
|
||||
* \param event event to post
|
||||
* \param events pointer to event queue
|
||||
* \param size event queue size
|
||||
*/
|
||||
void postImpl(Callback<SlotSize>& event, Callback<SlotSize> *events,
|
||||
unsigned int size);
|
||||
|
||||
/**
|
||||
* Post an event from an interrupt, or with interrupts disabled.
|
||||
* \param event event to post
|
||||
* \param events pointer to event queue
|
||||
* \param size event queue size
|
||||
* \param hppw set to true if a higher priority thread is awakened,
|
||||
* otherwise the variable is not modified
|
||||
*/
|
||||
bool IRQpostImpl(Callback<SlotSize>& event, Callback<SlotSize> *events,
|
||||
unsigned int size, bool *hppw=0);
|
||||
|
||||
/**
|
||||
* This function blocks waiting for events being posted, and when available
|
||||
* it calls the event function. To return from this event loop an event
|
||||
* function must throw an exception.
|
||||
*
|
||||
* \param events pointer to event queue
|
||||
* \param size event queue size
|
||||
* \throws any exception that is thrown by the event functions
|
||||
*/
|
||||
void runImpl(Callback<SlotSize> *events, unsigned int size);
|
||||
|
||||
/**
|
||||
* Run at most one event. This function does not block.
|
||||
*
|
||||
* \param events pointer to event queue
|
||||
* \param size event queue size
|
||||
* \throws any exception that is thrown by the event functions
|
||||
*/
|
||||
void runOneImpl(Callback<SlotSize> *events, unsigned int size);
|
||||
|
||||
/**
|
||||
* \return the number of events in the queue
|
||||
*/
|
||||
unsigned int sizeImpl() const
|
||||
{
|
||||
FastInterruptDisableLock dLock;
|
||||
return n;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* To allow multiple threads waiting on put and get
|
||||
*/
|
||||
struct WaitingList
|
||||
{
|
||||
WaitingList *next; ///< Pointer to next element of the list
|
||||
Thread *t; ///< Thread waiting
|
||||
bool token; ///< To tolerate spurious wakeups
|
||||
};
|
||||
|
||||
unsigned int put; ///< Put position into events
|
||||
unsigned int get; ///< Get position into events
|
||||
unsigned int n; ///< Number of occupied event slots
|
||||
WaitingList *waitingGet; ///< List of threads waiting to get an event
|
||||
WaitingList *waitingPut; ///< List of threads waiting to put an event
|
||||
};
|
||||
|
||||
template<unsigned SlotSize>
|
||||
void FixedEventQueueBase<SlotSize>::postImpl(Callback<SlotSize>& event,
|
||||
Callback<SlotSize> *events, unsigned int size)
|
||||
{
|
||||
//Not FastInterruptDisableLock as the operator= of the bound
|
||||
//parameters of the Callback may allocate
|
||||
InterruptDisableLock dLock;
|
||||
while(n>=size)
|
||||
{
|
||||
WaitingList w;
|
||||
w.token=false;
|
||||
w.t=Thread::IRQgetCurrentThread();
|
||||
w.next=waitingPut;
|
||||
waitingPut=&w;
|
||||
while(w.token==false)
|
||||
{
|
||||
Thread::IRQwait();
|
||||
{
|
||||
InterruptEnableLock eLock(dLock);
|
||||
Thread::yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
IRQpostImpl(event,events,size);
|
||||
}
|
||||
|
||||
template<unsigned SlotSize>
|
||||
bool FixedEventQueueBase<SlotSize>::IRQpostImpl(Callback<SlotSize>& event,
|
||||
Callback<SlotSize> *events, unsigned int size, bool *hppw)
|
||||
{
|
||||
if(n>=size) return false;
|
||||
events[put]=event; //This may allocate memory
|
||||
if(++put>=size) put=0;
|
||||
n++;
|
||||
if(waitingGet)
|
||||
{
|
||||
Thread *t=Thread::IRQgetCurrentThread();
|
||||
if(hppw && waitingGet->t->IRQgetPriority()>t->IRQgetPriority())
|
||||
*hppw=true;
|
||||
waitingGet->token=true;
|
||||
waitingGet->t->IRQwakeup();
|
||||
waitingGet=waitingGet->next;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<unsigned SlotSize>
|
||||
void FixedEventQueueBase<SlotSize>::runImpl(Callback<SlotSize> *events,
|
||||
unsigned int size)
|
||||
{
|
||||
//Not FastInterruptDisableLock as the operator= of the bound
|
||||
//parameters of the Callback may allocate
|
||||
InterruptDisableLock dLock;
|
||||
for(;;)
|
||||
{
|
||||
while(n<=0)
|
||||
{
|
||||
WaitingList w;
|
||||
w.token=false;
|
||||
w.t=Thread::IRQgetCurrentThread();
|
||||
w.next=waitingGet;
|
||||
waitingGet=&w;
|
||||
while(w.token==false)
|
||||
{
|
||||
Thread::IRQwait();
|
||||
{
|
||||
InterruptEnableLock eLock(dLock);
|
||||
Thread::yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
Callback<SlotSize> f=events[get]; //This may allocate memory
|
||||
if(++get>=size) get=0;
|
||||
n--;
|
||||
if(waitingPut)
|
||||
{
|
||||
waitingPut->token=true;
|
||||
waitingPut->t->IRQwakeup();
|
||||
waitingPut=waitingPut->next;
|
||||
}
|
||||
{
|
||||
InterruptEnableLock eLock(dLock);
|
||||
f();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned SlotSize>
|
||||
void FixedEventQueueBase<SlotSize>::runOneImpl(Callback<SlotSize> *events,
|
||||
unsigned int size)
|
||||
{
|
||||
Callback<SlotSize> f;
|
||||
{
|
||||
//Not FastInterruptDisableLock as the operator= of the bound
|
||||
//parameters of the Callback may allocate
|
||||
InterruptDisableLock dLock;
|
||||
if(n<=0) return;
|
||||
f=events[get]; //This may allocate memory
|
||||
if(++get>=size) get=0;
|
||||
n--;
|
||||
if(waitingPut)
|
||||
{
|
||||
waitingPut->token=true;
|
||||
waitingPut->t->IRQwakeup();
|
||||
waitingPut=waitingPut->next;
|
||||
}
|
||||
}
|
||||
f();
|
||||
}
|
||||
|
||||
/**
|
||||
* A fixed size event queue.
|
||||
*
|
||||
* This guarantees it makes no use of the heap, therefore events can be posted
|
||||
* also from within interrupt handlers. This simplifies the development of
|
||||
* device drivers.
|
||||
*
|
||||
* This class acts as a synchronization point, multiple threads (and IRQs) can
|
||||
* post events, and multiple threads can call run() or runOne()
|
||||
* (thread pooling).
|
||||
*
|
||||
* Events are function that are posted by a thread through post() but executed
|
||||
* in the context of the thread that calls run() or runOne()
|
||||
*
|
||||
* \param NumSlots maximum queue length
|
||||
* \param SlotSize size of the Callback objects. This limits the maximum number
|
||||
* of parameters that can be bound to a function. If you get compile-time
|
||||
* errors in callback.h, consider increasing this value. The default is 20
|
||||
* bytes, which is enough to bind a member function pointer, a "this" pointer
|
||||
* and two byte or pointer sized parameters.
|
||||
*/
|
||||
template<unsigned NumSlots, unsigned SlotSize=20>
|
||||
class FixedEventQueue : private FixedEventQueueBase<SlotSize>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
FixedEventQueue() {}
|
||||
|
||||
/**
|
||||
* Post an event, blocking if the event queue is full.
|
||||
*
|
||||
* \param event function function to be called in the thread that calls
|
||||
* run() or runOne(). Bind can be used to bind parameters to the function.
|
||||
* Unlike with the EventQueue, the operator= of the bound parameters have
|
||||
* the restriction that they need to be callable from inside a
|
||||
* InterruptDisableLock without causing undefined behaviour, so they
|
||||
* must not, open files, print, ... but can allocate memory.
|
||||
*/
|
||||
void post(Callback<SlotSize> event)
|
||||
{
|
||||
this->postImpl(event,events,NumSlots);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post an event in the queue, or return if the queue was full.
|
||||
*
|
||||
* \param event function function to be called in the thread that calls
|
||||
* run() or runOne(). Bind can be used to bind parameters to the function.
|
||||
* Unlike with the EventQueue, the operator= of the bound parameters have
|
||||
* the restriction that they need to be callable from inside a
|
||||
* InterruptDisableLock without causing undefined behaviour, so they
|
||||
* must not open files, print, ... but can allocate memory.
|
||||
* \return false if there was no space in the queue
|
||||
*/
|
||||
bool postNonBlocking(Callback<SlotSize> event)
|
||||
{
|
||||
InterruptDisableLock dLock;
|
||||
return this->IRQpostImpl(event,events,NumSlots);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post an event in the queue, or return if the queue was full.
|
||||
* Can be called only with interrupts disabled or within an interrupt
|
||||
* handler, allowing device drivers to post an event to a thread.
|
||||
*
|
||||
* \param event function function to be called in the thread that calls
|
||||
* run() or runOne(). Bind can be used to bind parameters to the function.
|
||||
* Unlike with the EventQueue, the operator= of the bound parameters have
|
||||
* the restriction that they need to be callable with interrupts disabled
|
||||
* so they must not open files, print, ...
|
||||
*
|
||||
* If the call is made from within an InterruptDisableLock the copy
|
||||
* constructors can allocate memory, while if the call is made from an
|
||||
* interrupt handler or a FastInterruptFisableLock memory allocation is
|
||||
* forbidden.
|
||||
* \return false if there was no space in the queue
|
||||
*/
|
||||
bool IRQpost(Callback<SlotSize> event)
|
||||
{
|
||||
return this->IRQpostImpl(event,events,NumSlots);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post an event in the queue, or return if the queue was full.
|
||||
* Can be called only with interrupts disabled or within an interrupt
|
||||
* handler, allowing device drivers to post an event to a thread.
|
||||
*
|
||||
* \param event function function to be called in the thread that calls
|
||||
* run() or runOne(). Bind can be used to bind parameters to the function.
|
||||
* Unlike with the EventQueue, the operator= of the bound parameters have
|
||||
* the restriction that they need to be callable with interrupts disabled
|
||||
* so they must not open files, print, ...
|
||||
*
|
||||
* If the call is made from within an InterruptDisableLock the copy
|
||||
* constructors can allocate memory, while if the call is made from an
|
||||
* interrupt handler or a FastInterruptFisableLock memory allocation is
|
||||
* forbidden.
|
||||
* \param hppw returns true if a higher priority thread was awakened as
|
||||
* part of posting the event. Can be used inside an IRQ to call the
|
||||
* scheduler.
|
||||
* \return false if there was no space in the queue
|
||||
*/
|
||||
bool IRQpost(Callback<SlotSize> event, bool& hppw)
|
||||
{
|
||||
hppw=false;
|
||||
return this->IRQpostImpl(event,events,NumSlots,&hppw);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function blocks waiting for events being posted, and when available
|
||||
* it calls the event function. To return from this event loop an event
|
||||
* function must throw an exception.
|
||||
*
|
||||
* \throws any exception that is thrown by the event functions
|
||||
*/
|
||||
void run()
|
||||
{
|
||||
this->runImpl(events,NumSlots);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run at most one event. This function does not block.
|
||||
*
|
||||
* \throws any exception that is thrown by the event functions
|
||||
*/
|
||||
void runOne()
|
||||
{
|
||||
this->runOneImpl(events,NumSlots);
|
||||
}
|
||||
|
||||
/**
|
||||
* \return the number of events in the queue
|
||||
*/
|
||||
unsigned int size() const
|
||||
{
|
||||
return this->sizeImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* \return true if the queue has no events
|
||||
*/
|
||||
unsigned int empty() const
|
||||
{
|
||||
return this->sizeImpl()==0;
|
||||
}
|
||||
|
||||
private:
|
||||
FixedEventQueue(const FixedEventQueue&);
|
||||
FixedEventQueue& operator= (const FixedEventQueue&);
|
||||
|
||||
Callback<SlotSize> events[NumSlots]; ///< Fixed size queue of events
|
||||
};
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //E20_H
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2017 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include "unmember.h"
|
||||
#include <cstdio>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace miosix {
|
||||
|
||||
tuple<void (*)(void*), void*> unmemberLogic(unsigned long mixedField,
|
||||
long thisOffset, unsigned long *o) noexcept
|
||||
{
|
||||
//ARM stores the "virtual function or not?" bit in bit #0 of thisOffset and
|
||||
//multiplies the offset by 2 to make room for it. Intel stores it in bit #0
|
||||
//of mixedField
|
||||
|
||||
#ifdef __ARM_EABI__
|
||||
//With multiple or virtual inheritance we need to add an offset to this.
|
||||
o+=(thisOffset & 0xfffffffe)/2/sizeof(long);
|
||||
|
||||
//Then we have two cases, we can have a member function pointer to a
|
||||
//virtual or nonvirtual function. For virtual functions mixedField is the
|
||||
//offset in the vtable where to find the function pointer.
|
||||
//For nonvirtual functions it is already the desired function pointer
|
||||
void (*result)(void*);
|
||||
if(thisOffset & 1)
|
||||
{
|
||||
//Pointer to virtual function. Dereference the object to get to the
|
||||
//virtual table, and then get the function pointer at the correct offset
|
||||
unsigned long *vtbl=reinterpret_cast<unsigned long *>(*o);
|
||||
result=reinterpret_cast<void (*)(void*)>(vtbl[mixedField/sizeof(long)]);
|
||||
} else {
|
||||
//Pointer to non virtual function
|
||||
result=reinterpret_cast<void (*)(void*)>(mixedField);
|
||||
}
|
||||
|
||||
#elif defined(__i386) || defined(__x86_64__)
|
||||
//With multiple or virtual inheritance we need to add an offset to this.
|
||||
o+=thisOffset/sizeof(long);
|
||||
|
||||
//Then we have two cases, we can have a member function pointer to a
|
||||
//virtual or nonvirtual function. For virtual functions mixedField is the
|
||||
//offset in the vtable where to find the function pointer.
|
||||
//For nonvirtual functions it is already the desired function pointer
|
||||
void (*result)(void*);
|
||||
if(mixedField & 1)
|
||||
{
|
||||
//Pointer to virtual function. Dereference the object to get to the
|
||||
//virtual table, and then get the function pointer at the correct offset
|
||||
unsigned long *vtbl=reinterpret_cast<unsigned long *>(*o);
|
||||
result=reinterpret_cast<void (*)(void*)>(vtbl[(mixedField-1)/sizeof(long)]);
|
||||
} else {
|
||||
//Pointer to non virtual function
|
||||
result=reinterpret_cast<void (*)(void*)>(mixedField);
|
||||
}
|
||||
#else
|
||||
#error The layout of virtual function pointer is unknown for this arch
|
||||
#endif
|
||||
|
||||
return make_tuple(result,reinterpret_cast<void*>(o));
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
//Testsuite for member function pointer implementation. Compile with:
|
||||
// g++ -std=c++11 -O2 -DTEST_ALGORITHM -o test unmember.cpp; ./test
|
||||
#ifdef TEST_ALGORITHM
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
using namespace std;
|
||||
using namespace miosix;
|
||||
|
||||
class Base
|
||||
{
|
||||
public:
|
||||
void m1() { printf("Base m1 %d %p\n",y,this); }
|
||||
virtual void m2() { printf("Base m2 %d %p\n",y,this); }
|
||||
virtual void m3() { printf("Base m3 %d %p\n",y,this); }
|
||||
int y=1234;
|
||||
};
|
||||
|
||||
class Base2
|
||||
{
|
||||
public:
|
||||
void m4() { printf("Base2 m4 %d %p\n",x,this); }
|
||||
virtual void m5() { printf("Base2 m5 %d %p\n",x,this); }
|
||||
int x=5678;
|
||||
};
|
||||
|
||||
class Derived : public Base
|
||||
{
|
||||
public:
|
||||
virtual void m3() { printf("Derived m3 %d %p\n",y,this); }
|
||||
};
|
||||
|
||||
class Derived2 : public Base, public Base2 {};
|
||||
|
||||
class A { public: int a=1; };
|
||||
class B : virtual public A { public: int b=2; };
|
||||
class C : virtual public A { public: int c=3; void mf() { printf("%d\n",c); } };
|
||||
class D : public B, public C { public: int d=4; };
|
||||
|
||||
void call(tuple<void (*)(void*), void*> a) { (*get<0>(a))(get<1>(a)); }
|
||||
|
||||
int main()
|
||||
{
|
||||
Base b;
|
||||
Derived d;
|
||||
Derived2 d2;
|
||||
D dd;
|
||||
call(unmember(&Base::m1,&b));
|
||||
call(unmember(&Base::m2,&b));
|
||||
call(unmember(&Base::m3,&b));
|
||||
call(unmember(&Derived::m3,&d));
|
||||
call(unmember<Derived2>(&Derived2::m4,&d2));
|
||||
call(unmember<Derived2>(&Derived2::m5,&d2));
|
||||
call(unmember<D>(&D::mf,&dd));
|
||||
}
|
||||
|
||||
#endif //TEST_ALGORITHM
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2017 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Template-independent code of unmember is here to reduce code size.
|
||||
* Do not call this function directly
|
||||
* \param mixedField either the function pointer for nonvirtual functions,
|
||||
* or the vtable offset (with 1 added to disambiguate it from the previous case)
|
||||
* for virtual functions
|
||||
* \param thisOffset the offset to add to this for multiple/virtual inheritance
|
||||
* \param o the object pointer that has to be passed as this
|
||||
* \retun the ordinary function pointer and void * to call it with
|
||||
*/
|
||||
std::tuple<void (*)(void*), void*> unmemberLogic(unsigned long mixedField,
|
||||
long thisOffset, unsigned long *o) noexcept;
|
||||
|
||||
/**
|
||||
* This function performs the forbidden cast of C++: casting from a member
|
||||
* function pointer of any class and a class pointer to an ordinary function
|
||||
* pointer and an opaque void * parameter to call it with. It allows to call
|
||||
* any member function of any class (that takes no parameters and returns void)
|
||||
* in an uniform and efficient way.
|
||||
*
|
||||
* The code is not portable, as the underlying representation of member function
|
||||
* pointers is not specified by the standard. According to
|
||||
* https://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible
|
||||
* there are multiple implementations.
|
||||
* This code has been tested with the GCC and the LLVM compilers, in both
|
||||
* X86, X86-64 and ARM32.
|
||||
*
|
||||
* \param mfn a member function pointer of any class that takes no parameters
|
||||
* and returns void
|
||||
* \param object an object on which the member function has to be called
|
||||
* \return the ordinary function pointer and void * to call it with
|
||||
*/
|
||||
template<typename T>
|
||||
std::tuple<void (*)(void*), void*> unmember(void (T::*mfn)(), T *object) noexcept
|
||||
{
|
||||
//This code only works with GCC/LLVM
|
||||
#if !defined(__clang__) && !defined(__GNUC__)
|
||||
#error Unknown member function pointer layout
|
||||
#endif
|
||||
|
||||
//A union is used to "inspect" the internals of the member function pointer
|
||||
union {
|
||||
void (T::*mfn)();
|
||||
struct {
|
||||
unsigned long mixedField;
|
||||
long thisOffset;
|
||||
};
|
||||
} unpack;
|
||||
unpack.mfn=mfn;
|
||||
|
||||
//Unsigned long is used as its size is 4 on 32bit systems and 8 in 64bit
|
||||
unsigned long *o=reinterpret_cast<unsigned long*>(object);
|
||||
|
||||
return unmemberLogic(unpack.mixedField,unpack.thisOffset,o);
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
/***************************************************************************
|
||||
* 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include "console_device.h"
|
||||
#include "filesystem/ioctl.h"
|
||||
#include <errno.h>
|
||||
#include <termios.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace miosix {
|
||||
|
||||
//
|
||||
// class TerminalDevice
|
||||
//
|
||||
|
||||
TerminalDevice::TerminalDevice(intrusive_ref_ptr<Device> device)
|
||||
: FileBase(intrusive_ref_ptr<FilesystemBase>()), 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<const char*>(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;i<length;i++,buffer++)
|
||||
{
|
||||
if(*buffer!='\n') continue;
|
||||
if(buffer>start)
|
||||
{
|
||||
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<FastMutex> l(mutex); //Reads are serialized
|
||||
char *buffer=static_cast<char*>(data);
|
||||
size_t readBytes=0;
|
||||
for(;;)
|
||||
{
|
||||
ssize_t r=device->readBlock(buffer+readBytes,length-readBytes,0);
|
||||
if(r<0) return r;
|
||||
pair<size_t,bool> 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<termios*>(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<size_t,bool> TerminalDevice::normalize(char *buffer, ssize_t begin,
|
||||
ssize_t end)
|
||||
{
|
||||
bool newlineFound=false;
|
||||
buffer+=begin;
|
||||
chunkStart=buffer;
|
||||
for(ssize_t i=begin;i<end;i++,buffer++)
|
||||
{
|
||||
switch(*buffer)
|
||||
{
|
||||
//Trying to be compatible with terminals that output \r, \n or \r\n
|
||||
//When receiving \r skipNewline is set to true so we skip the \n
|
||||
//if it comes right after the \r
|
||||
case '\r':
|
||||
*buffer='\n';
|
||||
echoBack(buffer,"\r\n",2);
|
||||
skipNewline=true;
|
||||
newlineFound=true;
|
||||
break;
|
||||
case '\n':
|
||||
if(skipNewline)
|
||||
{
|
||||
skipNewline=false;
|
||||
//Discard the \n because comes right after \r
|
||||
memmove(buffer,buffer+1,end-i-1);
|
||||
end--;
|
||||
i--; //Note that i may become -1, but it is acceptable.
|
||||
buffer--;
|
||||
} else {
|
||||
echoBack(buffer,"\r\n",2);
|
||||
newlineFound=true;
|
||||
}
|
||||
break;
|
||||
case 0x7f: //Unix backspace
|
||||
case 0x08: //DOS backspace
|
||||
{
|
||||
//Need to echo before moving buffer data
|
||||
echoBack(buffer,"\033[1D \033[1D",9);
|
||||
ssize_t backward= i==0 ? 1 : 2;
|
||||
memmove(buffer-(backward-1),buffer+1,end-i);
|
||||
end-=backward;
|
||||
i-=backward; //Note that i may become -1, but it is acceptable.
|
||||
buffer-=backward;
|
||||
chunkStart=buffer+1; //Fixup chunkStart after backspace
|
||||
break;
|
||||
}
|
||||
default:
|
||||
skipNewline=false;
|
||||
}
|
||||
}
|
||||
echoBack(buffer);
|
||||
return make_pair(end,newlineFound);
|
||||
}
|
||||
|
||||
void TerminalDevice::echoBack(const char *chunkEnd, const char *sep, size_t sepLen)
|
||||
{
|
||||
if(!echo) return;
|
||||
if(chunkEnd>chunkStart) 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<Device> 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<TerminalDevice>(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
|
||||
|
|
@ -0,0 +1,228 @@
|
|||
/***************************************************************************
|
||||
* 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef CONSOLE_DEVICE_H
|
||||
#define CONSOLE_DEVICE_H
|
||||
|
||||
#include "config/miosix_settings.h"
|
||||
#include "filesystem/devfs/devfs.h"
|
||||
#include "kernel/sync.h"
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* Teriminal device, proxy object supporting additional terminal-specific
|
||||
* features
|
||||
*/
|
||||
class TerminalDevice : public FileBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* \param device proxed device.
|
||||
*/
|
||||
TerminalDevice(intrusive_ref_ptr<Device> device);
|
||||
|
||||
/**
|
||||
* Write data to the file, if the file supports writing.
|
||||
* \param data the data to write
|
||||
* \param length the number of bytes to write
|
||||
* \return the number of written characters, or a negative number in case
|
||||
* of errors
|
||||
*/
|
||||
virtual ssize_t write(const void *data, size_t length);
|
||||
|
||||
/**
|
||||
* Read data from the file, if the file supports reading.
|
||||
* \param data buffer to store read data
|
||||
* \param length the number of bytes to read
|
||||
* \return the number of read characters, or a negative number in case
|
||||
* of errors
|
||||
*/
|
||||
virtual ssize_t read(void *data, size_t length);
|
||||
|
||||
#ifdef WITH_FILESYSTEM
|
||||
|
||||
/**
|
||||
* Move file pointer, if the file supports random-access.
|
||||
* \param pos offset to sum to the beginning of the file, current position
|
||||
* or end of file, depending on whence
|
||||
* \param whence SEEK_SET, SEEK_CUR or SEEK_END
|
||||
* \return the offset from the beginning of the file if the operation
|
||||
* completed, or a negative number in case of errors
|
||||
*/
|
||||
virtual off_t lseek(off_t pos, int whence);
|
||||
|
||||
/**
|
||||
* Return file information.
|
||||
* \param pstat pointer to stat struct
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int fstat(struct stat *pstat) const;
|
||||
|
||||
/**
|
||||
* Check whether the file refers to a terminal.
|
||||
* \return 1 if it is a terminal, 0 if it is not, or a negative number in
|
||||
* case of errors
|
||||
*/
|
||||
virtual int isatty() const;
|
||||
|
||||
#endif //WITH_FILESYSTEM
|
||||
|
||||
/**
|
||||
* Perform various operations on a file descriptor
|
||||
* \param cmd specifies the operation to perform
|
||||
* \param arg optional argument that some operation require
|
||||
* \return the exact return value depends on CMD, -1 is returned on error
|
||||
*/
|
||||
virtual int ioctl(int cmd, void *arg);
|
||||
|
||||
/**
|
||||
* Enables or disables echo of commands on the terminal
|
||||
* \param echo true to enable echo, false to disable it
|
||||
*/
|
||||
void setEcho(bool echoMode) { echo=echoMode; }
|
||||
|
||||
/**
|
||||
* \return true if echo is enabled
|
||||
*/
|
||||
bool isEchoEnabled() const { return echo; }
|
||||
|
||||
/**
|
||||
* Selects whether the terminal sholud be transparent to non ASCII data
|
||||
* \param rawMode true if raw mode is required
|
||||
*/
|
||||
void setBinary(bool binaryMode) { binary=binaryMode; }
|
||||
|
||||
/**
|
||||
* \return true if the terminal allows binary data
|
||||
*/
|
||||
bool isBinary() const { return binary; }
|
||||
|
||||
private:
|
||||
/**
|
||||
* Perform normalization of a read buffer (\r\n conversion to \n, backspace)
|
||||
* \param buffer pointer to read buffer
|
||||
* \param begin buffer[begin] is the first character to normalize
|
||||
* \param end buffer[end] is one past the las character to normalize
|
||||
* \return a pair with the number of valid character in the buffer (staring
|
||||
* from buffer[0], not from buffer[begin], and a bool that is true if at
|
||||
* least one \n was found.
|
||||
*/
|
||||
std::pair<size_t,bool> normalize(char *buffer, ssize_t begin, ssize_t end);
|
||||
|
||||
/**
|
||||
* Perform echo when reading a buffer
|
||||
* \param chunkEnd one past the last character to echo back. The first
|
||||
* character is chunkStart. As a side effect, this member function modifies
|
||||
* chunkStart to be equal to chunkEnd+1, if echo is enabled
|
||||
* \param sep optional line separator, printed after the chunk
|
||||
* \param sepLen separator length
|
||||
*/
|
||||
void echoBack(const char *chunkEnd, const char *sep=0, size_t sepLen=0);
|
||||
|
||||
intrusive_ref_ptr<Device> device; ///< Underlying TTY device
|
||||
FastMutex mutex; ///< Mutex to serialze concurrent reads
|
||||
const char *chunkStart; ///< First character to echo in echoBack()
|
||||
bool echo; ///< True if echo enabled
|
||||
bool binary; ///< True if binary mode enabled
|
||||
bool skipNewline; ///< Used by normalize()
|
||||
};
|
||||
|
||||
/**
|
||||
* This class holds the file object related to the console, that is set by
|
||||
* the board support package, and used to populate /dev/console in DevFs
|
||||
*/
|
||||
class DefaultConsole
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \return an instance of this class (singleton)
|
||||
*/
|
||||
static DefaultConsole& instance();
|
||||
|
||||
/**
|
||||
* Called by the board support package, in particular IRQbspInit(), to pass
|
||||
* to the kernel the console device. This device file is used as the default
|
||||
* one for stdin/stdout/stderr.
|
||||
* Notes: this has to be called in IRQbspInit(), since if it's called too
|
||||
* late the console gets initialized with a NullFile.
|
||||
* Also, calling this a second time to dynamically change the console device
|
||||
* is probably a bad idea, as the device is cached around in the filesystem
|
||||
* code and will result in some processes using the old device and some
|
||||
* other the new one.
|
||||
* \param console device file handling console I/O. Can only be called with
|
||||
* interrupts disabled.
|
||||
*/
|
||||
void IRQset(intrusive_ref_ptr<Device> console);
|
||||
|
||||
/**
|
||||
* Same as IRQset(), but can be called with interrupts enabled
|
||||
* \param console device file handling console I/O. Can only be called with
|
||||
* interrupts disabled.
|
||||
*/
|
||||
void set(intrusive_ref_ptr<Device> console) { IRQset(console); }
|
||||
|
||||
/**
|
||||
* \return the currently installed console device, wrapped in a
|
||||
* TerminalDevice
|
||||
*/
|
||||
intrusive_ref_ptr<Device> get() { return console; }
|
||||
|
||||
/**
|
||||
* \return the currently installed console device.
|
||||
* Can be called with interrupts disabled or within an interrupt routine.
|
||||
*/
|
||||
intrusive_ref_ptr<Device> IRQget() { return console; }
|
||||
|
||||
#ifndef WITH_FILESYSTEM
|
||||
/**
|
||||
* \return the terminal device, when filesystem support is disabled.
|
||||
* If filesystem is enabled, the terminal device can be found in the
|
||||
* FileDescriptorTable
|
||||
*/
|
||||
intrusive_ref_ptr<TerminalDevice> getTerminal() { return terminal; }
|
||||
#endif //WITH_FILESYSTEM
|
||||
|
||||
private:
|
||||
/**
|
||||
* Constructor, private as it is a singleton
|
||||
*/
|
||||
DefaultConsole();
|
||||
|
||||
DefaultConsole(const DefaultConsole&);
|
||||
DefaultConsole& operator= (const DefaultConsole&);
|
||||
|
||||
intrusive_ref_ptr<Device> console; ///< The raw console device
|
||||
#ifndef WITH_FILESYSTEM
|
||||
intrusive_ref_ptr<TerminalDevice> terminal; ///< The wrapped console device
|
||||
#endif //WITH_FILESYSTEM
|
||||
};
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //CONSOLE_DEVICE_H
|
||||
|
|
@ -0,0 +1,405 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include "devfs.h"
|
||||
#include <string>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include "filesystem/stringpart.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace miosix {
|
||||
|
||||
static const int _NOSEEK=0x20000; //Special flag used only here to disallow seek
|
||||
|
||||
static void fillStatHelper(struct stat* pstat, unsigned int st_ino,
|
||||
short st_dev, mode_t mode)
|
||||
{
|
||||
memset(pstat,0,sizeof(struct stat));
|
||||
pstat->st_dev=st_dev;
|
||||
pstat->st_ino=st_ino;
|
||||
pstat->st_mode=mode;
|
||||
pstat->st_nlink=1;
|
||||
pstat->st_blksize=0; //If zero means file buffer equals to BUFSIZ
|
||||
}
|
||||
|
||||
/**
|
||||
* This file type is for reading and writing from devices
|
||||
*/
|
||||
class DevFsFile : public FileBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* \param fs pointer to DevFs
|
||||
* \param dev the device to which this file refers
|
||||
* \param flags file open flags (_FREAD, _FWRITE, ...)
|
||||
*/
|
||||
DevFsFile(intrusive_ref_ptr<FilesystemBase> fs,
|
||||
intrusive_ref_ptr<Device> dev, int flags) : FileBase(fs),
|
||||
dev(dev), seekPoint(0), flags(flags) {}
|
||||
|
||||
/**
|
||||
* Write data to the file, if the file supports writing.
|
||||
* \param data the data to write
|
||||
* \param len the number of bytes to write
|
||||
* \return the number of written characters, or a negative number in
|
||||
* case of errors
|
||||
*/
|
||||
virtual ssize_t write(const void *data, size_t len);
|
||||
|
||||
/**
|
||||
* Read data from the file, if the file supports reading.
|
||||
* \param data buffer to store read data
|
||||
* \param len the number of bytes to read
|
||||
* \return the number of read characters, or a negative number in
|
||||
* case of errors
|
||||
*/
|
||||
virtual ssize_t read(void *data, size_t len);
|
||||
|
||||
/**
|
||||
* Move file pointer, if the file supports random-access.
|
||||
* \param pos offset to sum to the beginning of the file, current position
|
||||
* or end of file, depending on whence
|
||||
* \param whence SEEK_SET, SEEK_CUR or SEEK_END
|
||||
* \return the offset from the beginning of the file if the operation
|
||||
* completed, or a negative number in case of errors
|
||||
*/
|
||||
virtual off_t lseek(off_t pos, int whence);
|
||||
|
||||
/**
|
||||
* Return file information.
|
||||
* \param pstat pointer to stat struct
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int fstat(struct stat *pstat) const;
|
||||
|
||||
/**
|
||||
* Check whether the file refers to a terminal.
|
||||
* \return 1 if it is a terminal, 0 if it is not, or a negative number in
|
||||
* case of errors
|
||||
*/
|
||||
virtual int isatty() const;
|
||||
|
||||
/**
|
||||
* Perform various operations on a file descriptor
|
||||
* \param cmd specifies the operation to perform
|
||||
* \param arg optional argument that some operation require
|
||||
* \return the exact return value depends on CMD, -1 is returned on error
|
||||
*/
|
||||
virtual int ioctl(int cmd, void *arg);
|
||||
|
||||
private:
|
||||
intrusive_ref_ptr<Device> dev; ///< Device file
|
||||
off_t seekPoint; ///< Seek point (note that off_t is 64bit)
|
||||
int flags; ///< File open flags
|
||||
};
|
||||
|
||||
ssize_t DevFsFile::write(const void *data, size_t len)
|
||||
{
|
||||
if((flags & _FWRITE)==0) return -EINVAL;
|
||||
if(seekPoint+static_cast<off_t>(len)<0)
|
||||
len=numeric_limits<off_t>::max()-seekPoint-len;
|
||||
ssize_t result=dev->writeBlock(data,len,seekPoint);
|
||||
if(result>0 && ((flags & _NOSEEK)==0)) seekPoint+=result;
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t DevFsFile::read(void *data, size_t len)
|
||||
{
|
||||
if((flags & _FREAD)==0) return -EINVAL;
|
||||
if(seekPoint+static_cast<off_t>(len)<0)
|
||||
len=numeric_limits<off_t>::max()-seekPoint-len;
|
||||
ssize_t result=dev->readBlock(data,len,seekPoint);
|
||||
if(result>0 && ((flags & _NOSEEK)==0)) seekPoint+=result;
|
||||
return result;
|
||||
}
|
||||
|
||||
off_t DevFsFile::lseek(off_t pos, int whence)
|
||||
{
|
||||
if(flags & _NOSEEK) return -EBADF; //No seek support
|
||||
|
||||
off_t newSeekPoint=seekPoint;
|
||||
switch(whence)
|
||||
{
|
||||
case SEEK_CUR:
|
||||
newSeekPoint+=pos;
|
||||
break;
|
||||
case SEEK_SET:
|
||||
newSeekPoint=pos;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL; //TODO: how to implement SEEK_END?
|
||||
}
|
||||
if(newSeekPoint<0) return -EOVERFLOW;
|
||||
seekPoint=newSeekPoint;
|
||||
return seekPoint;
|
||||
}
|
||||
|
||||
int DevFsFile::fstat(struct stat *pstat) const
|
||||
{
|
||||
return dev->fstat(pstat);
|
||||
}
|
||||
|
||||
int DevFsFile::isatty() const
|
||||
{
|
||||
return dev->isatty();
|
||||
}
|
||||
|
||||
int DevFsFile::ioctl(int cmd, void *arg)
|
||||
{
|
||||
return dev->ioctl(cmd,arg);
|
||||
}
|
||||
|
||||
//
|
||||
// class Device
|
||||
//
|
||||
|
||||
int Device::open(intrusive_ref_ptr<FileBase>& file,
|
||||
intrusive_ref_ptr<FilesystemBase> fs, int flags, int mode)
|
||||
{
|
||||
flags++; //To convert from O_RDONLY, O_WRONLY, ... to _FREAD, _FWRITE, ...
|
||||
file=intrusive_ref_ptr<FileBase>(
|
||||
new DevFsFile(fs,shared_from_this(),flags | (seekable ? 0 : _NOSEEK)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Device::fstat(struct stat* pstat) const
|
||||
{
|
||||
mode_t mode=(block ? S_IFBLK : S_IFCHR) | 0750;//brwxr-x--- | crwxr-x---
|
||||
fillStatHelper(pstat,st_ino,st_dev,mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Device::isatty() const
|
||||
{
|
||||
return tty ? 1 : 0;
|
||||
}
|
||||
|
||||
ssize_t Device::readBlock(void *buffer, size_t size, off_t where)
|
||||
{
|
||||
memset(buffer,0,size); //Act as /dev/zero
|
||||
return size;
|
||||
}
|
||||
|
||||
ssize_t Device::writeBlock(const void *buffer, size_t size, off_t where)
|
||||
{
|
||||
return size; //Act as /dev/null
|
||||
}
|
||||
|
||||
void Device::IRQwrite(const char *str) {}
|
||||
|
||||
int Device::ioctl(int cmd, void *arg)
|
||||
{
|
||||
return -ENOTTY; //Means the operation does not apply to this descriptor
|
||||
}
|
||||
|
||||
Device::~Device() {}
|
||||
|
||||
#ifdef WITH_DEVFS
|
||||
|
||||
/**
|
||||
* Directory class for DevFs
|
||||
*/
|
||||
class DevFsDirectory : public DirectoryBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \param parent parent filesystem
|
||||
* \param mutex mutex to lock when accessing the file map
|
||||
* \param files file map
|
||||
* \param currentInode inode of the directory we're listing
|
||||
* \param parentInode inode of the parent directory
|
||||
*/
|
||||
DevFsDirectory(intrusive_ref_ptr<FilesystemBase> parent,
|
||||
FastMutex& mutex,
|
||||
map<StringPart,intrusive_ref_ptr<Device> >& files,
|
||||
int currentInode, int parentInode)
|
||||
: DirectoryBase(parent), mutex(mutex), files(files),
|
||||
currentInode(currentInode), parentInode(parentInode),
|
||||
first(true), last(false)
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(files.empty()==false) currentItem=files.begin()->first.c_str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Also directories can be opened as files. In this case, this system
|
||||
* call allows to retrieve directory entries.
|
||||
* \param dp pointer to a memory buffer where one or more struct dirent
|
||||
* will be placed. dp must be four words aligned.
|
||||
* \param len memory buffer size.
|
||||
* \return the number of bytes read on success, or a negative number on
|
||||
* failure.
|
||||
*/
|
||||
virtual int getdents(void *dp, int len);
|
||||
|
||||
private:
|
||||
FastMutex& mutex; ///< Mutex of parent class
|
||||
map<StringPart,intrusive_ref_ptr<Device> >& files; ///< Directory entries
|
||||
string currentItem; ///< First unhandled item in directory
|
||||
int currentInode,parentInode; ///< Inodes of . and ..
|
||||
|
||||
bool first; ///< True if first time getdents is called
|
||||
bool last; ///< True if directory has ended
|
||||
};
|
||||
|
||||
int DevFsDirectory::getdents(void *dp, int len)
|
||||
{
|
||||
if(len<minimumBufferSize) return -EINVAL;
|
||||
if(last) return 0;
|
||||
|
||||
Lock<FastMutex> l(mutex);
|
||||
char *begin=reinterpret_cast<char*>(dp);
|
||||
char *buffer=begin;
|
||||
char *end=buffer+len;
|
||||
if(first)
|
||||
{
|
||||
first=false;
|
||||
addDefaultEntries(&buffer,currentInode,parentInode);
|
||||
}
|
||||
if(currentItem.empty()==false)
|
||||
{
|
||||
map<StringPart,intrusive_ref_ptr<Device> >::iterator it;
|
||||
it=files.find(StringPart(currentItem));
|
||||
//Someone deleted the exact directory entry we had saved (unlikely)
|
||||
if(it==files.end()) return -EBADF;
|
||||
for(;it!=files.end();++it)
|
||||
{
|
||||
struct stat st;
|
||||
it->second->fstat(&st);
|
||||
if(addEntry(&buffer,end,st.st_ino,st.st_mode>>12,it->first)>0)
|
||||
continue;
|
||||
//Buffer finished
|
||||
currentItem=it->first.c_str();
|
||||
return buffer-begin;
|
||||
}
|
||||
}
|
||||
addTerminatingEntry(&buffer,end);
|
||||
last=true;
|
||||
return buffer-begin;
|
||||
}
|
||||
|
||||
//
|
||||
// class DevFs
|
||||
//
|
||||
|
||||
DevFs::DevFs() : mutex(FastMutex::RECURSIVE), inodeCount(rootDirInode+1)
|
||||
{
|
||||
addDevice("null",intrusive_ref_ptr<Device>(new Device(Device::STREAM)));
|
||||
addDevice("zero",intrusive_ref_ptr<Device>(new Device(Device::STREAM)));
|
||||
}
|
||||
|
||||
bool DevFs::addDevice(const char *name, intrusive_ref_ptr<Device> dev)
|
||||
{
|
||||
if(name==0 || name[0]=='\0') return false;
|
||||
int len=strlen(name);
|
||||
for(int i=0;i<len;i++) if(name[i]=='/') return false;
|
||||
Lock<FastMutex> l(mutex);
|
||||
bool result=files.insert(make_pair(StringPart(name),dev)).second;
|
||||
//Assign inode to the file
|
||||
if(result) dev->setFileInfo(atomicAddExchange(&inodeCount,1),filesystemId);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DevFs::remove(const char* name)
|
||||
{
|
||||
if(name==0 || name[0]=='\0') return false;
|
||||
Lock<FastMutex> l(mutex);
|
||||
map<StringPart,intrusive_ref_ptr<Device> >::iterator it;
|
||||
it=files.find(StringPart(name));
|
||||
if(it==files.end()) return false;
|
||||
files.erase(StringPart(name));
|
||||
return true;
|
||||
}
|
||||
|
||||
int DevFs::open(intrusive_ref_ptr<FileBase>& file, StringPart& name,
|
||||
int flags, int mode)
|
||||
{
|
||||
if(flags & (O_APPEND | O_EXCL)) return -EACCES;
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(name.empty()) //Trying to open the root directory of the fs
|
||||
{
|
||||
if(flags & (O_WRONLY | O_RDWR)) return -EACCES;
|
||||
file=intrusive_ref_ptr<FileBase>(
|
||||
new DevFsDirectory(shared_from_this(),
|
||||
mutex,files,rootDirInode,parentFsMountpointInode));
|
||||
return 0;
|
||||
}
|
||||
map<StringPart,intrusive_ref_ptr<Device> >::iterator it=files.find(name);
|
||||
if(it==files.end()) return -ENOENT;
|
||||
return it->second->open(file,shared_from_this(),flags,mode);
|
||||
}
|
||||
|
||||
int DevFs::lstat(StringPart& name, struct stat *pstat)
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(name.empty())
|
||||
{
|
||||
fillStatHelper(pstat,rootDirInode,filesystemId,S_IFDIR | 0755);//drwxr-xr-x
|
||||
return 0;
|
||||
}
|
||||
map<StringPart,intrusive_ref_ptr<Device> >::iterator it=files.find(name);
|
||||
if(it==files.end()) return -ENOENT;
|
||||
return it->second->fstat(pstat);
|
||||
}
|
||||
|
||||
int DevFs::unlink(StringPart& name)
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(files.erase(name)==1) return 0;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int DevFs::rename(StringPart& oldName, StringPart& newName)
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
map<StringPart,intrusive_ref_ptr<Device> >::iterator it=files.find(oldName);
|
||||
if(it==files.end()) return -ENOENT;
|
||||
for(unsigned int i=0;i<newName.length();i++)
|
||||
if(newName[i]=='/')
|
||||
return -EACCES; //DevFs does not support subdirectories
|
||||
files.erase(newName); //If it exists
|
||||
files.insert(make_pair(newName,it->second));
|
||||
files.erase(it);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DevFs::mkdir(StringPart& name, int mode)
|
||||
{
|
||||
return -EACCES; // No directories support in DevFs yet
|
||||
}
|
||||
|
||||
int DevFs::rmdir(StringPart& name)
|
||||
{
|
||||
return -EACCES; // No directories support in DevFs yet
|
||||
}
|
||||
|
||||
#endif //WITH_DEVFS
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,272 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef DEVFS_H
|
||||
#define DEVFS_H
|
||||
|
||||
#include <map>
|
||||
#include "filesystem/file.h"
|
||||
#include "filesystem/stringpart.h"
|
||||
#include "kernel/sync.h"
|
||||
#include "config/miosix_settings.h"
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* Instances of this class are devices inside DevFs. When open is called, a
|
||||
* DevFsFile is returned, which has its own seek point so that multiple files
|
||||
* can be opened on the same device retaining an unique seek point. A DevFsFile
|
||||
* then calls readBlock() and writeBlock() on this class. These functions have a
|
||||
* third argument which is the seek point, making them stateless.
|
||||
*
|
||||
* Individual devices must subclass Device and reimplement readBlock(),
|
||||
* writeBlock() and ioctl() as needed. A mutex may be required as multiple
|
||||
* concurrent readBlock(), writeBlock() and ioctl() can occur.
|
||||
*
|
||||
* Classes of this type are reference counted, must be allocated on the heap
|
||||
* and managed through intrusive_ref_ptr<FileBase>
|
||||
*
|
||||
* This class is defined also if WITH_DEVFS is not defined as it is used by the
|
||||
* Console interface, but in this case the interface is reduced to a minimum
|
||||
*/
|
||||
class Device : public IntrusiveRefCounted,
|
||||
public IntrusiveRefCountedSharedFromThis<Device>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Possible device types
|
||||
*/
|
||||
enum DeviceType
|
||||
{
|
||||
STREAM, ///< Not seekable device, like /dev/random
|
||||
BLOCK, ///< Seekable block device
|
||||
TTY ///< Like STREAM, but additionally is a TTY
|
||||
};
|
||||
/**
|
||||
* Constructor
|
||||
* \param d device type
|
||||
*/
|
||||
Device(DeviceType d) : seekable(d==BLOCK), block(d==BLOCK), tty(d==TTY)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Return an instance of the file type managed by this Device
|
||||
* \param file the file object will be stored here, if the call succeeds
|
||||
* \param fs pointer to the DevFs
|
||||
* \param flags file flags (open for reading, writing, ...)
|
||||
* \param mode file permissions
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
int open(intrusive_ref_ptr<FileBase>& file,
|
||||
intrusive_ref_ptr<FilesystemBase> fs, int flags, int mode);
|
||||
|
||||
/**
|
||||
* Obtain information for the file type managed by this Device
|
||||
* \param pstat file information is stored here
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
int fstat(struct stat *pstat) const;
|
||||
|
||||
/**
|
||||
* Check whether the file refers to a terminal.
|
||||
* \return 1 if it is a terminal, 0 if it is not, or a negative number in
|
||||
* case of errors
|
||||
*/
|
||||
virtual int isatty() const;
|
||||
|
||||
#ifdef WITH_DEVFS
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Called be DevFs to assign a device and inode to the Device
|
||||
*/
|
||||
void setFileInfo(unsigned int st_ino, short st_dev)
|
||||
{
|
||||
this->st_ino=st_ino;
|
||||
this->st_dev=st_dev;
|
||||
}
|
||||
|
||||
#endif //WITH_DEVFS
|
||||
|
||||
/**
|
||||
* Read a block of data
|
||||
* \param buffer buffer where read data will be stored
|
||||
* \param size buffer size
|
||||
* \param where where to read from
|
||||
* \return number of bytes read or a negative number on failure
|
||||
*/
|
||||
virtual ssize_t readBlock(void *buffer, size_t size, off_t where);
|
||||
|
||||
/**
|
||||
* Write a block of data
|
||||
* \param buffer buffer where take data to write
|
||||
* \param size buffer size
|
||||
* \param where where to write to
|
||||
* \return number of bytes written or a negative number on failure
|
||||
*/
|
||||
virtual ssize_t writeBlock(const void *buffer, size_t size, off_t where);
|
||||
|
||||
/**
|
||||
* Write a string.
|
||||
* An extension to the Device interface that adds a new member function,
|
||||
* which is used by the kernel on console devices to write debug information
|
||||
* before the kernel is started or in case of serious errors, right before
|
||||
* rebooting.
|
||||
* Can ONLY be called when the kernel is not yet started, paused or within
|
||||
* an interrupt. This default implementation ignores writes.
|
||||
* \param str the string to write. The string must be NUL terminated.
|
||||
*/
|
||||
virtual void IRQwrite(const char *str);
|
||||
|
||||
/**
|
||||
* Performs device-specific operations
|
||||
* \param cmd specifies the operation to perform
|
||||
* \param arg optional argument that some operation require
|
||||
* \return the exact return value depends on CMD, -1 is returned on error
|
||||
*/
|
||||
virtual int ioctl(int cmd, void *arg);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~Device();
|
||||
|
||||
protected:
|
||||
unsigned int st_ino; ///< inode of device file
|
||||
short st_dev; ///< device (unique id of the filesystem) of device file
|
||||
const bool seekable; ///< If true, device is seekable
|
||||
const bool block; ///< If true, it is a block device
|
||||
const bool tty; ///< If true, it is a tty
|
||||
};
|
||||
|
||||
#ifdef WITH_DEVFS
|
||||
|
||||
/**
|
||||
* DevFs is a special filesystem meant to access devices as they were files.
|
||||
* For this reason, it is a little different from other filesystems. Normal
|
||||
* filesystems create FileBase objects ondemand, to answer an open() call. Such
|
||||
* files have a parent pointer that points to the filesystem. On the contrary,
|
||||
* DevFs is a collection of both pre-existing DeviceFiles (for stateless files),
|
||||
* or DeviceFileGenerators for stateful ones. Each device file is a different
|
||||
* subclass of FileBase that overrides some of its member functions to access
|
||||
* the handled device. These FileBase subclasses do not have a parent pointer
|
||||
* into DevFs, and as such umounting DevFs should better be avoided, as it's
|
||||
* not possible to detect if some of its files are currently opened by some
|
||||
* application. What will happen is that the individual files (and
|
||||
* DeviceFileGenerators) won't be deleted until the processes that have them
|
||||
* opened close them.
|
||||
*/
|
||||
class DevFs : public FilesystemBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
DevFs();
|
||||
|
||||
/**
|
||||
* Add a device file to DevFs
|
||||
* \param name File name, must not start with a slash
|
||||
* \param df Device file. Every open() call will return the same file
|
||||
* \return true if the file was successfully added
|
||||
*/
|
||||
bool addDevice(const char *name, intrusive_ref_ptr<Device> dev);
|
||||
|
||||
/**
|
||||
* Remove a device. This prevents the device from being opened again,
|
||||
* but if at the time this member function is called the file is already
|
||||
* opened, it won't be deallocated till the application closes it, thanks
|
||||
* to the reference counting scheme.
|
||||
* \param name name of file to remove
|
||||
* \return true if the file was successfully removed
|
||||
*/
|
||||
bool remove(const char *name);
|
||||
|
||||
/**
|
||||
* Open a file
|
||||
* \param file the file object will be stored here, if the call succeeds
|
||||
* \param name the name of the file to open, relative to the local
|
||||
* filesystem
|
||||
* \param flags file flags (open for reading, writing, ...)
|
||||
* \param mode file permissions
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int open(intrusive_ref_ptr<FileBase>& file, StringPart& name,
|
||||
int flags, int mode);
|
||||
|
||||
/**
|
||||
* Obtain information on a file, identified by a path name. Does not follow
|
||||
* symlinks
|
||||
* \param name path name, relative to the local filesystem
|
||||
* \param pstat file information is stored here
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int lstat(StringPart& name, struct stat *pstat);
|
||||
|
||||
/**
|
||||
* Remove a file or directory
|
||||
* \param name path name of file or directory to remove
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int unlink(StringPart& name);
|
||||
|
||||
/**
|
||||
* Rename a file or directory
|
||||
* \param oldName old file name
|
||||
* \param newName new file name
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int rename(StringPart& oldName, StringPart& newName);
|
||||
|
||||
/**
|
||||
* Create a directory
|
||||
* \param name directory name
|
||||
* \param mode directory permissions
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int mkdir(StringPart& name, int mode);
|
||||
|
||||
/**
|
||||
* Remove a directory if empty
|
||||
* \param name directory name
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int rmdir(StringPart& name);
|
||||
|
||||
private:
|
||||
|
||||
FastMutex mutex;
|
||||
std::map<StringPart,intrusive_ref_ptr<Device> > files;
|
||||
int inodeCount;
|
||||
static const int rootDirInode=1;
|
||||
};
|
||||
|
||||
#endif //WITH_DEVFS
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //DEVFS_H
|
||||
|
|
@ -0,0 +1,530 @@
|
|||
/*------------------------------------------------------------------------*/
|
||||
/* Unicode - Local code bidirectional converter (C)ChaN, 2012 */
|
||||
/* (SBCS code pages) */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* 437 U.S. (OEM)
|
||||
/ 720 Arabic (OEM)
|
||||
/ 1256 Arabic (Windows)
|
||||
/ 737 Greek (OEM)
|
||||
/ 1253 Greek (Windows)
|
||||
/ 1250 Central Europe (Windows)
|
||||
/ 775 Baltic (OEM)
|
||||
/ 1257 Baltic (Windows)
|
||||
/ 850 Multilingual Latin 1 (OEM)
|
||||
/ 852 Latin 2 (OEM)
|
||||
/ 1252 Latin 1 (Windows)
|
||||
/ 855 Cyrillic (OEM)
|
||||
/ 1251 Cyrillic (Windows)
|
||||
/ 866 Russian (OEM)
|
||||
/ 857 Turkish (OEM)
|
||||
/ 1254 Turkish (Windows)
|
||||
/ 858 Multilingual Latin 1 + Euro (OEM)
|
||||
/ 862 Hebrew (OEM)
|
||||
/ 1255 Hebrew (Windows)
|
||||
/ 874 Thai (OEM, Windows)
|
||||
/ 1258 Vietnam (OEM, Windows)
|
||||
*/
|
||||
|
||||
#include "ff.h"
|
||||
#include "config/miosix_settings.h"
|
||||
|
||||
#ifdef WITH_FILESYSTEM
|
||||
|
||||
#if _CODE_PAGE == 437
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP437(0x80-0xFF) to Unicode conversion table */
|
||||
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
|
||||
0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
|
||||
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
|
||||
0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
|
||||
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
|
||||
0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
||||
0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
|
||||
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
|
||||
0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
|
||||
0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
|
||||
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
|
||||
0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 720
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP720(0x80-0xFF) to Unicode conversion table */
|
||||
0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7,
|
||||
0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9,
|
||||
0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627,
|
||||
0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F,
|
||||
0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
||||
0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
|
||||
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
|
||||
0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||
0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642,
|
||||
0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A,
|
||||
0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x2248,
|
||||
0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 737
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP737(0x80-0xFF) to Unicode conversion table */
|
||||
0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398,
|
||||
0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0,
|
||||
0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9,
|
||||
0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
|
||||
0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0,
|
||||
0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
||||
0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
|
||||
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
|
||||
0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||
0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD,
|
||||
0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E,
|
||||
0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248,
|
||||
0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 775
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP775(0x80-0xFF) to Unicode conversion table */
|
||||
0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107,
|
||||
0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5,
|
||||
0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A,
|
||||
0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4,
|
||||
0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6,
|
||||
0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118,
|
||||
0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A,
|
||||
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D,
|
||||
0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B,
|
||||
0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||
0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144,
|
||||
0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019,
|
||||
0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E,
|
||||
0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 850
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP850(0x80-0xFF) to Unicode conversion table */
|
||||
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
|
||||
0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
|
||||
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
|
||||
0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
|
||||
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
|
||||
0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
|
||||
0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
|
||||
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
|
||||
0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE,
|
||||
0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
|
||||
0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,
|
||||
0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
|
||||
0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
|
||||
0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 852
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP852(0x80-0xFF) to Unicode conversion table */
|
||||
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7,
|
||||
0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106,
|
||||
0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A,
|
||||
0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D,
|
||||
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E,
|
||||
0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A,
|
||||
0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103,
|
||||
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
|
||||
0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE,
|
||||
0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580,
|
||||
0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161,
|
||||
0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4,
|
||||
0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8,
|
||||
0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 855
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP855(0x80-0xFF) to Unicode conversion table */
|
||||
0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404,
|
||||
0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408,
|
||||
0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C,
|
||||
0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A,
|
||||
0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414,
|
||||
0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438,
|
||||
0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A,
|
||||
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
|
||||
0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E,
|
||||
0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580,
|
||||
0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443,
|
||||
0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116,
|
||||
0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D,
|
||||
0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 857
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP857(0x80-0xFF) to Unicode conversion table */
|
||||
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
|
||||
0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5,
|
||||
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
|
||||
0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F,
|
||||
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F,
|
||||
0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
|
||||
0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
|
||||
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
|
||||
0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE,
|
||||
0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
|
||||
0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000,
|
||||
0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4,
|
||||
0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
|
||||
0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 858
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP858(0x80-0xFF) to Unicode conversion table */
|
||||
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
|
||||
0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
|
||||
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
|
||||
0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
|
||||
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
|
||||
0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
|
||||
0x00A9, 0x2563, 0x2551, 0x2557, 0x2550, 0x00A2, 0x00A5, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
|
||||
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
|
||||
0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x20AC, 0x00CD, 0x00CE,
|
||||
0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00C6, 0x00CC, 0x2580,
|
||||
0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,
|
||||
0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
|
||||
0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
|
||||
0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 862
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP862(0x80-0xFF) to Unicode conversion table */
|
||||
0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,
|
||||
0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
|
||||
0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,
|
||||
0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
|
||||
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
|
||||
0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
||||
0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
|
||||
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
|
||||
0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
|
||||
0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
|
||||
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
|
||||
0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 866
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP866(0x80-0xFF) to Unicode conversion table */
|
||||
0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
|
||||
0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
|
||||
0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
|
||||
0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
|
||||
0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
|
||||
0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
||||
0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
|
||||
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
|
||||
0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||
0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
|
||||
0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
|
||||
0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E,
|
||||
0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 874
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP874(0x80-0xFF) to Unicode conversion table */
|
||||
0x20AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x2026, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x00A0, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07,
|
||||
0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F,
|
||||
0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17,
|
||||
0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F,
|
||||
0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27,
|
||||
0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F,
|
||||
0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37,
|
||||
0x0E38, 0x0E39, 0x0E3A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0E3F,
|
||||
0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47,
|
||||
0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F,
|
||||
0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57,
|
||||
0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0x0000, 0x0000, 0x0000, 0x0000
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 1250
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP1250(0x80-0xFF) to Unicode conversion table */
|
||||
0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||
0x0000, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179,
|
||||
0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||
0x0000, 0x2122, 0x0161, 0x203A, 0x015B, 0x0165, 0x017E, 0x017A,
|
||||
0x00A0, 0x02C7, 0x02D8, 0x0141, 0x00A4, 0x0104, 0x00A6, 0x00A7,
|
||||
0x00A8, 0x00A9, 0x015E, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x017B,
|
||||
0x00B0, 0x00B1, 0x02DB, 0x0142, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
|
||||
0x00B8, 0x0105, 0x015F, 0x00BB, 0x013D, 0x02DD, 0x013E, 0x017C,
|
||||
0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7,
|
||||
0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E,
|
||||
0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7,
|
||||
0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF,
|
||||
0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7,
|
||||
0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F,
|
||||
0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7,
|
||||
0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 1251
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP1251(0x80-0xFF) to Unicode conversion table */
|
||||
0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||
0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F,
|
||||
0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||
0x0000, 0x2111, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F,
|
||||
0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7,
|
||||
0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407,
|
||||
0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7,
|
||||
0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457,
|
||||
0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
|
||||
0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
|
||||
0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
|
||||
0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
|
||||
0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
|
||||
0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
|
||||
0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
|
||||
0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 1252
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP1252(0x80-0xFF) to Unicode conversion table */
|
||||
0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||
0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017D, 0x0000,
|
||||
0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||
0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x017E, 0x0178,
|
||||
0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
|
||||
0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
|
||||
0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
|
||||
0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
|
||||
0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
|
||||
0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
|
||||
0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
|
||||
0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
|
||||
0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
|
||||
0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
|
||||
0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
|
||||
0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 1253
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP1253(0x80-0xFF) to Unicode conversion table */
|
||||
0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||
0x0000, 0x2030, 0x0000, 0x2039, 0x000C, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||
0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x00A0, 0x0385, 0x0386, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
|
||||
0x00A8, 0x00A9, 0x0000, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x2015,
|
||||
0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x00B5, 0x00B6, 0x00B7,
|
||||
0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F,
|
||||
0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
|
||||
0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,
|
||||
0x03A0, 0x03A1, 0x0000, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7,
|
||||
0x03A8, 0x03A9, 0x03AA, 0x03AD, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
|
||||
0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
|
||||
0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
|
||||
0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,
|
||||
0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x0000
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 1254
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP1254(0x80-0xFF) to Unicode conversion table */
|
||||
0x20AC, 0x0000, 0x210A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||
0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||
0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178,
|
||||
0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
|
||||
0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
|
||||
0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
|
||||
0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
|
||||
0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
|
||||
0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
|
||||
0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
|
||||
0x00D8, 0x00D9, 0x00DA, 0x00BD, 0x00DC, 0x0130, 0x015E, 0x00DF,
|
||||
0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
|
||||
0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
|
||||
0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
|
||||
0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 1255
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP1255(0x80-0xFF) to Unicode conversion table */
|
||||
0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||
0x02C6, 0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||
0x02DC, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
|
||||
0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
|
||||
0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
|
||||
0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
|
||||
0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7,
|
||||
0x05B8, 0x05B9, 0x0000, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF,
|
||||
0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05F0, 0x05F1, 0x05F2, 0x05F3,
|
||||
0x05F4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,
|
||||
0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
|
||||
0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,
|
||||
0x05E8, 0x05E9, 0x05EA, 0x0000, 0x0000, 0x200E, 0x200F, 0x0000
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 1256
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP1256(0x80-0xFF) to Unicode conversion table */
|
||||
0x20AC, 0x067E, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||
0x02C6, 0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688,
|
||||
0x06AF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||
0x06A9, 0x2122, 0x0691, 0x203A, 0x0153, 0x200C, 0x200D, 0x06BA,
|
||||
0x00A0, 0x060C, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
|
||||
0x00A8, 0x00A9, 0x06BE, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
|
||||
0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
|
||||
0x00B8, 0x00B9, 0x061B, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x061F,
|
||||
0x06C1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627,
|
||||
0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F,
|
||||
0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00D7,
|
||||
0x0637, 0x0638, 0x0639, 0x063A, 0x0640, 0x0640, 0x0642, 0x0643,
|
||||
0x00E0, 0x0644, 0x00E2, 0x0645, 0x0646, 0x0647, 0x0648, 0x00E7,
|
||||
0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0649, 0x064A, 0x00EE, 0x00EF,
|
||||
0x064B, 0x064C, 0x064D, 0x064E, 0x00F4, 0x064F, 0x0650, 0x00F7,
|
||||
0x0651, 0x00F9, 0x0652, 0x00FB, 0x00FC, 0x200E, 0x200F, 0x06D2
|
||||
}
|
||||
|
||||
#elif _CODE_PAGE == 1257
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP1257(0x80-0xFF) to Unicode conversion table */
|
||||
0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||
0x0000, 0x2030, 0x0000, 0x2039, 0x0000, 0x00A8, 0x02C7, 0x00B8,
|
||||
0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||
0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x00AF, 0x02DB, 0x0000,
|
||||
0x00A0, 0x0000, 0x00A2, 0x00A3, 0x00A4, 0x0000, 0x00A6, 0x00A7,
|
||||
0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
|
||||
0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
|
||||
0x00B8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6,
|
||||
0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112,
|
||||
0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B,
|
||||
0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7,
|
||||
0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF,
|
||||
0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113,
|
||||
0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C,
|
||||
0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7,
|
||||
0x0173, 0x014E, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x02D9
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 1258
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP1258(0x80-0xFF) to Unicode conversion table */
|
||||
0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||
0x02C6, 0x2030, 0x0000, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||
0x02DC, 0x2122, 0x0000, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178,
|
||||
0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
|
||||
0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
|
||||
0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
|
||||
0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
|
||||
0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
|
||||
0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x0300, 0x00CD, 0x00CE, 0x00CF,
|
||||
0x0110, 0x00D1, 0x0309, 0x00D3, 0x00D4, 0x01A0, 0x00D6, 0x00D7,
|
||||
0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x01AF, 0x0303, 0x00DF,
|
||||
0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
|
||||
0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0301, 0x00ED, 0x00EE, 0x00EF,
|
||||
0x0111, 0x00F1, 0x0323, 0x00F3, 0x00F4, 0x01A1, 0x00F6, 0x00F7,
|
||||
0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x01B0, 0x20AB, 0x00FF
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if !_TBLDEF || !_USE_LFN
|
||||
#error This file is not needed in current configuration. Remove from the project.
|
||||
#endif
|
||||
|
||||
|
||||
WCHAR ff_convert ( /* Converted character, Returns zero on error */
|
||||
WCHAR chr, /* Character code to be converted */
|
||||
UINT dir /* 0: Unicode to OEMCP, 1: OEMCP to Unicode */
|
||||
)
|
||||
{
|
||||
WCHAR c;
|
||||
|
||||
|
||||
if (chr < 0x80) { /* ASCII */
|
||||
c = chr;
|
||||
|
||||
} else {
|
||||
if (dir) { /* OEMCP to Unicode */
|
||||
c = (chr >= 0x100) ? 0 : Tbl[chr - 0x80];
|
||||
|
||||
} else { /* Unicode to OEMCP */
|
||||
for (c = 0; c < 0x80; c++) {
|
||||
if (chr == Tbl[c]) break;
|
||||
}
|
||||
c = (c + 0x80) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
#endif //WITH_FILESYSTEM
|
||||
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Integration of FatFs filesystem module in Miosix by Terraneo Federico
|
||||
* based on original files diskio.c and mmc.c by ChaN
|
||||
*/
|
||||
|
||||
#include "diskio.h"
|
||||
#include "filesystem/ioctl.h"
|
||||
#include "config/miosix_settings.h"
|
||||
|
||||
#ifdef WITH_FILESYSTEM
|
||||
|
||||
using namespace miosix;
|
||||
|
||||
// #ifdef __cplusplus
|
||||
// extern "C" {
|
||||
// #endif
|
||||
|
||||
///**
|
||||
// * \internal
|
||||
// * Initializes drive.
|
||||
// */
|
||||
//DSTATUS disk_initialize (
|
||||
// intrusive_ref_ptr<FileBase> pdrv /* Physical drive nmuber (0..) */
|
||||
//)
|
||||
//{
|
||||
// if(Disk::isAvailable()==false) return STA_NODISK;
|
||||
// Disk::init();
|
||||
// if(Disk::isInitialized()) return RES_OK;
|
||||
// else return STA_NOINIT;
|
||||
//}
|
||||
|
||||
///**
|
||||
// * \internal
|
||||
// * Return status of drive.
|
||||
// */
|
||||
//DSTATUS disk_status (
|
||||
// intrusive_ref_ptr<FileBase> pdrv /* Physical drive nmuber (0..) */
|
||||
//)
|
||||
//{
|
||||
// if(Disk::isInitialized()) return RES_OK;
|
||||
// else return STA_NOINIT;
|
||||
//}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Read one or more sectors from drive
|
||||
*/
|
||||
DRESULT disk_read (
|
||||
intrusive_ref_ptr<FileBase> pdrv, /* Physical drive nmuber (0..) */
|
||||
BYTE *buff, /* Data buffer to store read data */
|
||||
DWORD sector, /* Sector address (LBA) */
|
||||
UINT count /* Number of sectors to read (1..255) */
|
||||
)
|
||||
{
|
||||
if(pdrv->lseek(static_cast<off_t>(sector)*512,SEEK_SET)<0) return RES_ERROR;
|
||||
if(pdrv->read(buff,count*512)!=static_cast<ssize_t>(count)*512) return RES_ERROR;
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Write one or more sectors to drive
|
||||
*/
|
||||
DRESULT disk_write (
|
||||
intrusive_ref_ptr<FileBase> pdrv, /* Physical drive nmuber (0..) */
|
||||
const BYTE *buff, /* Data to be written */
|
||||
DWORD sector, /* Sector address (LBA) */
|
||||
UINT count /* Number of sectors to write (1..255) */
|
||||
)
|
||||
{
|
||||
if(pdrv->lseek(static_cast<off_t>(sector)*512,SEEK_SET)<0) return RES_ERROR;
|
||||
if(pdrv->write(buff,count*512)!=static_cast<ssize_t>(count)*512) return RES_ERROR;
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* To perform disk functions other thar read/write
|
||||
*/
|
||||
DRESULT disk_ioctl (
|
||||
intrusive_ref_ptr<FileBase> pdrv, /* Physical drive nmuber (0..) */
|
||||
BYTE ctrl, /* Control code */
|
||||
void *buff /* Buffer to send/receive control data */
|
||||
)
|
||||
{
|
||||
switch(ctrl)
|
||||
{
|
||||
case CTRL_SYNC:
|
||||
if(pdrv->ioctl(IOCTL_SYNC,0)==0) return RES_OK; else return RES_ERROR;
|
||||
case GET_SECTOR_COUNT:
|
||||
return RES_ERROR; //unimplemented, so f_mkfs() does not work
|
||||
case GET_BLOCK_SIZE:
|
||||
return RES_ERROR; //unimplemented, so f_mkfs() does not work
|
||||
default:
|
||||
return RES_PARERR;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Return current time, used to save file creation time
|
||||
*/
|
||||
DWORD get_fattime()
|
||||
{
|
||||
return 0x210000;//TODO: this stub just returns date 01/01/1980 0.00.00
|
||||
}
|
||||
|
||||
// #ifdef __cplusplus
|
||||
// }
|
||||
// #endif
|
||||
|
||||
#endif //WITH_FILESYSTEM
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/*-----------------------------------------------------------------------
|
||||
/ Low level disk interface modlue include file (C)ChaN, 2013
|
||||
/-----------------------------------------------------------------------*/
|
||||
|
||||
#ifndef _DISKIO_DEFINED
|
||||
#define _DISKIO_DEFINED
|
||||
|
||||
//#ifdef __cplusplus
|
||||
//extern "C" {
|
||||
//#endif
|
||||
|
||||
#define _USE_WRITE 1 /* 1: Enable disk_write function */
|
||||
#define _USE_IOCTL 1 /* 1: Enable disk_ioctl fucntion */
|
||||
|
||||
#include "integer.h"
|
||||
#include <filesystem/file.h>
|
||||
#include "config/miosix_settings.h"
|
||||
|
||||
#ifdef WITH_FILESYSTEM
|
||||
|
||||
|
||||
/* Status of Disk Functions */
|
||||
typedef BYTE DSTATUS;
|
||||
|
||||
/* Results of Disk Functions */
|
||||
typedef enum {
|
||||
RES_OK = 0, /* 0: Successful */
|
||||
RES_ERROR, /* 1: R/W Error */
|
||||
RES_WRPRT, /* 2: Write Protected */
|
||||
RES_NOTRDY, /* 3: Not Ready */
|
||||
RES_PARERR /* 4: Invalid Parameter */
|
||||
} DRESULT;
|
||||
|
||||
|
||||
/*---------------------------------------*/
|
||||
/* Prototypes for disk control functions */
|
||||
|
||||
|
||||
DSTATUS disk_initialize (miosix::intrusive_ref_ptr<miosix::FileBase> pdrv);
|
||||
DSTATUS disk_status (miosix::intrusive_ref_ptr<miosix::FileBase> pdrv);
|
||||
DRESULT disk_read (miosix::intrusive_ref_ptr<miosix::FileBase> pdrv,
|
||||
BYTE*buff, DWORD sector, UINT count);
|
||||
DRESULT disk_write (miosix::intrusive_ref_ptr<miosix::FileBase> pdrv,
|
||||
const BYTE* buff, DWORD sector, UINT count);
|
||||
DRESULT disk_ioctl (miosix::intrusive_ref_ptr<miosix::FileBase> pdrv,
|
||||
BYTE cmd, void* buff);
|
||||
|
||||
|
||||
/* Disk Status Bits (DSTATUS) */
|
||||
#define STA_NOINIT 0x01 /* Drive not initialized */
|
||||
#define STA_NODISK 0x02 /* No medium in the drive */
|
||||
#define STA_PROTECT 0x04 /* Write protected */
|
||||
|
||||
|
||||
/* Command code for disk_ioctrl fucntion */
|
||||
|
||||
/* Generic command (used by FatFs) */
|
||||
#define CTRL_SYNC 0 /* Flush disk cache (for write functions) */
|
||||
#define GET_SECTOR_COUNT 1 /* Get media size (for only f_mkfs()) */
|
||||
#define GET_SECTOR_SIZE 2 /* Get sector size (for multiple sector size (_MAX_SS >= 1024)) */
|
||||
#define GET_BLOCK_SIZE 3 /* Get erase block size (for only f_mkfs()) */
|
||||
#define CTRL_ERASE_SECTOR 4 /* Force erased a block of sectors (for only _USE_ERASE) */
|
||||
|
||||
/* Generic command (not used by FatFs) */
|
||||
#define CTRL_POWER 5 /* Get/Set power status */
|
||||
#define CTRL_LOCK 6 /* Lock/Unlock media removal */
|
||||
#define CTRL_EJECT 7 /* Eject media */
|
||||
#define CTRL_FORMAT 8 /* Create physical format on the media */
|
||||
|
||||
/* MMC/SDC specific ioctl command */
|
||||
#define MMC_GET_TYPE 10 /* Get card type */
|
||||
#define MMC_GET_CSD 11 /* Get CSD */
|
||||
#define MMC_GET_CID 12 /* Get CID */
|
||||
#define MMC_GET_OCR 13 /* Get OCR */
|
||||
#define MMC_GET_SDSTAT 14 /* Get SD status */
|
||||
|
||||
/* ATA/CF specific ioctl command */
|
||||
#define ATA_GET_REV 20 /* Get F/W revision */
|
||||
#define ATA_GET_MODEL 21 /* Get model name */
|
||||
#define ATA_GET_SN 22 /* Get serial number */
|
||||
|
||||
|
||||
/* MMC card type flags (MMC_GET_TYPE) */
|
||||
#define CT_MMC 0x01 /* MMC ver 3 */
|
||||
#define CT_SD1 0x02 /* SD ver 1 */
|
||||
#define CT_SD2 0x04 /* SD ver 2 */
|
||||
#define CT_SDC (CT_SD1|CT_SD2) /* SD */
|
||||
#define CT_BLOCK 0x08 /* Block addressing */
|
||||
|
||||
#endif //WITH_FILESYSTEM
|
||||
|
||||
|
||||
//#ifdef __cplusplus
|
||||
//}
|
||||
//#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,510 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include "fat32.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include "filesystem/stringpart.h"
|
||||
#include "filesystem/ioctl.h"
|
||||
#include "util/unicode.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace miosix {
|
||||
|
||||
#ifdef WITH_FILESYSTEM
|
||||
|
||||
/**
|
||||
* Translate between FATFS error codes and POSIX ones
|
||||
* \param ec FATS error code
|
||||
* \return POSIX error code
|
||||
*/
|
||||
static int translateError(int ec)
|
||||
{
|
||||
switch(ec)
|
||||
{
|
||||
case FR_OK:
|
||||
return 0;
|
||||
case FR_NO_FILE:
|
||||
case FR_NO_PATH:
|
||||
return -ENOENT;
|
||||
case FR_DENIED:
|
||||
return -ENOSPC;
|
||||
case FR_EXIST:
|
||||
return -EEXIST;
|
||||
case FR_WRITE_PROTECTED:
|
||||
return -EROFS;
|
||||
case FR_LOCKED:
|
||||
return -EBUSY;
|
||||
case FR_NOT_ENOUGH_CORE:
|
||||
return -ENOMEM;
|
||||
case FR_TOO_MANY_OPEN_FILES:
|
||||
return -ENFILE;
|
||||
default:
|
||||
return -EACCES;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Directory class for Fat32Fs
|
||||
*/
|
||||
class Fat32Directory : public DirectoryBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \param parent parent filesystem
|
||||
* \param mutex mutex to lock when accessing the fiesystem
|
||||
* \param currentInode inode value for '.' entry
|
||||
* \param parentInode inode value for '..' entry
|
||||
*/
|
||||
Fat32Directory(intrusive_ref_ptr<FilesystemBase> parent, FastMutex& mutex,
|
||||
int currentInode, int parentInode) : DirectoryBase(parent),
|
||||
mutex(mutex), currentInode(currentInode), parentInode(parentInode),
|
||||
first(true), unfinished(false)
|
||||
{
|
||||
//Make sure a closedir of an uninitialized dir won't do any damage
|
||||
dir.fs=0;
|
||||
fi.lfname=lfn;
|
||||
fi.lfsize=sizeof(lfn);
|
||||
}
|
||||
|
||||
/**
|
||||
* \return the underlying directory object
|
||||
*/
|
||||
DIR_ *directory() { return &dir; }
|
||||
|
||||
/**
|
||||
* Also directories can be opened as files. In this case, this system call
|
||||
* allows to retrieve directory entries.
|
||||
* \param dp pointer to a memory buffer where one or more struct dirent
|
||||
* will be placed. dp must be four words aligned.
|
||||
* \param len memory buffer size.
|
||||
* \return the number of bytes read on success, or a negative number on
|
||||
* failure.
|
||||
*/
|
||||
virtual int getdents(void *dp, int len);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~Fat32Directory();
|
||||
|
||||
private:
|
||||
FastMutex& mutex; ///< Parent filesystem's mutex
|
||||
DIR_ dir; ///< Directory object
|
||||
FILINFO fi; ///< Information on a file
|
||||
int currentInode; ///< Inode of '.'
|
||||
int parentInode; ///< Inode of '..'
|
||||
bool first; ///< To display '.' and '..' entries
|
||||
bool unfinished; ///< True if fi contains unread data
|
||||
char lfn[(_MAX_LFN+1)*2]; ///< Long file name
|
||||
};
|
||||
|
||||
//
|
||||
// class Fat32Directory
|
||||
//
|
||||
|
||||
int Fat32Directory::getdents(void *dp, int len)
|
||||
{
|
||||
if(len<minimumBufferSize) return -EINVAL;
|
||||
char *begin=reinterpret_cast<char*>(dp);
|
||||
char *buffer=begin;
|
||||
char *end=buffer+len;
|
||||
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(first)
|
||||
{
|
||||
first=false;
|
||||
addDefaultEntries(&buffer,currentInode,parentInode);
|
||||
}
|
||||
if(unfinished)
|
||||
{
|
||||
unfinished=false;
|
||||
char type=fi.fattrib & AM_DIR ? DT_DIR : DT_REG;
|
||||
StringPart name(fi.lfname);
|
||||
if(addEntry(&buffer,end,fi.inode,type,name)<0) return -EINVAL;
|
||||
}
|
||||
for(;;)
|
||||
{
|
||||
if(int res=translateError(f_readdir(&dir,&fi))) return res;
|
||||
if(fi.fname[0]=='\0')
|
||||
{
|
||||
addTerminatingEntry(&buffer,end);
|
||||
return buffer-begin;
|
||||
}
|
||||
char type=fi.fattrib & AM_DIR ? DT_DIR : DT_REG;
|
||||
StringPart name(fi.lfname);
|
||||
if(addEntry(&buffer,end,fi.inode,type,name)<0)
|
||||
{
|
||||
unfinished=true;
|
||||
return buffer-begin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Fat32Directory::~Fat32Directory()
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
f_closedir(&dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Files of the Fat32Fs filesystem
|
||||
*/
|
||||
class Fat32File : public FileBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* \param parent the filesystem to which this file belongs
|
||||
*/
|
||||
Fat32File(intrusive_ref_ptr<FilesystemBase> parent, FastMutex& mutex);
|
||||
|
||||
/**
|
||||
* Write data to the file, if the file supports writing.
|
||||
* \param data the data to write
|
||||
* \param len the number of bytes to write
|
||||
* \return the number of written characters, or a negative number in case
|
||||
* of errors
|
||||
*/
|
||||
virtual ssize_t write(const void *data, size_t len);
|
||||
|
||||
/**
|
||||
* Read data from the file, if the file supports reading.
|
||||
* \param data buffer to store read data
|
||||
* \param len the number of bytes to read
|
||||
* \return the number of read characters, or a negative number in case
|
||||
* of errors
|
||||
*/
|
||||
virtual ssize_t read(void *data, size_t len);
|
||||
|
||||
/**
|
||||
* Move file pointer, if the file supports random-access.
|
||||
* \param pos offset to sum to the beginning of the file, current position
|
||||
* or end of file, depending on whence
|
||||
* \param whence SEEK_SET, SEEK_CUR or SEEK_END
|
||||
* \return the offset from the beginning of the file if the operation
|
||||
* completed, or a negative number in case of errors
|
||||
*/
|
||||
virtual off_t lseek(off_t pos, int whence);
|
||||
|
||||
/**
|
||||
* Return file information.
|
||||
* \param pstat pointer to stat struct
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int fstat(struct stat *pstat) const;
|
||||
|
||||
/**
|
||||
* Perform various operations on a file descriptor
|
||||
* \param cmd specifies the operation to perform
|
||||
* \param arg optional argument that some operation require
|
||||
* \return the exact return value depends on CMD, -1 is returned on error
|
||||
*/
|
||||
virtual int ioctl(int cmd, void *arg);
|
||||
|
||||
/**
|
||||
* \return the FatFs FIL object
|
||||
*/
|
||||
FIL *fil() { return &file; }
|
||||
|
||||
/**
|
||||
* \param inode file inode
|
||||
*/
|
||||
void setInode(int inode) { this->inode=inode; }
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Fat32File();
|
||||
|
||||
private:
|
||||
FIL file;
|
||||
FastMutex& mutex;
|
||||
int inode;
|
||||
};
|
||||
|
||||
//
|
||||
// class Fat32File
|
||||
//
|
||||
|
||||
Fat32File::Fat32File(intrusive_ref_ptr<FilesystemBase> parent, FastMutex& mutex)
|
||||
: FileBase(parent), mutex(mutex), inode(0) {}
|
||||
|
||||
ssize_t Fat32File::write(const void *data, size_t len)
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
unsigned int bytesWritten;
|
||||
if(int res=translateError(f_write(&file,data,len,&bytesWritten))) return res;
|
||||
#ifdef SYNC_AFTER_WRITE
|
||||
if(f_sync(&file)!=FR_OK) return -EIO;
|
||||
#endif //SYNC_AFTER_WRITE
|
||||
return static_cast<int>(bytesWritten);
|
||||
}
|
||||
|
||||
ssize_t Fat32File::read(void *data, size_t len)
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
unsigned int bytesRead;
|
||||
if(int res=translateError(f_read(&file,data,len,&bytesRead))) return res;
|
||||
return static_cast<int>(bytesRead);
|
||||
}
|
||||
|
||||
off_t Fat32File::lseek(off_t pos, int whence)
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
off_t offset;
|
||||
switch(whence)
|
||||
{
|
||||
case SEEK_CUR:
|
||||
offset=static_cast<off_t>(f_tell(&file))+pos;
|
||||
break;
|
||||
case SEEK_SET:
|
||||
offset=pos;
|
||||
break;
|
||||
case SEEK_END:
|
||||
offset=static_cast<off_t>(f_size(&file))+pos;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
//We don't support seek past EOF for Fat32
|
||||
if(offset<0 || offset>static_cast<off_t>(f_size(&file))) return -EOVERFLOW;
|
||||
if(int result=translateError(
|
||||
f_lseek(&file,static_cast<unsigned long>(offset)))) return result;
|
||||
return offset;
|
||||
}
|
||||
|
||||
int Fat32File::fstat(struct stat *pstat) const
|
||||
{
|
||||
memset(pstat,0,sizeof(struct stat));
|
||||
pstat->st_dev=getParent()->getFsId();
|
||||
pstat->st_ino=inode;
|
||||
pstat->st_mode=S_IFREG | 0755; //-rwxr-xr-x
|
||||
pstat->st_nlink=1;
|
||||
pstat->st_size=f_size(&file);
|
||||
pstat->st_blksize=512;
|
||||
pstat->st_blocks=(static_cast<off_t>(f_size(&file))+511)/512;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Fat32File::ioctl(int cmd, void *arg)
|
||||
{
|
||||
if(cmd!=IOCTL_SYNC) return -ENOTTY;
|
||||
Lock<FastMutex> l(mutex);
|
||||
return translateError(f_sync(&file));
|
||||
}
|
||||
|
||||
Fat32File::~Fat32File()
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(inode) f_close(&file); //TODO: what to do with error code?
|
||||
}
|
||||
|
||||
//
|
||||
// class Fat32Fs
|
||||
//
|
||||
|
||||
Fat32Fs::Fat32Fs(intrusive_ref_ptr<FileBase> disk)
|
||||
: mutex(FastMutex::RECURSIVE), failed(true)
|
||||
{
|
||||
filesystem.drv=disk;
|
||||
failed=f_mount(&filesystem,1,false)!=FR_OK;
|
||||
}
|
||||
|
||||
int Fat32Fs::open(intrusive_ref_ptr<FileBase>& file, StringPart& name,
|
||||
int flags, int mode)
|
||||
{
|
||||
if(failed) return -ENOENT;
|
||||
flags++; //To convert from O_RDONLY, O_WRONLY, ... to _FREAD, _FWRITE, ...
|
||||
|
||||
// Code path checklist:
|
||||
// Not existent | Regular file | Directory |
|
||||
// ok | ok | ok | _FREAD
|
||||
// ok | ok | ok | _FWRITE
|
||||
// ok | ok | ok | _FWRITE | _FCREAT
|
||||
|
||||
struct stat st;
|
||||
bool statFailed=false;
|
||||
if(int result=lstat(name,&st))
|
||||
{
|
||||
//If _FCREAT the file may not yet exist as we are asked to create it
|
||||
if((flags & (_FWRITE | _FCREAT)) != (_FWRITE | _FCREAT)) return result;
|
||||
else statFailed=true;
|
||||
}
|
||||
|
||||
//Using if short circuit, as st is not initialized if statFailed
|
||||
if(statFailed || !S_ISDIR(st.st_mode))
|
||||
{
|
||||
//About to open a file
|
||||
BYTE openflags=0;
|
||||
if(flags & _FREAD) openflags|=FA_READ;
|
||||
if(flags & _FWRITE) openflags|=FA_WRITE;
|
||||
if(flags & _FTRUNC) openflags|=FA_CREATE_ALWAYS;//Truncate
|
||||
else if(flags & _FCREAT) openflags|=FA_OPEN_ALWAYS;//If !exists create
|
||||
else openflags|=FA_OPEN_EXISTING;//If not exists fail
|
||||
|
||||
intrusive_ref_ptr<Fat32File> f(new Fat32File(shared_from_this(),mutex));
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(int res=translateError(f_open(&filesystem,f->fil(),name.c_str(),openflags)))
|
||||
return res;
|
||||
if(statFailed)
|
||||
{
|
||||
//If we didn't stat before, stat now to get the inode
|
||||
if(int result=lstat(name,&st)) return result;
|
||||
}
|
||||
f->setInode(st.st_ino);
|
||||
|
||||
//Can't open files larger than INT_MAX
|
||||
if(static_cast<int>(f_size(f->fil()))<0) return -EOVERFLOW;
|
||||
|
||||
#ifdef SYNC_AFTER_WRITE
|
||||
if(f_sync(f->fil())!=FR_OK) return -EFAULT;
|
||||
#endif //SYNC_AFTER_WRITE
|
||||
|
||||
//If file opened for appending, seek to end of file
|
||||
if(flags & _FAPPEND)
|
||||
if(f_lseek(f->fil(),f_size(f->fil()))!=FR_OK) return -EFAULT;
|
||||
|
||||
file=f;
|
||||
return 0;
|
||||
} else {
|
||||
//About to open a directory
|
||||
if(flags & (_FWRITE | _FAPPEND | _FCREAT | _FTRUNC)) return -EISDIR;
|
||||
|
||||
int parentInode;
|
||||
if(name.empty()==false)
|
||||
{
|
||||
unsigned int lastSlash=name.findLastOf('/');
|
||||
if(lastSlash!=string::npos)
|
||||
{
|
||||
StringPart parent(name,lastSlash);
|
||||
struct stat st2;
|
||||
if(int result=lstat(parent,&st2)) return result;
|
||||
parentInode=st2.st_ino;
|
||||
} else parentInode=1; //Asked to list subdir of root
|
||||
} else parentInode=parentFsMountpointInode; //Asked to list root dir
|
||||
|
||||
|
||||
intrusive_ref_ptr<Fat32Directory> d(
|
||||
new Fat32Directory(shared_from_this(),mutex,st.st_ino,parentInode));
|
||||
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(int res=translateError(f_opendir(&filesystem,d->directory(),name.c_str())))
|
||||
return res;
|
||||
|
||||
file=d;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int Fat32Fs::lstat(StringPart& name, struct stat *pstat)
|
||||
{
|
||||
if(failed) return -ENOENT;
|
||||
memset(pstat,0,sizeof(struct stat));
|
||||
pstat->st_dev=filesystemId;
|
||||
pstat->st_nlink=1;
|
||||
pstat->st_blksize=512;
|
||||
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(name.empty())
|
||||
{
|
||||
//We are asked to stat the filesystem's root directory
|
||||
//By convention, we use 1 for root dir inode, see INODE() macro in ff.c
|
||||
pstat->st_ino=1;
|
||||
pstat->st_mode=S_IFDIR | 0755; //drwxr-xr-x
|
||||
return 0;
|
||||
}
|
||||
FILINFO info;
|
||||
info.lfname=0; //We're not interested in getting the lfname
|
||||
info.lfsize=0;
|
||||
if(int result=translateError(f_stat(&filesystem,name.c_str(),&info))) return result;
|
||||
|
||||
pstat->st_ino=info.inode;
|
||||
pstat->st_mode=(info.fattrib & AM_DIR) ?
|
||||
S_IFDIR | 0755 //drwxr-xr-x
|
||||
: S_IFREG | 0755; //-rwxr-xr-x
|
||||
pstat->st_size=info.fsize;
|
||||
pstat->st_blocks=(info.fsize+511)/512;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Fat32Fs::unlink(StringPart& name)
|
||||
{
|
||||
return unlinkRmdirHelper(name,false);
|
||||
}
|
||||
|
||||
int Fat32Fs::rename(StringPart& oldName, StringPart& newName)
|
||||
{
|
||||
if(failed) return -ENOENT;
|
||||
Lock<FastMutex> l(mutex);
|
||||
return translateError(f_rename(&filesystem,oldName.c_str(),newName.c_str()));
|
||||
}
|
||||
|
||||
int Fat32Fs::mkdir(StringPart& name, int mode)
|
||||
{
|
||||
if(failed) return -ENOENT;
|
||||
Lock<FastMutex> l(mutex);
|
||||
return translateError(f_mkdir(&filesystem,name.c_str()));
|
||||
}
|
||||
|
||||
int Fat32Fs::rmdir(StringPart& name)
|
||||
{
|
||||
return unlinkRmdirHelper(name,true);
|
||||
}
|
||||
|
||||
Fat32Fs::~Fat32Fs()
|
||||
{
|
||||
if(failed) return;
|
||||
f_mount(&filesystem,0,true); //TODO: what to do with error code?
|
||||
filesystem.drv->ioctl(IOCTL_SYNC,0);
|
||||
filesystem.drv.reset();
|
||||
}
|
||||
|
||||
int Fat32Fs::unlinkRmdirHelper(StringPart& name, bool delDir)
|
||||
{
|
||||
if(failed) return -ENOENT;
|
||||
Lock<FastMutex> l(mutex);
|
||||
struct stat st;
|
||||
if(int result=lstat(name,&st)) return result;
|
||||
if(delDir)
|
||||
{
|
||||
if(!S_ISDIR(st.st_mode)) return -ENOTDIR;
|
||||
} else if(S_ISDIR(st.st_mode)) return -EISDIR;
|
||||
return translateError(f_unlink(&filesystem,name.c_str()));
|
||||
}
|
||||
|
||||
#endif //WITH_FILESYSTEM
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef FAT32_H
|
||||
#define FAT32_H
|
||||
|
||||
#include "filesystem/file.h"
|
||||
#include "kernel/sync.h"
|
||||
#include "ff.h"
|
||||
#include "config/miosix_settings.h"
|
||||
|
||||
namespace miosix {
|
||||
|
||||
#ifdef WITH_FILESYSTEM
|
||||
|
||||
/**
|
||||
* Fat32 Filesystem.
|
||||
*/
|
||||
class Fat32Fs : public FilesystemBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Fat32Fs(intrusive_ref_ptr<FileBase> disk);
|
||||
|
||||
/**
|
||||
* Open a file
|
||||
* \param file the file object will be stored here, if the call succeeds
|
||||
* \param name the name of the file to open, relative to the local
|
||||
* filesystem
|
||||
* \param flags file flags (open for reading, writing, ...)
|
||||
* \param mode file permissions
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int open(intrusive_ref_ptr<FileBase>& file, StringPart& name,
|
||||
int flags, int mode);
|
||||
|
||||
/**
|
||||
* Obtain information on a file, identified by a path name. Does not follow
|
||||
* symlinks
|
||||
* \param name path name, relative to the local filesystem
|
||||
* \param pstat file information is stored here
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int lstat(StringPart& name, struct stat *pstat);
|
||||
|
||||
/**
|
||||
* Remove a file or directory
|
||||
* \param name path name of file or directory to remove
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int unlink(StringPart& name);
|
||||
|
||||
/**
|
||||
* Rename a file or directory
|
||||
* \param oldName old file name
|
||||
* \param newName new file name
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int rename(StringPart& oldName, StringPart& newName);
|
||||
|
||||
/**
|
||||
* Create a directory
|
||||
* \param name directory name
|
||||
* \param mode directory permissions
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int mkdir(StringPart& name, int mode);
|
||||
|
||||
/**
|
||||
* Remove a directory if empty
|
||||
* \param name directory name
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int rmdir(StringPart& name);
|
||||
|
||||
/**
|
||||
* \return true if the filesystem failed to mount
|
||||
*/
|
||||
bool mountFailed() const { return failed; }
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Fat32Fs();
|
||||
|
||||
private:
|
||||
|
||||
int unlinkRmdirHelper(StringPart& name, bool delDir);
|
||||
|
||||
FATFS filesystem;
|
||||
FastMutex mutex;
|
||||
bool failed; ///< Failed to mount
|
||||
};
|
||||
|
||||
#endif //WITH_FILESYSTEM
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //FAT32_H
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,385 @@
|
|||
/*---------------------------------------------------------------------------/
|
||||
/ FatFs - FAT file system module include file R0.10 (C)ChaN, 2013
|
||||
/----------------------------------------------------------------------------/
|
||||
/ FatFs module is a generic FAT file system module for small embedded systems.
|
||||
/ This is a free software that opened for education, research and commercial
|
||||
/ developments under license policy of following terms.
|
||||
/
|
||||
/ Copyright (C) 2013, ChaN, all right reserved.
|
||||
/
|
||||
/ * The FatFs module is a free software and there is NO WARRANTY.
|
||||
/ * No restriction on use. You can use, modify and redistribute it for
|
||||
/ personal, non-profit or commercial product UNDER YOUR RESPONSIBILITY.
|
||||
/ * Redistributions of source code must retain the above copyright notice.
|
||||
/
|
||||
/----------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* This version of FatFs has been modified to adapt it to the requirements of
|
||||
* Miosix:
|
||||
* - C++: moved from C to C++ to allow calling other Miosix code
|
||||
* - utf8: the original FatFs API supported only utf16 for file names, but the
|
||||
* Miosix filesystem API has to be utf8 (aka, according with the
|
||||
* "utf8 everywhere mainfesto", doesn't want to deal with that crap).
|
||||
* For efficiency reasons the unicode conversion is done inside the FatFs code
|
||||
* - removal of global variables: to allow to create an arbitrary number of
|
||||
* independent Fat32 filesystems
|
||||
* - unixification: removal of the dos-like drive numbering scheme and
|
||||
* addition of an inode field in the FILINFO struct
|
||||
*/
|
||||
|
||||
#ifndef _FATFS
|
||||
#define _FATFS 80960 /* Revision ID */
|
||||
|
||||
//#ifdef __cplusplus
|
||||
//extern "C" {
|
||||
//#endif
|
||||
|
||||
#include <filesystem/file.h>
|
||||
#include "config/miosix_settings.h"
|
||||
|
||||
#include "integer.h" /* Basic integer types */
|
||||
#include "ffconf.h" /* FatFs configuration options */
|
||||
|
||||
#if _FATFS != _FFCONF
|
||||
#error Wrong configuration file (ffconf.h).
|
||||
#endif
|
||||
|
||||
#ifdef WITH_FILESYSTEM
|
||||
|
||||
|
||||
|
||||
/* Definitions of volume management */
|
||||
|
||||
#if _MULTI_PARTITION /* Multiple partition configuration */
|
||||
typedef struct {
|
||||
BYTE pd; /* Physical drive number */
|
||||
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||
} PARTITION;
|
||||
extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
|
||||
#define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive number */
|
||||
#define LD2PT(vol) (VolToPart[vol].pt) /* Get partition index */
|
||||
|
||||
#else /* Single partition configuration */
|
||||
#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */
|
||||
#define LD2PT(vol) 0 /* Find first valid partition or in SFD */
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Type of path name strings on FatFs API */
|
||||
|
||||
#if _LFN_UNICODE /* Unicode string */
|
||||
#if !_USE_LFN
|
||||
#error _LFN_UNICODE must be 0 in non-LFN cfg.
|
||||
#endif
|
||||
#ifndef _INC_TCHAR
|
||||
typedef WCHAR TCHAR;
|
||||
#define _T(x) L ## x
|
||||
#define _TEXT(x) L ## x
|
||||
#endif
|
||||
|
||||
#else /* ANSI/OEM string */
|
||||
#ifndef _INC_TCHAR
|
||||
typedef char TCHAR;
|
||||
#define _T(x) x
|
||||
#define _TEXT(x) x
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/* File access control feature */
|
||||
#if _FS_LOCK
|
||||
#if _FS_READONLY
|
||||
#error _FS_LOCK must be 0 at read-only cfg.
|
||||
#endif
|
||||
struct FATFS; //Forward decl
|
||||
|
||||
typedef struct {
|
||||
FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */
|
||||
DWORD clu; /* Object ID 2, directory */
|
||||
WORD idx; /* Object ID 3, directory index */
|
||||
WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */
|
||||
} FILESEM;
|
||||
#endif
|
||||
|
||||
|
||||
/* File system object structure (FATFS) */
|
||||
|
||||
struct FATFS {
|
||||
BYTE fs_type; /* FAT sub-type (0:Not mounted) */
|
||||
//BYTE drv; /* Physical drive number */
|
||||
BYTE csize; /* Sectors per cluster (1,2,4...128) */
|
||||
BYTE n_fats; /* Number of FAT copies (1 or 2) */
|
||||
BYTE wflag; /* win[] flag (b0:dirty) */
|
||||
BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */
|
||||
WORD id; /* File system mount ID */
|
||||
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
|
||||
#if _MAX_SS != 512
|
||||
WORD ssize; /* Bytes per sector (512, 1024, 2048 or 4096) */
|
||||
#endif
|
||||
#if _FS_REENTRANT
|
||||
_SYNC_t sobj; /* Identifier of sync object */
|
||||
#endif
|
||||
#if !_FS_READONLY
|
||||
DWORD last_clust; /* Last allocated cluster */
|
||||
DWORD free_clust; /* Number of free clusters */
|
||||
#endif
|
||||
#if _FS_RPATH
|
||||
DWORD cdir; /* Current directory start cluster (0:root) */
|
||||
#endif
|
||||
DWORD n_fatent; /* Number of FAT entries (= number of clusters + 2) */
|
||||
DWORD fsize; /* Sectors per FAT */
|
||||
DWORD volbase; /* Volume start sector */
|
||||
DWORD fatbase; /* FAT start sector */
|
||||
DWORD dirbase; /* Root directory start sector (FAT32:Cluster#) */
|
||||
DWORD database; /* Data start sector */
|
||||
DWORD winsect; /* Current sector appearing in the win[] */
|
||||
BYTE win[_MAX_SS] __attribute__((aligned(4))); /* Disk access window for Directory, FAT (and file data at tiny cfg) */
|
||||
#if _USE_LFN == 1
|
||||
WCHAR LfnBuf[_MAX_LFN+1];
|
||||
#endif
|
||||
#if _FS_LOCK
|
||||
FILESEM Files[_FS_LOCK];/* Open object lock semaphores */
|
||||
#endif
|
||||
miosix::intrusive_ref_ptr<miosix::FileBase> drv; /* drive device */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* File object structure (FIL) */
|
||||
|
||||
typedef struct {
|
||||
FATFS* fs; /* Pointer to the related file system object (**do not change order**) */
|
||||
WORD id; /* Owner file system mount ID (**do not change order**) */
|
||||
BYTE flag; /* File status flags */
|
||||
BYTE err; /* Abort flag (error code) */
|
||||
DWORD fptr; /* File read/write pointer (Zeroed on file open) */
|
||||
DWORD fsize; /* File size */
|
||||
DWORD sclust; /* File data start cluster (0:no data cluster, always 0 when fsize is 0) */
|
||||
DWORD clust; /* Current cluster of fpter */
|
||||
DWORD dsect; /* Current data sector of fpter */
|
||||
#if !_FS_READONLY
|
||||
DWORD dir_sect; /* Sector containing the directory entry */
|
||||
BYTE* dir_ptr; /* Pointer to the directory entry in the window */
|
||||
#endif
|
||||
#if _USE_FASTSEEK
|
||||
DWORD* cltbl; /* Pointer to the cluster link map table (Nulled on file open) */
|
||||
#endif
|
||||
#if _FS_LOCK
|
||||
UINT lockid; /* File lock ID (index of file semaphore table Files[]) */
|
||||
#endif
|
||||
#if !_FS_TINY
|
||||
BYTE buf[_MAX_SS]; /* File data read/write buffer */
|
||||
#endif
|
||||
} FIL;
|
||||
|
||||
|
||||
|
||||
/* Directory object structure (DIR) */
|
||||
|
||||
typedef struct {
|
||||
FATFS* fs; /* Pointer to the owner file system object (**do not change order**) */
|
||||
WORD id; /* Owner file system mount ID (**do not change order**) */
|
||||
WORD index; /* Current read/write index number */
|
||||
DWORD sclust; /* Table start cluster (0:Root dir) */
|
||||
DWORD clust; /* Current cluster */
|
||||
DWORD sect; /* Current sector */
|
||||
BYTE* dir; /* Pointer to the current SFN entry in the win[] */
|
||||
BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */
|
||||
#if _FS_LOCK
|
||||
UINT lockid; /* File lock ID (index of file semaphore table Files[]) */
|
||||
#endif
|
||||
#if _USE_LFN
|
||||
WCHAR* lfn; /* Pointer to the LFN working buffer */
|
||||
WORD lfn_idx; /* Last matched LFN index number (0xFFFF:No LFN) */
|
||||
#endif
|
||||
} DIR_;
|
||||
|
||||
|
||||
|
||||
/* File status structure (FILINFO) */
|
||||
|
||||
typedef struct {
|
||||
DWORD fsize; /* File size */
|
||||
WORD fdate; /* Last modified date */
|
||||
WORD ftime; /* Last modified time */
|
||||
BYTE fattrib; /* Attribute */
|
||||
TCHAR fname[13]; /* Short file name (8.3 format) */
|
||||
#if _USE_LFN
|
||||
/*TCHAR*/char *lfname; /* Pointer to the LFN buffer */
|
||||
UINT lfsize; /* Size of LFN buffer in TCHAR */
|
||||
#endif
|
||||
unsigned int inode; //By TFT: support inodes
|
||||
} FILINFO;
|
||||
|
||||
|
||||
|
||||
/* File function return code (FRESULT) */
|
||||
|
||||
typedef enum {
|
||||
FR_OK = 0, /* (0) Succeeded */
|
||||
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
|
||||
FR_INT_ERR, /* (2) Assertion failed */
|
||||
FR_NOT_READY, /* (3) The physical drive cannot work */
|
||||
FR_NO_FILE, /* (4) Could not find the file */
|
||||
FR_NO_PATH, /* (5) Could not find the path */
|
||||
FR_INVALID_NAME, /* (6) The path name format is invalid */
|
||||
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
|
||||
FR_EXIST, /* (8) Access denied due to prohibited access */
|
||||
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
|
||||
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
|
||||
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
|
||||
FR_NOT_ENABLED, /* (12) The volume has no work area */
|
||||
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
|
||||
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any parameter error */
|
||||
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
|
||||
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
|
||||
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
|
||||
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > _FS_SHARE */
|
||||
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
|
||||
} FRESULT;
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* FatFs module application interface */
|
||||
|
||||
FRESULT f_open (FATFS *fs, FIL* fp, const /*TCHAR*/char *path, BYTE mode); /* Open or create a file */
|
||||
FRESULT f_close (FIL* fp); /* Close an open file object */
|
||||
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from a file */
|
||||
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to a file */
|
||||
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
|
||||
FRESULT f_lseek (FIL* fp, DWORD ofs); /* Move file pointer of a file object */
|
||||
FRESULT f_truncate (FIL* fp); /* Truncate file */
|
||||
FRESULT f_sync (FIL* fp); /* Flush cached data of a writing file */
|
||||
FRESULT f_opendir (FATFS *fs, DIR_* dp, const /*TCHAR*/char *path); /* Open a directory */
|
||||
FRESULT f_closedir (DIR_* dp); /* Close an open directory */
|
||||
FRESULT f_readdir (DIR_* dp, FILINFO* fno); /* Read a directory item */
|
||||
FRESULT f_mkdir (FATFS *fs, const /*TCHAR*/char *path); /* Create a sub directory */
|
||||
FRESULT f_unlink (FATFS *fs, const /*TCHAR*/char *path); /* Delete an existing file or directory */
|
||||
FRESULT f_rename (FATFS *fs, const /*TCHAR*/char *path_old, const /*TCHAR*/char *path_new); /* Rename/Move a file or directory */
|
||||
FRESULT f_stat (FATFS *fs, const /*TCHAR*/char *path, FILINFO* fno); /* Get file status */
|
||||
FRESULT f_chmod (FATFS *fs, const /*TCHAR*/char *path, BYTE value, BYTE mask); /* Change attribute of the file/dir */
|
||||
FRESULT f_utime (FATFS *fs, const /*TCHAR*/char *path, const FILINFO* fno); /* Change times-tamp of the file/dir */
|
||||
FRESULT f_chdir (FATFS *fs, const TCHAR* path); /* Change current directory */
|
||||
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
|
||||
FRESULT f_getcwd (FATFS *fs, TCHAR* buff, UINT len); /* Get current directory */
|
||||
FRESULT f_getfree (FATFS *fs, /*const TCHAR* path,*/ DWORD* nclst/*, FATFS** fatfs*/); /* Get number of free clusters on the drive */
|
||||
FRESULT f_getlabel (FATFS *fs, const TCHAR* path, TCHAR* label, DWORD* sn); /* Get volume label */
|
||||
FRESULT f_setlabel (FATFS *fs, const TCHAR* label); /* Set volume label */
|
||||
FRESULT f_mount (FATFS* fs, /*const TCHAR* path,*/ BYTE opt, bool umount); /* Mount/Unmount a logical drive */
|
||||
FRESULT f_mkfs (const TCHAR* path, BYTE sfd, UINT au); /* Create a file system on the volume */
|
||||
FRESULT f_fdisk (BYTE pdrv, const DWORD szt[], void* work); /* Divide a physical drive into some partitions */
|
||||
int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
|
||||
int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
|
||||
int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
|
||||
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
|
||||
|
||||
#define f_eof(fp) (((fp)->fptr == (fp)->fsize) ? 1 : 0)
|
||||
#define f_error(fp) ((fp)->err)
|
||||
#define f_tell(fp) ((fp)->fptr)
|
||||
#define f_size(fp) ((fp)->fsize)
|
||||
|
||||
#ifndef EOF
|
||||
#define EOF (-1)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Additional user defined functions */
|
||||
|
||||
/* RTC function */
|
||||
#if !_FS_READONLY
|
||||
DWORD get_fattime (void);
|
||||
#endif
|
||||
|
||||
/* Unicode support functions */
|
||||
#if _USE_LFN /* Unicode - OEM code conversion */
|
||||
WCHAR ff_convert (WCHAR chr, UINT dir); /* OEM-Unicode bidirectional conversion */
|
||||
WCHAR ff_wtoupper (WCHAR chr); /* Unicode upper-case conversion */
|
||||
#if _USE_LFN == 3 /* Memory functions */
|
||||
void* ff_memalloc (UINT msize); /* Allocate memory block */
|
||||
void ff_memfree (void* mblock); /* Free memory block */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Sync functions */
|
||||
#if _FS_REENTRANT
|
||||
int ff_cre_syncobj (BYTE vol, _SYNC_t* sobj); /* Create a sync object */
|
||||
int ff_req_grant (_SYNC_t sobj); /* Lock sync object */
|
||||
void ff_rel_grant (_SYNC_t sobj); /* Unlock sync object */
|
||||
int ff_del_syncobj (_SYNC_t sobj); /* Delete a sync object */
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Flags and offset address */
|
||||
|
||||
|
||||
/* File access control and file status flags (FIL.flag) */
|
||||
|
||||
#define FA_READ 0x01
|
||||
#define FA_OPEN_EXISTING 0x00
|
||||
|
||||
#if !_FS_READONLY
|
||||
#define FA_WRITE 0x02
|
||||
#define FA_CREATE_NEW 0x04
|
||||
#define FA_CREATE_ALWAYS 0x08
|
||||
#define FA_OPEN_ALWAYS 0x10
|
||||
#define FA__WRITTEN 0x20
|
||||
#define FA__DIRTY 0x40
|
||||
#endif
|
||||
|
||||
|
||||
/* FAT sub type (FATFS.fs_type) */
|
||||
|
||||
#define FS_FAT12 1
|
||||
#define FS_FAT16 2
|
||||
#define FS_FAT32 3
|
||||
|
||||
|
||||
/* File attribute bits for directory entry */
|
||||
|
||||
#define AM_RDO 0x01 /* Read only */
|
||||
#define AM_HID 0x02 /* Hidden */
|
||||
#define AM_SYS 0x04 /* System */
|
||||
#define AM_VOL 0x08 /* Volume label */
|
||||
#define AM_LFN 0x0F /* LFN entry */
|
||||
#define AM_DIR 0x10 /* Directory */
|
||||
#define AM_ARC 0x20 /* Archive */
|
||||
#define AM_MASK 0x3F /* Mask of defined bits */
|
||||
|
||||
|
||||
/* Fast seek feature */
|
||||
#define CREATE_LINKMAP 0xFFFFFFFF
|
||||
|
||||
|
||||
|
||||
/*--------------------------------*/
|
||||
/* Multi-byte word access macros */
|
||||
|
||||
#if _WORD_ACCESS == 1 /* Enable word access to the FAT structure */
|
||||
#define LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr))
|
||||
#define LD_DWORD(ptr) (DWORD)(*(DWORD*)(BYTE*)(ptr))
|
||||
#define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(WORD)(val)
|
||||
#define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(DWORD)(val)
|
||||
#else /* Use byte-by-byte access to the FAT structure */
|
||||
#define LD_WORD(ptr) (WORD)(((WORD)*((BYTE*)(ptr)+1)<<8)|(WORD)*(BYTE*)(ptr))
|
||||
#define LD_DWORD(ptr) (DWORD)(((DWORD)*((BYTE*)(ptr)+3)<<24)|((DWORD)*((BYTE*)(ptr)+2)<<16)|((WORD)*((BYTE*)(ptr)+1)<<8)|*(BYTE*)(ptr))
|
||||
#define ST_WORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8)
|
||||
#define ST_DWORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8); *((BYTE*)(ptr)+2)=(BYTE)((DWORD)(val)>>16); *((BYTE*)(ptr)+3)=(BYTE)((DWORD)(val)>>24)
|
||||
#endif
|
||||
|
||||
#endif //WITH_FILESYSTEM
|
||||
|
||||
//#ifdef __cplusplus
|
||||
//}
|
||||
//#endif
|
||||
|
||||
#endif /* _FATFS */
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
/*---------------------------------------------------------------------------/
|
||||
/ FatFs - FAT file system module configuration file R0.10 (C)ChaN, 2013
|
||||
/----------------------------------------------------------------------------/
|
||||
/
|
||||
/ CAUTION! Do not forget to make clean the project after any changes to
|
||||
/ the configuration options.
|
||||
/
|
||||
/----------------------------------------------------------------------------*/
|
||||
#ifndef _FFCONF
|
||||
#define _FFCONF 80960 /* Revision ID */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Functions and Buffer Configurations
|
||||
/----------------------------------------------------------------------------*/
|
||||
|
||||
#define _FS_TINY 0 /* 0:Normal or 1:Tiny */
|
||||
/* When _FS_TINY is set to 1, FatFs uses the sector buffer in the file system
|
||||
/ object instead of the sector buffer in the individual file object for file
|
||||
/ data transfer. This reduces memory consumption 512 bytes each file object. */
|
||||
|
||||
|
||||
#define _FS_READONLY 0 /* 0:Read/Write or 1:Read only */
|
||||
/* Setting _FS_READONLY to 1 defines read only configuration. This removes
|
||||
/ writing functions, f_write(), f_sync(), f_unlink(), f_mkdir(), f_chmod(),
|
||||
/ f_rename(), f_truncate() and useless f_getfree(). */
|
||||
|
||||
|
||||
#define _FS_MINIMIZE 0 /* 0 to 3 */
|
||||
/* The _FS_MINIMIZE option defines minimization level to remove API functions.
|
||||
/
|
||||
/ 0: All basic functions are enabled.
|
||||
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_chmod(), f_utime(),
|
||||
/ f_truncate() and f_rename() function are removed.
|
||||
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
|
||||
/ 3: f_lseek() function is removed in addition to 2. */
|
||||
|
||||
|
||||
#define _USE_STRFUNC 0 /* 0:Disable or 1-2:Enable */
|
||||
/* To enable string functions, set _USE_STRFUNC to 1 or 2. */
|
||||
|
||||
|
||||
#define _USE_MKFS 0 /* 0:Disable or 1:Enable */
|
||||
/* To enable f_mkfs() function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */
|
||||
|
||||
|
||||
#define _USE_FASTSEEK 0 /* 0:Disable or 1:Enable */
|
||||
/* To enable fast seek feature, set _USE_FASTSEEK to 1. */
|
||||
|
||||
|
||||
#define _USE_LABEL 0 /* 0:Disable or 1:Enable */
|
||||
/* To enable volume label functions, set _USE_LAVEL to 1 */
|
||||
|
||||
|
||||
#define _USE_FORWARD 0 /* 0:Disable or 1:Enable */
|
||||
/* To enable f_forward() function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Locale and Namespace Configurations
|
||||
/----------------------------------------------------------------------------*/
|
||||
|
||||
#define _CODE_PAGE 1252
|
||||
/* The _CODE_PAGE specifies the OEM code page to be used on the target system.
|
||||
/ Incorrect setting of the code page can cause a file open failure.
|
||||
/
|
||||
/ 932 - Japanese Shift-JIS (DBCS, OEM, Windows)
|
||||
/ 936 - Simplified Chinese GBK (DBCS, OEM, Windows)
|
||||
/ 949 - Korean (DBCS, OEM, Windows)
|
||||
/ 950 - Traditional Chinese Big5 (DBCS, OEM, Windows)
|
||||
/ 1250 - Central Europe (Windows)
|
||||
/ 1251 - Cyrillic (Windows)
|
||||
/ 1252 - Latin 1 (Windows)
|
||||
/ 1253 - Greek (Windows)
|
||||
/ 1254 - Turkish (Windows)
|
||||
/ 1255 - Hebrew (Windows)
|
||||
/ 1256 - Arabic (Windows)
|
||||
/ 1257 - Baltic (Windows)
|
||||
/ 1258 - Vietnam (OEM, Windows)
|
||||
/ 437 - U.S. (OEM)
|
||||
/ 720 - Arabic (OEM)
|
||||
/ 737 - Greek (OEM)
|
||||
/ 775 - Baltic (OEM)
|
||||
/ 850 - Multilingual Latin 1 (OEM)
|
||||
/ 858 - Multilingual Latin 1 + Euro (OEM)
|
||||
/ 852 - Latin 2 (OEM)
|
||||
/ 855 - Cyrillic (OEM)
|
||||
/ 866 - Russian (OEM)
|
||||
/ 857 - Turkish (OEM)
|
||||
/ 862 - Hebrew (OEM)
|
||||
/ 874 - Thai (OEM, Windows)
|
||||
/ 1 - ASCII (Valid for only non-LFN cfg.)
|
||||
*/
|
||||
|
||||
//Note by TFT: DEF_NAMEBUF is used in the following functions: f_open(),
|
||||
//f_opendir(), f_readdir(), f_stat(), f_unlink(), f_mkdir(), f_chmod(),
|
||||
//f_utime(), f_rename(), and Miosix always locks a mutex before calling any of,
|
||||
//these. For this reason it was chosen to allocate the LFN buffer statically,
|
||||
//since the mutex protects from concurrent access to the buffer.
|
||||
#define _USE_LFN 1 /* 0 to 3 */
|
||||
#define _MAX_LFN 255 /* Maximum LFN length to handle (12 to 255) */
|
||||
/* The _USE_LFN option switches the LFN feature.
|
||||
/
|
||||
/ 0: Disable LFN feature. _MAX_LFN has no effect.
|
||||
/ 1: Enable LFN with static working buffer on the BSS. Always NOT reentrant.
|
||||
/ 2: Enable LFN with dynamic working buffer on the STACK.
|
||||
/ 3: Enable LFN with dynamic working buffer on the HEAP.
|
||||
/
|
||||
/ To enable LFN feature, Unicode handling functions ff_convert() and ff_wtoupper()
|
||||
/ function must be added to the project.
|
||||
/ The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. When use stack for the
|
||||
/ working buffer, take care on stack overflow. When use heap memory for the working
|
||||
/ buffer, memory management functions, ff_memalloc() and ff_memfree(), must be added
|
||||
/ to the project. */
|
||||
|
||||
//Note by TFT: we do want unicode and not ancient code pages
|
||||
#define _LFN_UNICODE 1 /* 0:ANSI/OEM or 1:Unicode */
|
||||
/* To switch the character encoding on the FatFs API to Unicode, enable LFN feature
|
||||
/ and set _LFN_UNICODE to 1. */
|
||||
|
||||
|
||||
#define _STRF_ENCODE 3 /* 0:ANSI/OEM, 1:UTF-16LE, 2:UTF-16BE, 3:UTF-8 */
|
||||
/* When Unicode API is enabled, character encoding on the all FatFs API is switched
|
||||
/ to Unicode. This option selects the character encoding on the file to be read/written
|
||||
/ via string functions, f_gets(), f_putc(), f_puts and f_printf().
|
||||
/ This option has no effect when _LFN_UNICODE is 0. */
|
||||
|
||||
|
||||
#define _FS_RPATH 0 /* 0 to 2 */
|
||||
/* The _FS_RPATH option configures relative path feature.
|
||||
/
|
||||
/ 0: Disable relative path feature and remove related functions.
|
||||
/ 1: Enable relative path. f_chdrive() and f_chdir() function are available.
|
||||
/ 2: f_getcwd() function is available in addition to 1.
|
||||
/
|
||||
/ Note that output of the f_readdir() fnction is affected by this option. */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Drive/Volume Configurations
|
||||
/----------------------------------------------------------------------------*/
|
||||
|
||||
#define _VOLUMES 1
|
||||
/* Number of volumes (logical drives) to be used. */
|
||||
|
||||
|
||||
#define _MULTI_PARTITION 0 /* 0:Single partition, 1:Enable multiple partition */
|
||||
/* When set to 0, each volume is bound to the same physical drive number and
|
||||
/ it can mount only first primaly partition. When it is set to 1, each volume
|
||||
/ is tied to the partitions listed in VolToPart[]. */
|
||||
|
||||
|
||||
#define _MAX_SS 512 /* 512, 1024, 2048 or 4096 */
|
||||
/* Maximum sector size to be handled.
|
||||
/ Always set 512 for memory card and hard disk but a larger value may be
|
||||
/ required for on-board flash memory, floppy disk and optical disk.
|
||||
/ When _MAX_SS is larger than 512, it configures FatFs to variable sector size
|
||||
/ and GET_SECTOR_SIZE command must be implemented to the disk_ioctl() function. */
|
||||
|
||||
|
||||
#define _USE_ERASE 0 /* 0:Disable or 1:Enable */
|
||||
/* To enable sector erase feature, set _USE_ERASE to 1. Also CTRL_ERASE_SECTOR command
|
||||
/ should be added to the disk_ioctl() function. */
|
||||
|
||||
|
||||
#define _FS_NOFSINFO 0 /* 0 or 1 */
|
||||
/* If you need to know the correct free space on the FAT32 volume, set this
|
||||
/ option to 1 and f_getfree() function at first time after volume mount will
|
||||
/ force a full FAT scan.
|
||||
/
|
||||
/ 0: Load all informations in the FSINFO if available.
|
||||
/ 1: Do not trust free cluster count in the FSINFO.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ System Configurations
|
||||
/----------------------------------------------------------------------------*/
|
||||
|
||||
#define _WORD_ACCESS 0 /* 0 or 1 */
|
||||
/* The _WORD_ACCESS option is an only platform dependent option. It defines
|
||||
/ which access method is used to the word data on the FAT volume.
|
||||
/
|
||||
/ 0: Byte-by-byte access. Always compatible with all platforms.
|
||||
/ 1: Word access. Do not choose this unless under both the following conditions.
|
||||
/
|
||||
/ * Byte order on the memory is little-endian.
|
||||
/ * Address miss-aligned word access is always allowed for all instructions.
|
||||
/
|
||||
/ If it is the case, _WORD_ACCESS can also be set to 1 to improve performance
|
||||
/ and reduce code size.
|
||||
*/
|
||||
|
||||
|
||||
/* A header file that defines sync object types on the O/S, such as
|
||||
/ windows.h, ucos_ii.h and semphr.h, must be included prior to ff.h. */
|
||||
|
||||
//Note by TFT: the reentrant option uses just one big lock for each FATFS, so
|
||||
//given there's no concurrency advantage in using this option, we're just using
|
||||
//an ordinary FastMutex in class Fat32Fs and locking it before calling FatFs.
|
||||
#define _FS_REENTRANT 0 /* 0:Disable or 1:Enable */
|
||||
#define _FS_TIMEOUT 1000 /* Timeout period in unit of time ticks */
|
||||
#define _SYNC_t HANDLE /* O/S dependent type of sync object. e.g. HANDLE, OS_EVENT*, ID and etc.. */
|
||||
|
||||
/* The _FS_REENTRANT option switches the re-entrancy (thread safe) of the FatFs module.
|
||||
/
|
||||
/ 0: Disable re-entrancy. _SYNC_t and _FS_TIMEOUT have no effect.
|
||||
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
|
||||
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
|
||||
/ function must be added to the project. */
|
||||
|
||||
//Note by TFT: this is very useful, as it avoids the danger of opening the same
|
||||
//file for writing multiple times
|
||||
#define _FS_LOCK 8 /* 0:Disable or >=1:Enable */
|
||||
/* To enable file lock control feature, set _FS_LOCK to 1 or greater.
|
||||
The value defines how many files can be opened simultaneously. */
|
||||
|
||||
|
||||
#endif /* _FFCONFIG */
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*-------------------------------------------*/
|
||||
/* Integer type definitions for FatFs module */
|
||||
/*-------------------------------------------*/
|
||||
|
||||
#ifndef _FF_INTEGER
|
||||
#define _FF_INTEGER
|
||||
|
||||
#ifdef _WIN32 /* FatFs development platform */
|
||||
|
||||
#include <windows.h>
|
||||
#include <tchar.h>
|
||||
|
||||
#else /* Embedded platform */
|
||||
|
||||
/* This type MUST be 8 bit */
|
||||
typedef unsigned char BYTE;
|
||||
|
||||
/* These types MUST be 16 bit */
|
||||
typedef short SHORT;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned short WCHAR;
|
||||
|
||||
/* These types MUST be 16 bit or 32 bit */
|
||||
typedef int INT;
|
||||
typedef unsigned int UINT;
|
||||
|
||||
/* These types MUST be 32 bit */
|
||||
typedef long LONG;
|
||||
typedef unsigned long DWORD;
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
|
||||
#include "ff.h"
|
||||
#include "config/miosix_settings.h"
|
||||
|
||||
/*
|
||||
* This is an alternative version of ff_wtoupper(), designed to be both smaller,
|
||||
* faster and to better conform to the unicode specification.
|
||||
*
|
||||
* Code size using gcc 4.7.3, with
|
||||
* arm-miosix-eabi-g++ -mcpu=cortex-m3 -mthumb -O2 -fno-exceptions -fno-rtti \
|
||||
* -c wtoupper.cpp
|
||||
* is:
|
||||
* - ChaN's ff_wtoupper(): 1000bytes
|
||||
* - fede.tft's enhanced version: 236bytes
|
||||
*
|
||||
* The design of this function is a bit tricky, as the usual way of making a
|
||||
* look up table is not optimized enough. It is old wisdom that a lut is
|
||||
* both faster and more space-efficient than a sequence of if, but unicode
|
||||
* conversion is somewhat peculiar. First, the input set is made of 0x10ffff
|
||||
* possible values, so the usual design that makes lut access O(1) would
|
||||
* require more than 2MB and is therefore out of question. However, the number
|
||||
* of characters that have an uppercase form is just around 1000, so the next
|
||||
* straightforward implementation would be to make a table of lowercase and
|
||||
* a table of upperacse characters. A character is checked against each entry
|
||||
* of the lowercase table, and if it matches the corresponding entry in the
|
||||
* upperacse table is returned, while if it matches none then the character
|
||||
* is either already uppercase, or does not have an uppercase form.
|
||||
*
|
||||
* This works, but requires roughly 4KB of tables, and is not very fast as it
|
||||
* requires a for loop and a thousand comparisons per converted character.
|
||||
* The next thing to notice is that many characters to convert are in
|
||||
* contiguous ranges, which can be dealt with using an if statement per range.
|
||||
* There is a tradeoff, however, as the range needs to contain more than a
|
||||
* certain number of elements to be faster and/or result in less code size than
|
||||
* the lut approach. For this reason, it was selected to use an if for every
|
||||
* range of 7 or more code points, and use a final round with a look up table
|
||||
* to deal with too small ranges.
|
||||
*
|
||||
* For what concerns unicode conformance, the result has been checked against
|
||||
* the file UnicodeData.txt downloaded from unicode's website, and the following
|
||||
* functional modifications have been done with respect to the original
|
||||
* ff_wtoupper():
|
||||
* - Code points 0x00a1, 0x00a2, 0x003, 0x00a5, 0x00ac, 0x00af are no longer
|
||||
* converted. This is because they do not have an uppercase form
|
||||
* - Code point 0x00b5 is converted to 0x039c
|
||||
* - Code point 0x0131 is converted to 0x0049 and not 0x130
|
||||
*
|
||||
* In addition, according to UnicodeData.txt there are many character that
|
||||
* were missing from the original implementation of ff_wtoupper(), but this was
|
||||
* not fixed, as it would lead to significantly larger tables.
|
||||
*/
|
||||
|
||||
#ifdef WITH_FILESYSTEM
|
||||
|
||||
static const unsigned short lowerCase[]=
|
||||
{
|
||||
0x00b5, 0x00ff, 0x0131, 0x0133, 0x0135, 0x0137, 0x017a, 0x017c,
|
||||
0x017e, 0x0192, 0x045e, 0x045f,
|
||||
};
|
||||
|
||||
static const unsigned short upperCase[]=
|
||||
{
|
||||
0x039c, 0x0178, 0x0049, 0x0132, 0x0134, 0x0136, 0x0179, 0x017b,
|
||||
0x017d, 0x0191, 0x040e, 0x040f,
|
||||
};
|
||||
|
||||
static const int tabSize=sizeof(lowerCase)/sizeof(lowerCase[0]);
|
||||
|
||||
unsigned short ff_wtoupper(unsigned short c)
|
||||
{
|
||||
if(c>='a' && c<='z') return c-('a'-'A'); //26 code points
|
||||
|
||||
if(c<0x80) return c;//Speed hack: there are no other lowercase char in ASCII
|
||||
|
||||
if(c>=0x00e0 && c<=0x00f6) return c-(0x00e0-0x00c0); //23 code points
|
||||
|
||||
if(c>=0x00f8 && c<=0x00fe) return c-(0x00f8-0x00d8); // 7 code points
|
||||
|
||||
if(c>=0x0101 && c<=0x012f && (c & 1)) return c-1; //24 code points
|
||||
|
||||
if(c>=0x013a && c<=0x0148 && ((c & 1)==0)) return c-1; // 8 code points
|
||||
|
||||
if(c>=0x014b && c<=0x0177 && (c & 1)) return c-1; //23 code points
|
||||
|
||||
if(c>=0x03b1 && c<=0x03c1) return c-(0x03b1-0x0391); //17 code points
|
||||
|
||||
if(c>=0x03c3 && c<=0x03ca) return c-(0x03c3-0x03a3); // 8 code points
|
||||
|
||||
if(c>=0x0430 && c<=0x044f) return c-(0x0430-0x0410); //32 code points
|
||||
|
||||
if(c>=0x0451 && c<=0x045c) return c-(0x0451-0x0401); //12 code points
|
||||
|
||||
if(c>=0x2170 && c<=0x217f) return c-(0x2170-0x2160); //16 code points
|
||||
|
||||
if(c>=0xff41 && c<=0xff5a) return c-(0xff41-0xff21); //26 code points
|
||||
|
||||
for(int i=0;i<tabSize;i++) if(lowerCase[i]==c) return upperCase[i];
|
||||
return c;
|
||||
}
|
||||
|
||||
#endif //WITH_FILESYSTEM
|
||||
|
||||
/*
|
||||
//Print all characters that have an uppercase
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main()
|
||||
{
|
||||
for(unisgned short i=0;i<0x10000;i++)
|
||||
{
|
||||
unisgned short up=ff_wtoupper(i);
|
||||
if(up==i) continue;
|
||||
cout<<hex<<uppercase<<setw(4)<<setfill('0')<<i
|
||||
<<" "<<setw(4)<<setfill('0')<<up<<endl;
|
||||
}
|
||||
}
|
||||
|
||||
//Crawl UnicodeData.txt making a table of code points and their uppercase
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
enum {
|
||||
codePoint=0,
|
||||
upperCase=12,
|
||||
lowerCase=13
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
ifstream in("UnicodeData.txt");
|
||||
string line;
|
||||
int lineno=0;
|
||||
while(getline(in,line))
|
||||
{
|
||||
lineno++;
|
||||
stringstream ss(line);
|
||||
vector<string> fields;
|
||||
string field;
|
||||
while(getline(ss,field,';')) fields.push_back(field);
|
||||
if(fields.at(upperCase).empty()==false &&
|
||||
fields.at(codePoint)!=fields.at(upperCase))
|
||||
cout<<fields.at(codePoint)<<" "<<fields.at(upperCase)<<endl;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include "file.h"
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <fcntl.h>
|
||||
#include "file_access.h"
|
||||
#include "config/miosix_settings.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace miosix {
|
||||
|
||||
//This is a static assert. The filesystem code assumes off_t is 64bit.
|
||||
typedef char check_sizeof_off_t[sizeof(off_t)==8 ? 1 : -1];
|
||||
|
||||
//
|
||||
// class FileBase
|
||||
//
|
||||
|
||||
FileBase::FileBase(intrusive_ref_ptr<FilesystemBase> parent) : parent(parent)
|
||||
{
|
||||
if(parent) parent->newFileOpened();
|
||||
}
|
||||
|
||||
#ifdef WITH_FILESYSTEM
|
||||
|
||||
int FileBase::isatty() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FileBase::fcntl(int cmd, int opt)
|
||||
{
|
||||
//Newlib makes some calls to fcntl, for example in opendir(). CLOEXEC isn't
|
||||
//supported, but for now we lie and return 0
|
||||
if(cmd==F_SETFD && (opt==FD_CLOEXEC || opt==0)) return 0;
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
int FileBase::ioctl(int cmd, void *arg)
|
||||
{
|
||||
return -ENOTTY; //Means the operation does not apply to this descriptor
|
||||
}
|
||||
|
||||
int FileBase::getdents(void *dp, int len)
|
||||
{
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
#endif //WITH_FILESYSTEM
|
||||
|
||||
FileBase::~FileBase()
|
||||
{
|
||||
if(parent) parent->fileCloseHook();
|
||||
}
|
||||
|
||||
//
|
||||
// class DirectoryBase
|
||||
//
|
||||
|
||||
ssize_t DirectoryBase::write(const void *data, size_t len)
|
||||
{
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
ssize_t DirectoryBase::read(void *data, size_t len)
|
||||
{
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
off_t DirectoryBase::lseek(off_t pos, int whence)
|
||||
{
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
int DirectoryBase::fstat(struct stat *pstat) const
|
||||
{
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
int DirectoryBase::addEntry(char **pos, char *end, int ino, char type,
|
||||
const StringPart& n)
|
||||
{
|
||||
int reclen=direntHeaderSizeNoPadding+n.length()+1;
|
||||
reclen=(reclen+3) & ~0x3; //Align to 4 bytes
|
||||
if(reclen>end-*pos) return -1;
|
||||
|
||||
struct dirent *data=reinterpret_cast<struct dirent*>(*pos);
|
||||
data->d_ino=ino;
|
||||
data->d_off=0;
|
||||
data->d_reclen=reclen;
|
||||
data->d_type=type;
|
||||
strcpy(data->d_name,n.c_str());
|
||||
|
||||
(*pos)+=reclen;
|
||||
return reclen;
|
||||
}
|
||||
|
||||
int DirectoryBase::addDefaultEntries(char **pos, int thisIno, int upIno)
|
||||
{
|
||||
struct dirent *data=reinterpret_cast<struct dirent*>(*pos);
|
||||
data->d_ino=thisIno;
|
||||
data->d_off=0;
|
||||
data->d_reclen=direntHeaderSize;
|
||||
data->d_type=DT_DIR;
|
||||
strcpy(data->d_name,".");
|
||||
|
||||
(*pos)+=direntHeaderSize;
|
||||
data=reinterpret_cast<struct dirent*>(*pos);
|
||||
data->d_ino=upIno;
|
||||
data->d_off=0;
|
||||
data->d_reclen=direntHeaderSize;
|
||||
data->d_type=DT_DIR;
|
||||
strcpy(data->d_name,"..");
|
||||
|
||||
(*pos)+=direntHeaderSize;
|
||||
return 2*direntHeaderSize;
|
||||
}
|
||||
|
||||
int DirectoryBase::addTerminatingEntry(char **pos, char *end)
|
||||
{
|
||||
if(direntHeaderSize>end-*pos) return -1;
|
||||
//This sets everything to zero, including d_reclen, terminating the
|
||||
//directory listing loop in readdir.c
|
||||
memset(*pos,0,direntHeaderSize);
|
||||
(*pos)+=direntHeaderSize;
|
||||
return direntHeaderSize;
|
||||
}
|
||||
|
||||
//
|
||||
// class FilesystemBase
|
||||
//
|
||||
|
||||
FilesystemBase::FilesystemBase() :
|
||||
#ifdef WITH_FILESYSTEM
|
||||
filesystemId(FilesystemManager::getFilesystemId()),
|
||||
#else //WITH_FILESYSTEM
|
||||
filesystemId(0),
|
||||
#endif //WITH_FILESYSTEM
|
||||
parentFsMountpointInode(1), openFileCount(0) {}
|
||||
|
||||
int FilesystemBase::readlink(StringPart& name, string& target)
|
||||
{
|
||||
return -EINVAL; //Default implementation, for filesystems without symlinks
|
||||
}
|
||||
|
||||
bool FilesystemBase::supportsSymlinks() const { return false; }
|
||||
|
||||
void FilesystemBase::newFileOpened() { atomicAdd(&openFileCount,1); }
|
||||
|
||||
void FilesystemBase::fileCloseHook()
|
||||
{
|
||||
#ifdef WITH_ERRLOG
|
||||
int result=atomicAddExchange(&openFileCount,-1);
|
||||
assert(result>=0);
|
||||
#else //WITH_ERRLOG
|
||||
atomicAdd(&openFileCount,-1);
|
||||
#endif //WITH_ERRLOG
|
||||
}
|
||||
|
||||
FilesystemBase::~FilesystemBase() {}
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,379 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include "kernel/intrusive.h"
|
||||
#include "config/miosix_settings.h"
|
||||
|
||||
#ifndef FILE_H
|
||||
#define FILE_H
|
||||
|
||||
namespace miosix {
|
||||
|
||||
// Forward decls
|
||||
class FilesystemBase;
|
||||
class StringPart;
|
||||
|
||||
/**
|
||||
* The unix file abstraction. Also some device drivers are seen as files.
|
||||
* Classes of this type are reference counted, must be allocated on the heap
|
||||
* and managed through intrusive_ref_ptr<FileBase>
|
||||
*/
|
||||
class FileBase : public IntrusiveRefCounted
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* \param parent the filesystem to which this file belongs
|
||||
*/
|
||||
FileBase(intrusive_ref_ptr<FilesystemBase> parent);
|
||||
|
||||
/**
|
||||
* Write data to the file, if the file supports writing.
|
||||
* \param data the data to write
|
||||
* \param len the number of bytes to write
|
||||
* \return the number of written characters, or a negative number in case
|
||||
* of errors
|
||||
*/
|
||||
virtual ssize_t write(const void *data, size_t len)=0;
|
||||
|
||||
/**
|
||||
* Read data from the file, if the file supports reading.
|
||||
* \param data buffer to store read data
|
||||
* \param len the number of bytes to read
|
||||
* \return the number of read characters, or a negative number in case
|
||||
* of errors
|
||||
*/
|
||||
virtual ssize_t read(void *data, size_t len)=0;
|
||||
|
||||
#ifdef WITH_FILESYSTEM
|
||||
|
||||
/**
|
||||
* Move file pointer, if the file supports random-access.
|
||||
* \param pos offset to sum to the beginning of the file, current position
|
||||
* or end of file, depending on whence
|
||||
* \param whence SEEK_SET, SEEK_CUR or SEEK_END
|
||||
* \return the offset from the beginning of the file if the operation
|
||||
* completed, or a negative number in case of errors
|
||||
*/
|
||||
virtual off_t lseek(off_t pos, int whence)=0;
|
||||
|
||||
/**
|
||||
* Return file information.
|
||||
* \param pstat pointer to stat struct
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int fstat(struct stat *pstat) const=0;
|
||||
|
||||
/**
|
||||
* Check whether the file refers to a terminal.
|
||||
* \return 1 if it is a terminal, 0 if it is not, or a negative number in
|
||||
* case of errors
|
||||
*/
|
||||
virtual int isatty() const;
|
||||
|
||||
/**
|
||||
* Perform various operations on a file descriptor
|
||||
* \param cmd specifies the operation to perform
|
||||
* \param opt optional argument that some operation require
|
||||
* \return the exact return value depends on CMD, -1 is returned on error
|
||||
*/
|
||||
virtual int fcntl(int cmd, int opt);
|
||||
|
||||
/**
|
||||
* Perform various operations on a file descriptor
|
||||
* \param cmd specifies the operation to perform
|
||||
* \param arg optional argument that some operation require
|
||||
* \return the exact return value depends on CMD, -1 is returned on error
|
||||
*/
|
||||
virtual int ioctl(int cmd, void *arg);
|
||||
|
||||
/**
|
||||
* Also directories can be opened as files. In this case, this system call
|
||||
* allows to retrieve directory entries.
|
||||
* \param dp pointer to a memory buffer where one or more struct dirent
|
||||
* will be placed. dp must be four words aligned.
|
||||
* \param len memory buffer size.
|
||||
* \return the number of bytes read on success, or a negative number on
|
||||
* failure.
|
||||
*/
|
||||
virtual int getdents(void *dp, int len);
|
||||
|
||||
/**
|
||||
* \return a pointer to the parent filesystem
|
||||
*/
|
||||
const intrusive_ref_ptr<FilesystemBase> getParent() const { return parent; }
|
||||
|
||||
#endif //WITH_FILESYSTEM
|
||||
|
||||
/**
|
||||
* File destructor
|
||||
*/
|
||||
virtual ~FileBase();
|
||||
|
||||
private:
|
||||
FileBase(const FileBase&);
|
||||
FileBase& operator=(const FileBase&);
|
||||
|
||||
intrusive_ref_ptr<FilesystemBase> parent; ///< Files may have a parent fs
|
||||
};
|
||||
|
||||
/**
|
||||
* Directories are a special kind of files that implement the getdents() call
|
||||
* Classes of this type are reference counted, must be allocated on the heap
|
||||
* and managed through intrusive_ref_ptr<DirectoryBase>
|
||||
*/
|
||||
class DirectoryBase : public FileBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* \param parent the filesystem to which this file belongs
|
||||
*/
|
||||
DirectoryBase(intrusive_ref_ptr<FilesystemBase> parent) : FileBase(parent) {}
|
||||
|
||||
/**
|
||||
* Write data to the file, if the file supports writing.
|
||||
* \param data the data to write
|
||||
* \param len the number of bytes to write
|
||||
* \return the number of written characters, or a negative number in case
|
||||
* of errors
|
||||
*/
|
||||
virtual ssize_t write(const void *data, size_t len);
|
||||
|
||||
/**
|
||||
* Read data from the file, if the file supports reading.
|
||||
* \param data buffer to store read data
|
||||
* \param len the number of bytes to read
|
||||
* \return the number of read characters, or a negative number in case
|
||||
* of errors
|
||||
*/
|
||||
virtual ssize_t read(void *data, size_t len);
|
||||
|
||||
/**
|
||||
* Move file pointer, if the file supports random-access.
|
||||
* \param pos offset to sum to the beginning of the file, current position
|
||||
* or end of file, depending on whence
|
||||
* \param whence SEEK_SET, SEEK_CUR or SEEK_END
|
||||
* \return the offset from the beginning of the file if the operation
|
||||
* completed, or a negative number in case of errors
|
||||
*/
|
||||
virtual off_t lseek(off_t pos, int whence);
|
||||
|
||||
/**
|
||||
* Return file information.
|
||||
* \param pstat pointer to stat struct
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int fstat(struct stat *pstat) const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Helper function to add a directory entry to a buffer
|
||||
* \param pos position where to add the entry (four word aligned). Pointer
|
||||
* is incremented.
|
||||
* \param end end of buffer (one char past the last), for bound checking
|
||||
* \param ino inode of file
|
||||
* \param type file type
|
||||
* \param name file name to append after the DirentHeader
|
||||
* \return the number of bytes written or -1 on failure (no space in buffer)
|
||||
*/
|
||||
static int addEntry(char **pos, char *end, int ino, char type,
|
||||
const StringPart& n);
|
||||
|
||||
/**
|
||||
* Helper function to add the default directory entries . and .. to a buffer
|
||||
* \param pos position where to add the entry (four word aligned). Pointer
|
||||
* is incremented. The caller is responsible to guarantee that there is at
|
||||
* least space for 2*direntHeaderSize
|
||||
* \param thisIno inode number of .
|
||||
* \param upInode inode number of ..
|
||||
* \return the number of bytes written
|
||||
*/
|
||||
static int addDefaultEntries(char **pos, int thisIno, int upIno);
|
||||
|
||||
/**
|
||||
* Add an entry with d_reclen=0 which is used to terminate directory listing
|
||||
* \param pos position where to add the entry (four word aligned). Pointer
|
||||
* is incremented. The caller is responsible to guarantee that there is at
|
||||
* least space for direntHeaderSize, including padding
|
||||
* \param end end of buffer (one char past the last), for bound checking
|
||||
* \return the number of bytes written or -1 on failure (no space in buffer)
|
||||
*/
|
||||
static int addTerminatingEntry(char **pos, char *end);
|
||||
|
||||
///Size of struct dirent excluding d_name. That is, the size of d_ino,
|
||||
///d_off, d_reclen and d_type. Notice that there are 4 bytes of padding
|
||||
///between d_ino and d_off as d_off is a 64 bit number. Should be 19.
|
||||
static const int direntHeaderSizeNoPadding=offsetof(struct dirent,d_name);
|
||||
|
||||
///Size of struct dirent including room for the "." and ".." string in
|
||||
///d_name, including terminating \0 and padding for 4-word alignment.
|
||||
///First +3: make room for '..\0', 3 bytes
|
||||
///Second +3 and /4*4: four word alignment
|
||||
static const int direntHeaderSize=(direntHeaderSizeNoPadding+3+3)/4*4;
|
||||
|
||||
///Minimum buffer accepted by getdents, two for . and .., plus terminating
|
||||
static const int minimumBufferSize=3*direntHeaderSize;
|
||||
};
|
||||
|
||||
/**
|
||||
* All filesystems derive from this class. Classes of this type are reference
|
||||
* counted, must be allocated on the heap and managed through
|
||||
* intrusive_ref_ptr<FilesystemBase>
|
||||
*/
|
||||
class FilesystemBase : public IntrusiveRefCounted,
|
||||
public IntrusiveRefCountedSharedFromThis<FilesystemBase>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
FilesystemBase();
|
||||
|
||||
/**
|
||||
* Open a file
|
||||
* \param file the file object will be stored here, if the call succeeds
|
||||
* \param name the name of the file to open, relative to the local
|
||||
* filesystem
|
||||
* \param flags file flags (open for reading, writing, ...)
|
||||
* \param mode file permissions
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int open(intrusive_ref_ptr<FileBase>& file, StringPart& name,
|
||||
int flags, int mode)=0;
|
||||
|
||||
/**
|
||||
* Obtain information on a file, identified by a path name. Does not follow
|
||||
* symlinks
|
||||
* \param name path name, relative to the local filesystem
|
||||
* \param pstat file information is stored here
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int lstat(StringPart& name, struct stat *pstat)=0;
|
||||
|
||||
/**
|
||||
* Remove a file or directory
|
||||
* \param name path name of file or directory to remove
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int unlink(StringPart& name)=0;
|
||||
|
||||
/**
|
||||
* Rename a file or directory
|
||||
* \param oldName old file name
|
||||
* \param newName new file name
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int rename(StringPart& oldName, StringPart& newName)=0;
|
||||
|
||||
/**
|
||||
* Create a directory
|
||||
* \param name directory name
|
||||
* \param mode directory permissions
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int mkdir(StringPart& name, int mode)=0;
|
||||
|
||||
/**
|
||||
* Remove a directory if empty
|
||||
* \param name directory name
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int rmdir(StringPart& name)=0;
|
||||
|
||||
/**
|
||||
* Follows a symbolic link
|
||||
* \param path path identifying a symlink, relative to the local filesystem
|
||||
* \param target the link target is returned here if the call succeeds.
|
||||
* Note that the returned path is not relative to this filesystem, and can
|
||||
* be either relative or absolute.
|
||||
* \return 0 on success, a negative number on failure
|
||||
*/
|
||||
virtual int readlink(StringPart& name, std::string& target);
|
||||
|
||||
/**
|
||||
* \return true if the filesystem supports symbolic links.
|
||||
* In this case, the filesystem should override readlink
|
||||
*/
|
||||
virtual bool supportsSymlinks() const;
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \return true if all files belonging to this filesystem are closed
|
||||
*/
|
||||
bool areAllFilesClosed() { return openFileCount==0; }
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Called by file constructor whenever a file belonging to this
|
||||
* filesystem is opened. Never call this function from user code.
|
||||
*/
|
||||
void newFileOpened();
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Called by file destructors whenever a file belonging to this
|
||||
* filesystem is closed. Never call this function from user code.
|
||||
*/
|
||||
void fileCloseHook();
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* This is used to inform a filesystem of the inode of the directory in the
|
||||
* parent fs where it is mounted. It is used for directory listing, to
|
||||
* resolve the inode of the .. entry of the filesystem's root directory
|
||||
* \param inode inode of the directory where the fs is mounted
|
||||
*/
|
||||
void setParentFsMountpointInode(int inode) { parentFsMountpointInode=inode; }
|
||||
|
||||
/**
|
||||
* \return filesystem id
|
||||
*/
|
||||
short int getFsId() const { return filesystemId; }
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~FilesystemBase();
|
||||
|
||||
protected:
|
||||
|
||||
const short int filesystemId; ///< The unique filesystem id, used by lstat
|
||||
int parentFsMountpointInode; ///< The inode of the directory in the parent fs
|
||||
|
||||
private:
|
||||
FilesystemBase(const FilesystemBase&);
|
||||
FilesystemBase& operator= (const FilesystemBase&);
|
||||
|
||||
volatile int openFileCount; ///< Number of open files
|
||||
};
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //FILE_H
|
||||
|
|
@ -0,0 +1,763 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include "file_access.h"
|
||||
#include <vector>
|
||||
#include <climits>
|
||||
#include <fcntl.h>
|
||||
#include "console/console_device.h"
|
||||
#include "mountpointfs/mountpointfs.h"
|
||||
#include "fat32/fat32.h"
|
||||
#include "kernel/logging.h"
|
||||
#ifdef WITH_PROCESSES
|
||||
#include "kernel/process.h"
|
||||
#endif //WITH_PROCESSES
|
||||
|
||||
using namespace std;
|
||||
|
||||
#ifdef WITH_FILESYSTEM
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/*
|
||||
* A note on the use of strings in this file. This file uses three string
|
||||
* types: C string, C++ std::string and StringPart which is an efficent
|
||||
* in-place substring of either a C or C++ string.
|
||||
*
|
||||
* The functions which are meant to be used by clients of the filesystem
|
||||
* API take file names as C strings. This is becase that's the signature
|
||||
* of the POSIX calls, such as fopen(), stat(), umount(), ... It is
|
||||
* worth noticing that these C strings are const. However, resolving paths
|
||||
* requires a writable scratchpad string to be able to remove
|
||||
* useless path components, such as "/./", go backwards when a "/../" is
|
||||
* found, and follow symbolic links. To this end, all these functions
|
||||
* make a copy of the passed string into a temporary C++ string that
|
||||
* will be deallocated as soon as the function returns.
|
||||
* Resolving a path, however, requires to scan all its path components
|
||||
* one by one, checking if the path up to that point is a symbolic link
|
||||
* or a mountpoint of a filesystem.
|
||||
* For example, resolving "/home/test/file" requires to make three
|
||||
* substrings, "/home", "/home/test" and "/home/test/file". Of course,
|
||||
* one can simply use the substr() member function of the std::string
|
||||
* class. However, this means making lots of tiny memory allocations on
|
||||
* the heap, increasing the RAM footprint to resolve a path. To this end,
|
||||
* the StringPart class was introduced, to create fast in-place substring
|
||||
* of a string.
|
||||
*/
|
||||
|
||||
//
|
||||
// class FileDescriptorTable
|
||||
//
|
||||
|
||||
FileDescriptorTable::FileDescriptorTable()
|
||||
: mutex(FastMutex::RECURSIVE), cwd("/")
|
||||
{
|
||||
FilesystemManager::instance().addFileDescriptorTable(this);
|
||||
files[0]=files[1]=files[2]=intrusive_ref_ptr<FileBase>(
|
||||
new TerminalDevice(DefaultConsole::instance().get()));
|
||||
}
|
||||
|
||||
FileDescriptorTable::FileDescriptorTable(const FileDescriptorTable& rhs)
|
||||
: mutex(FastMutex::RECURSIVE), cwd(rhs.cwd)
|
||||
{
|
||||
//No need to lock the mutex since we are in a constructor and there can't
|
||||
//be pointers to this in other threads yet
|
||||
for(int i=0;i<MAX_OPEN_FILES;i++) this->files[i]=atomic_load(&rhs.files[i]);
|
||||
FilesystemManager::instance().addFileDescriptorTable(this);
|
||||
}
|
||||
|
||||
FileDescriptorTable& FileDescriptorTable::operator=(
|
||||
const FileDescriptorTable& rhs)
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
for(int i=0;i<MAX_OPEN_FILES;i++)
|
||||
atomic_store(&this->files[i],atomic_load(&rhs.files[i]));
|
||||
return *this;
|
||||
}
|
||||
|
||||
int FileDescriptorTable::open(const char* name, int flags, int mode)
|
||||
{
|
||||
if(name==0 || name[0]=='\0') return -EFAULT;
|
||||
Lock<FastMutex> l(mutex);
|
||||
for(int i=3;i<MAX_OPEN_FILES;i++)
|
||||
{
|
||||
if(files[i]) continue;
|
||||
//Found an empty file descriptor
|
||||
string path=absolutePath(name);
|
||||
if(path.empty()) return -ENAMETOOLONG;
|
||||
ResolvedPath openData=FilesystemManager::instance().resolvePath(path);
|
||||
if(openData.result<0) return openData.result;
|
||||
StringPart sp(path,string::npos,openData.off);
|
||||
int result=openData.fs->open(files[i],sp,flags,mode);
|
||||
if(result==0) return i; //The file descriptor
|
||||
else return result; //The error code
|
||||
}
|
||||
return -ENFILE;
|
||||
}
|
||||
|
||||
int FileDescriptorTable::close(int fd)
|
||||
{
|
||||
//No need to lock the mutex when deleting
|
||||
if(fd<0 || fd>=MAX_OPEN_FILES) return -EBADF;
|
||||
intrusive_ref_ptr<FileBase> toClose;
|
||||
toClose=atomic_exchange(files+fd,intrusive_ref_ptr<FileBase>());
|
||||
if(!toClose) return -EBADF; //File entry was not open
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FileDescriptorTable::closeAll()
|
||||
{
|
||||
for(int i=0;i<MAX_OPEN_FILES;i++)
|
||||
atomic_exchange(files+i,intrusive_ref_ptr<FileBase>());
|
||||
}
|
||||
|
||||
int FileDescriptorTable::getcwd(char *buf, size_t len)
|
||||
{
|
||||
if(buf==0 || len<2) return -EINVAL; //We don't support the buf==0 extension
|
||||
Lock<FastMutex> l(mutex);
|
||||
struct stat st;
|
||||
if(stat(".",&st) || !S_ISDIR(st.st_mode)) return -ENOENT;
|
||||
if(cwd.length()>len) return -ERANGE;
|
||||
strncpy(buf,cwd.c_str(),len);
|
||||
if(cwd.length()>1) buf[cwd.length()-1]='\0'; //Erase last '/' in cwd
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FileDescriptorTable::chdir(const char* name)
|
||||
{
|
||||
if(name==0 || name[0]=='\0') return -EFAULT;
|
||||
size_t len=strlen(name);
|
||||
if(name[len-1]!='/') len++; //Reserve room for trailing slash
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(name[0]!='/') len+=cwd.length();
|
||||
if(len>PATH_MAX) return -ENAMETOOLONG;
|
||||
|
||||
string newCwd;
|
||||
newCwd.reserve(len);
|
||||
if(name[0]=='/') newCwd=name;
|
||||
else {
|
||||
newCwd=cwd;
|
||||
newCwd+=name;
|
||||
}
|
||||
ResolvedPath openData=FilesystemManager::instance().resolvePath(newCwd);
|
||||
if(openData.result<0) return openData.result;
|
||||
struct stat st;
|
||||
StringPart sp(newCwd,string::npos,openData.off);
|
||||
if(int result=openData.fs->lstat(sp,&st)) return result;
|
||||
if(!S_ISDIR(st.st_mode)) return -ENOTDIR;
|
||||
//NOTE: put after resolvePath() as it strips trailing /
|
||||
//Also put after lstat() as it fails if path has a trailing slash
|
||||
newCwd+='/';
|
||||
cwd=newCwd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FileDescriptorTable::mkdir(const char *name, int mode)
|
||||
{
|
||||
if(name==0 || name[0]=='\0') return -EFAULT;
|
||||
string path=absolutePath(name);
|
||||
if(path.empty()) return -ENAMETOOLONG;
|
||||
ResolvedPath openData=FilesystemManager::instance().resolvePath(path,true);
|
||||
if(openData.result<0) return openData.result;
|
||||
StringPart sp(path,string::npos,openData.off);
|
||||
return openData.fs->mkdir(sp,mode);
|
||||
}
|
||||
|
||||
int FileDescriptorTable::rmdir(const char *name)
|
||||
{
|
||||
if(name==0 || name[0]=='\0') return -EFAULT;
|
||||
string path=absolutePath(name);
|
||||
if(path.empty()) return -ENAMETOOLONG;
|
||||
ResolvedPath openData=FilesystemManager::instance().resolvePath(path,true);
|
||||
if(openData.result<0) return openData.result;
|
||||
StringPart sp(path,string::npos,openData.off);
|
||||
return openData.fs->rmdir(sp);
|
||||
}
|
||||
|
||||
int FileDescriptorTable::unlink(const char *name)
|
||||
{
|
||||
if(name==0 || name[0]=='\0') return -EFAULT;
|
||||
string path=absolutePath(name);
|
||||
if(path.empty()) return -ENAMETOOLONG;
|
||||
return FilesystemManager::instance().unlinkHelper(path);
|
||||
}
|
||||
|
||||
int FileDescriptorTable::rename(const char *oldName, const char *newName)
|
||||
{
|
||||
if(oldName==0 || oldName[0]=='\0') return -EFAULT;
|
||||
if(newName==0 || newName[0]=='\0') return -EFAULT;
|
||||
string oldPath=absolutePath(oldName);
|
||||
string newPath=absolutePath(newName);
|
||||
if(oldPath.empty() || newPath.empty()) return -ENAMETOOLONG;
|
||||
return FilesystemManager::instance().renameHelper(oldPath,newPath);
|
||||
}
|
||||
|
||||
int FileDescriptorTable::statImpl(const char* name, struct stat* pstat, bool f)
|
||||
{
|
||||
if(name==0 || name[0]=='\0' || pstat==0) return -EFAULT;
|
||||
string path=absolutePath(name);
|
||||
if(path.empty()) return -ENAMETOOLONG;
|
||||
return FilesystemManager::instance().statHelper(path,pstat,f);
|
||||
}
|
||||
|
||||
FileDescriptorTable::~FileDescriptorTable()
|
||||
{
|
||||
FilesystemManager::instance().removeFileDescriptorTable(this);
|
||||
//There's no need to lock the mutex and explicitly close files eventually
|
||||
//left open, because if there are other threads accessing this while we are
|
||||
//being deleted we have bigger problems anyway
|
||||
}
|
||||
|
||||
string FileDescriptorTable::absolutePath(const char* path)
|
||||
{
|
||||
size_t len=strlen(path);
|
||||
if(len>PATH_MAX) return "";
|
||||
if(path[0]=='/') return path;
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(len+cwd.length()>PATH_MAX) return "";
|
||||
return cwd+path;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class implements the path resolution logic
|
||||
*/
|
||||
class PathResolution
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* \param fs map of all mounted filesystems
|
||||
*/
|
||||
PathResolution(const map<StringPart,intrusive_ref_ptr<FilesystemBase> >& fs)
|
||||
: filesystems(fs) {}
|
||||
|
||||
/**
|
||||
* The main purpose of this class, resolve a path
|
||||
* \param path inout parameter with the path to resolve. The resolved path
|
||||
* will be modified in-place in this string. The path must be absolute and
|
||||
* start with a "/". The caller is responsible for that.
|
||||
* \param followLastSymlink if true, follow last symlink
|
||||
* \return a resolved path
|
||||
*/
|
||||
ResolvedPath resolvePath(string& path, bool followLastSymlink);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Handle a /../ in a path
|
||||
* \param path path string
|
||||
* \param slash path[slash] is the / character after the ..
|
||||
* \return 0 on success, a negative number on error
|
||||
*/
|
||||
int upPathComponent(string& path, size_t slash);
|
||||
|
||||
/**
|
||||
* Handle a normal path component in a path, i.e, a path component
|
||||
* that is neither //, /./ or /../
|
||||
* \param path path string
|
||||
* \param followIfSymlink if true, follow symbolic links
|
||||
* \return 0 on success, or a negative number on error
|
||||
*/
|
||||
int normalPathComponent(string& path, bool followIfSymlink);
|
||||
|
||||
/**
|
||||
* Follow a symbolic link
|
||||
* \param path path string. The relative path into the current filesystem
|
||||
* must be a symbolic link (verified by the caller).
|
||||
* \return 0 on success, a negative number on failure
|
||||
*/
|
||||
int followSymlink(string& path);
|
||||
|
||||
/**
|
||||
* Find to which filesystem this path belongs
|
||||
* \param path path string.
|
||||
* \return 0 on success, a negative number on failure
|
||||
*/
|
||||
int recursiveFindFs(string& path);
|
||||
|
||||
/// Mounted filesystems
|
||||
const map<StringPart,intrusive_ref_ptr<FilesystemBase> >& filesystems;
|
||||
|
||||
/// Pointer to root filesystem
|
||||
intrusive_ref_ptr<FilesystemBase> root;
|
||||
|
||||
/// Current filesystem while looking up path
|
||||
intrusive_ref_ptr<FilesystemBase> fs;
|
||||
|
||||
/// True if current filesystem supports symlinks
|
||||
bool syms;
|
||||
|
||||
/// path[index] is first unhandled char
|
||||
size_t index;
|
||||
|
||||
/// path.substr(indexIntoFs) is the relative path to current filesystem
|
||||
size_t indexIntoFs;
|
||||
|
||||
/// How many components does the relative path have in current fs
|
||||
int depthIntoFs;
|
||||
|
||||
/// How many symlinks we've found so far
|
||||
int linksFollowed;
|
||||
|
||||
/// Maximum number of symbolic links to follow (to avoid endless loops)
|
||||
static const int maxLinkToFollow=2;
|
||||
};
|
||||
|
||||
ResolvedPath PathResolution::resolvePath(string& path, bool followLastSymlink)
|
||||
{
|
||||
map<StringPart,intrusive_ref_ptr<FilesystemBase> >::const_iterator it;
|
||||
it=filesystems.find(StringPart("/"));
|
||||
if(it==filesystems.end()) return ResolvedPath(-ENOENT); //should not happen
|
||||
root=fs=it->second;
|
||||
syms=fs->supportsSymlinks();
|
||||
index=1; //Skip leading /
|
||||
indexIntoFs=1; //NOTE: caller must ensure path[0]=='/'
|
||||
depthIntoFs=1;
|
||||
linksFollowed=0;
|
||||
for(;;)
|
||||
{
|
||||
size_t slash=path.find_first_of('/',index);
|
||||
//cout<<path.substr(0,slash)<<endl;
|
||||
//Last component (no trailing /)
|
||||
if(slash==string::npos) slash=path.length(); //NOTE: one past the last
|
||||
|
||||
if(slash==index)
|
||||
{
|
||||
//Path component is empty, caused by double slash, remove it
|
||||
path.erase(index,1);
|
||||
} else if(slash-index==1 && path[index]=='.')
|
||||
{
|
||||
path.erase(index,2); //Path component is ".", ignore
|
||||
} else if(slash-index==2 && path[index]=='.' && path[index+1]=='.')
|
||||
{
|
||||
int result=upPathComponent(path,slash);
|
||||
if(result<0) return ResolvedPath(result);
|
||||
} else {
|
||||
index=slash+1; //NOTE: if(slash==string::npos) two past the last
|
||||
// follow=followLastSymlink for "/link", but is true for "/link/"
|
||||
bool follow=index>path.length() ? followLastSymlink : true;
|
||||
int result=normalPathComponent(path,follow);
|
||||
if(result<0) return ResolvedPath(result);
|
||||
}
|
||||
//Last component
|
||||
if(index>=path.length())
|
||||
{
|
||||
//Remove trailing /
|
||||
size_t last=path.length()-1;
|
||||
if(path[last]=='/')
|
||||
{
|
||||
path.erase(last,1);
|
||||
//This may happen if the last path component is a fs
|
||||
if(indexIntoFs>path.length()) indexIntoFs=path.length();
|
||||
}
|
||||
return ResolvedPath(fs,indexIntoFs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int PathResolution::upPathComponent(string& path, size_t slash)
|
||||
{
|
||||
if(index<=1) return -ENOENT; //root dir has no parent
|
||||
size_t removeStart=path.find_last_of('/',index-2);
|
||||
if(removeStart==string::npos) return -ENOENT; //should not happen
|
||||
path.erase(removeStart,slash-removeStart);
|
||||
index=removeStart+1;
|
||||
//This may happen when merging a path like "/dir/.."
|
||||
if(path.empty()) path='/';
|
||||
//This may happen if the new last path component is a fs, e.g. "/dev/null/.."
|
||||
if(indexIntoFs>path.length()) indexIntoFs=path.length();
|
||||
if(--depthIntoFs>0) return 0;
|
||||
|
||||
//Depth went to zero, escape current filesystem
|
||||
return recursiveFindFs(path);
|
||||
}
|
||||
|
||||
int PathResolution::normalPathComponent(string& path, bool followIfSymlink)
|
||||
{
|
||||
map<StringPart,intrusive_ref_ptr<FilesystemBase> >::const_iterator it;
|
||||
it=filesystems.find(StringPart(path,index-1));
|
||||
if(it!=filesystems.end())
|
||||
{
|
||||
//Jumped to a new filesystem. Not stat-ing the path as we're
|
||||
//relying on mount not allowing to mount a filesystem on anything
|
||||
//but a directory.
|
||||
fs=it->second;
|
||||
syms=fs->supportsSymlinks();
|
||||
indexIntoFs=index>path.length() ? index-1 : index;
|
||||
depthIntoFs=1;
|
||||
return 0;
|
||||
}
|
||||
depthIntoFs++;
|
||||
if(syms && followIfSymlink)
|
||||
{
|
||||
struct stat st;
|
||||
{
|
||||
StringPart sp(path,index-1,indexIntoFs);
|
||||
if(int res=fs->lstat(sp,&st)<0) return res;
|
||||
}
|
||||
if(S_ISLNK(st.st_mode)) return followSymlink(path);
|
||||
else if(index<=path.length() && !S_ISDIR(st.st_mode)) return -ENOTDIR;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PathResolution::followSymlink(string& path)
|
||||
{
|
||||
if(++linksFollowed>=maxLinkToFollow) return -ELOOP;
|
||||
string target;
|
||||
{
|
||||
StringPart sp(path,index-1,indexIntoFs);
|
||||
if(int res=fs->readlink(sp,target)<0) return res;
|
||||
}
|
||||
if(target.empty()) return -ENOENT; //Should not happen
|
||||
if(target[0]=='/')
|
||||
{
|
||||
//Symlink is absolute
|
||||
size_t newPathLen=target.length()+path.length()-index+1;
|
||||
if(newPathLen>PATH_MAX) return -ENAMETOOLONG;
|
||||
string newPath;
|
||||
newPath.reserve(newPathLen);
|
||||
newPath=target;
|
||||
if(index<=path.length())
|
||||
newPath.insert(newPath.length(),path,index-1,string::npos);
|
||||
path.swap(newPath);
|
||||
fs=root;
|
||||
syms=root->supportsSymlinks();
|
||||
index=1;
|
||||
indexIntoFs=1;
|
||||
depthIntoFs=1;
|
||||
} else {
|
||||
//Symlink is relative
|
||||
size_t removeStart=path.find_last_of('/',index-2);
|
||||
size_t newPathLen=path.length()-(index-removeStart-2)+target.length();
|
||||
if(newPathLen>PATH_MAX) return -ENAMETOOLONG;
|
||||
string newPath;
|
||||
newPath.reserve(newPathLen);
|
||||
newPath.insert(0,path,0,removeStart+1);
|
||||
newPath+=target;
|
||||
if(index<=path.length())
|
||||
newPath.insert(newPath.length(),path,index-1,string::npos);
|
||||
path.swap(newPath);
|
||||
index=removeStart+1;
|
||||
depthIntoFs--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PathResolution::recursiveFindFs(string& path)
|
||||
{
|
||||
depthIntoFs=1;
|
||||
size_t backIndex=index;
|
||||
for(;;)
|
||||
{
|
||||
backIndex=path.find_last_of('/',backIndex-1);
|
||||
if(backIndex==string::npos) return -ENOENT; //should not happpen
|
||||
if(backIndex==0)
|
||||
{
|
||||
fs=root;
|
||||
indexIntoFs=1;
|
||||
break;
|
||||
}
|
||||
map<StringPart,intrusive_ref_ptr<FilesystemBase> >::const_iterator it;
|
||||
it=filesystems.find(StringPart(path,backIndex));
|
||||
if(it!=filesystems.end())
|
||||
{
|
||||
fs=it->second;
|
||||
indexIntoFs=backIndex+1;
|
||||
break;
|
||||
}
|
||||
depthIntoFs++;
|
||||
}
|
||||
syms=fs->supportsSymlinks();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// class FilesystemManager
|
||||
//
|
||||
|
||||
FilesystemManager& FilesystemManager::instance()
|
||||
{
|
||||
static FilesystemManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
int FilesystemManager::kmount(const char* path, intrusive_ref_ptr<FilesystemBase> fs)
|
||||
{
|
||||
if(path==0 || path[0]=='\0' || fs==0) return -EFAULT;
|
||||
Lock<FastMutex> l(mutex);
|
||||
size_t len=strlen(path);
|
||||
if(len>PATH_MAX) return -ENAMETOOLONG;
|
||||
string temp(path);
|
||||
if(!(temp=="/" && filesystems.empty())) //Skip check when mounting /
|
||||
{
|
||||
struct stat st;
|
||||
if(int result=statHelper(temp,&st,false)) return result;
|
||||
if(!S_ISDIR(st.st_mode)) return -ENOTDIR;
|
||||
string parent=temp+"/..";
|
||||
if(int result=statHelper(parent,&st,false)) return result;
|
||||
fs->setParentFsMountpointInode(st.st_ino);
|
||||
}
|
||||
if(filesystems.insert(make_pair(StringPart(temp),fs)).second==false)
|
||||
return -EBUSY; //Means already mounted
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FilesystemManager::umount(const char* path, bool force)
|
||||
{
|
||||
typedef
|
||||
typename map<StringPart,intrusive_ref_ptr<FilesystemBase> >::iterator fsIt;
|
||||
|
||||
if(path==0 || path[0]=='\0') return -ENOENT;
|
||||
size_t len=strlen(path);
|
||||
if(len>PATH_MAX) return -ENAMETOOLONG;
|
||||
Lock<FastMutex> l(mutex); //A reader-writer lock would be better
|
||||
fsIt it=filesystems.find(StringPart(path));
|
||||
if(it==filesystems.end()) return -EINVAL;
|
||||
|
||||
//This finds all the filesystems that have to be recursively umounted
|
||||
//to umount the required filesystem. For example, if /path and /path/path2
|
||||
//are filesystems, umounting /path must umount also /path/path2
|
||||
vector<fsIt> fsToUmount;
|
||||
for(fsIt it2=filesystems.begin();it2!=filesystems.end();++it2)
|
||||
if(it2->first.startsWith(it->first)) fsToUmount.push_back(it2);
|
||||
|
||||
//Now look into all file descriptor tables if there are open files in the
|
||||
//filesystems to umount. If there are, return busy. This is an heavy
|
||||
//operation given the way the filesystem data structure is organized, but
|
||||
//it has been done like this to minimize the size of an entry in the file
|
||||
//descriptor table (4 bytes), and because umount happens infrequently.
|
||||
//Note that since we are locking the same mutex used by resolvePath(),
|
||||
//other threads can't open new files concurrently while we check
|
||||
#ifdef WITH_PROCESSES
|
||||
list<FileDescriptorTable*>::iterator it3;
|
||||
for(it3=fileTables.begin();it3!=fileTables.end();++it3)
|
||||
{
|
||||
for(int i=0;i<MAX_OPEN_FILES;i++)
|
||||
{
|
||||
intrusive_ref_ptr<FileBase> file=(*it3)->getFile(i);
|
||||
if(!file) continue;
|
||||
vector<fsIt>::iterator it4;
|
||||
for(it4=fsToUmount.begin();it4!=fsToUmount.end();++it4)
|
||||
{
|
||||
if(file->getParent()!=(*it4)->second) continue;
|
||||
if(force==false) return -EBUSY;
|
||||
(*it3)->close(i); //If forced umount, close the file
|
||||
}
|
||||
}
|
||||
}
|
||||
#else //WITH_PROCESSES
|
||||
for(int i=0;i<MAX_OPEN_FILES;i++)
|
||||
{
|
||||
intrusive_ref_ptr<FileBase> file=getFileDescriptorTable().getFile(i);
|
||||
if(!file) continue;
|
||||
vector<fsIt>::iterator it4;
|
||||
for(it4=fsToUmount.begin();it4!=fsToUmount.end();++it4)
|
||||
{
|
||||
if(file->getParent()!=(*it4)->second) continue;
|
||||
if(force==false) return -EBUSY;
|
||||
getFileDescriptorTable().close(i);//If forced umount, close the file
|
||||
}
|
||||
}
|
||||
#endif //WITH_PROCESSES
|
||||
|
||||
//Now there should be no more files belonging to the filesystems to umount,
|
||||
//but check if it is really so, as there is a possible race condition
|
||||
//which is the read/close,umount where one thread performs a read (or write)
|
||||
//operation on a file descriptor, it gets preempted and another thread does
|
||||
//a close on that descriptor and an umount of the filesystem. Also, this may
|
||||
//happen in case of a forced umount. In such a case there is no entry in
|
||||
//the descriptor table (as close was called) but the operation is still
|
||||
//ongoing.
|
||||
vector<fsIt>::iterator it5;
|
||||
const int maxRetry=3; //Retry up to three times
|
||||
for(int i=0;i<maxRetry;i++)
|
||||
{
|
||||
bool failed=false;
|
||||
for(it5=fsToUmount.begin();it5!=fsToUmount.end();++it5)
|
||||
{
|
||||
if((*it5)->second->areAllFilesClosed()) continue;
|
||||
if(force==false) return -EBUSY;
|
||||
failed=true;
|
||||
break;
|
||||
}
|
||||
if(!failed) break;
|
||||
if(i==maxRetry-1) return -EBUSY; //Failed to umount even if forced
|
||||
Thread::sleep(1000); //Wait to see if the filesystem operation completes
|
||||
}
|
||||
|
||||
//It is now safe to umount all filesystems
|
||||
for(it5=fsToUmount.begin();it5!=fsToUmount.end();++it5)
|
||||
filesystems.erase(*it5);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FilesystemManager::umountAll()
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
#ifdef WITH_PROCESSES
|
||||
list<FileDescriptorTable*>::iterator it;
|
||||
for(it=fileTables.begin();it!=fileTables.end();++it) (*it)->closeAll();
|
||||
#else //WITH_PROCESSES
|
||||
getFileDescriptorTable().closeAll();
|
||||
#endif //WITH_PROCESSES
|
||||
filesystems.clear();
|
||||
}
|
||||
|
||||
ResolvedPath FilesystemManager::resolvePath(string& path, bool followLastSymlink)
|
||||
{
|
||||
//see man path_resolution. This code supports arbitrarily mounted
|
||||
//filesystems, symbolic links resolution, but no hardlinks to directories
|
||||
if(path.length()>PATH_MAX) return ResolvedPath(-ENAMETOOLONG);
|
||||
if(path.empty() || path[0]!='/') return ResolvedPath(-ENOENT);
|
||||
|
||||
Lock<FastMutex> l(mutex);
|
||||
PathResolution pr(filesystems);
|
||||
return pr.resolvePath(path,followLastSymlink);
|
||||
}
|
||||
|
||||
int FilesystemManager::unlinkHelper(string& path)
|
||||
{
|
||||
//Do everything while keeping the mutex locked to prevent someone to
|
||||
//concurrently mount a filesystem on the directory we're unlinking
|
||||
Lock<FastMutex> l(mutex);
|
||||
ResolvedPath openData=resolvePath(path,true);
|
||||
if(openData.result<0) return openData.result;
|
||||
//After resolvePath() so path is in canonical form and symlinks are followed
|
||||
if(filesystems.find(StringPart(path))!=filesystems.end()) return -EBUSY;
|
||||
StringPart sp(path,string::npos,openData.off);
|
||||
return openData.fs->unlink(sp);
|
||||
}
|
||||
|
||||
int FilesystemManager::statHelper(string& path, struct stat *pstat, bool f)
|
||||
{
|
||||
ResolvedPath openData=resolvePath(path,f);
|
||||
if(openData.result<0) return openData.result;
|
||||
StringPart sp(path,string::npos,openData.off);
|
||||
return openData.fs->lstat(sp,pstat);
|
||||
}
|
||||
|
||||
int FilesystemManager::renameHelper(string& oldPath, string& newPath)
|
||||
{
|
||||
//Do everything while keeping the mutex locked to prevent someone to
|
||||
//concurrently mount a filesystem on the directory we're renaming
|
||||
Lock<FastMutex> l(mutex);
|
||||
ResolvedPath oldOpenData=resolvePath(oldPath,true);
|
||||
if(oldOpenData.result<0) return oldOpenData.result;
|
||||
ResolvedPath newOpenData=resolvePath(newPath,true);
|
||||
if(newOpenData.result<0) return newOpenData.result;
|
||||
|
||||
if(oldOpenData.fs!=newOpenData.fs) return -EXDEV; //Can't rename across fs
|
||||
|
||||
//After resolvePath() so path is in canonical form and symlinks are followed
|
||||
if(filesystems.find(StringPart(oldPath))!=filesystems.end()) return -EBUSY;
|
||||
if(filesystems.find(StringPart(newPath))!=filesystems.end()) return -EBUSY;
|
||||
|
||||
StringPart oldSp(oldPath,string::npos,oldOpenData.off);
|
||||
StringPart newSp(newPath,string::npos,newOpenData.off);
|
||||
|
||||
//Can't rename a directory into a subdirectory of itself
|
||||
if(newSp.startsWith(oldSp)) return -EINVAL;
|
||||
return oldOpenData.fs->rename(oldSp,newSp);
|
||||
}
|
||||
|
||||
short int FilesystemManager::getFilesystemId()
|
||||
{
|
||||
return atomicAddExchange(&devCount,1);
|
||||
}
|
||||
|
||||
int FilesystemManager::devCount=1;
|
||||
|
||||
#ifdef WITH_DEVFS
|
||||
intrusive_ref_ptr<DevFs> //return value is a pointer to DevFs
|
||||
#else //WITH_DEVFS
|
||||
void //return value is void
|
||||
#endif //WITH_DEVFS
|
||||
basicFilesystemSetup(intrusive_ref_ptr<Device> dev)
|
||||
{
|
||||
bootlog("Mounting MountpointFs as / ... ");
|
||||
FilesystemManager& fsm=FilesystemManager::instance();
|
||||
intrusive_ref_ptr<FilesystemBase> rootFs(new MountpointFs);
|
||||
bootlog(fsm.kmount("/",rootFs)==0 ? "Ok\n" : "Failed\n");
|
||||
|
||||
#ifdef WITH_DEVFS
|
||||
bootlog("Mounting DevFs as /dev ... ");
|
||||
StringPart sp("dev");
|
||||
int r1=rootFs->mkdir(sp,0755);
|
||||
intrusive_ref_ptr<DevFs> devfs(new DevFs);
|
||||
int r2=fsm.kmount("/dev",devfs);
|
||||
bool devFsOk=(r1==0 && r2==0);
|
||||
bootlog(devFsOk ? "Ok\n" : "Failed\n");
|
||||
if(!devFsOk) return devfs;
|
||||
fsm.setDevFs(devfs);
|
||||
#endif //WITH_DEVFS
|
||||
|
||||
bootlog("Mounting Fat32Fs as /sd ... ");
|
||||
bool fat32failed=false;
|
||||
intrusive_ref_ptr<FileBase> disk;
|
||||
#ifdef WITH_DEVFS
|
||||
if(dev) devfs->addDevice("sda",dev);
|
||||
StringPart sda("sda");
|
||||
if(devfs->open(disk,sda,O_RDWR,0)<0) fat32failed=true;
|
||||
#else //WITH_DEVFS
|
||||
if(dev && dev->open(disk,intrusive_ref_ptr<FilesystemBase>(0),O_RDWR,0)<0)
|
||||
fat32failed=true;
|
||||
#endif //WITH_DEVFS
|
||||
|
||||
intrusive_ref_ptr<Fat32Fs> fat32;
|
||||
if(fat32failed==false)
|
||||
{
|
||||
fat32=new Fat32Fs(disk);
|
||||
if(fat32->mountFailed()) fat32failed=true;
|
||||
}
|
||||
if(fat32failed==false)
|
||||
{
|
||||
StringPart sd("sd");
|
||||
fat32failed=rootFs->mkdir(sd,0755)!=0;
|
||||
fat32failed=fsm.kmount("/sd",fat32)!=0;
|
||||
}
|
||||
bootlog(fat32failed==0 ? "Ok\n" : "Failed\n");
|
||||
|
||||
#ifdef WITH_DEVFS
|
||||
return devfs;
|
||||
#endif //WITH_DEVFS
|
||||
}
|
||||
|
||||
FileDescriptorTable& getFileDescriptorTable()
|
||||
{
|
||||
#ifdef WITH_PROCESSES
|
||||
//Something like
|
||||
return Thread::getCurrentThread()->getProcess()->getFileTable();
|
||||
#else //WITH_PROCESSES
|
||||
static FileDescriptorTable fileTable; ///< The only file table
|
||||
return fileTable;
|
||||
#endif //WITH_PROCESSES
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //WITH_FILESYSTEM
|
||||
|
|
@ -0,0 +1,553 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef FILE_ACCESS_H
|
||||
#define FILE_ACCESS_H
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include "file.h"
|
||||
#include "stringpart.h"
|
||||
#include "devfs/devfs.h"
|
||||
#include "kernel/sync.h"
|
||||
#include "kernel/intrusive.h"
|
||||
#include "config/miosix_settings.h"
|
||||
|
||||
#ifdef WITH_FILESYSTEM
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* The result of resolvePath().
|
||||
*/
|
||||
class ResolvedPath
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
ResolvedPath() : result(-EINVAL), fs(0), off(0) {}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* \param result error code
|
||||
*/
|
||||
explicit ResolvedPath(int result) : result(result), fs(0), off(0) {}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* \param fs filesystem
|
||||
* \param off offset into path where the subpath relative to the current
|
||||
* filesystem starts
|
||||
*/
|
||||
ResolvedPath(intrusive_ref_ptr<FilesystemBase> fs, size_t offset)
|
||||
: result(0), fs(fs), off(offset) {}
|
||||
|
||||
int result; ///< 0 on success, a negative number on failure
|
||||
intrusive_ref_ptr<FilesystemBase> fs; ///< pointer to the filesystem to which the file belongs
|
||||
/// path.c_str()+off is a string containing the relative path into the
|
||||
/// filesystem for the looked up file
|
||||
size_t off;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class maps file descriptors to file objects, allowing to
|
||||
* perform file operations
|
||||
*/
|
||||
class FileDescriptorTable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
FileDescriptorTable();
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
* \param rhs object to copy from
|
||||
*/
|
||||
FileDescriptorTable(const FileDescriptorTable& rhs);
|
||||
|
||||
/**
|
||||
* Operator=
|
||||
* \param rhs object to copy from
|
||||
* \return *this
|
||||
*/
|
||||
FileDescriptorTable& operator=(const FileDescriptorTable& rhs);
|
||||
|
||||
/**
|
||||
* Open a file
|
||||
* \param name file name
|
||||
* \param flags file open mode
|
||||
* \param mode allows to set file permissions
|
||||
* \return a file descriptor, or a negative number on error
|
||||
*/
|
||||
int open(const char *name, int flags, int mode);
|
||||
|
||||
/**
|
||||
* Close a file
|
||||
* \param fd file descriptor to close
|
||||
* \return 0 on success or a negative number on failure
|
||||
*/
|
||||
int close(int fd);
|
||||
|
||||
/**
|
||||
* Close all files
|
||||
*/
|
||||
void closeAll();
|
||||
|
||||
/**
|
||||
* Write data to the file, if the file supports writing.
|
||||
* \param data the data to write
|
||||
* \param len the number of bytes to write
|
||||
* \return the number of written characters, or a negative number in case
|
||||
* of errors
|
||||
*/
|
||||
ssize_t write(int fd, const void *data, size_t len)
|
||||
{
|
||||
if(data==0) return -EFAULT;
|
||||
//Important, since len is specified by standard to be unsigned, but the
|
||||
//return value has to be signed
|
||||
if(static_cast<ssize_t>(len)<0) return -EINVAL;
|
||||
intrusive_ref_ptr<FileBase> file=getFile(fd);
|
||||
if(!file) return -EBADF;
|
||||
return file->write(data,len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data from the file, if the file supports reading.
|
||||
* \param data buffer to store read data
|
||||
* \param len the number of bytes to read
|
||||
* \return the number of read characters, or a negative number in case
|
||||
* of errors
|
||||
*/
|
||||
ssize_t read(int fd, void *data, size_t len)
|
||||
{
|
||||
if(data==0) return -EFAULT;
|
||||
//Important, since len is specified by standard to be unsigned, but the
|
||||
//return value has to be signed
|
||||
if(static_cast<ssize_t>(len)<0) return -EINVAL;
|
||||
intrusive_ref_ptr<FileBase> file=getFile(fd);
|
||||
if(!file) return -EBADF;
|
||||
return file->read(data,len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move file pointer, if the file supports random-access.
|
||||
* \param pos offset to sum to the beginning of the file, current position
|
||||
* or end of file, depending on whence
|
||||
* \param whence SEEK_SET, SEEK_CUR or SEEK_END
|
||||
* \return the offset from the beginning of the file if the operation
|
||||
* completed, or a negative number in case of errors
|
||||
*/
|
||||
off_t lseek(int fd, off_t pos, int whence)
|
||||
{
|
||||
intrusive_ref_ptr<FileBase> file=getFile(fd);
|
||||
if(!file) return -EBADF;
|
||||
return file->lseek(pos,whence);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return file information.
|
||||
* \param pstat pointer to stat struct
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
int fstat(int fd, struct stat *pstat) const
|
||||
{
|
||||
if(pstat==0) return -EFAULT;
|
||||
intrusive_ref_ptr<FileBase> file=getFile(fd);
|
||||
if(!file) return -EBADF;
|
||||
return file->fstat(pstat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the file refers to a terminal.
|
||||
* \return 1 if it is a terminal, 0 if it is not, or a negative number in
|
||||
* case of errors
|
||||
*/
|
||||
int isatty(int fd) const
|
||||
{
|
||||
intrusive_ref_ptr<FileBase> file=getFile(fd);
|
||||
if(!file) return -EBADF;
|
||||
return file->isatty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return file information, follows last symlink
|
||||
* \param path file to stat
|
||||
* \param pstat pointer to stat struct
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
int stat(const char *name, struct stat *pstat)
|
||||
{
|
||||
return statImpl(name,pstat,true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return file information, does not follow last symlink
|
||||
* \param path file to stat
|
||||
* \param pstat pointer to stat struct
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
int lstat(const char *name, struct stat *pstat)
|
||||
{
|
||||
return statImpl(name,pstat,false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform various operations on a file descriptor
|
||||
* \param cmd specifies the operation to perform
|
||||
* \param opt optional argument that some operation require
|
||||
* \return the exact return value depends on CMD, -1 is returned on error
|
||||
*/
|
||||
int fcntl(int fd, int cmd, int opt)
|
||||
{
|
||||
intrusive_ref_ptr<FileBase> file=getFile(fd);
|
||||
if(!file) return -EBADF;
|
||||
return file->fcntl(cmd,opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform various operations on a file descriptor
|
||||
* \param cmd specifies the operation to perform
|
||||
* \param arg optional argument that some operation require
|
||||
* \return the exact return value depends on CMD, -1 is returned on error
|
||||
*/
|
||||
int ioctl(int fd, int cmd, void *arg)
|
||||
{
|
||||
//arg unchecked here, as some ioctl don't use it
|
||||
intrusive_ref_ptr<FileBase> file=getFile(fd);
|
||||
if(!file) return -EBADF;
|
||||
return file->ioctl(cmd,arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* List directory content
|
||||
* \param dp dp pointer to a memory buffer where one or more struct dirent
|
||||
* will be placed. dp must be four words aligned.
|
||||
* \param len memory buffer size.
|
||||
* \return the number of bytes read on success, or a negative number on
|
||||
* failure.
|
||||
*/
|
||||
int getdents(int fd, void *dp, int len)
|
||||
{
|
||||
if(dp==0) return -EFAULT;
|
||||
if(reinterpret_cast<unsigned>(dp) & 0x3) return -EFAULT; //Not aligned
|
||||
intrusive_ref_ptr<FileBase> file=getFile(fd);
|
||||
if(!file) return -EBADF;
|
||||
return file->getdents(dp,len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return current directory
|
||||
* \param buf the current directory is stored here
|
||||
* \param len buffer length, if it is not big enough, ERANGE is returned
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
int getcwd(char *buf, size_t len);
|
||||
|
||||
/**
|
||||
* Change current directory
|
||||
* \param path new current directory
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
int chdir(const char *name);
|
||||
|
||||
/**
|
||||
* Create a directory
|
||||
* \param name directory to create
|
||||
* \param mode directory permissions
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
int mkdir(const char *name, int mode);
|
||||
|
||||
/**
|
||||
* Remove a directory if empty
|
||||
* \param name directory to create
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
int rmdir(const char *name);
|
||||
|
||||
/**
|
||||
* Remove a file or directory
|
||||
* \param name file or directory to remove
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
int unlink(const char *name);
|
||||
|
||||
/**
|
||||
* Rename a file or directory
|
||||
* \param oldName old file name
|
||||
* \param newName new file name
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
int rename(const char *oldName, const char *newName);
|
||||
|
||||
/**
|
||||
* Retrieves an entry in the file descriptor table
|
||||
* \param fd file descriptor, index into the table
|
||||
* \return a refcounted poiter to the file at the desired entry
|
||||
* (which may be empty), or an empty refcounted pointer if the index is
|
||||
* out of bounds
|
||||
*/
|
||||
intrusive_ref_ptr<FileBase> getFile(int fd) const
|
||||
{
|
||||
if(fd<0 || fd>=MAX_OPEN_FILES) return intrusive_ref_ptr<FileBase>();
|
||||
return atomic_load(files+fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~FileDescriptorTable();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Append cwd to path if it is not an absolute path
|
||||
* \param path an absolute or relative path, must not be null
|
||||
* \return an absolute path, or an empty string if the path would exceed
|
||||
* PATH_MAX
|
||||
*/
|
||||
std::string absolutePath(const char *path);
|
||||
|
||||
/**
|
||||
* Return file information (implements both stat and lstat)
|
||||
* \param path file to stat
|
||||
* \param pstat pointer to stat struct
|
||||
* \param f true to follow last synlink (stat),
|
||||
* false to not follow it (lstat)
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
int statImpl(const char *name, struct stat *pstat, bool f);
|
||||
|
||||
FastMutex mutex; ///< Locks on writes to file object pointers, not on accesses
|
||||
|
||||
std::string cwd; ///< Current working directory
|
||||
|
||||
/// Holds the mapping between fd and file objects
|
||||
intrusive_ref_ptr<FileBase> files[MAX_OPEN_FILES];
|
||||
};
|
||||
|
||||
/**
|
||||
* This class contains information on all the mounted filesystems
|
||||
*/
|
||||
class FilesystemManager
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \return the instance of the filesystem manager (singleton)
|
||||
*/
|
||||
static FilesystemManager& instance();
|
||||
|
||||
/**
|
||||
* Low level mount operation, meant to be used only inside the kernel,
|
||||
* and board support packages. It is the only mount operation that can
|
||||
* mount the root filesystem.
|
||||
* \param path path where to mount the filesystem
|
||||
* \param fs filesystem to mount. Ownership of the pointer is transferred
|
||||
* to the FilesystemManager class
|
||||
* \return 0 on success, a negative number on failure
|
||||
*/
|
||||
int kmount(const char *path, intrusive_ref_ptr<FilesystemBase> fs);
|
||||
|
||||
/**
|
||||
* Unmounts a filesystem
|
||||
* \param path path to a filesytem
|
||||
* \param force true to umount the filesystem even if busy
|
||||
* \return 0 on success, or a negative number on error
|
||||
*/
|
||||
int umount(const char *path, bool force=false);
|
||||
|
||||
/**
|
||||
* Umount all filesystems, to be called before system shutdown or reboot
|
||||
*/
|
||||
void umountAll();
|
||||
|
||||
#ifdef WITH_DEVFS
|
||||
/**
|
||||
* \return a pointer to the devfs, useful to add other device files
|
||||
*/
|
||||
intrusive_ref_ptr<DevFs> getDevFs() const { return atomic_load(&devFs); }
|
||||
|
||||
/**
|
||||
* Called by basicFilesystemSetup() or directly by the BSP to set the
|
||||
* pointer returned by getDevFs()
|
||||
* \param dev pointer to the DevFs
|
||||
*/
|
||||
void setDevFs(intrusive_ref_ptr<DevFs> dev)
|
||||
{
|
||||
atomic_store(&devFs,dev);
|
||||
}
|
||||
#endif //WITH_DEVFS
|
||||
|
||||
/**
|
||||
* Resolve a path to identify the filesystem it belongs
|
||||
* \param path an absolute path name, that must start with '/'. Note that
|
||||
* this is an inout parameter, the string is modified so as to return the
|
||||
* full resolved path. In particular, the returned string differs from the
|
||||
* passed one by not containing useless path components, such as "/./" and
|
||||
* "//", by not containing back path componenets ("/../"), and may be
|
||||
* entirely different from the passed one if a symlink was encountered
|
||||
* during name resolution. The use of an inout parameter is to minimize
|
||||
* the number of copies of the path string, optimizing for speed and size
|
||||
* in the common case, but also means that a copy of the original string
|
||||
* needs to be made if the original has to be used later.
|
||||
* \param followLastSymlink true if the symlink in the last path component
|
||||
*(the one that does not end with a /, if it exists, has to be followed)
|
||||
* \return the resolved path
|
||||
*/
|
||||
ResolvedPath resolvePath(std::string& path, bool followLastSymlink=true);
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Helper function to unlink a file or directory. Only meant to be used by
|
||||
* FileDescriptorTable::unlink()
|
||||
* \param path path of file or directory to unlink
|
||||
* \return 0 on success, or a neagtive number on failure
|
||||
*/
|
||||
int unlinkHelper(std::string& path);
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Helper function to stat a file or directory. Only meant to be used by
|
||||
* FileDescriptorTable::statImpl()
|
||||
* \param path path of file or directory to stat
|
||||
* \param pstat pointer to stat struct
|
||||
* \param f f true to follow last synlink (stat),
|
||||
* false to not follow it (lstat)
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
int statHelper(std::string& path, struct stat *pstat, bool f);
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Helper function to unlink a file or directory. Only meant to be used by
|
||||
* FileDescriptorTable::unlink()
|
||||
* \param oldPath path of file or directory to unlink
|
||||
* \param newPath path of file or directory to unlink
|
||||
* \return 0 on success, or a neagtive number on failure
|
||||
*/
|
||||
int renameHelper(std::string& oldPath, std::string& newPath);
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Called by FileDescriptorTable's constructor. Never call this function
|
||||
* from user code.
|
||||
*/
|
||||
void addFileDescriptorTable(FileDescriptorTable *fdt)
|
||||
{
|
||||
#ifdef WITH_PROCESSES
|
||||
if(isKernelRunning())
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
fileTables.push_back(fdt);
|
||||
} else {
|
||||
//This function is also called before the kernel is started,
|
||||
//and in this case it is forbidden to lock mutexes
|
||||
fileTables.push_back(fdt);
|
||||
}
|
||||
#endif //WITH_PROCESSES
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Called by FileDescriptorTable's constructor. Never call this function
|
||||
* from user code.
|
||||
*/
|
||||
void removeFileDescriptorTable(FileDescriptorTable *fdt)
|
||||
{
|
||||
#ifdef WITH_PROCESSES
|
||||
Lock<FastMutex> l(mutex);
|
||||
fileTables.remove(fdt);
|
||||
#endif //WITH_PROCESSES
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \return an unique id used to identify a filesystem, mostly for filling
|
||||
* in the st_dev field when stat is called.
|
||||
*/
|
||||
static short int getFilesystemId();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Constructor, private as it is a singleton
|
||||
*/
|
||||
FilesystemManager() : mutex(FastMutex::RECURSIVE) {}
|
||||
|
||||
FilesystemManager(const FilesystemManager&);
|
||||
FilesystemManager& operator=(const FilesystemManager&);
|
||||
|
||||
FastMutex mutex; ///< To protect against concurrent access
|
||||
|
||||
/// Mounted filesystem
|
||||
std::map<StringPart,intrusive_ref_ptr<FilesystemBase> > filesystems;
|
||||
|
||||
#ifdef WITH_PROCESSES
|
||||
std::list<FileDescriptorTable*> fileTables; ///< Process file tables
|
||||
#endif //WITH_PROCESSES
|
||||
#ifdef WITH_DEVFS
|
||||
intrusive_ref_ptr<DevFs> devFs;
|
||||
#endif //WITH_DEVFS
|
||||
|
||||
static int devCount; ///< For assigning filesystemId to filesystems
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a simplified function to mount the root and /dev filesystems,
|
||||
* meant to be called from bspInit2(). It mounts a MountpointFs as root, then
|
||||
* creates a /dev directory, and mounts /dev there. It also takes the passed
|
||||
* device and if it is not null it adds the device di DevFs as /dev/sda.
|
||||
* Last, it attempts to mount /dev/sda at /sd as a Fat32 filesystem.
|
||||
* In case the bsp needs another filesystem setup, such as having a fat32
|
||||
* filesystem as /, this function can't be used, but instead the bsp needs to
|
||||
* mount the filesystems manually.
|
||||
* \param dev disk device that will be added as /dev/sda and mounted on /sd
|
||||
* \return a pointer to the DevFs, so as to be able to add other device files,
|
||||
* but only if WITH_DEVFS is defined
|
||||
*/
|
||||
#ifdef WITH_DEVFS
|
||||
intrusive_ref_ptr<DevFs> //return value is a pointer to DevFs
|
||||
#else //WITH_DEVFS
|
||||
void //return value is void
|
||||
#endif //WITH_DEVFS
|
||||
basicFilesystemSetup(intrusive_ref_ptr<Device> dev);
|
||||
|
||||
/**
|
||||
* \return a pointer to the file descriptor table associated with the
|
||||
* current process.
|
||||
*/
|
||||
FileDescriptorTable& getFileDescriptorTable();
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //WITH_FILESYSTEM
|
||||
|
||||
#endif //FILE_ACCESS_H
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef IOCTL_H
|
||||
#define IOCTL_H
|
||||
|
||||
namespace miosix {
|
||||
|
||||
enum Ioctl
|
||||
{
|
||||
IOCTL_SYNC=100,
|
||||
IOCTL_TCGETATTR=101,
|
||||
IOCTL_TCSETATTR_NOW=102,
|
||||
IOCTL_TCSETATTR_FLUSH=103,
|
||||
IOCTL_TCSETATTR_DRAIN=104,
|
||||
IOCTL_FLUSH=105
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //IOCTL_H
|
||||
|
|
@ -0,0 +1,204 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include "mountpointfs.h"
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include "filesystem/stringpart.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace miosix {
|
||||
|
||||
#ifdef WITH_FILESYSTEM
|
||||
|
||||
/**
|
||||
* Directory class for MountpointFs
|
||||
*/
|
||||
class MountpointFsDirectory : public DirectoryBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \param parent parent filesystem
|
||||
* \param mutex mutex to lock when accessing the file map
|
||||
* \param dirs file map
|
||||
* \param root true if we're listing the root directory
|
||||
* \param currentInode inode of the directory we're listing
|
||||
* \param parentInode inode of the parent directory
|
||||
*/
|
||||
MountpointFsDirectory(intrusive_ref_ptr<FilesystemBase> parent,
|
||||
FastMutex& mutex, map<StringPart,int>& dirs, bool root,
|
||||
int currentInode, int parentInode)
|
||||
: DirectoryBase(parent), mutex(mutex), dirs(dirs),
|
||||
currentInode(currentInode), parentInode(parentInode),
|
||||
first(true), last(false)
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(root && dirs.empty()==false) currentItem=dirs.begin()->first.c_str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Also directories can be opened as files. In this case, this system call
|
||||
* allows to retrieve directory entries.
|
||||
* \param dp pointer to a memory buffer where one or more struct dirent
|
||||
* will be placed. dp must be four words aligned.
|
||||
* \param len memory buffer size.
|
||||
* \return the number of bytes read on success, or a negative number on
|
||||
* failure.
|
||||
*/
|
||||
virtual int getdents(void *dp, int len);
|
||||
|
||||
private:
|
||||
FastMutex& mutex; ///< Mutex of parent class
|
||||
std::map<StringPart,int>& dirs; ///< Directory entries of parent class
|
||||
string currentItem; ///< First unhandled item in directory
|
||||
int currentInode,parentInode; ///< Inodes of . and ..
|
||||
|
||||
bool first; ///< True if first time getdents is called
|
||||
bool last; ///< True if directory has ended
|
||||
};
|
||||
|
||||
//
|
||||
// class MountpointFsDirctory
|
||||
//
|
||||
|
||||
int MountpointFsDirectory::getdents(void *dp, int len)
|
||||
{
|
||||
if(len<minimumBufferSize) return -EINVAL;
|
||||
if(last) return 0;
|
||||
|
||||
Lock<FastMutex> l(mutex);
|
||||
char *begin=reinterpret_cast<char*>(dp);
|
||||
char *buffer=begin;
|
||||
char *end=buffer+len;
|
||||
if(first)
|
||||
{
|
||||
first=false;
|
||||
addDefaultEntries(&buffer,currentInode,parentInode);
|
||||
}
|
||||
if(currentItem.empty()==false)
|
||||
{
|
||||
map<StringPart,int>::iterator it=dirs.find(StringPart(currentItem));
|
||||
//Someone deleted the exact directory entry we had saved (unlikely)
|
||||
if(it==dirs.end()) return -EBADF;
|
||||
for(;it!=dirs.end();++it)
|
||||
{
|
||||
if(addEntry(&buffer,end,it->second,DT_DIR,it->first)>0) continue;
|
||||
//Buffer finished
|
||||
currentItem=it->first.c_str();
|
||||
return buffer-begin;
|
||||
}
|
||||
}
|
||||
addTerminatingEntry(&buffer,end);
|
||||
last=true;
|
||||
return buffer-begin;
|
||||
}
|
||||
|
||||
//
|
||||
// class MountpointFs
|
||||
//
|
||||
|
||||
int MountpointFs::open(intrusive_ref_ptr<FileBase>& file, StringPart& name,
|
||||
int flags, int mode)
|
||||
{
|
||||
if(flags & (O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC))
|
||||
return -EACCES;
|
||||
|
||||
Lock<FastMutex> l(mutex);
|
||||
int currentInode=rootDirInode;
|
||||
int parentInode=parentFsMountpointInode;
|
||||
if(name.empty()==false)
|
||||
{
|
||||
map<StringPart,int>::iterator it=dirs.find(name);
|
||||
if(it==dirs.end()) return -EACCES;
|
||||
parentInode=currentInode;
|
||||
currentInode=it->second;
|
||||
}
|
||||
file=intrusive_ref_ptr<FileBase>(
|
||||
new MountpointFsDirectory(
|
||||
shared_from_this(),mutex,dirs,name.empty(),currentInode,parentInode));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MountpointFs::lstat(StringPart& name, struct stat *pstat)
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
map<StringPart,int>::iterator it;
|
||||
if(name.empty()==false)
|
||||
{
|
||||
it=dirs.find(name);
|
||||
if(it==dirs.end()) return -ENOENT;
|
||||
}
|
||||
memset(pstat,0,sizeof(struct stat));
|
||||
pstat->st_dev=filesystemId;
|
||||
pstat->st_ino=name.empty() ? rootDirInode : it->second;
|
||||
pstat->st_mode=S_IFDIR | 0755; //drwxr-xr-x
|
||||
pstat->st_nlink=1;
|
||||
pstat->st_blksize=512;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MountpointFs::unlink(StringPart& name)
|
||||
{
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int MountpointFs::rename(StringPart& oldName, StringPart& newName)
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
map<StringPart,int>::iterator it=dirs.find(oldName);
|
||||
if(it==dirs.end()) return -ENOENT;
|
||||
for(unsigned int i=0;i<newName.length();i++)
|
||||
if(newName[i]=='/')
|
||||
return -EACCES; //MountpointFs does not support subdirectories
|
||||
dirs[newName]=it->second;
|
||||
dirs.erase(it);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MountpointFs::mkdir(StringPart& name, int mode)
|
||||
{
|
||||
for(unsigned int i=0;i<name.length();i++)
|
||||
if(name[i]=='/')
|
||||
return -EACCES; //MountpointFs does not support subdirectories
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(dirs.insert(make_pair(name,inodeCount)).second==false) return -EEXIST;
|
||||
inodeCount++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MountpointFs::rmdir(StringPart& name)
|
||||
{
|
||||
Lock<FastMutex> l(mutex);
|
||||
if(dirs.erase(name)==1) return 0;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
#endif //WITH_FILESYSTEM
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef MOUNTPOINTFS_H
|
||||
#define MOUNTPOINTFS_H
|
||||
|
||||
#include <map>
|
||||
#include "filesystem/file.h"
|
||||
#include "kernel/sync.h"
|
||||
#include "config/miosix_settings.h"
|
||||
|
||||
namespace miosix {
|
||||
|
||||
#ifdef WITH_FILESYSTEM
|
||||
|
||||
/**
|
||||
* MountpointFs is a special filesystem whose purpose is to create directories
|
||||
* to be used as mountpoints for other filesystems.
|
||||
*/
|
||||
class MountpointFs : public FilesystemBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
MountpointFs() : mutex(FastMutex::RECURSIVE), inodeCount(rootDirInode+1) {}
|
||||
|
||||
/**
|
||||
* Open a file
|
||||
* \param file the file object will be stored here, if the call succeeds
|
||||
* \param name the name of the file to open, relative to the local
|
||||
* filesystem
|
||||
* \param flags file flags (open for reading, writing, ...)
|
||||
* \param mode file permissions
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int open(intrusive_ref_ptr<FileBase>& file, StringPart& name,
|
||||
int flags, int mode);
|
||||
|
||||
/**
|
||||
* Obtain information on a file, identified by a path name. Does not follow
|
||||
* symlinks
|
||||
* \param name path name, relative to the local filesystem
|
||||
* \param pstat file information is stored here
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int lstat(StringPart& name, struct stat *pstat);
|
||||
|
||||
/**
|
||||
* Remove a file or directory
|
||||
* \param name path name of file or directory to remove
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int unlink(StringPart& name);
|
||||
|
||||
/**
|
||||
* Rename a file or directory
|
||||
* \param oldName old file name
|
||||
* \param newName new file name
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int rename(StringPart& oldName, StringPart& newName);
|
||||
|
||||
/**
|
||||
* Create a directory
|
||||
* \param name directory name
|
||||
* \param mode directory permissions
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int mkdir(StringPart& name, int mode);
|
||||
|
||||
/**
|
||||
* Remove a directory if empty
|
||||
* \param name directory name
|
||||
* \return 0 on success, or a negative number on failure
|
||||
*/
|
||||
virtual int rmdir(StringPart& name);
|
||||
|
||||
private:
|
||||
FastMutex mutex;
|
||||
std::map<StringPart,int> dirs;
|
||||
int inodeCount;
|
||||
static const int rootDirInode=1;
|
||||
};
|
||||
|
||||
#endif //WITH_FILESYSTEM
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //MOUNTPOINTFS_H
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
//Makes memrchr available in newer GCCs
|
||||
#define _GNU_SOURCE
|
||||
#include <string.h>
|
||||
|
||||
#include "stringpart.h"
|
||||
#include <cassert>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace miosix {
|
||||
|
||||
//
|
||||
// class StringPart
|
||||
//
|
||||
|
||||
StringPart::StringPart(string& str, size_t idx, size_t off)
|
||||
: str(&str), index(idx), offset(off), saved('\0'), owner(false),
|
||||
type(CPPSTR)
|
||||
{
|
||||
if(index==string::npos || index>=str.length()) index=str.length();
|
||||
else {
|
||||
saved=str[index];
|
||||
str[index]='\0';
|
||||
}
|
||||
offset=min(offset,index);
|
||||
}
|
||||
|
||||
StringPart::StringPart(char* s, size_t idx, size_t off)
|
||||
: cstr(s), index(idx), offset(off), saved('\0'), owner(false),
|
||||
type(CSTR)
|
||||
{
|
||||
assert(cstr); //Passed pointer can't be null
|
||||
size_t len=strlen(cstr);
|
||||
if(index==string::npos || index>=len) index=len;
|
||||
else {
|
||||
saved=cstr[index];
|
||||
cstr[index]='\0';
|
||||
}
|
||||
offset=min(offset,index);
|
||||
}
|
||||
|
||||
StringPart::StringPart(const char* s)
|
||||
: ccstr(s), offset(0), saved('\0'), owner(false), type(CCSTR)
|
||||
{
|
||||
assert(ccstr); //Passed pointer can't be null
|
||||
index=strlen(s);
|
||||
}
|
||||
|
||||
StringPart::StringPart(StringPart& rhs, size_t idx, size_t off)
|
||||
: saved('\0'), owner(false), type(rhs.type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case CSTR:
|
||||
this->cstr=rhs.cstr;
|
||||
break;
|
||||
case CCSTR:
|
||||
type=CSTR; //To make a substring of a CCSTR we need to make a copy
|
||||
if(rhs.empty()==false) assign(rhs); else cstr=&saved;
|
||||
break;
|
||||
case CPPSTR:
|
||||
this->str=rhs.str;
|
||||
break;
|
||||
}
|
||||
if(idx!=string::npos && idx<rhs.length())
|
||||
{
|
||||
index=rhs.offset+idx;//Make index relative to beginning of original str
|
||||
if(type==CPPSTR)
|
||||
{
|
||||
saved=(*str)[index];
|
||||
(*str)[index]='\0';
|
||||
} else {
|
||||
saved=cstr[index];
|
||||
cstr[index]='\0';
|
||||
}
|
||||
} else index=rhs.index;
|
||||
offset=min(rhs.offset+off,index); //Works for CCSTR as offset is always zero
|
||||
}
|
||||
|
||||
StringPart::StringPart(const StringPart& rhs)
|
||||
: cstr(&saved), index(0), offset(0), saved('\0'), owner(false),
|
||||
type(CSTR)
|
||||
{
|
||||
if(rhs.empty()==false) assign(rhs);
|
||||
}
|
||||
|
||||
StringPart& StringPart::operator= (const StringPart& rhs)
|
||||
{
|
||||
if(this==&rhs) return *this; //Self assignment
|
||||
//Don't forget that we may own a pointer to someone else's string,
|
||||
//so always clear() to detach from a string on assignment!
|
||||
clear();
|
||||
if(rhs.empty()==false) assign(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool StringPart::startsWith(const StringPart& rhs) const
|
||||
{
|
||||
if(this->length()<rhs.length()) return false;
|
||||
return memcmp(this->c_str(),rhs.c_str(),rhs.length())==0;
|
||||
}
|
||||
|
||||
size_t StringPart::findLastOf(char c) const
|
||||
{
|
||||
const char *begin=c_str();
|
||||
//Not strrchr() to take advantage of knowing the string length
|
||||
void *index=memrchr(begin,c,length());
|
||||
if(index==0) return std::string::npos;
|
||||
return reinterpret_cast<char*>(index)-begin;
|
||||
}
|
||||
|
||||
const char *StringPart::c_str() const
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case CSTR: return cstr+offset;
|
||||
case CCSTR: return ccstr; //Offset always 0
|
||||
default: return str->c_str()+offset;
|
||||
}
|
||||
}
|
||||
|
||||
char StringPart::operator[] (size_t index) const
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case CSTR: return cstr[offset+index];
|
||||
case CCSTR: return ccstr[index]; //Offset always 0
|
||||
default: return (*str)[offset+index];
|
||||
}
|
||||
}
|
||||
|
||||
void StringPart::clear()
|
||||
{
|
||||
if(type==CSTR)
|
||||
{
|
||||
cstr[index]=saved;//Worst case we'll overwrite terminating \0 with an \0
|
||||
if(owner) delete[] cstr;
|
||||
} else if(type==CPPSTR) {
|
||||
if(index!=str->length()) (*str)[index]=saved;
|
||||
if(owner) delete str;
|
||||
} //For CCSTR there's nothing to do
|
||||
cstr=&saved; //Reusing saved as an empty string
|
||||
saved='\0';
|
||||
index=offset=0;
|
||||
owner=false;
|
||||
type=CSTR;
|
||||
}
|
||||
|
||||
void StringPart::assign(const StringPart& rhs)
|
||||
{
|
||||
cstr=new char[rhs.length()+1];
|
||||
strcpy(cstr,rhs.c_str());
|
||||
index=rhs.length();
|
||||
offset=0;
|
||||
owner=true;
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef STRINGPART_H
|
||||
#define STRINGPART_H
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* This class is used to take a substring of a string containing a file path
|
||||
* without creating a copy, and therefore requiring additional memory
|
||||
* allocation.
|
||||
*
|
||||
* When parsing a path like "/home/test/directory/file" it is often necessary
|
||||
* to create substrings at the path component boundaries, such as "/home/test".
|
||||
* In this case, it is possible to temporarily make a substring by replacing a
|
||||
* '/' with a '\0'. The std::string will not 'forget' its orginal size, and
|
||||
* when the '\0' will be converted back to a '/', the string will look identical
|
||||
* to the previous one.
|
||||
*
|
||||
* This is an optimization made for filesystem mountpoint lookups, and is not
|
||||
* believed to be useful outside of that purpose. Given that in Miosix, the
|
||||
* mountpoints are stored in a map, this class supports operator< to correctly
|
||||
* lookup mountpoints in the map.
|
||||
*/
|
||||
class StringPart
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
StringPart() : cstr(&saved), index(0), offset(0), saved('\0'),
|
||||
owner(false), type(CSTR)
|
||||
{
|
||||
//We need an empty C string, that is, a pointer to a char that is \0,
|
||||
//so we make cstr point to saved, and set saved to \0.
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor from C++ string
|
||||
* \param str original string. A pointer to the string is taken, the string
|
||||
* is NOT copied. Therefore, the caller is responsible to guarantee the
|
||||
* string won't be deallocated while this class is alive. Note that the
|
||||
* original string is modified by inserting a '\0' at the index position,
|
||||
* if index is given. The string will be restored to the exact original
|
||||
* content only when this class is destroyed.
|
||||
* \param idx if this parameter is given, this class becomes an in-place
|
||||
* substring of the original string. Otherwise, this class will store the
|
||||
* entire string passed. In this case, the original string will not be
|
||||
* modified.
|
||||
* \param off if this parameter is given, the first off characters of the
|
||||
* string are skipped. Note that idx calculations take place <b>before</b>
|
||||
* offset computation, so idx is relative to the original string.
|
||||
*/
|
||||
explicit StringPart(std::string& str, size_t idx=std::string::npos,
|
||||
size_t off=0);
|
||||
|
||||
/**
|
||||
* Constructor from C string
|
||||
* \param s original string. A pointer to the string is taken, the string
|
||||
* is NOT copied. Therefore, the caller is responsible to guarantee the
|
||||
* string won't be deallocated while this class is alive. Note that the
|
||||
* original string is modified by inserting a '\0' at the index position,
|
||||
* if index is given. The string will be restored to the exact original
|
||||
* content only when this class is destroyed.
|
||||
* \param idx if this parameter is given, this class becomes an in-place
|
||||
* substring of the original string. Otherwise, this class will store the
|
||||
* entire string passed. In this case, the original string will not be
|
||||
* modified.
|
||||
* \param off if this parameter is given, the first off characters of the
|
||||
* string are skipped. Note that idx calculations take place <b>before</b>
|
||||
* offset computation, so idx is relative to the original string.
|
||||
*/
|
||||
explicit StringPart(char *s, size_t idx=std::string::npos, size_t off=0);
|
||||
|
||||
/**
|
||||
* Constructor from const C string
|
||||
* \param s original string. A pointer to the string is taken, the string
|
||||
* is NOT copied. Therefore, the caller is responsible to guarantee the
|
||||
* string won't be deallocated while this class is alive. Note that this
|
||||
* constructor misses the idx and off parameters as it's not possible to
|
||||
* make an in-place substring of a string if it's const.
|
||||
*/
|
||||
explicit StringPart(const char *s);
|
||||
|
||||
/**
|
||||
* Substring constructor. Given a StringPart, produce another StringPart
|
||||
* holding a substring of the original StringPart. Unlike the normal copy
|
||||
* constructor that does deep copy, this one does shallow copy, and
|
||||
* therefore the newly created object will share the same string pointer
|
||||
* as rhs. Useful for making substrings of a substring without memory
|
||||
* allocation.
|
||||
* \param rhs a StringPart
|
||||
*/
|
||||
StringPart(StringPart& rhs, size_t idx, size_t off=0);
|
||||
|
||||
/**
|
||||
* Copy constructor. Note that deep copying is used, so that the newly
|
||||
* created StringPart is a self-contained string. It has been done like
|
||||
* that to be able to store the paths of mounted filesystems in an std::map
|
||||
* \param rhs a StringPart
|
||||
*/
|
||||
StringPart(const StringPart& rhs);
|
||||
|
||||
/**
|
||||
* Operator = Note that deep copying is used, so that the assigned
|
||||
* StringPart becomes a self-contained string. It has been done like
|
||||
* that to be able to store the paths of mounted filesystems in an std::map
|
||||
* \param rhs a StringPart
|
||||
*/
|
||||
StringPart& operator= (const StringPart& rhs);
|
||||
|
||||
/**
|
||||
* Compare two StringParts for inequality
|
||||
* \param rhs second StringPart to compare to
|
||||
* \return true if *this < rhs
|
||||
*/
|
||||
bool operator<(const StringPart& rhs) const
|
||||
{
|
||||
return strcmp(this->c_str(),rhs.c_str())<0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \param rhs a StringPart
|
||||
* \return true if this starts with rhs
|
||||
*/
|
||||
bool startsWith(const StringPart& rhs) const;
|
||||
|
||||
/**
|
||||
* \param c char to find in the string, starting from the end
|
||||
* \return the index of the last occurrence of c, or string::npos
|
||||
*/
|
||||
size_t findLastOf(char c) const;
|
||||
|
||||
/**
|
||||
* \return the StringPart length, which is the same as strlen(this->c_str())
|
||||
*/
|
||||
size_t length() const { return index-offset; }
|
||||
|
||||
/**
|
||||
* \return the StringPart as a C string
|
||||
*/
|
||||
const char *c_str() const;
|
||||
|
||||
/**
|
||||
* \param index index into the string
|
||||
* \return the equivalent of this->c_str()[index]
|
||||
*/
|
||||
char operator[] (size_t index) const;
|
||||
|
||||
/**
|
||||
* \return true if the string is empty
|
||||
*/
|
||||
bool empty() const { return length()==0; }
|
||||
|
||||
/**
|
||||
* Make this an empty string
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~StringPart() { clear(); }
|
||||
|
||||
private:
|
||||
/**
|
||||
* To implement copy constructor and operator=. *this must be empty.
|
||||
* \param rhs other StringPart that is assigned to *this.
|
||||
*/
|
||||
void assign(const StringPart& rhs);
|
||||
|
||||
union {
|
||||
std::string *str; ///< Pointer to underlying C++ string
|
||||
char *cstr; ///< Pointer to underlying C string
|
||||
const char *ccstr; ///< Pointer to underlying const C string
|
||||
};
|
||||
size_t index; ///< Index into the character substituted by '\0'
|
||||
size_t offset; ///< Offset to skip the first part of a string
|
||||
char saved; ///< Char that was replaced by '\0'
|
||||
bool owner; ///< True if this class owns str
|
||||
char type; ///< either CPPSTR or CSTR. Using char to reduce size
|
||||
enum { CPPSTR, CSTR, CCSTR }; ///< Possible values fot type
|
||||
};
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //STRINGPART_H
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ARCH_REGISTERS_H
|
||||
#define ARCH_REGISTERS_H
|
||||
|
||||
/**
|
||||
* \addtogroup Interfaces
|
||||
* \{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file arch_registers.h
|
||||
* This file should contain the list of hardware registers of the selected
|
||||
* architecture, to allow application to directly access the hardware.
|
||||
*
|
||||
* The list of these registers is usually provided by the chip vendor in the
|
||||
* form of one or more header files.
|
||||
*
|
||||
* To include these registers in a portable way, here we only include
|
||||
* arch_registers_impl.h, which will be an header file in
|
||||
* arch/arch name/board name/interfaces_impl
|
||||
*
|
||||
* The usual implementation of arch_registers_impl.h is simply to include
|
||||
* the header files provided by the chip vendor.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
#include "boot/arch_registers_impl.h"
|
||||
|
||||
#endif //ARCH_REGISTERS_H
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013, 2014, 2015 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ATOMIC_OPS_H
|
||||
#define ATOMIC_OPS_H
|
||||
|
||||
/**
|
||||
* \addtogroup Interfaces
|
||||
* \{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file atomic_ops.h
|
||||
* This file contains various atomic operations useful for implementing
|
||||
* lock-free algorithms.
|
||||
*
|
||||
* For architectures without hardware support for these operations, they are
|
||||
* emulated by disabling interrupts. Note that these functions should be safe
|
||||
* to be called also with interrupts disabled, so implementations that disable
|
||||
* interrupts should be careful not to accidentally re-enable them if these
|
||||
* functions are called with interupts disabled.
|
||||
*/
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* Store a value in one memory location, and atomically read back the
|
||||
* previously stored value. Performs atomically the following operation:
|
||||
*
|
||||
* \code
|
||||
* inline int atomicSwap(volatile int *p, int v)
|
||||
* {
|
||||
* int result=*p;
|
||||
* *p=v;
|
||||
* return result;
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* \param p pointer to memory location where the atomic swap will take place
|
||||
* \param v new value to be stored in *p
|
||||
* \return the previous value of *p
|
||||
*/
|
||||
inline int atomicSwap(volatile int *p, int v);
|
||||
|
||||
/**
|
||||
* Atomically read the content of a memory location, add a number to the loaded
|
||||
* value, and store the result. Performs atomically the following operation:
|
||||
*
|
||||
* \code
|
||||
* inline void atomicAdd(volatile int *p, int incr)
|
||||
* {
|
||||
* *p+=incr;
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* \param p pointer to memory location where the atomic add will take place
|
||||
* \param incr value to be added to *p
|
||||
*/
|
||||
inline void atomicAdd(volatile int *p, int incr);
|
||||
|
||||
/**
|
||||
* Atomically read the content of a memory location, add a number to the loaded
|
||||
* value, store the result and return the previous value stored.
|
||||
* Performs atomically the following operation:
|
||||
*
|
||||
* \code
|
||||
* inline int atomicAddExchange(volatile int *p, int incr)
|
||||
* {
|
||||
* int result=*p;
|
||||
* *p+=incr;
|
||||
* return result;
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* \param pointer to memory location where the atomic add will take place
|
||||
* \param incr value to be added to *p
|
||||
* \return the previous value of *p
|
||||
*/
|
||||
inline int atomicAddExchange(volatile int *p, int incr);
|
||||
|
||||
/**
|
||||
* Atomically read the value of a memory location, and store a new value in it
|
||||
* if it matches a given value. Also, return the previously stored value.
|
||||
* Performs atomically the following operation:
|
||||
*
|
||||
* \code
|
||||
* inline int atomicCompareAndSwap(volatile int *p, int prev, int next)
|
||||
* {
|
||||
* int result=*p;
|
||||
* if(*p==prev) *p=next;
|
||||
* return result;
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* \param p pointer to the memory location to compare and swap
|
||||
* \param prev value to be compared against the content of *p
|
||||
* \param next value to be stored in *p if *p==prev
|
||||
* \return the value actually read from *p
|
||||
*/
|
||||
inline int atomicCompareAndSwap(volatile int *p, int prev, int next);
|
||||
|
||||
/**
|
||||
* An implementation of atomicFetchAndIncrement, as described in
|
||||
* http://www.drdobbs.com/atomic-reference-counting-pointers/184401888
|
||||
* Atomically read a pointer stored in one memory loaction, and add
|
||||
* a constant to a memory loaction placed at a given offset from the
|
||||
* pointer. Performs atomically the following operation:
|
||||
*
|
||||
* \code
|
||||
* void *atomicFetchAndIncrement(void * const volatile *p, int offset, int incr)
|
||||
* {
|
||||
* int *result=*p;
|
||||
* if(result==0) return 0;
|
||||
* *(result+offset)+=incr;
|
||||
* return result;
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* \param p pointer to a const volatile pointer to object. While p is not
|
||||
* subject to thread contention, *p is.
|
||||
* \param offset the memory location to increment is **p+offset*sizeof(int)
|
||||
* \param incr value to be added to **p+offset*sizeof(int)
|
||||
* \return *p
|
||||
*/
|
||||
inline void *atomicFetchAndIncrement(void * const volatile * p, int offset,
|
||||
int incr);
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
#ifdef _ARCH_ARM7_LPC2000
|
||||
#include "core/atomic_ops_impl_arm7.h"
|
||||
#elif defined(_ARCH_CORTEXM3) || defined(_ARCH_CORTEXM4) \
|
||||
|| defined(_ARCH_CORTEXM7)
|
||||
#include "core/atomic_ops_impl_cortexMx.h"
|
||||
#elif defined(_ARCH_CORTEXM0)
|
||||
#include "core/atomic_ops_impl_cortexM0.h"
|
||||
#else
|
||||
#error "No atomic ops for this architecture"
|
||||
#endif
|
||||
|
||||
#endif //ATOMIC_OPS_H
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef BSP_H
|
||||
#define BSP_H
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* \addtogroup Interfaces
|
||||
* \{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file bsp.h
|
||||
* This file contains architecture specific board support package.
|
||||
* It must at least provide these four functions:
|
||||
*
|
||||
* IRQbspInit(), to initialize the board to a known state early in the boot
|
||||
* process (before the kernel is started, and when interrupts are disabled)
|
||||
*
|
||||
* bspInit2(), to perform the remaining part of the initialization, once the
|
||||
* kernel is started
|
||||
*
|
||||
* shutdown(), for system shutdown. This function is called in case main()
|
||||
* returns, and is available to be called by user code.
|
||||
*
|
||||
* reboot(), a function that can be called to reboot the system under normal
|
||||
* (non error) conditions. It should sync and unmount the filesystem, and
|
||||
* perform a reboot. This function is available for user code.
|
||||
*
|
||||
* Other than this, the board support package might contain other functions,
|
||||
* classes, macros etc. to support peripherals and or board hardware.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Initializes the I/O pins, and put system peripherals to a known state.<br>
|
||||
* Must be called before starting kernel. Interrupts must be disabled.<br>
|
||||
* This function is used by the kernel and should not be called by user code.
|
||||
*/
|
||||
void IRQbspInit();
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Performs the part of initialization that must be done after the kernel is
|
||||
* started.<br>
|
||||
* This function is used by the kernel and should not be called by user code.
|
||||
*/
|
||||
void bspInit2();
|
||||
|
||||
/**
|
||||
* This function disables filesystem (if enabled), serial port (if enabled) and
|
||||
* shuts down the system, usually by putting the procesor in a deep sleep
|
||||
* state.<br>
|
||||
* The action to start a new boot is system-specific, can be for example a
|
||||
* reset, powercycle or a special GPIO configured to wakeup the processor from
|
||||
* deep sleep.<br>
|
||||
* This function does not return.<br>
|
||||
* WARNING: close all files before using this function, since it unmounts the
|
||||
* filesystem.<br>
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* The difference between this function and miosix_private::IRQsystemReboot()
|
||||
* is that this function disables filesystem (if enabled), serial port
|
||||
* (if enabled) while miosix_private::system_reboot() does not do all these
|
||||
* things. miosix_private::IRQsystemReboot() is designed to reboot the system
|
||||
* when an unrecoverable error occurs, and is used primarily in kernel code,
|
||||
* reboot() is designed to reboot the system in normal conditions.<br>
|
||||
* This function does not return.<br>
|
||||
* WARNING: close all files before using this function, since it unmounts the
|
||||
* filesystem.
|
||||
*/
|
||||
void reboot();
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //BSP_H
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef DELAYS_H
|
||||
#define DELAYS_H
|
||||
|
||||
namespace miosix {
|
||||
|
||||
/**
|
||||
* \addtogroup Interfaces
|
||||
* \{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file delays.h
|
||||
* This file contains two functions, delayMs() and delayUs() which implement
|
||||
* busy wait delays.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Delay function. Accuracy depends on the underlying implementation which is
|
||||
* architecture specific.<br>
|
||||
* Delay time can be inaccurate if interrupts are enabled or the kernel is
|
||||
* running due to time spent in interrupts and due to preemption.<br>
|
||||
* It is implemented using busy wait, so can be safely used even when the
|
||||
* kernel is paused or interrupts are disabled.<br>
|
||||
* If the kernel is running it is *highly* recomended to use Thread::sleep since
|
||||
* it gives CPU time to other threads and/or it puts the CPU in low power mode.
|
||||
* \param mseconds milliseconds to wait
|
||||
*/
|
||||
void delayMs(unsigned int mseconds);
|
||||
|
||||
/**
|
||||
* Delay function. Accuracy depends on the underlying implementation which is
|
||||
* architecture specific.<br>
|
||||
* Delay time can be inaccurate if interrupts are enabled or the kernel is
|
||||
* running due to time spent in interrupts and due to preemption.<br>
|
||||
* It is implemented using busy wait, so can be safely used even when the
|
||||
* kernel is paused or interrupts are disabled.<br>
|
||||
* \param useconds microseconds to wait. Only values between 1 and 1000 are
|
||||
* allowed. For greater delays use Thread::sleep() or delayMs().
|
||||
*/
|
||||
void delayUs(unsigned int useconds);
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
#endif //DELAYS_H
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2011 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef _ARCH_ARM7_LPC2000
|
||||
#include "core/endianness_impl_arm7.h"
|
||||
#elif defined(_ARCH_CORTEXM0) || defined(_ARCH_CORTEXM3) \
|
||||
|| defined(_ARCH_CORTEXM4) || defined(_ARCH_CORTEXM7)
|
||||
#include "core/endianness_impl_cortexMx.h"
|
||||
#else
|
||||
#error "No endianness code for this architecture"
|
||||
#endif
|
||||
|
||||
#ifndef ENDIANNESS_H
|
||||
#define ENDIANNESS_H
|
||||
|
||||
/**
|
||||
* \addtogroup Interfaces
|
||||
* \{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file endianness.h
|
||||
* This file contains optimized functions to convert data from the system's
|
||||
* endianness to little or big endian, as well as to perform byte swapping.
|
||||
*/
|
||||
|
||||
// Implementation of these functions is in endianness_impl.h
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define __MIOSIX_INLINE inline
|
||||
#else //__cplusplus
|
||||
#define __MIOSIX_INLINE static inline
|
||||
#endif //__cplusplus
|
||||
|
||||
/**
|
||||
* \fn inline unsigned short swapBytes16(unsigned short x)
|
||||
* \param x an short int
|
||||
* \return the same short with its bytes swapped
|
||||
*/
|
||||
__MIOSIX_INLINE unsigned short swapBytes16(unsigned short x);
|
||||
|
||||
/**
|
||||
* \fn inline unsigned int swapBytes32(unsigned int x)
|
||||
* \param x an int
|
||||
* \return the same int with its bytes swapped
|
||||
*/
|
||||
__MIOSIX_INLINE unsigned int swapBytes32(unsigned int x);
|
||||
|
||||
/**
|
||||
* \fn inline unsigned long long swapBytes64(unsigned long long x)
|
||||
* \param x a long long
|
||||
* \return the same long long with its bytes swapped
|
||||
*/
|
||||
__MIOSIX_INLINE unsigned long long swapBytes64(unsigned long long x);
|
||||
|
||||
#undef __MIOSIX_INLINE
|
||||
|
||||
/**
|
||||
* \def toLittleEndian16(x)
|
||||
* Convert a (signed or unsigned) short int from the system representation
|
||||
* to little endian
|
||||
* \param x value to convert
|
||||
* \return value converted to little endian
|
||||
*
|
||||
* \def toLittleEndian32(x)
|
||||
* Convert a (signed or unsigned) int from the system representation
|
||||
* to little endian
|
||||
* \param x value to convert
|
||||
* \return value converted to little endian
|
||||
*
|
||||
* \def toLittleEndian64(x)
|
||||
* Convert a (signed or unsigned) long long from the system representation
|
||||
* to little endian
|
||||
* \param x value to convert
|
||||
* \return value converted to little endian
|
||||
*
|
||||
* \def toBigEndian16(x)
|
||||
* Convert a (signed or unsigned) short int from the system representation
|
||||
* to big endian
|
||||
* \param x value to convert
|
||||
* \return value converted to big endian
|
||||
*
|
||||
* \def toBigEndian32(x)
|
||||
* Convert a (signed or unsigned) int from the system representation
|
||||
* to big endian
|
||||
* \param x value to convert
|
||||
* \return value converted to big endian
|
||||
*
|
||||
* \def toBigEndian64(x)
|
||||
* Convert a (signed or unsigned) long long from the system representation
|
||||
* to big endian
|
||||
* \param x value to convert
|
||||
* \return value converted to big endian
|
||||
*/
|
||||
|
||||
#ifdef MIOSIX_LITTLE_ENDIAN
|
||||
#define toLittleEndian16(x) (x)
|
||||
#define toLittleEndian32(x) (x)
|
||||
#define toLittleEndian64(x) (x)
|
||||
#define fromLittleEndian16(x) (x)
|
||||
#define fromLittleEndian32(x) (x)
|
||||
#define fromLittleEndian64(x) (x)
|
||||
#define toBigEndian16(x) swapBytes16(x)
|
||||
#define toBigEndian32(x) swapBytes32(x)
|
||||
#define toBigEndian64(x) swapBytes64(x)
|
||||
#define fromBigEndian16(x) swapBytes16(x)
|
||||
#define fromBigEndian32(x) swapBytes32(x)
|
||||
#define fromBigEndian64(x) swapBytes64(x)
|
||||
#elif defined(MIOSIX_BIG_ENDIAN)
|
||||
#define toLittleEndian16(x) swapBytes16(x)
|
||||
#define toLittleEndian32(x) swapBytes32(x)
|
||||
#define toLittleEndian64(x) swapBytes64(x)
|
||||
#define fromLittleEndian16(x) swapBytes16(x)
|
||||
#define fromLittleEndian32(x) swapBytes32(x)
|
||||
#define fromLittleEndian64(x) swapBytes64(x)
|
||||
#define toBigEndian16(x) (x)
|
||||
#define toBigEndian32(x) (x)
|
||||
#define toBigEndian64(x) (x)
|
||||
#define fromBigEndian16(x) (x)
|
||||
#define fromBigEndian32(x) (x)
|
||||
#define fromBigEndian64(x) (x)
|
||||
#else
|
||||
#error "endianness_impl.h does not define endianness"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
#endif //ENDIANNESS_H
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef GPIO_H
|
||||
#define GPIO_H
|
||||
|
||||
/**
|
||||
* \addtogroup Interfaces
|
||||
* \{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file gpio.h
|
||||
* The interface to gpios provided by Miosix is in the form of templates,
|
||||
* therefore this file can only include gpio_impl.h with the architecture
|
||||
* dependand code.
|
||||
*
|
||||
* The interface should be as follows:
|
||||
* First a class Mode containing an enum Mode_ needs to be defined. Its minimum
|
||||
* implementation is this:
|
||||
* \code
|
||||
* class Mode
|
||||
* {
|
||||
* public:
|
||||
* enum Mode_
|
||||
* {
|
||||
* INPUT,
|
||||
* OUTPUT
|
||||
* };
|
||||
* private:
|
||||
* Mode(); //Just a wrapper class, disallow creating instances
|
||||
* };
|
||||
* \endcode
|
||||
*
|
||||
* This class should define the possible configurations of a gpio pin.
|
||||
* The minimum required is INPUT and OUTPUT, but this can be extended to other
|
||||
* options to reflect the hardware capabilities of gpios. For example, if
|
||||
* gpios can be set as input with pull up, it is possible to add INPUT_PULL_UP
|
||||
* to the enum.
|
||||
*
|
||||
* Then a template Gpio class should be provided, with at least the following
|
||||
* member functions:
|
||||
* \code
|
||||
* template<unsigned int P, unsigned char N>
|
||||
* class Gpio
|
||||
* {
|
||||
* public:
|
||||
* static void mode(Mode::Mode_ m);
|
||||
* static void high();
|
||||
* static void low();
|
||||
* static int value();
|
||||
* unsigned int getPort() const;
|
||||
* unsigned char getNumber() const;
|
||||
* private:
|
||||
* Gpio();//Only static member functions, disallow creating instances
|
||||
* };
|
||||
* \endcode
|
||||
*
|
||||
* mode() should take a Mode::Mode_ enum and set the mode of the gpio
|
||||
* (input, output or other architecture specific)
|
||||
*
|
||||
* high() should set a gpio configured as output to high logic level
|
||||
*
|
||||
* low() should set a gpio configured as output to low logic level
|
||||
*
|
||||
* value() should return either 1 or 0 to refect the state of a gpio configured
|
||||
* as input
|
||||
*
|
||||
* getPort() should return the gpio port
|
||||
*
|
||||
* getNumber() should return the gpio pin number
|
||||
*
|
||||
* Lastly, a number of constants must be provided to be passed as first template
|
||||
* parameter of the Gpio class, usually identifying the gpio port, while the
|
||||
* second template parameter should be used to specify a gpio pin within a port.
|
||||
*
|
||||
* The intended use is this:
|
||||
* considering an architecture with two ports, PORTA and PORTB each with 8 pins.
|
||||
* The header gpio_impl.h should provide two constants, for example named
|
||||
* GPIOA_BASE and GPIOB_BASE.
|
||||
*
|
||||
* The user can declare the hardware mapping between gpios and what is connected
|
||||
* to them, usually in an header file. If for example PORTA.0 is connected to
|
||||
* a button while PORTB.4 to a led, the header file might contain:
|
||||
*
|
||||
* \code
|
||||
* typedef Gpio<GPIOA_BASE,0> button;
|
||||
* typedef Gpio<GPIOB_BASE,4> led;
|
||||
* \endcode
|
||||
*
|
||||
* This allows the rest of the code to be written in terms of leds and buttons,
|
||||
* without a reference to which pin they are connected to, something that might
|
||||
* change.
|
||||
*
|
||||
* A simple code using these gpios could be:
|
||||
* \code
|
||||
* void blinkUntilButtonPressed()
|
||||
* {
|
||||
* led::mode(Mode::OUTPUT);
|
||||
* button::mode(Mode::INPUT);
|
||||
* for(;;)
|
||||
* {
|
||||
* if(button::value()==1) break;
|
||||
* led::high();
|
||||
* Thread::sleep(250);
|
||||
* led::low();
|
||||
* Thread::sleep(250);
|
||||
* }
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
#include "interfaces-impl/gpio_impl.h"
|
||||
|
||||
#endif //GPIO_H
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010, 2011, 2012 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef PORTABILITY_H
|
||||
#define PORTABILITY_H
|
||||
|
||||
//For SCHED_TYPE_* config options
|
||||
#include "config/miosix_settings.h"
|
||||
#include <cstddef>
|
||||
|
||||
/**
|
||||
* \addtogroup Interfaces
|
||||
* \{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file portability.h
|
||||
* This file is the interface from the Miosix kernel to the hardware.
|
||||
* It ccontains what is required to perform a context switch, disable
|
||||
* interrupts, set up the stack frame and registers of a newly created thread,
|
||||
* and contains iterrupt handlers for preemption and yield.
|
||||
*
|
||||
* Since some of the functions in this file must be inline for speed reasons,
|
||||
* and context switch code must be macros, at the end of this file the file
|
||||
* portability_impl.h is included.
|
||||
* This file should contain the implementation of those inline functions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
/**
|
||||
* \namespace miosix_pivate
|
||||
* contains architecture-specific functions. These functions are separated from
|
||||
* the functions in kernel.h because:<br>
|
||||
* - to port the kernel to another processor you only need to rewrite these
|
||||
* functions.
|
||||
* - these functions are only useful for writing hardare drivers, most user code
|
||||
* does not need them.
|
||||
*/
|
||||
namespace miosix_private {
|
||||
|
||||
/**
|
||||
* \addtogroup Interfaces
|
||||
* \{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Used after an unrecoverable error condition to restart the system, even from
|
||||
* within an interrupt routine.
|
||||
*/
|
||||
void IRQsystemReboot();
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Cause a context switch.
|
||||
* It is used by the kernel, and should not be used by end users.
|
||||
*/
|
||||
inline void doYield();
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Initializes a ctxsave array when a thread is created.
|
||||
* It is used by the kernel, and should not be used by end users.
|
||||
* \param ctxsave a pointer to a field ctxsave inside a Thread class that need
|
||||
* to be filled
|
||||
* \param pc starting program counter of newly created thread, used to
|
||||
* initialize ctxsave
|
||||
* \param sp starting stack pointer of newly created thread, used to initialize
|
||||
* ctxsave
|
||||
* \param argv starting data passed to newly created thread, used to initialize
|
||||
* ctxsave
|
||||
*/
|
||||
void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp,
|
||||
void *argv);
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Used before every context switch to check if the stack of the thread has
|
||||
* overflowed must be called before IRQfindNextThread().
|
||||
*/
|
||||
void IRQstackOverflowCheck();
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Called by miosix::start_kernel to handle the architecture-specific part of
|
||||
* initialization. It is used by the kernel, and should not be used by end users
|
||||
*/
|
||||
void IRQportableStartKernel();
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* This function disables interrupts.
|
||||
* This is used by the kernel to implement disableInterrupts() and
|
||||
* enableInterrupts(). You should never need to call these functions directly.
|
||||
*/
|
||||
inline void doDisableInterrupts();
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* This function enables interrupts.
|
||||
* This is used by the kernel to implement disableInterrupts() and
|
||||
* enableInterrupts(). You should never need to call these functions directly.
|
||||
*/
|
||||
inline void doEnableInterrupts();
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* This is used by the kernel to implement areInterruptsEnabled()
|
||||
* You should never need to call this function directly.
|
||||
* \return true if interrupts are enabled
|
||||
*/
|
||||
inline bool checkAreInterruptsEnabled();
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* used by the idle thread to put cpu in low power mode
|
||||
*/
|
||||
void sleepCpu();
|
||||
|
||||
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||
/**
|
||||
* Allow access to a second timer to allow variable burst preemption together
|
||||
* with fixed tick timekeeping.
|
||||
*/
|
||||
class AuxiliaryTimer
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Initializes the auxiliary timer.
|
||||
*/
|
||||
static void IRQinit();
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Used to implement new control-based scheduler. Used to get the error
|
||||
* between desired burst time and real burst time.
|
||||
* It is responsibility of who implements this function to ensure that
|
||||
* the returned value has the three most significant bits set to zero
|
||||
* (that is, is a positive number between 0 and 0x1fffffff). This is
|
||||
* necessary to avoid oerflow in the scheduler implementation.
|
||||
* \return time since last time set_timer_value() was called.
|
||||
*/
|
||||
static int IRQgetValue();
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* Reset timer counter to zero and set next preempt after x timer counts.
|
||||
* \param x time before next preempt will occur. Must be >0
|
||||
*/
|
||||
static void IRQsetValue(int x);
|
||||
|
||||
private:
|
||||
//Unwanted functions
|
||||
AuxiliaryTimer();
|
||||
AuxiliaryTimer& operator= (AuxiliaryTimer& );
|
||||
};
|
||||
#endif //SCHED_TYPE_CONTROL_BASED
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
} //namespace miosix_private
|
||||
|
||||
// This contains the macros and the implementation of inline functions
|
||||
#include "portability_impl.h"
|
||||
|
||||
#endif //PORTABILITY_H
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2016 by Lorenzo Pinosa *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include "IRQDisplayPrint.h"
|
||||
#include "string"
|
||||
#include "display.h"
|
||||
#include <memory>
|
||||
|
||||
using namespace std;
|
||||
using namespace mxgui;
|
||||
|
||||
namespace miosix {
|
||||
|
||||
IRQDisplayPrint::IRQDisplayPrint()
|
||||
: Device(TTY), right_margin(5), bottom_margin(5),
|
||||
carriage_return_enabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
IRQDisplayPrint::~IRQDisplayPrint() {}
|
||||
|
||||
void IRQDisplayPrint::IRQwrite(const char * to_print)
|
||||
{
|
||||
string str = to_print;
|
||||
input_queue.put(str);
|
||||
}
|
||||
|
||||
ssize_t IRQDisplayPrint::writeBlock(const void * buffer, size_t size, off_t where)
|
||||
{
|
||||
string str = reinterpret_cast<const char*>(buffer);
|
||||
str = str.substr(where, size);
|
||||
input_queue.put(str);
|
||||
return size;
|
||||
}
|
||||
|
||||
void IRQDisplayPrint::printIRQ()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
string tmp;
|
||||
input_queue.get(tmp);
|
||||
if (tmp.size() == 0)
|
||||
continue;
|
||||
if (carriage_return_fix(tmp))
|
||||
continue;
|
||||
process_string(tmp);
|
||||
check_array_overflow();
|
||||
internal_print();
|
||||
}
|
||||
}
|
||||
|
||||
//returns true if the current string has to be discarded
|
||||
bool IRQDisplayPrint::carriage_return_fix(string str)
|
||||
{
|
||||
if (!carriage_return_enabled && str.compare("\r\n") == 0)
|
||||
{
|
||||
carriage_return_enabled = true;
|
||||
return true;
|
||||
}
|
||||
if (str.compare("\r\n") == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
carriage_return_enabled = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void IRQDisplayPrint::check_array_overflow()
|
||||
{
|
||||
int font_height, display_h;
|
||||
{
|
||||
DrawingContext dc(Display::instance());
|
||||
font_height = dc.getFont().getHeight();
|
||||
display_h = dc.getHeight();
|
||||
}
|
||||
while (font_height * print_lines.size() >= display_h - bottom_margin)
|
||||
{
|
||||
print_lines.erase(print_lines.begin());
|
||||
}
|
||||
}
|
||||
|
||||
void IRQDisplayPrint::process_string(string str)
|
||||
{
|
||||
int display_w;
|
||||
auto_ptr<Font> font;
|
||||
{
|
||||
DrawingContext dc(Display::instance());
|
||||
display_w = dc.getWidth();
|
||||
font = auto_ptr<Font>(new Font(dc.getFont()));
|
||||
}
|
||||
|
||||
vector<string> lines;
|
||||
while (str.length() > 0)
|
||||
{
|
||||
int lenght = font->calculateLength(str.c_str());
|
||||
if (lenght < display_w)
|
||||
{
|
||||
lines.push_back(str);
|
||||
break;
|
||||
}
|
||||
//else
|
||||
int len = 0;
|
||||
string str_part;
|
||||
for (int i = 1; len < display_w - right_margin; i++)
|
||||
{
|
||||
str_part = str.substr(0, i);
|
||||
len = font->calculateLength(str_part.c_str());
|
||||
}
|
||||
lines.push_back(str_part);
|
||||
str = str.substr(str_part.length());
|
||||
}
|
||||
|
||||
|
||||
for (vector<string>::iterator it = lines.begin(); it != lines.end(); ++it)
|
||||
{
|
||||
print_lines.push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
void IRQDisplayPrint::internal_print()
|
||||
{
|
||||
DrawingContext dc(Display::instance());
|
||||
//dc.clear(0xFFF);
|
||||
int cur_y = 0;
|
||||
|
||||
for (vector<string>::iterator it = print_lines.begin(); it != print_lines.end(); ++it)
|
||||
{
|
||||
string str = *it;
|
||||
dc.write(Point(0, cur_y), str.c_str());
|
||||
Point up_left(dc.getFont().calculateLength(str.c_str()), cur_y);
|
||||
Point down_right(dc.getWidth() -1, cur_y + dc.getFont().getHeight());
|
||||
dc.clear(up_left, down_right, 0x0);
|
||||
cur_y += dc.getFont().getHeight();
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2016 by Lorenzo Pinosa *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include "../filesystem/devfs/devfs.h"
|
||||
#include "queue.h"
|
||||
#include <vector>
|
||||
#include "kernel.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace miosix {
|
||||
|
||||
class IRQDisplayPrint : public Device
|
||||
{
|
||||
public:
|
||||
IRQDisplayPrint();
|
||||
~IRQDisplayPrint();
|
||||
|
||||
void IRQwrite(const char *str);
|
||||
ssize_t writeBlock(const void *buffer, size_t size, off_t where);
|
||||
|
||||
void printIRQ();
|
||||
private:
|
||||
Queue<string, 20> input_queue;
|
||||
vector<string> print_lines;
|
||||
|
||||
int right_margin;
|
||||
int bottom_margin;
|
||||
bool carriage_return_enabled;
|
||||
|
||||
bool carriage_return_fix(string str);
|
||||
void process_string(string str);
|
||||
void check_array_overflow();
|
||||
void internal_print();
|
||||
};
|
||||
|
||||
} //namespace miosix
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
|
||||
#include "SystemMap.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#ifdef WITH_PROCESSES
|
||||
|
||||
namespace miosix {
|
||||
|
||||
SystemMap& SystemMap::instance()
|
||||
{
|
||||
static SystemMap singleton;
|
||||
return singleton;
|
||||
}
|
||||
|
||||
void SystemMap::addElfProgram(const char* name, const unsigned int *elf, unsigned int size)
|
||||
{
|
||||
string sName(name);
|
||||
if(mPrograms.find(sName) == mPrograms.end())
|
||||
mPrograms.insert(make_pair(sName, make_pair(elf, size)));
|
||||
}
|
||||
|
||||
void SystemMap::removeElfProgram(const char* name)
|
||||
{
|
||||
string sName(name);
|
||||
ProgramsMap::iterator it = mPrograms.find(sName);
|
||||
|
||||
if(it != mPrograms.end())
|
||||
mPrograms.erase(it);
|
||||
}
|
||||
|
||||
pair<const unsigned int*, unsigned int> SystemMap::getElfProgram(const char* name) const
|
||||
{
|
||||
ProgramsMap::const_iterator it = mPrograms.find(string(name));
|
||||
|
||||
if(it == mPrograms.end())
|
||||
return make_pair<const unsigned int*, unsigned int>(0, 0);
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
unsigned int SystemMap::getElfCount() const
|
||||
{
|
||||
return mPrograms.size();
|
||||
}
|
||||
|
||||
} //namespace miosix
|
||||
|
||||
#endif //WITH_PROCESSES
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue