Compiling miosix kernel from sources instead of linking against a pre-build image

This commit is contained in:
Silvano Seva 2022-08-25 09:08:30 +02:00
parent 1b8106d607
commit b861beb0e6
159 changed files with 38954 additions and 17 deletions

View File

@ -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)

View File

@ -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' : ''}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();
};
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

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

View File

@ -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

View File

@ -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

View File

@ -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

View File

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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

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

View File

@ -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

View File

@ -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

View File

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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

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

View File

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

View File

@ -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

View File

@ -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;
}
}
*/

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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