Compiling miosix kernel from sources instead of linking against a pre-build image
This commit is contained in:
parent
1b8106d607
commit
b861beb0e6
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
This repository contains an adapted version of the miosix kernel, tailored for the usage with the OpenRTX targets equipped with an ARM cortex M4 microcontroller.
|
||||||
|
|
||||||
|
The original repository can be found here: [https://github.com/fedetft/miosix-kernel](https://github.com/fedetft/miosix-kernel)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
miosix_inc = ['lib/miosix-kernel/miosix',
|
||||||
|
'lib/miosix-kernel/miosix/config',
|
||||||
|
'lib/miosix-kernel/miosix/arch/common',
|
||||||
|
'lib/miosix-kernel/miosix/util']
|
||||||
|
|
||||||
|
miosix_src = ['lib/miosix-kernel/miosix/kernel/scheduler/priority/priority_scheduler.cpp',
|
||||||
|
'lib/miosix-kernel/miosix/kernel/error.cpp',
|
||||||
|
'lib/miosix-kernel/miosix/kernel/kernel.cpp',
|
||||||
|
'lib/miosix-kernel/miosix/kernel/pthread.cpp',
|
||||||
|
'lib/miosix-kernel/miosix/kernel/stage_2_boot.cpp',
|
||||||
|
'lib/miosix-kernel/miosix/kernel/sync.cpp',
|
||||||
|
'lib/miosix-kernel/miosix/kernel/timeconversion.cpp',
|
||||||
|
'lib/miosix-kernel/miosix/util/util.cpp',
|
||||||
|
'lib/miosix-kernel/miosix/stdlib_integration/libc_integration.cpp',
|
||||||
|
'lib/miosix-kernel/miosix/stdlib_integration/libstdcpp_integration.cpp']
|
||||||
|
|
||||||
|
miosix_def = {'DONT_USE_CMSIS_INIT' : '',
|
||||||
|
'COMPILING_MIOSIX' : '',
|
||||||
|
'_POSIX_PRIORITY_SCHEDULING': ''}
|
||||||
|
|
||||||
|
##
|
||||||
|
## ARM Cortex M4
|
||||||
|
##
|
||||||
|
miosix_cm4f_inc = miosix_inc + ['lib/miosix-kernel/miosix/arch/cortexM4F']
|
||||||
|
miosix_cm4f_src = miosix_src + ['lib/miosix-kernel/miosix/arch/cortexM4F/portability.cpp',
|
||||||
|
'lib/miosix-kernel/miosix/arch/common/core/interrupts_cortexMx.cpp']
|
||||||
|
miosix_cm4f_def = miosix_def + {'_ARCH_CORTEXM4' : ''}
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013, 2014, 2015 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef ATOMIC_OPS_IMPL_H
|
||||||
|
#define ATOMIC_OPS_IMPL_H
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
inline int atomicSwap(volatile int *p, int v)
|
||||||
|
{
|
||||||
|
//This is the only atomic operation in the ARM7 assembler
|
||||||
|
register int result;
|
||||||
|
asm volatile("swp %0, %1, [%2]" : "=&r"(result) : "r"(v),"r"(p) : "memory");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void atomicAdd(volatile int *p, int incr)
|
||||||
|
{
|
||||||
|
register int a,b; //Temporaries used by ASM code
|
||||||
|
asm volatile(" mrs %0, cpsr \n"
|
||||||
|
" tst %0, #0x80 \n"
|
||||||
|
" orreq %1, %0, #0x80 \n"
|
||||||
|
" msreq cpsr_c, %1 \n"
|
||||||
|
" ldr %1, [%3] \n"
|
||||||
|
" add %1, %1, %2 \n"
|
||||||
|
" str %1, [%3] \n"
|
||||||
|
" tst %0, #0x80 \n"
|
||||||
|
" msreq cpsr_c, %0 \n"
|
||||||
|
: "=&r"(a),"=&r"(b)
|
||||||
|
: "r"(incr),"r"(p)
|
||||||
|
: "cc","memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int atomicAddExchange(volatile int *p, int incr)
|
||||||
|
{
|
||||||
|
register int a; //Temporaries used by ASM code
|
||||||
|
register int result;
|
||||||
|
asm volatile(" mrs %0, cpsr \n"
|
||||||
|
" tst %0, #0x80 \n"
|
||||||
|
" orreq %1, %0, #0x80 \n"
|
||||||
|
" msreq cpsr_c, %1 \n"
|
||||||
|
" ldr %1, [%3] \n"
|
||||||
|
" add %2, %2, %1 \n"
|
||||||
|
" str %2, [%3] \n"
|
||||||
|
" tst %0, #0x80 \n"
|
||||||
|
" msreq cpsr_c, %0 \n"
|
||||||
|
: "=&r"(a),"=&r"(result),"+&r"(incr)//Incr is read and clobbered
|
||||||
|
: "r"(p)
|
||||||
|
: "cc","memory");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int atomicCompareAndSwap(volatile int *p, int prev, int next)
|
||||||
|
{
|
||||||
|
register int a; //Temporaries used by ASM code
|
||||||
|
register int result;
|
||||||
|
asm volatile(" mrs %0, cpsr \n"
|
||||||
|
" tst %0, #0x80 \n"
|
||||||
|
" orreq %1, %0, #0x80 \n"
|
||||||
|
" msreq cpsr_c, %1 \n"
|
||||||
|
" ldr %1, [%2] \n"
|
||||||
|
" cmp %1, %3 \n"
|
||||||
|
" streq %4, [%2] \n"
|
||||||
|
" tst %0, #0x80 \n"
|
||||||
|
" msreq cpsr_c, %0 \n"
|
||||||
|
: "=&r"(a),"=&r"(result)
|
||||||
|
: "r"(p),"r"(prev),"r"(next)
|
||||||
|
: "cc","memory");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void *atomicFetchAndIncrement(void * const volatile * p, int offset,
|
||||||
|
int incr)
|
||||||
|
{
|
||||||
|
register int a,b; //Temporaries used by ASM code
|
||||||
|
register void *result;
|
||||||
|
asm volatile(" mrs %0, cpsr \n"
|
||||||
|
" tst %0, #0x80 \n"
|
||||||
|
" orreq %1, %0, #0x80 \n"
|
||||||
|
" msreq cpsr_c, %1 \n"
|
||||||
|
" ldr %2, [%3] \n"
|
||||||
|
" ldr %1, [%2, %4, asl #2] \n"
|
||||||
|
" add %1, %1, %5 \n"
|
||||||
|
" str %1, [%2, %4, asl #2] \n"
|
||||||
|
" tst %0, #0x80 \n"
|
||||||
|
" msreq cpsr_c, %0 \n"
|
||||||
|
: "=&r"(a),"=&r"(b),"=&r"(result)
|
||||||
|
: "r"(p),"r"(offset),"r"(incr)
|
||||||
|
: "cc","memory");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //ATOMIC_OPS_IMPL_H
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013, 2014, 2015 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef ATOMIC_OPS_IMPL_M0_H
|
||||||
|
#define ATOMIC_OPS_IMPL_M0_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cortex M0/M0+ architectures does not support __LDREXW, __STREXW and __CLREX
|
||||||
|
* instructions, so we have to redefine the atomic operations using functions
|
||||||
|
* that disable the interrupts.
|
||||||
|
*
|
||||||
|
* TODO: actually this implementation is not very efficient
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "interfaces/arch_registers.h"
|
||||||
|
#include <kernel/kernel.h>
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
|
||||||
|
inline int atomicSwap(volatile int *p, int v)
|
||||||
|
{
|
||||||
|
InterruptDisableLock dLock;
|
||||||
|
|
||||||
|
int result = *p;
|
||||||
|
*p = v;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void atomicAdd(volatile int *p, int incr)
|
||||||
|
{
|
||||||
|
InterruptDisableLock dLock;
|
||||||
|
|
||||||
|
*p += incr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int atomicAddExchange(volatile int *p, int incr)
|
||||||
|
{
|
||||||
|
InterruptDisableLock dLock;
|
||||||
|
|
||||||
|
int result = *p;
|
||||||
|
*p += incr;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int atomicCompareAndSwap(volatile int *p, int prev, int next)
|
||||||
|
{
|
||||||
|
InterruptDisableLock dLock;
|
||||||
|
|
||||||
|
int result = *p;
|
||||||
|
if(*p == prev) *p = next;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void *atomicFetchAndIncrement(void * const volatile * p, int offset,
|
||||||
|
int incr)
|
||||||
|
{
|
||||||
|
InterruptDisableLock dLock;
|
||||||
|
|
||||||
|
volatile uint32_t *pt;
|
||||||
|
|
||||||
|
void *result = *p;
|
||||||
|
if(result == 0) return 0;
|
||||||
|
pt = reinterpret_cast<uint32_t*>(result) + offset;
|
||||||
|
*pt += incr;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //ATOMIC_OPS_IMPL_M0_H
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013, 2014, 2015 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef ATOMIC_OPS_IMPL_H
|
||||||
|
#define ATOMIC_OPS_IMPL_H
|
||||||
|
|
||||||
|
#include "interfaces/arch_registers.h"
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
inline int atomicSwap(volatile int *p, int v)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
volatile uint32_t *ptr=reinterpret_cast<volatile uint32_t*>(p);
|
||||||
|
do {
|
||||||
|
result=__LDREXW(ptr);
|
||||||
|
} while(__STREXW(v,ptr));
|
||||||
|
asm volatile("":::"memory");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void atomicAdd(volatile int *p, int incr)
|
||||||
|
{
|
||||||
|
int value;
|
||||||
|
volatile uint32_t *ptr=reinterpret_cast<volatile uint32_t*>(p);
|
||||||
|
do {
|
||||||
|
value=__LDREXW(ptr);
|
||||||
|
} while(__STREXW(value+incr,ptr));
|
||||||
|
asm volatile("":::"memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int atomicAddExchange(volatile int *p, int incr)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
volatile uint32_t *ptr=reinterpret_cast<volatile uint32_t*>(p);
|
||||||
|
do {
|
||||||
|
result=__LDREXW(ptr);
|
||||||
|
} while(__STREXW(result+incr,ptr));
|
||||||
|
asm volatile("":::"memory");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int atomicCompareAndSwap(volatile int *p, int prev, int next)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
volatile uint32_t *ptr=reinterpret_cast<volatile uint32_t*>(p);
|
||||||
|
do {
|
||||||
|
result=__LDREXW(ptr);
|
||||||
|
if(result!=prev)
|
||||||
|
{
|
||||||
|
__CLREX();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} while(__STREXW(next,ptr));
|
||||||
|
asm volatile("":::"memory");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void *atomicFetchAndIncrement(void * const volatile * p, int offset,
|
||||||
|
int incr)
|
||||||
|
{
|
||||||
|
void *result;
|
||||||
|
volatile uint32_t *rcp;
|
||||||
|
int rc;
|
||||||
|
do {
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
result=*p;
|
||||||
|
if(result==0) return 0;
|
||||||
|
rcp=reinterpret_cast<uint32_t*>(result)+offset;
|
||||||
|
rc=__LDREXW(rcp);
|
||||||
|
asm volatile("":::"memory");
|
||||||
|
if(result==*p) break;
|
||||||
|
__CLREX();
|
||||||
|
}
|
||||||
|
} while(__STREXW(rc+incr,rcp));
|
||||||
|
asm volatile("":::"memory");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //ATOMIC_OPS_IMPL_H
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2018 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "cache_cortexMx.h"
|
||||||
|
#include "mpu_cortexMx.h"
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
static const unsigned int cacheLine=32; //Cortex-M7 cache line size
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Using the MPU, configure a region of the memory space as
|
||||||
|
* - write-through cacheable
|
||||||
|
* - non-shareable
|
||||||
|
* - readable/writable/executable only by privileged code (for compatibility
|
||||||
|
* with the way processes use the MPU)
|
||||||
|
* \param region MPU region. Note that region 6 and 7 are used by processes, and
|
||||||
|
* should be avoided here
|
||||||
|
* \param base base address, aligned to a 32Byte cache line
|
||||||
|
* \param size size, must be at least 32 and a power of 2, or it is rounded to
|
||||||
|
* the next power of 2
|
||||||
|
*/
|
||||||
|
static void IRQconfigureCacheability(unsigned int region, unsigned int base,
|
||||||
|
unsigned int size)
|
||||||
|
{
|
||||||
|
// NOTE: The ARM documentation is unclear about the effect of the shareable
|
||||||
|
// bit on a single core architecture. Experimental evidence on an STM32F476
|
||||||
|
// shows that setting it in IRQconfigureCache for the internal RAM region
|
||||||
|
// causes the boot to fail.
|
||||||
|
// For this reason, all regions are marked as not shareable
|
||||||
|
MPU->RBAR=(base & (~(cacheLine-1))) | MPU_RBAR_VALID_Msk | region;
|
||||||
|
MPU->RASR=1<<MPU_RASR_AP_Pos //Privileged: RW, unprivileged: no access
|
||||||
|
| MPU_RASR_C_Msk //Cacheable, write through
|
||||||
|
| 1 //Enable bit
|
||||||
|
| sizeToMpu(size)<<1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRQconfigureCache(const unsigned int *xramBase, unsigned int xramSize)
|
||||||
|
{
|
||||||
|
IRQconfigureCacheability(0,0x00000000,0x20000000);
|
||||||
|
IRQconfigureCacheability(1,0x20000000,0x20000000);
|
||||||
|
if(xramSize)
|
||||||
|
IRQconfigureCacheability(2,reinterpret_cast<unsigned int>(xramBase),xramSize);
|
||||||
|
IRQenableMPUatBoot();
|
||||||
|
|
||||||
|
SCB_EnableICache();
|
||||||
|
SCB_EnableDCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT==1)
|
||||||
|
/**
|
||||||
|
* Align a generic buffer to another one that contains the first one, but the
|
||||||
|
* start size is aligned to a cache line
|
||||||
|
*/
|
||||||
|
static pair<uint32_t*, int32_t> alignBuffer(void *buffer, int size)
|
||||||
|
{
|
||||||
|
auto bufferAddr=reinterpret_cast<unsigned int>(buffer);
|
||||||
|
|
||||||
|
auto base=bufferAddr & (~(cacheLine-1));
|
||||||
|
size+=bufferAddr-base;
|
||||||
|
|
||||||
|
return make_pair(reinterpret_cast<uint32_t*>(base),size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void markBufferAfterDmaRead(void *buffer, int size)
|
||||||
|
{
|
||||||
|
//Since the current cache policy is write-through, we just invalidate the
|
||||||
|
//cache lines corresponding to the buffer. No need to flush (clean) the cache.
|
||||||
|
auto result=alignBuffer(buffer,size);
|
||||||
|
SCB_InvalidateDCache_by_Addr(result.first,result.second);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2018 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef CACHE_CORTEX_MX_H
|
||||||
|
#define CACHE_CORTEX_MX_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* README: Essentials about how cache support is implemented.
|
||||||
|
*
|
||||||
|
* Caches in the Cortex M7 are transparent to software, except when using
|
||||||
|
* the DMA. As the DMA reads and writes directly to memory, explicit management
|
||||||
|
* is required. The default cache policy is write-back, but this has been deemed
|
||||||
|
* unsuitable for Miosix, so for the time being only write-through is supported.
|
||||||
|
*
|
||||||
|
* The IRQconfigureCache() configures the cache as write-through and enables it.
|
||||||
|
* It should be called early at boot, in stage_1_boot.cpp
|
||||||
|
*
|
||||||
|
* When writing DMA drivers, before passing a buffer to the DMA for it to be
|
||||||
|
* written to a peripheral, call markBufferBeforeDmaWrite().
|
||||||
|
* After a DMA read from a peripheral to a memory buffer has completed,
|
||||||
|
* call markBufferAfterDmaRead(). These take care of keeping the DMA operations
|
||||||
|
* in sync with the cache. These become no-ops for other architectures, so you
|
||||||
|
* can freely put the in any driver.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some more info about caches. Why not supporting write-back?
|
||||||
|
* When writing data from memory to a peripheral using DMA, things are easy
|
||||||
|
* also with write-back. You just flush (clean) the relevant cache lines, and
|
||||||
|
* the DMA has access to the correct values. So it looks like it's ok.
|
||||||
|
* When instead the DMA has to write to a memory location things become
|
||||||
|
* complicated. Assume that a buffer not aligned to a cache line is passed to
|
||||||
|
* a DMA read routine. After that a context switch happens and another thread
|
||||||
|
* writes to a memory location that is on the same cache line as the (beginning
|
||||||
|
* or end of) the buffer passed to the DMA. At the same time the DMA is writing
|
||||||
|
* to the buffer.
|
||||||
|
* At the end the situation looks like this, where the thread has written to
|
||||||
|
* location X in the cache, while the DMA has written Y to the buffer.
|
||||||
|
* <-- outside buffer --x-- buffer -->
|
||||||
|
* +----------------------------------+
|
||||||
|
* | X | | cache
|
||||||
|
* +----------------------------------+
|
||||||
|
* | |YYYYYYYYYYYYY| memory
|
||||||
|
* +----------------------------------+
|
||||||
|
* What are you suppose to do? If you flush (clean) the cache line, X will be
|
||||||
|
* committed to memory, but the Y data written by the DMA will be lost. If you
|
||||||
|
* invalidate the cache, Y is preserved, but X is lost.
|
||||||
|
* If you're just thinking that the problem can be solved by making sure buffers
|
||||||
|
* are aligned to the cache line (and their size is a multiple of the cache
|
||||||
|
* line), well, there's a problem.
|
||||||
|
* Miosix is a real-time OS, and for performance and safety, most drivers are
|
||||||
|
* zero copy. Applications routinely pass to DMA drivers such as the SDIO large
|
||||||
|
* buffers (think 100+KB). Of course allocating an aligned buffer inside the
|
||||||
|
* DMA driver as large as the user-passed buffer and copying the data there
|
||||||
|
* isn't just slow, it wouldn't be safe, as you risk to exceed the free heap
|
||||||
|
* memory or fragment the heap. Allocating a small buffer and splitting the
|
||||||
|
* large DMA transfer in many small ones where the user passed buffer is copyied
|
||||||
|
* one chunk at a time would be feasible, but even slower, and even more so
|
||||||
|
* considering that some peripherals such as SD cards are optimized for large
|
||||||
|
* sequential writes, not for small chunks.
|
||||||
|
* But what if we make sure all buffers passed to DMA drivers are aligned?
|
||||||
|
* That is a joke, as it the burden of doing so is unmaintainable. Buffers
|
||||||
|
* passed to DMA memory are everywhere, in the C/C++ standard library
|
||||||
|
* (think the buffer used for IO formatting inside printf/fprintf), and
|
||||||
|
* everywherein application code. Something like
|
||||||
|
* char s[128]="Hello world";
|
||||||
|
* puts(s);
|
||||||
|
* may cause s to be passed to a DMA driver. We would spend our lives chasing
|
||||||
|
* unaligned buffers, and the risk of this causing difficult to reproduce memory
|
||||||
|
* corruptions is too high. For this reason, for the time being, Miosix only
|
||||||
|
* supports write-through caching on the Cortex-M7.
|
||||||
|
*
|
||||||
|
* A note about performance. Using the testsuite benchmark, when caches are
|
||||||
|
* disabled the STM32F746 @ 216MHz is less than half the speed of the
|
||||||
|
* STM32F407 @ 168MHz. By enabling the ICACHE things get better, but it is
|
||||||
|
* still slower, and achieves a speedup of 1.53 only when both ICACHE and
|
||||||
|
* DCACHE are enabled. The speedup also includes the gains due to the faster
|
||||||
|
* clock frequency. So if you want speed you have to use caches.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "interfaces/arch_registers.h"
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To be called in stage_1_boot.cpp to configure caches.
|
||||||
|
* Only call this function if the board has caches.
|
||||||
|
* \param xramBase base address of external memory, if present, otherwise nullptr
|
||||||
|
* \param xramSize size of external memory, if present, otherwise 0
|
||||||
|
*/
|
||||||
|
void IRQconfigureCache(const unsigned int *xramBase=nullptr, unsigned int xramSize=0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this function to mark a buffer before starting a DMA transfer where
|
||||||
|
* the DMA will read the buffer.
|
||||||
|
* \param buffer buffer
|
||||||
|
* \param size buffer size
|
||||||
|
*/
|
||||||
|
inline void markBufferBeforeDmaWrite(const void *buffer, int size)
|
||||||
|
{
|
||||||
|
#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT==1)
|
||||||
|
// You may think that since the cache is configured as write-through,
|
||||||
|
// there's nothing to do before the DMA can read a memory buffer just
|
||||||
|
// written by the CPU, right? Wrong! Other than the cache, there's the
|
||||||
|
// write buffer to worry about. My hypothesis is that once a memory region
|
||||||
|
// is marked as cacheable, the write buffer becomes more lax in
|
||||||
|
// automatically flushing as soon as possible. In the stm32 serial port
|
||||||
|
// driver writing just a few characters causes garbage to be printed if
|
||||||
|
// this __DSB() is removed. Apparently, the characters remian in the write
|
||||||
|
// buffer.
|
||||||
|
__DSB();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this function after having completed a DMA transfer where the DMA has
|
||||||
|
* written to the buffer.
|
||||||
|
* \param buffer buffer
|
||||||
|
* \param size buffer size
|
||||||
|
*/
|
||||||
|
#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT==1)
|
||||||
|
void markBufferAfterDmaRead(void *buffer, int size);
|
||||||
|
#else
|
||||||
|
inline void markBufferAfterDmaRead(void *buffer, int size) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //CACHE_CORTEX_MX_H
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2011 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef ENDIANNESS_IMPL_H
|
||||||
|
#define ENDIANNESS_IMPL_H
|
||||||
|
|
||||||
|
#ifndef MIOSIX_BIG_ENDIAN
|
||||||
|
//This target is little endian
|
||||||
|
#define MIOSIX_LITTLE_ENDIAN
|
||||||
|
#endif //MIOSIX_BIG_ENDIAN
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define __MIOSIX_INLINE inline
|
||||||
|
#else //__cplusplus
|
||||||
|
#define __MIOSIX_INLINE static inline
|
||||||
|
#endif //__cplusplus
|
||||||
|
|
||||||
|
|
||||||
|
__MIOSIX_INLINE unsigned short swapBytes16(unsigned short x)
|
||||||
|
{
|
||||||
|
return (x>>8) | (x<<8);
|
||||||
|
}
|
||||||
|
|
||||||
|
__MIOSIX_INLINE unsigned int swapBytes32(unsigned int x)
|
||||||
|
{
|
||||||
|
#ifdef __GNUC__
|
||||||
|
return __builtin_bswap32(x);
|
||||||
|
#else //__GNUC__
|
||||||
|
return ( x>>24) |
|
||||||
|
((x<< 8) & 0x00ff0000) |
|
||||||
|
((x>> 8) & 0x0000ff00) |
|
||||||
|
( x<<24);
|
||||||
|
#endif //__GNUC__
|
||||||
|
}
|
||||||
|
|
||||||
|
__MIOSIX_INLINE unsigned long long swapBytes64(unsigned long long x)
|
||||||
|
{
|
||||||
|
#ifdef __GNUC__
|
||||||
|
return __builtin_bswap64(x);
|
||||||
|
#else //__GNUC__
|
||||||
|
return ( x>>56) |
|
||||||
|
((x<<40) & 0x00ff000000000000ull) |
|
||||||
|
((x<<24) & 0x0000ff0000000000ull) |
|
||||||
|
((x<< 8) & 0x000000ff00000000ull) |
|
||||||
|
((x>> 8) & 0x00000000ff000000ull) |
|
||||||
|
((x>>24) & 0x0000000000ff0000ull) |
|
||||||
|
((x>>40) & 0x000000000000ff00ull) |
|
||||||
|
( x<<56);
|
||||||
|
#endif //__GNUC__
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef __MIOSIX_INLINE
|
||||||
|
|
||||||
|
#endif //ENDIANNESS_IMPL_H
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2011, 2012, 2013, 2014, 2015 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef ENDIANNESS_IMPL_H
|
||||||
|
#define ENDIANNESS_IMPL_H
|
||||||
|
|
||||||
|
#ifndef MIOSIX_BIG_ENDIAN
|
||||||
|
//This target is little endian
|
||||||
|
#define MIOSIX_LITTLE_ENDIAN
|
||||||
|
#endif //MIOSIX_BIG_ENDIAN
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define __MIOSIX_INLINE inline
|
||||||
|
#else //__cplusplus
|
||||||
|
#define __MIOSIX_INLINE static inline
|
||||||
|
#endif //__cplusplus
|
||||||
|
|
||||||
|
__MIOSIX_INLINE unsigned short swapBytes16(unsigned short x)
|
||||||
|
{
|
||||||
|
//It's kind of a shame that GCC can't automatically make use of
|
||||||
|
//instructions like rev and rev16 to do byte swapping.
|
||||||
|
//Moreover, while for 32 and 64 bit integers it has builtins, for 16 bit
|
||||||
|
//we're forced to use inline asm.
|
||||||
|
#ifdef __GNUC__
|
||||||
|
if(!__builtin_constant_p(x))
|
||||||
|
{
|
||||||
|
unsigned short y;
|
||||||
|
asm("rev16 %0, %1":"=r"(y):"r"(x));
|
||||||
|
return y;
|
||||||
|
} else {
|
||||||
|
//It gets worse: if value is constant inlining assembler disables
|
||||||
|
//contant folding, wtf...
|
||||||
|
return (x>>8) | (x<<8);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return (x>>8) | (x<<8);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__MIOSIX_INLINE unsigned int swapBytes32(unsigned int x)
|
||||||
|
{
|
||||||
|
#ifdef __GNUC__
|
||||||
|
return __builtin_bswap32(x);
|
||||||
|
#else
|
||||||
|
return ( x>>24) |
|
||||||
|
((x<< 8) & 0x00ff0000) |
|
||||||
|
((x>> 8) & 0x0000ff00) |
|
||||||
|
( x<<24);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__MIOSIX_INLINE unsigned long long swapBytes64(unsigned long long x)
|
||||||
|
{
|
||||||
|
#ifdef __GNUC__
|
||||||
|
return __builtin_bswap64(x);
|
||||||
|
#else
|
||||||
|
return ( x>>56) |
|
||||||
|
((x<<40) & 0x00ff000000000000ull) |
|
||||||
|
((x<<24) & 0x0000ff0000000000ull) |
|
||||||
|
((x<< 8) & 0x000000ff00000000ull) |
|
||||||
|
((x>> 8) & 0x00000000ff000000ull) |
|
||||||
|
((x>>24) & 0x0000000000ff0000ull) |
|
||||||
|
((x>>40) & 0x000000000000ff00ull) |
|
||||||
|
( x<<56);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef __MIOSIX_INLINE
|
||||||
|
|
||||||
|
#endif //ENDIANNESS_IMPL_H
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
//Interrupt code is common for all the cortex M cores, so it has been put here
|
||||||
|
|
||||||
|
#ifdef _ARCH_ARM7_LPC2000
|
||||||
|
#include "interrupts_arm7.h"
|
||||||
|
#elif defined(_ARCH_CORTEXM0) || defined(_ARCH_CORTEXM3) \
|
||||||
|
|| defined(_ARCH_CORTEXM4) || defined(_ARCH_CORTEXM7)
|
||||||
|
#include "interrupts_cortexMx.h"
|
||||||
|
#else
|
||||||
|
#error "Unknown arch"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Cortex M0 and M0+ does not have some SCB registers, in order to avoid
|
||||||
|
// compilation issues a flag is defined to disable code that accesses to
|
||||||
|
// registers not present in these families
|
||||||
|
|
||||||
|
#if defined(_ARCH_CORTEXM0_STM32)
|
||||||
|
#define _ARCH_CORTEXM0
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2008, 2009, 2010 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "kernel/logging.h"
|
||||||
|
#include "interfaces/portability.h"
|
||||||
|
#include "config/miosix_settings.h"
|
||||||
|
#include "interrupts.h"
|
||||||
|
|
||||||
|
using namespace miosix;
|
||||||
|
|
||||||
|
//Look up table used by printUnsignedInt()
|
||||||
|
static const char hexdigits[]="0123456789abcdef";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Used to print an unsigned int in hexadecimal format, and to reboot the system
|
||||||
|
* Note that printf/iprintf cannot be used inside an IRQ, so that's why there's
|
||||||
|
* this function.
|
||||||
|
* \param x number to print
|
||||||
|
*/
|
||||||
|
static void printUnsignedInt(unsigned int x)
|
||||||
|
{
|
||||||
|
char result[]="0x........\r\n";
|
||||||
|
for(int i=9;i>=2;i--)
|
||||||
|
{
|
||||||
|
result[i]=hexdigits[x & 0xf];
|
||||||
|
x>>=4;
|
||||||
|
}
|
||||||
|
IRQerrorLog(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Spurious interrupt handler.
|
||||||
|
* The LPC2138 datasheet says that spurious interrups can occur, but until now
|
||||||
|
* it never happened. If and when spurious interruts will occur, this code will
|
||||||
|
* be modified to deal with them. Until then, this code just reboots the system.
|
||||||
|
*/
|
||||||
|
void default_IRQ_Routine()
|
||||||
|
{
|
||||||
|
IRQerrorLog("\r\n***Unexpected IRQ\r\n");
|
||||||
|
miosix_private::IRQsystemReboot();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* FIQ is currently not used.
|
||||||
|
* Prints an error message, and reboots the system.
|
||||||
|
* Stack usage is 24 Bytes (measured with watermarking and stack dump)
|
||||||
|
* so a 32 byte stack is used (to leave some guard space).
|
||||||
|
* If the user wants to use FIQ, it is important to remember to increase the
|
||||||
|
* FIQ's stack size, which is defined in miosix.ld
|
||||||
|
*/
|
||||||
|
extern "C" void FIQ_Routine()
|
||||||
|
{
|
||||||
|
IRQerrorLog("\r\n***Unexpected FIQ\r\n");
|
||||||
|
miosix_private::IRQsystemReboot();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* This ISR handles Undefined instruction.
|
||||||
|
* Prints an error message, showing an address near the instruction that caused
|
||||||
|
* the exception. This address together with the map file allows finding the
|
||||||
|
* function that caused the exception.
|
||||||
|
* Please note that when compiling with some level of optimization, the compiler
|
||||||
|
* can inline functions so the address is no longer accurate.
|
||||||
|
* Stack usage is 47 Bytes (measured with watermarking and stack dump)
|
||||||
|
* so a 48 byte stack is used (stak must be word-aligned).
|
||||||
|
*/
|
||||||
|
extern "C" void UNDEF_Routine()
|
||||||
|
{
|
||||||
|
//These two instructions MUST be the first two instructions of the interrupt
|
||||||
|
//routine. They store in return_address the pc of the instruction that
|
||||||
|
//caused the interrupt.
|
||||||
|
register int returnAddress;
|
||||||
|
asm volatile("mov %0, lr" : "=r"(returnAddress));
|
||||||
|
|
||||||
|
IRQerrorLog("\r\n***Unexpected UNDEF @ ");
|
||||||
|
printUnsignedInt(returnAddress);
|
||||||
|
miosix_private::IRQsystemReboot();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* This ISR handles data abort.
|
||||||
|
* Prints an error message, showing an address near the instruction that caused
|
||||||
|
* the exception. This address together with the map file allows finding the
|
||||||
|
* function that caused the exception.
|
||||||
|
* Please note that when compiling with some level of optimization, the compiler
|
||||||
|
* can inline functions so the address is no longer accurate.
|
||||||
|
* Stack usage is 47 Bytes (measured with watermarking and stack dump)
|
||||||
|
* so a 48 byte stack is used (stak must be word-aligned).
|
||||||
|
*/
|
||||||
|
extern "C" void DABT_Routine()
|
||||||
|
{
|
||||||
|
//These two instructions MUST be the first two instructions of the interrupt
|
||||||
|
//routine. They store in return_address the pc of the instruction that
|
||||||
|
//caused the interrupt. (lr has an offset of 8 during a data abort)
|
||||||
|
register int returnAddress;
|
||||||
|
asm volatile("sub %0, lr, #8" : "=r"(returnAddress));
|
||||||
|
|
||||||
|
IRQerrorLog("\r\n***Unexpected data abort @ ");
|
||||||
|
printUnsignedInt(returnAddress);
|
||||||
|
miosix_private::IRQsystemReboot();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* This ISR handles prefetch abort.
|
||||||
|
* Prints an error message, showing an address near the instruction that caused
|
||||||
|
* the exception. This address together with the map file allows finding the
|
||||||
|
* function that caused the exception.
|
||||||
|
* Please note that when compiling with some level of optimization, the compiler
|
||||||
|
* can inline functions so the address is no longer accurate.
|
||||||
|
* Stack usage is 47 Bytes (measured with watermarking and stack dump)
|
||||||
|
* so a 48 byte stack is used (stak must be word-aligned).
|
||||||
|
*/
|
||||||
|
extern "C" void PABT_Routine()
|
||||||
|
{
|
||||||
|
//These two instructions MUST be the first two instructions of the interrupt
|
||||||
|
//routine. They store in return_address the pc of the instruction that
|
||||||
|
//caused the interrupt. (lr has an offset of 4 during a data abort)
|
||||||
|
register int returnAddress;
|
||||||
|
asm volatile("sub %0, lr, #4" : "=r"(returnAddress));
|
||||||
|
|
||||||
|
IRQerrorLog("\r\n***Unexpected prefetch abort @ ");
|
||||||
|
printUnsignedInt(returnAddress);
|
||||||
|
miosix_private::IRQsystemReboot();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2008 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* interrupts.h Part of the Miosix Embedded OS.
|
||||||
|
* Implementation of "Generic" interrupts, not related to a particular
|
||||||
|
* hardware driver.
|
||||||
|
************************************************************************/
|
||||||
|
|
||||||
|
#ifndef INTERRUPTS_H
|
||||||
|
#define INTERRUPTS_H
|
||||||
|
|
||||||
|
void default_IRQ_Routine() __attribute__ ((interrupt("IRQ")));
|
||||||
|
extern "C" void FIQ_Routine() __attribute__ ((interrupt("FIQ")));
|
||||||
|
extern "C" void UNDEF_Routine() __attribute__ ((interrupt("UNDEF")));
|
||||||
|
extern "C" void DABT_Routine() __attribute__ ((interrupt("DABT")));
|
||||||
|
extern "C" void PABT_Routine() __attribute__ ((interrupt("PABT")));
|
||||||
|
|
||||||
|
#endif //INTERRUPTS_H
|
||||||
|
|
@ -0,0 +1,282 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2010, 2011, 2012, 2013, 2014 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "kernel/logging.h"
|
||||||
|
#include "kernel/kernel.h"
|
||||||
|
#include "config/miosix_settings.h"
|
||||||
|
#include "interfaces/portability.h"
|
||||||
|
#include "interfaces/arch_registers.h"
|
||||||
|
#include "interrupts.h"
|
||||||
|
|
||||||
|
using namespace miosix;
|
||||||
|
|
||||||
|
#ifdef WITH_ERRLOG
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Used to print an unsigned int in hexadecimal format, and to reboot the system
|
||||||
|
* Note that printf/iprintf cannot be used inside an IRQ, so that's why there's
|
||||||
|
* this function.
|
||||||
|
* \param x number to print
|
||||||
|
*/
|
||||||
|
static void printUnsignedInt(unsigned int x)
|
||||||
|
{
|
||||||
|
static const char hexdigits[]="0123456789abcdef";
|
||||||
|
char result[]="0x........\r\n";
|
||||||
|
for(int i=9;i>=2;i--)
|
||||||
|
{
|
||||||
|
result[i]=hexdigits[x & 0xf];
|
||||||
|
x>>=4;
|
||||||
|
}
|
||||||
|
IRQerrorLog(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //WITH_ERRLOG
|
||||||
|
|
||||||
|
#if defined(WITH_PROCESSES) || defined(WITH_ERRLOG)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* \return the program counter of the thread that was running when the exception
|
||||||
|
* occurred.
|
||||||
|
*/
|
||||||
|
static unsigned int getProgramCounter()
|
||||||
|
{
|
||||||
|
register unsigned int result;
|
||||||
|
// Get program counter when the exception was thrown from stack frame
|
||||||
|
asm volatile("mrs %0, psp \n\t"
|
||||||
|
"add %0, %0, #24 \n\t"
|
||||||
|
"ldr %0, [%0] \n\t":"=r"(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //WITH_PROCESSES || WITH_ERRLOG
|
||||||
|
|
||||||
|
void NMI_Handler()
|
||||||
|
{
|
||||||
|
IRQerrorLog("\r\n***Unexpected NMI\r\n");
|
||||||
|
miosix_private::IRQsystemReboot();
|
||||||
|
}
|
||||||
|
|
||||||
|
void __attribute__((naked)) HardFault_Handler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
//Call HardFault_impl(). Name is a C++ mangled name.
|
||||||
|
asm volatile("bl _Z14HardFault_implv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
void __attribute__((noinline)) HardFault_impl()
|
||||||
|
{
|
||||||
|
#ifdef WITH_PROCESSES
|
||||||
|
if(miosix::Thread::IRQreportFault(miosix_private::FaultData(
|
||||||
|
HARDFAULT,getProgramCounter()))) return;
|
||||||
|
#endif //WITH_PROCESSES
|
||||||
|
#ifdef WITH_ERRLOG
|
||||||
|
IRQerrorLog("\r\n***Unexpected HardFault @ ");
|
||||||
|
printUnsignedInt(getProgramCounter());
|
||||||
|
#ifndef _ARCH_CORTEXM0
|
||||||
|
unsigned int hfsr=SCB->HFSR;
|
||||||
|
if(hfsr & 0x40000000) //SCB_HFSR_FORCED
|
||||||
|
IRQerrorLog("Fault escalation occurred\r\n");
|
||||||
|
if(hfsr & 0x00000002) //SCB_HFSR_VECTTBL
|
||||||
|
IRQerrorLog("A BusFault occurred during a vector table read\r\n");
|
||||||
|
#endif //_ARCH_CORTEXM0
|
||||||
|
#endif //WITH_ERRLOG
|
||||||
|
miosix_private::IRQsystemReboot();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cortex M0/M0+ architecture does not have the interrupts handled by code
|
||||||
|
// below this point
|
||||||
|
#ifndef _ARCH_CORTEXM0
|
||||||
|
|
||||||
|
void __attribute__((naked)) MemManage_Handler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
//Call MemManage_impl(). Name is a C++ mangled name.
|
||||||
|
asm volatile("bl _Z14MemManage_implv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
void __attribute__((noinline)) MemManage_impl()
|
||||||
|
{
|
||||||
|
#if defined(WITH_PROCESSES) || defined(WITH_ERRLOG)
|
||||||
|
unsigned int cfsr=SCB->CFSR;
|
||||||
|
#endif //WITH_PROCESSES || WITH_ERRLOG
|
||||||
|
#ifdef WITH_PROCESSES
|
||||||
|
int id, arg=0;
|
||||||
|
if(cfsr & 0x00000001) id=MP_XN;
|
||||||
|
else if(cfsr & 0x00000080) { id=MP; arg=SCB->MMFAR; }
|
||||||
|
else id=MP_NOADDR;
|
||||||
|
if(miosix::Thread::IRQreportFault(miosix_private::FaultData(
|
||||||
|
id,getProgramCounter(),arg)))
|
||||||
|
{
|
||||||
|
SCB->SHCSR &= ~(1<<13); //Clear MEMFAULTPENDED bit
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif //WITH_PROCESSES
|
||||||
|
#ifdef WITH_ERRLOG
|
||||||
|
IRQerrorLog("\r\n***Unexpected MemManage @ ");
|
||||||
|
printUnsignedInt(getProgramCounter());
|
||||||
|
if(cfsr & 0x00000080) //SCB_CFSR_MMARVALID
|
||||||
|
{
|
||||||
|
IRQerrorLog("Fault caused by attempted access to ");
|
||||||
|
printUnsignedInt(SCB->MMFAR);
|
||||||
|
} else IRQerrorLog("The address that caused the fault is missing\r\n");
|
||||||
|
if(cfsr & 0x00000010) //SCB_CFSR_MSTKERR
|
||||||
|
IRQerrorLog("Fault occurred during exception stacking\r\n");
|
||||||
|
if(cfsr & 0x00000008) //SCB_CFSR_MUNSTKERR
|
||||||
|
IRQerrorLog("Fault occurred during exception unstacking\r\n");
|
||||||
|
if(cfsr & 0x00000002) //SCB_CFSR_DACCVIOL
|
||||||
|
IRQerrorLog("Fault was caused by invalid PC\r\n");
|
||||||
|
if(cfsr & 0x00000001) //SCB_CFSR_IACCVIOL
|
||||||
|
IRQerrorLog("Fault was caused by attempted execution from XN area\r\n");
|
||||||
|
#endif //WITH_ERRLOG
|
||||||
|
miosix_private::IRQsystemReboot();
|
||||||
|
}
|
||||||
|
|
||||||
|
void __attribute__((naked)) BusFault_Handler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
//Call BusFault_impl(). Name is a C++ mangled name.
|
||||||
|
asm volatile("bl _Z13BusFault_implv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
void __attribute__((noinline)) BusFault_impl()
|
||||||
|
{
|
||||||
|
#if defined(WITH_PROCESSES) || defined(WITH_ERRLOG)
|
||||||
|
unsigned int cfsr=SCB->CFSR;
|
||||||
|
#endif //WITH_PROCESSES || WITH_ERRLOG
|
||||||
|
#ifdef WITH_PROCESSES
|
||||||
|
int id, arg=0;
|
||||||
|
if(cfsr & 0x00008000) { id=BF; arg=SCB->BFAR; }
|
||||||
|
else id=BF_NOADDR;
|
||||||
|
if(miosix::Thread::IRQreportFault(miosix_private::FaultData(
|
||||||
|
id,getProgramCounter(),arg)))
|
||||||
|
{
|
||||||
|
SCB->SHCSR &= ~(1<<14); //Clear BUSFAULTPENDED bit
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif //WITH_PROCESSES
|
||||||
|
#ifdef WITH_ERRLOG
|
||||||
|
IRQerrorLog("\r\n***Unexpected BusFault @ ");
|
||||||
|
printUnsignedInt(getProgramCounter());
|
||||||
|
if(cfsr & 0x00008000) //SCB_CFSR_BFARVALID
|
||||||
|
{
|
||||||
|
IRQerrorLog("Fault caused by attempted access to ");
|
||||||
|
printUnsignedInt(SCB->BFAR);
|
||||||
|
} else IRQerrorLog("The address that caused the fault is missing\r\n");
|
||||||
|
if(cfsr & 0x00001000) //SCB_CFSR_STKERR
|
||||||
|
IRQerrorLog("Fault occurred during exception stacking\r\n");
|
||||||
|
if(cfsr & 0x00000800) //SCB_CFSR_UNSTKERR
|
||||||
|
IRQerrorLog("Fault occurred during exception unstacking\r\n");
|
||||||
|
if(cfsr & 0x00000400) //SCB_CFSR_IMPRECISERR
|
||||||
|
IRQerrorLog("Fault is imprecise\r\n");
|
||||||
|
if(cfsr & 0x00000200) //SCB_CFSR_PRECISERR
|
||||||
|
IRQerrorLog("Fault is precise\r\n");
|
||||||
|
if(cfsr & 0x00000100) //SCB_CFSR_IBUSERR
|
||||||
|
IRQerrorLog("Fault happened during instruction fetch\r\n");
|
||||||
|
#endif //WITH_ERRLOG
|
||||||
|
miosix_private::IRQsystemReboot();
|
||||||
|
}
|
||||||
|
|
||||||
|
void __attribute__((naked)) UsageFault_Handler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
//Call UsageFault_impl(). Name is a C++ mangled name.
|
||||||
|
asm volatile("bl _Z15UsageFault_implv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
void __attribute__((noinline)) UsageFault_impl()
|
||||||
|
{
|
||||||
|
#if defined(WITH_PROCESSES) || defined(WITH_ERRLOG)
|
||||||
|
unsigned int cfsr=SCB->CFSR;
|
||||||
|
#endif //WITH_PROCESSES || WITH_ERRLOG
|
||||||
|
#ifdef WITH_PROCESSES
|
||||||
|
int id;
|
||||||
|
if(cfsr & 0x02000000) id=UF_DIVZERO;
|
||||||
|
else if(cfsr & 0x01000000) id=UF_UNALIGNED;
|
||||||
|
else if(cfsr & 0x00080000) id=UF_COPROC;
|
||||||
|
else if(cfsr & 0x00040000) id=UF_EXCRET;
|
||||||
|
else if(cfsr & 0x00020000) id=UF_EPSR;
|
||||||
|
else if(cfsr & 0x00010000) id=UF_UNDEF;
|
||||||
|
else id=UF_UNEXP;
|
||||||
|
if(miosix::Thread::IRQreportFault(miosix_private::FaultData(
|
||||||
|
id,getProgramCounter())))
|
||||||
|
{
|
||||||
|
SCB->SHCSR &= ~(1<<12); //Clear USGFAULTPENDED bit
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif //WITH_PROCESSES
|
||||||
|
#ifdef WITH_ERRLOG
|
||||||
|
IRQerrorLog("\r\n***Unexpected UsageFault @ ");
|
||||||
|
printUnsignedInt(getProgramCounter());
|
||||||
|
if(cfsr & 0x02000000) //SCB_CFSR_DIVBYZERO
|
||||||
|
IRQerrorLog("Divide by zero\r\n");
|
||||||
|
if(cfsr & 0x01000000) //SCB_CFSR_UNALIGNED
|
||||||
|
IRQerrorLog("Unaligned memory access\r\n");
|
||||||
|
if(cfsr & 0x00080000) //SCB_CFSR_NOCP
|
||||||
|
IRQerrorLog("Attempted coprocessor access\r\n");
|
||||||
|
if(cfsr & 0x00040000) //SCB_CFSR_INVPC
|
||||||
|
IRQerrorLog("EXC_RETURN not expected now\r\n");
|
||||||
|
if(cfsr & 0x00020000) //SCB_CFSR_INVSTATE
|
||||||
|
IRQerrorLog("Invalid EPSR usage\r\n");
|
||||||
|
if(cfsr & 0x00010000) //SCB_CFSR_UNDEFINSTR
|
||||||
|
IRQerrorLog("Undefined instruction\r\n");
|
||||||
|
#endif //WITH_ERRLOG
|
||||||
|
miosix_private::IRQsystemReboot();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugMon_Handler()
|
||||||
|
{
|
||||||
|
#ifdef WITH_ERRLOG
|
||||||
|
IRQerrorLog("\r\n***Unexpected DebugMon @ ");
|
||||||
|
printUnsignedInt(getProgramCounter());
|
||||||
|
#endif //WITH_ERRLOG
|
||||||
|
miosix_private::IRQsystemReboot();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //_ARCH_CORTEXM0
|
||||||
|
|
||||||
|
void PendSV_Handler()
|
||||||
|
{
|
||||||
|
#ifdef WITH_ERRLOG
|
||||||
|
IRQerrorLog("\r\n***Unexpected PendSV @ ");
|
||||||
|
printUnsignedInt(getProgramCounter());
|
||||||
|
#endif //WITH_ERRLOG
|
||||||
|
miosix_private::IRQsystemReboot();
|
||||||
|
}
|
||||||
|
|
||||||
|
void unexpectedInterrupt()
|
||||||
|
{
|
||||||
|
#ifdef WITH_ERRLOG
|
||||||
|
IRQerrorLog("\r\n***Unexpected Peripheral interrupt\r\n");
|
||||||
|
#endif //WITH_ERRLOG
|
||||||
|
miosix_private::IRQsystemReboot();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2010, 2011, 2012, 2013, 2014 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef INTERRUPTS_H
|
||||||
|
#define INTERRUPTS_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an unexpected interrupt occurs.
|
||||||
|
* It is called by stage_1_boot.cpp for all weak interrupts not defined.
|
||||||
|
*/
|
||||||
|
void unexpectedInterrupt();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible kind of faults that the Cortex-M3 can report.
|
||||||
|
* They are used to print debug information if a process causes a fault
|
||||||
|
*/
|
||||||
|
enum FaultType
|
||||||
|
{
|
||||||
|
MP=1, //Process attempted data access outside its memory
|
||||||
|
MP_NOADDR=2, //Process attempted data access outside its memory (missing addr)
|
||||||
|
MP_XN=3, //Process attempted code access outside its memory
|
||||||
|
UF_DIVZERO=4, //Process attempted to divide by zero
|
||||||
|
UF_UNALIGNED=5,//Process attempted unaligned memory access
|
||||||
|
UF_COPROC=6, //Process attempted a coprocessor access
|
||||||
|
UF_EXCRET=7, //Process attempted an exception return
|
||||||
|
UF_EPSR=8, //Process attempted to access the EPSR
|
||||||
|
UF_UNDEF=9, //Process attempted to execute an invalid instruction
|
||||||
|
UF_UNEXP=10, //Unexpected usage fault
|
||||||
|
HARDFAULT=11, //Hardfault (for example process executed a BKPT instruction)
|
||||||
|
BF=12, //Busfault
|
||||||
|
BF_NOADDR=13 //Busfault (missing addr)
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //INTERRUPTS_H
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
|
||||||
|
//MPU code is common for all the cortex M cores, so it has been put here
|
||||||
|
|
||||||
|
#ifndef MEMORY_PROTECTION_H
|
||||||
|
#define MEMORY_PROTECTION_H
|
||||||
|
|
||||||
|
#if defined(_ARCH_CORTEXM3_STM32F2) || defined(_ARCH_CORTEXM4_STM32F4) \
|
||||||
|
|| defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7) \
|
||||||
|
|| defined(_ARCH_CORTEXM3_EFM32GG) || defined(_ARCH_CORTEXM4_STM32L4)
|
||||||
|
#include "mpu_cortexMx.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //MEMORY_PROTECTION_H
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2018 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "mpu_cortexMx.h"
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
unsigned int sizeToMpu(unsigned int size)
|
||||||
|
{
|
||||||
|
assert(size>=32);
|
||||||
|
unsigned int result=30-__builtin_clz(size);
|
||||||
|
if(size & (size-1)) result++;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_PROCESSES
|
||||||
|
|
||||||
|
//
|
||||||
|
// class MPUConfiguration
|
||||||
|
//
|
||||||
|
|
||||||
|
MPUConfiguration::MPUConfiguration(unsigned int *elfBase, unsigned int elfSize,
|
||||||
|
unsigned int *imageBase, unsigned int imageSize)
|
||||||
|
{
|
||||||
|
// NOTE: The ARM documentation is unclear about the effect of the shareable
|
||||||
|
// bit on a single core architecture. Experimental evidence on an STM32F476
|
||||||
|
// shows that setting it in IRQconfigureCache for the internal RAM region
|
||||||
|
// causes the boot to fail.
|
||||||
|
// For this reason, all regions are marked as not shareable
|
||||||
|
regValues[0]=(reinterpret_cast<unsigned int>(elfBase) & (~0x1f))
|
||||||
|
| MPU_RBAR_VALID_Msk | 6; //Region 6
|
||||||
|
regValues[2]=(reinterpret_cast<unsigned int>(imageBase) & (~0x1f))
|
||||||
|
| MPU_RBAR_VALID_Msk | 7; //Region 7
|
||||||
|
regValues[1]=2<<MPU_RASR_AP_Pos //Privileged: RW, unprivileged: RO
|
||||||
|
| MPU_RASR_C_Msk
|
||||||
|
| 1 //Enable bit
|
||||||
|
| sizeToMpu(elfSize)<<1;
|
||||||
|
regValues[3]=3<<MPU_RASR_AP_Pos //Privileged: RW, unprivileged: RW
|
||||||
|
| MPU_RASR_XN_Msk
|
||||||
|
| MPU_RASR_C_Msk
|
||||||
|
| 1 //Enable bit
|
||||||
|
| sizeToMpu(imageSize)<<1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MPUConfiguration::dumpConfiguration()
|
||||||
|
{
|
||||||
|
for(int i=0;i<2;i++)
|
||||||
|
{
|
||||||
|
unsigned int base=regValues[2*i] & (~0x1f);
|
||||||
|
unsigned int end=base+(1<<(((regValues[2*i+1]>>1) & 31)+1));
|
||||||
|
char w=regValues[2*i+1] & (1<<MPU_RASR_AP_Pos) ? 'w' : '-';
|
||||||
|
char x=regValues[2*i+1] & MPU_RASR_XN_Msk ? '-' : 'x';
|
||||||
|
iprintf("* MPU region %d 0x%08x-0x%08x r%c%c\n",i+6,base,end,w,x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int MPUConfiguration::roundSizeForMPU(unsigned int size)
|
||||||
|
{
|
||||||
|
return 1<<(sizeToMpu(size)+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MPUConfiguration::withinForReading(const void *ptr, size_t size) const
|
||||||
|
{
|
||||||
|
size_t codeStart=regValues[0] & (~0x1f);
|
||||||
|
size_t codeEnd=codeStart+(1<<(((regValues[1]>>1) & 31)+1));
|
||||||
|
size_t dataStart=regValues[2] & (~0x1f);
|
||||||
|
size_t dataEnd=dataStart+(1<<(((regValues[3]>>1) & 31)+1));
|
||||||
|
size_t base=reinterpret_cast<size_t>(ptr);
|
||||||
|
//The last check is to prevent a wraparound to be considered valid
|
||||||
|
return ( (base>=codeStart && base+size<codeEnd)
|
||||||
|
|| (base>=dataStart && base+size<dataEnd)) && base+size>=base;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MPUConfiguration::withinForWriting(const void *ptr, size_t size) const
|
||||||
|
{
|
||||||
|
size_t dataStart=regValues[2] & (~0x1f);
|
||||||
|
size_t dataEnd=dataStart+(1<<(((regValues[3]>>1) & 31)+1));
|
||||||
|
size_t base=reinterpret_cast<size_t>(ptr);
|
||||||
|
//The last check is to prevent a wraparound to be considered valid
|
||||||
|
return base>=dataStart && base+size<dataEnd && base+size>=base;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MPUConfiguration::withinForReading(const char* str) const
|
||||||
|
{
|
||||||
|
size_t codeStart=regValues[0] & (~0x1f);
|
||||||
|
size_t codeEnd=codeStart+(1<<(((regValues[1]>>1) & 31)+1));
|
||||||
|
size_t dataStart=regValues[2] & (~0x1f);
|
||||||
|
size_t dataEnd=dataStart+(1<<(((regValues[3]>>1) & 31)+1));
|
||||||
|
size_t base=reinterpret_cast<size_t>(str);
|
||||||
|
if((base>=codeStart) && (base<codeEnd))
|
||||||
|
return strnlen(str,codeEnd-base)<codeEnd-base;
|
||||||
|
if((base>=dataStart) && (base<dataEnd))
|
||||||
|
return strnlen(str,dataEnd-base)<dataEnd-base;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //WITH_PROCESSES
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,152 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2018 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef MPU_CORTEX_MX_H
|
||||||
|
#define MPU_CORTEX_MX_H
|
||||||
|
|
||||||
|
#include "config/miosix_settings.h"
|
||||||
|
#include "interfaces/arch_registers.h"
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \param size in bytes >32
|
||||||
|
* \return a value that can be written to MPU->RASR to represent that size
|
||||||
|
*/
|
||||||
|
unsigned int sizeToMpu(unsigned int size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To be called at boot to enable the MPU.
|
||||||
|
* Without calling this function, the MPU will not work even if regions are
|
||||||
|
* configured in MPUConfiguration
|
||||||
|
*/
|
||||||
|
inline void IRQenableMPUatBoot()
|
||||||
|
{
|
||||||
|
MPU->CTRL=MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_ENABLE_Msk;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_PROCESSES
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* This class is used to manage the MemoryProtectionUnit
|
||||||
|
*/
|
||||||
|
class MPUConfiguration
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Default constructor, leaves the MPU regions unconfigured
|
||||||
|
*/
|
||||||
|
MPUConfiguration() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* \param elfBase base address of the ELF file
|
||||||
|
* \param elfSize size of the ELF file
|
||||||
|
* \param imageBase base address of the Process RAM image
|
||||||
|
* \param imageSize size of the Process RAM image
|
||||||
|
*/
|
||||||
|
MPUConfiguration(unsigned int *elfBase, unsigned int elfSize,
|
||||||
|
unsigned int *imageBase, unsigned int imageSize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* This method is used to configure the Memoy Protection region for a
|
||||||
|
* Process during a context-switch to a userspace thread.
|
||||||
|
* Can only be called inside an IRQ, not even with interrupts disabled
|
||||||
|
*/
|
||||||
|
void IRQenable()
|
||||||
|
{
|
||||||
|
MPU->RBAR=regValues[0];
|
||||||
|
MPU->RASR=regValues[1];
|
||||||
|
MPU->RBAR=regValues[2];
|
||||||
|
MPU->RASR=regValues[3];
|
||||||
|
__set_CONTROL(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* This method is used to disable the MPU during a context-switch to a
|
||||||
|
* kernelspace thread.
|
||||||
|
* Can only be called inside an IRQ, not even with interrupts disabled
|
||||||
|
*/
|
||||||
|
static void IRQdisable()
|
||||||
|
{
|
||||||
|
__set_CONTROL(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print the MPU configuration for debugging purposes
|
||||||
|
*/
|
||||||
|
void dumpConfiguration();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some MPU implementations may not allow regions of arbitrary size,
|
||||||
|
* this function allows to round a size up to the minimum value that
|
||||||
|
* the MPU support.
|
||||||
|
* \param size the size of a memory area to be configured as an MPU
|
||||||
|
* region
|
||||||
|
* \return the size rounded to the minimum MPU region allowed that is
|
||||||
|
* greater or equal to the given size
|
||||||
|
*/
|
||||||
|
static unsigned int roundSizeForMPU(unsigned int size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a buffer is within a readable segment of the process
|
||||||
|
* \param ptr base pointer of the buffer to check
|
||||||
|
* \param size buffer size
|
||||||
|
* \return true if the buffer is correctly within the process
|
||||||
|
*/
|
||||||
|
bool withinForReading(const void *ptr, size_t size) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a buffer is within a writable segment of the process
|
||||||
|
* \param ptr base pointer of the buffer to check
|
||||||
|
* \param size buffer size
|
||||||
|
* \return true if the buffer is correctly within the process
|
||||||
|
*/
|
||||||
|
bool withinForWriting(const void *ptr, size_t size) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a nul terminated string is entirely contained in the process,
|
||||||
|
* \param str a pointer to a nul terminated string
|
||||||
|
* \return true if the buffer is correctly within the process
|
||||||
|
*/
|
||||||
|
bool withinForReading(const char *str) const;
|
||||||
|
|
||||||
|
//Uses default copy constructor and operator=
|
||||||
|
private:
|
||||||
|
///These value are copied into the MPU registers to configure them
|
||||||
|
unsigned int regValues[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //WITH_PROCESSES
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //MPU_CORTEX_MX_H
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2011, 2012, 2013, 2014 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DCC support inspired by dcc_stdio.c in OpenOCD
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "filesystem/ioctl.h"
|
||||||
|
#include "dcc.h"
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
ssize_t ARMDCC::readBlock(void* buffer, size_t size, off_t where)
|
||||||
|
{
|
||||||
|
return -EBADF;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t ARMDCC::writeBlock(const void* buffer, size_t size, off_t where)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
debugStr(reinterpret_cast<const char*>(buffer),size);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMDCC::IRQwrite(const char* str)
|
||||||
|
{
|
||||||
|
//Actually, there is a race condition if IRQdebugWrite is called from an
|
||||||
|
//interrupt while the main code is printing with debugWrite, but it is
|
||||||
|
//not such an issue since IRQdebugWrite is called only
|
||||||
|
//- at boot before the kernel is started, so there are no other threads
|
||||||
|
//- in case of a serious error, to print what went wrong before rebooting
|
||||||
|
//In the second case, which is rare, the data may not be printed correctly
|
||||||
|
debugStr(str,-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ARMDCC::ioctl(int cmd, void* arg)
|
||||||
|
{
|
||||||
|
if(cmd==IOCTL_SYNC) return 0; //Nothing to do, but say we did somaething
|
||||||
|
return -ENOTTY; //Means the operation does not apply to this descriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMDCC::send(unsigned char c)
|
||||||
|
{
|
||||||
|
const unsigned int busy=1;
|
||||||
|
while(CoreDebug->DCRDR & busy) ;
|
||||||
|
CoreDebug->DCRDR=(static_cast<unsigned int>(c)<<8) | busy;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMDCC::debugStr(const char *str, int length)
|
||||||
|
{
|
||||||
|
//If not being debugged, don't print anything
|
||||||
|
if((CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk)==0) return;
|
||||||
|
|
||||||
|
if(length<0) length=strlen(str);
|
||||||
|
if(length>0 && str[0]=='\r') str++, length--; //TODO: better \r\n removal
|
||||||
|
if(length>0 && str[0]=='\n') str++, length--;
|
||||||
|
if(length==0) return;
|
||||||
|
send(1); //1=sending a string
|
||||||
|
send(0);
|
||||||
|
send(length & 0xff);
|
||||||
|
send(length>>8);
|
||||||
|
for(int i=0;i<length;i++) send(*str++);
|
||||||
|
//OpenOCD expects data in 4 byte blocks, so send trailing zeros to align
|
||||||
|
int remaining=4-(length & 3);
|
||||||
|
if(remaining<4) for(int i=0;i<remaining;i++) send(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2011, 2012, 2013, 2014 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef DCC_H
|
||||||
|
#define DCC_H
|
||||||
|
|
||||||
|
#include "filesystem/console/console_device.h"
|
||||||
|
#include "kernel/sync.h"
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class exposes the ARM debug communication channel through the Device
|
||||||
|
* interface, to allow redirecting stdin and stdout to the DCC for boards that
|
||||||
|
* have JTAG/SWD but no serial port
|
||||||
|
*/
|
||||||
|
class ARMDCC : public Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
ARMDCC() : Device(Device::TTY) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a block of data. As the DCC is write only, this function returns an
|
||||||
|
* error
|
||||||
|
* \param buffer buffer where read data will be stored
|
||||||
|
* \param size buffer size
|
||||||
|
* \param where where to read from
|
||||||
|
* \return number of bytes read or a negative number on failure
|
||||||
|
*/
|
||||||
|
ssize_t readBlock(void *buffer, size_t size, off_t where);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a block of data
|
||||||
|
* \param buffer buffer where take data to write
|
||||||
|
* \param size buffer size
|
||||||
|
* \param where where to write to
|
||||||
|
* \return number of bytes written or a negative number on failure
|
||||||
|
*/
|
||||||
|
ssize_t writeBlock(const void *buffer, size_t size, off_t where);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a string.
|
||||||
|
* An extension to the Device interface that adds a new member function,
|
||||||
|
* which is used by the kernel on console devices to write debug information
|
||||||
|
* before the kernel is started or in case of serious errors, right before
|
||||||
|
* rebooting.
|
||||||
|
* Can ONLY be called when the kernel is not yet started, paused or within
|
||||||
|
* an interrupt. This default implementation ignores writes.
|
||||||
|
* \param str the string to write. The string must be NUL terminated.
|
||||||
|
*/
|
||||||
|
void IRQwrite(const char *str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs device-specific operations
|
||||||
|
* \param cmd specifies the operation to perform
|
||||||
|
* \param arg optional argument that some operation require
|
||||||
|
* \return the exact return value depends on CMD, -1 is returned on error
|
||||||
|
*/
|
||||||
|
int ioctl(int cmd, void *arg);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Send data to the host
|
||||||
|
*/
|
||||||
|
static void send(unsigned char c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a line of text to the host.
|
||||||
|
* OpenOCD will insert a \n after each line, unfortunately,
|
||||||
|
* as this complicates things.
|
||||||
|
*/
|
||||||
|
static void debugStr(const char *str, int length);
|
||||||
|
|
||||||
|
FastMutex mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //DCC_H
|
||||||
|
|
@ -0,0 +1,587 @@
|
||||||
|
/*
|
||||||
|
* Integration in Miosix by Terraneo Federico.
|
||||||
|
* Based on code by Martin Thomas to initialize SD cards from LPC2000
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sd_lpc2000.h"
|
||||||
|
#include "interfaces/bsp.h"
|
||||||
|
#include "LPC213x.h"
|
||||||
|
#include "board_settings.h" //For sdVoltage
|
||||||
|
#include <cstdio>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
//Note: enabling debugging might cause deadlock when using sleep() or reboot()
|
||||||
|
//The bug won't be fixed because debugging is only useful for driver development
|
||||||
|
///\internal Debug macro, for normal conditions
|
||||||
|
//#define DBG iprintf
|
||||||
|
#define DBG(x,...) do {} while(0)
|
||||||
|
///\internal Debug macro, for errors only
|
||||||
|
//#define DBGERR iprintf
|
||||||
|
#define DBGERR(x,...) do {} while(0)
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
///\internal Type of card (1<<0)=MMC (1<<1)=SDv1 (1<<2)=SDv2 (1<<2)|(1<<3)=SDHC
|
||||||
|
static unsigned char cardType=0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Definitions for MMC/SDC command.
|
||||||
|
* A command has the following format:
|
||||||
|
* - 1 bit @ 0 (start bit)
|
||||||
|
* - 1 bit @ 1 (transmission bit)
|
||||||
|
* - 6 bit which identify command index (CMD0..CMD63)
|
||||||
|
* - 32 bit command argument
|
||||||
|
* - 7 bit CRC
|
||||||
|
* - 1 bit @ 1 (end bit)
|
||||||
|
* In addition, ACMDxx are the sequence of two commands, CMD55 and CMDxx
|
||||||
|
* These constants have the following meaninig:
|
||||||
|
* - bit #7 @ 1 indicates that it is an ACMD. send_cmd() will send CMD55, then
|
||||||
|
* clear this bit and send the command with this bit @ 0 (which is start bit)
|
||||||
|
* - bit #6 always @ 1, because it is the transmission bit
|
||||||
|
* - remaining 6 bit represent command index
|
||||||
|
*/
|
||||||
|
#define CMD0 (0x40+0) /* GO_IDLE_STATE */
|
||||||
|
#define CMD1 (0x40+1) /* SEND_OP_COND (MMC) */
|
||||||
|
#define ACMD41 (0xC0+41) /* SEND_OP_COND (SDC) */
|
||||||
|
#define CMD8 (0x40+8) /* SEND_IF_COND */
|
||||||
|
#define CMD9 (0x40+9) /* SEND_CSD */
|
||||||
|
#define CMD10 (0x40+10) /* SEND_CID */
|
||||||
|
#define CMD12 (0x40+12) /* STOP_TRANSMISSION */
|
||||||
|
#define CMD13 (0x40+13) /* SEND_STATUS */
|
||||||
|
#define ACMD13 (0xC0+13) /* SD_STATUS (SDC) */
|
||||||
|
#define CMD16 (0x40+16) /* SET_BLOCKLEN */
|
||||||
|
#define CMD17 (0x40+17) /* READ_SINGLE_BLOCK */
|
||||||
|
#define CMD18 (0x40+18) /* READ_MULTIPLE_BLOCK */
|
||||||
|
#define CMD23 (0x40+23) /* SET_BLOCK_COUNT (MMC) */
|
||||||
|
#define ACMD23 (0xC0+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */
|
||||||
|
#define CMD24 (0x40+24) /* WRITE_BLOCK */
|
||||||
|
#define CMD25 (0x40+25) /* WRITE_MULTIPLE_BLOCK */
|
||||||
|
#define CMD42 (0x40+42) /* LOCK_UNLOCK */
|
||||||
|
#define CMD55 (0x40+55) /* APP_CMD */
|
||||||
|
#define CMD58 (0x40+58) /* READ_OCR */
|
||||||
|
|
||||||
|
// SSPCR0 Bit-Definitions
|
||||||
|
#define CPOL 6
|
||||||
|
#define CPHA 7
|
||||||
|
// SSPCR1 Bit-Defintions
|
||||||
|
#define SSE 1
|
||||||
|
#define MS 2
|
||||||
|
#define SCR 8
|
||||||
|
// SSPSR Bit-Definitions
|
||||||
|
#define TNF 1
|
||||||
|
#define RNE 2
|
||||||
|
#define BSY 4
|
||||||
|
|
||||||
|
#define SPI_SCK_PIN 17 // SCK P0.17 out
|
||||||
|
#define SPI_MISO_PIN 18 // MISO P0.18 in
|
||||||
|
#define SPI_MOSI_PIN 19 // MOSI P0.19 out
|
||||||
|
#define SPI_SS_PIN 20 // CS p0.20 out
|
||||||
|
|
||||||
|
#define SPI_SCK_FUNCBIT 2
|
||||||
|
#define SPI_MISO_FUNCBIT 4
|
||||||
|
#define SPI_MOSI_FUNCBIT 6
|
||||||
|
#define SPI_SS_FUNCBIT 8
|
||||||
|
|
||||||
|
///\internal Maximum speed 14745600/2=7372800
|
||||||
|
#define SPI_PRESCALE_MIN 2
|
||||||
|
|
||||||
|
///\internal Select/unselect card
|
||||||
|
#define CS_LOW() IOCLR0 = (1<<SPI_SS_PIN)
|
||||||
|
#define CS_HIGH() IOSET0 = (1<<SPI_SS_PIN)
|
||||||
|
|
||||||
|
//Function prototypes
|
||||||
|
static unsigned char send_cmd(unsigned char cmd, unsigned int arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Initialize SPI
|
||||||
|
*/
|
||||||
|
static void spi_1_init()
|
||||||
|
{
|
||||||
|
unsigned char incoming;
|
||||||
|
PCONP|=(1<<10);//Enable SPI1
|
||||||
|
// setup GPIO
|
||||||
|
IODIR0 |= (1<<SPI_SCK_PIN)|(1<<SPI_MOSI_PIN)|(1<<SPI_SS_PIN);
|
||||||
|
IODIR0 &= ~(1<<SPI_MISO_PIN);
|
||||||
|
// Unselect card
|
||||||
|
CS_HIGH();
|
||||||
|
// Set GPIO mode
|
||||||
|
PINSEL1 &= ~( (3<<SPI_SCK_FUNCBIT) | (3<<SPI_MISO_FUNCBIT) |
|
||||||
|
(3<<SPI_MOSI_FUNCBIT) | (3<<SPI_SS_FUNCBIT) );
|
||||||
|
|
||||||
|
// setup Pin-Functions - keep automatic CS disabled during init
|
||||||
|
PINSEL1 |= ( (2<<SPI_SCK_FUNCBIT) | (2<<SPI_MISO_FUNCBIT) |
|
||||||
|
(2<<SPI_MOSI_FUNCBIT) );
|
||||||
|
// enable SPI-Master - slowest speed
|
||||||
|
SSPCR0 = ((8-1)<<0) | (0<<CPOL) | (0x20<<SCR);
|
||||||
|
SSPCR1 = (1<<SSE);
|
||||||
|
// low speed during init
|
||||||
|
SSPCPSR=254;
|
||||||
|
|
||||||
|
// Send 20 spi commands with card not selected
|
||||||
|
for(int i=0;i<20;i++)
|
||||||
|
{
|
||||||
|
while( !(SSPSR & (1<<TNF)) ) ; //Wait
|
||||||
|
SSPDR=0xff;
|
||||||
|
while( !(SSPSR & (1<<RNE)) ) ; //Wait
|
||||||
|
incoming=SSPDR;
|
||||||
|
(void)incoming;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Send and receive one byte through SPI
|
||||||
|
*/
|
||||||
|
static unsigned char spi_1_send(unsigned char outgoing)
|
||||||
|
{
|
||||||
|
while(!(SSPSR & (1<<TNF))) ;
|
||||||
|
SSPDR=outgoing;
|
||||||
|
while(!(SSPSR & (1<<RNE))) ;
|
||||||
|
return SSPDR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Used for debugging, print 8 bit error code from SD card
|
||||||
|
*/
|
||||||
|
static void print_error_code(unsigned char value)
|
||||||
|
{
|
||||||
|
switch(value)
|
||||||
|
{
|
||||||
|
case 0x40:
|
||||||
|
DBGERR("Parameter error\n");
|
||||||
|
break;
|
||||||
|
case 0x20:
|
||||||
|
DBGERR("Address error\n");
|
||||||
|
break;
|
||||||
|
case 0x10:
|
||||||
|
DBGERR("Erase sequence error\n");
|
||||||
|
break;
|
||||||
|
case 0x08:
|
||||||
|
DBGERR("CRC error\n");
|
||||||
|
break;
|
||||||
|
case 0x04:
|
||||||
|
DBGERR("Illegal command\n");
|
||||||
|
break;
|
||||||
|
case 0x02:
|
||||||
|
DBGERR("Erase reset\n");
|
||||||
|
break;
|
||||||
|
case 0x01:
|
||||||
|
DBGERR("Card is initializing\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DBGERR("Unknown error 0x%x\n",value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Return 1 if card is OK, otherwise print 16 bit error code from SD card
|
||||||
|
*/
|
||||||
|
static char sd_status()
|
||||||
|
{
|
||||||
|
short value=send_cmd(CMD13,0);
|
||||||
|
value<<=8;
|
||||||
|
value|=spi_1_send(0xff);
|
||||||
|
|
||||||
|
switch(value)
|
||||||
|
{
|
||||||
|
case 0x0000:
|
||||||
|
return 1;
|
||||||
|
case 0x0001:
|
||||||
|
DBGERR("Card is Locked\n");
|
||||||
|
/*//Try to fix the problem by erasing all the SD card.
|
||||||
|
char e=send_cmd(CMD16,1);
|
||||||
|
if(e!=0) print_error_code(e);
|
||||||
|
e=send_cmd(CMD42,0);
|
||||||
|
if(e!=0) print_error_code(e);
|
||||||
|
spi_1_send(0xfe); // Start block
|
||||||
|
spi_1_send(1<<3); //Erase all disk command
|
||||||
|
spi_1_send(0xff); // Checksum part 1
|
||||||
|
spi_1_send(0xff); // Checksum part 2
|
||||||
|
e=spi_1_send(0xff);
|
||||||
|
iprintf("Reached here 0x%x\n",e);//Should return 0xe5
|
||||||
|
while(spi_1_send(0xff)!=0xff);*/
|
||||||
|
break;
|
||||||
|
case 0x0002:
|
||||||
|
DBGERR("WP erase skip or lock/unlock cmd failed\n");
|
||||||
|
break;
|
||||||
|
case 0x0004:
|
||||||
|
DBGERR("General error\n");
|
||||||
|
break;
|
||||||
|
case 0x0008:
|
||||||
|
DBGERR("Internal card controller error\n");
|
||||||
|
break;
|
||||||
|
case 0x0010:
|
||||||
|
DBGERR("Card ECC failed\n");
|
||||||
|
break;
|
||||||
|
case 0x0020:
|
||||||
|
DBGERR("Write protect violation\n");
|
||||||
|
break;
|
||||||
|
case 0x0040:
|
||||||
|
DBGERR("Invalid selection for erase\n");
|
||||||
|
break;
|
||||||
|
case 0x0080:
|
||||||
|
DBGERR("Out of range or CSD overwrite\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if(value>0x00FF)
|
||||||
|
print_error_code((unsigned char)(value>>8));
|
||||||
|
else
|
||||||
|
DBGERR("Unknown error 0x%x\n",value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Wait until card is ready
|
||||||
|
*/
|
||||||
|
static unsigned char wait_ready()
|
||||||
|
{
|
||||||
|
unsigned char result;
|
||||||
|
spi_1_send(0xff);
|
||||||
|
for(int i=0;i<460800;i++)//Timeout ~500ms
|
||||||
|
{
|
||||||
|
result=spi_1_send(0xff);
|
||||||
|
if(result==0xff) return 0xff;
|
||||||
|
if(i%500==0) DBG("*\n");
|
||||||
|
}
|
||||||
|
DBGERR("Error: wait_ready()\n");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Send a command to the SD card
|
||||||
|
* \param cmd one among the #define'd commands
|
||||||
|
* \param arg command's argument
|
||||||
|
* \return command's r1 response. If command returns a longer response, the user
|
||||||
|
* can continue reading the response with spi_1_send(0xff)
|
||||||
|
*/
|
||||||
|
static unsigned char send_cmd(unsigned char cmd, unsigned int arg)
|
||||||
|
{
|
||||||
|
unsigned char n, res;
|
||||||
|
if(cmd & 0x80)
|
||||||
|
{ // ACMD<n> is the command sequence of CMD55-CMD<n>
|
||||||
|
cmd&=0x7f;
|
||||||
|
res=send_cmd(CMD55,0);
|
||||||
|
if(res>1) return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select the card and wait for ready
|
||||||
|
CS_HIGH();
|
||||||
|
CS_LOW();
|
||||||
|
|
||||||
|
if(cmd==CMD0)
|
||||||
|
{
|
||||||
|
//wait_ready on CMD0 seems to cause infinite loop
|
||||||
|
spi_1_send(0xff);
|
||||||
|
} else {
|
||||||
|
if(wait_ready()!=0xff) return 0xff;
|
||||||
|
}
|
||||||
|
// Send command
|
||||||
|
spi_1_send(cmd); // Start + Command index
|
||||||
|
spi_1_send((unsigned char)(arg >> 24)); // Argument[31..24]
|
||||||
|
spi_1_send((unsigned char)(arg >> 16)); // Argument[23..16]
|
||||||
|
spi_1_send((unsigned char)(arg >> 8)); // Argument[15..8]
|
||||||
|
spi_1_send((unsigned char)arg); // Argument[7..0]
|
||||||
|
n=0x01; // Dummy CRC + Stop
|
||||||
|
if (cmd==CMD0) n=0x95; // Valid CRC for CMD0(0)
|
||||||
|
if (cmd==CMD8) n=0x87; // Valid CRC for CMD8(0x1AA)
|
||||||
|
spi_1_send(n);
|
||||||
|
// Receive response
|
||||||
|
if (cmd==CMD12) spi_1_send(0xff); // Skip a stuff byte when stop reading
|
||||||
|
n=10; // Wait response, try 10 times
|
||||||
|
do
|
||||||
|
res=spi_1_send(0xff);
|
||||||
|
while ((res & 0x80) && --n);
|
||||||
|
return res; // Return with the response value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Receive a data packet from the SD card
|
||||||
|
* \param buf data buffer to store received data
|
||||||
|
* \param byte count (must be multiple of 4)
|
||||||
|
* \return true on success, false on failure
|
||||||
|
*/
|
||||||
|
static bool rx_datablock(unsigned char *buf, unsigned int btr)
|
||||||
|
{
|
||||||
|
unsigned char token;
|
||||||
|
for(int i=0;i<0xffff;i++)
|
||||||
|
{
|
||||||
|
token=spi_1_send(0xff);
|
||||||
|
if(token!=0xff) break;
|
||||||
|
}
|
||||||
|
if(token!=0xfe) return false; // If not valid data token, retutn error
|
||||||
|
|
||||||
|
do { // Receive the data block into buffer
|
||||||
|
*buf=spi_1_send(0xff); buf++;
|
||||||
|
*buf=spi_1_send(0xff); buf++;
|
||||||
|
*buf=spi_1_send(0xff); buf++;
|
||||||
|
*buf=spi_1_send(0xff); buf++;
|
||||||
|
} while(btr-=4);
|
||||||
|
spi_1_send(0xff); // Discard CRC
|
||||||
|
spi_1_send(0xff);
|
||||||
|
|
||||||
|
return true; // Return success
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Send a data packet to the SD card
|
||||||
|
* \param buf 512 byte data block to be transmitted
|
||||||
|
* \param token data start/stop token
|
||||||
|
* \return true on success, false on failure
|
||||||
|
*/
|
||||||
|
static bool tx_datablock (const unsigned char *buf, unsigned char token)
|
||||||
|
{
|
||||||
|
unsigned char resp;
|
||||||
|
if(wait_ready()!=0xff) return false;
|
||||||
|
|
||||||
|
spi_1_send(token); // Xmit data token
|
||||||
|
if (token!=0xfd)
|
||||||
|
{ // Is data token
|
||||||
|
for(int i=0;i<256;i++)
|
||||||
|
{ // Xmit the 512 byte data block
|
||||||
|
spi_1_send(*buf); buf++;
|
||||||
|
spi_1_send(*buf); buf++;
|
||||||
|
}
|
||||||
|
spi_1_send(0xff); // CRC (Dummy)
|
||||||
|
spi_1_send(0xff);
|
||||||
|
resp=spi_1_send(0xff); // Receive data response
|
||||||
|
if((resp & 0x1f)!=0x05) // If not accepted, return error
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// class SPISDDriver
|
||||||
|
//
|
||||||
|
|
||||||
|
intrusive_ref_ptr<SPISDDriver> SPISDDriver::instance()
|
||||||
|
{
|
||||||
|
static FastMutex m;
|
||||||
|
static intrusive_ref_ptr<SPISDDriver> instance;
|
||||||
|
Lock<FastMutex> l(m);
|
||||||
|
if(!instance) instance=new SPISDDriver();
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t SPISDDriver::readBlock(void* buffer, size_t size, off_t where)
|
||||||
|
{
|
||||||
|
if(where % 512 || size % 512) return -EFAULT;
|
||||||
|
unsigned int lba=where/512;
|
||||||
|
unsigned int nSectors=size/512;
|
||||||
|
unsigned char *buf=reinterpret_cast<unsigned char*>(buffer);
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
DBG("SPISDDriver::readBlock(): nSectors=%d\n",nSectors);
|
||||||
|
if(!(cardType & 8)) lba*=512; // Convert to byte address if needed
|
||||||
|
unsigned char result;
|
||||||
|
if(nSectors==1)
|
||||||
|
{ // Single block read
|
||||||
|
result=send_cmd(CMD17,lba); // READ_SINGLE_BLOCK
|
||||||
|
if(result!=0)
|
||||||
|
{
|
||||||
|
print_error_code(result);
|
||||||
|
CS_HIGH();
|
||||||
|
return -EBADF;
|
||||||
|
}
|
||||||
|
if(rx_datablock(buf,512)==false)
|
||||||
|
{
|
||||||
|
DBGERR("Block read error\n");
|
||||||
|
CS_HIGH();
|
||||||
|
return -EBADF;
|
||||||
|
}
|
||||||
|
} else { // Multiple block read
|
||||||
|
//DBGERR("Mbr\n");//debug only
|
||||||
|
result=send_cmd(CMD18,lba); // READ_MULTIPLE_BLOCK
|
||||||
|
if(result!=0)
|
||||||
|
{
|
||||||
|
print_error_code(result);
|
||||||
|
CS_HIGH();
|
||||||
|
return -EBADF;
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
if(!rx_datablock(buf,512)) break;
|
||||||
|
buf+=512;
|
||||||
|
} while(--nSectors);
|
||||||
|
send_cmd(CMD12,0); // STOP_TRANSMISSION
|
||||||
|
if(nSectors!=0)
|
||||||
|
{
|
||||||
|
DBGERR("Multiple block read error\n");
|
||||||
|
CS_HIGH();
|
||||||
|
return -EBADF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CS_HIGH();
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t SPISDDriver::writeBlock(const void* buffer, size_t size, off_t where)
|
||||||
|
{
|
||||||
|
if(where % 512 || size % 512) return -EFAULT;
|
||||||
|
unsigned int lba=where/512;
|
||||||
|
unsigned int nSectors=size/512;
|
||||||
|
const unsigned char *buf=reinterpret_cast<const unsigned char*>(buffer);
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
DBG("SPISDDriver::writeBlock(): nSectors=%d\n",nSectors);
|
||||||
|
if(!(cardType & 8)) lba*=512; // Convert to byte address if needed
|
||||||
|
unsigned char result;
|
||||||
|
if(nSectors==1)
|
||||||
|
{ // Single block write
|
||||||
|
result=send_cmd(CMD24,lba); // WRITE_BLOCK
|
||||||
|
if(result!=0)
|
||||||
|
{
|
||||||
|
print_error_code(result);
|
||||||
|
CS_HIGH();
|
||||||
|
return -EBADF;
|
||||||
|
}
|
||||||
|
if(tx_datablock(buf,0xfe)==false) // WRITE_BLOCK
|
||||||
|
{
|
||||||
|
DBGERR("Block write error\n");
|
||||||
|
CS_HIGH();
|
||||||
|
return -EBADF;
|
||||||
|
}
|
||||||
|
} else { // Multiple block write
|
||||||
|
//DBGERR("Mbw\n");//debug only
|
||||||
|
if(cardType & 6) send_cmd(ACMD23,nSectors);//Only if it is SD card
|
||||||
|
result=send_cmd(CMD25,lba); // WRITE_MULTIPLE_BLOCK
|
||||||
|
if(result!=0)
|
||||||
|
{
|
||||||
|
print_error_code(result);
|
||||||
|
CS_HIGH();
|
||||||
|
return -EBADF;
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
if(!tx_datablock(buf,0xfc)) break;
|
||||||
|
buf+=512;
|
||||||
|
} while(--nSectors);
|
||||||
|
if(!tx_datablock(0,0xfd)) // STOP_TRAN token
|
||||||
|
{
|
||||||
|
DBGERR("Multiple block write error\n");
|
||||||
|
CS_HIGH();
|
||||||
|
return -EBADF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CS_HIGH();
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SPISDDriver::ioctl(int cmd, void* arg)
|
||||||
|
{
|
||||||
|
DBG("SPISDDriver::ioctl()\n");
|
||||||
|
if(cmd!=IOCTL_SYNC) return -ENOTTY;
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
CS_LOW();
|
||||||
|
unsigned char result=wait_ready();
|
||||||
|
CS_HIGH();
|
||||||
|
if(result==0xff) return 0;
|
||||||
|
else return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPISDDriver::SPISDDriver() : Device(Device::BLOCK)
|
||||||
|
{
|
||||||
|
const int MAX_RETRY=20;//Maximum command retry before failing
|
||||||
|
spi_1_init(); /* init at low speed */
|
||||||
|
unsigned char resp;
|
||||||
|
int i;
|
||||||
|
for(i=0;i<MAX_RETRY;i++)
|
||||||
|
{
|
||||||
|
resp=send_cmd(CMD0,0);
|
||||||
|
if(resp==1) break;
|
||||||
|
}
|
||||||
|
DBG("CMD0 required %d commands\n",i+1);
|
||||||
|
|
||||||
|
if(resp!=1)
|
||||||
|
{
|
||||||
|
print_error_code(resp);
|
||||||
|
DBGERR("Init failed\n");
|
||||||
|
CS_HIGH();
|
||||||
|
return; //Error
|
||||||
|
}
|
||||||
|
unsigned char n, cmd, ty=0, ocr[4];
|
||||||
|
// Enter Idle state
|
||||||
|
if(send_cmd(CMD8,0x1aa)==1)
|
||||||
|
{ /* SDHC */
|
||||||
|
for(n=0;n<4;n++) ocr[n]=spi_1_send(0xff);// Get return value of R7 resp
|
||||||
|
if((ocr[2]==0x01)&&(ocr[3]==0xaa))
|
||||||
|
{ // The card can work at vdd range of 2.7-3.6V
|
||||||
|
for(i=0;i<MAX_RETRY;i++)
|
||||||
|
{
|
||||||
|
resp=send_cmd(ACMD41, 1UL << 30);
|
||||||
|
if(resp==0)
|
||||||
|
{
|
||||||
|
if(send_cmd(CMD58,0)==0)
|
||||||
|
{ // Check CCS bit in the OCR
|
||||||
|
for(n=0;n<4;n++) ocr[n]=spi_1_send(0xff);
|
||||||
|
if(ocr[0] & 0x40)
|
||||||
|
{
|
||||||
|
ty=12;
|
||||||
|
DBG("SDHC, block addressing supported\n");
|
||||||
|
} else {
|
||||||
|
ty=4;
|
||||||
|
DBG("SDHC, no block addressing\n");
|
||||||
|
}
|
||||||
|
} else DBGERR("CMD58 failed\n");
|
||||||
|
|
||||||
|
break; //Exit from for
|
||||||
|
} else print_error_code(resp);
|
||||||
|
}
|
||||||
|
DBG("ACMD41 required %d commands\n",i+1);
|
||||||
|
} else DBGERR("CMD8 failed\n");
|
||||||
|
} else { /* SDSC or MMC */
|
||||||
|
if(send_cmd(ACMD41,0)<=1)
|
||||||
|
{
|
||||||
|
ty=2;
|
||||||
|
cmd=ACMD41; /* SDSC */
|
||||||
|
DBG("SD card\n");
|
||||||
|
} else {
|
||||||
|
ty=1;
|
||||||
|
cmd=CMD1; /* MMC */
|
||||||
|
DBG("MMC card\n");
|
||||||
|
}
|
||||||
|
for(i=0;i<MAX_RETRY;i++)
|
||||||
|
{
|
||||||
|
resp=send_cmd(cmd,0);
|
||||||
|
if(resp==0)
|
||||||
|
{
|
||||||
|
if(send_cmd(CMD16,512)!=0)
|
||||||
|
{
|
||||||
|
ty=0;
|
||||||
|
DBGERR("CMD16 failed\n");
|
||||||
|
}
|
||||||
|
break; //Exit from for
|
||||||
|
} else print_error_code(resp);
|
||||||
|
}
|
||||||
|
DBG("CMD required %d commands\n",i+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ty==0)
|
||||||
|
{
|
||||||
|
CS_HIGH();
|
||||||
|
return; //Error
|
||||||
|
}
|
||||||
|
cardType=ty;
|
||||||
|
|
||||||
|
if(sd_status()<0)
|
||||||
|
{
|
||||||
|
DBGERR("Status error\n");
|
||||||
|
CS_HIGH();
|
||||||
|
return; //Error
|
||||||
|
}
|
||||||
|
|
||||||
|
CS_HIGH();
|
||||||
|
//Configure the SPI interface to use the 7.4MHz high speed mode
|
||||||
|
SSPCR0=((8-1)<<0) | (0<<CPOL);
|
||||||
|
SSPCPSR=SPI_PRESCALE_MIN;
|
||||||
|
|
||||||
|
DBG("Init done...\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2014 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef SD_LPC2000_H
|
||||||
|
#define SD_LPC2000_H
|
||||||
|
|
||||||
|
#include "kernel/sync.h"
|
||||||
|
#include "filesystem/devfs/devfs.h"
|
||||||
|
#include "filesystem/ioctl.h"
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Driver for interfacing to an SD card through SPI
|
||||||
|
*/
|
||||||
|
class SPISDDriver : public Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \return an instance to this class, singleton
|
||||||
|
*/
|
||||||
|
static intrusive_ref_ptr<SPISDDriver> instance();
|
||||||
|
|
||||||
|
virtual ssize_t readBlock(void *buffer, size_t size, off_t where);
|
||||||
|
|
||||||
|
virtual ssize_t writeBlock(const void *buffer, size_t size, off_t where);
|
||||||
|
|
||||||
|
virtual int ioctl(int cmd, void *arg);
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
SPISDDriver();
|
||||||
|
|
||||||
|
FastMutex mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //SD_LPC2000_H
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,64 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2014 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef SD_STM32F1_H
|
||||||
|
#define SD_STM32F1_H
|
||||||
|
|
||||||
|
#include "kernel/sync.h"
|
||||||
|
#include "filesystem/devfs/devfs.h"
|
||||||
|
#include "filesystem/ioctl.h"
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Driver for the SDIO peripheral in STM32F1 microcontrollers
|
||||||
|
*/
|
||||||
|
class SDIODriver : public Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \return an instance to this class, singleton
|
||||||
|
*/
|
||||||
|
static intrusive_ref_ptr<SDIODriver> instance();
|
||||||
|
|
||||||
|
virtual ssize_t readBlock(void *buffer, size_t size, off_t where);
|
||||||
|
|
||||||
|
virtual ssize_t writeBlock(const void *buffer, size_t size, off_t where);
|
||||||
|
|
||||||
|
virtual int ioctl(int cmd, void *arg);
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
SDIODriver();
|
||||||
|
|
||||||
|
FastMutex mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //SD_STM32F1_H
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,64 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2014 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef SD_STM32F2_F4_H
|
||||||
|
#define SD_STM32F2_F4_H
|
||||||
|
|
||||||
|
#include "kernel/sync.h"
|
||||||
|
#include "filesystem/devfs/devfs.h"
|
||||||
|
#include "filesystem/ioctl.h"
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Driver for the SDIO peripheral in STM32F2 and F4 microcontrollers
|
||||||
|
*/
|
||||||
|
class SDIODriver : public Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \return an instance to this class, singleton
|
||||||
|
*/
|
||||||
|
static intrusive_ref_ptr<SDIODriver> instance();
|
||||||
|
|
||||||
|
virtual ssize_t readBlock(void *buffer, size_t size, off_t where);
|
||||||
|
|
||||||
|
virtual ssize_t writeBlock(const void *buffer, size_t size, off_t where);
|
||||||
|
|
||||||
|
virtual int ioctl(int cmd, void *arg);
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
SDIODriver();
|
||||||
|
|
||||||
|
FastMutex mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //SD_STM32F2_F4_H
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
|
||||||
|
//Serial code is common for all the cortex M cores, so it has been put here
|
||||||
|
|
||||||
|
#ifdef _ARCH_ARM7_LPC2000
|
||||||
|
#include "serial_lpc2000.h"
|
||||||
|
#elif defined(_ARCH_CORTEXM0_STM32) || defined(_ARCH_CORTEXM3_STM32) \
|
||||||
|
|| defined(_ARCH_CORTEXM4_STM32F4) || defined(_ARCH_CORTEXM3_STM32F2) \
|
||||||
|
|| defined(_ARCH_CORTEXM3_STM32L1) || defined(_ARCH_CORTEXM7_STM32F7) \
|
||||||
|
|| defined(_ARCH_CORTEXM7_STM32H7) || defined(_ARCH_CORTEXM4_STM32F3) \
|
||||||
|
|| defined(_ARCH_CORTEXM4_STM32L4)
|
||||||
|
#include "serial_stm32.h"
|
||||||
|
#elif defined(_ARCH_CORTEXM3_EFM32GG)
|
||||||
|
#include "serial_efm32.h"
|
||||||
|
#elif defined(_ARCH_CORTEXM4_ATSAM4L)
|
||||||
|
#include "serial_atsam4l.h"
|
||||||
|
#else
|
||||||
|
#error "Unknown arch"
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,245 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2020 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <cstring>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include "serial_atsam4l.h"
|
||||||
|
#include "kernel/sync.h"
|
||||||
|
#include "kernel/scheduler/scheduler.h"
|
||||||
|
#include "interfaces/portability.h"
|
||||||
|
#include "filesystem/ioctl.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace miosix;
|
||||||
|
|
||||||
|
static const int numPorts=4; //USART0 to USART3
|
||||||
|
|
||||||
|
/// Pointer to serial port classes to let interrupts access the classes
|
||||||
|
static ATSAMSerial *ports[numPorts]={0};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal interrupt routine for usart2 rx actual implementation
|
||||||
|
*/
|
||||||
|
void __attribute__((noinline)) usart2rxIrqImpl()
|
||||||
|
{
|
||||||
|
if(ports[2]) ports[2]->IRQhandleInterrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal interrupt routine for usart2 rx
|
||||||
|
*/
|
||||||
|
void __attribute__((naked)) USART2_Handler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
asm volatile("bl _Z15usart2rxIrqImplv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
//
|
||||||
|
// class ATSAMSerial
|
||||||
|
//
|
||||||
|
|
||||||
|
// A note on the baudrate/500: the buffer is selected so as to withstand
|
||||||
|
// 20ms of full data rate. In the 8N1 format one char is made of 10 bits.
|
||||||
|
// So (baudrate/10)*0.02=baudrate/500
|
||||||
|
ATSAMSerial::ATSAMSerial(int id, int baudrate)
|
||||||
|
: Device(Device::TTY), rxQueue(rxQueueMin+baudrate/500), rxWaiting(0),
|
||||||
|
idle(true), portId(id)
|
||||||
|
{
|
||||||
|
if(id!=2 || ports[portId]) errorHandler(UNEXPECTED);
|
||||||
|
|
||||||
|
{
|
||||||
|
InterruptDisableLock dLock;
|
||||||
|
ports[portId]=this;
|
||||||
|
|
||||||
|
port=USART2;
|
||||||
|
|
||||||
|
//TODO: USART2 hardcoded
|
||||||
|
PM->PM_UNLOCK=0xaa<<24 | PM_PBAMASK_OFFSET;
|
||||||
|
PM->PM_PBAMASK |= PM_PBAMASK_USART2;
|
||||||
|
NVIC_SetPriority(USART2_IRQn,15);//Lowest priority for serial
|
||||||
|
NVIC_EnableIRQ(USART2_IRQn);
|
||||||
|
}
|
||||||
|
|
||||||
|
port->US_BRGR = ((SystemCoreClock/baudrate/4)+1)/2; //TODO: fractional part
|
||||||
|
port->US_MR = US_MR_FILTER // Filter input with majority of 3 samples
|
||||||
|
| US_MR_OVER // 8 cycles oversample
|
||||||
|
| US_MR_PAR_NONE // No parity
|
||||||
|
| US_MR_CHRL_8 // 8 bit char
|
||||||
|
| US_MR_USCLKS_MCK // CLK_USART is clock source
|
||||||
|
| US_MR_MODE_NORMAL;// Just a plain usart, please
|
||||||
|
|
||||||
|
port->US_RTOR=10; //Timeout 10 bits (one char time)
|
||||||
|
port->US_IER = US_IER_RXRDY | US_IER_TIMEOUT;
|
||||||
|
|
||||||
|
port->US_CR = US_CR_TXEN | US_CR_RXEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t ATSAMSerial::readBlock(void *buffer, size_t size, off_t where)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(rxMutex);
|
||||||
|
char *buf=reinterpret_cast<char*>(buffer);
|
||||||
|
size_t result=0;
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
//Try to get data from the queue
|
||||||
|
for(;result<size;result++)
|
||||||
|
{
|
||||||
|
if(rxQueue.tryGet(buf[result])==false) break;
|
||||||
|
//This is here just not to keep IRQ disabled for the whole loop
|
||||||
|
FastInterruptEnableLock eLock(dLock);
|
||||||
|
}
|
||||||
|
if(idle && result>0) break;
|
||||||
|
if(result==size) break;
|
||||||
|
//Wait for data in the queue
|
||||||
|
do {
|
||||||
|
rxWaiting=Thread::IRQgetCurrentThread();
|
||||||
|
Thread::IRQwait();
|
||||||
|
{
|
||||||
|
FastInterruptEnableLock eLock(dLock);
|
||||||
|
Thread::yield();
|
||||||
|
}
|
||||||
|
} while(rxWaiting);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t ATSAMSerial::writeBlock(const void *buffer, size_t size, off_t where)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(txMutex);
|
||||||
|
const char *buf=reinterpret_cast<const char*>(buffer);
|
||||||
|
for(size_t i=0;i<size;i++)
|
||||||
|
{
|
||||||
|
while((port->US_CSR & US_CSR_TXRDY) == 0) ;
|
||||||
|
port->US_THR =*buf++;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATSAMSerial::IRQwrite(const char *str)
|
||||||
|
{
|
||||||
|
// We can reach here also with only kernel paused, so make sure
|
||||||
|
// interrupts are disabled.
|
||||||
|
bool interrupts=areInterruptsEnabled();
|
||||||
|
if(interrupts) fastDisableInterrupts();
|
||||||
|
while(*str)
|
||||||
|
{
|
||||||
|
while((port->US_CSR & US_CSR_TXRDY) == 0) ;
|
||||||
|
port->US_THR = *str++;
|
||||||
|
}
|
||||||
|
waitSerialTxFifoEmpty();
|
||||||
|
if(interrupts) fastEnableInterrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ATSAMSerial::ioctl(int cmd, void *arg)
|
||||||
|
{
|
||||||
|
if(reinterpret_cast<unsigned>(arg) & 0b11) return -EFAULT; //Unaligned
|
||||||
|
termios *t=reinterpret_cast<termios*>(arg);
|
||||||
|
switch(cmd)
|
||||||
|
{
|
||||||
|
case IOCTL_SYNC:
|
||||||
|
waitSerialTxFifoEmpty();
|
||||||
|
return 0;
|
||||||
|
case IOCTL_TCGETATTR:
|
||||||
|
t->c_iflag=IGNBRK | IGNPAR;
|
||||||
|
t->c_oflag=0;
|
||||||
|
t->c_cflag=CS8;
|
||||||
|
t->c_lflag=0;
|
||||||
|
return 0;
|
||||||
|
case IOCTL_TCSETATTR_NOW:
|
||||||
|
case IOCTL_TCSETATTR_DRAIN:
|
||||||
|
case IOCTL_TCSETATTR_FLUSH:
|
||||||
|
//Changing things at runtime unsupported, so do nothing, but don't
|
||||||
|
//return error as console_device.h implements some attribute changes
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return -ENOTTY; //Means the operation does not apply to this descriptor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATSAMSerial::IRQhandleInterrupt()
|
||||||
|
{
|
||||||
|
bool wake=false;
|
||||||
|
unsigned int status=port->US_CSR;
|
||||||
|
|
||||||
|
if(status & US_CSR_RXRDY)
|
||||||
|
{
|
||||||
|
wake=true;
|
||||||
|
//Always read the char (resets flags), but put it in the queue only if
|
||||||
|
//no framing error
|
||||||
|
char c=port->US_RHR;
|
||||||
|
if((status & US_CSR_FRAME) == 0)
|
||||||
|
{
|
||||||
|
if(rxQueue.tryPut(c & 0xff)==false) /*fifo overflow*/;
|
||||||
|
idle=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(status & US_CSR_TIMEOUT)
|
||||||
|
{
|
||||||
|
wake=true;
|
||||||
|
port->US_CR=US_CR_STTTO;
|
||||||
|
idle=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(wake && rxWaiting)
|
||||||
|
{
|
||||||
|
rxWaiting->IRQwakeup();
|
||||||
|
if(rxWaiting->IRQgetPriority()>
|
||||||
|
Thread::IRQgetCurrentThread()->IRQgetPriority())
|
||||||
|
Scheduler::IRQfindNextThread();
|
||||||
|
rxWaiting=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ATSAMSerial::~ATSAMSerial()
|
||||||
|
{
|
||||||
|
waitSerialTxFifoEmpty();
|
||||||
|
|
||||||
|
port->US_CR = US_CR_TXDIS | US_CR_RXDIS;
|
||||||
|
port->US_IDR = US_IDR_RXRDY | US_IDR_TIMEOUT;
|
||||||
|
|
||||||
|
InterruptDisableLock dLock;
|
||||||
|
ports[portId]=nullptr;
|
||||||
|
|
||||||
|
//TODO: USART2 hardcoded
|
||||||
|
NVIC_DisableIRQ(USART2_IRQn);
|
||||||
|
NVIC_ClearPendingIRQ(USART2_IRQn);
|
||||||
|
PM->PM_UNLOCK=0xaa<<24 | PM_PBAMASK_OFFSET;
|
||||||
|
PM->PM_PBAMASK &= ~PM_PBAMASK_USART2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATSAMSerial::waitSerialTxFifoEmpty()
|
||||||
|
{
|
||||||
|
while((port->US_CSR & US_CSR_TXEMPTY) == 0) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2020 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "filesystem/console/console_device.h"
|
||||||
|
#include "kernel/sync.h"
|
||||||
|
#include "kernel/queue.h"
|
||||||
|
#include "board_settings.h"
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serial port class for ATSAM4L microcontrollers.
|
||||||
|
*
|
||||||
|
* This is a simple implementation with the following limitations:
|
||||||
|
* -only supports USART2
|
||||||
|
* -no DMA, TX uses polling, while RX is interrupt-based
|
||||||
|
*
|
||||||
|
* Classes of this type are reference counted, must be allocated on the heap
|
||||||
|
* and managed through intrusive_ref_ptr<FileBase>
|
||||||
|
*/
|
||||||
|
class ATSAMSerial : public Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor, initializes the serial port.
|
||||||
|
* Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when
|
||||||
|
* attempting to construct multiple objects with the same id. That is,
|
||||||
|
* it is possible to instantiate only one instance of this class for each
|
||||||
|
* hardware USART.
|
||||||
|
* \param id a number to select the USART
|
||||||
|
* \param baudrate serial port baudrate
|
||||||
|
*/
|
||||||
|
ATSAMSerial(int id, int baudrate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a block of data
|
||||||
|
* \param buffer buffer where read data will be stored
|
||||||
|
* \param size buffer size
|
||||||
|
* \param where where to read from
|
||||||
|
* \return number of bytes read or a negative number on failure. Note that
|
||||||
|
* it is normal for this function to return less character than the amount
|
||||||
|
* asked
|
||||||
|
*/
|
||||||
|
ssize_t readBlock(void *buffer, size_t size, off_t where);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a block of data
|
||||||
|
* \param buffer buffer where take data to write
|
||||||
|
* \param size buffer size
|
||||||
|
* \param where where to write to
|
||||||
|
* \return number of bytes written or a negative number on failure
|
||||||
|
*/
|
||||||
|
ssize_t writeBlock(const void *buffer, size_t size, off_t where);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a string.
|
||||||
|
* An extension to the Device interface that adds a new member function,
|
||||||
|
* which is used by the kernel on console devices to write debug information
|
||||||
|
* before the kernel is started or in case of serious errors, right before
|
||||||
|
* rebooting.
|
||||||
|
* Can ONLY be called when the kernel is not yet started, paused or within
|
||||||
|
* an interrupt. This default implementation ignores writes.
|
||||||
|
* \param str the string to write. The string must be NUL terminated.
|
||||||
|
*/
|
||||||
|
void IRQwrite(const char *str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs device-specific operations
|
||||||
|
* \param cmd specifies the operation to perform
|
||||||
|
* \param arg optional argument that some operation require
|
||||||
|
* \return the exact return value depends on CMD, -1 is returned on error
|
||||||
|
*/
|
||||||
|
int ioctl(int cmd, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal the serial port interrupts call this member function.
|
||||||
|
* Never call this from user code.
|
||||||
|
*/
|
||||||
|
void IRQhandleInterrupt();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return port id, 0 for USART0, ...
|
||||||
|
*/
|
||||||
|
int getId() const { return portId; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor
|
||||||
|
*/
|
||||||
|
~ATSAMSerial();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Wait until all characters have been written to the serial port
|
||||||
|
*/
|
||||||
|
void waitSerialTxFifoEmpty();
|
||||||
|
|
||||||
|
FastMutex txMutex; ///< Mutex locked during transmission
|
||||||
|
FastMutex rxMutex; ///< Mutex locked during reception
|
||||||
|
|
||||||
|
DynUnsyncQueue<char> rxQueue; ///< Receiving queue
|
||||||
|
static const unsigned int rxQueueMin=1; ///< Minimum queue size
|
||||||
|
Thread *rxWaiting; ///< Thread waiting for rx, or 0
|
||||||
|
bool idle; ///< Receiver idle
|
||||||
|
|
||||||
|
Usart *port; ///< Pointer to USART peripheral
|
||||||
|
|
||||||
|
const unsigned char portId; ///< 0 for USART0, ...
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,279 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2015 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <cstring>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include "serial_efm32.h"
|
||||||
|
#include "kernel/sync.h"
|
||||||
|
#include "kernel/scheduler/scheduler.h"
|
||||||
|
#include "interfaces/portability.h"
|
||||||
|
#include "interfaces/gpio.h"
|
||||||
|
#include "filesystem/ioctl.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace miosix;
|
||||||
|
|
||||||
|
static const int numPorts=1; //Supporting only USART0 for now
|
||||||
|
|
||||||
|
//Hope GPIO mapping doesn't change among EFM32 microcontrollers, otherwise
|
||||||
|
//we'll fix that with #ifdefs
|
||||||
|
typedef Gpio<GPIOE_BASE,10> u0tx;
|
||||||
|
typedef Gpio<GPIOE_BASE,11> u0rx;
|
||||||
|
|
||||||
|
/// Pointer to serial port classes to let interrupts access the classes
|
||||||
|
static EFM32Serial *ports[numPorts]={0};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal interrupt routine for usart0 rx actual implementation
|
||||||
|
*/
|
||||||
|
void __attribute__((noinline)) usart0rxIrqImpl()
|
||||||
|
{
|
||||||
|
if(ports[0]) ports[0]->IRQhandleInterrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal interrupt routine for usart0 rx
|
||||||
|
*/
|
||||||
|
void __attribute__((naked)) USART0_RX_IRQHandler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
asm volatile("bl _Z15usart0rxIrqImplv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
//
|
||||||
|
// class EFM32Serial
|
||||||
|
//
|
||||||
|
|
||||||
|
// A note on the baudrate/500: the buffer is selected so as to withstand
|
||||||
|
// 20ms of full data rate. In the 8N1 format one char is made of 10 bits.
|
||||||
|
// So (baudrate/10)*0.02=baudrate/500
|
||||||
|
EFM32Serial::EFM32Serial(int id, int baudrate)
|
||||||
|
: Device(Device::TTY), rxQueue(rxQueueMin+baudrate/500), rxWaiting(0),
|
||||||
|
portId(id), baudrate(baudrate)
|
||||||
|
{
|
||||||
|
if(id<0 || id>=numPorts || ports[id]!=0) errorHandler(UNEXPECTED);
|
||||||
|
|
||||||
|
{
|
||||||
|
InterruptDisableLock dLock;
|
||||||
|
ports[id]=this;
|
||||||
|
|
||||||
|
switch(id)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
u0tx::mode(Mode::OUTPUT_HIGH);
|
||||||
|
u0rx::mode(Mode::INPUT_PULL_UP_FILTER);
|
||||||
|
|
||||||
|
CMU->HFPERCLKEN0|=CMU_HFPERCLKEN0_USART0;
|
||||||
|
port=USART0;
|
||||||
|
|
||||||
|
port->IEN=USART_IEN_RXDATAV;
|
||||||
|
port->IRCTRL=0; //USART0 also has IrDA mode
|
||||||
|
|
||||||
|
NVIC_SetPriority(USART0_RX_IRQn,15);//Lowest priority for serial
|
||||||
|
NVIC_EnableIRQ(USART0_RX_IRQn);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
port->CTRL=USART_CTRL_TXBIL_HALFFULL; //Use the buffer more efficiently
|
||||||
|
port->FRAME=USART_FRAME_STOPBITS_ONE
|
||||||
|
| USART_FRAME_PARITY_NONE
|
||||||
|
| USART_FRAME_DATABITS_EIGHT;
|
||||||
|
port->TRIGCTRL=0;
|
||||||
|
port->INPUT=0;
|
||||||
|
port->I2SCTRL=0;
|
||||||
|
port->ROUTE=USART_ROUTE_LOCATION_LOC0 //Default location
|
||||||
|
| USART_ROUTE_TXPEN //Enable TX pin
|
||||||
|
| USART_ROUTE_RXPEN; //Enable RX pin
|
||||||
|
unsigned int periphClock=SystemHFClockGet()/(1<<(CMU->HFPERCLKDIV & 0xf));
|
||||||
|
//The number we need is periphClock/baudrate/16-1, but with two bits of
|
||||||
|
//fractional part. We divide by 2 instead of 16 to have 3 bit of fractional
|
||||||
|
//part. We use the additional fractional bit to add one to round towards
|
||||||
|
//the nearest. This gets us a little more precision. Then we subtract 8
|
||||||
|
//which is one with three fractional bits. Then we shift to fit the integer
|
||||||
|
//part in bits 20:8 and the fractional part in bits 7:6, masking away the
|
||||||
|
//third fractional bit. Easy, isn't it? Not quite.
|
||||||
|
port->CLKDIV=((((periphClock/baudrate/2)+1)-8)<<5) & 0x1fffc0;
|
||||||
|
port->CMD=USART_CMD_CLEARRX
|
||||||
|
| USART_CMD_CLEARTX
|
||||||
|
| USART_CMD_TXTRIDIS
|
||||||
|
| USART_CMD_RXBLOCKDIS
|
||||||
|
| USART_CMD_MASTEREN
|
||||||
|
| USART_CMD_TXEN
|
||||||
|
| USART_CMD_RXEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t EFM32Serial::readBlock(void *buffer, size_t size, off_t where)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(rxMutex);
|
||||||
|
char *buf=reinterpret_cast<char*>(buffer);
|
||||||
|
size_t result=0;
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
//Try to get data from the queue
|
||||||
|
for(;result<size;result++)
|
||||||
|
{
|
||||||
|
if(rxQueue.tryGet(buf[result])==false) break;
|
||||||
|
//This is here just not to keep IRQ disabled for the whole loop
|
||||||
|
FastInterruptEnableLock eLock(dLock);
|
||||||
|
}
|
||||||
|
//Don't block if we have at least one char
|
||||||
|
//This is required for \n detection
|
||||||
|
if(result>0) break;
|
||||||
|
//Wait for data in the queue
|
||||||
|
do {
|
||||||
|
rxWaiting=Thread::IRQgetCurrentThread();
|
||||||
|
Thread::IRQwait();
|
||||||
|
{
|
||||||
|
FastInterruptEnableLock eLock(dLock);
|
||||||
|
Thread::yield();
|
||||||
|
}
|
||||||
|
} while(rxWaiting);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t EFM32Serial::writeBlock(const void *buffer, size_t size, off_t where)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(txMutex);
|
||||||
|
const char *buf=reinterpret_cast<const char*>(buffer);
|
||||||
|
for(size_t i=0;i<size;i++)
|
||||||
|
{
|
||||||
|
while((port->STATUS & USART_STATUS_TXBL)==0) ;
|
||||||
|
port->TXDATA=*buf++;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EFM32Serial::IRQwrite(const char *str)
|
||||||
|
{
|
||||||
|
// We can reach here also with only kernel paused, so make sure
|
||||||
|
// interrupts are disabled.
|
||||||
|
bool interrupts=areInterruptsEnabled();
|
||||||
|
if(interrupts) fastDisableInterrupts();
|
||||||
|
while(*str)
|
||||||
|
{
|
||||||
|
while((port->STATUS & USART_STATUS_TXBL)==0) ;
|
||||||
|
port->TXDATA=*str++;
|
||||||
|
}
|
||||||
|
waitSerialTxFifoEmpty();
|
||||||
|
if(interrupts) fastEnableInterrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
int EFM32Serial::ioctl(int cmd, void* arg)
|
||||||
|
{
|
||||||
|
if(reinterpret_cast<unsigned>(arg) & 0b11) return -EFAULT; //Unaligned
|
||||||
|
termios *t=reinterpret_cast<termios*>(arg);
|
||||||
|
switch(cmd)
|
||||||
|
{
|
||||||
|
case IOCTL_SYNC:
|
||||||
|
waitSerialTxFifoEmpty();
|
||||||
|
return 0;
|
||||||
|
case IOCTL_TCGETATTR:
|
||||||
|
t->c_iflag=IGNBRK | IGNPAR;
|
||||||
|
t->c_oflag=0;
|
||||||
|
t->c_cflag=CS8;
|
||||||
|
t->c_lflag=0;
|
||||||
|
return 0;
|
||||||
|
case IOCTL_TCSETATTR_NOW:
|
||||||
|
case IOCTL_TCSETATTR_DRAIN:
|
||||||
|
case IOCTL_TCSETATTR_FLUSH:
|
||||||
|
//Changing things at runtime unsupported, so do nothing, but don't
|
||||||
|
//return error as console_device.h implements some attribute changes
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return -ENOTTY; //Means the operation does not apply to this descriptor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EFM32Serial::IRQhandleInterrupt()
|
||||||
|
{
|
||||||
|
bool atLeastOne=false;
|
||||||
|
while(port->STATUS & USART_STATUS_RXDATAV)
|
||||||
|
{
|
||||||
|
unsigned int c=port->RXDATAX;
|
||||||
|
if((c & (USART_RXDATAX_FERR | USART_RXDATAX_PERR))==0)
|
||||||
|
{
|
||||||
|
atLeastOne=true;
|
||||||
|
if(rxQueue.tryPut(c & 0xff)==false) /*fifo overflow*/;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(atLeastOne && rxWaiting)
|
||||||
|
{
|
||||||
|
rxWaiting->IRQwakeup();
|
||||||
|
if(rxWaiting->IRQgetPriority()>
|
||||||
|
Thread::IRQgetCurrentThread()->IRQgetPriority())
|
||||||
|
Scheduler::IRQfindNextThread();
|
||||||
|
rxWaiting=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
EFM32Serial::~EFM32Serial()
|
||||||
|
{
|
||||||
|
waitSerialTxFifoEmpty();
|
||||||
|
|
||||||
|
port->CMD=USART_CMD_TXDIS
|
||||||
|
| USART_CMD_RXDIS;
|
||||||
|
port->ROUTE=0;
|
||||||
|
|
||||||
|
InterruptDisableLock dLock;
|
||||||
|
ports[portId]=0;
|
||||||
|
switch(portId)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
NVIC_DisableIRQ(USART0_RX_IRQn);
|
||||||
|
NVIC_ClearPendingIRQ(USART0_RX_IRQn);
|
||||||
|
|
||||||
|
u0tx::mode(Mode::DISABLED);
|
||||||
|
u0rx::mode(Mode::DISABLED);
|
||||||
|
|
||||||
|
CMU->HFPERCLKEN0 &= ~CMU_HFPERCLKEN0_USART0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EFM32Serial::waitSerialTxFifoEmpty()
|
||||||
|
{
|
||||||
|
//The documentation states that the TXC bit goes to one as soon as a
|
||||||
|
//transmission is complete. However, this bit is initially zero, so if we
|
||||||
|
//call this function before transmitting, the loop will wait forever. As a
|
||||||
|
//solution, add a timeout having as value the time needed to send three
|
||||||
|
//bytes (the current one in the shift register plus the two in the buffer).
|
||||||
|
//The +1 is to produce rounding on the safe side, the 30 is the time to send
|
||||||
|
//three char through the port, including start and stop bits.
|
||||||
|
int timeout=(SystemCoreClock/baudrate+1)*30;
|
||||||
|
while(timeout-->0 && (port->STATUS & USART_STATUS_TXC)==0) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2015 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef SERIAL_EFM32_H
|
||||||
|
#define SERIAL_EFM32_H
|
||||||
|
|
||||||
|
#include "filesystem/console/console_device.h"
|
||||||
|
#include "kernel/sync.h"
|
||||||
|
#include "kernel/queue.h"
|
||||||
|
#include "board_settings.h"
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serial port class for EFM32 microcontrollers.
|
||||||
|
*
|
||||||
|
* This is a simple implementation with the following limitations:
|
||||||
|
* -only supports USART0
|
||||||
|
* -no DMA, TX uses polling, while RX is interrupt-based
|
||||||
|
*
|
||||||
|
* Classes of this type are reference counted, must be allocated on the heap
|
||||||
|
* and managed through intrusive_ref_ptr<FileBase>
|
||||||
|
*/
|
||||||
|
class EFM32Serial : public Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor, initializes the serial port.
|
||||||
|
* Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when
|
||||||
|
* attempting to construct multiple objects with the same id. That is,
|
||||||
|
* it is possible to instantiate only one instance of this class for each
|
||||||
|
* hardware USART.
|
||||||
|
* \param id a number to select the USART
|
||||||
|
* \param baudrate serial port baudrate
|
||||||
|
*/
|
||||||
|
EFM32Serial(int id, int baudrate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a block of data
|
||||||
|
* \param buffer buffer where read data will be stored
|
||||||
|
* \param size buffer size
|
||||||
|
* \param where where to read from
|
||||||
|
* \return number of bytes read or a negative number on failure. Note that
|
||||||
|
* it is normal for this function to return less character than the amount
|
||||||
|
* asked
|
||||||
|
*/
|
||||||
|
ssize_t readBlock(void *buffer, size_t size, off_t where);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a block of data
|
||||||
|
* \param buffer buffer where take data to write
|
||||||
|
* \param size buffer size
|
||||||
|
* \param where where to write to
|
||||||
|
* \return number of bytes written or a negative number on failure
|
||||||
|
*/
|
||||||
|
ssize_t writeBlock(const void *buffer, size_t size, off_t where);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a string.
|
||||||
|
* An extension to the Device interface that adds a new member function,
|
||||||
|
* which is used by the kernel on console devices to write debug information
|
||||||
|
* before the kernel is started or in case of serious errors, right before
|
||||||
|
* rebooting.
|
||||||
|
* Can ONLY be called when the kernel is not yet started, paused or within
|
||||||
|
* an interrupt. This default implementation ignores writes.
|
||||||
|
* \param str the string to write. The string must be NUL terminated.
|
||||||
|
*/
|
||||||
|
void IRQwrite(const char *str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs device-specific operations
|
||||||
|
* \param cmd specifies the operation to perform
|
||||||
|
* \param arg optional argument that some operation require
|
||||||
|
* \return the exact return value depends on CMD, -1 is returned on error
|
||||||
|
*/
|
||||||
|
int ioctl(int cmd, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal the serial port interrupts call this member function.
|
||||||
|
* Never call this from user code.
|
||||||
|
*/
|
||||||
|
void IRQhandleInterrupt();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return port id, 0 for USART0, ...
|
||||||
|
*/
|
||||||
|
int getId() const { return portId; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor
|
||||||
|
*/
|
||||||
|
~EFM32Serial();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Wait until all characters have been written to the serial port
|
||||||
|
*/
|
||||||
|
void waitSerialTxFifoEmpty();
|
||||||
|
|
||||||
|
FastMutex txMutex; ///< Mutex locked during transmission
|
||||||
|
FastMutex rxMutex; ///< Mutex locked during reception
|
||||||
|
|
||||||
|
DynUnsyncQueue<char> rxQueue; ///< Receiving queue
|
||||||
|
static const unsigned int rxQueueMin=1; ///< Minimum queue size
|
||||||
|
Thread *rxWaiting; ///< Thread waiting for rx, or 0
|
||||||
|
|
||||||
|
USART_TypeDef *port; ///< Pointer to USART peripheral
|
||||||
|
|
||||||
|
const unsigned char portId; ///< 0 for USART0, ...
|
||||||
|
int baudrate; ///< Baudrate
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //SERIAL_EFM32_H
|
||||||
|
|
@ -0,0 +1,304 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014 *
|
||||||
|
* by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include "serial_lpc2000.h"
|
||||||
|
#include "kernel/sync.h"
|
||||||
|
#include "kernel/scheduler/scheduler.h"
|
||||||
|
#include "interfaces/portability.h"
|
||||||
|
#include "filesystem/ioctl.h"
|
||||||
|
#include "LPC213x.h"
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/// Pointer to serial port classes to let interrupts access the classes
|
||||||
|
static LPC2000Serial *ports[2]={0};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal interrupt routine for usart0 actual implementation
|
||||||
|
*/
|
||||||
|
void __attribute__((noinline)) usart0irqImpl()
|
||||||
|
{
|
||||||
|
if(ports[0]) ports[0]->IRQhandleInterrupt();
|
||||||
|
VICVectAddr=0xff;//Restart VIC
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal interrupt routine for usart0
|
||||||
|
*/
|
||||||
|
void __attribute__((interrupt("IRQ"),naked)) usart0irq()
|
||||||
|
{
|
||||||
|
saveContextFromIrq();
|
||||||
|
asm volatile("bl _ZN6miosix13usart0irqImplEv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal interrupt routine for usart1 actual implementation
|
||||||
|
*/
|
||||||
|
void __attribute__((noinline)) usart1irqImpl()
|
||||||
|
{
|
||||||
|
if(ports[1]) ports[1]->IRQhandleInterrupt();
|
||||||
|
VICVectAddr=0xff;//Restart VIC
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal interrupt routine for usart1
|
||||||
|
*/
|
||||||
|
void __attribute__ ((interrupt("IRQ"),naked)) usart1irq()
|
||||||
|
{
|
||||||
|
saveContextFromIrq();
|
||||||
|
asm volatile("bl _ZN6miosix13usart1irqImplEv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// class LPC2000Serial
|
||||||
|
//
|
||||||
|
|
||||||
|
// A note on the baudrate/500: the buffer is selected so as to withstand
|
||||||
|
// 20ms of full data rate. In the 8N1 format one char is made of 10 bits.
|
||||||
|
// So (baudrate/10)*0.02=baudrate/500
|
||||||
|
LPC2000Serial::LPC2000Serial(int id, int baudrate) : Device(Device::TTY),
|
||||||
|
rxQueue(hwRxQueueLen+baudrate/500), rxWaiting(0), idle(true)
|
||||||
|
{
|
||||||
|
InterruptDisableLock dLock;
|
||||||
|
if(id<0 || id>1 || ports[id]!=0) errorHandler(UNEXPECTED);
|
||||||
|
ports[id]=this;
|
||||||
|
if(id==0)
|
||||||
|
{
|
||||||
|
serial=reinterpret_cast<Usart16550*>(0xe000c000);
|
||||||
|
PCONP|=(1<<3);//Enable UART0 peripheral
|
||||||
|
PINSEL0&=~(0xf);//Clear bits 0 to 3 of PINSEL0
|
||||||
|
PINSEL0|=0x5;//Set p0.0 as TXD and p0.1 as RXD
|
||||||
|
//Init VIC
|
||||||
|
VICSoftIntClr=(1<<6);//Clear uart0 interrupt flag (if previously set)
|
||||||
|
VICIntSelect&=~(1<<6);//uart0=IRQ
|
||||||
|
VICIntEnable=(1<<6);//uart0 interrupt ON
|
||||||
|
VICVectCntl2=0x20 | 0x6;//Slot 2 of VIC used by uart0
|
||||||
|
VICVectAddr2=reinterpret_cast<unsigned int>(&usart0irq);
|
||||||
|
} else {
|
||||||
|
serial=reinterpret_cast<Usart16550*>(0xe0010000);
|
||||||
|
PCONP|=(1<<4);//Enable UART1 peripheral
|
||||||
|
PINSEL0&=~(0xf0000);//Clear bits 16 to 19 of PINSEL0
|
||||||
|
PINSEL0|=0x50000;//Set p0.8 as TXD and p0.9 as RXD
|
||||||
|
//Init VIC
|
||||||
|
VICSoftIntClr=(1<<7);//Clear uart1 interrupt flag (if previously set)
|
||||||
|
VICIntSelect&=~(1<<7);//uart1=IRQ
|
||||||
|
VICIntEnable=(1<<7);//uart1 interrupt ON
|
||||||
|
VICVectCntl3=0x20 | 0x7;//Slot 3 of VIC used by uart1
|
||||||
|
VICVectAddr3=reinterpret_cast<unsigned int>(&usart1irq);
|
||||||
|
}
|
||||||
|
serial->LCR=0x3;//DLAB disabled
|
||||||
|
//0x07= fifo enabled, reset tx and rx hardware fifos
|
||||||
|
//0x80= uart rx fifo trigger level 8 characters
|
||||||
|
serial->FCR=0x07 | 0x80;
|
||||||
|
serial->LCR=0x83;//8data bit, 1stop, no parity, DLAB enabled
|
||||||
|
int div=TIMER_CLOCK/16/baudrate;
|
||||||
|
serial->DLL=div & 0xff;
|
||||||
|
serial->DLM=(div>>8) & 0xff;
|
||||||
|
serial->LCR=0x3;//DLAB disabled
|
||||||
|
serial->IER=0x7;//Enable RLS, RDA, CTI and THRE interrupts
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t LPC2000Serial::readBlock(void *buffer, size_t size, off_t where)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(rxMutex);
|
||||||
|
char *buf=reinterpret_cast<char*>(buffer);
|
||||||
|
size_t result=0;
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
//Try to get data from the queue
|
||||||
|
for(;result<size;result++)
|
||||||
|
{
|
||||||
|
if(rxQueue.tryGet(buf[result])==false) break;
|
||||||
|
//This is here just not to keep IRQ disabled for the whole loop
|
||||||
|
FastInterruptEnableLock eLock(dLock);
|
||||||
|
}
|
||||||
|
if(idle && result>0) break;
|
||||||
|
if(result==size) break;
|
||||||
|
//Wait for data in the queue
|
||||||
|
do {
|
||||||
|
rxWaiting=Thread::IRQgetCurrentThread();
|
||||||
|
Thread::IRQwait();
|
||||||
|
{
|
||||||
|
FastInterruptEnableLock eLock(dLock);
|
||||||
|
Thread::yield();
|
||||||
|
}
|
||||||
|
} while(rxWaiting);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t LPC2000Serial::writeBlock(const void *buffer, size_t size, off_t where)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(txMutex);
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
size_t len=size;
|
||||||
|
const char *buf=reinterpret_cast<const char*>(buffer);
|
||||||
|
while(len>0)
|
||||||
|
{
|
||||||
|
//If no data in software and hardware queue
|
||||||
|
if((serial->LSR & (1<<5)) && (txQueue.isEmpty()))
|
||||||
|
{
|
||||||
|
//Fill hardware queue first
|
||||||
|
for(int i=0;i<hwTxQueueLen;i++)
|
||||||
|
{
|
||||||
|
serial->THR=*buf++;
|
||||||
|
len--;
|
||||||
|
if(len==0) break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(txQueue.IRQput(*buf)==true)
|
||||||
|
{
|
||||||
|
buf++;
|
||||||
|
len--;
|
||||||
|
} else {
|
||||||
|
FastInterruptEnableLock eLock(dLock);
|
||||||
|
txQueue.waitUntilNotFull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LPC2000Serial::IRQwrite(const char *str)
|
||||||
|
{
|
||||||
|
while((*str)!='\0')
|
||||||
|
{
|
||||||
|
//Wait until the hardware fifo is ready to accept one char
|
||||||
|
while(!(serial->LSR & (1<<5))) ; //wait
|
||||||
|
serial->THR=*str++;
|
||||||
|
}
|
||||||
|
waitSerialTxFifoEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
int LPC2000Serial::ioctl(int cmd, void* arg)
|
||||||
|
{
|
||||||
|
if(reinterpret_cast<unsigned>(arg) & 0b11) return -EFAULT; //Unaligned
|
||||||
|
termios *t=reinterpret_cast<termios*>(arg);
|
||||||
|
switch(cmd)
|
||||||
|
{
|
||||||
|
case IOCTL_SYNC:
|
||||||
|
waitSerialTxFifoEmpty();
|
||||||
|
return 0;
|
||||||
|
case IOCTL_TCGETATTR:
|
||||||
|
t->c_iflag=IGNBRK | IGNPAR;
|
||||||
|
t->c_oflag=0;
|
||||||
|
t->c_cflag=CS8;
|
||||||
|
t->c_lflag=0;
|
||||||
|
return 0;
|
||||||
|
case IOCTL_TCSETATTR_NOW:
|
||||||
|
case IOCTL_TCSETATTR_DRAIN:
|
||||||
|
case IOCTL_TCSETATTR_FLUSH:
|
||||||
|
//Changing things at runtime unsupported, so do nothing, but don't
|
||||||
|
//return error as console_device.h implements some attribute changes
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return -ENOTTY; //Means the operation does not apply to this descriptor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LPC2000Serial::IRQhandleInterrupt()
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
bool hppw=false;
|
||||||
|
bool wakeup=false;
|
||||||
|
switch(serial->IIR & 0xf)
|
||||||
|
{
|
||||||
|
case 0x6: //RLS
|
||||||
|
c=serial->LSR;//Read LSR to clear interrupt
|
||||||
|
c=serial->RBR;//Read RBR to discard char that caused error
|
||||||
|
break;
|
||||||
|
case 0x4: //RDA
|
||||||
|
//Note: read one less char than HARDWARE_RX_QUEUE_LENGTH as the
|
||||||
|
//CTI interrupt only occurs if there's at least one character in
|
||||||
|
//the buffer, and we always want the CTI interrupt
|
||||||
|
for(int i=0;i<hwRxQueueLen-1;i++)
|
||||||
|
if(rxQueue.tryPut(serial->RBR)==false) /*fifo overflow*/;
|
||||||
|
wakeup=true;
|
||||||
|
idle=false;
|
||||||
|
break;
|
||||||
|
case 0xc: //CTI
|
||||||
|
while(serial->LSR & (1<<0))
|
||||||
|
if(rxQueue.tryPut(serial->RBR)==false) /*fifo overflow*/;
|
||||||
|
wakeup=true;
|
||||||
|
idle=true;
|
||||||
|
break;
|
||||||
|
case 0x2: //THRE
|
||||||
|
for(int i=0;i<hwTxQueueLen;i++)
|
||||||
|
{
|
||||||
|
//If software queue empty, stop
|
||||||
|
if(txQueue.IRQget(c,hppw)==false) break;
|
||||||
|
serial->THR=c;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(wakeup && rxWaiting)
|
||||||
|
{
|
||||||
|
rxWaiting->IRQwakeup();
|
||||||
|
if(rxWaiting->IRQgetPriority()>
|
||||||
|
Thread::IRQgetCurrentThread()->IRQgetPriority()) hppw=true;
|
||||||
|
rxWaiting=0;
|
||||||
|
}
|
||||||
|
if(hppw) Scheduler::IRQfindNextThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
LPC2000Serial::~LPC2000Serial()
|
||||||
|
{
|
||||||
|
waitSerialTxFifoEmpty();
|
||||||
|
|
||||||
|
InterruptDisableLock dLock;
|
||||||
|
//Disable UART0
|
||||||
|
serial->LCR=0;//DLAB disabled
|
||||||
|
serial->FCR=0;
|
||||||
|
|
||||||
|
int id=0;
|
||||||
|
if(ports[0]==this) id=0;
|
||||||
|
else if(ports[1]==this) id=1;
|
||||||
|
else errorHandler(UNEXPECTED);
|
||||||
|
ports[id]=0;
|
||||||
|
|
||||||
|
if(id==0)
|
||||||
|
{
|
||||||
|
//Disable VIC
|
||||||
|
VICIntEnClr=(1<<6);
|
||||||
|
//Disable PIN
|
||||||
|
PINSEL0&=~(0xf);//Clear bits 0 to 3 of PINSEL0
|
||||||
|
} else {
|
||||||
|
//Disable VIC
|
||||||
|
VICIntEnClr=(1<<7);
|
||||||
|
//Disable PIN
|
||||||
|
PINSEL0&=~(0xf0000);//Clear bits 16 to 19 of PINSEL0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,184 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014 *
|
||||||
|
* by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef SERIAL_LPC2000_H
|
||||||
|
#define SERIAL_LPC2000_H
|
||||||
|
|
||||||
|
#include "filesystem/console/console_device.h"
|
||||||
|
#include "kernel/sync.h"
|
||||||
|
#include "kernel/queue.h"
|
||||||
|
#include "interfaces/delays.h"
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serial port class for LPC2000 microcontrollers.
|
||||||
|
*
|
||||||
|
* Classes of this type are reference counted, must be allocated on the heap
|
||||||
|
* and managed through intrusive_ref_ptr<FileBase>
|
||||||
|
*/
|
||||||
|
class LPC2000Serial : public Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor, initializes the serial port.
|
||||||
|
* Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when
|
||||||
|
* attempting to construct multiple objects with the same id. That is,
|
||||||
|
* it is possible to instantiate only one instance of this class for each
|
||||||
|
* hardware USART.
|
||||||
|
* \param id 0=USART0, 1=USART1
|
||||||
|
* \param baudrate serial port baudrate.
|
||||||
|
*/
|
||||||
|
LPC2000Serial(int id, int baudrate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a block of data
|
||||||
|
* \param buffer buffer where read data will be stored
|
||||||
|
* \param size buffer size
|
||||||
|
* \param where where to read from
|
||||||
|
* \return number of bytes read or a negative number on failure
|
||||||
|
*/
|
||||||
|
ssize_t readBlock(void *buffer, size_t size, off_t where);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a block of data
|
||||||
|
* \param buffer buffer where take data to write
|
||||||
|
* \param size buffer size
|
||||||
|
* \param where where to write to
|
||||||
|
* \return number of bytes written or a negative number on failure
|
||||||
|
*/
|
||||||
|
ssize_t writeBlock(const void *buffer, size_t size, off_t where);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a string.
|
||||||
|
* An extension to the Device interface that adds a new member function,
|
||||||
|
* which is used by the kernel on console devices to write debug information
|
||||||
|
* before the kernel is started or in case of serious errors, right before
|
||||||
|
* rebooting.
|
||||||
|
* Can ONLY be called when the kernel is not yet started, paused or within
|
||||||
|
* an interrupt. This default implementation ignores writes.
|
||||||
|
* \param str the string to write. The string must be NUL terminated.
|
||||||
|
*/
|
||||||
|
void IRQwrite(const char *str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs device-specific operations
|
||||||
|
* \param cmd specifies the operation to perform
|
||||||
|
* \param arg optional argument that some operation require
|
||||||
|
* \return the exact return value depends on CMD, -1 is returned on error
|
||||||
|
*/
|
||||||
|
int ioctl(int cmd, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal the serial port interrupts call this member function.
|
||||||
|
* Never call this from user code.
|
||||||
|
*/
|
||||||
|
void IRQhandleInterrupt();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor
|
||||||
|
*/
|
||||||
|
~LPC2000Serial();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Wait until all characters have been written to the serial port
|
||||||
|
*/
|
||||||
|
void waitSerialTxFifoEmpty()
|
||||||
|
{
|
||||||
|
while((serial->LSR & (1<<6))==0) ;
|
||||||
|
|
||||||
|
//This delay has been added to fix a quirk on the Miosix board. When
|
||||||
|
//writing a message to the console and rebooting, if the reboot happens
|
||||||
|
//too fast with respect to the last character sent out of the serial
|
||||||
|
//port, the FT232 gets confused and the last charcters are lost,
|
||||||
|
//probably from the FT232 buffer. Using delayMs() to be callable from IRQ
|
||||||
|
delayMs(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The registers of the USART in struct form, to make a generic driver
|
||||||
|
*/
|
||||||
|
struct Usart16550
|
||||||
|
{
|
||||||
|
//Offset 0x00
|
||||||
|
union {
|
||||||
|
volatile unsigned char RBR;
|
||||||
|
volatile unsigned char THR;
|
||||||
|
volatile unsigned char DLL;
|
||||||
|
};
|
||||||
|
char padding0[3];
|
||||||
|
//Offset 0x04
|
||||||
|
union {
|
||||||
|
volatile unsigned char IER;
|
||||||
|
volatile unsigned char DLM;
|
||||||
|
};
|
||||||
|
char padding1[3];
|
||||||
|
//Offset 0x08
|
||||||
|
union {
|
||||||
|
volatile unsigned char IIR;
|
||||||
|
volatile unsigned char FCR;
|
||||||
|
};
|
||||||
|
char padding2[3];
|
||||||
|
//Offset 0x0c
|
||||||
|
volatile unsigned char LCR;
|
||||||
|
char padding3[3];
|
||||||
|
//Offset 0x10
|
||||||
|
volatile unsigned char MCR; //Only USART1 has this
|
||||||
|
char padding4[3];
|
||||||
|
//Offset 0x14
|
||||||
|
volatile unsigned char LSR;
|
||||||
|
char padding5[7];
|
||||||
|
//Offset 0x1c
|
||||||
|
volatile unsigned char SCR;
|
||||||
|
char padding6[19];
|
||||||
|
//Offset 0x30
|
||||||
|
volatile unsigned char TER;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Configure the software queue here
|
||||||
|
static const int swTxQueue=32;///< Size of tx software queue
|
||||||
|
|
||||||
|
//The hardware queues cannot be modified, since their length is hardware-specific
|
||||||
|
static const int hwTxQueueLen=16;
|
||||||
|
static const int hwRxQueueLen=8;
|
||||||
|
|
||||||
|
FastMutex txMutex;///< Mutex used to guard the tx queue
|
||||||
|
FastMutex rxMutex;///< Mutex used to guard the rx queue
|
||||||
|
|
||||||
|
Queue<char,swTxQueue> txQueue;///< Tx software queue
|
||||||
|
DynUnsyncQueue<char> rxQueue;///< Rx software queue
|
||||||
|
Thread *rxWaiting; ///< Thread waiting on rx queue
|
||||||
|
bool idle; ///< Receiver idle
|
||||||
|
|
||||||
|
Usart16550 *serial; ///< Serial port registers
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //SERIAL_LPC2000_H
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,295 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2010-2018 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef SERIAL_STM32_H
|
||||||
|
#define SERIAL_STM32_H
|
||||||
|
|
||||||
|
#include "filesystem/console/console_device.h"
|
||||||
|
#include "kernel/sync.h"
|
||||||
|
#include "kernel/queue.h"
|
||||||
|
#include "interfaces/gpio.h"
|
||||||
|
#include "board_settings.h"
|
||||||
|
|
||||||
|
#if defined(_ARCH_CORTEXM3_STM32) && defined(__ENABLE_XRAM)
|
||||||
|
//Quirk: concurrent access to the FSMC from both core and DMA is broken in
|
||||||
|
//the stm32f1, so disable DMA mode if XRAM is enabled.
|
||||||
|
#undef SERIAL_1_DMA
|
||||||
|
#undef SERIAL_2_DMA
|
||||||
|
#undef SERIAL_3_DMA
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(SERIAL_1_DMA) || defined(SERIAL_2_DMA) || defined(SERIAL_3_DMA)
|
||||||
|
#define SERIAL_DMA
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(SERIAL_DMA) && defined(_ARCH_CORTEXM0_STM32)
|
||||||
|
#undef SERIAL_1_DMA
|
||||||
|
#undef SERIAL_2_DMA
|
||||||
|
#undef SERIAL_3_DMA
|
||||||
|
#undef SERIAL_DMA
|
||||||
|
#warning "DMA not yet implemented for STM32F0 family"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serial port class for stm32 microcontrollers.
|
||||||
|
* Only supports USART1, USART2 and USART3
|
||||||
|
* Additionally, USARTx can use DMA if SERIAL_x_DMA is defined in
|
||||||
|
* board_settings.h, while the other serial use polling for transmission,
|
||||||
|
* and interrupt for reception.
|
||||||
|
*
|
||||||
|
* Classes of this type are reference counted, must be allocated on the heap
|
||||||
|
* and managed through intrusive_ref_ptr<FileBase>
|
||||||
|
*/
|
||||||
|
class STM32Serial : public Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum FlowCtrl
|
||||||
|
{
|
||||||
|
NOFLOWCTRL, ///< No hardware flow control
|
||||||
|
RTSCTS ///< RTS/CTS hardware flow control
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor, initializes the serial port using the default pins, which
|
||||||
|
* are:
|
||||||
|
* USART1: tx=PA9 rx=PA10 cts=PA11 rts=PA12
|
||||||
|
* USART2: tx=PA2 rx=PA3 cts=PA0 rts=PA1
|
||||||
|
* USART3: tx=PB10 rx=PB11 cts=PB13 rts=PB14
|
||||||
|
* If you board has a different mapping, use one of the other constructors.
|
||||||
|
*
|
||||||
|
* Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when
|
||||||
|
* attempting to construct multiple objects with the same id. That is,
|
||||||
|
* it is possible to instantiate only one instance of this class for each
|
||||||
|
* hardware USART.
|
||||||
|
* \param id a number 1 to 3 to select which USART
|
||||||
|
* \param baudrate serial port baudrate
|
||||||
|
* \param flowControl to enable hardware flow control on this port
|
||||||
|
*/
|
||||||
|
STM32Serial(int id, int baudrate, FlowCtrl flowControl=NOFLOWCTRL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor, initializes the serial port using remapped pins and disables
|
||||||
|
* flow control.
|
||||||
|
*
|
||||||
|
* NOTE: for stm32f2, f4, f7 and h7 you have to set the correct alternate
|
||||||
|
* function to the pins in order to connect then to the USART peripheral
|
||||||
|
* before passing them to this class.
|
||||||
|
*
|
||||||
|
* Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when
|
||||||
|
* attempting to construct multiple objects with the same id. That is,
|
||||||
|
* it is possible to instantiate only one instance of this class for each
|
||||||
|
* hardware USART.
|
||||||
|
* \param id a number 1 to 3 to select which USART
|
||||||
|
* \param baudrate serial port baudrate
|
||||||
|
* \param tx tx pin
|
||||||
|
* \param rx rx pin
|
||||||
|
*/
|
||||||
|
STM32Serial(int id, int baudrate, miosix::GpioPin tx, miosix::GpioPin rx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor, initializes the serial port using remapped pins and enables
|
||||||
|
* flow control.
|
||||||
|
*
|
||||||
|
* NOTE: for stm32f2, f4, f7 and h7 you have to set the correct alternate
|
||||||
|
* function to the pins in order to connect then to the USART peripheral
|
||||||
|
* before passing them to this class.
|
||||||
|
*
|
||||||
|
* Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when
|
||||||
|
* attempting to construct multiple objects with the same id. That is,
|
||||||
|
* it is possible to instantiate only one instance of this class for each
|
||||||
|
* hardware USART.
|
||||||
|
* \param id a number 1 to 3 to select which USART
|
||||||
|
* \param tx tx pin
|
||||||
|
* \param rx rx pin
|
||||||
|
* \param rts rts pin
|
||||||
|
* \param cts cts pin
|
||||||
|
*/
|
||||||
|
STM32Serial(int id, int baudrate, miosix::GpioPin tx, miosix::GpioPin rx,
|
||||||
|
miosix::GpioPin rts, miosix::GpioPin cts);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a block of data
|
||||||
|
* \param buffer buffer where read data will be stored
|
||||||
|
* \param size buffer size
|
||||||
|
* \param where where to read from
|
||||||
|
* \return number of bytes read or a negative number on failure. Note that
|
||||||
|
* it is normal for this function to return less character than the amount
|
||||||
|
* asked
|
||||||
|
*/
|
||||||
|
ssize_t readBlock(void *buffer, size_t size, off_t where);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a block of data
|
||||||
|
* \param buffer buffer where take data to write
|
||||||
|
* \param size buffer size
|
||||||
|
* \param where where to write to
|
||||||
|
* \return number of bytes written or a negative number on failure
|
||||||
|
*/
|
||||||
|
ssize_t writeBlock(const void *buffer, size_t size, off_t where);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a string.
|
||||||
|
* An extension to the Device interface that adds a new member function,
|
||||||
|
* which is used by the kernel on console devices to write debug information
|
||||||
|
* before the kernel is started or in case of serious errors, right before
|
||||||
|
* rebooting.
|
||||||
|
* Can ONLY be called when the kernel is not yet started, paused or within
|
||||||
|
* an interrupt. This default implementation ignores writes.
|
||||||
|
* \param str the string to write. The string must be NUL terminated.
|
||||||
|
*/
|
||||||
|
void IRQwrite(const char *str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs device-specific operations
|
||||||
|
* \param cmd specifies the operation to perform
|
||||||
|
* \param arg optional argument that some operation require
|
||||||
|
* \return the exact return value depends on CMD, -1 is returned on error
|
||||||
|
*/
|
||||||
|
int ioctl(int cmd, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal the serial port interrupts call this member function.
|
||||||
|
* Never call this from user code.
|
||||||
|
*/
|
||||||
|
void IRQhandleInterrupt();
|
||||||
|
|
||||||
|
#ifdef SERIAL_DMA
|
||||||
|
/**
|
||||||
|
* \internal the serial port DMA tx interrupts call this member function.
|
||||||
|
* Never call this from user code.
|
||||||
|
*/
|
||||||
|
void IRQhandleDMAtx();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal the serial port DMA rx interrupts call this member function.
|
||||||
|
* Never call this from user code.
|
||||||
|
*/
|
||||||
|
void IRQhandleDMArx();
|
||||||
|
#endif //SERIAL_DMA
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return port id, 1 for USART1, 2 for USART2, ...
|
||||||
|
*/
|
||||||
|
int getId() const { return portId; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor
|
||||||
|
*/
|
||||||
|
~STM32Serial();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Code common for all constructors
|
||||||
|
*/
|
||||||
|
void commonInit(int id, int baudrate, miosix::GpioPin tx, miosix::GpioPin rx,
|
||||||
|
miosix::GpioPin rts, miosix::GpioPin cts);
|
||||||
|
|
||||||
|
#ifdef SERIAL_DMA
|
||||||
|
/**
|
||||||
|
* Wait until a pending DMA TX completes, if any
|
||||||
|
*/
|
||||||
|
void waitDmaTxCompletion();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write to the serial port using DMA. When the function returns, the DMA
|
||||||
|
* transfer is still in progress.
|
||||||
|
* \param buffer buffer to write
|
||||||
|
* \param size size of buffer to write
|
||||||
|
*/
|
||||||
|
void writeDma(const char *buffer, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read from DMA buffer and write data to queue
|
||||||
|
*/
|
||||||
|
void IRQreadDma();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start DMA read
|
||||||
|
*/
|
||||||
|
void IRQdmaReadStart();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop DMA read
|
||||||
|
* \return the number of characters in rxBuffer
|
||||||
|
*/
|
||||||
|
int IRQdmaReadStop();
|
||||||
|
#endif //SERIAL_DMA
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait until all characters have been written to the serial port.
|
||||||
|
* Needs to be callable from interrupts disabled (it is used in IRQwrite)
|
||||||
|
*/
|
||||||
|
void waitSerialTxFifoEmpty()
|
||||||
|
{
|
||||||
|
#if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
|
||||||
|
&& !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \
|
||||||
|
&& !defined(_ARCH_CORTEXM4_STM32L4)
|
||||||
|
while((port->SR & USART_SR_TC)==0) ;
|
||||||
|
#else //_ARCH_CORTEXM7_STM32F7/H7
|
||||||
|
while((port->ISR & USART_ISR_TC)==0) ;
|
||||||
|
#endif //_ARCH_CORTEXM7_STM32F7/H7
|
||||||
|
}
|
||||||
|
|
||||||
|
FastMutex txMutex; ///< Mutex locked during transmission
|
||||||
|
FastMutex rxMutex; ///< Mutex locked during reception
|
||||||
|
|
||||||
|
DynUnsyncQueue<char> rxQueue; ///< Receiving queue
|
||||||
|
static const unsigned int rxQueueMin=16; ///< Minimum queue size
|
||||||
|
Thread *rxWaiting=0; ///< Thread waiting for rx, or 0
|
||||||
|
|
||||||
|
USART_TypeDef *port; ///< Pointer to USART peripheral
|
||||||
|
#ifdef SERIAL_DMA
|
||||||
|
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \
|
||||||
|
|| defined(_ARCH_CORTEXM4_STM32L4)
|
||||||
|
DMA_Channel_TypeDef *dmaTx; ///< Pointer to DMA TX peripheral
|
||||||
|
DMA_Channel_TypeDef *dmaRx; ///< Pointer to DMA RX peripheral
|
||||||
|
#else //_ARCH_CORTEXM3_STM32 and _ARCH_CORTEXM4_STM32F3
|
||||||
|
DMA_Stream_TypeDef *dmaTx; ///< Pointer to DMA TX peripheral
|
||||||
|
DMA_Stream_TypeDef *dmaRx; ///< Pointer to DMA RX peripheral
|
||||||
|
#endif //_ARCH_CORTEXM3_STM32 and _ARCH_CORTEXM4_STM32F3
|
||||||
|
Thread *txWaiting; ///< Thread waiting for tx, or 0
|
||||||
|
static const unsigned int txBufferSize=16; ///< Size of tx buffer, for tx speedup
|
||||||
|
/// Tx buffer, for tx speedup. This buffer must not end up in the CCM of the
|
||||||
|
/// STM32F4, as it is used to perform DMA operations. This is guaranteed by
|
||||||
|
/// the fact that this class must be allocated on the heap as it derives
|
||||||
|
/// from Device, and the Miosix linker scripts never put the heap in CCM
|
||||||
|
char txBuffer[txBufferSize];
|
||||||
|
/// This buffer emulates the behaviour of a 16550. It is filled using DMA
|
||||||
|
/// and an interrupt is fired as soon as it is half full
|
||||||
|
char rxBuffer[rxQueueMin];
|
||||||
|
bool dmaTxInProgress; ///< True if a DMA tx is in progress
|
||||||
|
#endif //SERIAL_DMA
|
||||||
|
bool idle=true; ///< Receiver idle
|
||||||
|
const bool flowControl; ///< True if flow control GPIOs enabled
|
||||||
|
const unsigned char portId; ///< 1 for USART1, 2 for USART2, ...
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //SERIAL_STM32_H
|
||||||
|
|
@ -0,0 +1,356 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2014 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "servo_stm32.h"
|
||||||
|
#include "kernel/scheduler/scheduler.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace miosix;
|
||||||
|
|
||||||
|
typedef Gpio<GPIOB_BASE,6> servo1out;
|
||||||
|
typedef Gpio<GPIOB_BASE,7> servo2out;
|
||||||
|
typedef Gpio<GPIOB_BASE,8> servo3out;
|
||||||
|
typedef Gpio<GPIOB_BASE,9> servo4out;
|
||||||
|
static Thread *waiting=0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timer 4 interrupt handler actual implementation
|
||||||
|
*/
|
||||||
|
void __attribute__((used)) tim4impl()
|
||||||
|
{
|
||||||
|
TIM4->SR=0; //Clear interrupt flag
|
||||||
|
if(waiting==0) return;
|
||||||
|
waiting->IRQwakeup();
|
||||||
|
if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
|
||||||
|
Scheduler::IRQfindNextThread();
|
||||||
|
waiting=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timer 4 interrupt handler
|
||||||
|
*/
|
||||||
|
void __attribute__((naked)) TIM4_IRQHandler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
asm volatile("bl _Z8tim4implv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/* TODO: find a better place for this */
|
||||||
|
unsigned int divideRoundToNearest(unsigned int a, unsigned int b)
|
||||||
|
{
|
||||||
|
const unsigned int quot=2*a/b;
|
||||||
|
return quot/2 + (quot & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// class SynchronizedServo
|
||||||
|
//
|
||||||
|
|
||||||
|
SynchronizedServo& SynchronizedServo::instance()
|
||||||
|
{
|
||||||
|
static SynchronizedServo singleton;
|
||||||
|
return singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SynchronizedServo::enable(int channel)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(status!=STOPPED) return; // If timer enabled ignore the call
|
||||||
|
|
||||||
|
{
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
// Calling the mode() function on a GPIO is subject to race conditions
|
||||||
|
// between threads on the STM32, so we disable interrupts
|
||||||
|
switch(channel)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
TIM4->CCMR1 |= TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE;
|
||||||
|
TIM4->CCER |= TIM_CCER_CC1E;
|
||||||
|
#ifndef _ARCH_CORTEXM3_STM32 //Only stm32f2 and stm32f4 have it
|
||||||
|
servo1out::alternateFunction(2);
|
||||||
|
#endif //_ARCH_CORTEXM3_STM32
|
||||||
|
servo1out::mode(Mode::ALTERNATE);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
TIM4->CCMR1 |= TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE;
|
||||||
|
TIM4->CCER |= TIM_CCER_CC2E;
|
||||||
|
#ifndef _ARCH_CORTEXM3_STM32 //Only stm32f2 and stm32f4 have it
|
||||||
|
servo2out::alternateFunction(2);
|
||||||
|
#endif //_ARCH_CORTEXM3_STM32
|
||||||
|
servo2out::mode(Mode::ALTERNATE);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
TIM4->CCMR2 |= TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE;
|
||||||
|
TIM4->CCER |= TIM_CCER_CC3E;
|
||||||
|
#ifndef _ARCH_CORTEXM3_STM32 //Only stm32f2 and stm32f4 have it
|
||||||
|
servo3out::alternateFunction(2);
|
||||||
|
#endif //_ARCH_CORTEXM3_STM32
|
||||||
|
servo3out::mode(Mode::ALTERNATE);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
TIM4->CCMR2 |= TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE;
|
||||||
|
TIM4->CCER |= TIM_CCER_CC4E;
|
||||||
|
#ifndef _ARCH_CORTEXM3_STM32 //Only stm32f2 and stm32f4 have it
|
||||||
|
servo4out::alternateFunction(2);
|
||||||
|
#endif //_ARCH_CORTEXM3_STM32
|
||||||
|
servo4out::mode(Mode::ALTERNATE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SynchronizedServo::disable(int channel)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(status!=STOPPED) return; // If timer enabled ignore the call
|
||||||
|
|
||||||
|
{
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
// Calling the mode() function on a GPIO is subject to race conditions
|
||||||
|
// between threads on the STM32, so we disable interrupts
|
||||||
|
switch(channel)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
servo1out::mode(Mode::INPUT);
|
||||||
|
TIM4->CCER &= ~TIM_CCER_CC1E;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
servo2out::mode(Mode::INPUT);
|
||||||
|
TIM4->CCER &= ~TIM_CCER_CC2E;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
servo3out::mode(Mode::INPUT);
|
||||||
|
TIM4->CCER &= ~TIM_CCER_CC3E;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
servo4out::mode(Mode::INPUT);
|
||||||
|
TIM4->CCER &= ~TIM_CCER_CC4E;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SynchronizedServo::setPosition(int channel, float value)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(status!=STARTED) return; // If timer disabled ignore the call
|
||||||
|
|
||||||
|
value=min(1.0f,max(0.0f,value));
|
||||||
|
switch(channel)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
TIM4->CCR1=a*value+b;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
TIM4->CCR2=a*value+b;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
TIM4->CCR3=a*value+b;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
TIM4->CCR4=a*value+b;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float SynchronizedServo::getPosition(int channel)
|
||||||
|
{
|
||||||
|
switch(channel)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return TIM4->CCR1==0 ? NAN : TIM4->CCR1/a-b;
|
||||||
|
case 1:
|
||||||
|
return TIM4->CCR2==0 ? NAN : TIM4->CCR2/a-b;
|
||||||
|
case 2:
|
||||||
|
return TIM4->CCR3==0 ? NAN : TIM4->CCR3/a-b;
|
||||||
|
case 3:
|
||||||
|
return TIM4->CCR4==0 ? NAN : TIM4->CCR4/a-b;
|
||||||
|
default:
|
||||||
|
return NAN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SynchronizedServo::start()
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(status!=STOPPED) return; // If timer enabled ignore the call
|
||||||
|
|
||||||
|
// While status is starting neither memeber function callable with timer
|
||||||
|
// started nor stopped are allowed
|
||||||
|
status=STARTED;
|
||||||
|
TIM4->CNT=0;
|
||||||
|
TIM4->EGR=TIM_EGR_UG;
|
||||||
|
TIM4->CR1=TIM_CR1_CEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SynchronizedServo::stop()
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(status!=STARTED) return; // If timer disabled ignore the call
|
||||||
|
|
||||||
|
status=STOPPED;
|
||||||
|
// Stopping the timer is a bit difficult because we don't want to
|
||||||
|
// accidentally create glitches on the outputs which may turn the servos
|
||||||
|
// to random positions. So, we set all PWM outputs to 0, then wait until the
|
||||||
|
// end of the period, and then disable the timer
|
||||||
|
TIM4->CCR1=0;
|
||||||
|
TIM4->CCR2=0;
|
||||||
|
TIM4->CCR3=0;
|
||||||
|
TIM4->CCR4=0;
|
||||||
|
{
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
// Wakeup an eventual thread waiting on waitForCycleBegin()
|
||||||
|
if(waiting) waiting->IRQwakeup();
|
||||||
|
IRQwaitForTimerOverflow(dLock);
|
||||||
|
}
|
||||||
|
TIM4->CR1=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SynchronizedServo::waitForCycleBegin()
|
||||||
|
{
|
||||||
|
// No need to lock the mutex because disabling interrupts is enough to avoid
|
||||||
|
// race conditions. Also, locking the mutex here would prevent other threads
|
||||||
|
// from calling other member functions of this class
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
if(status!=STARTED) return true;
|
||||||
|
IRQwaitForTimerOverflow(dLock);
|
||||||
|
return status!=STARTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SynchronizedServo::setFrequency(unsigned int frequency)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(status!=STOPPED) return; // If timer enabled ignore the call
|
||||||
|
|
||||||
|
TIM4->PSC=divideRoundToNearest(getPrescalerInputFrequency(),frequency*65536)-1;
|
||||||
|
precomputeCoefficients();
|
||||||
|
}
|
||||||
|
|
||||||
|
float SynchronizedServo::getFrequency() const
|
||||||
|
{
|
||||||
|
float prescalerFreq=getPrescalerInputFrequency();
|
||||||
|
return prescalerFreq/((TIM4->PSC+1)*65536);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SynchronizedServo::setMinPulseWidth(float minPulse)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(status!=STOPPED) return; // If timer enabled ignore the call
|
||||||
|
|
||||||
|
minWidth=1e-6f*min(1300.0f,max(500.0f,minPulse));
|
||||||
|
precomputeCoefficients();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SynchronizedServo::setMaxPulseWidth(float maxPulse)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(status!=STOPPED) return; // If timer enabled ignore the call
|
||||||
|
|
||||||
|
maxWidth=1e-6f*min(2500.0f,max(1700.0f,maxPulse));
|
||||||
|
precomputeCoefficients();
|
||||||
|
}
|
||||||
|
|
||||||
|
SynchronizedServo::SynchronizedServo() : status(STOPPED)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
// The RCC register should be written with interrupts disabled to
|
||||||
|
// prevent race conditions with other threads.
|
||||||
|
RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;
|
||||||
|
RCC_SYNC();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure timer
|
||||||
|
TIM4->CR1=0;
|
||||||
|
TIM4->ARR=0xffff;
|
||||||
|
TIM4->CCR1=0;
|
||||||
|
TIM4->CCR2=0;
|
||||||
|
TIM4->CCR3=0;
|
||||||
|
TIM4->CCR4=0;
|
||||||
|
// Configure interrupt on timer overflow
|
||||||
|
TIM4->DIER=TIM_DIER_UIE;
|
||||||
|
NVIC_SetPriority(TIM4_IRQn,13); //Low priority for timer IRQ
|
||||||
|
NVIC_EnableIRQ(TIM4_IRQn);
|
||||||
|
// Set default parameters
|
||||||
|
setFrequency(50);
|
||||||
|
setMinPulseWidth(1000);
|
||||||
|
setMaxPulseWidth(2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SynchronizedServo::precomputeCoefficients()
|
||||||
|
{
|
||||||
|
float realPeriod=1.0f/getFrequency();
|
||||||
|
a=65536.0f*(maxWidth-minWidth)/realPeriod;
|
||||||
|
b=65536.0f*minWidth/realPeriod;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int SynchronizedServo::getPrescalerInputFrequency()
|
||||||
|
{
|
||||||
|
// The global variable SystemCoreClock from ARM's CMSIS allows to know
|
||||||
|
// the CPU frequency.
|
||||||
|
unsigned int freq=SystemCoreClock;
|
||||||
|
|
||||||
|
//The position of the PPRE1 bit in RCC->CFGR is different in some stm32
|
||||||
|
#ifdef _ARCH_CORTEXM3_STM32
|
||||||
|
const unsigned int ppre1=8;
|
||||||
|
#else //stm32f2 and f4
|
||||||
|
const unsigned int ppre1=10;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// The timer frequency may however be a submultiple of the CPU frequency,
|
||||||
|
// due to the bus at whch the periheral is connected being slower. The
|
||||||
|
// RCC->CFGR register tells us how slower the APB1 bus is running.
|
||||||
|
// This formula takes into account that if the APB1 clock is divided by a
|
||||||
|
// factor of two or greater, the timer is clocked at twice the bus
|
||||||
|
// interface. After this, the freq variable contains the frequency in Hz
|
||||||
|
// at which the timer prescaler is clocked.
|
||||||
|
if(RCC->CFGR & RCC_CFGR_PPRE1_2) freq/=1<<((RCC->CFGR>>ppre1) & 0x3);
|
||||||
|
|
||||||
|
return freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SynchronizedServo::IRQwaitForTimerOverflow(FastInterruptDisableLock& dLock)
|
||||||
|
{
|
||||||
|
waiting=Thread::IRQgetCurrentThread();
|
||||||
|
do {
|
||||||
|
Thread::IRQwait();
|
||||||
|
{
|
||||||
|
FastInterruptEnableLock eLock(dLock);
|
||||||
|
Thread::yield();
|
||||||
|
}
|
||||||
|
} while(waiting);
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,196 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2014 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef SERVO_STM32_H
|
||||||
|
#define SERVO_STM32_H
|
||||||
|
|
||||||
|
#include "miosix.h"
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is designed to drive up to 4 servomotors. It generates
|
||||||
|
* four square waves that are synchronized with respect to each other,
|
||||||
|
* and allows the execution of code that is too synchronized with the
|
||||||
|
* waveform generation, to ease the development of closed loop control
|
||||||
|
* code using the servos as actuators.
|
||||||
|
* This class can be safely accessed by multiple threads, except the
|
||||||
|
* waitForCycleBegin() member function.
|
||||||
|
*/
|
||||||
|
class SynchronizedServo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \return an instance of the SynchronizedServo class (singleton)
|
||||||
|
* When first returned, the servo waveform generation is stopped. You must
|
||||||
|
* enable at least one channel call start() and setPosition() before the
|
||||||
|
* servo driving waveforms will be generated.
|
||||||
|
*/
|
||||||
|
static SynchronizedServo& instance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable a channel. Can only be called with the outputs stopped. Even if
|
||||||
|
* the channel is enabled, when it is started it will not produce any
|
||||||
|
* waveform until the first call to setPosition()
|
||||||
|
* \param channel which channel to enable, must be between 0 and 3.
|
||||||
|
*/
|
||||||
|
void enable(int channel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable a channel. Can only be called with the outputs stopped.
|
||||||
|
* \param channel which channel to disable, must be between 0 and 3.
|
||||||
|
*/
|
||||||
|
void disable(int channel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the servo position. Can only be called with the outputs started.
|
||||||
|
* The written value takes effect at the next waveform generation cycle.
|
||||||
|
* \param channel channel whose output need to be changed, between 0 and 3
|
||||||
|
* \param value a float value from 0.0 to 1.0. Due to the limited timer
|
||||||
|
* resolution, the actual set value is approximated. With the default values
|
||||||
|
* of waveform frequency, min and max width the range between 0.0 and 1.0
|
||||||
|
* is covered by around 3200 points.
|
||||||
|
*/
|
||||||
|
void setPosition(int channel, float value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \param channel channel whose output need to be read, between 0 and 3
|
||||||
|
* \return the exact servo position, considering the approximations.
|
||||||
|
* For this reason, the returned value may differ from the last call to
|
||||||
|
* setPosition(). NAN is returned if no setValue() was called on the channel
|
||||||
|
* since the last call to start()
|
||||||
|
*/
|
||||||
|
float getPosition(int channel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start producing the output waveforms.
|
||||||
|
*/
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop producing the output waveforms. If a thread is waiting in
|
||||||
|
* waitForCycleBegin() it will be forecefully woken up.
|
||||||
|
* As a side effect, the position of all the channels will be set to NAN.
|
||||||
|
* When you restart the timer, you must call setPosition() on each enabled
|
||||||
|
* channel before the channel will actually produce a waveform.
|
||||||
|
* This function waits until the end of a waveform generation cycle in order
|
||||||
|
* to not produce glitches. For this reason, it may take up to
|
||||||
|
* 1/getFrequency() to complete, which with the default value of 50Hz is 20ms
|
||||||
|
*/
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait until the begin of a waveform generation cycle
|
||||||
|
* \return false if a new cycle of waveform generation has begun, or true
|
||||||
|
* if another thread called stop(). Only one thread at a time can call this
|
||||||
|
* member function. If more than one thread calls this deadlock will occur
|
||||||
|
* so don't do it!
|
||||||
|
*/
|
||||||
|
bool waitForCycleBegin();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the frequency of the generated waveform. Can only be called
|
||||||
|
* with the outputs stopped. The default is 50Hz. Note that due to prescaler
|
||||||
|
* resolution, the actual frequency is set to the closest possible value.
|
||||||
|
* To know the actual frequency, call getFrequency()
|
||||||
|
* \param frequency desired servo update frequency in Hz
|
||||||
|
* Must be between 10 and 100Hz
|
||||||
|
*/
|
||||||
|
void setFrequency(unsigned int frequency);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return the actual servo update frequency in Hz. Note that the
|
||||||
|
* returned value is floating point as the returned frequency takes into
|
||||||
|
* account approximations due to the prescaler resolution
|
||||||
|
*/
|
||||||
|
float getFrequency() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the minimum width of the generated pulses, that is, the pulse width
|
||||||
|
* generated when an output is set to zero with setPosition(x,0).
|
||||||
|
* The default is 1000us. Can only be called with the outputs stopped.
|
||||||
|
* \param minPulse minimum pulse width in microseconds.
|
||||||
|
* Must be between 500 and 1300.
|
||||||
|
*/
|
||||||
|
void setMinPulseWidth(float minPulse);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return minimum pulse width in microseconds
|
||||||
|
*/
|
||||||
|
float getMinPulseWidth() const { return minWidth*1e6f; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the maximum width of the generated pulses, that is, the pulse width
|
||||||
|
* generated when an output is set to one with setPosition(x,1).
|
||||||
|
* The default is 2000us. Can only be called with the outputs stopped.
|
||||||
|
* \param maxPulse maximum pulse width in microseconds.
|
||||||
|
* Must be between 1700 and 2500.
|
||||||
|
*/
|
||||||
|
void setMaxPulseWidth(float maxPulse);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return maximum pulse width in microseconds
|
||||||
|
*/
|
||||||
|
float getMaxPulseWidth() const { return maxWidth*1e6f; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
SynchronizedServo(const SynchronizedServo&);
|
||||||
|
SynchronizedServo& operator= (const SynchronizedServo&);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
SynchronizedServo();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Precompute a and b coefficient to make setPosition() faster
|
||||||
|
*/
|
||||||
|
void precomputeCoefficients();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return the input frequency of the timer prescaler
|
||||||
|
*/
|
||||||
|
static unsigned int getPrescalerInputFrequency();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait until the timer overflows from 0xffff to 0. Can only be called with
|
||||||
|
* interrupts disabled
|
||||||
|
*/
|
||||||
|
static void IRQwaitForTimerOverflow(FastInterruptDisableLock& dLock);
|
||||||
|
|
||||||
|
float minWidth, maxWidth; ///< Minimum and maximum pulse widths
|
||||||
|
float a, b; ///< Precomputed coefficients
|
||||||
|
FastMutex mutex; ///< Mutex to protect from concurrent access
|
||||||
|
enum {
|
||||||
|
STOPPED, ///< Timer is stopped
|
||||||
|
STARTED ///< Timer is started
|
||||||
|
} status;
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //SERVO_STM32_H
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include "interfaces/delays.h"
|
||||||
|
#include "stm32_hardware_rng.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
//
|
||||||
|
// class HardwareRng
|
||||||
|
//
|
||||||
|
|
||||||
|
HardwareRng& HardwareRng::instance()
|
||||||
|
{
|
||||||
|
static HardwareRng singleton;
|
||||||
|
return singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int HardwareRng::get()
|
||||||
|
{
|
||||||
|
miosix::Lock<miosix::FastMutex> l(mutex);
|
||||||
|
PeripheralEnable pe;
|
||||||
|
return getImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardwareRng::get(void* buf, unsigned int size)
|
||||||
|
{
|
||||||
|
unsigned char *buffer=reinterpret_cast<unsigned char*>(buf);
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
PeripheralEnable pe;
|
||||||
|
union Cast
|
||||||
|
{
|
||||||
|
unsigned int theInt;
|
||||||
|
unsigned char theChar[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
if(reinterpret_cast<unsigned int>(buffer) & 0x3)
|
||||||
|
{
|
||||||
|
Cast cast;
|
||||||
|
cast.theInt=getImpl();
|
||||||
|
int i=0;
|
||||||
|
while(reinterpret_cast<unsigned int>(buffer) & 0x3)
|
||||||
|
{
|
||||||
|
if(size--==0) return; //May happen if buffer is small and unaligned
|
||||||
|
*buffer++=cast.theChar[i++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int *aligned=reinterpret_cast<unsigned int*>(buffer);
|
||||||
|
for(unsigned int i=0;i<size/4;i++) aligned[i]=getImpl();
|
||||||
|
|
||||||
|
if(size & 0x3)
|
||||||
|
{
|
||||||
|
buffer+=(size/4)*4;
|
||||||
|
Cast cast;
|
||||||
|
cast.theInt=getImpl();
|
||||||
|
int i=0;
|
||||||
|
while(size & 0x3)
|
||||||
|
{
|
||||||
|
size--;
|
||||||
|
*buffer++=cast.theChar[i++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int HardwareRng::getImpl()
|
||||||
|
{
|
||||||
|
#ifndef __NO_EXCEPTIONS
|
||||||
|
for(int i=0;i<16;i++) //Try up to a reasonable # of times, then throw
|
||||||
|
#else
|
||||||
|
for(;;) //Can't return an error, keep retrying
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
int timeout=1000000;
|
||||||
|
unsigned int sr;
|
||||||
|
while(--timeout>0) { sr=RNG->SR; if(sr & RNG_SR_DRDY) break; }
|
||||||
|
if((sr & RNG_SR_SECS) || (sr & RNG_SR_CECS) || timeout<=0)
|
||||||
|
{
|
||||||
|
RNG->CR=0;
|
||||||
|
delayUs(1);
|
||||||
|
RNG->CR=RNG_CR_RNGEN;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unsigned int result=RNG->DR;
|
||||||
|
if(result==old) continue;
|
||||||
|
old=result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#ifndef __NO_EXCEPTIONS
|
||||||
|
throw runtime_error("RNG Fault detected");
|
||||||
|
#endif //__NO_EXCEPTIONS
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef RNG_H
|
||||||
|
#define RNG_H
|
||||||
|
|
||||||
|
#include "interfaces/arch_registers.h"
|
||||||
|
#include "kernel/sync.h"
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to access the hardware random number generator in Miosix
|
||||||
|
* Works with the hardware RNG in stm32f2 and stm32f4
|
||||||
|
*/
|
||||||
|
class HardwareRng
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \return an instance of this class (singleton)
|
||||||
|
*/
|
||||||
|
static HardwareRng& instance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return a 32 bit random number
|
||||||
|
* \throws runtime_error if the self test is not passed
|
||||||
|
*/
|
||||||
|
unsigned int get();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill a buffer with random data
|
||||||
|
* \param buf buffer to be filled
|
||||||
|
* \param size buffer size
|
||||||
|
* \throws runtime_error if the self test is not passed
|
||||||
|
*/
|
||||||
|
void get(void *buf, unsigned int size);
|
||||||
|
|
||||||
|
private:
|
||||||
|
HardwareRng(const HardwareRng&);
|
||||||
|
HardwareRng& operator=(const HardwareRng&);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
HardwareRng() : old(0)
|
||||||
|
{
|
||||||
|
miosix::FastInterruptDisableLock dLock;
|
||||||
|
RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN;
|
||||||
|
RCC_SYNC();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return a 32 bit random number
|
||||||
|
* \throws runtime_error if the self test is not passed
|
||||||
|
*/
|
||||||
|
unsigned int getImpl();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To save power, enable the peripheral ony when requested
|
||||||
|
*/
|
||||||
|
class PeripheralEnable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PeripheralEnable() { RNG->CR=RNG_CR_RNGEN; }
|
||||||
|
~PeripheralEnable() { RNG->CR=0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
miosix::FastMutex mutex; ///< To protect against concurrent access
|
||||||
|
unsigned int old; ///< Previously read value
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //RNG_H
|
||||||
|
|
@ -0,0 +1,322 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2017 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "stm32_rtc.h"
|
||||||
|
#include <miosix.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <kernel/scheduler/scheduler.h>
|
||||||
|
|
||||||
|
using namespace miosix;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
//
|
||||||
|
// class ScopedCnf, RAII to allow writing to the RTC alarm and prescaler.
|
||||||
|
// NOTE: datasheet says that after CNF bit has been cleared, no write is allowed
|
||||||
|
// to *any* of the RTC registers, not just PRL,CNT,ALR until RTOFF goes to 1.
|
||||||
|
// We do not wait until RTOFF is 1 in the destructor for performance reasons,
|
||||||
|
// so the rest of the code must be careful.
|
||||||
|
//
|
||||||
|
class ScopedCnf
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ScopedCnf()
|
||||||
|
{
|
||||||
|
while((RTC->CRL & RTC_CRL_RTOFF)==0) ;
|
||||||
|
RTC->CRL=0b11111;
|
||||||
|
}
|
||||||
|
|
||||||
|
~ScopedCnf()
|
||||||
|
{
|
||||||
|
RTC->CRL=0b01111;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
long long swTime=0; //64bit software extension of current time in ticks
|
||||||
|
long long irqTime=0; //64bit software extension of scheduled irq time in ticks
|
||||||
|
Thread *waiting=nullptr; //waiting thread
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return the hardware counter
|
||||||
|
*/
|
||||||
|
inline unsigned int IRQgetHwTick()
|
||||||
|
{
|
||||||
|
unsigned int h1=RTC->CNTH;
|
||||||
|
unsigned int l1=RTC->CNTL;
|
||||||
|
unsigned int h2=RTC->CNTH;
|
||||||
|
if(h1==h2) return (h1<<16) | l1;
|
||||||
|
return (h2<<16) | RTC->CNTL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return the time in ticks (hardware part + software extension to 64bits)
|
||||||
|
*/
|
||||||
|
long long IRQgetTick()
|
||||||
|
{
|
||||||
|
//Pending bit trick
|
||||||
|
unsigned int hwTime=IRQgetHwTick();
|
||||||
|
if((RTC->CRL & RTC_CRL_OWF) && IRQgetHwTick()>=hwTime)
|
||||||
|
return (swTime + static_cast<long long>(hwTime)) + (1LL<<32);
|
||||||
|
return swTime + static_cast<long long>(hwTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sleep the current thread till the specified time
|
||||||
|
* \param tick absolute time in ticks
|
||||||
|
* \param dLock used to reenable interrupts while sleeping
|
||||||
|
* \return true if the wait time was in the past
|
||||||
|
*/
|
||||||
|
bool IRQabsoluteWaitTick(long long tick, FastInterruptDisableLock& dLock)
|
||||||
|
{
|
||||||
|
irqTime=tick;
|
||||||
|
unsigned int hwAlarm=(tick & 0xffffffffULL) - (swTime & 0xffffffffULL);
|
||||||
|
{
|
||||||
|
ScopedCnf cnf;
|
||||||
|
RTC->ALRL=hwAlarm;
|
||||||
|
RTC->ALRH=hwAlarm>>16;
|
||||||
|
}
|
||||||
|
//NOTE: We do not wait for the alarm register to be written back to the low
|
||||||
|
//frequency domain for performance reasons. The datasheet says it takes
|
||||||
|
//at least 3 cycles of the 32KHz clock, but experiments show that it takes
|
||||||
|
//from 2 to 3, so perhaps they meant "at most 3". Because of this we
|
||||||
|
//consider the time in the past if we are more than 2 ticks of the 16KHz
|
||||||
|
//clock (4 ticks of the 32KHz one) in advance. Sleeps less than 122us are
|
||||||
|
//thus not supported.
|
||||||
|
if(IRQgetTick()>=tick-2) return true;
|
||||||
|
waiting=Thread::IRQgetCurrentThread();
|
||||||
|
do {
|
||||||
|
Thread::IRQwait();
|
||||||
|
{
|
||||||
|
FastInterruptEnableLock eLock(dLock);
|
||||||
|
Thread::yield();
|
||||||
|
}
|
||||||
|
} while(waiting);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} //anon namespace
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RTC interrupt
|
||||||
|
*/
|
||||||
|
void __attribute__((naked)) RTC_IRQHandler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
asm volatile("bl _Z10RTCIrqImplv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RTC interrupt actual implementation
|
||||||
|
*/
|
||||||
|
void __attribute__((used)) RTCIrqImpl()
|
||||||
|
{
|
||||||
|
unsigned int crl=RTC->CRL;
|
||||||
|
if(crl & RTC_CRL_OWF)
|
||||||
|
{
|
||||||
|
RTC->CRL=(RTC->CRL | 0xf) & ~RTC_CRL_OWF;
|
||||||
|
swTime+=1LL<<32;
|
||||||
|
} else if(crl & RTC_CRL_ALRF) {
|
||||||
|
RTC->CRL=(RTC->CRL | 0xf) & ~RTC_CRL_ALRF;
|
||||||
|
if(waiting && IRQgetTick()>=irqTime)
|
||||||
|
{
|
||||||
|
waiting->IRQwakeup();
|
||||||
|
if(waiting->IRQgetPriority()>
|
||||||
|
Thread::IRQgetCurrentThread()->IRQgetPriority())
|
||||||
|
Scheduler::IRQfindNextThread();
|
||||||
|
waiting=nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
void absoluteDeepSleep(long long int value)
|
||||||
|
{
|
||||||
|
#ifdef RUN_WITH_HSI
|
||||||
|
const int wakeupAdvance=3; //waking up takes time
|
||||||
|
#else //RUN_WITH_HSI
|
||||||
|
const int wakeupAdvance=33; //HSE starup time is 2ms
|
||||||
|
#endif //RUN_WITH_HSI
|
||||||
|
|
||||||
|
Rtc& rtc=Rtc::instance();
|
||||||
|
ioctl(STDOUT_FILENO,IOCTL_SYNC,0);
|
||||||
|
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
|
||||||
|
//Set alarm and enable EXTI
|
||||||
|
long long wkupTick=rtc.tc.ns2tick(value)-wakeupAdvance;
|
||||||
|
unsigned int hwAlarm=(wkupTick & 0xffffffffULL) - (swTime & 0xffffffffULL);
|
||||||
|
{
|
||||||
|
ScopedCnf cnf;
|
||||||
|
RTC->ALRL=hwAlarm;
|
||||||
|
RTC->ALRH=hwAlarm>>16;
|
||||||
|
}
|
||||||
|
while((RTC->CRL & RTC_CRL_RTOFF)==0) ;
|
||||||
|
|
||||||
|
|
||||||
|
EXTI->RTSR |= EXTI_RTSR_TR17;
|
||||||
|
EXTI->EMR |= EXTI_EMR_MR17; //enable event for wakeup
|
||||||
|
|
||||||
|
long long tick=IRQgetTick();
|
||||||
|
while(tick<wkupTick)
|
||||||
|
{
|
||||||
|
PWR->CR |= PWR_CR_LPDS;
|
||||||
|
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
|
||||||
|
// Using WFE instead of WFI because if while we are with interrupts
|
||||||
|
// disabled an interrupt (such as the tick interrupt) occurs, it
|
||||||
|
// remains pending and the WFI becomes a nop, and the device never goes
|
||||||
|
// in sleep mode. WFE events are latched in a separate pending register
|
||||||
|
// so interrupts do not interfere with them
|
||||||
|
__WFE();
|
||||||
|
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
|
||||||
|
PWR->CR &= ~PWR_CR_LPDS;
|
||||||
|
|
||||||
|
#ifndef SYSCLK_FREQ_24MHz
|
||||||
|
#error TODO: support more PLL frequencies
|
||||||
|
#endif
|
||||||
|
//STOP mode resets the clock to the HSI 8MHz, so restore the 24MHz clock
|
||||||
|
#ifndef RUN_WITH_HSI
|
||||||
|
RCC->CR |= RCC_CR_HSEON;
|
||||||
|
while((RCC->CR & RCC_CR_HSERDY)==0) ;
|
||||||
|
//PLL = (HSE / 2) * 6 = 24 MHz
|
||||||
|
RCC->CFGR &= ~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL);
|
||||||
|
RCC->CFGR |= RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL6;
|
||||||
|
#else //RUN_WITH_HSI
|
||||||
|
//PLL = (HSI / 2) * 6 = 24 MHz
|
||||||
|
RCC->CFGR &= ~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL);
|
||||||
|
RCC->CFGR |= RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL6;
|
||||||
|
#endif //RUN_WITH_HSI
|
||||||
|
RCC->CR |= RCC_CR_PLLON;
|
||||||
|
while((RCC->CR & RCC_CR_PLLRDY)==0) ;
|
||||||
|
RCC->CFGR &= ~RCC_CFGR_SW;
|
||||||
|
RCC->CFGR |= RCC_CFGR_SW_PLL;
|
||||||
|
while((RCC->CFGR & RCC_CFGR_SWS)!=0x08) ;
|
||||||
|
|
||||||
|
//Wait RSF
|
||||||
|
RTC->CRL=(RTC->CRL | 0xf) & ~RTC_CRL_RSF;
|
||||||
|
while((RTC->CRL & RTC_CRL_RSF)==0) ;
|
||||||
|
|
||||||
|
//Clear IRQ
|
||||||
|
unsigned int crl=RTC->CRL;
|
||||||
|
//NOTE: ST docs say OWF, ALRF are not updated in deep sleep. So, to
|
||||||
|
//detect counter ovreflow we check for oldTick>newTick. The check for
|
||||||
|
//flags is still there in case overflow/alarm occurs before/after sleep
|
||||||
|
if(crl & RTC_CRL_OWF || tick>IRQgetTick())
|
||||||
|
{
|
||||||
|
RTC->CRL=(RTC->CRL | 0xf) & ~RTC_CRL_OWF;
|
||||||
|
swTime+=1LL<<32;
|
||||||
|
} else if(crl & RTC_CRL_ALRF) {
|
||||||
|
RTC->CRL=(RTC->CRL | 0xf) & ~RTC_CRL_ALRF;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Update tick, done after checking IRQ flags to avoid race condition
|
||||||
|
tick=IRQgetTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
EXTI->EMR &=~ EXTI_EMR_MR17; //disable event for wakeup
|
||||||
|
|
||||||
|
wkupTick+=wakeupAdvance;
|
||||||
|
|
||||||
|
//NOTE: if we use the HSE we may spin for a while, but adding a
|
||||||
|
//IRQabsoluteWaitTick here makes this function wakeup too late
|
||||||
|
while(IRQgetTick()<wkupTick) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// class Rtc
|
||||||
|
//
|
||||||
|
|
||||||
|
Rtc& Rtc::instance()
|
||||||
|
{
|
||||||
|
static Rtc singleton;
|
||||||
|
return singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
long long Rtc::getValue() const
|
||||||
|
{
|
||||||
|
//Function takes ~170 clock cycles ~60 cycles IRQgetTick, ~96 cycles tick2ns
|
||||||
|
long long tick;
|
||||||
|
{
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
tick=IRQgetTick();
|
||||||
|
}
|
||||||
|
//tick2ns is reentrant, so can be called with interrupt enabled
|
||||||
|
return tc.tick2ns(tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
long long Rtc::IRQgetValue() const
|
||||||
|
{
|
||||||
|
return tc.tick2ns(IRQgetTick());
|
||||||
|
}
|
||||||
|
|
||||||
|
// May not work due to the way hwAlarm is computed in sleep functions
|
||||||
|
// void Rtc::setValue(long long value)
|
||||||
|
// {
|
||||||
|
// FastInterruptDisableLock dLock;
|
||||||
|
// swTime=tc.ns2tick(value)-IRQgetHwTick();
|
||||||
|
// }
|
||||||
|
|
||||||
|
void Rtc::wait(long long value)
|
||||||
|
{
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
IRQabsoluteWaitTick(IRQgetTick()+tc.ns2tick(value),dLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Rtc::absoluteWait(long long value)
|
||||||
|
{
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
return IRQabsoluteWaitTick(tc.ns2tick(value),dLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rtc::Rtc() : tc(getTickFrequency())
|
||||||
|
{
|
||||||
|
{
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
RCC->APB1ENR |= RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN;
|
||||||
|
PWR->CR |= PWR_CR_DBP;
|
||||||
|
RCC->BDCR=RCC_BDCR_RTCEN //RTC enabled
|
||||||
|
| RCC_BDCR_LSEON //External 32KHz oscillator enabled
|
||||||
|
| RCC_BDCR_RTCSEL_0; //Select LSE as clock source for RTC
|
||||||
|
RCC_SYNC();
|
||||||
|
#ifdef RTC_CLKOUT_ENABLE
|
||||||
|
BKP->RTCCR=BKP_RTCCR_CCO; //Output RTC clock/64 on pin
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
while((RCC->BDCR & RCC_BDCR_LSERDY)==0) ; //Wait for LSE to start
|
||||||
|
|
||||||
|
RTC->CRH=RTC_CRH_OWIE | RTC_CRH_ALRIE;
|
||||||
|
{
|
||||||
|
ScopedCnf cnf;
|
||||||
|
RTC->PRLH=0;
|
||||||
|
RTC->PRLL=1;
|
||||||
|
}
|
||||||
|
NVIC_SetPriority(RTC_IRQn,5);
|
||||||
|
NVIC_EnableIRQ(RTC_IRQn);
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2017 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef RTC_H
|
||||||
|
#define RTC_H
|
||||||
|
|
||||||
|
#include <kernel/timeconversion.h>
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Uncomment this directive to enable the RTC clock output functionality
|
||||||
|
///
|
||||||
|
|
||||||
|
//#define RTC_CLKOUT_ENABLE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts the MCU in deep sleep until the specified absolute time.
|
||||||
|
* \param value absolute wait time in nanoseconds
|
||||||
|
* If value of absolute time is in the past no waiting will be set
|
||||||
|
* and function return immediately.
|
||||||
|
*/
|
||||||
|
void absoluteDeepSleep(long long value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Driver for the stm32 RTC.
|
||||||
|
* All the wait and deepSleep functions cannot be called concurrently by
|
||||||
|
* multiple threads.
|
||||||
|
*/
|
||||||
|
class Rtc
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \return an instance of this class
|
||||||
|
*/
|
||||||
|
static Rtc& instance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return the timer counter value in nanoseconds
|
||||||
|
*/
|
||||||
|
long long getValue() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return the timer counter value in nanoseconds
|
||||||
|
*
|
||||||
|
* Can be called with interrupt disabled, or inside an interrupt
|
||||||
|
*/
|
||||||
|
long long IRQgetValue() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the timer counter value
|
||||||
|
* \param value new timer value in nanoseconds
|
||||||
|
*
|
||||||
|
* NOTE: if alarm is set wakeup time is not updated
|
||||||
|
*/
|
||||||
|
// void setValue(long long value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put thread in wait for the specified relative time.
|
||||||
|
* This function wait for a relative time passed as parameter.
|
||||||
|
* \param value relative time to wait, expressed in nanoseconds
|
||||||
|
*/
|
||||||
|
void wait(long long value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts the thread in wait until the specified absolute time.
|
||||||
|
* \param value absolute wait time in nanoseconds
|
||||||
|
* If value of absolute time is in the past no waiting will be set
|
||||||
|
* and function return immediately.
|
||||||
|
* \return true if the wait time was in the past
|
||||||
|
*/
|
||||||
|
bool absoluteWait(long long value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return the timer frequency in Hz
|
||||||
|
*/
|
||||||
|
unsigned int getTickFrequency() const { return 16384; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Rtc();
|
||||||
|
Rtc(const Rtc&)=delete;
|
||||||
|
Rtc& operator= (const Rtc&)=delete;
|
||||||
|
|
||||||
|
TimeConversion tc;
|
||||||
|
|
||||||
|
friend void absoluteDeepSleep(long long value);
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //RTC_H
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2017 by Matteo Michele Piazzolla *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "board_settings.h"
|
||||||
|
#include "stm32_sgm.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include "miosix.h"
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
extern unsigned char _preserve_start asm("_preserve_start");
|
||||||
|
extern unsigned char _preserve_end asm("_preserve_end");
|
||||||
|
|
||||||
|
static unsigned char *preserve_start=&_preserve_start;
|
||||||
|
static unsigned char *preserve_end=&_preserve_end;
|
||||||
|
|
||||||
|
SGM& SGM::instance()
|
||||||
|
{
|
||||||
|
static SGM singleton;
|
||||||
|
return singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
SGM::SGM()
|
||||||
|
{
|
||||||
|
/* Enable PWR clock */
|
||||||
|
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
|
||||||
|
|
||||||
|
/* Enable backup SRAM Clock */
|
||||||
|
RCC->AHB1ENR |= RCC_AHB1ENR_BKPSRAMEN;
|
||||||
|
|
||||||
|
enableWrite();
|
||||||
|
|
||||||
|
/* Enable Backup regulator */
|
||||||
|
PWR->CSR |= PWR_CSR_BRE;
|
||||||
|
|
||||||
|
/* Enable the Backup SRAM low power Regulator */
|
||||||
|
PWR->CSR |= PWR_CSR_BRE;
|
||||||
|
|
||||||
|
/* Wait for backup regulator */
|
||||||
|
while (!(PWR->CSR & (PWR_CSR_BRR)));
|
||||||
|
|
||||||
|
/* Retrive last reset reason and clear the pending flag */
|
||||||
|
readResetRegister();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the reset wasn't caused by software failure we cannot trust
|
||||||
|
* the content of the backup memory and we need to clear it.
|
||||||
|
*/
|
||||||
|
if(lastReset != RST_SW)
|
||||||
|
{
|
||||||
|
memset(preserve_start, 0, preserve_end-preserve_start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SGM::disableWrite()
|
||||||
|
{
|
||||||
|
/* Enable Backup Domain write protection */
|
||||||
|
PWR->CR &= ~PWR_CR_DBP;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SGM::enableWrite()
|
||||||
|
{
|
||||||
|
/* Disable Backup Domain write protection */
|
||||||
|
PWR->CR |= PWR_CR_DBP;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SGM::clearResetFlag()
|
||||||
|
{
|
||||||
|
RCC->CSR |= RCC_CSR_RMVF;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SGM::readResetRegister()
|
||||||
|
{
|
||||||
|
uint32_t resetReg = RCC->CSR;
|
||||||
|
clearResetFlag();
|
||||||
|
if(resetReg & RCC_CSR_LPWRRSTF)
|
||||||
|
{
|
||||||
|
lastReset = RST_LOW_PWR;
|
||||||
|
}
|
||||||
|
else if( resetReg & RCC_CSR_WWDGRSTF)
|
||||||
|
{
|
||||||
|
lastReset = RST_WINDOW_WDG;
|
||||||
|
}
|
||||||
|
else if( resetReg & RCC_CSR_WDGRSTF)
|
||||||
|
{
|
||||||
|
lastReset = RST_INDEPENDENT_WDG;
|
||||||
|
}
|
||||||
|
else if( resetReg & RCC_CSR_SFTRSTF)
|
||||||
|
{
|
||||||
|
lastReset = RST_SW;
|
||||||
|
}
|
||||||
|
else if( resetReg & RCC_CSR_PORRSTF)
|
||||||
|
{
|
||||||
|
lastReset = RST_POWER_ON;
|
||||||
|
}
|
||||||
|
else if( resetReg & RCC_CSR_PADRSTF)
|
||||||
|
{
|
||||||
|
lastReset = RST_PIN;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastReset = RST_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace miosix
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2017 by Matteo Michele Piazzolla *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "board_settings.h"
|
||||||
|
|
||||||
|
#define PRESERVE __attribute__((section(".preserve")))
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible causes for an STM32 reset
|
||||||
|
*/
|
||||||
|
enum ResetReason
|
||||||
|
{
|
||||||
|
RST_LOW_PWR=0,
|
||||||
|
RST_WINDOW_WDG=1,
|
||||||
|
RST_INDEPENDENT_WDG=2,
|
||||||
|
RST_SW=3,
|
||||||
|
RST_POWER_ON=4,
|
||||||
|
RST_PIN=5,
|
||||||
|
RST_UNKNOWN=6,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Driver for the STM32F2 and STM32F4 backup SRAM, here used as
|
||||||
|
* SafeGuard Memory, that is, a memory whose value is preseved across resets.
|
||||||
|
*/
|
||||||
|
class SGM
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \return an instance of this class (singleton)
|
||||||
|
*/
|
||||||
|
static SGM& instance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Temporarily disable writing to the safeguard memory.
|
||||||
|
* By deafult, from reset to when the contrsuctor of this class is called
|
||||||
|
* the safeguard memory is not writable. After the constructor is called,
|
||||||
|
* the safeguard memory is writable.
|
||||||
|
*/
|
||||||
|
void disableWrite();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make the safeguard memory writable again, after a call to disableWrite()
|
||||||
|
*/
|
||||||
|
void enableWrite();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the cause of the last reset of the microcontroller
|
||||||
|
*/
|
||||||
|
ResetReason lastResetReason() { return lastReset; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ResetReason lastReset;
|
||||||
|
|
||||||
|
SGM(const SGM&)=delete;
|
||||||
|
SGM& operator=(const SGM&)=delete;
|
||||||
|
|
||||||
|
SGM();
|
||||||
|
void readResetRegister();
|
||||||
|
void clearResetFlag();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2017 by Matteo Michele Piazzolla *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "stm32_wd.h"
|
||||||
|
#include "miosix.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
IWatchDog& IWatchDog::instance()
|
||||||
|
{
|
||||||
|
static IWatchDog singleton;
|
||||||
|
return singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IWatchDog::enable(int ms)
|
||||||
|
{
|
||||||
|
/* disable WD configuration protection */
|
||||||
|
IWDG->KR = 0x5555;
|
||||||
|
|
||||||
|
/* set prescaler divider to /32 */
|
||||||
|
IWDG->PR = 0x3;
|
||||||
|
|
||||||
|
/* set reload register */
|
||||||
|
IWDG->RLR = max(1,min(4096,ms))-1;
|
||||||
|
|
||||||
|
/* start the watchdog */
|
||||||
|
IWDG->KR = 0xCCCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IWatchDog::refresh()
|
||||||
|
{
|
||||||
|
IWDG->KR=0xAAAA;
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2017 by Matteo Michele Piazzolla *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Driver for the STM32 independent watchdog
|
||||||
|
*/
|
||||||
|
class IWatchDog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \return an instance of this class (singleton)
|
||||||
|
*/
|
||||||
|
static IWatchDog& instance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the watchdog
|
||||||
|
* \param ms reload period, from 1 (1 millisecond) to 4096 (4.096 seconds)
|
||||||
|
*/
|
||||||
|
void enable(int ms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the watchdog is not periodically reloaded at least within the
|
||||||
|
* period selected by enable, the microcontroller is reset.
|
||||||
|
* The datsheet says that the oscillator clocking the watchdog can be
|
||||||
|
* up to twice as fast, so it is recomended to reload the watchdog three
|
||||||
|
* to four times faster to prevent spurious resets.
|
||||||
|
*/
|
||||||
|
void refresh();
|
||||||
|
|
||||||
|
private:
|
||||||
|
IWatchDog(const IWatchDog&)=delete;
|
||||||
|
IWatchDog& operator=(const IWatchDog&)=delete;
|
||||||
|
IWatchDog() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,476 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013 by Terraneo Federico and Silvano Seva *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "stm32f2_f4_i2c.h"
|
||||||
|
#include <miosix.h>
|
||||||
|
#include <kernel/scheduler/scheduler.h>
|
||||||
|
|
||||||
|
using namespace miosix;
|
||||||
|
|
||||||
|
static volatile bool error; ///< Set to true by IRQ on error
|
||||||
|
static Thread *waiting=0; ///< Thread waiting for an operation to complete
|
||||||
|
|
||||||
|
/* In non-DMA mode the variables below are used to
|
||||||
|
* handle the reception of 2 or more bytes through
|
||||||
|
* an interrupt, avoiding the thread that calls recv
|
||||||
|
* to be locked in polling
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef I2C_WITH_DMA
|
||||||
|
static uint8_t *rxBuf = 0;
|
||||||
|
static unsigned int rxBufCnt = 0;
|
||||||
|
static unsigned int rxBufSize = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef I2C_WITH_DMA
|
||||||
|
/**
|
||||||
|
* DMA I2C rx end of transfer
|
||||||
|
*/
|
||||||
|
void __attribute__((naked)) DMA1_Stream0_IRQHandler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
asm volatile("bl _Z20I2C1rxDmaHandlerImplv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DMA I2C rx end of transfer actual implementation
|
||||||
|
*/
|
||||||
|
void __attribute__((used)) I2C1rxDmaHandlerImpl()
|
||||||
|
{
|
||||||
|
DMA1->LIFCR=DMA_LIFCR_CTCIF0
|
||||||
|
| DMA_LIFCR_CTEIF0
|
||||||
|
| DMA_LIFCR_CDMEIF0
|
||||||
|
| DMA_LIFCR_CFEIF0;
|
||||||
|
I2C1->CR1 |= I2C_CR1_STOP;
|
||||||
|
if(waiting==0) return;
|
||||||
|
waiting->IRQwakeup();
|
||||||
|
if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
|
||||||
|
Scheduler::IRQfindNextThread();
|
||||||
|
waiting=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DMA I2C tx end of transfer
|
||||||
|
*/
|
||||||
|
void DMA1_Stream7_IRQHandler()
|
||||||
|
{
|
||||||
|
DMA1->HIFCR=DMA_HIFCR_CTCIF7
|
||||||
|
| DMA_HIFCR_CTEIF7
|
||||||
|
| DMA_HIFCR_CDMEIF7
|
||||||
|
| DMA_HIFCR_CFEIF7;
|
||||||
|
|
||||||
|
//We can't just wake the thread because the I2C is double buffered, and this
|
||||||
|
//interrupt is fired at the same time as the second last byte is starting
|
||||||
|
//to be sent out of the bus. If we return now, the main code would send a
|
||||||
|
//stop condiotion too soon, and the last byte would never be sent. Instead,
|
||||||
|
//we change from DMA mode to IRQ mode, so when the second last byte is sent,
|
||||||
|
//that interrupt is fired and the last byte is sent out.
|
||||||
|
//Note that since no thread is awakened from this IRQ, there's no need for
|
||||||
|
//the saveContext(), restoreContext() and __attribute__((naked))
|
||||||
|
I2C1->CR2 &= ~I2C_CR2_DMAEN;
|
||||||
|
I2C1->CR2 |= I2C_CR2_ITBUFEN | I2C_CR2_ITEVTEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I2C address sent interrupt
|
||||||
|
*/
|
||||||
|
void __attribute__((naked)) I2C1_EV_IRQHandler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
asm volatile("bl _Z15I2C1HandlerImplv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I2C address sent interrupt actual implementation
|
||||||
|
*/
|
||||||
|
void __attribute__((used)) I2C1HandlerImpl()
|
||||||
|
{
|
||||||
|
#ifdef I2C_WITH_DMA
|
||||||
|
//When called to resolve the last byte not sent issue, clearing
|
||||||
|
//I2C_CR2_ITBUFEN prevents this interrupt being re-entered forever, as
|
||||||
|
//it does not send another byte to the I2C, so the interrupt would remain
|
||||||
|
//pending. When called after the start bit has been sent, clearing
|
||||||
|
//I2C_CR2_ITEVTEN prevents the same infinite re-enter as this interrupt
|
||||||
|
//does not start an address transmission, which is necessary to stop
|
||||||
|
//this interrupt from being pending
|
||||||
|
I2C1->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITBUFEN);
|
||||||
|
if(waiting==0) return;
|
||||||
|
#else
|
||||||
|
|
||||||
|
bool rxFinished = false;
|
||||||
|
|
||||||
|
/* If rxBuf is equal to zero means that we are sending the slave
|
||||||
|
address and this ISR is used to manage the address sent interrupt */
|
||||||
|
|
||||||
|
if(rxBuf == 0)
|
||||||
|
{
|
||||||
|
I2C1->CR2 &= ~I2C_CR2_ITEVTEN;
|
||||||
|
rxFinished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(I2C1->SR1 & I2C_SR1_RXNE)
|
||||||
|
{
|
||||||
|
rxBuf[rxBufCnt++] = I2C1->DR;
|
||||||
|
if(rxBufCnt >= rxBufSize)
|
||||||
|
{
|
||||||
|
I2C1->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITBUFEN);
|
||||||
|
rxFinished = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(waiting==0 || !rxFinished) return;
|
||||||
|
#endif
|
||||||
|
waiting->IRQwakeup();
|
||||||
|
if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
|
||||||
|
Scheduler::IRQfindNextThread();
|
||||||
|
waiting=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I2C error interrupt
|
||||||
|
*/
|
||||||
|
void __attribute__((naked)) I2C1_ER_IRQHandler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
asm volatile("bl _Z18I2C1errHandlerImplv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I2C error interrupt actual implementation
|
||||||
|
*/
|
||||||
|
void __attribute__((used)) I2C1errHandlerImpl()
|
||||||
|
{
|
||||||
|
I2C1->SR1=0; //Clear error flags
|
||||||
|
error=true;
|
||||||
|
if(waiting==0) return;
|
||||||
|
waiting->IRQwakeup();
|
||||||
|
if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
|
||||||
|
Scheduler::IRQfindNextThread();
|
||||||
|
waiting=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
//
|
||||||
|
// class I2C
|
||||||
|
//
|
||||||
|
|
||||||
|
I2C1Driver& I2C1Driver::instance()
|
||||||
|
{
|
||||||
|
static I2C1Driver singleton;
|
||||||
|
return singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2C1Driver::init()
|
||||||
|
{
|
||||||
|
//I2C devices are connected to APB1, whose frequency is the system clock
|
||||||
|
//divided by a value set in the PPRE1 bits of RCC->CFGR
|
||||||
|
const int ppre1=(RCC->CFGR & RCC_CFGR_PPRE1)>>10;
|
||||||
|
const int divFactor= (ppre1 & 1<<2) ? (2<<(ppre1 & 0x3)) : 1;
|
||||||
|
const int fpclk1=SystemCoreClock/divFactor;
|
||||||
|
//iprintf("fpclk1=%d\n",fpclk1);
|
||||||
|
|
||||||
|
{
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
|
||||||
|
#ifdef I2C_WITH_DMA
|
||||||
|
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
|
||||||
|
RCC_SYNC();
|
||||||
|
#endif
|
||||||
|
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; //Enable clock gating
|
||||||
|
RCC_SYNC();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef I2C_WITH_DMA
|
||||||
|
NVIC_SetPriority(DMA1_Stream7_IRQn,10);//Low priority for DMA
|
||||||
|
NVIC_ClearPendingIRQ(DMA1_Stream7_IRQn);//DMA1 stream 7 channel 1 = I2C1 TX
|
||||||
|
NVIC_EnableIRQ(DMA1_Stream7_IRQn);
|
||||||
|
|
||||||
|
NVIC_SetPriority(DMA1_Stream0_IRQn,10);//Low priority for DMA
|
||||||
|
NVIC_ClearPendingIRQ(DMA1_Stream0_IRQn);//DMA1 stream 0 channel 1 = I2C1 RX
|
||||||
|
NVIC_EnableIRQ(DMA1_Stream0_IRQn);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NVIC_SetPriority(I2C1_EV_IRQn,10);//Low priority for I2C
|
||||||
|
NVIC_ClearPendingIRQ(I2C1_EV_IRQn);
|
||||||
|
NVIC_EnableIRQ(I2C1_EV_IRQn);
|
||||||
|
|
||||||
|
NVIC_SetPriority(I2C1_ER_IRQn,10);
|
||||||
|
NVIC_ClearPendingIRQ(I2C1_ER_IRQn);
|
||||||
|
NVIC_EnableIRQ(I2C1_ER_IRQn);
|
||||||
|
|
||||||
|
I2C1->CR1=I2C_CR1_SWRST;
|
||||||
|
I2C1->CR1=0;
|
||||||
|
I2C1->CR2=fpclk1/1000000; //Set pclk frequency in MHz
|
||||||
|
//This sets the duration of both Thigh and Tlow (master mode))
|
||||||
|
const int i2cSpeed=100000; //100KHz
|
||||||
|
I2C1->CCR=std::max(4,fpclk1/(2*i2cSpeed)); //Duty=2, standard mode (100KHz)
|
||||||
|
//Datasheet says with I2C @ 100KHz, maximum SCL rise time is 1000ns
|
||||||
|
//Need to change formula if I2C needs to run @ 400kHz
|
||||||
|
I2C1->TRISE=fpclk1/1000000+1;
|
||||||
|
I2C1->CR1=I2C_CR1_PE; //Enable peripheral
|
||||||
|
}
|
||||||
|
|
||||||
|
bool I2C1Driver::send(unsigned char address,
|
||||||
|
const void *data, int len, bool sendStop)
|
||||||
|
{
|
||||||
|
address &= 0xfe; //Mask bit 0, as we are writing
|
||||||
|
if(start(address)==false || (I2C1->SR2 & I2C_SR2_TRA)==0)
|
||||||
|
{
|
||||||
|
I2C1->CR1 |= I2C_CR1_STOP;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
error=false;
|
||||||
|
|
||||||
|
#ifdef I2C_WITH_DMA
|
||||||
|
waiting=Thread::getCurrentThread();
|
||||||
|
DMA1_Stream7->CR=0;
|
||||||
|
DMA1_Stream7->PAR=reinterpret_cast<unsigned int>(&I2C1->DR);
|
||||||
|
DMA1_Stream7->M0AR=reinterpret_cast<unsigned int>(data);
|
||||||
|
DMA1_Stream7->NDTR=len;
|
||||||
|
DMA1_Stream7->FCR=DMA_SxFCR_FEIE
|
||||||
|
| DMA_SxFCR_DMDIS;
|
||||||
|
DMA1_Stream7->CR=DMA_SxCR_CHSEL_0 //Channel 1
|
||||||
|
| DMA_SxCR_MINC //Increment memory pointer
|
||||||
|
| DMA_SxCR_DIR_0 //Memory to peripheral
|
||||||
|
| DMA_SxCR_TCIE //Interrupt on transfer complete
|
||||||
|
| DMA_SxCR_TEIE //Interrupt on transfer error
|
||||||
|
| DMA_SxCR_DMEIE //Interrupt on direct mode error
|
||||||
|
| DMA_SxCR_EN; //Start DMA
|
||||||
|
|
||||||
|
//Enable DMA in the I2C peripheral *after* having configured the DMA
|
||||||
|
//peripheral, or a spurious interrupt is triggered
|
||||||
|
I2C1->CR2 |= I2C_CR2_DMAEN | I2C_CR2_ITERREN;
|
||||||
|
|
||||||
|
{
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
while(waiting)
|
||||||
|
{
|
||||||
|
waiting->IRQwait();
|
||||||
|
{
|
||||||
|
FastInterruptEnableLock eLock(dLock);
|
||||||
|
Thread::yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DMA1_Stream7->CR=0;
|
||||||
|
|
||||||
|
//The DMA interrupt routine changes the interrupt flags!
|
||||||
|
I2C1->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITERREN);
|
||||||
|
#else
|
||||||
|
|
||||||
|
I2C1->CR2 |= I2C_CR2_ITERREN;
|
||||||
|
|
||||||
|
const uint8_t *txData = reinterpret_cast<const uint8_t*>(data);
|
||||||
|
for(int i=0; i<len && !error; i++)
|
||||||
|
{
|
||||||
|
I2C1->DR = txData[i];
|
||||||
|
while(!(I2C1->SR1 & I2C_SR1_TXE)) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
I2C1->CR2 &= ~I2C_CR2_ITERREN;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The main idea of this driver is to avoid having the processor spinning
|
||||||
|
* waiting on some status flag. Why? Because I2C is slow compared to a
|
||||||
|
* modern processor. A 120MHz core does 1200 clock cycles in the time it
|
||||||
|
* takes to transfer a single bit through an I2C clocked at 100KHz.
|
||||||
|
* This time could be better spent doing a context switch and letting
|
||||||
|
* another thread do useful work, or (and Miosix does it automatically if
|
||||||
|
* there are no ready threads) sleeping the processor core. However,
|
||||||
|
* I'm quite disappointed by the STM32 I2C peripheral, as it seems overly
|
||||||
|
* complicated to use. To come close to achieving this goal I had to
|
||||||
|
* orchestrate among *four* interrupt handlers, two of the DMA, and two
|
||||||
|
* of the I2C itself. And in the end, what's even more disappointing, is
|
||||||
|
* that I haven't found a way to completely avoid spinning. Why?
|
||||||
|
* There's no interrupt that's fired when the stop bit is sent!
|
||||||
|
* And what's worse, the documentation says that after you set the stop
|
||||||
|
* bit in the CR2 register you can't write to it again (for example, to send
|
||||||
|
* a start bit because two i2c api calls are made back to back) until the
|
||||||
|
* MSL bit is cleared. But there's no interrupt tied to that event!
|
||||||
|
* What's worse, is that the closest interrupt flag I've found when doing
|
||||||
|
* an I2C send is fired when the last byte is *beginning* to be sent.
|
||||||
|
* Maybe I haven't searched well enough, but the fact is I found nothing,
|
||||||
|
* so this code below spins for 8 data bits of the last byte plus the ack
|
||||||
|
* bit, plus the stop bit. That's 12000 wasted CPU cycles. Thanks, ST...
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(sendStop)
|
||||||
|
{
|
||||||
|
I2C1->CR1 |= I2C_CR1_STOP;
|
||||||
|
while(I2C1->SR2 & I2C_SR2_MSL) ; //Wait for stop bit sent
|
||||||
|
} else {
|
||||||
|
// Dummy write, is the only way to clear
|
||||||
|
// the TxE flag if stop bit is not sent...
|
||||||
|
I2C1->DR = 0x00;
|
||||||
|
}
|
||||||
|
return !error;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool I2C1Driver::recv(unsigned char address, void *data, int len)
|
||||||
|
{
|
||||||
|
address |= 0x01;
|
||||||
|
if(start(address,len==1)==false || I2C1->SR2 & I2C_SR2_TRA)
|
||||||
|
{
|
||||||
|
I2C1->CR1 |= I2C_CR1_STOP;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
error=false;
|
||||||
|
waiting=Thread::getCurrentThread();
|
||||||
|
|
||||||
|
#ifdef I2C_WITH_DMA
|
||||||
|
I2C1->CR2 |= I2C_CR2_DMAEN | I2C_CR2_LAST | I2C_CR2_ITERREN;
|
||||||
|
|
||||||
|
DMA1_Stream0->CR=0;
|
||||||
|
DMA1_Stream0->PAR=reinterpret_cast<unsigned int>(&I2C1->DR);
|
||||||
|
DMA1_Stream0->M0AR=reinterpret_cast<unsigned int>(data);
|
||||||
|
DMA1_Stream0->NDTR=len;
|
||||||
|
DMA1_Stream0->FCR=DMA_SxFCR_FEIE
|
||||||
|
| DMA_SxFCR_DMDIS;
|
||||||
|
DMA1_Stream0->CR=DMA_SxCR_CHSEL_0 //Channel 1
|
||||||
|
| DMA_SxCR_MINC //Increment memory pointer
|
||||||
|
| DMA_SxCR_TCIE //Interrupt on transfer complete
|
||||||
|
| DMA_SxCR_TEIE //Interrupt on transfer error
|
||||||
|
| DMA_SxCR_DMEIE //Interrupt on direct mode error
|
||||||
|
| DMA_SxCR_EN; //Start DMA
|
||||||
|
|
||||||
|
{
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
while(waiting)
|
||||||
|
{
|
||||||
|
waiting->IRQwait();
|
||||||
|
{
|
||||||
|
FastInterruptEnableLock eLock(dLock);
|
||||||
|
Thread::yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DMA1_Stream7->CR=0;
|
||||||
|
|
||||||
|
I2C1->CR2 &= ~(I2C_CR2_DMAEN | I2C_CR2_LAST | I2C_CR2_ITERREN);
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* Since i2c data reception is a bit tricky (see ST's reference manual for
|
||||||
|
* further details), the thread that calls recv is yelded and reception is
|
||||||
|
* handled using interrupts only if the number of bytes to be received is
|
||||||
|
* greater than one.
|
||||||
|
*/
|
||||||
|
|
||||||
|
rxBuf = reinterpret_cast<uint8_t*>(data);
|
||||||
|
|
||||||
|
if(len > 1)
|
||||||
|
{
|
||||||
|
I2C1->CR2 |= I2C_CR2_ITERREN | I2C_CR2_ITEVTEN | I2C_CR2_ITBUFEN;
|
||||||
|
|
||||||
|
rxBufCnt = 0;
|
||||||
|
rxBufSize = len-2;
|
||||||
|
|
||||||
|
{
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
while(waiting)
|
||||||
|
{
|
||||||
|
waiting->IRQwait();
|
||||||
|
{
|
||||||
|
FastInterruptEnableLock eLock(dLock);
|
||||||
|
Thread::yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
I2C1->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITBUFEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
I2C1->CR1 &= ~I2C_CR1_ACK;
|
||||||
|
I2C1->CR1 |= I2C_CR1_STOP;
|
||||||
|
|
||||||
|
while(!(I2C1->SR1 & I2C_SR1_RXNE)) ;
|
||||||
|
rxBuf[len-1] = I2C1->DR;
|
||||||
|
|
||||||
|
//set pointer to rx buffer to zero after having used it, see i2c event ISR
|
||||||
|
rxBuf = 0;
|
||||||
|
|
||||||
|
I2C1->CR2 &= ~I2C_CR2_ITERREN;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while(I2C1->SR2 & I2C_SR2_MSL) ; //Wait for stop bit sent
|
||||||
|
return !error;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool I2C1Driver::start(unsigned char address, bool immediateNak)
|
||||||
|
{
|
||||||
|
/* Because the only way to send a restart is having the send function not
|
||||||
|
* sending a stop condition after the data transfer, here we have to manage
|
||||||
|
* a couple of things in SR1:
|
||||||
|
* - the BTF flag is set, cleared by a dummy read of DR
|
||||||
|
* - The Berr flag is set, this because the I2C harware detects the start
|
||||||
|
* condition sent without a stop before it as a misplaced start and
|
||||||
|
* rises an error
|
||||||
|
*/
|
||||||
|
|
||||||
|
I2C1->CR1 |= I2C_CR1_START | I2C_CR1_ACK;
|
||||||
|
if(!waitStatus1()) return false;
|
||||||
|
if((I2C1->SR1 & I2C_SR1_SB)==0) return false; //Must read SR1 to clear flag
|
||||||
|
I2C1->DR=address;
|
||||||
|
if(immediateNak) I2C1->CR1 &= ~I2C_CR1_ACK;
|
||||||
|
if(!waitStatus1()) return false;
|
||||||
|
if(I2C1->SR1 & I2C_SR1_AF) return false; //Must read SR1 and SR2
|
||||||
|
if((I2C1->SR2 & I2C_SR2_MSL)==0) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool I2C1Driver::waitStatus1()
|
||||||
|
{
|
||||||
|
error=false;
|
||||||
|
waiting=Thread::getCurrentThread();
|
||||||
|
I2C1->CR2 |= I2C_CR2_ITEVTEN | I2C_CR2_ITERREN;
|
||||||
|
{
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
while(waiting)
|
||||||
|
{
|
||||||
|
waiting->IRQwait();
|
||||||
|
{
|
||||||
|
FastInterruptEnableLock eLock(dLock);
|
||||||
|
Thread::yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
I2C1->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITERREN);
|
||||||
|
return !error;
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013 by Terraneo Federico and Silvano Seva *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef STM32F2_I2C_H
|
||||||
|
#define STM32F2_I2C_H
|
||||||
|
|
||||||
|
#include <interfaces/arch_registers.h>
|
||||||
|
#include "board_settings.h"
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Driver for the I2C1 peripheral in STM32F2 and STM32F4 under Miosix
|
||||||
|
*/
|
||||||
|
class I2C1Driver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \return an instance of this class (singleton)
|
||||||
|
*/
|
||||||
|
static I2C1Driver& instance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the peripheral. The only supported mode is 100KHz, master,
|
||||||
|
* 7bit address. Note that there is no need to manually call this member
|
||||||
|
* function as the constructor already inizializes the I2C peripheral.
|
||||||
|
* The only use of this member function is to reinitialize the peripheral
|
||||||
|
* if the microcontroller clock frequency or the APB prescaler is changed.
|
||||||
|
*/
|
||||||
|
void init();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send data to a device connected to the I2C bus
|
||||||
|
* \param address device address (bit 0 is forced at 0)
|
||||||
|
* \param data pointer with data to send
|
||||||
|
* \param len length of data to send
|
||||||
|
* \param sendStop if set to false disables the sending of a stop condition
|
||||||
|
* after data transmission has finished
|
||||||
|
* \return true on success, false on failure
|
||||||
|
*/
|
||||||
|
bool send(unsigned char address,
|
||||||
|
const void *data, int len, bool sendStop = true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive data from a device connected to the I2C bus
|
||||||
|
* \param address device address (bit 0 is forced at 1)
|
||||||
|
* \param data pointer to a buffer where data will be received
|
||||||
|
* \param len length of data to receive
|
||||||
|
* \return true on success, false on failure
|
||||||
|
*/
|
||||||
|
bool recv(unsigned char address, void *data, int len);
|
||||||
|
|
||||||
|
private:
|
||||||
|
I2C1Driver(const I2C1Driver&);
|
||||||
|
I2C1Driver& operator=(const I2C1Driver&);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor. Initializes the peripheral except the GPIOs, that must be
|
||||||
|
* set by the caller to the appropriate alternate function mode prior to
|
||||||
|
* creating an instance of this class.
|
||||||
|
* \param i2c pinter to the desired I2C peripheral, such as I2C1, I2C2, ...
|
||||||
|
*/
|
||||||
|
I2C1Driver() { init(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a start condition
|
||||||
|
* \param address
|
||||||
|
* \param immediateNak
|
||||||
|
* \return
|
||||||
|
*/
|
||||||
|
bool start(unsigned char address, bool immediateNak=false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait until until an interrupt occurs during the send start bit and
|
||||||
|
* send address phases of the i2c communication.
|
||||||
|
* \return true if the operation was successful, false on error
|
||||||
|
*/
|
||||||
|
bool waitStatus1();
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //STM32F2_I2C_H
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
#ifndef ARCH_REGISTERS_IMPL_H
|
||||||
|
#define ARCH_REGISTERS_IMPL_H
|
||||||
|
|
||||||
|
//Always include stm32f0xx.h before core_cm0.h, there's some nasty dependency
|
||||||
|
#include "CMSIS/Device/ST/STM32F0xx/Include/stm32f0xx.h"
|
||||||
|
#include "CMSIS/Include/core_cm0.h"
|
||||||
|
#include "CMSIS/Device/ST/STM32F0xx/Include/system_stm32f0xx.h"
|
||||||
|
|
||||||
|
#define RCC_SYNC() //Workaround for a bug in stm32f42x
|
||||||
|
|
||||||
|
#endif //ARCH_REGISTERS_IMPL_H
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2010 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef ARCH_SETTINGS_H
|
||||||
|
#define ARCH_SETTINGS_H
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Settings
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// \internal Size of vector to store registers during ctx switch (9*4=36Bytes)
|
||||||
|
/// Only sp and r4-r11 are saved here, since r0-r3,r12,lr,pc,xPSR and
|
||||||
|
/// old sp are saved by hardware on the process stack on Cortex M3 CPUs.
|
||||||
|
const unsigned char CTXSAVE_SIZE=9;
|
||||||
|
|
||||||
|
/// \internal some architectures save part of the context on their stack.
|
||||||
|
/// This constant is used to increase the stack size by the size of context
|
||||||
|
/// save frame. If zero, this architecture does not save anything on stack
|
||||||
|
/// during context save. Size is in bytes, not words.
|
||||||
|
/// MUST be divisible by 4.
|
||||||
|
const unsigned int CTXSAVE_ON_STACK=32;
|
||||||
|
|
||||||
|
/// \internal stack alignment for this specific architecture
|
||||||
|
const unsigned int CTXSAVE_STACK_ALIGNMENT=8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif /* ARCH_SETTINGS_H */
|
||||||
|
|
@ -0,0 +1,218 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2010, 2011, 2012 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
//Miosix kernel
|
||||||
|
|
||||||
|
#include "interfaces/portability.h"
|
||||||
|
#include "kernel/kernel.h"
|
||||||
|
#include "kernel/error.h"
|
||||||
|
#include "interfaces/bsp.h"
|
||||||
|
#include "kernel/scheduler/scheduler.h"
|
||||||
|
#include "kernel/scheduler/tick_interrupt.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* timer interrupt routine.
|
||||||
|
* Since inside naked functions only assembler code is allowed, this function
|
||||||
|
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||||
|
* the implementation code in ISR_preempt()
|
||||||
|
*/
|
||||||
|
void SysTick_Handler() __attribute__((naked));
|
||||||
|
void SysTick_Handler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
//Call ISR_preempt(). Name is a C++ mangled name.
|
||||||
|
asm volatile("bl _ZN14miosix_private11ISR_preemptEv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* software interrupt routine.
|
||||||
|
* Since inside naked functions only assembler code is allowed, this function
|
||||||
|
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||||
|
* the implementation code in ISR_yield()
|
||||||
|
*/
|
||||||
|
void SVC_Handler() __attribute__((naked));
|
||||||
|
void SVC_Handler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
//Call ISR_yield(). Name is a C++ mangled name.
|
||||||
|
asm volatile("bl _ZN14miosix_private9ISR_yieldEv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Auxiliary timer interupt routine.
|
||||||
|
* Used for variable lenght bursts in control based scheduler.
|
||||||
|
* Since inside naked functions only assembler code is allowed, this function
|
||||||
|
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||||
|
* the implementation code in ISR_yield()
|
||||||
|
*/
|
||||||
|
void XXX_IRQHandler() __attribute__((naked));
|
||||||
|
void XXX_IRQHandler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
//Call ISR_auxTimer(). Name is a C++ mangled name.
|
||||||
|
asm volatile("bl _ZN14miosix_private12ISR_auxTimerEv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
#endif //SCHED_TYPE_CONTROL_BASED
|
||||||
|
|
||||||
|
namespace miosix_private {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Called by the timer interrupt, preempt to next thread
|
||||||
|
* Declared noinline to avoid the compiler trying to inline it into the caller,
|
||||||
|
* which would violate the requirement on naked functions. Function is not
|
||||||
|
* static because otherwise the compiler optimizes it out...
|
||||||
|
*/
|
||||||
|
void ISR_preempt() __attribute__((noinline));
|
||||||
|
void ISR_preempt()
|
||||||
|
{
|
||||||
|
IRQstackOverflowCheck();
|
||||||
|
miosix::IRQtickInterrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Called by the software interrupt, yield to next thread
|
||||||
|
* Declared noinline to avoid the compiler trying to inline it into the caller,
|
||||||
|
* which would violate the requirement on naked functions. Function is not
|
||||||
|
* static because otherwise the compiler optimizes it out...
|
||||||
|
*/
|
||||||
|
void ISR_yield() __attribute__((noinline));
|
||||||
|
void ISR_yield()
|
||||||
|
{
|
||||||
|
IRQstackOverflowCheck();
|
||||||
|
miosix::Scheduler::IRQfindNextThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Auxiliary timer interupt routine.
|
||||||
|
* Used for variable lenght bursts in control based scheduler.
|
||||||
|
*/
|
||||||
|
void ISR_auxTimer() __attribute__((noinline));
|
||||||
|
void ISR_auxTimer()
|
||||||
|
{
|
||||||
|
IRQstackOverflowCheck();
|
||||||
|
miosix::Scheduler::IRQfindNextThread();//If the kernel is running, preempt
|
||||||
|
if(miosix::kernel_running!=0) miosix::tick_skew=true;
|
||||||
|
TIM2->SR=0;
|
||||||
|
}
|
||||||
|
#endif //SCHED_TYPE_CONTROL_BASED
|
||||||
|
|
||||||
|
void IRQstackOverflowCheck()
|
||||||
|
{
|
||||||
|
const unsigned int watermarkSize=miosix::WATERMARK_LEN/sizeof(unsigned int);
|
||||||
|
for(unsigned int i=0;i<watermarkSize;i++)
|
||||||
|
{
|
||||||
|
if(miosix::cur->watermark[i]!=miosix::WATERMARK_FILL)
|
||||||
|
miosix::errorHandler(miosix::STACK_OVERFLOW);
|
||||||
|
}
|
||||||
|
if(miosix::cur->ctxsave[0] < reinterpret_cast<unsigned int>(
|
||||||
|
miosix::cur->watermark+watermarkSize))
|
||||||
|
miosix::errorHandler(miosix::STACK_OVERFLOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRQsystemReboot()
|
||||||
|
{
|
||||||
|
NVIC_SystemReset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp,
|
||||||
|
void *argv)
|
||||||
|
{
|
||||||
|
unsigned int *stackPtr=sp;
|
||||||
|
stackPtr--; //Stack is full descending, so decrement first
|
||||||
|
*stackPtr=0x01000000; stackPtr--; //--> xPSR
|
||||||
|
*stackPtr=reinterpret_cast<unsigned long>(
|
||||||
|
&miosix::Thread::threadLauncher); stackPtr--; //--> pc
|
||||||
|
*stackPtr=0xffffffff; stackPtr--; //--> lr
|
||||||
|
*stackPtr=0; stackPtr--; //--> r12
|
||||||
|
*stackPtr=0; stackPtr--; //--> r3
|
||||||
|
*stackPtr=0; stackPtr--; //--> r2
|
||||||
|
*stackPtr=reinterpret_cast<unsigned long >(argv); stackPtr--; //--> r1
|
||||||
|
*stackPtr=reinterpret_cast<unsigned long >(pc); //--> r0
|
||||||
|
|
||||||
|
ctxsave[0]=reinterpret_cast<unsigned long>(stackPtr); //--> psp
|
||||||
|
//leaving the content of r4-r11 uninitialized
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRQportableStartKernel()
|
||||||
|
{
|
||||||
|
NVIC_SetPriority(SVC_IRQn,3);//High priority for SVC (Max=0, min=15)
|
||||||
|
NVIC_SetPriority(SysTick_IRQn,3);//High priority for SysTick (Max=0, min=15)
|
||||||
|
SysTick->LOAD=SystemCoreClock/miosix::TICK_FREQ;
|
||||||
|
//Start SysTick, set to generate interrupts
|
||||||
|
SysTick->CTRL=SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk |
|
||||||
|
SysTick_CTRL_CLKSOURCE_Msk;
|
||||||
|
|
||||||
|
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||||
|
AuxiliaryTimer::IRQinit();
|
||||||
|
#endif //SCHED_TYPE_CONTROL_BASED
|
||||||
|
|
||||||
|
//create a temporary space to save current registers. This data is useless
|
||||||
|
//since there's no way to stop the sheduler, but we need to save it anyway.
|
||||||
|
unsigned int s_ctxsave[miosix::CTXSAVE_SIZE];
|
||||||
|
ctxsave=s_ctxsave;//make global ctxsave point to it
|
||||||
|
//Note, we can't use enableInterrupts() now since the call is not mathced
|
||||||
|
//by a call to disableInterrupts()
|
||||||
|
__enable_irq();
|
||||||
|
miosix::Thread::yield();
|
||||||
|
//Never reaches here
|
||||||
|
}
|
||||||
|
|
||||||
|
void sleepCpu()
|
||||||
|
{
|
||||||
|
__WFI();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||||
|
#error "AUX_TIMER not yet implemented"
|
||||||
|
void AuxiliaryTimer::IRQinit()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int AuxiliaryTimer::IRQgetValue()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuxiliaryTimer::IRQsetValue(int x)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif //SCHED_TYPE_CONTROL_BASED
|
||||||
|
|
||||||
|
} //namespace miosix_private
|
||||||
|
|
@ -0,0 +1,165 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2010, 2011, 2012 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
//Miosix kernel
|
||||||
|
|
||||||
|
#ifndef PORTABILITY_IMPL_H
|
||||||
|
#define PORTABILITY_IMPL_H
|
||||||
|
|
||||||
|
#include "interfaces/arch_registers.h"
|
||||||
|
#include "config/miosix_settings.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Drivers
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This pointer is used by the kernel, and should not be used by end users.
|
||||||
|
* this is a pointer to a location where to store the thread's registers during
|
||||||
|
* context switch. It requires C linkage to be used inside asm statement.
|
||||||
|
* Registers are saved in the following order:
|
||||||
|
* *ctxsave+32 --> r11
|
||||||
|
* *ctxsave+28 --> r10
|
||||||
|
* *ctxsave+24 --> r9
|
||||||
|
* *ctxsave+20 --> r8
|
||||||
|
* *ctxsave+16 --> r7
|
||||||
|
* *ctxsave+12 --> r6
|
||||||
|
* *ctxsave+8 --> r5
|
||||||
|
* *ctxsave+4 --> r4
|
||||||
|
* *ctxsave+0 --> psp
|
||||||
|
*/
|
||||||
|
extern "C" {
|
||||||
|
extern volatile unsigned int *ctxsave;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* \def saveContext()
|
||||||
|
* Save context from an interrupt<br>
|
||||||
|
* Must be the first line of an IRQ where a context switch can happen.
|
||||||
|
* The IRQ must be "naked" to prevent the compiler from generating context save.
|
||||||
|
*
|
||||||
|
* A note on the dmb instruction, without it a race condition was observed
|
||||||
|
* between pauseKernel() and IRQfindNextThread(). pauseKernel() uses an strex
|
||||||
|
* instruction to store a value in the global variable kernel_running which is
|
||||||
|
* tested by the context switch code in IRQfindNextThread(). Without the memory
|
||||||
|
* barrier IRQfindNextThread() would occasionally read the previous value and
|
||||||
|
* perform a context switch while the kernel was paused, leading to deadlock.
|
||||||
|
* The failure was only observed within the exception_test() in the testsuite
|
||||||
|
* running on the stm32f429zi_stm32f4discovery.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define saveContext() \
|
||||||
|
{ \
|
||||||
|
asm volatile("push {lr} \n\t" /*save lr on MAIN stack*/ \
|
||||||
|
"mrs r1, psp \n\t" /*get PROCESS stack pointer*/ \
|
||||||
|
"ldr r0, =ctxsave \n\t" /*get current context*/ \
|
||||||
|
"ldr r0, [r0] \n\t" \
|
||||||
|
"stmia r0, {r1,r4-r7} \n\t" /*save PROCESS sp + r4-r7*/ \
|
||||||
|
"mov r4, r8 \n\t" \
|
||||||
|
"mov r5, r9 \n\t" \
|
||||||
|
"mov r6, r10 \n\t" \
|
||||||
|
"mov r7, r11 \n\t" \
|
||||||
|
"stmia r0, {r4-r7} \n\t" \
|
||||||
|
"dmb \n\t" \
|
||||||
|
); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \def restoreContext()
|
||||||
|
* Restore context in an IRQ where saveContext() is used. Must be the last line
|
||||||
|
* of an IRQ where a context switch can happen. The IRQ must be "naked" to
|
||||||
|
* prevent the compiler from generating context restore.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define restoreContext() \
|
||||||
|
{ \
|
||||||
|
asm volatile("ldr r0, =ctxsave \n\t" /*get current context*/ \
|
||||||
|
"ldr r0, [r0] \n\t" \
|
||||||
|
"ldmia r0, {r1,r4-r7} \n\t" /*pop r8-r11 saving in r4-r7*/ \
|
||||||
|
"msr psp, r1 \n\t" /*restore PROCESS sp*/ \
|
||||||
|
"ldmia r0!, {r0-r3} \n\t" \
|
||||||
|
"mov r9, r0 \n\t" \
|
||||||
|
"mov r10, r1 \n\t" \
|
||||||
|
"mov r11, r2 \n\t" \
|
||||||
|
"mov r11, r3 \n\t" \
|
||||||
|
"pop {pc} \n\t" /*return*/ \
|
||||||
|
); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace miosix_private {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Drivers
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
inline void doYield()
|
||||||
|
{
|
||||||
|
asm volatile("movs r3, #0\n\t"
|
||||||
|
"svc 0"
|
||||||
|
:::"r3");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void doDisableInterrupts()
|
||||||
|
{
|
||||||
|
// Documentation says __disable_irq() disables all interrupts with
|
||||||
|
// configurable priority, so also SysTick and SVC.
|
||||||
|
// No need to disable faults with __disable_fault_irq()
|
||||||
|
__disable_irq();
|
||||||
|
//The new fastDisableInterrupts/fastEnableInterrupts are inline, so there's
|
||||||
|
//the need for a memory barrier to avoid aggressive reordering
|
||||||
|
asm volatile("":::"memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void doEnableInterrupts()
|
||||||
|
{
|
||||||
|
__enable_irq();
|
||||||
|
//The new fastDisableInterrupts/fastEnableInterrupts are inline, so there's
|
||||||
|
//the need for a memory barrier to avoid aggressive reordering
|
||||||
|
asm volatile("":::"memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool checkAreInterruptsEnabled()
|
||||||
|
{
|
||||||
|
register int i;
|
||||||
|
asm volatile("mrs %0, primask \n\t":"=r"(i));
|
||||||
|
if(i!=0) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} //namespace miosix_private
|
||||||
|
|
||||||
|
#endif //PORTABILITY_IMPL_H
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
#ifndef ARCH_REGISTERS_IMPL_H
|
||||||
|
#define ARCH_REGISTERS_IMPL_H
|
||||||
|
|
||||||
|
//Always include stm32f10x.h before core_cm3.h, there's some nasty dependency
|
||||||
|
#include "CMSIS/Device/ST/STM32F10x/Include/stm32f10x.h"
|
||||||
|
#include "CMSIS/Include/core_cm3.h"
|
||||||
|
#include "CMSIS/Device/ST/STM32F10x/Include/system_stm32f10x.h"
|
||||||
|
|
||||||
|
#define RCC_SYNC() //Workaround for a bug in stm32f42x
|
||||||
|
|
||||||
|
#endif //ARCH_REGISTERS_IMPL_H
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2010 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef ARCH_SETTINGS_H
|
||||||
|
#define ARCH_SETTINGS_H
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Settings
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// \internal Size of vector to store registers during ctx switch (9*4=36Bytes)
|
||||||
|
/// Only sp and r4-r11 are saved here, since r0-r3,r12,lr,pc,xPSR and
|
||||||
|
/// old sp are saved by hardware on the process stack on Cortex M3 CPUs.
|
||||||
|
const unsigned char CTXSAVE_SIZE=9;
|
||||||
|
|
||||||
|
/// \internal some architectures save part of the context on their stack.
|
||||||
|
/// This constant is used to increase the stack size by the size of context
|
||||||
|
/// save frame. If zero, this architecture does not save anything on stack
|
||||||
|
/// during context save. Size is in bytes, not words.
|
||||||
|
/// MUST be divisible by 4.
|
||||||
|
const unsigned int CTXSAVE_ON_STACK=32;
|
||||||
|
|
||||||
|
/// \internal stack alignment for this specific architecture
|
||||||
|
const unsigned int CTXSAVE_STACK_ALIGNMENT=8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif /* ARCH_SETTINGS_H */
|
||||||
|
|
@ -0,0 +1,273 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2010, 2011, 2012 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
//Miosix kernel
|
||||||
|
|
||||||
|
#include "interfaces/portability.h"
|
||||||
|
#include "kernel/kernel.h"
|
||||||
|
#include "kernel/error.h"
|
||||||
|
#include "interfaces/bsp.h"
|
||||||
|
#include "kernel/scheduler/scheduler.h"
|
||||||
|
#include "kernel/scheduler/tick_interrupt.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* timer interrupt routine.
|
||||||
|
* Since inside naked functions only assembler code is allowed, this function
|
||||||
|
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||||
|
* the implementation code in ISR_preempt()
|
||||||
|
*/
|
||||||
|
void SysTick_Handler() __attribute__((naked));
|
||||||
|
void SysTick_Handler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
//Call ISR_preempt(). Name is a C++ mangled name.
|
||||||
|
asm volatile("bl _ZN14miosix_private11ISR_preemptEv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* software interrupt routine.
|
||||||
|
* Since inside naked functions only assembler code is allowed, this function
|
||||||
|
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||||
|
* the implementation code in ISR_yield()
|
||||||
|
*/
|
||||||
|
void SVC_Handler() __attribute__((naked));
|
||||||
|
void SVC_Handler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
//Call ISR_yield(). Name is a C++ mangled name.
|
||||||
|
asm volatile("bl _ZN14miosix_private9ISR_yieldEv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Auxiliary timer interupt routine.
|
||||||
|
* Used for variable lenght bursts in control based scheduler.
|
||||||
|
* Since inside naked functions only assembler code is allowed, this function
|
||||||
|
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||||
|
* the implementation code in ISR_yield()
|
||||||
|
*/
|
||||||
|
void TIM2_IRQHandler() __attribute__((naked));
|
||||||
|
void TIM2_IRQHandler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
//Call ISR_auxTimer(). Name is a C++ mangled name.
|
||||||
|
asm volatile("bl _ZN14miosix_private12ISR_auxTimerEv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
#endif //SCHED_TYPE_CONTROL_BASED
|
||||||
|
|
||||||
|
namespace miosix_private {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Called by the timer interrupt, preempt to next thread
|
||||||
|
* Declared noinline to avoid the compiler trying to inline it into the caller,
|
||||||
|
* which would violate the requirement on naked functions. Function is not
|
||||||
|
* static because otherwise the compiler optimizes it out...
|
||||||
|
*/
|
||||||
|
void ISR_preempt() __attribute__((noinline));
|
||||||
|
void ISR_preempt()
|
||||||
|
{
|
||||||
|
IRQstackOverflowCheck();
|
||||||
|
miosix::IRQtickInterrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Called by the software interrupt, yield to next thread
|
||||||
|
* Declared noinline to avoid the compiler trying to inline it into the caller,
|
||||||
|
* which would violate the requirement on naked functions. Function is not
|
||||||
|
* static because otherwise the compiler optimizes it out...
|
||||||
|
*/
|
||||||
|
void ISR_yield() __attribute__((noinline));
|
||||||
|
void ISR_yield()
|
||||||
|
{
|
||||||
|
IRQstackOverflowCheck();
|
||||||
|
miosix::Scheduler::IRQfindNextThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Auxiliary timer interupt routine.
|
||||||
|
* Used for variable lenght bursts in control based scheduler.
|
||||||
|
*/
|
||||||
|
void ISR_auxTimer() __attribute__((noinline));
|
||||||
|
void ISR_auxTimer()
|
||||||
|
{
|
||||||
|
IRQstackOverflowCheck();
|
||||||
|
miosix::Scheduler::IRQfindNextThread();//If the kernel is running, preempt
|
||||||
|
if(miosix::kernel_running!=0) miosix::tick_skew=true;
|
||||||
|
TIM2->SR=0;
|
||||||
|
}
|
||||||
|
#endif //SCHED_TYPE_CONTROL_BASED
|
||||||
|
|
||||||
|
void IRQstackOverflowCheck()
|
||||||
|
{
|
||||||
|
const unsigned int watermarkSize=miosix::WATERMARK_LEN/sizeof(unsigned int);
|
||||||
|
for(unsigned int i=0;i<watermarkSize;i++)
|
||||||
|
{
|
||||||
|
if(miosix::cur->watermark[i]!=miosix::WATERMARK_FILL)
|
||||||
|
miosix::errorHandler(miosix::STACK_OVERFLOW);
|
||||||
|
}
|
||||||
|
if(miosix::cur->ctxsave[0] < reinterpret_cast<unsigned int>(
|
||||||
|
miosix::cur->watermark+watermarkSize))
|
||||||
|
miosix::errorHandler(miosix::STACK_OVERFLOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRQsystemReboot()
|
||||||
|
{
|
||||||
|
NVIC_SystemReset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp,
|
||||||
|
void *argv)
|
||||||
|
{
|
||||||
|
unsigned int *stackPtr=sp;
|
||||||
|
stackPtr--; //Stack is full descending, so decrement first
|
||||||
|
*stackPtr=0x01000000; stackPtr--; //--> xPSR
|
||||||
|
*stackPtr=reinterpret_cast<unsigned long>(
|
||||||
|
&miosix::Thread::threadLauncher); stackPtr--; //--> pc
|
||||||
|
*stackPtr=0xffffffff; stackPtr--; //--> lr
|
||||||
|
*stackPtr=0; stackPtr--; //--> r12
|
||||||
|
*stackPtr=0; stackPtr--; //--> r3
|
||||||
|
*stackPtr=0; stackPtr--; //--> r2
|
||||||
|
*stackPtr=reinterpret_cast<unsigned long >(argv); stackPtr--; //--> r1
|
||||||
|
*stackPtr=reinterpret_cast<unsigned long >(pc); //--> r0
|
||||||
|
|
||||||
|
ctxsave[0]=reinterpret_cast<unsigned long>(stackPtr); //--> psp
|
||||||
|
//leaving the content of r4-r11 uninitialized
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_PROCESSES
|
||||||
|
|
||||||
|
void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp,
|
||||||
|
void *argv, unsigned int *gotBase)
|
||||||
|
{
|
||||||
|
unsigned int *stackPtr=sp;
|
||||||
|
stackPtr--; //Stack is full descending, so decrement first
|
||||||
|
*stackPtr=0x01000000; stackPtr--; //--> xPSR
|
||||||
|
*stackPtr=reinterpret_cast<unsigned long>(pc); stackPtr--; //--> pc
|
||||||
|
*stackPtr=0xffffffff; stackPtr--; //--> lr
|
||||||
|
*stackPtr=0; stackPtr--; //--> r12
|
||||||
|
*stackPtr=0; stackPtr--; //--> r3
|
||||||
|
*stackPtr=0; stackPtr--; //--> r2
|
||||||
|
*stackPtr=0; stackPtr--; //--> r1
|
||||||
|
*stackPtr=reinterpret_cast<unsigned long >(argv); //--> r0
|
||||||
|
|
||||||
|
ctxsave[0]=reinterpret_cast<unsigned long>(stackPtr); //--> psp
|
||||||
|
ctxsave[6]=reinterpret_cast<unsigned long>(gotBase); //--> r9
|
||||||
|
//leaving the content of r4-r8,r10-r11 uninitialized
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //WITH_PROCESSES
|
||||||
|
|
||||||
|
void IRQportableStartKernel()
|
||||||
|
{
|
||||||
|
//Enable fault handlers
|
||||||
|
SCB->SHCSR |= SCB_SHCSR_USGFAULTENA | SCB_SHCSR_BUSFAULTENA
|
||||||
|
| SCB_SHCSR_MEMFAULTENA;
|
||||||
|
//Enable traps for division by zero. Trap for unaligned memory access
|
||||||
|
//was removed as gcc starting from 4.7.2 generates unaligned accesses by
|
||||||
|
//default (https://www.gnu.org/software/gcc/gcc-4.7/changes.html)
|
||||||
|
SCB->CCR |= SCB_CCR_DIV_0_TRP;
|
||||||
|
NVIC_SetPriorityGrouping(7);//This should disable interrupt nesting
|
||||||
|
NVIC_SetPriority(SVCall_IRQn,3);//High priority for SVC (Max=0, min=15)
|
||||||
|
NVIC_SetPriority(SysTick_IRQn,3);//High priority for SysTick (Max=0, min=15)
|
||||||
|
SysTick->LOAD=SystemCoreClock/miosix::TICK_FREQ;
|
||||||
|
//Start SysTick, set to generate interrupts
|
||||||
|
SysTick->CTRL=SysTick_CTRL_ENABLE | SysTick_CTRL_TICKINT |
|
||||||
|
SysTick_CTRL_CLKSOURCE;
|
||||||
|
|
||||||
|
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||||
|
AuxiliaryTimer::IRQinit();
|
||||||
|
#endif //SCHED_TYPE_CONTROL_BASED
|
||||||
|
|
||||||
|
//create a temporary space to save current registers. This data is useless
|
||||||
|
//since there's no way to stop the sheduler, but we need to save it anyway.
|
||||||
|
unsigned int s_ctxsave[miosix::CTXSAVE_SIZE];
|
||||||
|
ctxsave=s_ctxsave;//make global ctxsave point to it
|
||||||
|
//Note, we can't use enableInterrupts() now since the call is not mathced
|
||||||
|
//by a call to disableInterrupts()
|
||||||
|
__enable_fault_irq();
|
||||||
|
__enable_irq();
|
||||||
|
miosix::Thread::yield();
|
||||||
|
//Never reaches here
|
||||||
|
}
|
||||||
|
|
||||||
|
void sleepCpu()
|
||||||
|
{
|
||||||
|
__WFI();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||||
|
void AuxiliaryTimer::IRQinit()
|
||||||
|
{
|
||||||
|
RCC->APB1ENR|=RCC_APB1ENR_TIM2EN;
|
||||||
|
RCC_SYNC();
|
||||||
|
DBGMCU->CR|=DBGMCU_CR_DBG_TIM2_STOP; //Tim2 stops while debugging
|
||||||
|
TIM2->CR1=0; //Upcounter, not started, no special options
|
||||||
|
TIM2->CR2=0; //No special options
|
||||||
|
TIM2->SMCR=0; //No external trigger
|
||||||
|
TIM2->CNT=0; //Clear timer
|
||||||
|
TIM2->PSC=(SystemCoreClock/miosix::AUX_TIMER_CLOCK)-1;
|
||||||
|
TIM2->ARR=0xffff; //Count from zero to 0xffff
|
||||||
|
TIM2->DIER=TIM_DIER_CC1IE; //Enable interrupt on compare
|
||||||
|
TIM2->CCR1=0xffff; //This will be initialized later with setValue
|
||||||
|
NVIC_SetPriority(TIM2_IRQn,3);//High priority for TIM2 (Max=0, min=15)
|
||||||
|
NVIC_EnableIRQ(TIM2_IRQn);
|
||||||
|
TIM2->CR1=TIM_CR1_CEN; //Start timer
|
||||||
|
//This is very important: without this the prescaler shadow register may
|
||||||
|
//not be updated
|
||||||
|
TIM2->EGR=TIM_EGR_UG;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AuxiliaryTimer::IRQgetValue()
|
||||||
|
{
|
||||||
|
return static_cast<int>(TIM2->CNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuxiliaryTimer::IRQsetValue(int x)
|
||||||
|
{
|
||||||
|
TIM2->CR1=0; //Stop timer since changing CNT or CCR1 while running fails
|
||||||
|
TIM2->CNT=0;
|
||||||
|
TIM2->CCR1=static_cast<unsigned short>(std::min(x,0xffff));
|
||||||
|
TIM2->CR1=TIM_CR1_CEN; //Start timer again
|
||||||
|
//The above instructions cause a spurious if not called within the
|
||||||
|
//timer 2 IRQ (This happens if called from an SVC).
|
||||||
|
//Clearing the pending bit prevents this spurious interrupt
|
||||||
|
TIM2->SR=0;
|
||||||
|
NVIC_ClearPendingIRQ(TIM2_IRQn);
|
||||||
|
}
|
||||||
|
#endif //SCHED_TYPE_CONTROL_BASED
|
||||||
|
|
||||||
|
} //namespace miosix_private
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2010, 2011, 2012 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
//Miosix kernel
|
||||||
|
|
||||||
|
#ifndef PORTABILITY_IMPL_H
|
||||||
|
#define PORTABILITY_IMPL_H
|
||||||
|
|
||||||
|
#include "interfaces/arch_registers.h"
|
||||||
|
#include "config/miosix_settings.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Drivers
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This pointer is used by the kernel, and should not be used by end users.
|
||||||
|
* this is a pointer to a location where to store the thread's registers during
|
||||||
|
* context switch. It requires C linkage to be used inside asm statement.
|
||||||
|
* Registers are saved in the following order:
|
||||||
|
* *ctxsave+32 --> r11
|
||||||
|
* *ctxsave+28 --> r10
|
||||||
|
* *ctxsave+24 --> r9
|
||||||
|
* *ctxsave+20 --> r8
|
||||||
|
* *ctxsave+16 --> r7
|
||||||
|
* *ctxsave+12 --> r6
|
||||||
|
* *ctxsave+8 --> r5
|
||||||
|
* *ctxsave+4 --> r4
|
||||||
|
* *ctxsave+0 --> psp
|
||||||
|
*/
|
||||||
|
extern "C" {
|
||||||
|
extern volatile unsigned int *ctxsave;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* \def saveContext()
|
||||||
|
* Save context from an interrupt<br>
|
||||||
|
* Must be the first line of an IRQ where a context switch can happen.
|
||||||
|
* The IRQ must be "naked" to prevent the compiler from generating context save.
|
||||||
|
*
|
||||||
|
* A note on the dmb instruction, without it a race condition was observed
|
||||||
|
* between pauseKernel() and IRQfindNextThread(). pauseKernel() uses an strex
|
||||||
|
* instruction to store a value in the global variable kernel_running which is
|
||||||
|
* tested by the context switch code in IRQfindNextThread(). Without the memory
|
||||||
|
* barrier IRQfindNextThread() would occasionally read the previous value and
|
||||||
|
* perform a context switch while the kernel was paused, leading to deadlock.
|
||||||
|
* The failure was only observed within the exception_test() in the testsuite
|
||||||
|
* running on the stm32f429zi_stm32f4discovery.
|
||||||
|
*/
|
||||||
|
#define saveContext() \
|
||||||
|
{ \
|
||||||
|
asm volatile("stmdb sp!, {lr} \n\t" /*save lr on MAIN stack*/ \
|
||||||
|
"mrs r1, psp \n\t" /*get PROCESS stack pointer*/ \
|
||||||
|
"ldr r0, =ctxsave \n\t" /*get current context*/ \
|
||||||
|
"ldr r0, [r0] \n\t" \
|
||||||
|
"stmia r0, {r1,r4-r11} \n\t" /*save PROCESS sp + r4-r11*/ \
|
||||||
|
"dmb \n\t" \
|
||||||
|
); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \def restoreContext()
|
||||||
|
* Restore context in an IRQ where saveContext() is used. Must be the last line
|
||||||
|
* of an IRQ where a context switch can happen. The IRQ must be "naked" to
|
||||||
|
* prevent the compiler from generating context restore.
|
||||||
|
*/
|
||||||
|
#define restoreContext() \
|
||||||
|
{ \
|
||||||
|
asm volatile("ldr r0, =ctxsave \n\t" /*get current context*/ \
|
||||||
|
"ldr r0, [r0] \n\t" \
|
||||||
|
"ldmia r0, {r1,r4-r11} \n\t" /*restore r4-r11 + r1=psp*/ \
|
||||||
|
"msr psp, r1 \n\t" /*restore PROCESS sp*/ \
|
||||||
|
"ldmia sp!, {pc} \n\t" /*return*/ \
|
||||||
|
); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace miosix_private {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Drivers
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
inline void doYield()
|
||||||
|
{
|
||||||
|
asm volatile("movs r3, #0\n\t"
|
||||||
|
"svc 0"
|
||||||
|
:::"r3");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void doDisableInterrupts()
|
||||||
|
{
|
||||||
|
// Documentation says __disable_irq() disables all interrupts with
|
||||||
|
// configurable priority, so also SysTick and SVC.
|
||||||
|
// No need to disable faults with __disable_fault_irq()
|
||||||
|
__disable_irq();
|
||||||
|
//The new fastDisableInterrupts/fastEnableInterrupts are inline, so there's
|
||||||
|
//the need for a memory barrier to avoid aggressive reordering
|
||||||
|
asm volatile("":::"memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void doEnableInterrupts()
|
||||||
|
{
|
||||||
|
__enable_irq();
|
||||||
|
//The new fastDisableInterrupts/fastEnableInterrupts are inline, so there's
|
||||||
|
//the need for a memory barrier to avoid aggressive reordering
|
||||||
|
asm volatile("":::"memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool checkAreInterruptsEnabled()
|
||||||
|
{
|
||||||
|
register int i;
|
||||||
|
asm volatile("mrs %0, primask \n\t":"=r"(i));
|
||||||
|
if(i!=0) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} //namespace miosix_private
|
||||||
|
|
||||||
|
#endif //PORTABILITY_IMPL_H
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2015-2020 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Settings
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// \internal Size of vector to store registers during ctx switch (9*4=36Bytes)
|
||||||
|
/// Only sp and r4-r11 are saved here, since r0-r3,r12,lr,pc,xPSR and
|
||||||
|
/// old sp are saved by hardware on the process stack on Cortex CPUs.
|
||||||
|
const unsigned char CTXSAVE_SIZE=9;
|
||||||
|
|
||||||
|
/// \internal some architectures save part of the context on their stack.
|
||||||
|
/// This constant is used to increase the stack size by the size of context
|
||||||
|
/// save frame. If zero, this architecture does not save anything on stack
|
||||||
|
/// during context save. Size is in bytes, not words.
|
||||||
|
/// MUST be divisible by 4.
|
||||||
|
const unsigned int CTXSAVE_ON_STACK=32;
|
||||||
|
|
||||||
|
/// \internal stack alignment for this specific architecture
|
||||||
|
const unsigned int CTXSAVE_STACK_ALIGNMENT=8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,233 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2010-2020 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
//Miosix kernel
|
||||||
|
|
||||||
|
#include "interfaces/portability.h"
|
||||||
|
#include "kernel/kernel.h"
|
||||||
|
#include "kernel/error.h"
|
||||||
|
#include "interfaces/bsp.h"
|
||||||
|
#include "kernel/scheduler/scheduler.h"
|
||||||
|
#include "kernel/scheduler/tick_interrupt.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
extern void (* const __Vectors[])();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* timer interrupt routine.
|
||||||
|
* Since inside naked functions only assembler code is allowed, this function
|
||||||
|
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||||
|
* the implementation code in ISR_preempt()
|
||||||
|
*/
|
||||||
|
void SysTick_Handler() __attribute__((naked));
|
||||||
|
void SysTick_Handler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
//Call ISR_preempt(). Name is a C++ mangled name.
|
||||||
|
asm volatile("bl _ZN14miosix_private11ISR_preemptEv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* software interrupt routine.
|
||||||
|
* Since inside naked functions only assembler code is allowed, this function
|
||||||
|
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||||
|
* the implementation code in ISR_yield()
|
||||||
|
*/
|
||||||
|
void SVC_Handler() __attribute__((naked));
|
||||||
|
void SVC_Handler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
//Call ISR_yield(). Name is a C++ mangled name.
|
||||||
|
asm volatile("bl _ZN14miosix_private9ISR_yieldEv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Auxiliary timer interupt routine.
|
||||||
|
* Used for variable lenght bursts in control based scheduler.
|
||||||
|
* Since inside naked functions only assembler code is allowed, this function
|
||||||
|
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||||
|
* the implementation code in ISR_yield()
|
||||||
|
*/
|
||||||
|
void XXX_IRQHandler() __attribute__((naked));
|
||||||
|
void XXX_IRQHandler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
//Call ISR_auxTimer(). Name is a C++ mangled name.
|
||||||
|
asm volatile("bl _ZN14miosix_private12ISR_auxTimerEv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
#endif //SCHED_TYPE_CONTROL_BASED
|
||||||
|
|
||||||
|
namespace miosix_private {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Called by the timer interrupt, preempt to next thread
|
||||||
|
* Declared noinline to avoid the compiler trying to inline it into the caller,
|
||||||
|
* which would violate the requirement on naked functions. Function is not
|
||||||
|
* static because otherwise the compiler optimizes it out...
|
||||||
|
*/
|
||||||
|
void ISR_preempt() __attribute__((noinline));
|
||||||
|
void ISR_preempt()
|
||||||
|
{
|
||||||
|
IRQstackOverflowCheck();
|
||||||
|
miosix::IRQtickInterrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Called by the software interrupt, yield to next thread
|
||||||
|
* Declared noinline to avoid the compiler trying to inline it into the caller,
|
||||||
|
* which would violate the requirement on naked functions. Function is not
|
||||||
|
* static because otherwise the compiler optimizes it out...
|
||||||
|
*/
|
||||||
|
void ISR_yield() __attribute__((noinline));
|
||||||
|
void ISR_yield()
|
||||||
|
{
|
||||||
|
IRQstackOverflowCheck();
|
||||||
|
miosix::Scheduler::IRQfindNextThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Auxiliary timer interupt routine.
|
||||||
|
* Used for variable lenght bursts in control based scheduler.
|
||||||
|
*/
|
||||||
|
void ISR_auxTimer() __attribute__((noinline));
|
||||||
|
void ISR_auxTimer()
|
||||||
|
{
|
||||||
|
IRQstackOverflowCheck();
|
||||||
|
miosix::Scheduler::IRQfindNextThread();//If the kernel is running, preempt
|
||||||
|
if(miosix::kernel_running!=0) miosix::tick_skew=true;
|
||||||
|
}
|
||||||
|
#endif //SCHED_TYPE_CONTROL_BASED
|
||||||
|
|
||||||
|
void IRQstackOverflowCheck()
|
||||||
|
{
|
||||||
|
const unsigned int watermarkSize=miosix::WATERMARK_LEN/sizeof(unsigned int);
|
||||||
|
for(unsigned int i=0;i<watermarkSize;i++)
|
||||||
|
{
|
||||||
|
if(miosix::cur->watermark[i]!=miosix::WATERMARK_FILL)
|
||||||
|
miosix::errorHandler(miosix::STACK_OVERFLOW);
|
||||||
|
}
|
||||||
|
if(miosix::cur->ctxsave[0] < reinterpret_cast<unsigned int>(
|
||||||
|
miosix::cur->watermark+watermarkSize))
|
||||||
|
miosix::errorHandler(miosix::STACK_OVERFLOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRQsystemReboot()
|
||||||
|
{
|
||||||
|
NVIC_SystemReset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp,
|
||||||
|
void *argv)
|
||||||
|
{
|
||||||
|
unsigned int *stackPtr=sp;
|
||||||
|
stackPtr--; //Stack is full descending, so decrement first
|
||||||
|
*stackPtr=0x01000000; stackPtr--; //--> xPSR
|
||||||
|
*stackPtr=reinterpret_cast<unsigned long>(
|
||||||
|
&miosix::Thread::threadLauncher); stackPtr--; //--> pc
|
||||||
|
*stackPtr=0xffffffff; stackPtr--; //--> lr
|
||||||
|
*stackPtr=0; stackPtr--; //--> r12
|
||||||
|
*stackPtr=0; stackPtr--; //--> r3
|
||||||
|
*stackPtr=0; stackPtr--; //--> r2
|
||||||
|
*stackPtr=reinterpret_cast<unsigned long >(argv); stackPtr--; //--> r1
|
||||||
|
*stackPtr=reinterpret_cast<unsigned long >(pc); //--> r0
|
||||||
|
|
||||||
|
ctxsave[0]=reinterpret_cast<unsigned long>(stackPtr); //--> psp
|
||||||
|
//leaving the content of r4-r11 uninitialized
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRQportableStartKernel()
|
||||||
|
{
|
||||||
|
//NOTE: the SAM-BA bootloader does not relocate the vector table offset,
|
||||||
|
//so any interrupt would call the SAM-BA IRQ handler, not the application
|
||||||
|
//ones.
|
||||||
|
SCB->VTOR = reinterpret_cast<unsigned int>(&__Vectors);
|
||||||
|
|
||||||
|
//Enable fault handlers
|
||||||
|
SCB->SHCSR |= SCB_SHCSR_USGFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk
|
||||||
|
| SCB_SHCSR_MEMFAULTENA_Msk;
|
||||||
|
//Enable traps for division by zero. Trap for unaligned memory access
|
||||||
|
//was removed as gcc starting from 4.7.2 generates unaligned accesses by
|
||||||
|
//default (https://www.gnu.org/software/gcc/gcc-4.7/changes.html)
|
||||||
|
SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk;
|
||||||
|
NVIC_SetPriorityGrouping(7);//This should disable interrupt nesting
|
||||||
|
NVIC_SetPriority(SVCall_IRQn,3);//High priority for SVC (Max=0, min=15)
|
||||||
|
NVIC_SetPriority(SysTick_IRQn,3);//High priority for SysTick (Max=0, min=15)
|
||||||
|
SysTick->LOAD=SystemCoreClock/miosix::TICK_FREQ-1;
|
||||||
|
//Start SysTick, set to generate interrupts
|
||||||
|
SysTick->CTRL=SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk |
|
||||||
|
SysTick_CTRL_CLKSOURCE_Msk;
|
||||||
|
|
||||||
|
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||||
|
AuxiliaryTimer::IRQinit();
|
||||||
|
#endif //SCHED_TYPE_CONTROL_BASED
|
||||||
|
|
||||||
|
//create a temporary space to save current registers. This data is useless
|
||||||
|
//since there's no way to stop the sheduler, but we need to save it anyway.
|
||||||
|
unsigned int s_ctxsave[miosix::CTXSAVE_SIZE];
|
||||||
|
ctxsave=s_ctxsave;//make global ctxsave point to it
|
||||||
|
//Note, we can't use enableInterrupts() now since the call is not mathced
|
||||||
|
//by a call to disableInterrupts()
|
||||||
|
__enable_fault_irq();
|
||||||
|
__enable_irq();
|
||||||
|
miosix::Thread::yield();
|
||||||
|
//Never reaches here
|
||||||
|
}
|
||||||
|
|
||||||
|
void sleepCpu()
|
||||||
|
{
|
||||||
|
__WFI();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||||
|
#error "AUX_TIMER not yet implemented"
|
||||||
|
void AuxiliaryTimer::IRQinit()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int AuxiliaryTimer::IRQgetValue()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuxiliaryTimer::IRQsetValue(int x)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif //SCHED_TYPE_CONTROL_BASED
|
||||||
|
|
||||||
|
} //namespace miosix_private
|
||||||
|
|
@ -0,0 +1,151 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2010-2020 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "interfaces/arch_registers.h"
|
||||||
|
#include "interfaces/portability.h"
|
||||||
|
#include "config/miosix_settings.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Drivers
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This pointer is used by the kernel, and should not be used by end users.
|
||||||
|
* this is a pointer to a location where to store the thread's registers during
|
||||||
|
* context switch. It requires C linkage to be used inside asm statement.
|
||||||
|
* Registers are saved in the following order:
|
||||||
|
* *ctxsave+32 --> r11
|
||||||
|
* *ctxsave+28 --> r10
|
||||||
|
* *ctxsave+24 --> r9
|
||||||
|
* *ctxsave+20 --> r8
|
||||||
|
* *ctxsave+16 --> r7
|
||||||
|
* *ctxsave+12 --> r6
|
||||||
|
* *ctxsave+8 --> r5
|
||||||
|
* *ctxsave+4 --> r4
|
||||||
|
* *ctxsave+0 --> psp
|
||||||
|
*/
|
||||||
|
extern "C" {
|
||||||
|
extern volatile unsigned int *ctxsave;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* \def saveContext()
|
||||||
|
* Save context from an interrupt<br>
|
||||||
|
* Must be the first line of an IRQ where a context switch can happen.
|
||||||
|
* The IRQ must be "naked" to prevent the compiler from generating context save.
|
||||||
|
*
|
||||||
|
* A note on the dmb instruction, without it a race condition was observed
|
||||||
|
* between pauseKernel() and IRQfindNextThread(). pauseKernel() uses an strex
|
||||||
|
* instruction to store a value in the global variable kernel_running which is
|
||||||
|
* tested by the context switch code in IRQfindNextThread(). Without the memory
|
||||||
|
* barrier IRQfindNextThread() would occasionally read the previous value and
|
||||||
|
* perform a context switch while the kernel was paused, leading to deadlock.
|
||||||
|
* The failure was only observed within the exception_test() in the testsuite
|
||||||
|
* running on the stm32f429zi_stm32f4discovery.
|
||||||
|
*/
|
||||||
|
#define saveContext() \
|
||||||
|
{ \
|
||||||
|
asm volatile("stmdb sp!, {lr} \n\t" /*save lr on MAIN stack*/ \
|
||||||
|
"mrs r1, psp \n\t" /*get PROCESS stack pointer*/ \
|
||||||
|
"ldr r0, =ctxsave \n\t" /*get current context*/ \
|
||||||
|
"ldr r0, [r0] \n\t" \
|
||||||
|
"stmia r0, {r1,r4-r11} \n\t" /*save PROCESS sp + r4-r11*/ \
|
||||||
|
"dmb \n\t" \
|
||||||
|
); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \def restoreContext()
|
||||||
|
* Restore context in an IRQ where saveContext() is used. Must be the last line
|
||||||
|
* of an IRQ where a context switch can happen. The IRQ must be "naked" to
|
||||||
|
* prevent the compiler from generating context restore.
|
||||||
|
*/
|
||||||
|
#define restoreContext() \
|
||||||
|
{ \
|
||||||
|
asm volatile("ldr r0, =ctxsave \n\t" /*get current context*/ \
|
||||||
|
"ldr r0, [r0] \n\t" \
|
||||||
|
"ldmia r0, {r1,r4-r11} \n\t" /*restore r4-r11 + r1=psp*/ \
|
||||||
|
"msr psp, r1 \n\t" /*restore PROCESS sp*/ \
|
||||||
|
"ldmia sp!, {pc} \n\t" /*return*/ \
|
||||||
|
); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace miosix_private {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Drivers
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
inline void doYield()
|
||||||
|
{
|
||||||
|
asm volatile("movs r3, #0\n\t"
|
||||||
|
"svc 0"
|
||||||
|
:::"r3");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void doDisableInterrupts()
|
||||||
|
{
|
||||||
|
// Documentation says __disable_irq() disables all interrupts with
|
||||||
|
// configurable priority, so also SysTick and SVC.
|
||||||
|
// No need to disable faults with __disable_fault_irq()
|
||||||
|
__disable_irq();
|
||||||
|
//The new fastDisableInterrupts/fastEnableInterrupts are inline, so there's
|
||||||
|
//the need for a memory barrier to avoid aggressive reordering
|
||||||
|
asm volatile("":::"memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void doEnableInterrupts()
|
||||||
|
{
|
||||||
|
__enable_irq();
|
||||||
|
//The new fastDisableInterrupts/fastEnableInterrupts are inline, so there's
|
||||||
|
//the need for a memory barrier to avoid aggressive reordering
|
||||||
|
asm volatile("":::"memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool checkAreInterruptsEnabled()
|
||||||
|
{
|
||||||
|
register int i;
|
||||||
|
asm volatile("mrs %0, primask \n\t":"=r"(i));
|
||||||
|
if(i!=0) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} //namespace miosix_private
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2012 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef ARCH_SETTINGS_H
|
||||||
|
#define ARCH_SETTINGS_H
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Settings
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// \internal size of vector to store registers during ctx switch
|
||||||
|
/// ((10+16)*4=104Bytes). Only sp, r4-r11, EXC_RETURN and s16-s31 are saved
|
||||||
|
/// here, since r0-r3,r12,lr,pc,xPSR, old sp and s0-s15,fpscr are saved by
|
||||||
|
/// hardware on the process stack on Cortex M4F CPUs. EXC_RETURN, or the lr,
|
||||||
|
/// value to use to return from the exception is necessary to know if the
|
||||||
|
/// thread has used fp regs, as an extension specific to Cortex-M4F CPUs.
|
||||||
|
const unsigned char CTXSAVE_SIZE=10+16;
|
||||||
|
|
||||||
|
/// \internal some architectures save part of the context on their stack.
|
||||||
|
/// ((8+17)*4=100Bytes). This constant is used to increase the stack size by
|
||||||
|
/// the size of context save frame. If zero, this architecture does not save
|
||||||
|
/// anything on stack during context save. Size is in bytes, not words.
|
||||||
|
/// 8 registers=r0-r3,r12,lr,pc,xPSR
|
||||||
|
/// 17 registers=s0-s15,fpscr
|
||||||
|
/// MUST be divisible by 4.
|
||||||
|
const unsigned int CTXSAVE_ON_STACK=(8+17)*4;
|
||||||
|
|
||||||
|
/// \internal stack alignment for this specific architecture
|
||||||
|
const unsigned int CTXSAVE_STACK_ALIGNMENT=8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif /* ARCH_SETTINGS_H */
|
||||||
|
|
@ -0,0 +1,213 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2010, 2011, 2012 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
//Miosix kernel
|
||||||
|
|
||||||
|
#include "interfaces/portability.h"
|
||||||
|
#include "kernel/kernel.h"
|
||||||
|
#include "kernel/error.h"
|
||||||
|
#include "interfaces/bsp.h"
|
||||||
|
#include "kernel/scheduler/scheduler.h"
|
||||||
|
#include "kernel/scheduler/tick_interrupt.h"
|
||||||
|
#include "core/interrupts.h"
|
||||||
|
#include "kernel/process.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* timer interrupt routine.
|
||||||
|
* Since inside naked functions only assembler code is allowed, this function
|
||||||
|
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||||
|
* the implementation code in ISR_preempt()
|
||||||
|
*/
|
||||||
|
void SysTick_Handler() __attribute__((naked));
|
||||||
|
void SysTick_Handler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
//Call ISR_preempt(). Name is a C++ mangled name.
|
||||||
|
asm volatile("bl _ZN14miosix_private11ISR_preemptEv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* software interrupt routine.
|
||||||
|
* Since inside naked functions only assembler code is allowed, this function
|
||||||
|
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||||
|
* the implementation code in ISR_yield()
|
||||||
|
*/
|
||||||
|
void SVC_Handler() __attribute__((naked));
|
||||||
|
void SVC_Handler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
//Call ISR_yield(). Name is a C++ mangled name.
|
||||||
|
asm volatile("bl _ZN14miosix_private9ISR_yieldEv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Auxiliary timer interupt routine.
|
||||||
|
* Used for variable lenght bursts in control based scheduler.
|
||||||
|
* Since inside naked functions only assembler code is allowed, this function
|
||||||
|
* only calls the ctxsave/ctxrestore macros (which are in assembler), and calls
|
||||||
|
* the implementation code in ISR_yield()
|
||||||
|
*/
|
||||||
|
void TIM3_IRQHandler() __attribute__((naked));
|
||||||
|
void TIM3_IRQHandler()
|
||||||
|
{
|
||||||
|
saveContext();
|
||||||
|
//Call ISR_auxTimer(). Name is a C++ mangled name.
|
||||||
|
asm volatile("bl _ZN14miosix_private12ISR_auxTimerEv");
|
||||||
|
restoreContext();
|
||||||
|
}
|
||||||
|
#endif //SCHED_TYPE_CONTROL_BASED
|
||||||
|
|
||||||
|
namespace miosix_private {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Called by the timer interrupt, preempt to next thread
|
||||||
|
* Declared noinline to avoid the compiler trying to inline it into the caller,
|
||||||
|
* which would violate the requirement on naked functions. Function is not
|
||||||
|
* static because otherwise the compiler optimizes it out...
|
||||||
|
*/
|
||||||
|
void ISR_preempt() __attribute__((noinline));
|
||||||
|
void ISR_preempt()
|
||||||
|
{
|
||||||
|
IRQstackOverflowCheck();
|
||||||
|
miosix::IRQtickInterrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Called by the software interrupt, yield to next thread
|
||||||
|
* Declared noinline to avoid the compiler trying to inline it into the caller,
|
||||||
|
* which would violate the requirement on naked functions. Function is not
|
||||||
|
* static because otherwise the compiler optimizes it out...
|
||||||
|
*/
|
||||||
|
void ISR_yield() __attribute__((noinline));
|
||||||
|
void ISR_yield()
|
||||||
|
{
|
||||||
|
IRQstackOverflowCheck();
|
||||||
|
miosix::Scheduler::IRQfindNextThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Auxiliary timer interupt routine.
|
||||||
|
* Used for variable lenght bursts in control based scheduler.
|
||||||
|
*/
|
||||||
|
void ISR_auxTimer() __attribute__((noinline));
|
||||||
|
void ISR_auxTimer()
|
||||||
|
{
|
||||||
|
IRQstackOverflowCheck();
|
||||||
|
miosix::Scheduler::IRQfindNextThread();//If the kernel is running, preempt
|
||||||
|
if(miosix::kernel_running!=0) miosix::tick_skew=true;
|
||||||
|
TIM3->SR=0;
|
||||||
|
}
|
||||||
|
#endif //SCHED_TYPE_CONTROL_BASED
|
||||||
|
|
||||||
|
void IRQstackOverflowCheck()
|
||||||
|
{
|
||||||
|
const unsigned int watermarkSize=miosix::WATERMARK_LEN/sizeof(unsigned int);
|
||||||
|
for(unsigned int i=0;i<watermarkSize;i++)
|
||||||
|
{
|
||||||
|
if(miosix::cur->watermark[i]!=miosix::WATERMARK_FILL)
|
||||||
|
miosix::errorHandler(miosix::STACK_OVERFLOW);
|
||||||
|
}
|
||||||
|
if(miosix::cur->ctxsave[0] < reinterpret_cast<unsigned int>(
|
||||||
|
miosix::cur->watermark+watermarkSize))
|
||||||
|
miosix::errorHandler(miosix::STACK_OVERFLOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRQsystemReboot()
|
||||||
|
{
|
||||||
|
NVIC_SystemReset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp,
|
||||||
|
void *argv)
|
||||||
|
{
|
||||||
|
unsigned int *stackPtr=sp;
|
||||||
|
stackPtr--; //Stack is full descending, so decrement first
|
||||||
|
*stackPtr=0x01000000; stackPtr--; //--> xPSR
|
||||||
|
*stackPtr=reinterpret_cast<unsigned long>(
|
||||||
|
&miosix::Thread::threadLauncher); stackPtr--; //--> pc
|
||||||
|
*stackPtr=0xffffffff; stackPtr--; //--> lr
|
||||||
|
*stackPtr=0; stackPtr--; //--> r12
|
||||||
|
*stackPtr=0; stackPtr--; //--> r3
|
||||||
|
*stackPtr=0; stackPtr--; //--> r2
|
||||||
|
*stackPtr=reinterpret_cast<unsigned long >(argv); stackPtr--; //--> r1
|
||||||
|
*stackPtr=reinterpret_cast<unsigned long >(pc); //--> r0
|
||||||
|
|
||||||
|
ctxsave[0]=reinterpret_cast<unsigned long>(stackPtr); //--> psp
|
||||||
|
//leaving the content of r4-r11 uninitialized
|
||||||
|
ctxsave[9]=0xfffffffd; //EXC_RETURN=thread mode, use psp, no floating ops
|
||||||
|
//leaving the content of s16-s31 uninitialized
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRQportableStartKernel()
|
||||||
|
{
|
||||||
|
//Enable fault handlers
|
||||||
|
SCB->SHCSR |= SCB_SHCSR_USGFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk
|
||||||
|
| SCB_SHCSR_MEMFAULTENA_Msk;
|
||||||
|
//Enable traps for division by zero. Trap for unaligned memory access
|
||||||
|
//was removed as gcc starting from 4.7.2 generates unaligned accesses by
|
||||||
|
//default (https://www.gnu.org/software/gcc/gcc-4.7/changes.html)
|
||||||
|
SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk;
|
||||||
|
NVIC_SetPriorityGrouping(7);//This should disable interrupt nesting
|
||||||
|
NVIC_SetPriority(SVCall_IRQn,3);//High priority for SVC (Max=0, min=15)
|
||||||
|
NVIC_SetPriority(SysTick_IRQn,3);//High priority for SysTick (Max=0, min=15)
|
||||||
|
NVIC_SetPriority(MemoryManagement_IRQn,2);//Higher priority for MemoryManagement (Max=0, min=15)
|
||||||
|
SysTick->LOAD=SystemCoreClock/miosix::TICK_FREQ;
|
||||||
|
//Start SysTick, set to generate interrupts
|
||||||
|
SysTick->CTRL=SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk |
|
||||||
|
SysTick_CTRL_CLKSOURCE_Msk;
|
||||||
|
|
||||||
|
//create a temporary space to save current registers. This data is useless
|
||||||
|
//since there's no way to stop the sheduler, but we need to save it anyway.
|
||||||
|
unsigned int s_ctxsave[miosix::CTXSAVE_SIZE];
|
||||||
|
ctxsave=s_ctxsave;//make global ctxsave point to it
|
||||||
|
//Note, we can't use enableInterrupts() now since the call is not mathced
|
||||||
|
//by a call to disableInterrupts()
|
||||||
|
__enable_fault_irq();
|
||||||
|
__enable_irq();
|
||||||
|
miosix::Thread::yield();
|
||||||
|
//Never reaches here
|
||||||
|
}
|
||||||
|
|
||||||
|
void sleepCpu()
|
||||||
|
{
|
||||||
|
__WFI();
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix_private
|
||||||
|
|
@ -0,0 +1,163 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2010, 2011, 2012 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
//Miosix kernel
|
||||||
|
|
||||||
|
#ifndef PORTABILITY_IMPL_H
|
||||||
|
#define PORTABILITY_IMPL_H
|
||||||
|
|
||||||
|
#include "interfaces/arch_registers.h"
|
||||||
|
#include "interfaces/portability.h"
|
||||||
|
#include "config/miosix_settings.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Drivers
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This pointer is used by the kernel, and should not be used by end users.
|
||||||
|
* this is a pointer to a location where to store the thread's registers during
|
||||||
|
* context switch. It requires C linkage to be used inside asm statement.
|
||||||
|
* Registers are saved in the following order:
|
||||||
|
* *ctxsave+100 --> s31
|
||||||
|
* ...
|
||||||
|
* *ctxsave+40 --> s16
|
||||||
|
* *ctxsave+36 --> lr (contains EXC_RETURN whose bit #4 tells if fpu is used)
|
||||||
|
* *ctxsave+32 --> r11
|
||||||
|
* *ctxsave+28 --> r10
|
||||||
|
* *ctxsave+24 --> r9
|
||||||
|
* *ctxsave+20 --> r8
|
||||||
|
* *ctxsave+16 --> r7
|
||||||
|
* *ctxsave+12 --> r6
|
||||||
|
* *ctxsave+8 --> r5
|
||||||
|
* *ctxsave+4 --> r4
|
||||||
|
* *ctxsave+0 --> psp
|
||||||
|
*/
|
||||||
|
extern "C" {
|
||||||
|
extern volatile unsigned int *ctxsave;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* \def saveContext()
|
||||||
|
* Save context from an interrupt<br>
|
||||||
|
* Must be the first line of an IRQ where a context switch can happen.
|
||||||
|
* The IRQ must be "naked" to prevent the compiler from generating context save.
|
||||||
|
*
|
||||||
|
* A note on the dmb instruction, without it a race condition was observed
|
||||||
|
* between pauseKernel() and IRQfindNextThread(). pauseKernel() uses an strex
|
||||||
|
* instruction to store a value in the global variable kernel_running which is
|
||||||
|
* tested by the context switch code in IRQfindNextThread(). Without the memory
|
||||||
|
* barrier IRQfindNextThread() would occasionally read the previous value and
|
||||||
|
* perform a context switch while the kernel was paused, leading to deadlock.
|
||||||
|
* The failure was only observed within the exception_test() in the testsuite
|
||||||
|
* running on the stm32f429zi_stm32f4discovery.
|
||||||
|
*/
|
||||||
|
#define saveContext() \
|
||||||
|
{ \
|
||||||
|
asm volatile(" mrs r1, psp \n"/*get PROCESS stack ptr */ \
|
||||||
|
" ldr r0, =ctxsave \n"/*get current context */ \
|
||||||
|
" ldr r0, [r0] \n" \
|
||||||
|
" stmia r0!, {r1,r4-r11,lr} \n"/*save r1(psp),r4-r11,lr */ \
|
||||||
|
" lsls r2, lr, #27 \n"/*check if bit #4 is set */ \
|
||||||
|
" bmi 0f \n" \
|
||||||
|
" vstmia.32 r0, {s16-s31} \n"/*save s16-s31 if we need*/ \
|
||||||
|
"0: dmb \n" \
|
||||||
|
); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \def restoreContext()
|
||||||
|
* Restore context in an IRQ where saveContext() is used. Must be the last line
|
||||||
|
* of an IRQ where a context switch can happen. The IRQ must be "naked" to
|
||||||
|
* prevent the compiler from generating context restore.
|
||||||
|
*/
|
||||||
|
#define restoreContext() \
|
||||||
|
{ \
|
||||||
|
asm volatile(" ldr r0, =ctxsave \n"/*get current context */ \
|
||||||
|
" ldr r0, [r0] \n" \
|
||||||
|
" ldmia r0!, {r1,r4-r11,lr} \n"/*load r1(psp),r4-r11,lr */ \
|
||||||
|
" lsls r2, lr, #27 \n"/*check if bit #4 is set */ \
|
||||||
|
" bmi 0f \n" \
|
||||||
|
" vldmia.32 r0, {s16-s31} \n"/*restore s16-s31 if need*/ \
|
||||||
|
"0: msr psp, r1 \n"/*restore PROCESS sp*/ \
|
||||||
|
" bx lr \n"/*return*/ \
|
||||||
|
); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace miosix_private {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Drivers
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
inline void doYield()
|
||||||
|
{
|
||||||
|
asm volatile("movs r3, #0\n\t"
|
||||||
|
"svc 0"
|
||||||
|
:::"r3");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void doDisableInterrupts()
|
||||||
|
{
|
||||||
|
// Documentation says __disable_irq() disables all interrupts with
|
||||||
|
// configurable priority, so also SysTick and SVC.
|
||||||
|
// No need to disable faults with __disable_fault_irq()
|
||||||
|
__disable_irq();
|
||||||
|
//The new fastDisableInterrupts/fastEnableInterrupts are inline, so there's
|
||||||
|
//the need for a memory barrier to avoid aggressive reordering
|
||||||
|
asm volatile("":::"memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void doEnableInterrupts()
|
||||||
|
{
|
||||||
|
__enable_irq();
|
||||||
|
//The new fastDisableInterrupts/fastEnableInterrupts are inline, so there's
|
||||||
|
//the need for a memory barrier to avoid aggressive reordering
|
||||||
|
asm volatile("":::"memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool checkAreInterruptsEnabled()
|
||||||
|
{
|
||||||
|
register int i;
|
||||||
|
asm volatile("mrs %0, primask \n\t":"=r"(i));
|
||||||
|
if(i!=0) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} //namespace miosix_private
|
||||||
|
|
||||||
|
#endif //PORTABILITY_IMPL_H
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2012, 2013, 2014 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef BOARD_SETTINGS_H
|
||||||
|
#define BOARD_SETTINGS_H
|
||||||
|
|
||||||
|
#include "util/version.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Versioning for board_settings.h for out of git tree projects
|
||||||
|
*/
|
||||||
|
#define BOARD_SETTINGS_VERSION 100
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Settings
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// Size of stack for main().
|
||||||
|
/// The C standard library is stack-heavy (iprintf requires 1KB) but the
|
||||||
|
/// STM32F407VG only has 192KB of RAM so there is room for a big 4K stack.
|
||||||
|
const unsigned int MAIN_STACK_SIZE=4*1024;
|
||||||
|
|
||||||
|
/// Frequency of tick (in Hz). The frequency of the STM32F100RB timer in the
|
||||||
|
/// stm32vldiscovery board can be divided by 1000. This allows to use a 1KHz
|
||||||
|
/// tick and the minimun Thread::sleep value is 1ms
|
||||||
|
/// For the priority scheduler this is also the context switch frequency
|
||||||
|
const unsigned int TICK_FREQ=1000;
|
||||||
|
|
||||||
|
///\internal Aux timer run @ 100KHz
|
||||||
|
///Note that since the timer is only 16 bits this imposes a limit on the
|
||||||
|
///burst measurement of 655ms. If due to a pause_kernel() or
|
||||||
|
///disable_interrupts() section a thread runs for more than that time, a wrong
|
||||||
|
///burst value will be measured
|
||||||
|
const unsigned int AUX_TIMER_CLOCK=100000;
|
||||||
|
const unsigned int AUX_TIMER_MAX=0xffff; ///<\internal Aux timer is 16 bits
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif /* BOARD_SETTINGS_H */
|
||||||
|
|
@ -0,0 +1,235 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 *
|
||||||
|
* by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef MIOSIX_SETTINGS_H
|
||||||
|
#define MIOSIX_SETTINGS_H
|
||||||
|
|
||||||
|
// Before you can compile the kernel you have to configure it by editing this
|
||||||
|
// file. After that, comment out this line to disable the reminder error.
|
||||||
|
// The PARSING_FROM_IDE is because Netbeans gets confused by this, it is never
|
||||||
|
// defined when compiling the code.
|
||||||
|
#ifndef PARSING_FROM_IDE
|
||||||
|
// #error This error is a reminder that you have not edited miosix_settings.h yet.
|
||||||
|
#endif //PARSING_FROM_IDE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file miosix_settings.h
|
||||||
|
* NOTE: this file contains ONLY configuration options that are not dependent
|
||||||
|
* on architecture specific details. The other options are in the following
|
||||||
|
* files which are included here:
|
||||||
|
* miosix/arch/architecture name/common/arch_settings.h
|
||||||
|
* miosix/arch/architecture name/board name/board_settings.h
|
||||||
|
*/
|
||||||
|
#include "arch_settings.h"
|
||||||
|
#include "board_settings.h"
|
||||||
|
#include "util/version.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Versioning for miosix_settings.h for out of git tree projects
|
||||||
|
*/
|
||||||
|
#define MIOSIX_SETTINGS_VERSION 100
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Settings
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
//
|
||||||
|
// Scheduler options
|
||||||
|
//
|
||||||
|
|
||||||
|
/// \def SCHED_TYPE_PRIORITY
|
||||||
|
/// If uncommented selects the priority scheduler
|
||||||
|
/// \def SCHED_TYPE_CONTROL_BASED
|
||||||
|
/// If uncommented selects the control based scheduler
|
||||||
|
/// \def SCHED_TYPE_EDF
|
||||||
|
///If uncommented selects the EDF scheduler
|
||||||
|
//Uncomment only *one* of those
|
||||||
|
|
||||||
|
#define SCHED_TYPE_PRIORITY
|
||||||
|
//#define SCHED_TYPE_CONTROL_BASED
|
||||||
|
//#define SCHED_TYPE_EDF
|
||||||
|
|
||||||
|
//
|
||||||
|
// Filesystem options
|
||||||
|
//
|
||||||
|
|
||||||
|
/// \def WITH_FILESYSTEM
|
||||||
|
/// Allows to enable/disable filesystem support to save code size
|
||||||
|
/// By default it is defined (filesystem support is enabled)
|
||||||
|
// #define WITH_FILESYSTEM
|
||||||
|
|
||||||
|
/// \def WITH_DEVFS
|
||||||
|
/// Allows to enable/disable DevFs support to save code size
|
||||||
|
/// By default it is defined (DevFs is enabled)
|
||||||
|
// #define WITH_DEVFS
|
||||||
|
|
||||||
|
/// \def SYNC_AFTER_WRITE
|
||||||
|
/// Increases filesystem write robustness. After each write operation the
|
||||||
|
/// filesystem is synced so that a power failure happens data is not lost
|
||||||
|
/// (unless power failure happens exactly between the write and the sync)
|
||||||
|
/// Unfortunately write latency and throughput becomes twice as worse
|
||||||
|
/// By default it is defined (slow but safe)
|
||||||
|
// #define SYNC_AFTER_WRITE
|
||||||
|
|
||||||
|
/// Maximum number of open files. Trying to open more will fail.
|
||||||
|
/// Cannot be lower than 3, as the first three are stdin, stdout, stderr
|
||||||
|
const unsigned char MAX_OPEN_FILES=8;
|
||||||
|
|
||||||
|
/// \def WITH_PROCESSES
|
||||||
|
/// If uncommented enables support for processes as well as threads.
|
||||||
|
/// This enables the dynamic loader to load elf programs, the extended system
|
||||||
|
/// call service and, if the hardware supports it, the MPU to provide memory
|
||||||
|
/// isolation of processes
|
||||||
|
//#define WITH_PROCESSES
|
||||||
|
|
||||||
|
#if defined(WITH_PROCESSES) && defined(__NO_EXCEPTIONS)
|
||||||
|
#error Processes require C++ exception support
|
||||||
|
#endif //defined(WITH_PROCESSES) && defined(__NO_EXCEPTIONS)
|
||||||
|
|
||||||
|
#if defined(WITH_PROCESSES) && !defined(WITH_FILESYSTEM)
|
||||||
|
#error Processes require filesystem support
|
||||||
|
#endif //defined(WITH_PROCESSES) && !defined(WITH_FILESYSTEM)
|
||||||
|
|
||||||
|
#if defined(WITH_PROCESSES) && !defined(WITH_DEVFS)
|
||||||
|
#error Processes require devfs support
|
||||||
|
#endif //defined(WITH_PROCESSES) && !defined(WITH_DEVFS)
|
||||||
|
|
||||||
|
//
|
||||||
|
// C/C++ standard library I/O (stdin, stdout and stderr related)
|
||||||
|
//
|
||||||
|
|
||||||
|
/// \def WITH_BOOTLOG
|
||||||
|
/// Uncomment to print bootlogs on stdout.
|
||||||
|
/// By default it is defined (bootlogs are printed)
|
||||||
|
// #define WITH_BOOTLOG
|
||||||
|
|
||||||
|
/// \def WITH_ERRLOG
|
||||||
|
/// Uncomment for debug information on stdout.
|
||||||
|
/// By default it is defined (error information is printed)
|
||||||
|
// #define WITH_ERRLOG
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Kernel related options (stack sizes, priorities)
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \def JTAG_DISABLE_SLEEP
|
||||||
|
* JTAG debuggers lose communication with the device if it enters sleep
|
||||||
|
* mode, so to use debugging it is necessary to disable sleep in the idle thread.
|
||||||
|
* By default it is not defined (idle thread calls sleep).
|
||||||
|
*/
|
||||||
|
//#define JTAG_DISABLE_SLEEP
|
||||||
|
|
||||||
|
/// Minimum stack size (MUST be divisible by 4)
|
||||||
|
const unsigned int STACK_MIN=256;
|
||||||
|
|
||||||
|
/// \internal Size of idle thread stack.
|
||||||
|
/// Should be >=STACK_MIN (MUST be divisible by 4)
|
||||||
|
const unsigned int STACK_IDLE=256;
|
||||||
|
|
||||||
|
/// Default stack size for pthread_create.
|
||||||
|
/// The chosen value is enough to call C standard library functions
|
||||||
|
/// such as printf/fopen which are stack-heavy
|
||||||
|
const unsigned int STACK_DEFAULT_FOR_PTHREAD=2048;
|
||||||
|
|
||||||
|
/// Maximum size of the RAM image of a process. If a program requires more
|
||||||
|
/// the kernel will not run it (MUST be divisible by 4)
|
||||||
|
const unsigned int MAX_PROCESS_IMAGE_SIZE=64*1024;
|
||||||
|
|
||||||
|
/// Minimum size of the stack for a process. If a program specifies a lower
|
||||||
|
/// size the kernel will not run it (MUST be divisible by 4)
|
||||||
|
const unsigned int MIN_PROCESS_STACK_SIZE=STACK_MIN;
|
||||||
|
|
||||||
|
/// Every userspace thread has two stacks, one for when it is running in
|
||||||
|
/// userspace and one for when it is running in kernelspace (that is, while it
|
||||||
|
/// is executing system calls). This is the size of the stack for when the
|
||||||
|
/// thread is running in kernelspace (MUST be divisible by 4)
|
||||||
|
const unsigned int SYSTEM_MODE_PROCESS_STACK_SIZE=2*1024;
|
||||||
|
|
||||||
|
/// Number of priorities (MUST be >1)
|
||||||
|
/// PRIORITY_MAX-1 is the highest priority, 0 is the lowest. -1 is reserved as
|
||||||
|
/// the priority of the idle thread.
|
||||||
|
/// The meaning of a thread's priority depends on the chosen scheduler.
|
||||||
|
#ifdef SCHED_TYPE_PRIORITY
|
||||||
|
//Can be modified, but a high value makes context switches more expensive
|
||||||
|
const short int PRIORITY_MAX=4;
|
||||||
|
#elif defined(SCHED_TYPE_CONTROL_BASED)
|
||||||
|
//Don't touch, the limit is due to the fixed point implementation
|
||||||
|
//It's not needed for if floating point is selected, but kept for consistency
|
||||||
|
const short int PRIORITY_MAX=64;
|
||||||
|
#else //SCHED_TYPE_EDF
|
||||||
|
//Doesn't exist for this kind of scheduler
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Priority of main()
|
||||||
|
/// The meaning of a thread's priority depends on the chosen scheduler.
|
||||||
|
const unsigned char MAIN_PRIORITY=1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Other low level kernel options. There is usually no need to modify these.
|
||||||
|
//
|
||||||
|
|
||||||
|
/// \internal Length of wartermark (in bytes) to check stack overflow.
|
||||||
|
/// MUST be divisible by 4 and can also be zero.
|
||||||
|
/// A high value increases context switch time.
|
||||||
|
const unsigned int WATERMARK_LEN=16;
|
||||||
|
|
||||||
|
/// \internal Used to fill watermark
|
||||||
|
const unsigned int WATERMARK_FILL=0xaaaaaaaa;
|
||||||
|
|
||||||
|
/// \internal Used to fill stack (for checking stack usage)
|
||||||
|
const unsigned int STACK_FILL=0xbbbbbbbb;
|
||||||
|
|
||||||
|
// Compiler version checks
|
||||||
|
#if _MIOSIX_GCC_PATCH_MAJOR > 3
|
||||||
|
#warning "You are using a too new compiler, which may not be supported"
|
||||||
|
#elif _MIOSIX_GCC_PATCH_MAJOR == 2
|
||||||
|
#error "The compiler you are using has known incomplete patches and is not supported. Get the latest one from https://miosix.org/wiki/index.php?title=Miosix_Toolchain"
|
||||||
|
#elif _MIOSIX_GCC_PATCH_VERSION == 1
|
||||||
|
#warning "You are using an unsupported compiler. Get the latest one from https://miosix.org/wiki/index.php?title=Miosix_Toolchain"
|
||||||
|
#endif
|
||||||
|
#if !defined(_MIOSIX_GCC_PATCH_MAJOR) && \
|
||||||
|
(!defined(_MIOSIX_GCC_PATCH_VERSION) || _MIOSIX_GCC_PATCH_VERSION < 1)
|
||||||
|
#error "You are using an unsupported compiler. Get the latest one from https://miosix.org/wiki/index.php?title=Miosix_Toolchain"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //MIOSIX_SETTINGS_H
|
||||||
|
|
@ -0,0 +1,243 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2012, 2013, 2014, 2105, 2106 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef CALLBACK_H
|
||||||
|
#define CALLBACK_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is to extract from Callback code that
|
||||||
|
* does not depend on the template parameter N.
|
||||||
|
*/
|
||||||
|
class CallbackBase
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Possible operations performed by TypeDependentOperation::operation()
|
||||||
|
*/
|
||||||
|
enum Op
|
||||||
|
{
|
||||||
|
CALL,
|
||||||
|
ASSIGN,
|
||||||
|
DESTROY
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* This class is part of the any idiom used by Callback.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
class TypeDependentOperation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Perform the type-dependent operations
|
||||||
|
* \param a storage for the any object, stores the function object
|
||||||
|
* \param b storage for the source object for the copy constructor
|
||||||
|
* \param op operation
|
||||||
|
*/
|
||||||
|
static void operation(int32_t *a, const int32_t *b, Op op)
|
||||||
|
{
|
||||||
|
T *o1=reinterpret_cast<T*>(a);
|
||||||
|
const T *o2=reinterpret_cast<const T*>(b);
|
||||||
|
switch(op)
|
||||||
|
{
|
||||||
|
case CALL:
|
||||||
|
(*o1)();
|
||||||
|
break;
|
||||||
|
case ASSIGN:
|
||||||
|
//This used to be simply *o1=*o2 when we were using
|
||||||
|
//tr1/functional, but in C++11 the type returned by bind
|
||||||
|
//due to having a move constructor doesn't like being
|
||||||
|
//assigned, only copy construction works so we have to
|
||||||
|
//use placement new
|
||||||
|
new (o1) T(*o2);
|
||||||
|
break;
|
||||||
|
case DESTROY:
|
||||||
|
o1->~T();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Callback works just like an std::function, but has some additional
|
||||||
|
* <b>limitations</b>. First, it can only accept function objects that take void
|
||||||
|
* as a parameter and return void, and second if the size of the
|
||||||
|
* implementation-defined type returned by bind is larger than N a
|
||||||
|
* compile-time error is generated. Also, calling an empty Callback does
|
||||||
|
* nothing, while doing the same on a function results in an exception
|
||||||
|
* being thrown.
|
||||||
|
*
|
||||||
|
* The reason why one would want to use this class is because, other than the
|
||||||
|
* limitations, this class also offers a guarantee: it will never allocate
|
||||||
|
* data on the heap. It is not just a matter of code speed: in Miosix calling
|
||||||
|
* new/delete/malloc/free from an interrupt routine produces undefined
|
||||||
|
* behaviour, so this class enables binding function calls form an interrupt
|
||||||
|
* safely.
|
||||||
|
*
|
||||||
|
* \param N the size in bytes that an instance of this class reserves to
|
||||||
|
* store the function objects. If the line starting with 'typedef char check1'
|
||||||
|
* starts failing it means it is time to increase this number. The size
|
||||||
|
* of an instance of this object is N+sizeof(void (*)()), but with N rounded
|
||||||
|
* by excess to four byte boundaries.
|
||||||
|
*/
|
||||||
|
template<unsigned N>
|
||||||
|
class Callback : private CallbackBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Default constructor. Produces an empty callback.
|
||||||
|
*/
|
||||||
|
Callback() : operation(0) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor. Not explicit by design.
|
||||||
|
* \param functor function object a copy of which is stored internally
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
Callback(T functor) : operation(0)
|
||||||
|
{
|
||||||
|
*this=functor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy constructor
|
||||||
|
* \param rhs object to copy
|
||||||
|
*/
|
||||||
|
Callback(const Callback& rhs)
|
||||||
|
{
|
||||||
|
operation=rhs.operation;
|
||||||
|
if(operation) operation(any,rhs.any,ASSIGN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Operator =
|
||||||
|
* \param rhs object to copy
|
||||||
|
* \return *this
|
||||||
|
*/
|
||||||
|
Callback& operator= (const Callback& rhs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assignment operation, assigns a function object to this callback.
|
||||||
|
* \param funtor function object a copy of which is stored internally
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
Callback& operator= (T functor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes any function object stored in this class
|
||||||
|
*/
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
if(operation) operation(any,0,DESTROY);
|
||||||
|
operation=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call the callback, or do nothing if no callback is set
|
||||||
|
*/
|
||||||
|
void operator() ()
|
||||||
|
{
|
||||||
|
if(operation) operation(any,0,CALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call the callback, generating undefined behaviour if no callback is set
|
||||||
|
*/
|
||||||
|
void call()
|
||||||
|
{
|
||||||
|
operation(any,0,CALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Safe bool idiom
|
||||||
|
struct SafeBoolStruct { void* b; };
|
||||||
|
typedef void* SafeBoolStruct::* SafeBool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return true if the object contains a callback
|
||||||
|
*/
|
||||||
|
operator SafeBool() const
|
||||||
|
{
|
||||||
|
return operation==0 ? 0 : &SafeBoolStruct::b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor
|
||||||
|
*/
|
||||||
|
~Callback()
|
||||||
|
{
|
||||||
|
if(operation) operation(any,0,DESTROY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// This declaration is done like that to save memory. On 32 bit systems
|
||||||
|
/// the size of a pointer is 4 bytes, but the strictest alignment is 8
|
||||||
|
/// which is that of double and long long. Using an array of doubles would
|
||||||
|
/// have guaranteed alignment but the array size would have been a multiple
|
||||||
|
/// of 8 bytes, and by summing the 4 bytes of the operation pointer there
|
||||||
|
/// would have been 4 bytes of slack space left unused when declaring arrays
|
||||||
|
/// of callbacks. Therefore any is declared as an array of ints but aligned
|
||||||
|
/// to 8 bytes. This allows i.e. declaring Callback<20> with 20 bytes of
|
||||||
|
/// useful storage and 4 bytes of pointer, despite 20 is not a multiple of 8
|
||||||
|
int32_t any[(N+3)/4] __attribute__((aligned(8)));
|
||||||
|
void (*operation)(int32_t *a, const int32_t *b, Op op);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<unsigned N>
|
||||||
|
Callback<N>& Callback<N>::operator= (const Callback<N>& rhs)
|
||||||
|
{
|
||||||
|
if(this==&rhs) return *this; //Handle assignmento to self
|
||||||
|
if(operation) operation(any,0,DESTROY);
|
||||||
|
operation=rhs.operation;
|
||||||
|
if(operation) operation(any,rhs.any,ASSIGN);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<unsigned N>
|
||||||
|
template<typename T>
|
||||||
|
Callback<N>& Callback<N>::operator= (T functor)
|
||||||
|
{
|
||||||
|
//If an error is reported about this line an attempt to store a too large
|
||||||
|
//object is made. Increase N.
|
||||||
|
static_assert(sizeof(any)>=sizeof(T),"");
|
||||||
|
|
||||||
|
//This should not fail unless something has a stricter alignment than double
|
||||||
|
static_assert(__alignof__(any)>=__alignof__(T),"");
|
||||||
|
|
||||||
|
if(operation) operation(any,0,DESTROY);
|
||||||
|
|
||||||
|
new (reinterpret_cast<T*>(any)) T(functor);
|
||||||
|
operation=TypeDependentOperation<T>::operation;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //CALLBACK_H
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2012, 2013, 2014, 2015, 2016 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "e20.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Class EventQueue
|
||||||
|
//
|
||||||
|
|
||||||
|
void EventQueue::post(function<void ()> event)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(m);
|
||||||
|
events.push_back(event);
|
||||||
|
cv.signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventQueue::run()
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(m);
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
while(events.empty()) cv.wait(l);
|
||||||
|
function<void ()> f=events.front();
|
||||||
|
events.pop_front();
|
||||||
|
{
|
||||||
|
Unlock<FastMutex> u(l);
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventQueue::runOne()
|
||||||
|
{
|
||||||
|
function<void ()> f;
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(m);
|
||||||
|
if(events.empty()) return;
|
||||||
|
f=events.front();
|
||||||
|
events.pop_front();
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,460 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2012, 2013, 2014, 2015, 2016 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
//Miosix event based API
|
||||||
|
|
||||||
|
#ifndef E20_H
|
||||||
|
#define E20_H
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <functional>
|
||||||
|
#include <miosix.h>
|
||||||
|
#include "callback.h"
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A variable sized event queue.
|
||||||
|
*
|
||||||
|
* Makes use of heap allocations and as such it is not possible to post events
|
||||||
|
* from within interrupt service routines. For this, use FixedEventQueue.
|
||||||
|
*
|
||||||
|
* This class acts as a synchronization point, multiple threads can post
|
||||||
|
* events, and multiple threads can call run() or runOne() (thread pooling).
|
||||||
|
*
|
||||||
|
* Events are function that are posted by a thread through post() but executed
|
||||||
|
* in the context of the thread that calls run() or runOne()
|
||||||
|
*/
|
||||||
|
class EventQueue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
EventQueue() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post an event to the queue. This function never blocks.
|
||||||
|
*
|
||||||
|
* \param event function function to be called in the thread that calls
|
||||||
|
* run() or runOne(). Bind can be used to bind parameters to the function.
|
||||||
|
* \throws std::bad_alloc if there is not enough heap memory
|
||||||
|
*/
|
||||||
|
void post(std::function<void ()> event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function blocks waiting for events being posted, and when available
|
||||||
|
* it calls the event function. To return from this event loop an event
|
||||||
|
* function must throw an exception.
|
||||||
|
*
|
||||||
|
* \throws any exception that is thrown by the event functions
|
||||||
|
*/
|
||||||
|
void run();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run at most one event. This function does not block.
|
||||||
|
*
|
||||||
|
* \throws any exception that is thrown by the event functions
|
||||||
|
*/
|
||||||
|
void runOne();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return the number of events in the queue
|
||||||
|
*/
|
||||||
|
unsigned int size() const
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(m);
|
||||||
|
return events.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return true if the queue has no events
|
||||||
|
*/
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(m);
|
||||||
|
return events.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
EventQueue(const EventQueue&);
|
||||||
|
EventQueue& operator= (const EventQueue&);
|
||||||
|
|
||||||
|
std::list<std::function<void ()> > events; ///< Event queue
|
||||||
|
mutable FastMutex m; ///< Mutex for synchronisation
|
||||||
|
ConditionVariable cv; ///< Condition variable for synchronisation
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is to extract from FixedEventQueue code that
|
||||||
|
* does not depend on the NumSlots template parameters.
|
||||||
|
*/
|
||||||
|
template<unsigned SlotSize>
|
||||||
|
class FixedEventQueueBase
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
FixedEventQueueBase() : put(0), get(0), n(0), waitingGet(0), waitingPut(0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post an event. Blocks if event queue is full.
|
||||||
|
* \param event event to post
|
||||||
|
* \param events pointer to event queue
|
||||||
|
* \param size event queue size
|
||||||
|
*/
|
||||||
|
void postImpl(Callback<SlotSize>& event, Callback<SlotSize> *events,
|
||||||
|
unsigned int size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post an event from an interrupt, or with interrupts disabled.
|
||||||
|
* \param event event to post
|
||||||
|
* \param events pointer to event queue
|
||||||
|
* \param size event queue size
|
||||||
|
* \param hppw set to true if a higher priority thread is awakened,
|
||||||
|
* otherwise the variable is not modified
|
||||||
|
*/
|
||||||
|
bool IRQpostImpl(Callback<SlotSize>& event, Callback<SlotSize> *events,
|
||||||
|
unsigned int size, bool *hppw=0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function blocks waiting for events being posted, and when available
|
||||||
|
* it calls the event function. To return from this event loop an event
|
||||||
|
* function must throw an exception.
|
||||||
|
*
|
||||||
|
* \param events pointer to event queue
|
||||||
|
* \param size event queue size
|
||||||
|
* \throws any exception that is thrown by the event functions
|
||||||
|
*/
|
||||||
|
void runImpl(Callback<SlotSize> *events, unsigned int size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run at most one event. This function does not block.
|
||||||
|
*
|
||||||
|
* \param events pointer to event queue
|
||||||
|
* \param size event queue size
|
||||||
|
* \throws any exception that is thrown by the event functions
|
||||||
|
*/
|
||||||
|
void runOneImpl(Callback<SlotSize> *events, unsigned int size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return the number of events in the queue
|
||||||
|
*/
|
||||||
|
unsigned int sizeImpl() const
|
||||||
|
{
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* To allow multiple threads waiting on put and get
|
||||||
|
*/
|
||||||
|
struct WaitingList
|
||||||
|
{
|
||||||
|
WaitingList *next; ///< Pointer to next element of the list
|
||||||
|
Thread *t; ///< Thread waiting
|
||||||
|
bool token; ///< To tolerate spurious wakeups
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned int put; ///< Put position into events
|
||||||
|
unsigned int get; ///< Get position into events
|
||||||
|
unsigned int n; ///< Number of occupied event slots
|
||||||
|
WaitingList *waitingGet; ///< List of threads waiting to get an event
|
||||||
|
WaitingList *waitingPut; ///< List of threads waiting to put an event
|
||||||
|
};
|
||||||
|
|
||||||
|
template<unsigned SlotSize>
|
||||||
|
void FixedEventQueueBase<SlotSize>::postImpl(Callback<SlotSize>& event,
|
||||||
|
Callback<SlotSize> *events, unsigned int size)
|
||||||
|
{
|
||||||
|
//Not FastInterruptDisableLock as the operator= of the bound
|
||||||
|
//parameters of the Callback may allocate
|
||||||
|
InterruptDisableLock dLock;
|
||||||
|
while(n>=size)
|
||||||
|
{
|
||||||
|
WaitingList w;
|
||||||
|
w.token=false;
|
||||||
|
w.t=Thread::IRQgetCurrentThread();
|
||||||
|
w.next=waitingPut;
|
||||||
|
waitingPut=&w;
|
||||||
|
while(w.token==false)
|
||||||
|
{
|
||||||
|
Thread::IRQwait();
|
||||||
|
{
|
||||||
|
InterruptEnableLock eLock(dLock);
|
||||||
|
Thread::yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IRQpostImpl(event,events,size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<unsigned SlotSize>
|
||||||
|
bool FixedEventQueueBase<SlotSize>::IRQpostImpl(Callback<SlotSize>& event,
|
||||||
|
Callback<SlotSize> *events, unsigned int size, bool *hppw)
|
||||||
|
{
|
||||||
|
if(n>=size) return false;
|
||||||
|
events[put]=event; //This may allocate memory
|
||||||
|
if(++put>=size) put=0;
|
||||||
|
n++;
|
||||||
|
if(waitingGet)
|
||||||
|
{
|
||||||
|
Thread *t=Thread::IRQgetCurrentThread();
|
||||||
|
if(hppw && waitingGet->t->IRQgetPriority()>t->IRQgetPriority())
|
||||||
|
*hppw=true;
|
||||||
|
waitingGet->token=true;
|
||||||
|
waitingGet->t->IRQwakeup();
|
||||||
|
waitingGet=waitingGet->next;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<unsigned SlotSize>
|
||||||
|
void FixedEventQueueBase<SlotSize>::runImpl(Callback<SlotSize> *events,
|
||||||
|
unsigned int size)
|
||||||
|
{
|
||||||
|
//Not FastInterruptDisableLock as the operator= of the bound
|
||||||
|
//parameters of the Callback may allocate
|
||||||
|
InterruptDisableLock dLock;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
while(n<=0)
|
||||||
|
{
|
||||||
|
WaitingList w;
|
||||||
|
w.token=false;
|
||||||
|
w.t=Thread::IRQgetCurrentThread();
|
||||||
|
w.next=waitingGet;
|
||||||
|
waitingGet=&w;
|
||||||
|
while(w.token==false)
|
||||||
|
{
|
||||||
|
Thread::IRQwait();
|
||||||
|
{
|
||||||
|
InterruptEnableLock eLock(dLock);
|
||||||
|
Thread::yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Callback<SlotSize> f=events[get]; //This may allocate memory
|
||||||
|
if(++get>=size) get=0;
|
||||||
|
n--;
|
||||||
|
if(waitingPut)
|
||||||
|
{
|
||||||
|
waitingPut->token=true;
|
||||||
|
waitingPut->t->IRQwakeup();
|
||||||
|
waitingPut=waitingPut->next;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
InterruptEnableLock eLock(dLock);
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<unsigned SlotSize>
|
||||||
|
void FixedEventQueueBase<SlotSize>::runOneImpl(Callback<SlotSize> *events,
|
||||||
|
unsigned int size)
|
||||||
|
{
|
||||||
|
Callback<SlotSize> f;
|
||||||
|
{
|
||||||
|
//Not FastInterruptDisableLock as the operator= of the bound
|
||||||
|
//parameters of the Callback may allocate
|
||||||
|
InterruptDisableLock dLock;
|
||||||
|
if(n<=0) return;
|
||||||
|
f=events[get]; //This may allocate memory
|
||||||
|
if(++get>=size) get=0;
|
||||||
|
n--;
|
||||||
|
if(waitingPut)
|
||||||
|
{
|
||||||
|
waitingPut->token=true;
|
||||||
|
waitingPut->t->IRQwakeup();
|
||||||
|
waitingPut=waitingPut->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A fixed size event queue.
|
||||||
|
*
|
||||||
|
* This guarantees it makes no use of the heap, therefore events can be posted
|
||||||
|
* also from within interrupt handlers. This simplifies the development of
|
||||||
|
* device drivers.
|
||||||
|
*
|
||||||
|
* This class acts as a synchronization point, multiple threads (and IRQs) can
|
||||||
|
* post events, and multiple threads can call run() or runOne()
|
||||||
|
* (thread pooling).
|
||||||
|
*
|
||||||
|
* Events are function that are posted by a thread through post() but executed
|
||||||
|
* in the context of the thread that calls run() or runOne()
|
||||||
|
*
|
||||||
|
* \param NumSlots maximum queue length
|
||||||
|
* \param SlotSize size of the Callback objects. This limits the maximum number
|
||||||
|
* of parameters that can be bound to a function. If you get compile-time
|
||||||
|
* errors in callback.h, consider increasing this value. The default is 20
|
||||||
|
* bytes, which is enough to bind a member function pointer, a "this" pointer
|
||||||
|
* and two byte or pointer sized parameters.
|
||||||
|
*/
|
||||||
|
template<unsigned NumSlots, unsigned SlotSize=20>
|
||||||
|
class FixedEventQueue : private FixedEventQueueBase<SlotSize>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
FixedEventQueue() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post an event, blocking if the event queue is full.
|
||||||
|
*
|
||||||
|
* \param event function function to be called in the thread that calls
|
||||||
|
* run() or runOne(). Bind can be used to bind parameters to the function.
|
||||||
|
* Unlike with the EventQueue, the operator= of the bound parameters have
|
||||||
|
* the restriction that they need to be callable from inside a
|
||||||
|
* InterruptDisableLock without causing undefined behaviour, so they
|
||||||
|
* must not, open files, print, ... but can allocate memory.
|
||||||
|
*/
|
||||||
|
void post(Callback<SlotSize> event)
|
||||||
|
{
|
||||||
|
this->postImpl(event,events,NumSlots);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post an event in the queue, or return if the queue was full.
|
||||||
|
*
|
||||||
|
* \param event function function to be called in the thread that calls
|
||||||
|
* run() or runOne(). Bind can be used to bind parameters to the function.
|
||||||
|
* Unlike with the EventQueue, the operator= of the bound parameters have
|
||||||
|
* the restriction that they need to be callable from inside a
|
||||||
|
* InterruptDisableLock without causing undefined behaviour, so they
|
||||||
|
* must not open files, print, ... but can allocate memory.
|
||||||
|
* \return false if there was no space in the queue
|
||||||
|
*/
|
||||||
|
bool postNonBlocking(Callback<SlotSize> event)
|
||||||
|
{
|
||||||
|
InterruptDisableLock dLock;
|
||||||
|
return this->IRQpostImpl(event,events,NumSlots);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post an event in the queue, or return if the queue was full.
|
||||||
|
* Can be called only with interrupts disabled or within an interrupt
|
||||||
|
* handler, allowing device drivers to post an event to a thread.
|
||||||
|
*
|
||||||
|
* \param event function function to be called in the thread that calls
|
||||||
|
* run() or runOne(). Bind can be used to bind parameters to the function.
|
||||||
|
* Unlike with the EventQueue, the operator= of the bound parameters have
|
||||||
|
* the restriction that they need to be callable with interrupts disabled
|
||||||
|
* so they must not open files, print, ...
|
||||||
|
*
|
||||||
|
* If the call is made from within an InterruptDisableLock the copy
|
||||||
|
* constructors can allocate memory, while if the call is made from an
|
||||||
|
* interrupt handler or a FastInterruptFisableLock memory allocation is
|
||||||
|
* forbidden.
|
||||||
|
* \return false if there was no space in the queue
|
||||||
|
*/
|
||||||
|
bool IRQpost(Callback<SlotSize> event)
|
||||||
|
{
|
||||||
|
return this->IRQpostImpl(event,events,NumSlots);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post an event in the queue, or return if the queue was full.
|
||||||
|
* Can be called only with interrupts disabled or within an interrupt
|
||||||
|
* handler, allowing device drivers to post an event to a thread.
|
||||||
|
*
|
||||||
|
* \param event function function to be called in the thread that calls
|
||||||
|
* run() or runOne(). Bind can be used to bind parameters to the function.
|
||||||
|
* Unlike with the EventQueue, the operator= of the bound parameters have
|
||||||
|
* the restriction that they need to be callable with interrupts disabled
|
||||||
|
* so they must not open files, print, ...
|
||||||
|
*
|
||||||
|
* If the call is made from within an InterruptDisableLock the copy
|
||||||
|
* constructors can allocate memory, while if the call is made from an
|
||||||
|
* interrupt handler or a FastInterruptFisableLock memory allocation is
|
||||||
|
* forbidden.
|
||||||
|
* \param hppw returns true if a higher priority thread was awakened as
|
||||||
|
* part of posting the event. Can be used inside an IRQ to call the
|
||||||
|
* scheduler.
|
||||||
|
* \return false if there was no space in the queue
|
||||||
|
*/
|
||||||
|
bool IRQpost(Callback<SlotSize> event, bool& hppw)
|
||||||
|
{
|
||||||
|
hppw=false;
|
||||||
|
return this->IRQpostImpl(event,events,NumSlots,&hppw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function blocks waiting for events being posted, and when available
|
||||||
|
* it calls the event function. To return from this event loop an event
|
||||||
|
* function must throw an exception.
|
||||||
|
*
|
||||||
|
* \throws any exception that is thrown by the event functions
|
||||||
|
*/
|
||||||
|
void run()
|
||||||
|
{
|
||||||
|
this->runImpl(events,NumSlots);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run at most one event. This function does not block.
|
||||||
|
*
|
||||||
|
* \throws any exception that is thrown by the event functions
|
||||||
|
*/
|
||||||
|
void runOne()
|
||||||
|
{
|
||||||
|
this->runOneImpl(events,NumSlots);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return the number of events in the queue
|
||||||
|
*/
|
||||||
|
unsigned int size() const
|
||||||
|
{
|
||||||
|
return this->sizeImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return true if the queue has no events
|
||||||
|
*/
|
||||||
|
unsigned int empty() const
|
||||||
|
{
|
||||||
|
return this->sizeImpl()==0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FixedEventQueue(const FixedEventQueue&);
|
||||||
|
FixedEventQueue& operator= (const FixedEventQueue&);
|
||||||
|
|
||||||
|
Callback<SlotSize> events[NumSlots]; ///< Fixed size queue of events
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //E20_H
|
||||||
|
|
@ -0,0 +1,146 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2017 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "unmember.h"
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
tuple<void (*)(void*), void*> unmemberLogic(unsigned long mixedField,
|
||||||
|
long thisOffset, unsigned long *o) noexcept
|
||||||
|
{
|
||||||
|
//ARM stores the "virtual function or not?" bit in bit #0 of thisOffset and
|
||||||
|
//multiplies the offset by 2 to make room for it. Intel stores it in bit #0
|
||||||
|
//of mixedField
|
||||||
|
|
||||||
|
#ifdef __ARM_EABI__
|
||||||
|
//With multiple or virtual inheritance we need to add an offset to this.
|
||||||
|
o+=(thisOffset & 0xfffffffe)/2/sizeof(long);
|
||||||
|
|
||||||
|
//Then we have two cases, we can have a member function pointer to a
|
||||||
|
//virtual or nonvirtual function. For virtual functions mixedField is the
|
||||||
|
//offset in the vtable where to find the function pointer.
|
||||||
|
//For nonvirtual functions it is already the desired function pointer
|
||||||
|
void (*result)(void*);
|
||||||
|
if(thisOffset & 1)
|
||||||
|
{
|
||||||
|
//Pointer to virtual function. Dereference the object to get to the
|
||||||
|
//virtual table, and then get the function pointer at the correct offset
|
||||||
|
unsigned long *vtbl=reinterpret_cast<unsigned long *>(*o);
|
||||||
|
result=reinterpret_cast<void (*)(void*)>(vtbl[mixedField/sizeof(long)]);
|
||||||
|
} else {
|
||||||
|
//Pointer to non virtual function
|
||||||
|
result=reinterpret_cast<void (*)(void*)>(mixedField);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__i386) || defined(__x86_64__)
|
||||||
|
//With multiple or virtual inheritance we need to add an offset to this.
|
||||||
|
o+=thisOffset/sizeof(long);
|
||||||
|
|
||||||
|
//Then we have two cases, we can have a member function pointer to a
|
||||||
|
//virtual or nonvirtual function. For virtual functions mixedField is the
|
||||||
|
//offset in the vtable where to find the function pointer.
|
||||||
|
//For nonvirtual functions it is already the desired function pointer
|
||||||
|
void (*result)(void*);
|
||||||
|
if(mixedField & 1)
|
||||||
|
{
|
||||||
|
//Pointer to virtual function. Dereference the object to get to the
|
||||||
|
//virtual table, and then get the function pointer at the correct offset
|
||||||
|
unsigned long *vtbl=reinterpret_cast<unsigned long *>(*o);
|
||||||
|
result=reinterpret_cast<void (*)(void*)>(vtbl[(mixedField-1)/sizeof(long)]);
|
||||||
|
} else {
|
||||||
|
//Pointer to non virtual function
|
||||||
|
result=reinterpret_cast<void (*)(void*)>(mixedField);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#error The layout of virtual function pointer is unknown for this arch
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return make_tuple(result,reinterpret_cast<void*>(o));
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
//Testsuite for member function pointer implementation. Compile with:
|
||||||
|
// g++ -std=c++11 -O2 -DTEST_ALGORITHM -o test unmember.cpp; ./test
|
||||||
|
#ifdef TEST_ALGORITHM
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace miosix;
|
||||||
|
|
||||||
|
class Base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void m1() { printf("Base m1 %d %p\n",y,this); }
|
||||||
|
virtual void m2() { printf("Base m2 %d %p\n",y,this); }
|
||||||
|
virtual void m3() { printf("Base m3 %d %p\n",y,this); }
|
||||||
|
int y=1234;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Base2
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void m4() { printf("Base2 m4 %d %p\n",x,this); }
|
||||||
|
virtual void m5() { printf("Base2 m5 %d %p\n",x,this); }
|
||||||
|
int x=5678;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Derived : public Base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void m3() { printf("Derived m3 %d %p\n",y,this); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Derived2 : public Base, public Base2 {};
|
||||||
|
|
||||||
|
class A { public: int a=1; };
|
||||||
|
class B : virtual public A { public: int b=2; };
|
||||||
|
class C : virtual public A { public: int c=3; void mf() { printf("%d\n",c); } };
|
||||||
|
class D : public B, public C { public: int d=4; };
|
||||||
|
|
||||||
|
void call(tuple<void (*)(void*), void*> a) { (*get<0>(a))(get<1>(a)); }
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
Base b;
|
||||||
|
Derived d;
|
||||||
|
Derived2 d2;
|
||||||
|
D dd;
|
||||||
|
call(unmember(&Base::m1,&b));
|
||||||
|
call(unmember(&Base::m2,&b));
|
||||||
|
call(unmember(&Base::m3,&b));
|
||||||
|
call(unmember(&Derived::m3,&d));
|
||||||
|
call(unmember<Derived2>(&Derived2::m4,&d2));
|
||||||
|
call(unmember<Derived2>(&Derived2::m5,&d2));
|
||||||
|
call(unmember<D>(&D::mf,&dd));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //TEST_ALGORITHM
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2017 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Template-independent code of unmember is here to reduce code size.
|
||||||
|
* Do not call this function directly
|
||||||
|
* \param mixedField either the function pointer for nonvirtual functions,
|
||||||
|
* or the vtable offset (with 1 added to disambiguate it from the previous case)
|
||||||
|
* for virtual functions
|
||||||
|
* \param thisOffset the offset to add to this for multiple/virtual inheritance
|
||||||
|
* \param o the object pointer that has to be passed as this
|
||||||
|
* \retun the ordinary function pointer and void * to call it with
|
||||||
|
*/
|
||||||
|
std::tuple<void (*)(void*), void*> unmemberLogic(unsigned long mixedField,
|
||||||
|
long thisOffset, unsigned long *o) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function performs the forbidden cast of C++: casting from a member
|
||||||
|
* function pointer of any class and a class pointer to an ordinary function
|
||||||
|
* pointer and an opaque void * parameter to call it with. It allows to call
|
||||||
|
* any member function of any class (that takes no parameters and returns void)
|
||||||
|
* in an uniform and efficient way.
|
||||||
|
*
|
||||||
|
* The code is not portable, as the underlying representation of member function
|
||||||
|
* pointers is not specified by the standard. According to
|
||||||
|
* https://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible
|
||||||
|
* there are multiple implementations.
|
||||||
|
* This code has been tested with the GCC and the LLVM compilers, in both
|
||||||
|
* X86, X86-64 and ARM32.
|
||||||
|
*
|
||||||
|
* \param mfn a member function pointer of any class that takes no parameters
|
||||||
|
* and returns void
|
||||||
|
* \param object an object on which the member function has to be called
|
||||||
|
* \return the ordinary function pointer and void * to call it with
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
std::tuple<void (*)(void*), void*> unmember(void (T::*mfn)(), T *object) noexcept
|
||||||
|
{
|
||||||
|
//This code only works with GCC/LLVM
|
||||||
|
#if !defined(__clang__) && !defined(__GNUC__)
|
||||||
|
#error Unknown member function pointer layout
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//A union is used to "inspect" the internals of the member function pointer
|
||||||
|
union {
|
||||||
|
void (T::*mfn)();
|
||||||
|
struct {
|
||||||
|
unsigned long mixedField;
|
||||||
|
long thisOffset;
|
||||||
|
};
|
||||||
|
} unpack;
|
||||||
|
unpack.mfn=mfn;
|
||||||
|
|
||||||
|
//Unsigned long is used as its size is 4 on 32bit systems and 8 in 64bit
|
||||||
|
unsigned long *o=reinterpret_cast<unsigned long*>(object);
|
||||||
|
|
||||||
|
return unmemberLogic(unpack.mixedField,unpack.thisOffset,o);
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,223 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013, 2014 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "console_device.h"
|
||||||
|
#include "filesystem/ioctl.h"
|
||||||
|
#include <errno.h>
|
||||||
|
#include <termios.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
//
|
||||||
|
// class TerminalDevice
|
||||||
|
//
|
||||||
|
|
||||||
|
TerminalDevice::TerminalDevice(intrusive_ref_ptr<Device> device)
|
||||||
|
: FileBase(intrusive_ref_ptr<FilesystemBase>()), device(device),
|
||||||
|
mutex(), echo(true), binary(false), skipNewline(false) {}
|
||||||
|
|
||||||
|
ssize_t TerminalDevice::write(const void *data, size_t length)
|
||||||
|
{
|
||||||
|
if(binary) return device->writeBlock(data,length,0);
|
||||||
|
//No mutex here to avoid blocking writes while reads are in progress
|
||||||
|
const char *buffer=static_cast<const char*>(data);
|
||||||
|
const char *start=buffer;
|
||||||
|
//Try to write data in chunks, stop at every \n to replace with \r\n
|
||||||
|
//Although it may be tempting to call echoBack() from here since it performs
|
||||||
|
//a similar task, it is not possible, as echoBack() uses a class field,
|
||||||
|
//chunkStart, and write is not mutexed to allow concurrent writing
|
||||||
|
for(size_t i=0;i<length;i++,buffer++)
|
||||||
|
{
|
||||||
|
if(*buffer!='\n') continue;
|
||||||
|
if(buffer>start)
|
||||||
|
{
|
||||||
|
ssize_t r=device->writeBlock(start,buffer-start,0);
|
||||||
|
if(r<=0) return r;
|
||||||
|
}
|
||||||
|
ssize_t r=device->writeBlock("\r\n",2,0);//Add \r\n
|
||||||
|
if(r<=0) return r;
|
||||||
|
start=buffer+1;
|
||||||
|
}
|
||||||
|
if(buffer>start)
|
||||||
|
{
|
||||||
|
ssize_t r=device->writeBlock(start,buffer-start,0);
|
||||||
|
if(r<=0) return r;
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t TerminalDevice::read(void *data, size_t length)
|
||||||
|
{
|
||||||
|
if(binary)
|
||||||
|
{
|
||||||
|
ssize_t result=device->readBlock(data,length,0);
|
||||||
|
if(echo && result>0) device->writeBlock(data,result,0);//Ignore write errors
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
Lock<FastMutex> l(mutex); //Reads are serialized
|
||||||
|
char *buffer=static_cast<char*>(data);
|
||||||
|
size_t readBytes=0;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
ssize_t r=device->readBlock(buffer+readBytes,length-readBytes,0);
|
||||||
|
if(r<0) return r;
|
||||||
|
pair<size_t,bool> result=normalize(buffer,readBytes,readBytes+r);
|
||||||
|
readBytes=result.first;
|
||||||
|
if(readBytes==length || result.second) return readBytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_FILESYSTEM
|
||||||
|
|
||||||
|
off_t TerminalDevice::lseek(off_t pos, int whence)
|
||||||
|
{
|
||||||
|
return -EBADF;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TerminalDevice::fstat(struct stat *pstat) const
|
||||||
|
{
|
||||||
|
return device->fstat(pstat);
|
||||||
|
}
|
||||||
|
|
||||||
|
int TerminalDevice::isatty() const { return device->isatty(); }
|
||||||
|
|
||||||
|
#endif //WITH_FILESYSTEM
|
||||||
|
|
||||||
|
int TerminalDevice::ioctl(int cmd, void *arg)
|
||||||
|
{
|
||||||
|
if(int result=device->ioctl(cmd,arg)!=0) return result;
|
||||||
|
termios *t=reinterpret_cast<termios*>(arg);
|
||||||
|
switch(cmd)
|
||||||
|
{
|
||||||
|
case IOCTL_TCGETATTR:
|
||||||
|
if(echo) t->c_lflag |= ECHO; else t->c_lflag &= ~ECHO;
|
||||||
|
if(binary==false) t->c_lflag |= ICANON; else t->c_lflag &= ~ICANON;
|
||||||
|
break;
|
||||||
|
case IOCTL_TCSETATTR_NOW:
|
||||||
|
case IOCTL_TCSETATTR_DRAIN:
|
||||||
|
case IOCTL_TCSETATTR_FLUSH:
|
||||||
|
echo=(t->c_lflag & ECHO) ? true : false;
|
||||||
|
binary=(t->c_lflag & ICANON) ? false : true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pair<size_t,bool> TerminalDevice::normalize(char *buffer, ssize_t begin,
|
||||||
|
ssize_t end)
|
||||||
|
{
|
||||||
|
bool newlineFound=false;
|
||||||
|
buffer+=begin;
|
||||||
|
chunkStart=buffer;
|
||||||
|
for(ssize_t i=begin;i<end;i++,buffer++)
|
||||||
|
{
|
||||||
|
switch(*buffer)
|
||||||
|
{
|
||||||
|
//Trying to be compatible with terminals that output \r, \n or \r\n
|
||||||
|
//When receiving \r skipNewline is set to true so we skip the \n
|
||||||
|
//if it comes right after the \r
|
||||||
|
case '\r':
|
||||||
|
*buffer='\n';
|
||||||
|
echoBack(buffer,"\r\n",2);
|
||||||
|
skipNewline=true;
|
||||||
|
newlineFound=true;
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
if(skipNewline)
|
||||||
|
{
|
||||||
|
skipNewline=false;
|
||||||
|
//Discard the \n because comes right after \r
|
||||||
|
memmove(buffer,buffer+1,end-i-1);
|
||||||
|
end--;
|
||||||
|
i--; //Note that i may become -1, but it is acceptable.
|
||||||
|
buffer--;
|
||||||
|
} else {
|
||||||
|
echoBack(buffer,"\r\n",2);
|
||||||
|
newlineFound=true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x7f: //Unix backspace
|
||||||
|
case 0x08: //DOS backspace
|
||||||
|
{
|
||||||
|
//Need to echo before moving buffer data
|
||||||
|
echoBack(buffer,"\033[1D \033[1D",9);
|
||||||
|
ssize_t backward= i==0 ? 1 : 2;
|
||||||
|
memmove(buffer-(backward-1),buffer+1,end-i);
|
||||||
|
end-=backward;
|
||||||
|
i-=backward; //Note that i may become -1, but it is acceptable.
|
||||||
|
buffer-=backward;
|
||||||
|
chunkStart=buffer+1; //Fixup chunkStart after backspace
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
skipNewline=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echoBack(buffer);
|
||||||
|
return make_pair(end,newlineFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalDevice::echoBack(const char *chunkEnd, const char *sep, size_t sepLen)
|
||||||
|
{
|
||||||
|
if(!echo) return;
|
||||||
|
if(chunkEnd>chunkStart) device->writeBlock(chunkStart,chunkEnd-chunkStart,0);
|
||||||
|
chunkStart=chunkEnd+1;
|
||||||
|
if(sep) device->writeBlock(sep,sepLen,0); //Ignore write errors
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// class DefaultConsole
|
||||||
|
//
|
||||||
|
|
||||||
|
DefaultConsole& DefaultConsole::instance()
|
||||||
|
{
|
||||||
|
static DefaultConsole singleton;
|
||||||
|
return singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultConsole::IRQset(intrusive_ref_ptr<Device> console)
|
||||||
|
{
|
||||||
|
//Note: should be safe to be called also outside of IRQ as set() calls
|
||||||
|
//IRQset()
|
||||||
|
atomic_store(&this->console,console);
|
||||||
|
#ifndef WITH_FILESYSTEM
|
||||||
|
atomic_store(&terminal,
|
||||||
|
intrusive_ref_ptr<TerminalDevice>(new TerminalDevice(console)));
|
||||||
|
#endif //WITH_FILESYSTEM
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultConsole::DefaultConsole() : console(new Device(Device::STREAM))
|
||||||
|
#ifndef WITH_FILESYSTEM
|
||||||
|
, terminal(new TerminalDevice(console))
|
||||||
|
#endif //WITH_FILESYSTEM
|
||||||
|
{}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,228 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013, 2014 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef CONSOLE_DEVICE_H
|
||||||
|
#define CONSOLE_DEVICE_H
|
||||||
|
|
||||||
|
#include "config/miosix_settings.h"
|
||||||
|
#include "filesystem/devfs/devfs.h"
|
||||||
|
#include "kernel/sync.h"
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Teriminal device, proxy object supporting additional terminal-specific
|
||||||
|
* features
|
||||||
|
*/
|
||||||
|
class TerminalDevice : public FileBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* \param device proxed device.
|
||||||
|
*/
|
||||||
|
TerminalDevice(intrusive_ref_ptr<Device> device);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data to the file, if the file supports writing.
|
||||||
|
* \param data the data to write
|
||||||
|
* \param length the number of bytes to write
|
||||||
|
* \return the number of written characters, or a negative number in case
|
||||||
|
* of errors
|
||||||
|
*/
|
||||||
|
virtual ssize_t write(const void *data, size_t length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read data from the file, if the file supports reading.
|
||||||
|
* \param data buffer to store read data
|
||||||
|
* \param length the number of bytes to read
|
||||||
|
* \return the number of read characters, or a negative number in case
|
||||||
|
* of errors
|
||||||
|
*/
|
||||||
|
virtual ssize_t read(void *data, size_t length);
|
||||||
|
|
||||||
|
#ifdef WITH_FILESYSTEM
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move file pointer, if the file supports random-access.
|
||||||
|
* \param pos offset to sum to the beginning of the file, current position
|
||||||
|
* or end of file, depending on whence
|
||||||
|
* \param whence SEEK_SET, SEEK_CUR or SEEK_END
|
||||||
|
* \return the offset from the beginning of the file if the operation
|
||||||
|
* completed, or a negative number in case of errors
|
||||||
|
*/
|
||||||
|
virtual off_t lseek(off_t pos, int whence);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return file information.
|
||||||
|
* \param pstat pointer to stat struct
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int fstat(struct stat *pstat) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the file refers to a terminal.
|
||||||
|
* \return 1 if it is a terminal, 0 if it is not, or a negative number in
|
||||||
|
* case of errors
|
||||||
|
*/
|
||||||
|
virtual int isatty() const;
|
||||||
|
|
||||||
|
#endif //WITH_FILESYSTEM
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform various operations on a file descriptor
|
||||||
|
* \param cmd specifies the operation to perform
|
||||||
|
* \param arg optional argument that some operation require
|
||||||
|
* \return the exact return value depends on CMD, -1 is returned on error
|
||||||
|
*/
|
||||||
|
virtual int ioctl(int cmd, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables or disables echo of commands on the terminal
|
||||||
|
* \param echo true to enable echo, false to disable it
|
||||||
|
*/
|
||||||
|
void setEcho(bool echoMode) { echo=echoMode; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return true if echo is enabled
|
||||||
|
*/
|
||||||
|
bool isEchoEnabled() const { return echo; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects whether the terminal sholud be transparent to non ASCII data
|
||||||
|
* \param rawMode true if raw mode is required
|
||||||
|
*/
|
||||||
|
void setBinary(bool binaryMode) { binary=binaryMode; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return true if the terminal allows binary data
|
||||||
|
*/
|
||||||
|
bool isBinary() const { return binary; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Perform normalization of a read buffer (\r\n conversion to \n, backspace)
|
||||||
|
* \param buffer pointer to read buffer
|
||||||
|
* \param begin buffer[begin] is the first character to normalize
|
||||||
|
* \param end buffer[end] is one past the las character to normalize
|
||||||
|
* \return a pair with the number of valid character in the buffer (staring
|
||||||
|
* from buffer[0], not from buffer[begin], and a bool that is true if at
|
||||||
|
* least one \n was found.
|
||||||
|
*/
|
||||||
|
std::pair<size_t,bool> normalize(char *buffer, ssize_t begin, ssize_t end);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform echo when reading a buffer
|
||||||
|
* \param chunkEnd one past the last character to echo back. The first
|
||||||
|
* character is chunkStart. As a side effect, this member function modifies
|
||||||
|
* chunkStart to be equal to chunkEnd+1, if echo is enabled
|
||||||
|
* \param sep optional line separator, printed after the chunk
|
||||||
|
* \param sepLen separator length
|
||||||
|
*/
|
||||||
|
void echoBack(const char *chunkEnd, const char *sep=0, size_t sepLen=0);
|
||||||
|
|
||||||
|
intrusive_ref_ptr<Device> device; ///< Underlying TTY device
|
||||||
|
FastMutex mutex; ///< Mutex to serialze concurrent reads
|
||||||
|
const char *chunkStart; ///< First character to echo in echoBack()
|
||||||
|
bool echo; ///< True if echo enabled
|
||||||
|
bool binary; ///< True if binary mode enabled
|
||||||
|
bool skipNewline; ///< Used by normalize()
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class holds the file object related to the console, that is set by
|
||||||
|
* the board support package, and used to populate /dev/console in DevFs
|
||||||
|
*/
|
||||||
|
class DefaultConsole
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \return an instance of this class (singleton)
|
||||||
|
*/
|
||||||
|
static DefaultConsole& instance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the board support package, in particular IRQbspInit(), to pass
|
||||||
|
* to the kernel the console device. This device file is used as the default
|
||||||
|
* one for stdin/stdout/stderr.
|
||||||
|
* Notes: this has to be called in IRQbspInit(), since if it's called too
|
||||||
|
* late the console gets initialized with a NullFile.
|
||||||
|
* Also, calling this a second time to dynamically change the console device
|
||||||
|
* is probably a bad idea, as the device is cached around in the filesystem
|
||||||
|
* code and will result in some processes using the old device and some
|
||||||
|
* other the new one.
|
||||||
|
* \param console device file handling console I/O. Can only be called with
|
||||||
|
* interrupts disabled.
|
||||||
|
*/
|
||||||
|
void IRQset(intrusive_ref_ptr<Device> console);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as IRQset(), but can be called with interrupts enabled
|
||||||
|
* \param console device file handling console I/O. Can only be called with
|
||||||
|
* interrupts disabled.
|
||||||
|
*/
|
||||||
|
void set(intrusive_ref_ptr<Device> console) { IRQset(console); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return the currently installed console device, wrapped in a
|
||||||
|
* TerminalDevice
|
||||||
|
*/
|
||||||
|
intrusive_ref_ptr<Device> get() { return console; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return the currently installed console device.
|
||||||
|
* Can be called with interrupts disabled or within an interrupt routine.
|
||||||
|
*/
|
||||||
|
intrusive_ref_ptr<Device> IRQget() { return console; }
|
||||||
|
|
||||||
|
#ifndef WITH_FILESYSTEM
|
||||||
|
/**
|
||||||
|
* \return the terminal device, when filesystem support is disabled.
|
||||||
|
* If filesystem is enabled, the terminal device can be found in the
|
||||||
|
* FileDescriptorTable
|
||||||
|
*/
|
||||||
|
intrusive_ref_ptr<TerminalDevice> getTerminal() { return terminal; }
|
||||||
|
#endif //WITH_FILESYSTEM
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Constructor, private as it is a singleton
|
||||||
|
*/
|
||||||
|
DefaultConsole();
|
||||||
|
|
||||||
|
DefaultConsole(const DefaultConsole&);
|
||||||
|
DefaultConsole& operator= (const DefaultConsole&);
|
||||||
|
|
||||||
|
intrusive_ref_ptr<Device> console; ///< The raw console device
|
||||||
|
#ifndef WITH_FILESYSTEM
|
||||||
|
intrusive_ref_ptr<TerminalDevice> terminal; ///< The wrapped console device
|
||||||
|
#endif //WITH_FILESYSTEM
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //CONSOLE_DEVICE_H
|
||||||
|
|
@ -0,0 +1,405 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "devfs.h"
|
||||||
|
#include <string>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include "filesystem/stringpart.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
static const int _NOSEEK=0x20000; //Special flag used only here to disallow seek
|
||||||
|
|
||||||
|
static void fillStatHelper(struct stat* pstat, unsigned int st_ino,
|
||||||
|
short st_dev, mode_t mode)
|
||||||
|
{
|
||||||
|
memset(pstat,0,sizeof(struct stat));
|
||||||
|
pstat->st_dev=st_dev;
|
||||||
|
pstat->st_ino=st_ino;
|
||||||
|
pstat->st_mode=mode;
|
||||||
|
pstat->st_nlink=1;
|
||||||
|
pstat->st_blksize=0; //If zero means file buffer equals to BUFSIZ
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file type is for reading and writing from devices
|
||||||
|
*/
|
||||||
|
class DevFsFile : public FileBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* \param fs pointer to DevFs
|
||||||
|
* \param dev the device to which this file refers
|
||||||
|
* \param flags file open flags (_FREAD, _FWRITE, ...)
|
||||||
|
*/
|
||||||
|
DevFsFile(intrusive_ref_ptr<FilesystemBase> fs,
|
||||||
|
intrusive_ref_ptr<Device> dev, int flags) : FileBase(fs),
|
||||||
|
dev(dev), seekPoint(0), flags(flags) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data to the file, if the file supports writing.
|
||||||
|
* \param data the data to write
|
||||||
|
* \param len the number of bytes to write
|
||||||
|
* \return the number of written characters, or a negative number in
|
||||||
|
* case of errors
|
||||||
|
*/
|
||||||
|
virtual ssize_t write(const void *data, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read data from the file, if the file supports reading.
|
||||||
|
* \param data buffer to store read data
|
||||||
|
* \param len the number of bytes to read
|
||||||
|
* \return the number of read characters, or a negative number in
|
||||||
|
* case of errors
|
||||||
|
*/
|
||||||
|
virtual ssize_t read(void *data, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move file pointer, if the file supports random-access.
|
||||||
|
* \param pos offset to sum to the beginning of the file, current position
|
||||||
|
* or end of file, depending on whence
|
||||||
|
* \param whence SEEK_SET, SEEK_CUR or SEEK_END
|
||||||
|
* \return the offset from the beginning of the file if the operation
|
||||||
|
* completed, or a negative number in case of errors
|
||||||
|
*/
|
||||||
|
virtual off_t lseek(off_t pos, int whence);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return file information.
|
||||||
|
* \param pstat pointer to stat struct
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int fstat(struct stat *pstat) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the file refers to a terminal.
|
||||||
|
* \return 1 if it is a terminal, 0 if it is not, or a negative number in
|
||||||
|
* case of errors
|
||||||
|
*/
|
||||||
|
virtual int isatty() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform various operations on a file descriptor
|
||||||
|
* \param cmd specifies the operation to perform
|
||||||
|
* \param arg optional argument that some operation require
|
||||||
|
* \return the exact return value depends on CMD, -1 is returned on error
|
||||||
|
*/
|
||||||
|
virtual int ioctl(int cmd, void *arg);
|
||||||
|
|
||||||
|
private:
|
||||||
|
intrusive_ref_ptr<Device> dev; ///< Device file
|
||||||
|
off_t seekPoint; ///< Seek point (note that off_t is 64bit)
|
||||||
|
int flags; ///< File open flags
|
||||||
|
};
|
||||||
|
|
||||||
|
ssize_t DevFsFile::write(const void *data, size_t len)
|
||||||
|
{
|
||||||
|
if((flags & _FWRITE)==0) return -EINVAL;
|
||||||
|
if(seekPoint+static_cast<off_t>(len)<0)
|
||||||
|
len=numeric_limits<off_t>::max()-seekPoint-len;
|
||||||
|
ssize_t result=dev->writeBlock(data,len,seekPoint);
|
||||||
|
if(result>0 && ((flags & _NOSEEK)==0)) seekPoint+=result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t DevFsFile::read(void *data, size_t len)
|
||||||
|
{
|
||||||
|
if((flags & _FREAD)==0) return -EINVAL;
|
||||||
|
if(seekPoint+static_cast<off_t>(len)<0)
|
||||||
|
len=numeric_limits<off_t>::max()-seekPoint-len;
|
||||||
|
ssize_t result=dev->readBlock(data,len,seekPoint);
|
||||||
|
if(result>0 && ((flags & _NOSEEK)==0)) seekPoint+=result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
off_t DevFsFile::lseek(off_t pos, int whence)
|
||||||
|
{
|
||||||
|
if(flags & _NOSEEK) return -EBADF; //No seek support
|
||||||
|
|
||||||
|
off_t newSeekPoint=seekPoint;
|
||||||
|
switch(whence)
|
||||||
|
{
|
||||||
|
case SEEK_CUR:
|
||||||
|
newSeekPoint+=pos;
|
||||||
|
break;
|
||||||
|
case SEEK_SET:
|
||||||
|
newSeekPoint=pos;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL; //TODO: how to implement SEEK_END?
|
||||||
|
}
|
||||||
|
if(newSeekPoint<0) return -EOVERFLOW;
|
||||||
|
seekPoint=newSeekPoint;
|
||||||
|
return seekPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DevFsFile::fstat(struct stat *pstat) const
|
||||||
|
{
|
||||||
|
return dev->fstat(pstat);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DevFsFile::isatty() const
|
||||||
|
{
|
||||||
|
return dev->isatty();
|
||||||
|
}
|
||||||
|
|
||||||
|
int DevFsFile::ioctl(int cmd, void *arg)
|
||||||
|
{
|
||||||
|
return dev->ioctl(cmd,arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// class Device
|
||||||
|
//
|
||||||
|
|
||||||
|
int Device::open(intrusive_ref_ptr<FileBase>& file,
|
||||||
|
intrusive_ref_ptr<FilesystemBase> fs, int flags, int mode)
|
||||||
|
{
|
||||||
|
flags++; //To convert from O_RDONLY, O_WRONLY, ... to _FREAD, _FWRITE, ...
|
||||||
|
file=intrusive_ref_ptr<FileBase>(
|
||||||
|
new DevFsFile(fs,shared_from_this(),flags | (seekable ? 0 : _NOSEEK)));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Device::fstat(struct stat* pstat) const
|
||||||
|
{
|
||||||
|
mode_t mode=(block ? S_IFBLK : S_IFCHR) | 0750;//brwxr-x--- | crwxr-x---
|
||||||
|
fillStatHelper(pstat,st_ino,st_dev,mode);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Device::isatty() const
|
||||||
|
{
|
||||||
|
return tty ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t Device::readBlock(void *buffer, size_t size, off_t where)
|
||||||
|
{
|
||||||
|
memset(buffer,0,size); //Act as /dev/zero
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t Device::writeBlock(const void *buffer, size_t size, off_t where)
|
||||||
|
{
|
||||||
|
return size; //Act as /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::IRQwrite(const char *str) {}
|
||||||
|
|
||||||
|
int Device::ioctl(int cmd, void *arg)
|
||||||
|
{
|
||||||
|
return -ENOTTY; //Means the operation does not apply to this descriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
Device::~Device() {}
|
||||||
|
|
||||||
|
#ifdef WITH_DEVFS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directory class for DevFs
|
||||||
|
*/
|
||||||
|
class DevFsDirectory : public DirectoryBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \param parent parent filesystem
|
||||||
|
* \param mutex mutex to lock when accessing the file map
|
||||||
|
* \param files file map
|
||||||
|
* \param currentInode inode of the directory we're listing
|
||||||
|
* \param parentInode inode of the parent directory
|
||||||
|
*/
|
||||||
|
DevFsDirectory(intrusive_ref_ptr<FilesystemBase> parent,
|
||||||
|
FastMutex& mutex,
|
||||||
|
map<StringPart,intrusive_ref_ptr<Device> >& files,
|
||||||
|
int currentInode, int parentInode)
|
||||||
|
: DirectoryBase(parent), mutex(mutex), files(files),
|
||||||
|
currentInode(currentInode), parentInode(parentInode),
|
||||||
|
first(true), last(false)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(files.empty()==false) currentItem=files.begin()->first.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Also directories can be opened as files. In this case, this system
|
||||||
|
* call allows to retrieve directory entries.
|
||||||
|
* \param dp pointer to a memory buffer where one or more struct dirent
|
||||||
|
* will be placed. dp must be four words aligned.
|
||||||
|
* \param len memory buffer size.
|
||||||
|
* \return the number of bytes read on success, or a negative number on
|
||||||
|
* failure.
|
||||||
|
*/
|
||||||
|
virtual int getdents(void *dp, int len);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FastMutex& mutex; ///< Mutex of parent class
|
||||||
|
map<StringPart,intrusive_ref_ptr<Device> >& files; ///< Directory entries
|
||||||
|
string currentItem; ///< First unhandled item in directory
|
||||||
|
int currentInode,parentInode; ///< Inodes of . and ..
|
||||||
|
|
||||||
|
bool first; ///< True if first time getdents is called
|
||||||
|
bool last; ///< True if directory has ended
|
||||||
|
};
|
||||||
|
|
||||||
|
int DevFsDirectory::getdents(void *dp, int len)
|
||||||
|
{
|
||||||
|
if(len<minimumBufferSize) return -EINVAL;
|
||||||
|
if(last) return 0;
|
||||||
|
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
char *begin=reinterpret_cast<char*>(dp);
|
||||||
|
char *buffer=begin;
|
||||||
|
char *end=buffer+len;
|
||||||
|
if(first)
|
||||||
|
{
|
||||||
|
first=false;
|
||||||
|
addDefaultEntries(&buffer,currentInode,parentInode);
|
||||||
|
}
|
||||||
|
if(currentItem.empty()==false)
|
||||||
|
{
|
||||||
|
map<StringPart,intrusive_ref_ptr<Device> >::iterator it;
|
||||||
|
it=files.find(StringPart(currentItem));
|
||||||
|
//Someone deleted the exact directory entry we had saved (unlikely)
|
||||||
|
if(it==files.end()) return -EBADF;
|
||||||
|
for(;it!=files.end();++it)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
it->second->fstat(&st);
|
||||||
|
if(addEntry(&buffer,end,st.st_ino,st.st_mode>>12,it->first)>0)
|
||||||
|
continue;
|
||||||
|
//Buffer finished
|
||||||
|
currentItem=it->first.c_str();
|
||||||
|
return buffer-begin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addTerminatingEntry(&buffer,end);
|
||||||
|
last=true;
|
||||||
|
return buffer-begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// class DevFs
|
||||||
|
//
|
||||||
|
|
||||||
|
DevFs::DevFs() : mutex(FastMutex::RECURSIVE), inodeCount(rootDirInode+1)
|
||||||
|
{
|
||||||
|
addDevice("null",intrusive_ref_ptr<Device>(new Device(Device::STREAM)));
|
||||||
|
addDevice("zero",intrusive_ref_ptr<Device>(new Device(Device::STREAM)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DevFs::addDevice(const char *name, intrusive_ref_ptr<Device> dev)
|
||||||
|
{
|
||||||
|
if(name==0 || name[0]=='\0') return false;
|
||||||
|
int len=strlen(name);
|
||||||
|
for(int i=0;i<len;i++) if(name[i]=='/') return false;
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
bool result=files.insert(make_pair(StringPart(name),dev)).second;
|
||||||
|
//Assign inode to the file
|
||||||
|
if(result) dev->setFileInfo(atomicAddExchange(&inodeCount,1),filesystemId);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DevFs::remove(const char* name)
|
||||||
|
{
|
||||||
|
if(name==0 || name[0]=='\0') return false;
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
map<StringPart,intrusive_ref_ptr<Device> >::iterator it;
|
||||||
|
it=files.find(StringPart(name));
|
||||||
|
if(it==files.end()) return false;
|
||||||
|
files.erase(StringPart(name));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DevFs::open(intrusive_ref_ptr<FileBase>& file, StringPart& name,
|
||||||
|
int flags, int mode)
|
||||||
|
{
|
||||||
|
if(flags & (O_APPEND | O_EXCL)) return -EACCES;
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(name.empty()) //Trying to open the root directory of the fs
|
||||||
|
{
|
||||||
|
if(flags & (O_WRONLY | O_RDWR)) return -EACCES;
|
||||||
|
file=intrusive_ref_ptr<FileBase>(
|
||||||
|
new DevFsDirectory(shared_from_this(),
|
||||||
|
mutex,files,rootDirInode,parentFsMountpointInode));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
map<StringPart,intrusive_ref_ptr<Device> >::iterator it=files.find(name);
|
||||||
|
if(it==files.end()) return -ENOENT;
|
||||||
|
return it->second->open(file,shared_from_this(),flags,mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DevFs::lstat(StringPart& name, struct stat *pstat)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(name.empty())
|
||||||
|
{
|
||||||
|
fillStatHelper(pstat,rootDirInode,filesystemId,S_IFDIR | 0755);//drwxr-xr-x
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
map<StringPart,intrusive_ref_ptr<Device> >::iterator it=files.find(name);
|
||||||
|
if(it==files.end()) return -ENOENT;
|
||||||
|
return it->second->fstat(pstat);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DevFs::unlink(StringPart& name)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(files.erase(name)==1) return 0;
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DevFs::rename(StringPart& oldName, StringPart& newName)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
map<StringPart,intrusive_ref_ptr<Device> >::iterator it=files.find(oldName);
|
||||||
|
if(it==files.end()) return -ENOENT;
|
||||||
|
for(unsigned int i=0;i<newName.length();i++)
|
||||||
|
if(newName[i]=='/')
|
||||||
|
return -EACCES; //DevFs does not support subdirectories
|
||||||
|
files.erase(newName); //If it exists
|
||||||
|
files.insert(make_pair(newName,it->second));
|
||||||
|
files.erase(it);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DevFs::mkdir(StringPart& name, int mode)
|
||||||
|
{
|
||||||
|
return -EACCES; // No directories support in DevFs yet
|
||||||
|
}
|
||||||
|
|
||||||
|
int DevFs::rmdir(StringPart& name)
|
||||||
|
{
|
||||||
|
return -EACCES; // No directories support in DevFs yet
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //WITH_DEVFS
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,272 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef DEVFS_H
|
||||||
|
#define DEVFS_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include "filesystem/file.h"
|
||||||
|
#include "filesystem/stringpart.h"
|
||||||
|
#include "kernel/sync.h"
|
||||||
|
#include "config/miosix_settings.h"
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instances of this class are devices inside DevFs. When open is called, a
|
||||||
|
* DevFsFile is returned, which has its own seek point so that multiple files
|
||||||
|
* can be opened on the same device retaining an unique seek point. A DevFsFile
|
||||||
|
* then calls readBlock() and writeBlock() on this class. These functions have a
|
||||||
|
* third argument which is the seek point, making them stateless.
|
||||||
|
*
|
||||||
|
* Individual devices must subclass Device and reimplement readBlock(),
|
||||||
|
* writeBlock() and ioctl() as needed. A mutex may be required as multiple
|
||||||
|
* concurrent readBlock(), writeBlock() and ioctl() can occur.
|
||||||
|
*
|
||||||
|
* Classes of this type are reference counted, must be allocated on the heap
|
||||||
|
* and managed through intrusive_ref_ptr<FileBase>
|
||||||
|
*
|
||||||
|
* This class is defined also if WITH_DEVFS is not defined as it is used by the
|
||||||
|
* Console interface, but in this case the interface is reduced to a minimum
|
||||||
|
*/
|
||||||
|
class Device : public IntrusiveRefCounted,
|
||||||
|
public IntrusiveRefCountedSharedFromThis<Device>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Possible device types
|
||||||
|
*/
|
||||||
|
enum DeviceType
|
||||||
|
{
|
||||||
|
STREAM, ///< Not seekable device, like /dev/random
|
||||||
|
BLOCK, ///< Seekable block device
|
||||||
|
TTY ///< Like STREAM, but additionally is a TTY
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* \param d device type
|
||||||
|
*/
|
||||||
|
Device(DeviceType d) : seekable(d==BLOCK), block(d==BLOCK), tty(d==TTY)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance of the file type managed by this Device
|
||||||
|
* \param file the file object will be stored here, if the call succeeds
|
||||||
|
* \param fs pointer to the DevFs
|
||||||
|
* \param flags file flags (open for reading, writing, ...)
|
||||||
|
* \param mode file permissions
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
int open(intrusive_ref_ptr<FileBase>& file,
|
||||||
|
intrusive_ref_ptr<FilesystemBase> fs, int flags, int mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain information for the file type managed by this Device
|
||||||
|
* \param pstat file information is stored here
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
int fstat(struct stat *pstat) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the file refers to a terminal.
|
||||||
|
* \return 1 if it is a terminal, 0 if it is not, or a negative number in
|
||||||
|
* case of errors
|
||||||
|
*/
|
||||||
|
virtual int isatty() const;
|
||||||
|
|
||||||
|
#ifdef WITH_DEVFS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Called be DevFs to assign a device and inode to the Device
|
||||||
|
*/
|
||||||
|
void setFileInfo(unsigned int st_ino, short st_dev)
|
||||||
|
{
|
||||||
|
this->st_ino=st_ino;
|
||||||
|
this->st_dev=st_dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //WITH_DEVFS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a block of data
|
||||||
|
* \param buffer buffer where read data will be stored
|
||||||
|
* \param size buffer size
|
||||||
|
* \param where where to read from
|
||||||
|
* \return number of bytes read or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual ssize_t readBlock(void *buffer, size_t size, off_t where);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a block of data
|
||||||
|
* \param buffer buffer where take data to write
|
||||||
|
* \param size buffer size
|
||||||
|
* \param where where to write to
|
||||||
|
* \return number of bytes written or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual ssize_t writeBlock(const void *buffer, size_t size, off_t where);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a string.
|
||||||
|
* An extension to the Device interface that adds a new member function,
|
||||||
|
* which is used by the kernel on console devices to write debug information
|
||||||
|
* before the kernel is started or in case of serious errors, right before
|
||||||
|
* rebooting.
|
||||||
|
* Can ONLY be called when the kernel is not yet started, paused or within
|
||||||
|
* an interrupt. This default implementation ignores writes.
|
||||||
|
* \param str the string to write. The string must be NUL terminated.
|
||||||
|
*/
|
||||||
|
virtual void IRQwrite(const char *str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs device-specific operations
|
||||||
|
* \param cmd specifies the operation to perform
|
||||||
|
* \param arg optional argument that some operation require
|
||||||
|
* \return the exact return value depends on CMD, -1 is returned on error
|
||||||
|
*/
|
||||||
|
virtual int ioctl(int cmd, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor
|
||||||
|
*/
|
||||||
|
virtual ~Device();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
unsigned int st_ino; ///< inode of device file
|
||||||
|
short st_dev; ///< device (unique id of the filesystem) of device file
|
||||||
|
const bool seekable; ///< If true, device is seekable
|
||||||
|
const bool block; ///< If true, it is a block device
|
||||||
|
const bool tty; ///< If true, it is a tty
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef WITH_DEVFS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DevFs is a special filesystem meant to access devices as they were files.
|
||||||
|
* For this reason, it is a little different from other filesystems. Normal
|
||||||
|
* filesystems create FileBase objects ondemand, to answer an open() call. Such
|
||||||
|
* files have a parent pointer that points to the filesystem. On the contrary,
|
||||||
|
* DevFs is a collection of both pre-existing DeviceFiles (for stateless files),
|
||||||
|
* or DeviceFileGenerators for stateful ones. Each device file is a different
|
||||||
|
* subclass of FileBase that overrides some of its member functions to access
|
||||||
|
* the handled device. These FileBase subclasses do not have a parent pointer
|
||||||
|
* into DevFs, and as such umounting DevFs should better be avoided, as it's
|
||||||
|
* not possible to detect if some of its files are currently opened by some
|
||||||
|
* application. What will happen is that the individual files (and
|
||||||
|
* DeviceFileGenerators) won't be deleted until the processes that have them
|
||||||
|
* opened close them.
|
||||||
|
*/
|
||||||
|
class DevFs : public FilesystemBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
DevFs();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a device file to DevFs
|
||||||
|
* \param name File name, must not start with a slash
|
||||||
|
* \param df Device file. Every open() call will return the same file
|
||||||
|
* \return true if the file was successfully added
|
||||||
|
*/
|
||||||
|
bool addDevice(const char *name, intrusive_ref_ptr<Device> dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a device. This prevents the device from being opened again,
|
||||||
|
* but if at the time this member function is called the file is already
|
||||||
|
* opened, it won't be deallocated till the application closes it, thanks
|
||||||
|
* to the reference counting scheme.
|
||||||
|
* \param name name of file to remove
|
||||||
|
* \return true if the file was successfully removed
|
||||||
|
*/
|
||||||
|
bool remove(const char *name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a file
|
||||||
|
* \param file the file object will be stored here, if the call succeeds
|
||||||
|
* \param name the name of the file to open, relative to the local
|
||||||
|
* filesystem
|
||||||
|
* \param flags file flags (open for reading, writing, ...)
|
||||||
|
* \param mode file permissions
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int open(intrusive_ref_ptr<FileBase>& file, StringPart& name,
|
||||||
|
int flags, int mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain information on a file, identified by a path name. Does not follow
|
||||||
|
* symlinks
|
||||||
|
* \param name path name, relative to the local filesystem
|
||||||
|
* \param pstat file information is stored here
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int lstat(StringPart& name, struct stat *pstat);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a file or directory
|
||||||
|
* \param name path name of file or directory to remove
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int unlink(StringPart& name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename a file or directory
|
||||||
|
* \param oldName old file name
|
||||||
|
* \param newName new file name
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int rename(StringPart& oldName, StringPart& newName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a directory
|
||||||
|
* \param name directory name
|
||||||
|
* \param mode directory permissions
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int mkdir(StringPart& name, int mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a directory if empty
|
||||||
|
* \param name directory name
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int rmdir(StringPart& name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
FastMutex mutex;
|
||||||
|
std::map<StringPart,intrusive_ref_ptr<Device> > files;
|
||||||
|
int inodeCount;
|
||||||
|
static const int rootDirInode=1;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //WITH_DEVFS
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //DEVFS_H
|
||||||
|
|
@ -0,0 +1,530 @@
|
||||||
|
/*------------------------------------------------------------------------*/
|
||||||
|
/* Unicode - Local code bidirectional converter (C)ChaN, 2012 */
|
||||||
|
/* (SBCS code pages) */
|
||||||
|
/*------------------------------------------------------------------------*/
|
||||||
|
/* 437 U.S. (OEM)
|
||||||
|
/ 720 Arabic (OEM)
|
||||||
|
/ 1256 Arabic (Windows)
|
||||||
|
/ 737 Greek (OEM)
|
||||||
|
/ 1253 Greek (Windows)
|
||||||
|
/ 1250 Central Europe (Windows)
|
||||||
|
/ 775 Baltic (OEM)
|
||||||
|
/ 1257 Baltic (Windows)
|
||||||
|
/ 850 Multilingual Latin 1 (OEM)
|
||||||
|
/ 852 Latin 2 (OEM)
|
||||||
|
/ 1252 Latin 1 (Windows)
|
||||||
|
/ 855 Cyrillic (OEM)
|
||||||
|
/ 1251 Cyrillic (Windows)
|
||||||
|
/ 866 Russian (OEM)
|
||||||
|
/ 857 Turkish (OEM)
|
||||||
|
/ 1254 Turkish (Windows)
|
||||||
|
/ 858 Multilingual Latin 1 + Euro (OEM)
|
||||||
|
/ 862 Hebrew (OEM)
|
||||||
|
/ 1255 Hebrew (Windows)
|
||||||
|
/ 874 Thai (OEM, Windows)
|
||||||
|
/ 1258 Vietnam (OEM, Windows)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ff.h"
|
||||||
|
#include "config/miosix_settings.h"
|
||||||
|
|
||||||
|
#ifdef WITH_FILESYSTEM
|
||||||
|
|
||||||
|
#if _CODE_PAGE == 437
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP437(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
|
||||||
|
0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
|
||||||
|
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
|
||||||
|
0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
|
||||||
|
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
|
||||||
|
0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
||||||
|
0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
|
||||||
|
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||||
|
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
|
||||||
|
0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||||
|
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
|
||||||
|
0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
|
||||||
|
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
|
||||||
|
0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 720
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP720(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7,
|
||||||
|
0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9,
|
||||||
|
0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627,
|
||||||
|
0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F,
|
||||||
|
0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
||||||
|
0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
|
||||||
|
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||||
|
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
|
||||||
|
0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||||
|
0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642,
|
||||||
|
0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A,
|
||||||
|
0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x2248,
|
||||||
|
0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 737
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP737(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398,
|
||||||
|
0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0,
|
||||||
|
0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9,
|
||||||
|
0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
|
||||||
|
0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0,
|
||||||
|
0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
||||||
|
0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
|
||||||
|
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||||
|
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
|
||||||
|
0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||||
|
0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD,
|
||||||
|
0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E,
|
||||||
|
0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248,
|
||||||
|
0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 775
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP775(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107,
|
||||||
|
0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5,
|
||||||
|
0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A,
|
||||||
|
0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4,
|
||||||
|
0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6,
|
||||||
|
0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118,
|
||||||
|
0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A,
|
||||||
|
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D,
|
||||||
|
0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B,
|
||||||
|
0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||||
|
0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144,
|
||||||
|
0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019,
|
||||||
|
0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E,
|
||||||
|
0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 850
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP850(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
|
||||||
|
0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
|
||||||
|
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
|
||||||
|
0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
|
||||||
|
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
|
||||||
|
0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
|
||||||
|
0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
|
||||||
|
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
|
||||||
|
0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE,
|
||||||
|
0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
|
||||||
|
0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,
|
||||||
|
0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
|
||||||
|
0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
|
||||||
|
0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 852
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP852(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7,
|
||||||
|
0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106,
|
||||||
|
0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A,
|
||||||
|
0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D,
|
||||||
|
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E,
|
||||||
|
0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A,
|
||||||
|
0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103,
|
||||||
|
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
|
||||||
|
0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE,
|
||||||
|
0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580,
|
||||||
|
0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161,
|
||||||
|
0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4,
|
||||||
|
0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8,
|
||||||
|
0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 855
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP855(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404,
|
||||||
|
0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408,
|
||||||
|
0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C,
|
||||||
|
0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A,
|
||||||
|
0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414,
|
||||||
|
0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438,
|
||||||
|
0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A,
|
||||||
|
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
|
||||||
|
0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E,
|
||||||
|
0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580,
|
||||||
|
0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443,
|
||||||
|
0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116,
|
||||||
|
0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D,
|
||||||
|
0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 857
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP857(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
|
||||||
|
0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5,
|
||||||
|
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
|
||||||
|
0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F,
|
||||||
|
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F,
|
||||||
|
0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
|
||||||
|
0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
|
||||||
|
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
|
||||||
|
0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE,
|
||||||
|
0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
|
||||||
|
0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000,
|
||||||
|
0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4,
|
||||||
|
0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
|
||||||
|
0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 858
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP858(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
|
||||||
|
0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
|
||||||
|
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
|
||||||
|
0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
|
||||||
|
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
|
||||||
|
0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
|
||||||
|
0x00A9, 0x2563, 0x2551, 0x2557, 0x2550, 0x00A2, 0x00A5, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
|
||||||
|
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
|
||||||
|
0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x20AC, 0x00CD, 0x00CE,
|
||||||
|
0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00C6, 0x00CC, 0x2580,
|
||||||
|
0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,
|
||||||
|
0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
|
||||||
|
0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
|
||||||
|
0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 862
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP862(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,
|
||||||
|
0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
|
||||||
|
0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,
|
||||||
|
0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
|
||||||
|
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
|
||||||
|
0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
||||||
|
0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
|
||||||
|
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||||
|
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
|
||||||
|
0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||||
|
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
|
||||||
|
0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
|
||||||
|
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
|
||||||
|
0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 866
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP866(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
|
||||||
|
0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
|
||||||
|
0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
|
||||||
|
0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
|
||||||
|
0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
|
||||||
|
0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
||||||
|
0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
|
||||||
|
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||||
|
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
|
||||||
|
0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||||
|
0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
|
||||||
|
0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
|
||||||
|
0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E,
|
||||||
|
0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 874
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP874(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x20AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x2026, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x00A0, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07,
|
||||||
|
0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F,
|
||||||
|
0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17,
|
||||||
|
0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F,
|
||||||
|
0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27,
|
||||||
|
0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F,
|
||||||
|
0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37,
|
||||||
|
0x0E38, 0x0E39, 0x0E3A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0E3F,
|
||||||
|
0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47,
|
||||||
|
0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F,
|
||||||
|
0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57,
|
||||||
|
0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0x0000, 0x0000, 0x0000, 0x0000
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 1250
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP1250(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||||
|
0x0000, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179,
|
||||||
|
0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||||
|
0x0000, 0x2122, 0x0161, 0x203A, 0x015B, 0x0165, 0x017E, 0x017A,
|
||||||
|
0x00A0, 0x02C7, 0x02D8, 0x0141, 0x00A4, 0x0104, 0x00A6, 0x00A7,
|
||||||
|
0x00A8, 0x00A9, 0x015E, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x017B,
|
||||||
|
0x00B0, 0x00B1, 0x02DB, 0x0142, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
|
||||||
|
0x00B8, 0x0105, 0x015F, 0x00BB, 0x013D, 0x02DD, 0x013E, 0x017C,
|
||||||
|
0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7,
|
||||||
|
0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E,
|
||||||
|
0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7,
|
||||||
|
0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF,
|
||||||
|
0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7,
|
||||||
|
0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F,
|
||||||
|
0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7,
|
||||||
|
0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 1251
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP1251(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||||
|
0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F,
|
||||||
|
0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||||
|
0x0000, 0x2111, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F,
|
||||||
|
0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7,
|
||||||
|
0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407,
|
||||||
|
0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7,
|
||||||
|
0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457,
|
||||||
|
0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
|
||||||
|
0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
|
||||||
|
0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
|
||||||
|
0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
|
||||||
|
0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
|
||||||
|
0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
|
||||||
|
0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
|
||||||
|
0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 1252
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP1252(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||||
|
0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017D, 0x0000,
|
||||||
|
0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||||
|
0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x017E, 0x0178,
|
||||||
|
0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
|
||||||
|
0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
|
||||||
|
0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
|
||||||
|
0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
|
||||||
|
0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
|
||||||
|
0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
|
||||||
|
0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
|
||||||
|
0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
|
||||||
|
0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
|
||||||
|
0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
|
||||||
|
0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
|
||||||
|
0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 1253
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP1253(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||||
|
0x0000, 0x2030, 0x0000, 0x2039, 0x000C, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||||
|
0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x00A0, 0x0385, 0x0386, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
|
||||||
|
0x00A8, 0x00A9, 0x0000, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x2015,
|
||||||
|
0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x00B5, 0x00B6, 0x00B7,
|
||||||
|
0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F,
|
||||||
|
0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
|
||||||
|
0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,
|
||||||
|
0x03A0, 0x03A1, 0x0000, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7,
|
||||||
|
0x03A8, 0x03A9, 0x03AA, 0x03AD, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
|
||||||
|
0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
|
||||||
|
0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
|
||||||
|
0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,
|
||||||
|
0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x0000
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 1254
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP1254(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x20AC, 0x0000, 0x210A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||||
|
0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||||
|
0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178,
|
||||||
|
0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
|
||||||
|
0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
|
||||||
|
0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
|
||||||
|
0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
|
||||||
|
0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
|
||||||
|
0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
|
||||||
|
0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
|
||||||
|
0x00D8, 0x00D9, 0x00DA, 0x00BD, 0x00DC, 0x0130, 0x015E, 0x00DF,
|
||||||
|
0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
|
||||||
|
0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
|
||||||
|
0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
|
||||||
|
0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 1255
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP1255(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||||
|
0x02C6, 0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||||
|
0x02DC, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
|
||||||
|
0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
|
||||||
|
0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
|
||||||
|
0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
|
||||||
|
0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7,
|
||||||
|
0x05B8, 0x05B9, 0x0000, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF,
|
||||||
|
0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05F0, 0x05F1, 0x05F2, 0x05F3,
|
||||||
|
0x05F4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,
|
||||||
|
0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
|
||||||
|
0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,
|
||||||
|
0x05E8, 0x05E9, 0x05EA, 0x0000, 0x0000, 0x200E, 0x200F, 0x0000
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 1256
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP1256(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x20AC, 0x067E, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||||
|
0x02C6, 0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688,
|
||||||
|
0x06AF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||||
|
0x06A9, 0x2122, 0x0691, 0x203A, 0x0153, 0x200C, 0x200D, 0x06BA,
|
||||||
|
0x00A0, 0x060C, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
|
||||||
|
0x00A8, 0x00A9, 0x06BE, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
|
||||||
|
0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
|
||||||
|
0x00B8, 0x00B9, 0x061B, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x061F,
|
||||||
|
0x06C1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627,
|
||||||
|
0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F,
|
||||||
|
0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00D7,
|
||||||
|
0x0637, 0x0638, 0x0639, 0x063A, 0x0640, 0x0640, 0x0642, 0x0643,
|
||||||
|
0x00E0, 0x0644, 0x00E2, 0x0645, 0x0646, 0x0647, 0x0648, 0x00E7,
|
||||||
|
0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0649, 0x064A, 0x00EE, 0x00EF,
|
||||||
|
0x064B, 0x064C, 0x064D, 0x064E, 0x00F4, 0x064F, 0x0650, 0x00F7,
|
||||||
|
0x0651, 0x00F9, 0x0652, 0x00FB, 0x00FC, 0x200E, 0x200F, 0x06D2
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 1257
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP1257(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||||
|
0x0000, 0x2030, 0x0000, 0x2039, 0x0000, 0x00A8, 0x02C7, 0x00B8,
|
||||||
|
0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||||
|
0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x00AF, 0x02DB, 0x0000,
|
||||||
|
0x00A0, 0x0000, 0x00A2, 0x00A3, 0x00A4, 0x0000, 0x00A6, 0x00A7,
|
||||||
|
0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
|
||||||
|
0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
|
||||||
|
0x00B8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6,
|
||||||
|
0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112,
|
||||||
|
0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B,
|
||||||
|
0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7,
|
||||||
|
0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF,
|
||||||
|
0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113,
|
||||||
|
0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C,
|
||||||
|
0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7,
|
||||||
|
0x0173, 0x014E, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x02D9
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 1258
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP1258(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||||
|
0x02C6, 0x2030, 0x0000, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||||
|
0x02DC, 0x2122, 0x0000, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178,
|
||||||
|
0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
|
||||||
|
0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
|
||||||
|
0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
|
||||||
|
0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
|
||||||
|
0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
|
||||||
|
0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x0300, 0x00CD, 0x00CE, 0x00CF,
|
||||||
|
0x0110, 0x00D1, 0x0309, 0x00D3, 0x00D4, 0x01A0, 0x00D6, 0x00D7,
|
||||||
|
0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x01AF, 0x0303, 0x00DF,
|
||||||
|
0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
|
||||||
|
0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0301, 0x00ED, 0x00EE, 0x00EF,
|
||||||
|
0x0111, 0x00F1, 0x0323, 0x00F3, 0x00F4, 0x01A1, 0x00F6, 0x00F7,
|
||||||
|
0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x01B0, 0x20AB, 0x00FF
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if !_TBLDEF || !_USE_LFN
|
||||||
|
#error This file is not needed in current configuration. Remove from the project.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
WCHAR ff_convert ( /* Converted character, Returns zero on error */
|
||||||
|
WCHAR chr, /* Character code to be converted */
|
||||||
|
UINT dir /* 0: Unicode to OEMCP, 1: OEMCP to Unicode */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
WCHAR c;
|
||||||
|
|
||||||
|
|
||||||
|
if (chr < 0x80) { /* ASCII */
|
||||||
|
c = chr;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (dir) { /* OEMCP to Unicode */
|
||||||
|
c = (chr >= 0x100) ? 0 : Tbl[chr - 0x80];
|
||||||
|
|
||||||
|
} else { /* Unicode to OEMCP */
|
||||||
|
for (c = 0; c < 0x80; c++) {
|
||||||
|
if (chr == Tbl[c]) break;
|
||||||
|
}
|
||||||
|
c = (c + 0x80) & 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //WITH_FILESYSTEM
|
||||||
|
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* Integration of FatFs filesystem module in Miosix by Terraneo Federico
|
||||||
|
* based on original files diskio.c and mmc.c by ChaN
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "diskio.h"
|
||||||
|
#include "filesystem/ioctl.h"
|
||||||
|
#include "config/miosix_settings.h"
|
||||||
|
|
||||||
|
#ifdef WITH_FILESYSTEM
|
||||||
|
|
||||||
|
using namespace miosix;
|
||||||
|
|
||||||
|
// #ifdef __cplusplus
|
||||||
|
// extern "C" {
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
///**
|
||||||
|
// * \internal
|
||||||
|
// * Initializes drive.
|
||||||
|
// */
|
||||||
|
//DSTATUS disk_initialize (
|
||||||
|
// intrusive_ref_ptr<FileBase> pdrv /* Physical drive nmuber (0..) */
|
||||||
|
//)
|
||||||
|
//{
|
||||||
|
// if(Disk::isAvailable()==false) return STA_NODISK;
|
||||||
|
// Disk::init();
|
||||||
|
// if(Disk::isInitialized()) return RES_OK;
|
||||||
|
// else return STA_NOINIT;
|
||||||
|
//}
|
||||||
|
|
||||||
|
///**
|
||||||
|
// * \internal
|
||||||
|
// * Return status of drive.
|
||||||
|
// */
|
||||||
|
//DSTATUS disk_status (
|
||||||
|
// intrusive_ref_ptr<FileBase> pdrv /* Physical drive nmuber (0..) */
|
||||||
|
//)
|
||||||
|
//{
|
||||||
|
// if(Disk::isInitialized()) return RES_OK;
|
||||||
|
// else return STA_NOINIT;
|
||||||
|
//}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Read one or more sectors from drive
|
||||||
|
*/
|
||||||
|
DRESULT disk_read (
|
||||||
|
intrusive_ref_ptr<FileBase> pdrv, /* Physical drive nmuber (0..) */
|
||||||
|
BYTE *buff, /* Data buffer to store read data */
|
||||||
|
DWORD sector, /* Sector address (LBA) */
|
||||||
|
UINT count /* Number of sectors to read (1..255) */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if(pdrv->lseek(static_cast<off_t>(sector)*512,SEEK_SET)<0) return RES_ERROR;
|
||||||
|
if(pdrv->read(buff,count*512)!=static_cast<ssize_t>(count)*512) return RES_ERROR;
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Write one or more sectors to drive
|
||||||
|
*/
|
||||||
|
DRESULT disk_write (
|
||||||
|
intrusive_ref_ptr<FileBase> pdrv, /* Physical drive nmuber (0..) */
|
||||||
|
const BYTE *buff, /* Data to be written */
|
||||||
|
DWORD sector, /* Sector address (LBA) */
|
||||||
|
UINT count /* Number of sectors to write (1..255) */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if(pdrv->lseek(static_cast<off_t>(sector)*512,SEEK_SET)<0) return RES_ERROR;
|
||||||
|
if(pdrv->write(buff,count*512)!=static_cast<ssize_t>(count)*512) return RES_ERROR;
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* To perform disk functions other thar read/write
|
||||||
|
*/
|
||||||
|
DRESULT disk_ioctl (
|
||||||
|
intrusive_ref_ptr<FileBase> pdrv, /* Physical drive nmuber (0..) */
|
||||||
|
BYTE ctrl, /* Control code */
|
||||||
|
void *buff /* Buffer to send/receive control data */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
switch(ctrl)
|
||||||
|
{
|
||||||
|
case CTRL_SYNC:
|
||||||
|
if(pdrv->ioctl(IOCTL_SYNC,0)==0) return RES_OK; else return RES_ERROR;
|
||||||
|
case GET_SECTOR_COUNT:
|
||||||
|
return RES_ERROR; //unimplemented, so f_mkfs() does not work
|
||||||
|
case GET_BLOCK_SIZE:
|
||||||
|
return RES_ERROR; //unimplemented, so f_mkfs() does not work
|
||||||
|
default:
|
||||||
|
return RES_PARERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Return current time, used to save file creation time
|
||||||
|
*/
|
||||||
|
DWORD get_fattime()
|
||||||
|
{
|
||||||
|
return 0x210000;//TODO: this stub just returns date 01/01/1980 0.00.00
|
||||||
|
}
|
||||||
|
|
||||||
|
// #ifdef __cplusplus
|
||||||
|
// }
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
#endif //WITH_FILESYSTEM
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*-----------------------------------------------------------------------
|
||||||
|
/ Low level disk interface modlue include file (C)ChaN, 2013
|
||||||
|
/-----------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#ifndef _DISKIO_DEFINED
|
||||||
|
#define _DISKIO_DEFINED
|
||||||
|
|
||||||
|
//#ifdef __cplusplus
|
||||||
|
//extern "C" {
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
#define _USE_WRITE 1 /* 1: Enable disk_write function */
|
||||||
|
#define _USE_IOCTL 1 /* 1: Enable disk_ioctl fucntion */
|
||||||
|
|
||||||
|
#include "integer.h"
|
||||||
|
#include <filesystem/file.h>
|
||||||
|
#include "config/miosix_settings.h"
|
||||||
|
|
||||||
|
#ifdef WITH_FILESYSTEM
|
||||||
|
|
||||||
|
|
||||||
|
/* Status of Disk Functions */
|
||||||
|
typedef BYTE DSTATUS;
|
||||||
|
|
||||||
|
/* Results of Disk Functions */
|
||||||
|
typedef enum {
|
||||||
|
RES_OK = 0, /* 0: Successful */
|
||||||
|
RES_ERROR, /* 1: R/W Error */
|
||||||
|
RES_WRPRT, /* 2: Write Protected */
|
||||||
|
RES_NOTRDY, /* 3: Not Ready */
|
||||||
|
RES_PARERR /* 4: Invalid Parameter */
|
||||||
|
} DRESULT;
|
||||||
|
|
||||||
|
|
||||||
|
/*---------------------------------------*/
|
||||||
|
/* Prototypes for disk control functions */
|
||||||
|
|
||||||
|
|
||||||
|
DSTATUS disk_initialize (miosix::intrusive_ref_ptr<miosix::FileBase> pdrv);
|
||||||
|
DSTATUS disk_status (miosix::intrusive_ref_ptr<miosix::FileBase> pdrv);
|
||||||
|
DRESULT disk_read (miosix::intrusive_ref_ptr<miosix::FileBase> pdrv,
|
||||||
|
BYTE*buff, DWORD sector, UINT count);
|
||||||
|
DRESULT disk_write (miosix::intrusive_ref_ptr<miosix::FileBase> pdrv,
|
||||||
|
const BYTE* buff, DWORD sector, UINT count);
|
||||||
|
DRESULT disk_ioctl (miosix::intrusive_ref_ptr<miosix::FileBase> pdrv,
|
||||||
|
BYTE cmd, void* buff);
|
||||||
|
|
||||||
|
|
||||||
|
/* Disk Status Bits (DSTATUS) */
|
||||||
|
#define STA_NOINIT 0x01 /* Drive not initialized */
|
||||||
|
#define STA_NODISK 0x02 /* No medium in the drive */
|
||||||
|
#define STA_PROTECT 0x04 /* Write protected */
|
||||||
|
|
||||||
|
|
||||||
|
/* Command code for disk_ioctrl fucntion */
|
||||||
|
|
||||||
|
/* Generic command (used by FatFs) */
|
||||||
|
#define CTRL_SYNC 0 /* Flush disk cache (for write functions) */
|
||||||
|
#define GET_SECTOR_COUNT 1 /* Get media size (for only f_mkfs()) */
|
||||||
|
#define GET_SECTOR_SIZE 2 /* Get sector size (for multiple sector size (_MAX_SS >= 1024)) */
|
||||||
|
#define GET_BLOCK_SIZE 3 /* Get erase block size (for only f_mkfs()) */
|
||||||
|
#define CTRL_ERASE_SECTOR 4 /* Force erased a block of sectors (for only _USE_ERASE) */
|
||||||
|
|
||||||
|
/* Generic command (not used by FatFs) */
|
||||||
|
#define CTRL_POWER 5 /* Get/Set power status */
|
||||||
|
#define CTRL_LOCK 6 /* Lock/Unlock media removal */
|
||||||
|
#define CTRL_EJECT 7 /* Eject media */
|
||||||
|
#define CTRL_FORMAT 8 /* Create physical format on the media */
|
||||||
|
|
||||||
|
/* MMC/SDC specific ioctl command */
|
||||||
|
#define MMC_GET_TYPE 10 /* Get card type */
|
||||||
|
#define MMC_GET_CSD 11 /* Get CSD */
|
||||||
|
#define MMC_GET_CID 12 /* Get CID */
|
||||||
|
#define MMC_GET_OCR 13 /* Get OCR */
|
||||||
|
#define MMC_GET_SDSTAT 14 /* Get SD status */
|
||||||
|
|
||||||
|
/* ATA/CF specific ioctl command */
|
||||||
|
#define ATA_GET_REV 20 /* Get F/W revision */
|
||||||
|
#define ATA_GET_MODEL 21 /* Get model name */
|
||||||
|
#define ATA_GET_SN 22 /* Get serial number */
|
||||||
|
|
||||||
|
|
||||||
|
/* MMC card type flags (MMC_GET_TYPE) */
|
||||||
|
#define CT_MMC 0x01 /* MMC ver 3 */
|
||||||
|
#define CT_SD1 0x02 /* SD ver 1 */
|
||||||
|
#define CT_SD2 0x04 /* SD ver 2 */
|
||||||
|
#define CT_SDC (CT_SD1|CT_SD2) /* SD */
|
||||||
|
#define CT_BLOCK 0x08 /* Block addressing */
|
||||||
|
|
||||||
|
#endif //WITH_FILESYSTEM
|
||||||
|
|
||||||
|
|
||||||
|
//#ifdef __cplusplus
|
||||||
|
//}
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,510 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "fat32.h"
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/fcntl.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
#include <cstdio>
|
||||||
|
#include "filesystem/stringpart.h"
|
||||||
|
#include "filesystem/ioctl.h"
|
||||||
|
#include "util/unicode.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
#ifdef WITH_FILESYSTEM
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate between FATFS error codes and POSIX ones
|
||||||
|
* \param ec FATS error code
|
||||||
|
* \return POSIX error code
|
||||||
|
*/
|
||||||
|
static int translateError(int ec)
|
||||||
|
{
|
||||||
|
switch(ec)
|
||||||
|
{
|
||||||
|
case FR_OK:
|
||||||
|
return 0;
|
||||||
|
case FR_NO_FILE:
|
||||||
|
case FR_NO_PATH:
|
||||||
|
return -ENOENT;
|
||||||
|
case FR_DENIED:
|
||||||
|
return -ENOSPC;
|
||||||
|
case FR_EXIST:
|
||||||
|
return -EEXIST;
|
||||||
|
case FR_WRITE_PROTECTED:
|
||||||
|
return -EROFS;
|
||||||
|
case FR_LOCKED:
|
||||||
|
return -EBUSY;
|
||||||
|
case FR_NOT_ENOUGH_CORE:
|
||||||
|
return -ENOMEM;
|
||||||
|
case FR_TOO_MANY_OPEN_FILES:
|
||||||
|
return -ENFILE;
|
||||||
|
default:
|
||||||
|
return -EACCES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directory class for Fat32Fs
|
||||||
|
*/
|
||||||
|
class Fat32Directory : public DirectoryBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \param parent parent filesystem
|
||||||
|
* \param mutex mutex to lock when accessing the fiesystem
|
||||||
|
* \param currentInode inode value for '.' entry
|
||||||
|
* \param parentInode inode value for '..' entry
|
||||||
|
*/
|
||||||
|
Fat32Directory(intrusive_ref_ptr<FilesystemBase> parent, FastMutex& mutex,
|
||||||
|
int currentInode, int parentInode) : DirectoryBase(parent),
|
||||||
|
mutex(mutex), currentInode(currentInode), parentInode(parentInode),
|
||||||
|
first(true), unfinished(false)
|
||||||
|
{
|
||||||
|
//Make sure a closedir of an uninitialized dir won't do any damage
|
||||||
|
dir.fs=0;
|
||||||
|
fi.lfname=lfn;
|
||||||
|
fi.lfsize=sizeof(lfn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return the underlying directory object
|
||||||
|
*/
|
||||||
|
DIR_ *directory() { return &dir; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Also directories can be opened as files. In this case, this system call
|
||||||
|
* allows to retrieve directory entries.
|
||||||
|
* \param dp pointer to a memory buffer where one or more struct dirent
|
||||||
|
* will be placed. dp must be four words aligned.
|
||||||
|
* \param len memory buffer size.
|
||||||
|
* \return the number of bytes read on success, or a negative number on
|
||||||
|
* failure.
|
||||||
|
*/
|
||||||
|
virtual int getdents(void *dp, int len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor
|
||||||
|
*/
|
||||||
|
virtual ~Fat32Directory();
|
||||||
|
|
||||||
|
private:
|
||||||
|
FastMutex& mutex; ///< Parent filesystem's mutex
|
||||||
|
DIR_ dir; ///< Directory object
|
||||||
|
FILINFO fi; ///< Information on a file
|
||||||
|
int currentInode; ///< Inode of '.'
|
||||||
|
int parentInode; ///< Inode of '..'
|
||||||
|
bool first; ///< To display '.' and '..' entries
|
||||||
|
bool unfinished; ///< True if fi contains unread data
|
||||||
|
char lfn[(_MAX_LFN+1)*2]; ///< Long file name
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// class Fat32Directory
|
||||||
|
//
|
||||||
|
|
||||||
|
int Fat32Directory::getdents(void *dp, int len)
|
||||||
|
{
|
||||||
|
if(len<minimumBufferSize) return -EINVAL;
|
||||||
|
char *begin=reinterpret_cast<char*>(dp);
|
||||||
|
char *buffer=begin;
|
||||||
|
char *end=buffer+len;
|
||||||
|
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(first)
|
||||||
|
{
|
||||||
|
first=false;
|
||||||
|
addDefaultEntries(&buffer,currentInode,parentInode);
|
||||||
|
}
|
||||||
|
if(unfinished)
|
||||||
|
{
|
||||||
|
unfinished=false;
|
||||||
|
char type=fi.fattrib & AM_DIR ? DT_DIR : DT_REG;
|
||||||
|
StringPart name(fi.lfname);
|
||||||
|
if(addEntry(&buffer,end,fi.inode,type,name)<0) return -EINVAL;
|
||||||
|
}
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
if(int res=translateError(f_readdir(&dir,&fi))) return res;
|
||||||
|
if(fi.fname[0]=='\0')
|
||||||
|
{
|
||||||
|
addTerminatingEntry(&buffer,end);
|
||||||
|
return buffer-begin;
|
||||||
|
}
|
||||||
|
char type=fi.fattrib & AM_DIR ? DT_DIR : DT_REG;
|
||||||
|
StringPart name(fi.lfname);
|
||||||
|
if(addEntry(&buffer,end,fi.inode,type,name)<0)
|
||||||
|
{
|
||||||
|
unfinished=true;
|
||||||
|
return buffer-begin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Fat32Directory::~Fat32Directory()
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
f_closedir(&dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Files of the Fat32Fs filesystem
|
||||||
|
*/
|
||||||
|
class Fat32File : public FileBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* \param parent the filesystem to which this file belongs
|
||||||
|
*/
|
||||||
|
Fat32File(intrusive_ref_ptr<FilesystemBase> parent, FastMutex& mutex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data to the file, if the file supports writing.
|
||||||
|
* \param data the data to write
|
||||||
|
* \param len the number of bytes to write
|
||||||
|
* \return the number of written characters, or a negative number in case
|
||||||
|
* of errors
|
||||||
|
*/
|
||||||
|
virtual ssize_t write(const void *data, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read data from the file, if the file supports reading.
|
||||||
|
* \param data buffer to store read data
|
||||||
|
* \param len the number of bytes to read
|
||||||
|
* \return the number of read characters, or a negative number in case
|
||||||
|
* of errors
|
||||||
|
*/
|
||||||
|
virtual ssize_t read(void *data, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move file pointer, if the file supports random-access.
|
||||||
|
* \param pos offset to sum to the beginning of the file, current position
|
||||||
|
* or end of file, depending on whence
|
||||||
|
* \param whence SEEK_SET, SEEK_CUR or SEEK_END
|
||||||
|
* \return the offset from the beginning of the file if the operation
|
||||||
|
* completed, or a negative number in case of errors
|
||||||
|
*/
|
||||||
|
virtual off_t lseek(off_t pos, int whence);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return file information.
|
||||||
|
* \param pstat pointer to stat struct
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int fstat(struct stat *pstat) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform various operations on a file descriptor
|
||||||
|
* \param cmd specifies the operation to perform
|
||||||
|
* \param arg optional argument that some operation require
|
||||||
|
* \return the exact return value depends on CMD, -1 is returned on error
|
||||||
|
*/
|
||||||
|
virtual int ioctl(int cmd, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return the FatFs FIL object
|
||||||
|
*/
|
||||||
|
FIL *fil() { return &file; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \param inode file inode
|
||||||
|
*/
|
||||||
|
void setInode(int inode) { this->inode=inode; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor
|
||||||
|
*/
|
||||||
|
~Fat32File();
|
||||||
|
|
||||||
|
private:
|
||||||
|
FIL file;
|
||||||
|
FastMutex& mutex;
|
||||||
|
int inode;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// class Fat32File
|
||||||
|
//
|
||||||
|
|
||||||
|
Fat32File::Fat32File(intrusive_ref_ptr<FilesystemBase> parent, FastMutex& mutex)
|
||||||
|
: FileBase(parent), mutex(mutex), inode(0) {}
|
||||||
|
|
||||||
|
ssize_t Fat32File::write(const void *data, size_t len)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
unsigned int bytesWritten;
|
||||||
|
if(int res=translateError(f_write(&file,data,len,&bytesWritten))) return res;
|
||||||
|
#ifdef SYNC_AFTER_WRITE
|
||||||
|
if(f_sync(&file)!=FR_OK) return -EIO;
|
||||||
|
#endif //SYNC_AFTER_WRITE
|
||||||
|
return static_cast<int>(bytesWritten);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t Fat32File::read(void *data, size_t len)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
unsigned int bytesRead;
|
||||||
|
if(int res=translateError(f_read(&file,data,len,&bytesRead))) return res;
|
||||||
|
return static_cast<int>(bytesRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
off_t Fat32File::lseek(off_t pos, int whence)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
off_t offset;
|
||||||
|
switch(whence)
|
||||||
|
{
|
||||||
|
case SEEK_CUR:
|
||||||
|
offset=static_cast<off_t>(f_tell(&file))+pos;
|
||||||
|
break;
|
||||||
|
case SEEK_SET:
|
||||||
|
offset=pos;
|
||||||
|
break;
|
||||||
|
case SEEK_END:
|
||||||
|
offset=static_cast<off_t>(f_size(&file))+pos;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
//We don't support seek past EOF for Fat32
|
||||||
|
if(offset<0 || offset>static_cast<off_t>(f_size(&file))) return -EOVERFLOW;
|
||||||
|
if(int result=translateError(
|
||||||
|
f_lseek(&file,static_cast<unsigned long>(offset)))) return result;
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Fat32File::fstat(struct stat *pstat) const
|
||||||
|
{
|
||||||
|
memset(pstat,0,sizeof(struct stat));
|
||||||
|
pstat->st_dev=getParent()->getFsId();
|
||||||
|
pstat->st_ino=inode;
|
||||||
|
pstat->st_mode=S_IFREG | 0755; //-rwxr-xr-x
|
||||||
|
pstat->st_nlink=1;
|
||||||
|
pstat->st_size=f_size(&file);
|
||||||
|
pstat->st_blksize=512;
|
||||||
|
pstat->st_blocks=(static_cast<off_t>(f_size(&file))+511)/512;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Fat32File::ioctl(int cmd, void *arg)
|
||||||
|
{
|
||||||
|
if(cmd!=IOCTL_SYNC) return -ENOTTY;
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
return translateError(f_sync(&file));
|
||||||
|
}
|
||||||
|
|
||||||
|
Fat32File::~Fat32File()
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(inode) f_close(&file); //TODO: what to do with error code?
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// class Fat32Fs
|
||||||
|
//
|
||||||
|
|
||||||
|
Fat32Fs::Fat32Fs(intrusive_ref_ptr<FileBase> disk)
|
||||||
|
: mutex(FastMutex::RECURSIVE), failed(true)
|
||||||
|
{
|
||||||
|
filesystem.drv=disk;
|
||||||
|
failed=f_mount(&filesystem,1,false)!=FR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Fat32Fs::open(intrusive_ref_ptr<FileBase>& file, StringPart& name,
|
||||||
|
int flags, int mode)
|
||||||
|
{
|
||||||
|
if(failed) return -ENOENT;
|
||||||
|
flags++; //To convert from O_RDONLY, O_WRONLY, ... to _FREAD, _FWRITE, ...
|
||||||
|
|
||||||
|
// Code path checklist:
|
||||||
|
// Not existent | Regular file | Directory |
|
||||||
|
// ok | ok | ok | _FREAD
|
||||||
|
// ok | ok | ok | _FWRITE
|
||||||
|
// ok | ok | ok | _FWRITE | _FCREAT
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
bool statFailed=false;
|
||||||
|
if(int result=lstat(name,&st))
|
||||||
|
{
|
||||||
|
//If _FCREAT the file may not yet exist as we are asked to create it
|
||||||
|
if((flags & (_FWRITE | _FCREAT)) != (_FWRITE | _FCREAT)) return result;
|
||||||
|
else statFailed=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Using if short circuit, as st is not initialized if statFailed
|
||||||
|
if(statFailed || !S_ISDIR(st.st_mode))
|
||||||
|
{
|
||||||
|
//About to open a file
|
||||||
|
BYTE openflags=0;
|
||||||
|
if(flags & _FREAD) openflags|=FA_READ;
|
||||||
|
if(flags & _FWRITE) openflags|=FA_WRITE;
|
||||||
|
if(flags & _FTRUNC) openflags|=FA_CREATE_ALWAYS;//Truncate
|
||||||
|
else if(flags & _FCREAT) openflags|=FA_OPEN_ALWAYS;//If !exists create
|
||||||
|
else openflags|=FA_OPEN_EXISTING;//If not exists fail
|
||||||
|
|
||||||
|
intrusive_ref_ptr<Fat32File> f(new Fat32File(shared_from_this(),mutex));
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(int res=translateError(f_open(&filesystem,f->fil(),name.c_str(),openflags)))
|
||||||
|
return res;
|
||||||
|
if(statFailed)
|
||||||
|
{
|
||||||
|
//If we didn't stat before, stat now to get the inode
|
||||||
|
if(int result=lstat(name,&st)) return result;
|
||||||
|
}
|
||||||
|
f->setInode(st.st_ino);
|
||||||
|
|
||||||
|
//Can't open files larger than INT_MAX
|
||||||
|
if(static_cast<int>(f_size(f->fil()))<0) return -EOVERFLOW;
|
||||||
|
|
||||||
|
#ifdef SYNC_AFTER_WRITE
|
||||||
|
if(f_sync(f->fil())!=FR_OK) return -EFAULT;
|
||||||
|
#endif //SYNC_AFTER_WRITE
|
||||||
|
|
||||||
|
//If file opened for appending, seek to end of file
|
||||||
|
if(flags & _FAPPEND)
|
||||||
|
if(f_lseek(f->fil(),f_size(f->fil()))!=FR_OK) return -EFAULT;
|
||||||
|
|
||||||
|
file=f;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
//About to open a directory
|
||||||
|
if(flags & (_FWRITE | _FAPPEND | _FCREAT | _FTRUNC)) return -EISDIR;
|
||||||
|
|
||||||
|
int parentInode;
|
||||||
|
if(name.empty()==false)
|
||||||
|
{
|
||||||
|
unsigned int lastSlash=name.findLastOf('/');
|
||||||
|
if(lastSlash!=string::npos)
|
||||||
|
{
|
||||||
|
StringPart parent(name,lastSlash);
|
||||||
|
struct stat st2;
|
||||||
|
if(int result=lstat(parent,&st2)) return result;
|
||||||
|
parentInode=st2.st_ino;
|
||||||
|
} else parentInode=1; //Asked to list subdir of root
|
||||||
|
} else parentInode=parentFsMountpointInode; //Asked to list root dir
|
||||||
|
|
||||||
|
|
||||||
|
intrusive_ref_ptr<Fat32Directory> d(
|
||||||
|
new Fat32Directory(shared_from_this(),mutex,st.st_ino,parentInode));
|
||||||
|
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(int res=translateError(f_opendir(&filesystem,d->directory(),name.c_str())))
|
||||||
|
return res;
|
||||||
|
|
||||||
|
file=d;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Fat32Fs::lstat(StringPart& name, struct stat *pstat)
|
||||||
|
{
|
||||||
|
if(failed) return -ENOENT;
|
||||||
|
memset(pstat,0,sizeof(struct stat));
|
||||||
|
pstat->st_dev=filesystemId;
|
||||||
|
pstat->st_nlink=1;
|
||||||
|
pstat->st_blksize=512;
|
||||||
|
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(name.empty())
|
||||||
|
{
|
||||||
|
//We are asked to stat the filesystem's root directory
|
||||||
|
//By convention, we use 1 for root dir inode, see INODE() macro in ff.c
|
||||||
|
pstat->st_ino=1;
|
||||||
|
pstat->st_mode=S_IFDIR | 0755; //drwxr-xr-x
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
FILINFO info;
|
||||||
|
info.lfname=0; //We're not interested in getting the lfname
|
||||||
|
info.lfsize=0;
|
||||||
|
if(int result=translateError(f_stat(&filesystem,name.c_str(),&info))) return result;
|
||||||
|
|
||||||
|
pstat->st_ino=info.inode;
|
||||||
|
pstat->st_mode=(info.fattrib & AM_DIR) ?
|
||||||
|
S_IFDIR | 0755 //drwxr-xr-x
|
||||||
|
: S_IFREG | 0755; //-rwxr-xr-x
|
||||||
|
pstat->st_size=info.fsize;
|
||||||
|
pstat->st_blocks=(info.fsize+511)/512;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Fat32Fs::unlink(StringPart& name)
|
||||||
|
{
|
||||||
|
return unlinkRmdirHelper(name,false);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Fat32Fs::rename(StringPart& oldName, StringPart& newName)
|
||||||
|
{
|
||||||
|
if(failed) return -ENOENT;
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
return translateError(f_rename(&filesystem,oldName.c_str(),newName.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
int Fat32Fs::mkdir(StringPart& name, int mode)
|
||||||
|
{
|
||||||
|
if(failed) return -ENOENT;
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
return translateError(f_mkdir(&filesystem,name.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
int Fat32Fs::rmdir(StringPart& name)
|
||||||
|
{
|
||||||
|
return unlinkRmdirHelper(name,true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Fat32Fs::~Fat32Fs()
|
||||||
|
{
|
||||||
|
if(failed) return;
|
||||||
|
f_mount(&filesystem,0,true); //TODO: what to do with error code?
|
||||||
|
filesystem.drv->ioctl(IOCTL_SYNC,0);
|
||||||
|
filesystem.drv.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Fat32Fs::unlinkRmdirHelper(StringPart& name, bool delDir)
|
||||||
|
{
|
||||||
|
if(failed) return -ENOENT;
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
struct stat st;
|
||||||
|
if(int result=lstat(name,&st)) return result;
|
||||||
|
if(delDir)
|
||||||
|
{
|
||||||
|
if(!S_ISDIR(st.st_mode)) return -ENOTDIR;
|
||||||
|
} else if(S_ISDIR(st.st_mode)) return -EISDIR;
|
||||||
|
return translateError(f_unlink(&filesystem,name.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //WITH_FILESYSTEM
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef FAT32_H
|
||||||
|
#define FAT32_H
|
||||||
|
|
||||||
|
#include "filesystem/file.h"
|
||||||
|
#include "kernel/sync.h"
|
||||||
|
#include "ff.h"
|
||||||
|
#include "config/miosix_settings.h"
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
#ifdef WITH_FILESYSTEM
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fat32 Filesystem.
|
||||||
|
*/
|
||||||
|
class Fat32Fs : public FilesystemBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
Fat32Fs(intrusive_ref_ptr<FileBase> disk);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a file
|
||||||
|
* \param file the file object will be stored here, if the call succeeds
|
||||||
|
* \param name the name of the file to open, relative to the local
|
||||||
|
* filesystem
|
||||||
|
* \param flags file flags (open for reading, writing, ...)
|
||||||
|
* \param mode file permissions
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int open(intrusive_ref_ptr<FileBase>& file, StringPart& name,
|
||||||
|
int flags, int mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain information on a file, identified by a path name. Does not follow
|
||||||
|
* symlinks
|
||||||
|
* \param name path name, relative to the local filesystem
|
||||||
|
* \param pstat file information is stored here
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int lstat(StringPart& name, struct stat *pstat);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a file or directory
|
||||||
|
* \param name path name of file or directory to remove
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int unlink(StringPart& name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename a file or directory
|
||||||
|
* \param oldName old file name
|
||||||
|
* \param newName new file name
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int rename(StringPart& oldName, StringPart& newName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a directory
|
||||||
|
* \param name directory name
|
||||||
|
* \param mode directory permissions
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int mkdir(StringPart& name, int mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a directory if empty
|
||||||
|
* \param name directory name
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int rmdir(StringPart& name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return true if the filesystem failed to mount
|
||||||
|
*/
|
||||||
|
bool mountFailed() const { return failed; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor
|
||||||
|
*/
|
||||||
|
~Fat32Fs();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
int unlinkRmdirHelper(StringPart& name, bool delDir);
|
||||||
|
|
||||||
|
FATFS filesystem;
|
||||||
|
FastMutex mutex;
|
||||||
|
bool failed; ///< Failed to mount
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //WITH_FILESYSTEM
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //FAT32_H
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,385 @@
|
||||||
|
/*---------------------------------------------------------------------------/
|
||||||
|
/ FatFs - FAT file system module include file R0.10 (C)ChaN, 2013
|
||||||
|
/----------------------------------------------------------------------------/
|
||||||
|
/ FatFs module is a generic FAT file system module for small embedded systems.
|
||||||
|
/ This is a free software that opened for education, research and commercial
|
||||||
|
/ developments under license policy of following terms.
|
||||||
|
/
|
||||||
|
/ Copyright (C) 2013, ChaN, all right reserved.
|
||||||
|
/
|
||||||
|
/ * The FatFs module is a free software and there is NO WARRANTY.
|
||||||
|
/ * No restriction on use. You can use, modify and redistribute it for
|
||||||
|
/ personal, non-profit or commercial product UNDER YOUR RESPONSIBILITY.
|
||||||
|
/ * Redistributions of source code must retain the above copyright notice.
|
||||||
|
/
|
||||||
|
/----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This version of FatFs has been modified to adapt it to the requirements of
|
||||||
|
* Miosix:
|
||||||
|
* - C++: moved from C to C++ to allow calling other Miosix code
|
||||||
|
* - utf8: the original FatFs API supported only utf16 for file names, but the
|
||||||
|
* Miosix filesystem API has to be utf8 (aka, according with the
|
||||||
|
* "utf8 everywhere mainfesto", doesn't want to deal with that crap).
|
||||||
|
* For efficiency reasons the unicode conversion is done inside the FatFs code
|
||||||
|
* - removal of global variables: to allow to create an arbitrary number of
|
||||||
|
* independent Fat32 filesystems
|
||||||
|
* - unixification: removal of the dos-like drive numbering scheme and
|
||||||
|
* addition of an inode field in the FILINFO struct
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _FATFS
|
||||||
|
#define _FATFS 80960 /* Revision ID */
|
||||||
|
|
||||||
|
//#ifdef __cplusplus
|
||||||
|
//extern "C" {
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
#include <filesystem/file.h>
|
||||||
|
#include "config/miosix_settings.h"
|
||||||
|
|
||||||
|
#include "integer.h" /* Basic integer types */
|
||||||
|
#include "ffconf.h" /* FatFs configuration options */
|
||||||
|
|
||||||
|
#if _FATFS != _FFCONF
|
||||||
|
#error Wrong configuration file (ffconf.h).
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_FILESYSTEM
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Definitions of volume management */
|
||||||
|
|
||||||
|
#if _MULTI_PARTITION /* Multiple partition configuration */
|
||||||
|
typedef struct {
|
||||||
|
BYTE pd; /* Physical drive number */
|
||||||
|
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||||
|
} PARTITION;
|
||||||
|
extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
|
||||||
|
#define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive number */
|
||||||
|
#define LD2PT(vol) (VolToPart[vol].pt) /* Get partition index */
|
||||||
|
|
||||||
|
#else /* Single partition configuration */
|
||||||
|
#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */
|
||||||
|
#define LD2PT(vol) 0 /* Find first valid partition or in SFD */
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Type of path name strings on FatFs API */
|
||||||
|
|
||||||
|
#if _LFN_UNICODE /* Unicode string */
|
||||||
|
#if !_USE_LFN
|
||||||
|
#error _LFN_UNICODE must be 0 in non-LFN cfg.
|
||||||
|
#endif
|
||||||
|
#ifndef _INC_TCHAR
|
||||||
|
typedef WCHAR TCHAR;
|
||||||
|
#define _T(x) L ## x
|
||||||
|
#define _TEXT(x) L ## x
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else /* ANSI/OEM string */
|
||||||
|
#ifndef _INC_TCHAR
|
||||||
|
typedef char TCHAR;
|
||||||
|
#define _T(x) x
|
||||||
|
#define _TEXT(x) x
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* File access control feature */
|
||||||
|
#if _FS_LOCK
|
||||||
|
#if _FS_READONLY
|
||||||
|
#error _FS_LOCK must be 0 at read-only cfg.
|
||||||
|
#endif
|
||||||
|
struct FATFS; //Forward decl
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */
|
||||||
|
DWORD clu; /* Object ID 2, directory */
|
||||||
|
WORD idx; /* Object ID 3, directory index */
|
||||||
|
WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */
|
||||||
|
} FILESEM;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* File system object structure (FATFS) */
|
||||||
|
|
||||||
|
struct FATFS {
|
||||||
|
BYTE fs_type; /* FAT sub-type (0:Not mounted) */
|
||||||
|
//BYTE drv; /* Physical drive number */
|
||||||
|
BYTE csize; /* Sectors per cluster (1,2,4...128) */
|
||||||
|
BYTE n_fats; /* Number of FAT copies (1 or 2) */
|
||||||
|
BYTE wflag; /* win[] flag (b0:dirty) */
|
||||||
|
BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */
|
||||||
|
WORD id; /* File system mount ID */
|
||||||
|
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
|
||||||
|
#if _MAX_SS != 512
|
||||||
|
WORD ssize; /* Bytes per sector (512, 1024, 2048 or 4096) */
|
||||||
|
#endif
|
||||||
|
#if _FS_REENTRANT
|
||||||
|
_SYNC_t sobj; /* Identifier of sync object */
|
||||||
|
#endif
|
||||||
|
#if !_FS_READONLY
|
||||||
|
DWORD last_clust; /* Last allocated cluster */
|
||||||
|
DWORD free_clust; /* Number of free clusters */
|
||||||
|
#endif
|
||||||
|
#if _FS_RPATH
|
||||||
|
DWORD cdir; /* Current directory start cluster (0:root) */
|
||||||
|
#endif
|
||||||
|
DWORD n_fatent; /* Number of FAT entries (= number of clusters + 2) */
|
||||||
|
DWORD fsize; /* Sectors per FAT */
|
||||||
|
DWORD volbase; /* Volume start sector */
|
||||||
|
DWORD fatbase; /* FAT start sector */
|
||||||
|
DWORD dirbase; /* Root directory start sector (FAT32:Cluster#) */
|
||||||
|
DWORD database; /* Data start sector */
|
||||||
|
DWORD winsect; /* Current sector appearing in the win[] */
|
||||||
|
BYTE win[_MAX_SS] __attribute__((aligned(4))); /* Disk access window for Directory, FAT (and file data at tiny cfg) */
|
||||||
|
#if _USE_LFN == 1
|
||||||
|
WCHAR LfnBuf[_MAX_LFN+1];
|
||||||
|
#endif
|
||||||
|
#if _FS_LOCK
|
||||||
|
FILESEM Files[_FS_LOCK];/* Open object lock semaphores */
|
||||||
|
#endif
|
||||||
|
miosix::intrusive_ref_ptr<miosix::FileBase> drv; /* drive device */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* File object structure (FIL) */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FATFS* fs; /* Pointer to the related file system object (**do not change order**) */
|
||||||
|
WORD id; /* Owner file system mount ID (**do not change order**) */
|
||||||
|
BYTE flag; /* File status flags */
|
||||||
|
BYTE err; /* Abort flag (error code) */
|
||||||
|
DWORD fptr; /* File read/write pointer (Zeroed on file open) */
|
||||||
|
DWORD fsize; /* File size */
|
||||||
|
DWORD sclust; /* File data start cluster (0:no data cluster, always 0 when fsize is 0) */
|
||||||
|
DWORD clust; /* Current cluster of fpter */
|
||||||
|
DWORD dsect; /* Current data sector of fpter */
|
||||||
|
#if !_FS_READONLY
|
||||||
|
DWORD dir_sect; /* Sector containing the directory entry */
|
||||||
|
BYTE* dir_ptr; /* Pointer to the directory entry in the window */
|
||||||
|
#endif
|
||||||
|
#if _USE_FASTSEEK
|
||||||
|
DWORD* cltbl; /* Pointer to the cluster link map table (Nulled on file open) */
|
||||||
|
#endif
|
||||||
|
#if _FS_LOCK
|
||||||
|
UINT lockid; /* File lock ID (index of file semaphore table Files[]) */
|
||||||
|
#endif
|
||||||
|
#if !_FS_TINY
|
||||||
|
BYTE buf[_MAX_SS]; /* File data read/write buffer */
|
||||||
|
#endif
|
||||||
|
} FIL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Directory object structure (DIR) */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FATFS* fs; /* Pointer to the owner file system object (**do not change order**) */
|
||||||
|
WORD id; /* Owner file system mount ID (**do not change order**) */
|
||||||
|
WORD index; /* Current read/write index number */
|
||||||
|
DWORD sclust; /* Table start cluster (0:Root dir) */
|
||||||
|
DWORD clust; /* Current cluster */
|
||||||
|
DWORD sect; /* Current sector */
|
||||||
|
BYTE* dir; /* Pointer to the current SFN entry in the win[] */
|
||||||
|
BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */
|
||||||
|
#if _FS_LOCK
|
||||||
|
UINT lockid; /* File lock ID (index of file semaphore table Files[]) */
|
||||||
|
#endif
|
||||||
|
#if _USE_LFN
|
||||||
|
WCHAR* lfn; /* Pointer to the LFN working buffer */
|
||||||
|
WORD lfn_idx; /* Last matched LFN index number (0xFFFF:No LFN) */
|
||||||
|
#endif
|
||||||
|
} DIR_;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* File status structure (FILINFO) */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
DWORD fsize; /* File size */
|
||||||
|
WORD fdate; /* Last modified date */
|
||||||
|
WORD ftime; /* Last modified time */
|
||||||
|
BYTE fattrib; /* Attribute */
|
||||||
|
TCHAR fname[13]; /* Short file name (8.3 format) */
|
||||||
|
#if _USE_LFN
|
||||||
|
/*TCHAR*/char *lfname; /* Pointer to the LFN buffer */
|
||||||
|
UINT lfsize; /* Size of LFN buffer in TCHAR */
|
||||||
|
#endif
|
||||||
|
unsigned int inode; //By TFT: support inodes
|
||||||
|
} FILINFO;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* File function return code (FRESULT) */
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FR_OK = 0, /* (0) Succeeded */
|
||||||
|
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
|
||||||
|
FR_INT_ERR, /* (2) Assertion failed */
|
||||||
|
FR_NOT_READY, /* (3) The physical drive cannot work */
|
||||||
|
FR_NO_FILE, /* (4) Could not find the file */
|
||||||
|
FR_NO_PATH, /* (5) Could not find the path */
|
||||||
|
FR_INVALID_NAME, /* (6) The path name format is invalid */
|
||||||
|
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
|
||||||
|
FR_EXIST, /* (8) Access denied due to prohibited access */
|
||||||
|
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
|
||||||
|
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
|
||||||
|
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
|
||||||
|
FR_NOT_ENABLED, /* (12) The volume has no work area */
|
||||||
|
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
|
||||||
|
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any parameter error */
|
||||||
|
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
|
||||||
|
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
|
||||||
|
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
|
||||||
|
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > _FS_SHARE */
|
||||||
|
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
|
||||||
|
} FRESULT;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------*/
|
||||||
|
/* FatFs module application interface */
|
||||||
|
|
||||||
|
FRESULT f_open (FATFS *fs, FIL* fp, const /*TCHAR*/char *path, BYTE mode); /* Open or create a file */
|
||||||
|
FRESULT f_close (FIL* fp); /* Close an open file object */
|
||||||
|
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from a file */
|
||||||
|
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to a file */
|
||||||
|
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
|
||||||
|
FRESULT f_lseek (FIL* fp, DWORD ofs); /* Move file pointer of a file object */
|
||||||
|
FRESULT f_truncate (FIL* fp); /* Truncate file */
|
||||||
|
FRESULT f_sync (FIL* fp); /* Flush cached data of a writing file */
|
||||||
|
FRESULT f_opendir (FATFS *fs, DIR_* dp, const /*TCHAR*/char *path); /* Open a directory */
|
||||||
|
FRESULT f_closedir (DIR_* dp); /* Close an open directory */
|
||||||
|
FRESULT f_readdir (DIR_* dp, FILINFO* fno); /* Read a directory item */
|
||||||
|
FRESULT f_mkdir (FATFS *fs, const /*TCHAR*/char *path); /* Create a sub directory */
|
||||||
|
FRESULT f_unlink (FATFS *fs, const /*TCHAR*/char *path); /* Delete an existing file or directory */
|
||||||
|
FRESULT f_rename (FATFS *fs, const /*TCHAR*/char *path_old, const /*TCHAR*/char *path_new); /* Rename/Move a file or directory */
|
||||||
|
FRESULT f_stat (FATFS *fs, const /*TCHAR*/char *path, FILINFO* fno); /* Get file status */
|
||||||
|
FRESULT f_chmod (FATFS *fs, const /*TCHAR*/char *path, BYTE value, BYTE mask); /* Change attribute of the file/dir */
|
||||||
|
FRESULT f_utime (FATFS *fs, const /*TCHAR*/char *path, const FILINFO* fno); /* Change times-tamp of the file/dir */
|
||||||
|
FRESULT f_chdir (FATFS *fs, const TCHAR* path); /* Change current directory */
|
||||||
|
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
|
||||||
|
FRESULT f_getcwd (FATFS *fs, TCHAR* buff, UINT len); /* Get current directory */
|
||||||
|
FRESULT f_getfree (FATFS *fs, /*const TCHAR* path,*/ DWORD* nclst/*, FATFS** fatfs*/); /* Get number of free clusters on the drive */
|
||||||
|
FRESULT f_getlabel (FATFS *fs, const TCHAR* path, TCHAR* label, DWORD* sn); /* Get volume label */
|
||||||
|
FRESULT f_setlabel (FATFS *fs, const TCHAR* label); /* Set volume label */
|
||||||
|
FRESULT f_mount (FATFS* fs, /*const TCHAR* path,*/ BYTE opt, bool umount); /* Mount/Unmount a logical drive */
|
||||||
|
FRESULT f_mkfs (const TCHAR* path, BYTE sfd, UINT au); /* Create a file system on the volume */
|
||||||
|
FRESULT f_fdisk (BYTE pdrv, const DWORD szt[], void* work); /* Divide a physical drive into some partitions */
|
||||||
|
int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
|
||||||
|
int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
|
||||||
|
int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
|
||||||
|
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
|
||||||
|
|
||||||
|
#define f_eof(fp) (((fp)->fptr == (fp)->fsize) ? 1 : 0)
|
||||||
|
#define f_error(fp) ((fp)->err)
|
||||||
|
#define f_tell(fp) ((fp)->fptr)
|
||||||
|
#define f_size(fp) ((fp)->fsize)
|
||||||
|
|
||||||
|
#ifndef EOF
|
||||||
|
#define EOF (-1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------*/
|
||||||
|
/* Additional user defined functions */
|
||||||
|
|
||||||
|
/* RTC function */
|
||||||
|
#if !_FS_READONLY
|
||||||
|
DWORD get_fattime (void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Unicode support functions */
|
||||||
|
#if _USE_LFN /* Unicode - OEM code conversion */
|
||||||
|
WCHAR ff_convert (WCHAR chr, UINT dir); /* OEM-Unicode bidirectional conversion */
|
||||||
|
WCHAR ff_wtoupper (WCHAR chr); /* Unicode upper-case conversion */
|
||||||
|
#if _USE_LFN == 3 /* Memory functions */
|
||||||
|
void* ff_memalloc (UINT msize); /* Allocate memory block */
|
||||||
|
void ff_memfree (void* mblock); /* Free memory block */
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Sync functions */
|
||||||
|
#if _FS_REENTRANT
|
||||||
|
int ff_cre_syncobj (BYTE vol, _SYNC_t* sobj); /* Create a sync object */
|
||||||
|
int ff_req_grant (_SYNC_t sobj); /* Lock sync object */
|
||||||
|
void ff_rel_grant (_SYNC_t sobj); /* Unlock sync object */
|
||||||
|
int ff_del_syncobj (_SYNC_t sobj); /* Delete a sync object */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------*/
|
||||||
|
/* Flags and offset address */
|
||||||
|
|
||||||
|
|
||||||
|
/* File access control and file status flags (FIL.flag) */
|
||||||
|
|
||||||
|
#define FA_READ 0x01
|
||||||
|
#define FA_OPEN_EXISTING 0x00
|
||||||
|
|
||||||
|
#if !_FS_READONLY
|
||||||
|
#define FA_WRITE 0x02
|
||||||
|
#define FA_CREATE_NEW 0x04
|
||||||
|
#define FA_CREATE_ALWAYS 0x08
|
||||||
|
#define FA_OPEN_ALWAYS 0x10
|
||||||
|
#define FA__WRITTEN 0x20
|
||||||
|
#define FA__DIRTY 0x40
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* FAT sub type (FATFS.fs_type) */
|
||||||
|
|
||||||
|
#define FS_FAT12 1
|
||||||
|
#define FS_FAT16 2
|
||||||
|
#define FS_FAT32 3
|
||||||
|
|
||||||
|
|
||||||
|
/* File attribute bits for directory entry */
|
||||||
|
|
||||||
|
#define AM_RDO 0x01 /* Read only */
|
||||||
|
#define AM_HID 0x02 /* Hidden */
|
||||||
|
#define AM_SYS 0x04 /* System */
|
||||||
|
#define AM_VOL 0x08 /* Volume label */
|
||||||
|
#define AM_LFN 0x0F /* LFN entry */
|
||||||
|
#define AM_DIR 0x10 /* Directory */
|
||||||
|
#define AM_ARC 0x20 /* Archive */
|
||||||
|
#define AM_MASK 0x3F /* Mask of defined bits */
|
||||||
|
|
||||||
|
|
||||||
|
/* Fast seek feature */
|
||||||
|
#define CREATE_LINKMAP 0xFFFFFFFF
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------*/
|
||||||
|
/* Multi-byte word access macros */
|
||||||
|
|
||||||
|
#if _WORD_ACCESS == 1 /* Enable word access to the FAT structure */
|
||||||
|
#define LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr))
|
||||||
|
#define LD_DWORD(ptr) (DWORD)(*(DWORD*)(BYTE*)(ptr))
|
||||||
|
#define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(WORD)(val)
|
||||||
|
#define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(DWORD)(val)
|
||||||
|
#else /* Use byte-by-byte access to the FAT structure */
|
||||||
|
#define LD_WORD(ptr) (WORD)(((WORD)*((BYTE*)(ptr)+1)<<8)|(WORD)*(BYTE*)(ptr))
|
||||||
|
#define LD_DWORD(ptr) (DWORD)(((DWORD)*((BYTE*)(ptr)+3)<<24)|((DWORD)*((BYTE*)(ptr)+2)<<16)|((WORD)*((BYTE*)(ptr)+1)<<8)|*(BYTE*)(ptr))
|
||||||
|
#define ST_WORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8)
|
||||||
|
#define ST_DWORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8); *((BYTE*)(ptr)+2)=(BYTE)((DWORD)(val)>>16); *((BYTE*)(ptr)+3)=(BYTE)((DWORD)(val)>>24)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //WITH_FILESYSTEM
|
||||||
|
|
||||||
|
//#ifdef __cplusplus
|
||||||
|
//}
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
#endif /* _FATFS */
|
||||||
|
|
@ -0,0 +1,220 @@
|
||||||
|
/*---------------------------------------------------------------------------/
|
||||||
|
/ FatFs - FAT file system module configuration file R0.10 (C)ChaN, 2013
|
||||||
|
/----------------------------------------------------------------------------/
|
||||||
|
/
|
||||||
|
/ CAUTION! Do not forget to make clean the project after any changes to
|
||||||
|
/ the configuration options.
|
||||||
|
/
|
||||||
|
/----------------------------------------------------------------------------*/
|
||||||
|
#ifndef _FFCONF
|
||||||
|
#define _FFCONF 80960 /* Revision ID */
|
||||||
|
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------/
|
||||||
|
/ Functions and Buffer Configurations
|
||||||
|
/----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#define _FS_TINY 0 /* 0:Normal or 1:Tiny */
|
||||||
|
/* When _FS_TINY is set to 1, FatFs uses the sector buffer in the file system
|
||||||
|
/ object instead of the sector buffer in the individual file object for file
|
||||||
|
/ data transfer. This reduces memory consumption 512 bytes each file object. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _FS_READONLY 0 /* 0:Read/Write or 1:Read only */
|
||||||
|
/* Setting _FS_READONLY to 1 defines read only configuration. This removes
|
||||||
|
/ writing functions, f_write(), f_sync(), f_unlink(), f_mkdir(), f_chmod(),
|
||||||
|
/ f_rename(), f_truncate() and useless f_getfree(). */
|
||||||
|
|
||||||
|
|
||||||
|
#define _FS_MINIMIZE 0 /* 0 to 3 */
|
||||||
|
/* The _FS_MINIMIZE option defines minimization level to remove API functions.
|
||||||
|
/
|
||||||
|
/ 0: All basic functions are enabled.
|
||||||
|
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_chmod(), f_utime(),
|
||||||
|
/ f_truncate() and f_rename() function are removed.
|
||||||
|
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
|
||||||
|
/ 3: f_lseek() function is removed in addition to 2. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _USE_STRFUNC 0 /* 0:Disable or 1-2:Enable */
|
||||||
|
/* To enable string functions, set _USE_STRFUNC to 1 or 2. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _USE_MKFS 0 /* 0:Disable or 1:Enable */
|
||||||
|
/* To enable f_mkfs() function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */
|
||||||
|
|
||||||
|
|
||||||
|
#define _USE_FASTSEEK 0 /* 0:Disable or 1:Enable */
|
||||||
|
/* To enable fast seek feature, set _USE_FASTSEEK to 1. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _USE_LABEL 0 /* 0:Disable or 1:Enable */
|
||||||
|
/* To enable volume label functions, set _USE_LAVEL to 1 */
|
||||||
|
|
||||||
|
|
||||||
|
#define _USE_FORWARD 0 /* 0:Disable or 1:Enable */
|
||||||
|
/* To enable f_forward() function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */
|
||||||
|
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------/
|
||||||
|
/ Locale and Namespace Configurations
|
||||||
|
/----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#define _CODE_PAGE 1252
|
||||||
|
/* The _CODE_PAGE specifies the OEM code page to be used on the target system.
|
||||||
|
/ Incorrect setting of the code page can cause a file open failure.
|
||||||
|
/
|
||||||
|
/ 932 - Japanese Shift-JIS (DBCS, OEM, Windows)
|
||||||
|
/ 936 - Simplified Chinese GBK (DBCS, OEM, Windows)
|
||||||
|
/ 949 - Korean (DBCS, OEM, Windows)
|
||||||
|
/ 950 - Traditional Chinese Big5 (DBCS, OEM, Windows)
|
||||||
|
/ 1250 - Central Europe (Windows)
|
||||||
|
/ 1251 - Cyrillic (Windows)
|
||||||
|
/ 1252 - Latin 1 (Windows)
|
||||||
|
/ 1253 - Greek (Windows)
|
||||||
|
/ 1254 - Turkish (Windows)
|
||||||
|
/ 1255 - Hebrew (Windows)
|
||||||
|
/ 1256 - Arabic (Windows)
|
||||||
|
/ 1257 - Baltic (Windows)
|
||||||
|
/ 1258 - Vietnam (OEM, Windows)
|
||||||
|
/ 437 - U.S. (OEM)
|
||||||
|
/ 720 - Arabic (OEM)
|
||||||
|
/ 737 - Greek (OEM)
|
||||||
|
/ 775 - Baltic (OEM)
|
||||||
|
/ 850 - Multilingual Latin 1 (OEM)
|
||||||
|
/ 858 - Multilingual Latin 1 + Euro (OEM)
|
||||||
|
/ 852 - Latin 2 (OEM)
|
||||||
|
/ 855 - Cyrillic (OEM)
|
||||||
|
/ 866 - Russian (OEM)
|
||||||
|
/ 857 - Turkish (OEM)
|
||||||
|
/ 862 - Hebrew (OEM)
|
||||||
|
/ 874 - Thai (OEM, Windows)
|
||||||
|
/ 1 - ASCII (Valid for only non-LFN cfg.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
//Note by TFT: DEF_NAMEBUF is used in the following functions: f_open(),
|
||||||
|
//f_opendir(), f_readdir(), f_stat(), f_unlink(), f_mkdir(), f_chmod(),
|
||||||
|
//f_utime(), f_rename(), and Miosix always locks a mutex before calling any of,
|
||||||
|
//these. For this reason it was chosen to allocate the LFN buffer statically,
|
||||||
|
//since the mutex protects from concurrent access to the buffer.
|
||||||
|
#define _USE_LFN 1 /* 0 to 3 */
|
||||||
|
#define _MAX_LFN 255 /* Maximum LFN length to handle (12 to 255) */
|
||||||
|
/* The _USE_LFN option switches the LFN feature.
|
||||||
|
/
|
||||||
|
/ 0: Disable LFN feature. _MAX_LFN has no effect.
|
||||||
|
/ 1: Enable LFN with static working buffer on the BSS. Always NOT reentrant.
|
||||||
|
/ 2: Enable LFN with dynamic working buffer on the STACK.
|
||||||
|
/ 3: Enable LFN with dynamic working buffer on the HEAP.
|
||||||
|
/
|
||||||
|
/ To enable LFN feature, Unicode handling functions ff_convert() and ff_wtoupper()
|
||||||
|
/ function must be added to the project.
|
||||||
|
/ The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. When use stack for the
|
||||||
|
/ working buffer, take care on stack overflow. When use heap memory for the working
|
||||||
|
/ buffer, memory management functions, ff_memalloc() and ff_memfree(), must be added
|
||||||
|
/ to the project. */
|
||||||
|
|
||||||
|
//Note by TFT: we do want unicode and not ancient code pages
|
||||||
|
#define _LFN_UNICODE 1 /* 0:ANSI/OEM or 1:Unicode */
|
||||||
|
/* To switch the character encoding on the FatFs API to Unicode, enable LFN feature
|
||||||
|
/ and set _LFN_UNICODE to 1. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _STRF_ENCODE 3 /* 0:ANSI/OEM, 1:UTF-16LE, 2:UTF-16BE, 3:UTF-8 */
|
||||||
|
/* When Unicode API is enabled, character encoding on the all FatFs API is switched
|
||||||
|
/ to Unicode. This option selects the character encoding on the file to be read/written
|
||||||
|
/ via string functions, f_gets(), f_putc(), f_puts and f_printf().
|
||||||
|
/ This option has no effect when _LFN_UNICODE is 0. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _FS_RPATH 0 /* 0 to 2 */
|
||||||
|
/* The _FS_RPATH option configures relative path feature.
|
||||||
|
/
|
||||||
|
/ 0: Disable relative path feature and remove related functions.
|
||||||
|
/ 1: Enable relative path. f_chdrive() and f_chdir() function are available.
|
||||||
|
/ 2: f_getcwd() function is available in addition to 1.
|
||||||
|
/
|
||||||
|
/ Note that output of the f_readdir() fnction is affected by this option. */
|
||||||
|
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------/
|
||||||
|
/ Drive/Volume Configurations
|
||||||
|
/----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#define _VOLUMES 1
|
||||||
|
/* Number of volumes (logical drives) to be used. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _MULTI_PARTITION 0 /* 0:Single partition, 1:Enable multiple partition */
|
||||||
|
/* When set to 0, each volume is bound to the same physical drive number and
|
||||||
|
/ it can mount only first primaly partition. When it is set to 1, each volume
|
||||||
|
/ is tied to the partitions listed in VolToPart[]. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _MAX_SS 512 /* 512, 1024, 2048 or 4096 */
|
||||||
|
/* Maximum sector size to be handled.
|
||||||
|
/ Always set 512 for memory card and hard disk but a larger value may be
|
||||||
|
/ required for on-board flash memory, floppy disk and optical disk.
|
||||||
|
/ When _MAX_SS is larger than 512, it configures FatFs to variable sector size
|
||||||
|
/ and GET_SECTOR_SIZE command must be implemented to the disk_ioctl() function. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _USE_ERASE 0 /* 0:Disable or 1:Enable */
|
||||||
|
/* To enable sector erase feature, set _USE_ERASE to 1. Also CTRL_ERASE_SECTOR command
|
||||||
|
/ should be added to the disk_ioctl() function. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _FS_NOFSINFO 0 /* 0 or 1 */
|
||||||
|
/* If you need to know the correct free space on the FAT32 volume, set this
|
||||||
|
/ option to 1 and f_getfree() function at first time after volume mount will
|
||||||
|
/ force a full FAT scan.
|
||||||
|
/
|
||||||
|
/ 0: Load all informations in the FSINFO if available.
|
||||||
|
/ 1: Do not trust free cluster count in the FSINFO.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------/
|
||||||
|
/ System Configurations
|
||||||
|
/----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#define _WORD_ACCESS 0 /* 0 or 1 */
|
||||||
|
/* The _WORD_ACCESS option is an only platform dependent option. It defines
|
||||||
|
/ which access method is used to the word data on the FAT volume.
|
||||||
|
/
|
||||||
|
/ 0: Byte-by-byte access. Always compatible with all platforms.
|
||||||
|
/ 1: Word access. Do not choose this unless under both the following conditions.
|
||||||
|
/
|
||||||
|
/ * Byte order on the memory is little-endian.
|
||||||
|
/ * Address miss-aligned word access is always allowed for all instructions.
|
||||||
|
/
|
||||||
|
/ If it is the case, _WORD_ACCESS can also be set to 1 to improve performance
|
||||||
|
/ and reduce code size.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* A header file that defines sync object types on the O/S, such as
|
||||||
|
/ windows.h, ucos_ii.h and semphr.h, must be included prior to ff.h. */
|
||||||
|
|
||||||
|
//Note by TFT: the reentrant option uses just one big lock for each FATFS, so
|
||||||
|
//given there's no concurrency advantage in using this option, we're just using
|
||||||
|
//an ordinary FastMutex in class Fat32Fs and locking it before calling FatFs.
|
||||||
|
#define _FS_REENTRANT 0 /* 0:Disable or 1:Enable */
|
||||||
|
#define _FS_TIMEOUT 1000 /* Timeout period in unit of time ticks */
|
||||||
|
#define _SYNC_t HANDLE /* O/S dependent type of sync object. e.g. HANDLE, OS_EVENT*, ID and etc.. */
|
||||||
|
|
||||||
|
/* The _FS_REENTRANT option switches the re-entrancy (thread safe) of the FatFs module.
|
||||||
|
/
|
||||||
|
/ 0: Disable re-entrancy. _SYNC_t and _FS_TIMEOUT have no effect.
|
||||||
|
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
|
||||||
|
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
|
||||||
|
/ function must be added to the project. */
|
||||||
|
|
||||||
|
//Note by TFT: this is very useful, as it avoids the danger of opening the same
|
||||||
|
//file for writing multiple times
|
||||||
|
#define _FS_LOCK 8 /* 0:Disable or >=1:Enable */
|
||||||
|
/* To enable file lock control feature, set _FS_LOCK to 1 or greater.
|
||||||
|
The value defines how many files can be opened simultaneously. */
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _FFCONFIG */
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*-------------------------------------------*/
|
||||||
|
/* Integer type definitions for FatFs module */
|
||||||
|
/*-------------------------------------------*/
|
||||||
|
|
||||||
|
#ifndef _FF_INTEGER
|
||||||
|
#define _FF_INTEGER
|
||||||
|
|
||||||
|
#ifdef _WIN32 /* FatFs development platform */
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <tchar.h>
|
||||||
|
|
||||||
|
#else /* Embedded platform */
|
||||||
|
|
||||||
|
/* This type MUST be 8 bit */
|
||||||
|
typedef unsigned char BYTE;
|
||||||
|
|
||||||
|
/* These types MUST be 16 bit */
|
||||||
|
typedef short SHORT;
|
||||||
|
typedef unsigned short WORD;
|
||||||
|
typedef unsigned short WCHAR;
|
||||||
|
|
||||||
|
/* These types MUST be 16 bit or 32 bit */
|
||||||
|
typedef int INT;
|
||||||
|
typedef unsigned int UINT;
|
||||||
|
|
||||||
|
/* These types MUST be 32 bit */
|
||||||
|
typedef long LONG;
|
||||||
|
typedef unsigned long DWORD;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
|
||||||
|
#include "ff.h"
|
||||||
|
#include "config/miosix_settings.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is an alternative version of ff_wtoupper(), designed to be both smaller,
|
||||||
|
* faster and to better conform to the unicode specification.
|
||||||
|
*
|
||||||
|
* Code size using gcc 4.7.3, with
|
||||||
|
* arm-miosix-eabi-g++ -mcpu=cortex-m3 -mthumb -O2 -fno-exceptions -fno-rtti \
|
||||||
|
* -c wtoupper.cpp
|
||||||
|
* is:
|
||||||
|
* - ChaN's ff_wtoupper(): 1000bytes
|
||||||
|
* - fede.tft's enhanced version: 236bytes
|
||||||
|
*
|
||||||
|
* The design of this function is a bit tricky, as the usual way of making a
|
||||||
|
* look up table is not optimized enough. It is old wisdom that a lut is
|
||||||
|
* both faster and more space-efficient than a sequence of if, but unicode
|
||||||
|
* conversion is somewhat peculiar. First, the input set is made of 0x10ffff
|
||||||
|
* possible values, so the usual design that makes lut access O(1) would
|
||||||
|
* require more than 2MB and is therefore out of question. However, the number
|
||||||
|
* of characters that have an uppercase form is just around 1000, so the next
|
||||||
|
* straightforward implementation would be to make a table of lowercase and
|
||||||
|
* a table of upperacse characters. A character is checked against each entry
|
||||||
|
* of the lowercase table, and if it matches the corresponding entry in the
|
||||||
|
* upperacse table is returned, while if it matches none then the character
|
||||||
|
* is either already uppercase, or does not have an uppercase form.
|
||||||
|
*
|
||||||
|
* This works, but requires roughly 4KB of tables, and is not very fast as it
|
||||||
|
* requires a for loop and a thousand comparisons per converted character.
|
||||||
|
* The next thing to notice is that many characters to convert are in
|
||||||
|
* contiguous ranges, which can be dealt with using an if statement per range.
|
||||||
|
* There is a tradeoff, however, as the range needs to contain more than a
|
||||||
|
* certain number of elements to be faster and/or result in less code size than
|
||||||
|
* the lut approach. For this reason, it was selected to use an if for every
|
||||||
|
* range of 7 or more code points, and use a final round with a look up table
|
||||||
|
* to deal with too small ranges.
|
||||||
|
*
|
||||||
|
* For what concerns unicode conformance, the result has been checked against
|
||||||
|
* the file UnicodeData.txt downloaded from unicode's website, and the following
|
||||||
|
* functional modifications have been done with respect to the original
|
||||||
|
* ff_wtoupper():
|
||||||
|
* - Code points 0x00a1, 0x00a2, 0x003, 0x00a5, 0x00ac, 0x00af are no longer
|
||||||
|
* converted. This is because they do not have an uppercase form
|
||||||
|
* - Code point 0x00b5 is converted to 0x039c
|
||||||
|
* - Code point 0x0131 is converted to 0x0049 and not 0x130
|
||||||
|
*
|
||||||
|
* In addition, according to UnicodeData.txt there are many character that
|
||||||
|
* were missing from the original implementation of ff_wtoupper(), but this was
|
||||||
|
* not fixed, as it would lead to significantly larger tables.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef WITH_FILESYSTEM
|
||||||
|
|
||||||
|
static const unsigned short lowerCase[]=
|
||||||
|
{
|
||||||
|
0x00b5, 0x00ff, 0x0131, 0x0133, 0x0135, 0x0137, 0x017a, 0x017c,
|
||||||
|
0x017e, 0x0192, 0x045e, 0x045f,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned short upperCase[]=
|
||||||
|
{
|
||||||
|
0x039c, 0x0178, 0x0049, 0x0132, 0x0134, 0x0136, 0x0179, 0x017b,
|
||||||
|
0x017d, 0x0191, 0x040e, 0x040f,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int tabSize=sizeof(lowerCase)/sizeof(lowerCase[0]);
|
||||||
|
|
||||||
|
unsigned short ff_wtoupper(unsigned short c)
|
||||||
|
{
|
||||||
|
if(c>='a' && c<='z') return c-('a'-'A'); //26 code points
|
||||||
|
|
||||||
|
if(c<0x80) return c;//Speed hack: there are no other lowercase char in ASCII
|
||||||
|
|
||||||
|
if(c>=0x00e0 && c<=0x00f6) return c-(0x00e0-0x00c0); //23 code points
|
||||||
|
|
||||||
|
if(c>=0x00f8 && c<=0x00fe) return c-(0x00f8-0x00d8); // 7 code points
|
||||||
|
|
||||||
|
if(c>=0x0101 && c<=0x012f && (c & 1)) return c-1; //24 code points
|
||||||
|
|
||||||
|
if(c>=0x013a && c<=0x0148 && ((c & 1)==0)) return c-1; // 8 code points
|
||||||
|
|
||||||
|
if(c>=0x014b && c<=0x0177 && (c & 1)) return c-1; //23 code points
|
||||||
|
|
||||||
|
if(c>=0x03b1 && c<=0x03c1) return c-(0x03b1-0x0391); //17 code points
|
||||||
|
|
||||||
|
if(c>=0x03c3 && c<=0x03ca) return c-(0x03c3-0x03a3); // 8 code points
|
||||||
|
|
||||||
|
if(c>=0x0430 && c<=0x044f) return c-(0x0430-0x0410); //32 code points
|
||||||
|
|
||||||
|
if(c>=0x0451 && c<=0x045c) return c-(0x0451-0x0401); //12 code points
|
||||||
|
|
||||||
|
if(c>=0x2170 && c<=0x217f) return c-(0x2170-0x2160); //16 code points
|
||||||
|
|
||||||
|
if(c>=0xff41 && c<=0xff5a) return c-(0xff41-0xff21); //26 code points
|
||||||
|
|
||||||
|
for(int i=0;i<tabSize;i++) if(lowerCase[i]==c) return upperCase[i];
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //WITH_FILESYSTEM
|
||||||
|
|
||||||
|
/*
|
||||||
|
//Print all characters that have an uppercase
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
for(unisgned short i=0;i<0x10000;i++)
|
||||||
|
{
|
||||||
|
unisgned short up=ff_wtoupper(i);
|
||||||
|
if(up==i) continue;
|
||||||
|
cout<<hex<<uppercase<<setw(4)<<setfill('0')<<i
|
||||||
|
<<" "<<setw(4)<<setfill('0')<<up<<endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Crawl UnicodeData.txt making a table of code points and their uppercase
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
codePoint=0,
|
||||||
|
upperCase=12,
|
||||||
|
lowerCase=13
|
||||||
|
};
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
ifstream in("UnicodeData.txt");
|
||||||
|
string line;
|
||||||
|
int lineno=0;
|
||||||
|
while(getline(in,line))
|
||||||
|
{
|
||||||
|
lineno++;
|
||||||
|
stringstream ss(line);
|
||||||
|
vector<string> fields;
|
||||||
|
string field;
|
||||||
|
while(getline(ss,field,';')) fields.push_back(field);
|
||||||
|
if(fields.at(upperCase).empty()==false &&
|
||||||
|
fields.at(codePoint)!=fields.at(upperCase))
|
||||||
|
cout<<fields.at(codePoint)<<" "<<fields.at(upperCase)<<endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
@ -0,0 +1,189 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "file.h"
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include "file_access.h"
|
||||||
|
#include "config/miosix_settings.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
//This is a static assert. The filesystem code assumes off_t is 64bit.
|
||||||
|
typedef char check_sizeof_off_t[sizeof(off_t)==8 ? 1 : -1];
|
||||||
|
|
||||||
|
//
|
||||||
|
// class FileBase
|
||||||
|
//
|
||||||
|
|
||||||
|
FileBase::FileBase(intrusive_ref_ptr<FilesystemBase> parent) : parent(parent)
|
||||||
|
{
|
||||||
|
if(parent) parent->newFileOpened();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_FILESYSTEM
|
||||||
|
|
||||||
|
int FileBase::isatty() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileBase::fcntl(int cmd, int opt)
|
||||||
|
{
|
||||||
|
//Newlib makes some calls to fcntl, for example in opendir(). CLOEXEC isn't
|
||||||
|
//supported, but for now we lie and return 0
|
||||||
|
if(cmd==F_SETFD && (opt==FD_CLOEXEC || opt==0)) return 0;
|
||||||
|
return -EBADF;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileBase::ioctl(int cmd, void *arg)
|
||||||
|
{
|
||||||
|
return -ENOTTY; //Means the operation does not apply to this descriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileBase::getdents(void *dp, int len)
|
||||||
|
{
|
||||||
|
return -EBADF;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //WITH_FILESYSTEM
|
||||||
|
|
||||||
|
FileBase::~FileBase()
|
||||||
|
{
|
||||||
|
if(parent) parent->fileCloseHook();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// class DirectoryBase
|
||||||
|
//
|
||||||
|
|
||||||
|
ssize_t DirectoryBase::write(const void *data, size_t len)
|
||||||
|
{
|
||||||
|
return -EBADF;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t DirectoryBase::read(void *data, size_t len)
|
||||||
|
{
|
||||||
|
return -EBADF;
|
||||||
|
}
|
||||||
|
|
||||||
|
off_t DirectoryBase::lseek(off_t pos, int whence)
|
||||||
|
{
|
||||||
|
return -EBADF;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DirectoryBase::fstat(struct stat *pstat) const
|
||||||
|
{
|
||||||
|
return -EBADF;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DirectoryBase::addEntry(char **pos, char *end, int ino, char type,
|
||||||
|
const StringPart& n)
|
||||||
|
{
|
||||||
|
int reclen=direntHeaderSizeNoPadding+n.length()+1;
|
||||||
|
reclen=(reclen+3) & ~0x3; //Align to 4 bytes
|
||||||
|
if(reclen>end-*pos) return -1;
|
||||||
|
|
||||||
|
struct dirent *data=reinterpret_cast<struct dirent*>(*pos);
|
||||||
|
data->d_ino=ino;
|
||||||
|
data->d_off=0;
|
||||||
|
data->d_reclen=reclen;
|
||||||
|
data->d_type=type;
|
||||||
|
strcpy(data->d_name,n.c_str());
|
||||||
|
|
||||||
|
(*pos)+=reclen;
|
||||||
|
return reclen;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DirectoryBase::addDefaultEntries(char **pos, int thisIno, int upIno)
|
||||||
|
{
|
||||||
|
struct dirent *data=reinterpret_cast<struct dirent*>(*pos);
|
||||||
|
data->d_ino=thisIno;
|
||||||
|
data->d_off=0;
|
||||||
|
data->d_reclen=direntHeaderSize;
|
||||||
|
data->d_type=DT_DIR;
|
||||||
|
strcpy(data->d_name,".");
|
||||||
|
|
||||||
|
(*pos)+=direntHeaderSize;
|
||||||
|
data=reinterpret_cast<struct dirent*>(*pos);
|
||||||
|
data->d_ino=upIno;
|
||||||
|
data->d_off=0;
|
||||||
|
data->d_reclen=direntHeaderSize;
|
||||||
|
data->d_type=DT_DIR;
|
||||||
|
strcpy(data->d_name,"..");
|
||||||
|
|
||||||
|
(*pos)+=direntHeaderSize;
|
||||||
|
return 2*direntHeaderSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DirectoryBase::addTerminatingEntry(char **pos, char *end)
|
||||||
|
{
|
||||||
|
if(direntHeaderSize>end-*pos) return -1;
|
||||||
|
//This sets everything to zero, including d_reclen, terminating the
|
||||||
|
//directory listing loop in readdir.c
|
||||||
|
memset(*pos,0,direntHeaderSize);
|
||||||
|
(*pos)+=direntHeaderSize;
|
||||||
|
return direntHeaderSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// class FilesystemBase
|
||||||
|
//
|
||||||
|
|
||||||
|
FilesystemBase::FilesystemBase() :
|
||||||
|
#ifdef WITH_FILESYSTEM
|
||||||
|
filesystemId(FilesystemManager::getFilesystemId()),
|
||||||
|
#else //WITH_FILESYSTEM
|
||||||
|
filesystemId(0),
|
||||||
|
#endif //WITH_FILESYSTEM
|
||||||
|
parentFsMountpointInode(1), openFileCount(0) {}
|
||||||
|
|
||||||
|
int FilesystemBase::readlink(StringPart& name, string& target)
|
||||||
|
{
|
||||||
|
return -EINVAL; //Default implementation, for filesystems without symlinks
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FilesystemBase::supportsSymlinks() const { return false; }
|
||||||
|
|
||||||
|
void FilesystemBase::newFileOpened() { atomicAdd(&openFileCount,1); }
|
||||||
|
|
||||||
|
void FilesystemBase::fileCloseHook()
|
||||||
|
{
|
||||||
|
#ifdef WITH_ERRLOG
|
||||||
|
int result=atomicAddExchange(&openFileCount,-1);
|
||||||
|
assert(result>=0);
|
||||||
|
#else //WITH_ERRLOG
|
||||||
|
atomicAdd(&openFileCount,-1);
|
||||||
|
#endif //WITH_ERRLOG
|
||||||
|
}
|
||||||
|
|
||||||
|
FilesystemBase::~FilesystemBase() {}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,379 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "kernel/intrusive.h"
|
||||||
|
#include "config/miosix_settings.h"
|
||||||
|
|
||||||
|
#ifndef FILE_H
|
||||||
|
#define FILE_H
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
// Forward decls
|
||||||
|
class FilesystemBase;
|
||||||
|
class StringPart;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unix file abstraction. Also some device drivers are seen as files.
|
||||||
|
* Classes of this type are reference counted, must be allocated on the heap
|
||||||
|
* and managed through intrusive_ref_ptr<FileBase>
|
||||||
|
*/
|
||||||
|
class FileBase : public IntrusiveRefCounted
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* \param parent the filesystem to which this file belongs
|
||||||
|
*/
|
||||||
|
FileBase(intrusive_ref_ptr<FilesystemBase> parent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data to the file, if the file supports writing.
|
||||||
|
* \param data the data to write
|
||||||
|
* \param len the number of bytes to write
|
||||||
|
* \return the number of written characters, or a negative number in case
|
||||||
|
* of errors
|
||||||
|
*/
|
||||||
|
virtual ssize_t write(const void *data, size_t len)=0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read data from the file, if the file supports reading.
|
||||||
|
* \param data buffer to store read data
|
||||||
|
* \param len the number of bytes to read
|
||||||
|
* \return the number of read characters, or a negative number in case
|
||||||
|
* of errors
|
||||||
|
*/
|
||||||
|
virtual ssize_t read(void *data, size_t len)=0;
|
||||||
|
|
||||||
|
#ifdef WITH_FILESYSTEM
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move file pointer, if the file supports random-access.
|
||||||
|
* \param pos offset to sum to the beginning of the file, current position
|
||||||
|
* or end of file, depending on whence
|
||||||
|
* \param whence SEEK_SET, SEEK_CUR or SEEK_END
|
||||||
|
* \return the offset from the beginning of the file if the operation
|
||||||
|
* completed, or a negative number in case of errors
|
||||||
|
*/
|
||||||
|
virtual off_t lseek(off_t pos, int whence)=0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return file information.
|
||||||
|
* \param pstat pointer to stat struct
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int fstat(struct stat *pstat) const=0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the file refers to a terminal.
|
||||||
|
* \return 1 if it is a terminal, 0 if it is not, or a negative number in
|
||||||
|
* case of errors
|
||||||
|
*/
|
||||||
|
virtual int isatty() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform various operations on a file descriptor
|
||||||
|
* \param cmd specifies the operation to perform
|
||||||
|
* \param opt optional argument that some operation require
|
||||||
|
* \return the exact return value depends on CMD, -1 is returned on error
|
||||||
|
*/
|
||||||
|
virtual int fcntl(int cmd, int opt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform various operations on a file descriptor
|
||||||
|
* \param cmd specifies the operation to perform
|
||||||
|
* \param arg optional argument that some operation require
|
||||||
|
* \return the exact return value depends on CMD, -1 is returned on error
|
||||||
|
*/
|
||||||
|
virtual int ioctl(int cmd, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Also directories can be opened as files. In this case, this system call
|
||||||
|
* allows to retrieve directory entries.
|
||||||
|
* \param dp pointer to a memory buffer where one or more struct dirent
|
||||||
|
* will be placed. dp must be four words aligned.
|
||||||
|
* \param len memory buffer size.
|
||||||
|
* \return the number of bytes read on success, or a negative number on
|
||||||
|
* failure.
|
||||||
|
*/
|
||||||
|
virtual int getdents(void *dp, int len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return a pointer to the parent filesystem
|
||||||
|
*/
|
||||||
|
const intrusive_ref_ptr<FilesystemBase> getParent() const { return parent; }
|
||||||
|
|
||||||
|
#endif //WITH_FILESYSTEM
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File destructor
|
||||||
|
*/
|
||||||
|
virtual ~FileBase();
|
||||||
|
|
||||||
|
private:
|
||||||
|
FileBase(const FileBase&);
|
||||||
|
FileBase& operator=(const FileBase&);
|
||||||
|
|
||||||
|
intrusive_ref_ptr<FilesystemBase> parent; ///< Files may have a parent fs
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directories are a special kind of files that implement the getdents() call
|
||||||
|
* Classes of this type are reference counted, must be allocated on the heap
|
||||||
|
* and managed through intrusive_ref_ptr<DirectoryBase>
|
||||||
|
*/
|
||||||
|
class DirectoryBase : public FileBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* \param parent the filesystem to which this file belongs
|
||||||
|
*/
|
||||||
|
DirectoryBase(intrusive_ref_ptr<FilesystemBase> parent) : FileBase(parent) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data to the file, if the file supports writing.
|
||||||
|
* \param data the data to write
|
||||||
|
* \param len the number of bytes to write
|
||||||
|
* \return the number of written characters, or a negative number in case
|
||||||
|
* of errors
|
||||||
|
*/
|
||||||
|
virtual ssize_t write(const void *data, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read data from the file, if the file supports reading.
|
||||||
|
* \param data buffer to store read data
|
||||||
|
* \param len the number of bytes to read
|
||||||
|
* \return the number of read characters, or a negative number in case
|
||||||
|
* of errors
|
||||||
|
*/
|
||||||
|
virtual ssize_t read(void *data, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move file pointer, if the file supports random-access.
|
||||||
|
* \param pos offset to sum to the beginning of the file, current position
|
||||||
|
* or end of file, depending on whence
|
||||||
|
* \param whence SEEK_SET, SEEK_CUR or SEEK_END
|
||||||
|
* \return the offset from the beginning of the file if the operation
|
||||||
|
* completed, or a negative number in case of errors
|
||||||
|
*/
|
||||||
|
virtual off_t lseek(off_t pos, int whence);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return file information.
|
||||||
|
* \param pstat pointer to stat struct
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int fstat(struct stat *pstat) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Helper function to add a directory entry to a buffer
|
||||||
|
* \param pos position where to add the entry (four word aligned). Pointer
|
||||||
|
* is incremented.
|
||||||
|
* \param end end of buffer (one char past the last), for bound checking
|
||||||
|
* \param ino inode of file
|
||||||
|
* \param type file type
|
||||||
|
* \param name file name to append after the DirentHeader
|
||||||
|
* \return the number of bytes written or -1 on failure (no space in buffer)
|
||||||
|
*/
|
||||||
|
static int addEntry(char **pos, char *end, int ino, char type,
|
||||||
|
const StringPart& n);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to add the default directory entries . and .. to a buffer
|
||||||
|
* \param pos position where to add the entry (four word aligned). Pointer
|
||||||
|
* is incremented. The caller is responsible to guarantee that there is at
|
||||||
|
* least space for 2*direntHeaderSize
|
||||||
|
* \param thisIno inode number of .
|
||||||
|
* \param upInode inode number of ..
|
||||||
|
* \return the number of bytes written
|
||||||
|
*/
|
||||||
|
static int addDefaultEntries(char **pos, int thisIno, int upIno);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an entry with d_reclen=0 which is used to terminate directory listing
|
||||||
|
* \param pos position where to add the entry (four word aligned). Pointer
|
||||||
|
* is incremented. The caller is responsible to guarantee that there is at
|
||||||
|
* least space for direntHeaderSize, including padding
|
||||||
|
* \param end end of buffer (one char past the last), for bound checking
|
||||||
|
* \return the number of bytes written or -1 on failure (no space in buffer)
|
||||||
|
*/
|
||||||
|
static int addTerminatingEntry(char **pos, char *end);
|
||||||
|
|
||||||
|
///Size of struct dirent excluding d_name. That is, the size of d_ino,
|
||||||
|
///d_off, d_reclen and d_type. Notice that there are 4 bytes of padding
|
||||||
|
///between d_ino and d_off as d_off is a 64 bit number. Should be 19.
|
||||||
|
static const int direntHeaderSizeNoPadding=offsetof(struct dirent,d_name);
|
||||||
|
|
||||||
|
///Size of struct dirent including room for the "." and ".." string in
|
||||||
|
///d_name, including terminating \0 and padding for 4-word alignment.
|
||||||
|
///First +3: make room for '..\0', 3 bytes
|
||||||
|
///Second +3 and /4*4: four word alignment
|
||||||
|
static const int direntHeaderSize=(direntHeaderSizeNoPadding+3+3)/4*4;
|
||||||
|
|
||||||
|
///Minimum buffer accepted by getdents, two for . and .., plus terminating
|
||||||
|
static const int minimumBufferSize=3*direntHeaderSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All filesystems derive from this class. Classes of this type are reference
|
||||||
|
* counted, must be allocated on the heap and managed through
|
||||||
|
* intrusive_ref_ptr<FilesystemBase>
|
||||||
|
*/
|
||||||
|
class FilesystemBase : public IntrusiveRefCounted,
|
||||||
|
public IntrusiveRefCountedSharedFromThis<FilesystemBase>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
FilesystemBase();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a file
|
||||||
|
* \param file the file object will be stored here, if the call succeeds
|
||||||
|
* \param name the name of the file to open, relative to the local
|
||||||
|
* filesystem
|
||||||
|
* \param flags file flags (open for reading, writing, ...)
|
||||||
|
* \param mode file permissions
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int open(intrusive_ref_ptr<FileBase>& file, StringPart& name,
|
||||||
|
int flags, int mode)=0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain information on a file, identified by a path name. Does not follow
|
||||||
|
* symlinks
|
||||||
|
* \param name path name, relative to the local filesystem
|
||||||
|
* \param pstat file information is stored here
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int lstat(StringPart& name, struct stat *pstat)=0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a file or directory
|
||||||
|
* \param name path name of file or directory to remove
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int unlink(StringPart& name)=0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename a file or directory
|
||||||
|
* \param oldName old file name
|
||||||
|
* \param newName new file name
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int rename(StringPart& oldName, StringPart& newName)=0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a directory
|
||||||
|
* \param name directory name
|
||||||
|
* \param mode directory permissions
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int mkdir(StringPart& name, int mode)=0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a directory if empty
|
||||||
|
* \param name directory name
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int rmdir(StringPart& name)=0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Follows a symbolic link
|
||||||
|
* \param path path identifying a symlink, relative to the local filesystem
|
||||||
|
* \param target the link target is returned here if the call succeeds.
|
||||||
|
* Note that the returned path is not relative to this filesystem, and can
|
||||||
|
* be either relative or absolute.
|
||||||
|
* \return 0 on success, a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int readlink(StringPart& name, std::string& target);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return true if the filesystem supports symbolic links.
|
||||||
|
* In this case, the filesystem should override readlink
|
||||||
|
*/
|
||||||
|
virtual bool supportsSymlinks() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* \return true if all files belonging to this filesystem are closed
|
||||||
|
*/
|
||||||
|
bool areAllFilesClosed() { return openFileCount==0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Called by file constructor whenever a file belonging to this
|
||||||
|
* filesystem is opened. Never call this function from user code.
|
||||||
|
*/
|
||||||
|
void newFileOpened();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Called by file destructors whenever a file belonging to this
|
||||||
|
* filesystem is closed. Never call this function from user code.
|
||||||
|
*/
|
||||||
|
void fileCloseHook();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* This is used to inform a filesystem of the inode of the directory in the
|
||||||
|
* parent fs where it is mounted. It is used for directory listing, to
|
||||||
|
* resolve the inode of the .. entry of the filesystem's root directory
|
||||||
|
* \param inode inode of the directory where the fs is mounted
|
||||||
|
*/
|
||||||
|
void setParentFsMountpointInode(int inode) { parentFsMountpointInode=inode; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return filesystem id
|
||||||
|
*/
|
||||||
|
short int getFsId() const { return filesystemId; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor
|
||||||
|
*/
|
||||||
|
virtual ~FilesystemBase();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
const short int filesystemId; ///< The unique filesystem id, used by lstat
|
||||||
|
int parentFsMountpointInode; ///< The inode of the directory in the parent fs
|
||||||
|
|
||||||
|
private:
|
||||||
|
FilesystemBase(const FilesystemBase&);
|
||||||
|
FilesystemBase& operator= (const FilesystemBase&);
|
||||||
|
|
||||||
|
volatile int openFileCount; ///< Number of open files
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //FILE_H
|
||||||
|
|
@ -0,0 +1,763 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "file_access.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <climits>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include "console/console_device.h"
|
||||||
|
#include "mountpointfs/mountpointfs.h"
|
||||||
|
#include "fat32/fat32.h"
|
||||||
|
#include "kernel/logging.h"
|
||||||
|
#ifdef WITH_PROCESSES
|
||||||
|
#include "kernel/process.h"
|
||||||
|
#endif //WITH_PROCESSES
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#ifdef WITH_FILESYSTEM
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A note on the use of strings in this file. This file uses three string
|
||||||
|
* types: C string, C++ std::string and StringPart which is an efficent
|
||||||
|
* in-place substring of either a C or C++ string.
|
||||||
|
*
|
||||||
|
* The functions which are meant to be used by clients of the filesystem
|
||||||
|
* API take file names as C strings. This is becase that's the signature
|
||||||
|
* of the POSIX calls, such as fopen(), stat(), umount(), ... It is
|
||||||
|
* worth noticing that these C strings are const. However, resolving paths
|
||||||
|
* requires a writable scratchpad string to be able to remove
|
||||||
|
* useless path components, such as "/./", go backwards when a "/../" is
|
||||||
|
* found, and follow symbolic links. To this end, all these functions
|
||||||
|
* make a copy of the passed string into a temporary C++ string that
|
||||||
|
* will be deallocated as soon as the function returns.
|
||||||
|
* Resolving a path, however, requires to scan all its path components
|
||||||
|
* one by one, checking if the path up to that point is a symbolic link
|
||||||
|
* or a mountpoint of a filesystem.
|
||||||
|
* For example, resolving "/home/test/file" requires to make three
|
||||||
|
* substrings, "/home", "/home/test" and "/home/test/file". Of course,
|
||||||
|
* one can simply use the substr() member function of the std::string
|
||||||
|
* class. However, this means making lots of tiny memory allocations on
|
||||||
|
* the heap, increasing the RAM footprint to resolve a path. To this end,
|
||||||
|
* the StringPart class was introduced, to create fast in-place substring
|
||||||
|
* of a string.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//
|
||||||
|
// class FileDescriptorTable
|
||||||
|
//
|
||||||
|
|
||||||
|
FileDescriptorTable::FileDescriptorTable()
|
||||||
|
: mutex(FastMutex::RECURSIVE), cwd("/")
|
||||||
|
{
|
||||||
|
FilesystemManager::instance().addFileDescriptorTable(this);
|
||||||
|
files[0]=files[1]=files[2]=intrusive_ref_ptr<FileBase>(
|
||||||
|
new TerminalDevice(DefaultConsole::instance().get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
FileDescriptorTable::FileDescriptorTable(const FileDescriptorTable& rhs)
|
||||||
|
: mutex(FastMutex::RECURSIVE), cwd(rhs.cwd)
|
||||||
|
{
|
||||||
|
//No need to lock the mutex since we are in a constructor and there can't
|
||||||
|
//be pointers to this in other threads yet
|
||||||
|
for(int i=0;i<MAX_OPEN_FILES;i++) this->files[i]=atomic_load(&rhs.files[i]);
|
||||||
|
FilesystemManager::instance().addFileDescriptorTable(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileDescriptorTable& FileDescriptorTable::operator=(
|
||||||
|
const FileDescriptorTable& rhs)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
for(int i=0;i<MAX_OPEN_FILES;i++)
|
||||||
|
atomic_store(&this->files[i],atomic_load(&rhs.files[i]));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileDescriptorTable::open(const char* name, int flags, int mode)
|
||||||
|
{
|
||||||
|
if(name==0 || name[0]=='\0') return -EFAULT;
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
for(int i=3;i<MAX_OPEN_FILES;i++)
|
||||||
|
{
|
||||||
|
if(files[i]) continue;
|
||||||
|
//Found an empty file descriptor
|
||||||
|
string path=absolutePath(name);
|
||||||
|
if(path.empty()) return -ENAMETOOLONG;
|
||||||
|
ResolvedPath openData=FilesystemManager::instance().resolvePath(path);
|
||||||
|
if(openData.result<0) return openData.result;
|
||||||
|
StringPart sp(path,string::npos,openData.off);
|
||||||
|
int result=openData.fs->open(files[i],sp,flags,mode);
|
||||||
|
if(result==0) return i; //The file descriptor
|
||||||
|
else return result; //The error code
|
||||||
|
}
|
||||||
|
return -ENFILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileDescriptorTable::close(int fd)
|
||||||
|
{
|
||||||
|
//No need to lock the mutex when deleting
|
||||||
|
if(fd<0 || fd>=MAX_OPEN_FILES) return -EBADF;
|
||||||
|
intrusive_ref_ptr<FileBase> toClose;
|
||||||
|
toClose=atomic_exchange(files+fd,intrusive_ref_ptr<FileBase>());
|
||||||
|
if(!toClose) return -EBADF; //File entry was not open
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDescriptorTable::closeAll()
|
||||||
|
{
|
||||||
|
for(int i=0;i<MAX_OPEN_FILES;i++)
|
||||||
|
atomic_exchange(files+i,intrusive_ref_ptr<FileBase>());
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileDescriptorTable::getcwd(char *buf, size_t len)
|
||||||
|
{
|
||||||
|
if(buf==0 || len<2) return -EINVAL; //We don't support the buf==0 extension
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
struct stat st;
|
||||||
|
if(stat(".",&st) || !S_ISDIR(st.st_mode)) return -ENOENT;
|
||||||
|
if(cwd.length()>len) return -ERANGE;
|
||||||
|
strncpy(buf,cwd.c_str(),len);
|
||||||
|
if(cwd.length()>1) buf[cwd.length()-1]='\0'; //Erase last '/' in cwd
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileDescriptorTable::chdir(const char* name)
|
||||||
|
{
|
||||||
|
if(name==0 || name[0]=='\0') return -EFAULT;
|
||||||
|
size_t len=strlen(name);
|
||||||
|
if(name[len-1]!='/') len++; //Reserve room for trailing slash
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(name[0]!='/') len+=cwd.length();
|
||||||
|
if(len>PATH_MAX) return -ENAMETOOLONG;
|
||||||
|
|
||||||
|
string newCwd;
|
||||||
|
newCwd.reserve(len);
|
||||||
|
if(name[0]=='/') newCwd=name;
|
||||||
|
else {
|
||||||
|
newCwd=cwd;
|
||||||
|
newCwd+=name;
|
||||||
|
}
|
||||||
|
ResolvedPath openData=FilesystemManager::instance().resolvePath(newCwd);
|
||||||
|
if(openData.result<0) return openData.result;
|
||||||
|
struct stat st;
|
||||||
|
StringPart sp(newCwd,string::npos,openData.off);
|
||||||
|
if(int result=openData.fs->lstat(sp,&st)) return result;
|
||||||
|
if(!S_ISDIR(st.st_mode)) return -ENOTDIR;
|
||||||
|
//NOTE: put after resolvePath() as it strips trailing /
|
||||||
|
//Also put after lstat() as it fails if path has a trailing slash
|
||||||
|
newCwd+='/';
|
||||||
|
cwd=newCwd;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileDescriptorTable::mkdir(const char *name, int mode)
|
||||||
|
{
|
||||||
|
if(name==0 || name[0]=='\0') return -EFAULT;
|
||||||
|
string path=absolutePath(name);
|
||||||
|
if(path.empty()) return -ENAMETOOLONG;
|
||||||
|
ResolvedPath openData=FilesystemManager::instance().resolvePath(path,true);
|
||||||
|
if(openData.result<0) return openData.result;
|
||||||
|
StringPart sp(path,string::npos,openData.off);
|
||||||
|
return openData.fs->mkdir(sp,mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileDescriptorTable::rmdir(const char *name)
|
||||||
|
{
|
||||||
|
if(name==0 || name[0]=='\0') return -EFAULT;
|
||||||
|
string path=absolutePath(name);
|
||||||
|
if(path.empty()) return -ENAMETOOLONG;
|
||||||
|
ResolvedPath openData=FilesystemManager::instance().resolvePath(path,true);
|
||||||
|
if(openData.result<0) return openData.result;
|
||||||
|
StringPart sp(path,string::npos,openData.off);
|
||||||
|
return openData.fs->rmdir(sp);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileDescriptorTable::unlink(const char *name)
|
||||||
|
{
|
||||||
|
if(name==0 || name[0]=='\0') return -EFAULT;
|
||||||
|
string path=absolutePath(name);
|
||||||
|
if(path.empty()) return -ENAMETOOLONG;
|
||||||
|
return FilesystemManager::instance().unlinkHelper(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileDescriptorTable::rename(const char *oldName, const char *newName)
|
||||||
|
{
|
||||||
|
if(oldName==0 || oldName[0]=='\0') return -EFAULT;
|
||||||
|
if(newName==0 || newName[0]=='\0') return -EFAULT;
|
||||||
|
string oldPath=absolutePath(oldName);
|
||||||
|
string newPath=absolutePath(newName);
|
||||||
|
if(oldPath.empty() || newPath.empty()) return -ENAMETOOLONG;
|
||||||
|
return FilesystemManager::instance().renameHelper(oldPath,newPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileDescriptorTable::statImpl(const char* name, struct stat* pstat, bool f)
|
||||||
|
{
|
||||||
|
if(name==0 || name[0]=='\0' || pstat==0) return -EFAULT;
|
||||||
|
string path=absolutePath(name);
|
||||||
|
if(path.empty()) return -ENAMETOOLONG;
|
||||||
|
return FilesystemManager::instance().statHelper(path,pstat,f);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileDescriptorTable::~FileDescriptorTable()
|
||||||
|
{
|
||||||
|
FilesystemManager::instance().removeFileDescriptorTable(this);
|
||||||
|
//There's no need to lock the mutex and explicitly close files eventually
|
||||||
|
//left open, because if there are other threads accessing this while we are
|
||||||
|
//being deleted we have bigger problems anyway
|
||||||
|
}
|
||||||
|
|
||||||
|
string FileDescriptorTable::absolutePath(const char* path)
|
||||||
|
{
|
||||||
|
size_t len=strlen(path);
|
||||||
|
if(len>PATH_MAX) return "";
|
||||||
|
if(path[0]=='/') return path;
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(len+cwd.length()>PATH_MAX) return "";
|
||||||
|
return cwd+path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class implements the path resolution logic
|
||||||
|
*/
|
||||||
|
class PathResolution
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* \param fs map of all mounted filesystems
|
||||||
|
*/
|
||||||
|
PathResolution(const map<StringPart,intrusive_ref_ptr<FilesystemBase> >& fs)
|
||||||
|
: filesystems(fs) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main purpose of this class, resolve a path
|
||||||
|
* \param path inout parameter with the path to resolve. The resolved path
|
||||||
|
* will be modified in-place in this string. The path must be absolute and
|
||||||
|
* start with a "/". The caller is responsible for that.
|
||||||
|
* \param followLastSymlink if true, follow last symlink
|
||||||
|
* \return a resolved path
|
||||||
|
*/
|
||||||
|
ResolvedPath resolvePath(string& path, bool followLastSymlink);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Handle a /../ in a path
|
||||||
|
* \param path path string
|
||||||
|
* \param slash path[slash] is the / character after the ..
|
||||||
|
* \return 0 on success, a negative number on error
|
||||||
|
*/
|
||||||
|
int upPathComponent(string& path, size_t slash);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a normal path component in a path, i.e, a path component
|
||||||
|
* that is neither //, /./ or /../
|
||||||
|
* \param path path string
|
||||||
|
* \param followIfSymlink if true, follow symbolic links
|
||||||
|
* \return 0 on success, or a negative number on error
|
||||||
|
*/
|
||||||
|
int normalPathComponent(string& path, bool followIfSymlink);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Follow a symbolic link
|
||||||
|
* \param path path string. The relative path into the current filesystem
|
||||||
|
* must be a symbolic link (verified by the caller).
|
||||||
|
* \return 0 on success, a negative number on failure
|
||||||
|
*/
|
||||||
|
int followSymlink(string& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find to which filesystem this path belongs
|
||||||
|
* \param path path string.
|
||||||
|
* \return 0 on success, a negative number on failure
|
||||||
|
*/
|
||||||
|
int recursiveFindFs(string& path);
|
||||||
|
|
||||||
|
/// Mounted filesystems
|
||||||
|
const map<StringPart,intrusive_ref_ptr<FilesystemBase> >& filesystems;
|
||||||
|
|
||||||
|
/// Pointer to root filesystem
|
||||||
|
intrusive_ref_ptr<FilesystemBase> root;
|
||||||
|
|
||||||
|
/// Current filesystem while looking up path
|
||||||
|
intrusive_ref_ptr<FilesystemBase> fs;
|
||||||
|
|
||||||
|
/// True if current filesystem supports symlinks
|
||||||
|
bool syms;
|
||||||
|
|
||||||
|
/// path[index] is first unhandled char
|
||||||
|
size_t index;
|
||||||
|
|
||||||
|
/// path.substr(indexIntoFs) is the relative path to current filesystem
|
||||||
|
size_t indexIntoFs;
|
||||||
|
|
||||||
|
/// How many components does the relative path have in current fs
|
||||||
|
int depthIntoFs;
|
||||||
|
|
||||||
|
/// How many symlinks we've found so far
|
||||||
|
int linksFollowed;
|
||||||
|
|
||||||
|
/// Maximum number of symbolic links to follow (to avoid endless loops)
|
||||||
|
static const int maxLinkToFollow=2;
|
||||||
|
};
|
||||||
|
|
||||||
|
ResolvedPath PathResolution::resolvePath(string& path, bool followLastSymlink)
|
||||||
|
{
|
||||||
|
map<StringPart,intrusive_ref_ptr<FilesystemBase> >::const_iterator it;
|
||||||
|
it=filesystems.find(StringPart("/"));
|
||||||
|
if(it==filesystems.end()) return ResolvedPath(-ENOENT); //should not happen
|
||||||
|
root=fs=it->second;
|
||||||
|
syms=fs->supportsSymlinks();
|
||||||
|
index=1; //Skip leading /
|
||||||
|
indexIntoFs=1; //NOTE: caller must ensure path[0]=='/'
|
||||||
|
depthIntoFs=1;
|
||||||
|
linksFollowed=0;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
size_t slash=path.find_first_of('/',index);
|
||||||
|
//cout<<path.substr(0,slash)<<endl;
|
||||||
|
//Last component (no trailing /)
|
||||||
|
if(slash==string::npos) slash=path.length(); //NOTE: one past the last
|
||||||
|
|
||||||
|
if(slash==index)
|
||||||
|
{
|
||||||
|
//Path component is empty, caused by double slash, remove it
|
||||||
|
path.erase(index,1);
|
||||||
|
} else if(slash-index==1 && path[index]=='.')
|
||||||
|
{
|
||||||
|
path.erase(index,2); //Path component is ".", ignore
|
||||||
|
} else if(slash-index==2 && path[index]=='.' && path[index+1]=='.')
|
||||||
|
{
|
||||||
|
int result=upPathComponent(path,slash);
|
||||||
|
if(result<0) return ResolvedPath(result);
|
||||||
|
} else {
|
||||||
|
index=slash+1; //NOTE: if(slash==string::npos) two past the last
|
||||||
|
// follow=followLastSymlink for "/link", but is true for "/link/"
|
||||||
|
bool follow=index>path.length() ? followLastSymlink : true;
|
||||||
|
int result=normalPathComponent(path,follow);
|
||||||
|
if(result<0) return ResolvedPath(result);
|
||||||
|
}
|
||||||
|
//Last component
|
||||||
|
if(index>=path.length())
|
||||||
|
{
|
||||||
|
//Remove trailing /
|
||||||
|
size_t last=path.length()-1;
|
||||||
|
if(path[last]=='/')
|
||||||
|
{
|
||||||
|
path.erase(last,1);
|
||||||
|
//This may happen if the last path component is a fs
|
||||||
|
if(indexIntoFs>path.length()) indexIntoFs=path.length();
|
||||||
|
}
|
||||||
|
return ResolvedPath(fs,indexIntoFs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int PathResolution::upPathComponent(string& path, size_t slash)
|
||||||
|
{
|
||||||
|
if(index<=1) return -ENOENT; //root dir has no parent
|
||||||
|
size_t removeStart=path.find_last_of('/',index-2);
|
||||||
|
if(removeStart==string::npos) return -ENOENT; //should not happen
|
||||||
|
path.erase(removeStart,slash-removeStart);
|
||||||
|
index=removeStart+1;
|
||||||
|
//This may happen when merging a path like "/dir/.."
|
||||||
|
if(path.empty()) path='/';
|
||||||
|
//This may happen if the new last path component is a fs, e.g. "/dev/null/.."
|
||||||
|
if(indexIntoFs>path.length()) indexIntoFs=path.length();
|
||||||
|
if(--depthIntoFs>0) return 0;
|
||||||
|
|
||||||
|
//Depth went to zero, escape current filesystem
|
||||||
|
return recursiveFindFs(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
int PathResolution::normalPathComponent(string& path, bool followIfSymlink)
|
||||||
|
{
|
||||||
|
map<StringPart,intrusive_ref_ptr<FilesystemBase> >::const_iterator it;
|
||||||
|
it=filesystems.find(StringPart(path,index-1));
|
||||||
|
if(it!=filesystems.end())
|
||||||
|
{
|
||||||
|
//Jumped to a new filesystem. Not stat-ing the path as we're
|
||||||
|
//relying on mount not allowing to mount a filesystem on anything
|
||||||
|
//but a directory.
|
||||||
|
fs=it->second;
|
||||||
|
syms=fs->supportsSymlinks();
|
||||||
|
indexIntoFs=index>path.length() ? index-1 : index;
|
||||||
|
depthIntoFs=1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
depthIntoFs++;
|
||||||
|
if(syms && followIfSymlink)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
{
|
||||||
|
StringPart sp(path,index-1,indexIntoFs);
|
||||||
|
if(int res=fs->lstat(sp,&st)<0) return res;
|
||||||
|
}
|
||||||
|
if(S_ISLNK(st.st_mode)) return followSymlink(path);
|
||||||
|
else if(index<=path.length() && !S_ISDIR(st.st_mode)) return -ENOTDIR;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PathResolution::followSymlink(string& path)
|
||||||
|
{
|
||||||
|
if(++linksFollowed>=maxLinkToFollow) return -ELOOP;
|
||||||
|
string target;
|
||||||
|
{
|
||||||
|
StringPart sp(path,index-1,indexIntoFs);
|
||||||
|
if(int res=fs->readlink(sp,target)<0) return res;
|
||||||
|
}
|
||||||
|
if(target.empty()) return -ENOENT; //Should not happen
|
||||||
|
if(target[0]=='/')
|
||||||
|
{
|
||||||
|
//Symlink is absolute
|
||||||
|
size_t newPathLen=target.length()+path.length()-index+1;
|
||||||
|
if(newPathLen>PATH_MAX) return -ENAMETOOLONG;
|
||||||
|
string newPath;
|
||||||
|
newPath.reserve(newPathLen);
|
||||||
|
newPath=target;
|
||||||
|
if(index<=path.length())
|
||||||
|
newPath.insert(newPath.length(),path,index-1,string::npos);
|
||||||
|
path.swap(newPath);
|
||||||
|
fs=root;
|
||||||
|
syms=root->supportsSymlinks();
|
||||||
|
index=1;
|
||||||
|
indexIntoFs=1;
|
||||||
|
depthIntoFs=1;
|
||||||
|
} else {
|
||||||
|
//Symlink is relative
|
||||||
|
size_t removeStart=path.find_last_of('/',index-2);
|
||||||
|
size_t newPathLen=path.length()-(index-removeStart-2)+target.length();
|
||||||
|
if(newPathLen>PATH_MAX) return -ENAMETOOLONG;
|
||||||
|
string newPath;
|
||||||
|
newPath.reserve(newPathLen);
|
||||||
|
newPath.insert(0,path,0,removeStart+1);
|
||||||
|
newPath+=target;
|
||||||
|
if(index<=path.length())
|
||||||
|
newPath.insert(newPath.length(),path,index-1,string::npos);
|
||||||
|
path.swap(newPath);
|
||||||
|
index=removeStart+1;
|
||||||
|
depthIntoFs--;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PathResolution::recursiveFindFs(string& path)
|
||||||
|
{
|
||||||
|
depthIntoFs=1;
|
||||||
|
size_t backIndex=index;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
backIndex=path.find_last_of('/',backIndex-1);
|
||||||
|
if(backIndex==string::npos) return -ENOENT; //should not happpen
|
||||||
|
if(backIndex==0)
|
||||||
|
{
|
||||||
|
fs=root;
|
||||||
|
indexIntoFs=1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
map<StringPart,intrusive_ref_ptr<FilesystemBase> >::const_iterator it;
|
||||||
|
it=filesystems.find(StringPart(path,backIndex));
|
||||||
|
if(it!=filesystems.end())
|
||||||
|
{
|
||||||
|
fs=it->second;
|
||||||
|
indexIntoFs=backIndex+1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
depthIntoFs++;
|
||||||
|
}
|
||||||
|
syms=fs->supportsSymlinks();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// class FilesystemManager
|
||||||
|
//
|
||||||
|
|
||||||
|
FilesystemManager& FilesystemManager::instance()
|
||||||
|
{
|
||||||
|
static FilesystemManager instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilesystemManager::kmount(const char* path, intrusive_ref_ptr<FilesystemBase> fs)
|
||||||
|
{
|
||||||
|
if(path==0 || path[0]=='\0' || fs==0) return -EFAULT;
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
size_t len=strlen(path);
|
||||||
|
if(len>PATH_MAX) return -ENAMETOOLONG;
|
||||||
|
string temp(path);
|
||||||
|
if(!(temp=="/" && filesystems.empty())) //Skip check when mounting /
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
if(int result=statHelper(temp,&st,false)) return result;
|
||||||
|
if(!S_ISDIR(st.st_mode)) return -ENOTDIR;
|
||||||
|
string parent=temp+"/..";
|
||||||
|
if(int result=statHelper(parent,&st,false)) return result;
|
||||||
|
fs->setParentFsMountpointInode(st.st_ino);
|
||||||
|
}
|
||||||
|
if(filesystems.insert(make_pair(StringPart(temp),fs)).second==false)
|
||||||
|
return -EBUSY; //Means already mounted
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilesystemManager::umount(const char* path, bool force)
|
||||||
|
{
|
||||||
|
typedef
|
||||||
|
typename map<StringPart,intrusive_ref_ptr<FilesystemBase> >::iterator fsIt;
|
||||||
|
|
||||||
|
if(path==0 || path[0]=='\0') return -ENOENT;
|
||||||
|
size_t len=strlen(path);
|
||||||
|
if(len>PATH_MAX) return -ENAMETOOLONG;
|
||||||
|
Lock<FastMutex> l(mutex); //A reader-writer lock would be better
|
||||||
|
fsIt it=filesystems.find(StringPart(path));
|
||||||
|
if(it==filesystems.end()) return -EINVAL;
|
||||||
|
|
||||||
|
//This finds all the filesystems that have to be recursively umounted
|
||||||
|
//to umount the required filesystem. For example, if /path and /path/path2
|
||||||
|
//are filesystems, umounting /path must umount also /path/path2
|
||||||
|
vector<fsIt> fsToUmount;
|
||||||
|
for(fsIt it2=filesystems.begin();it2!=filesystems.end();++it2)
|
||||||
|
if(it2->first.startsWith(it->first)) fsToUmount.push_back(it2);
|
||||||
|
|
||||||
|
//Now look into all file descriptor tables if there are open files in the
|
||||||
|
//filesystems to umount. If there are, return busy. This is an heavy
|
||||||
|
//operation given the way the filesystem data structure is organized, but
|
||||||
|
//it has been done like this to minimize the size of an entry in the file
|
||||||
|
//descriptor table (4 bytes), and because umount happens infrequently.
|
||||||
|
//Note that since we are locking the same mutex used by resolvePath(),
|
||||||
|
//other threads can't open new files concurrently while we check
|
||||||
|
#ifdef WITH_PROCESSES
|
||||||
|
list<FileDescriptorTable*>::iterator it3;
|
||||||
|
for(it3=fileTables.begin();it3!=fileTables.end();++it3)
|
||||||
|
{
|
||||||
|
for(int i=0;i<MAX_OPEN_FILES;i++)
|
||||||
|
{
|
||||||
|
intrusive_ref_ptr<FileBase> file=(*it3)->getFile(i);
|
||||||
|
if(!file) continue;
|
||||||
|
vector<fsIt>::iterator it4;
|
||||||
|
for(it4=fsToUmount.begin();it4!=fsToUmount.end();++it4)
|
||||||
|
{
|
||||||
|
if(file->getParent()!=(*it4)->second) continue;
|
||||||
|
if(force==false) return -EBUSY;
|
||||||
|
(*it3)->close(i); //If forced umount, close the file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else //WITH_PROCESSES
|
||||||
|
for(int i=0;i<MAX_OPEN_FILES;i++)
|
||||||
|
{
|
||||||
|
intrusive_ref_ptr<FileBase> file=getFileDescriptorTable().getFile(i);
|
||||||
|
if(!file) continue;
|
||||||
|
vector<fsIt>::iterator it4;
|
||||||
|
for(it4=fsToUmount.begin();it4!=fsToUmount.end();++it4)
|
||||||
|
{
|
||||||
|
if(file->getParent()!=(*it4)->second) continue;
|
||||||
|
if(force==false) return -EBUSY;
|
||||||
|
getFileDescriptorTable().close(i);//If forced umount, close the file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif //WITH_PROCESSES
|
||||||
|
|
||||||
|
//Now there should be no more files belonging to the filesystems to umount,
|
||||||
|
//but check if it is really so, as there is a possible race condition
|
||||||
|
//which is the read/close,umount where one thread performs a read (or write)
|
||||||
|
//operation on a file descriptor, it gets preempted and another thread does
|
||||||
|
//a close on that descriptor and an umount of the filesystem. Also, this may
|
||||||
|
//happen in case of a forced umount. In such a case there is no entry in
|
||||||
|
//the descriptor table (as close was called) but the operation is still
|
||||||
|
//ongoing.
|
||||||
|
vector<fsIt>::iterator it5;
|
||||||
|
const int maxRetry=3; //Retry up to three times
|
||||||
|
for(int i=0;i<maxRetry;i++)
|
||||||
|
{
|
||||||
|
bool failed=false;
|
||||||
|
for(it5=fsToUmount.begin();it5!=fsToUmount.end();++it5)
|
||||||
|
{
|
||||||
|
if((*it5)->second->areAllFilesClosed()) continue;
|
||||||
|
if(force==false) return -EBUSY;
|
||||||
|
failed=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!failed) break;
|
||||||
|
if(i==maxRetry-1) return -EBUSY; //Failed to umount even if forced
|
||||||
|
Thread::sleep(1000); //Wait to see if the filesystem operation completes
|
||||||
|
}
|
||||||
|
|
||||||
|
//It is now safe to umount all filesystems
|
||||||
|
for(it5=fsToUmount.begin();it5!=fsToUmount.end();++it5)
|
||||||
|
filesystems.erase(*it5);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilesystemManager::umountAll()
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
#ifdef WITH_PROCESSES
|
||||||
|
list<FileDescriptorTable*>::iterator it;
|
||||||
|
for(it=fileTables.begin();it!=fileTables.end();++it) (*it)->closeAll();
|
||||||
|
#else //WITH_PROCESSES
|
||||||
|
getFileDescriptorTable().closeAll();
|
||||||
|
#endif //WITH_PROCESSES
|
||||||
|
filesystems.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolvedPath FilesystemManager::resolvePath(string& path, bool followLastSymlink)
|
||||||
|
{
|
||||||
|
//see man path_resolution. This code supports arbitrarily mounted
|
||||||
|
//filesystems, symbolic links resolution, but no hardlinks to directories
|
||||||
|
if(path.length()>PATH_MAX) return ResolvedPath(-ENAMETOOLONG);
|
||||||
|
if(path.empty() || path[0]!='/') return ResolvedPath(-ENOENT);
|
||||||
|
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
PathResolution pr(filesystems);
|
||||||
|
return pr.resolvePath(path,followLastSymlink);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilesystemManager::unlinkHelper(string& path)
|
||||||
|
{
|
||||||
|
//Do everything while keeping the mutex locked to prevent someone to
|
||||||
|
//concurrently mount a filesystem on the directory we're unlinking
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
ResolvedPath openData=resolvePath(path,true);
|
||||||
|
if(openData.result<0) return openData.result;
|
||||||
|
//After resolvePath() so path is in canonical form and symlinks are followed
|
||||||
|
if(filesystems.find(StringPart(path))!=filesystems.end()) return -EBUSY;
|
||||||
|
StringPart sp(path,string::npos,openData.off);
|
||||||
|
return openData.fs->unlink(sp);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilesystemManager::statHelper(string& path, struct stat *pstat, bool f)
|
||||||
|
{
|
||||||
|
ResolvedPath openData=resolvePath(path,f);
|
||||||
|
if(openData.result<0) return openData.result;
|
||||||
|
StringPart sp(path,string::npos,openData.off);
|
||||||
|
return openData.fs->lstat(sp,pstat);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilesystemManager::renameHelper(string& oldPath, string& newPath)
|
||||||
|
{
|
||||||
|
//Do everything while keeping the mutex locked to prevent someone to
|
||||||
|
//concurrently mount a filesystem on the directory we're renaming
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
ResolvedPath oldOpenData=resolvePath(oldPath,true);
|
||||||
|
if(oldOpenData.result<0) return oldOpenData.result;
|
||||||
|
ResolvedPath newOpenData=resolvePath(newPath,true);
|
||||||
|
if(newOpenData.result<0) return newOpenData.result;
|
||||||
|
|
||||||
|
if(oldOpenData.fs!=newOpenData.fs) return -EXDEV; //Can't rename across fs
|
||||||
|
|
||||||
|
//After resolvePath() so path is in canonical form and symlinks are followed
|
||||||
|
if(filesystems.find(StringPart(oldPath))!=filesystems.end()) return -EBUSY;
|
||||||
|
if(filesystems.find(StringPart(newPath))!=filesystems.end()) return -EBUSY;
|
||||||
|
|
||||||
|
StringPart oldSp(oldPath,string::npos,oldOpenData.off);
|
||||||
|
StringPart newSp(newPath,string::npos,newOpenData.off);
|
||||||
|
|
||||||
|
//Can't rename a directory into a subdirectory of itself
|
||||||
|
if(newSp.startsWith(oldSp)) return -EINVAL;
|
||||||
|
return oldOpenData.fs->rename(oldSp,newSp);
|
||||||
|
}
|
||||||
|
|
||||||
|
short int FilesystemManager::getFilesystemId()
|
||||||
|
{
|
||||||
|
return atomicAddExchange(&devCount,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilesystemManager::devCount=1;
|
||||||
|
|
||||||
|
#ifdef WITH_DEVFS
|
||||||
|
intrusive_ref_ptr<DevFs> //return value is a pointer to DevFs
|
||||||
|
#else //WITH_DEVFS
|
||||||
|
void //return value is void
|
||||||
|
#endif //WITH_DEVFS
|
||||||
|
basicFilesystemSetup(intrusive_ref_ptr<Device> dev)
|
||||||
|
{
|
||||||
|
bootlog("Mounting MountpointFs as / ... ");
|
||||||
|
FilesystemManager& fsm=FilesystemManager::instance();
|
||||||
|
intrusive_ref_ptr<FilesystemBase> rootFs(new MountpointFs);
|
||||||
|
bootlog(fsm.kmount("/",rootFs)==0 ? "Ok\n" : "Failed\n");
|
||||||
|
|
||||||
|
#ifdef WITH_DEVFS
|
||||||
|
bootlog("Mounting DevFs as /dev ... ");
|
||||||
|
StringPart sp("dev");
|
||||||
|
int r1=rootFs->mkdir(sp,0755);
|
||||||
|
intrusive_ref_ptr<DevFs> devfs(new DevFs);
|
||||||
|
int r2=fsm.kmount("/dev",devfs);
|
||||||
|
bool devFsOk=(r1==0 && r2==0);
|
||||||
|
bootlog(devFsOk ? "Ok\n" : "Failed\n");
|
||||||
|
if(!devFsOk) return devfs;
|
||||||
|
fsm.setDevFs(devfs);
|
||||||
|
#endif //WITH_DEVFS
|
||||||
|
|
||||||
|
bootlog("Mounting Fat32Fs as /sd ... ");
|
||||||
|
bool fat32failed=false;
|
||||||
|
intrusive_ref_ptr<FileBase> disk;
|
||||||
|
#ifdef WITH_DEVFS
|
||||||
|
if(dev) devfs->addDevice("sda",dev);
|
||||||
|
StringPart sda("sda");
|
||||||
|
if(devfs->open(disk,sda,O_RDWR,0)<0) fat32failed=true;
|
||||||
|
#else //WITH_DEVFS
|
||||||
|
if(dev && dev->open(disk,intrusive_ref_ptr<FilesystemBase>(0),O_RDWR,0)<0)
|
||||||
|
fat32failed=true;
|
||||||
|
#endif //WITH_DEVFS
|
||||||
|
|
||||||
|
intrusive_ref_ptr<Fat32Fs> fat32;
|
||||||
|
if(fat32failed==false)
|
||||||
|
{
|
||||||
|
fat32=new Fat32Fs(disk);
|
||||||
|
if(fat32->mountFailed()) fat32failed=true;
|
||||||
|
}
|
||||||
|
if(fat32failed==false)
|
||||||
|
{
|
||||||
|
StringPart sd("sd");
|
||||||
|
fat32failed=rootFs->mkdir(sd,0755)!=0;
|
||||||
|
fat32failed=fsm.kmount("/sd",fat32)!=0;
|
||||||
|
}
|
||||||
|
bootlog(fat32failed==0 ? "Ok\n" : "Failed\n");
|
||||||
|
|
||||||
|
#ifdef WITH_DEVFS
|
||||||
|
return devfs;
|
||||||
|
#endif //WITH_DEVFS
|
||||||
|
}
|
||||||
|
|
||||||
|
FileDescriptorTable& getFileDescriptorTable()
|
||||||
|
{
|
||||||
|
#ifdef WITH_PROCESSES
|
||||||
|
//Something like
|
||||||
|
return Thread::getCurrentThread()->getProcess()->getFileTable();
|
||||||
|
#else //WITH_PROCESSES
|
||||||
|
static FileDescriptorTable fileTable; ///< The only file table
|
||||||
|
return fileTable;
|
||||||
|
#endif //WITH_PROCESSES
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //WITH_FILESYSTEM
|
||||||
|
|
@ -0,0 +1,553 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef FILE_ACCESS_H
|
||||||
|
#define FILE_ACCESS_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <list>
|
||||||
|
#include <string>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "file.h"
|
||||||
|
#include "stringpart.h"
|
||||||
|
#include "devfs/devfs.h"
|
||||||
|
#include "kernel/sync.h"
|
||||||
|
#include "kernel/intrusive.h"
|
||||||
|
#include "config/miosix_settings.h"
|
||||||
|
|
||||||
|
#ifdef WITH_FILESYSTEM
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The result of resolvePath().
|
||||||
|
*/
|
||||||
|
class ResolvedPath
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
ResolvedPath() : result(-EINVAL), fs(0), off(0) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* \param result error code
|
||||||
|
*/
|
||||||
|
explicit ResolvedPath(int result) : result(result), fs(0), off(0) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* \param fs filesystem
|
||||||
|
* \param off offset into path where the subpath relative to the current
|
||||||
|
* filesystem starts
|
||||||
|
*/
|
||||||
|
ResolvedPath(intrusive_ref_ptr<FilesystemBase> fs, size_t offset)
|
||||||
|
: result(0), fs(fs), off(offset) {}
|
||||||
|
|
||||||
|
int result; ///< 0 on success, a negative number on failure
|
||||||
|
intrusive_ref_ptr<FilesystemBase> fs; ///< pointer to the filesystem to which the file belongs
|
||||||
|
/// path.c_str()+off is a string containing the relative path into the
|
||||||
|
/// filesystem for the looked up file
|
||||||
|
size_t off;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class maps file descriptors to file objects, allowing to
|
||||||
|
* perform file operations
|
||||||
|
*/
|
||||||
|
class FileDescriptorTable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
FileDescriptorTable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy constructor
|
||||||
|
* \param rhs object to copy from
|
||||||
|
*/
|
||||||
|
FileDescriptorTable(const FileDescriptorTable& rhs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Operator=
|
||||||
|
* \param rhs object to copy from
|
||||||
|
* \return *this
|
||||||
|
*/
|
||||||
|
FileDescriptorTable& operator=(const FileDescriptorTable& rhs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a file
|
||||||
|
* \param name file name
|
||||||
|
* \param flags file open mode
|
||||||
|
* \param mode allows to set file permissions
|
||||||
|
* \return a file descriptor, or a negative number on error
|
||||||
|
*/
|
||||||
|
int open(const char *name, int flags, int mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close a file
|
||||||
|
* \param fd file descriptor to close
|
||||||
|
* \return 0 on success or a negative number on failure
|
||||||
|
*/
|
||||||
|
int close(int fd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close all files
|
||||||
|
*/
|
||||||
|
void closeAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data to the file, if the file supports writing.
|
||||||
|
* \param data the data to write
|
||||||
|
* \param len the number of bytes to write
|
||||||
|
* \return the number of written characters, or a negative number in case
|
||||||
|
* of errors
|
||||||
|
*/
|
||||||
|
ssize_t write(int fd, const void *data, size_t len)
|
||||||
|
{
|
||||||
|
if(data==0) return -EFAULT;
|
||||||
|
//Important, since len is specified by standard to be unsigned, but the
|
||||||
|
//return value has to be signed
|
||||||
|
if(static_cast<ssize_t>(len)<0) return -EINVAL;
|
||||||
|
intrusive_ref_ptr<FileBase> file=getFile(fd);
|
||||||
|
if(!file) return -EBADF;
|
||||||
|
return file->write(data,len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read data from the file, if the file supports reading.
|
||||||
|
* \param data buffer to store read data
|
||||||
|
* \param len the number of bytes to read
|
||||||
|
* \return the number of read characters, or a negative number in case
|
||||||
|
* of errors
|
||||||
|
*/
|
||||||
|
ssize_t read(int fd, void *data, size_t len)
|
||||||
|
{
|
||||||
|
if(data==0) return -EFAULT;
|
||||||
|
//Important, since len is specified by standard to be unsigned, but the
|
||||||
|
//return value has to be signed
|
||||||
|
if(static_cast<ssize_t>(len)<0) return -EINVAL;
|
||||||
|
intrusive_ref_ptr<FileBase> file=getFile(fd);
|
||||||
|
if(!file) return -EBADF;
|
||||||
|
return file->read(data,len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move file pointer, if the file supports random-access.
|
||||||
|
* \param pos offset to sum to the beginning of the file, current position
|
||||||
|
* or end of file, depending on whence
|
||||||
|
* \param whence SEEK_SET, SEEK_CUR or SEEK_END
|
||||||
|
* \return the offset from the beginning of the file if the operation
|
||||||
|
* completed, or a negative number in case of errors
|
||||||
|
*/
|
||||||
|
off_t lseek(int fd, off_t pos, int whence)
|
||||||
|
{
|
||||||
|
intrusive_ref_ptr<FileBase> file=getFile(fd);
|
||||||
|
if(!file) return -EBADF;
|
||||||
|
return file->lseek(pos,whence);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return file information.
|
||||||
|
* \param pstat pointer to stat struct
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
int fstat(int fd, struct stat *pstat) const
|
||||||
|
{
|
||||||
|
if(pstat==0) return -EFAULT;
|
||||||
|
intrusive_ref_ptr<FileBase> file=getFile(fd);
|
||||||
|
if(!file) return -EBADF;
|
||||||
|
return file->fstat(pstat);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the file refers to a terminal.
|
||||||
|
* \return 1 if it is a terminal, 0 if it is not, or a negative number in
|
||||||
|
* case of errors
|
||||||
|
*/
|
||||||
|
int isatty(int fd) const
|
||||||
|
{
|
||||||
|
intrusive_ref_ptr<FileBase> file=getFile(fd);
|
||||||
|
if(!file) return -EBADF;
|
||||||
|
return file->isatty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return file information, follows last symlink
|
||||||
|
* \param path file to stat
|
||||||
|
* \param pstat pointer to stat struct
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
int stat(const char *name, struct stat *pstat)
|
||||||
|
{
|
||||||
|
return statImpl(name,pstat,true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return file information, does not follow last symlink
|
||||||
|
* \param path file to stat
|
||||||
|
* \param pstat pointer to stat struct
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
int lstat(const char *name, struct stat *pstat)
|
||||||
|
{
|
||||||
|
return statImpl(name,pstat,false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform various operations on a file descriptor
|
||||||
|
* \param cmd specifies the operation to perform
|
||||||
|
* \param opt optional argument that some operation require
|
||||||
|
* \return the exact return value depends on CMD, -1 is returned on error
|
||||||
|
*/
|
||||||
|
int fcntl(int fd, int cmd, int opt)
|
||||||
|
{
|
||||||
|
intrusive_ref_ptr<FileBase> file=getFile(fd);
|
||||||
|
if(!file) return -EBADF;
|
||||||
|
return file->fcntl(cmd,opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform various operations on a file descriptor
|
||||||
|
* \param cmd specifies the operation to perform
|
||||||
|
* \param arg optional argument that some operation require
|
||||||
|
* \return the exact return value depends on CMD, -1 is returned on error
|
||||||
|
*/
|
||||||
|
int ioctl(int fd, int cmd, void *arg)
|
||||||
|
{
|
||||||
|
//arg unchecked here, as some ioctl don't use it
|
||||||
|
intrusive_ref_ptr<FileBase> file=getFile(fd);
|
||||||
|
if(!file) return -EBADF;
|
||||||
|
return file->ioctl(cmd,arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List directory content
|
||||||
|
* \param dp dp pointer to a memory buffer where one or more struct dirent
|
||||||
|
* will be placed. dp must be four words aligned.
|
||||||
|
* \param len memory buffer size.
|
||||||
|
* \return the number of bytes read on success, or a negative number on
|
||||||
|
* failure.
|
||||||
|
*/
|
||||||
|
int getdents(int fd, void *dp, int len)
|
||||||
|
{
|
||||||
|
if(dp==0) return -EFAULT;
|
||||||
|
if(reinterpret_cast<unsigned>(dp) & 0x3) return -EFAULT; //Not aligned
|
||||||
|
intrusive_ref_ptr<FileBase> file=getFile(fd);
|
||||||
|
if(!file) return -EBADF;
|
||||||
|
return file->getdents(dp,len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return current directory
|
||||||
|
* \param buf the current directory is stored here
|
||||||
|
* \param len buffer length, if it is not big enough, ERANGE is returned
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
int getcwd(char *buf, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change current directory
|
||||||
|
* \param path new current directory
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
int chdir(const char *name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a directory
|
||||||
|
* \param name directory to create
|
||||||
|
* \param mode directory permissions
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
int mkdir(const char *name, int mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a directory if empty
|
||||||
|
* \param name directory to create
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
int rmdir(const char *name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a file or directory
|
||||||
|
* \param name file or directory to remove
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
int unlink(const char *name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename a file or directory
|
||||||
|
* \param oldName old file name
|
||||||
|
* \param newName new file name
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
int rename(const char *oldName, const char *newName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves an entry in the file descriptor table
|
||||||
|
* \param fd file descriptor, index into the table
|
||||||
|
* \return a refcounted poiter to the file at the desired entry
|
||||||
|
* (which may be empty), or an empty refcounted pointer if the index is
|
||||||
|
* out of bounds
|
||||||
|
*/
|
||||||
|
intrusive_ref_ptr<FileBase> getFile(int fd) const
|
||||||
|
{
|
||||||
|
if(fd<0 || fd>=MAX_OPEN_FILES) return intrusive_ref_ptr<FileBase>();
|
||||||
|
return atomic_load(files+fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor
|
||||||
|
*/
|
||||||
|
~FileDescriptorTable();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Append cwd to path if it is not an absolute path
|
||||||
|
* \param path an absolute or relative path, must not be null
|
||||||
|
* \return an absolute path, or an empty string if the path would exceed
|
||||||
|
* PATH_MAX
|
||||||
|
*/
|
||||||
|
std::string absolutePath(const char *path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return file information (implements both stat and lstat)
|
||||||
|
* \param path file to stat
|
||||||
|
* \param pstat pointer to stat struct
|
||||||
|
* \param f true to follow last synlink (stat),
|
||||||
|
* false to not follow it (lstat)
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
int statImpl(const char *name, struct stat *pstat, bool f);
|
||||||
|
|
||||||
|
FastMutex mutex; ///< Locks on writes to file object pointers, not on accesses
|
||||||
|
|
||||||
|
std::string cwd; ///< Current working directory
|
||||||
|
|
||||||
|
/// Holds the mapping between fd and file objects
|
||||||
|
intrusive_ref_ptr<FileBase> files[MAX_OPEN_FILES];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class contains information on all the mounted filesystems
|
||||||
|
*/
|
||||||
|
class FilesystemManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \return the instance of the filesystem manager (singleton)
|
||||||
|
*/
|
||||||
|
static FilesystemManager& instance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Low level mount operation, meant to be used only inside the kernel,
|
||||||
|
* and board support packages. It is the only mount operation that can
|
||||||
|
* mount the root filesystem.
|
||||||
|
* \param path path where to mount the filesystem
|
||||||
|
* \param fs filesystem to mount. Ownership of the pointer is transferred
|
||||||
|
* to the FilesystemManager class
|
||||||
|
* \return 0 on success, a negative number on failure
|
||||||
|
*/
|
||||||
|
int kmount(const char *path, intrusive_ref_ptr<FilesystemBase> fs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unmounts a filesystem
|
||||||
|
* \param path path to a filesytem
|
||||||
|
* \param force true to umount the filesystem even if busy
|
||||||
|
* \return 0 on success, or a negative number on error
|
||||||
|
*/
|
||||||
|
int umount(const char *path, bool force=false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Umount all filesystems, to be called before system shutdown or reboot
|
||||||
|
*/
|
||||||
|
void umountAll();
|
||||||
|
|
||||||
|
#ifdef WITH_DEVFS
|
||||||
|
/**
|
||||||
|
* \return a pointer to the devfs, useful to add other device files
|
||||||
|
*/
|
||||||
|
intrusive_ref_ptr<DevFs> getDevFs() const { return atomic_load(&devFs); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by basicFilesystemSetup() or directly by the BSP to set the
|
||||||
|
* pointer returned by getDevFs()
|
||||||
|
* \param dev pointer to the DevFs
|
||||||
|
*/
|
||||||
|
void setDevFs(intrusive_ref_ptr<DevFs> dev)
|
||||||
|
{
|
||||||
|
atomic_store(&devFs,dev);
|
||||||
|
}
|
||||||
|
#endif //WITH_DEVFS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a path to identify the filesystem it belongs
|
||||||
|
* \param path an absolute path name, that must start with '/'. Note that
|
||||||
|
* this is an inout parameter, the string is modified so as to return the
|
||||||
|
* full resolved path. In particular, the returned string differs from the
|
||||||
|
* passed one by not containing useless path components, such as "/./" and
|
||||||
|
* "//", by not containing back path componenets ("/../"), and may be
|
||||||
|
* entirely different from the passed one if a symlink was encountered
|
||||||
|
* during name resolution. The use of an inout parameter is to minimize
|
||||||
|
* the number of copies of the path string, optimizing for speed and size
|
||||||
|
* in the common case, but also means that a copy of the original string
|
||||||
|
* needs to be made if the original has to be used later.
|
||||||
|
* \param followLastSymlink true if the symlink in the last path component
|
||||||
|
*(the one that does not end with a /, if it exists, has to be followed)
|
||||||
|
* \return the resolved path
|
||||||
|
*/
|
||||||
|
ResolvedPath resolvePath(std::string& path, bool followLastSymlink=true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Helper function to unlink a file or directory. Only meant to be used by
|
||||||
|
* FileDescriptorTable::unlink()
|
||||||
|
* \param path path of file or directory to unlink
|
||||||
|
* \return 0 on success, or a neagtive number on failure
|
||||||
|
*/
|
||||||
|
int unlinkHelper(std::string& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Helper function to stat a file or directory. Only meant to be used by
|
||||||
|
* FileDescriptorTable::statImpl()
|
||||||
|
* \param path path of file or directory to stat
|
||||||
|
* \param pstat pointer to stat struct
|
||||||
|
* \param f f true to follow last synlink (stat),
|
||||||
|
* false to not follow it (lstat)
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
int statHelper(std::string& path, struct stat *pstat, bool f);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Helper function to unlink a file or directory. Only meant to be used by
|
||||||
|
* FileDescriptorTable::unlink()
|
||||||
|
* \param oldPath path of file or directory to unlink
|
||||||
|
* \param newPath path of file or directory to unlink
|
||||||
|
* \return 0 on success, or a neagtive number on failure
|
||||||
|
*/
|
||||||
|
int renameHelper(std::string& oldPath, std::string& newPath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Called by FileDescriptorTable's constructor. Never call this function
|
||||||
|
* from user code.
|
||||||
|
*/
|
||||||
|
void addFileDescriptorTable(FileDescriptorTable *fdt)
|
||||||
|
{
|
||||||
|
#ifdef WITH_PROCESSES
|
||||||
|
if(isKernelRunning())
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
fileTables.push_back(fdt);
|
||||||
|
} else {
|
||||||
|
//This function is also called before the kernel is started,
|
||||||
|
//and in this case it is forbidden to lock mutexes
|
||||||
|
fileTables.push_back(fdt);
|
||||||
|
}
|
||||||
|
#endif //WITH_PROCESSES
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Called by FileDescriptorTable's constructor. Never call this function
|
||||||
|
* from user code.
|
||||||
|
*/
|
||||||
|
void removeFileDescriptorTable(FileDescriptorTable *fdt)
|
||||||
|
{
|
||||||
|
#ifdef WITH_PROCESSES
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
fileTables.remove(fdt);
|
||||||
|
#endif //WITH_PROCESSES
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* \return an unique id used to identify a filesystem, mostly for filling
|
||||||
|
* in the st_dev field when stat is called.
|
||||||
|
*/
|
||||||
|
static short int getFilesystemId();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Constructor, private as it is a singleton
|
||||||
|
*/
|
||||||
|
FilesystemManager() : mutex(FastMutex::RECURSIVE) {}
|
||||||
|
|
||||||
|
FilesystemManager(const FilesystemManager&);
|
||||||
|
FilesystemManager& operator=(const FilesystemManager&);
|
||||||
|
|
||||||
|
FastMutex mutex; ///< To protect against concurrent access
|
||||||
|
|
||||||
|
/// Mounted filesystem
|
||||||
|
std::map<StringPart,intrusive_ref_ptr<FilesystemBase> > filesystems;
|
||||||
|
|
||||||
|
#ifdef WITH_PROCESSES
|
||||||
|
std::list<FileDescriptorTable*> fileTables; ///< Process file tables
|
||||||
|
#endif //WITH_PROCESSES
|
||||||
|
#ifdef WITH_DEVFS
|
||||||
|
intrusive_ref_ptr<DevFs> devFs;
|
||||||
|
#endif //WITH_DEVFS
|
||||||
|
|
||||||
|
static int devCount; ///< For assigning filesystemId to filesystems
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a simplified function to mount the root and /dev filesystems,
|
||||||
|
* meant to be called from bspInit2(). It mounts a MountpointFs as root, then
|
||||||
|
* creates a /dev directory, and mounts /dev there. It also takes the passed
|
||||||
|
* device and if it is not null it adds the device di DevFs as /dev/sda.
|
||||||
|
* Last, it attempts to mount /dev/sda at /sd as a Fat32 filesystem.
|
||||||
|
* In case the bsp needs another filesystem setup, such as having a fat32
|
||||||
|
* filesystem as /, this function can't be used, but instead the bsp needs to
|
||||||
|
* mount the filesystems manually.
|
||||||
|
* \param dev disk device that will be added as /dev/sda and mounted on /sd
|
||||||
|
* \return a pointer to the DevFs, so as to be able to add other device files,
|
||||||
|
* but only if WITH_DEVFS is defined
|
||||||
|
*/
|
||||||
|
#ifdef WITH_DEVFS
|
||||||
|
intrusive_ref_ptr<DevFs> //return value is a pointer to DevFs
|
||||||
|
#else //WITH_DEVFS
|
||||||
|
void //return value is void
|
||||||
|
#endif //WITH_DEVFS
|
||||||
|
basicFilesystemSetup(intrusive_ref_ptr<Device> dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return a pointer to the file descriptor table associated with the
|
||||||
|
* current process.
|
||||||
|
*/
|
||||||
|
FileDescriptorTable& getFileDescriptorTable();
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //WITH_FILESYSTEM
|
||||||
|
|
||||||
|
#endif //FILE_ACCESS_H
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef IOCTL_H
|
||||||
|
#define IOCTL_H
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
enum Ioctl
|
||||||
|
{
|
||||||
|
IOCTL_SYNC=100,
|
||||||
|
IOCTL_TCGETATTR=101,
|
||||||
|
IOCTL_TCSETATTR_NOW=102,
|
||||||
|
IOCTL_TCSETATTR_FLUSH=103,
|
||||||
|
IOCTL_TCSETATTR_DRAIN=104,
|
||||||
|
IOCTL_FLUSH=105
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //IOCTL_H
|
||||||
|
|
@ -0,0 +1,204 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "mountpointfs.h"
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include "filesystem/stringpart.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
#ifdef WITH_FILESYSTEM
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directory class for MountpointFs
|
||||||
|
*/
|
||||||
|
class MountpointFsDirectory : public DirectoryBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \param parent parent filesystem
|
||||||
|
* \param mutex mutex to lock when accessing the file map
|
||||||
|
* \param dirs file map
|
||||||
|
* \param root true if we're listing the root directory
|
||||||
|
* \param currentInode inode of the directory we're listing
|
||||||
|
* \param parentInode inode of the parent directory
|
||||||
|
*/
|
||||||
|
MountpointFsDirectory(intrusive_ref_ptr<FilesystemBase> parent,
|
||||||
|
FastMutex& mutex, map<StringPart,int>& dirs, bool root,
|
||||||
|
int currentInode, int parentInode)
|
||||||
|
: DirectoryBase(parent), mutex(mutex), dirs(dirs),
|
||||||
|
currentInode(currentInode), parentInode(parentInode),
|
||||||
|
first(true), last(false)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(root && dirs.empty()==false) currentItem=dirs.begin()->first.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Also directories can be opened as files. In this case, this system call
|
||||||
|
* allows to retrieve directory entries.
|
||||||
|
* \param dp pointer to a memory buffer where one or more struct dirent
|
||||||
|
* will be placed. dp must be four words aligned.
|
||||||
|
* \param len memory buffer size.
|
||||||
|
* \return the number of bytes read on success, or a negative number on
|
||||||
|
* failure.
|
||||||
|
*/
|
||||||
|
virtual int getdents(void *dp, int len);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FastMutex& mutex; ///< Mutex of parent class
|
||||||
|
std::map<StringPart,int>& dirs; ///< Directory entries of parent class
|
||||||
|
string currentItem; ///< First unhandled item in directory
|
||||||
|
int currentInode,parentInode; ///< Inodes of . and ..
|
||||||
|
|
||||||
|
bool first; ///< True if first time getdents is called
|
||||||
|
bool last; ///< True if directory has ended
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// class MountpointFsDirctory
|
||||||
|
//
|
||||||
|
|
||||||
|
int MountpointFsDirectory::getdents(void *dp, int len)
|
||||||
|
{
|
||||||
|
if(len<minimumBufferSize) return -EINVAL;
|
||||||
|
if(last) return 0;
|
||||||
|
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
char *begin=reinterpret_cast<char*>(dp);
|
||||||
|
char *buffer=begin;
|
||||||
|
char *end=buffer+len;
|
||||||
|
if(first)
|
||||||
|
{
|
||||||
|
first=false;
|
||||||
|
addDefaultEntries(&buffer,currentInode,parentInode);
|
||||||
|
}
|
||||||
|
if(currentItem.empty()==false)
|
||||||
|
{
|
||||||
|
map<StringPart,int>::iterator it=dirs.find(StringPart(currentItem));
|
||||||
|
//Someone deleted the exact directory entry we had saved (unlikely)
|
||||||
|
if(it==dirs.end()) return -EBADF;
|
||||||
|
for(;it!=dirs.end();++it)
|
||||||
|
{
|
||||||
|
if(addEntry(&buffer,end,it->second,DT_DIR,it->first)>0) continue;
|
||||||
|
//Buffer finished
|
||||||
|
currentItem=it->first.c_str();
|
||||||
|
return buffer-begin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addTerminatingEntry(&buffer,end);
|
||||||
|
last=true;
|
||||||
|
return buffer-begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// class MountpointFs
|
||||||
|
//
|
||||||
|
|
||||||
|
int MountpointFs::open(intrusive_ref_ptr<FileBase>& file, StringPart& name,
|
||||||
|
int flags, int mode)
|
||||||
|
{
|
||||||
|
if(flags & (O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC))
|
||||||
|
return -EACCES;
|
||||||
|
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
int currentInode=rootDirInode;
|
||||||
|
int parentInode=parentFsMountpointInode;
|
||||||
|
if(name.empty()==false)
|
||||||
|
{
|
||||||
|
map<StringPart,int>::iterator it=dirs.find(name);
|
||||||
|
if(it==dirs.end()) return -EACCES;
|
||||||
|
parentInode=currentInode;
|
||||||
|
currentInode=it->second;
|
||||||
|
}
|
||||||
|
file=intrusive_ref_ptr<FileBase>(
|
||||||
|
new MountpointFsDirectory(
|
||||||
|
shared_from_this(),mutex,dirs,name.empty(),currentInode,parentInode));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MountpointFs::lstat(StringPart& name, struct stat *pstat)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
map<StringPart,int>::iterator it;
|
||||||
|
if(name.empty()==false)
|
||||||
|
{
|
||||||
|
it=dirs.find(name);
|
||||||
|
if(it==dirs.end()) return -ENOENT;
|
||||||
|
}
|
||||||
|
memset(pstat,0,sizeof(struct stat));
|
||||||
|
pstat->st_dev=filesystemId;
|
||||||
|
pstat->st_ino=name.empty() ? rootDirInode : it->second;
|
||||||
|
pstat->st_mode=S_IFDIR | 0755; //drwxr-xr-x
|
||||||
|
pstat->st_nlink=1;
|
||||||
|
pstat->st_blksize=512;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MountpointFs::unlink(StringPart& name)
|
||||||
|
{
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MountpointFs::rename(StringPart& oldName, StringPart& newName)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
map<StringPart,int>::iterator it=dirs.find(oldName);
|
||||||
|
if(it==dirs.end()) return -ENOENT;
|
||||||
|
for(unsigned int i=0;i<newName.length();i++)
|
||||||
|
if(newName[i]=='/')
|
||||||
|
return -EACCES; //MountpointFs does not support subdirectories
|
||||||
|
dirs[newName]=it->second;
|
||||||
|
dirs.erase(it);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MountpointFs::mkdir(StringPart& name, int mode)
|
||||||
|
{
|
||||||
|
for(unsigned int i=0;i<name.length();i++)
|
||||||
|
if(name[i]=='/')
|
||||||
|
return -EACCES; //MountpointFs does not support subdirectories
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(dirs.insert(make_pair(name,inodeCount)).second==false) return -EEXIST;
|
||||||
|
inodeCount++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MountpointFs::rmdir(StringPart& name)
|
||||||
|
{
|
||||||
|
Lock<FastMutex> l(mutex);
|
||||||
|
if(dirs.erase(name)==1) return 0;
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //WITH_FILESYSTEM
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef MOUNTPOINTFS_H
|
||||||
|
#define MOUNTPOINTFS_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include "filesystem/file.h"
|
||||||
|
#include "kernel/sync.h"
|
||||||
|
#include "config/miosix_settings.h"
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
#ifdef WITH_FILESYSTEM
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MountpointFs is a special filesystem whose purpose is to create directories
|
||||||
|
* to be used as mountpoints for other filesystems.
|
||||||
|
*/
|
||||||
|
class MountpointFs : public FilesystemBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
MountpointFs() : mutex(FastMutex::RECURSIVE), inodeCount(rootDirInode+1) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a file
|
||||||
|
* \param file the file object will be stored here, if the call succeeds
|
||||||
|
* \param name the name of the file to open, relative to the local
|
||||||
|
* filesystem
|
||||||
|
* \param flags file flags (open for reading, writing, ...)
|
||||||
|
* \param mode file permissions
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int open(intrusive_ref_ptr<FileBase>& file, StringPart& name,
|
||||||
|
int flags, int mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain information on a file, identified by a path name. Does not follow
|
||||||
|
* symlinks
|
||||||
|
* \param name path name, relative to the local filesystem
|
||||||
|
* \param pstat file information is stored here
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int lstat(StringPart& name, struct stat *pstat);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a file or directory
|
||||||
|
* \param name path name of file or directory to remove
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int unlink(StringPart& name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename a file or directory
|
||||||
|
* \param oldName old file name
|
||||||
|
* \param newName new file name
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int rename(StringPart& oldName, StringPart& newName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a directory
|
||||||
|
* \param name directory name
|
||||||
|
* \param mode directory permissions
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int mkdir(StringPart& name, int mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a directory if empty
|
||||||
|
* \param name directory name
|
||||||
|
* \return 0 on success, or a negative number on failure
|
||||||
|
*/
|
||||||
|
virtual int rmdir(StringPart& name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FastMutex mutex;
|
||||||
|
std::map<StringPart,int> dirs;
|
||||||
|
int inodeCount;
|
||||||
|
static const int rootDirInode=1;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //WITH_FILESYSTEM
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //MOUNTPOINTFS_H
|
||||||
|
|
@ -0,0 +1,185 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
//Makes memrchr available in newer GCCs
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "stringpart.h"
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
//
|
||||||
|
// class StringPart
|
||||||
|
//
|
||||||
|
|
||||||
|
StringPart::StringPart(string& str, size_t idx, size_t off)
|
||||||
|
: str(&str), index(idx), offset(off), saved('\0'), owner(false),
|
||||||
|
type(CPPSTR)
|
||||||
|
{
|
||||||
|
if(index==string::npos || index>=str.length()) index=str.length();
|
||||||
|
else {
|
||||||
|
saved=str[index];
|
||||||
|
str[index]='\0';
|
||||||
|
}
|
||||||
|
offset=min(offset,index);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringPart::StringPart(char* s, size_t idx, size_t off)
|
||||||
|
: cstr(s), index(idx), offset(off), saved('\0'), owner(false),
|
||||||
|
type(CSTR)
|
||||||
|
{
|
||||||
|
assert(cstr); //Passed pointer can't be null
|
||||||
|
size_t len=strlen(cstr);
|
||||||
|
if(index==string::npos || index>=len) index=len;
|
||||||
|
else {
|
||||||
|
saved=cstr[index];
|
||||||
|
cstr[index]='\0';
|
||||||
|
}
|
||||||
|
offset=min(offset,index);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringPart::StringPart(const char* s)
|
||||||
|
: ccstr(s), offset(0), saved('\0'), owner(false), type(CCSTR)
|
||||||
|
{
|
||||||
|
assert(ccstr); //Passed pointer can't be null
|
||||||
|
index=strlen(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringPart::StringPart(StringPart& rhs, size_t idx, size_t off)
|
||||||
|
: saved('\0'), owner(false), type(rhs.type)
|
||||||
|
{
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case CSTR:
|
||||||
|
this->cstr=rhs.cstr;
|
||||||
|
break;
|
||||||
|
case CCSTR:
|
||||||
|
type=CSTR; //To make a substring of a CCSTR we need to make a copy
|
||||||
|
if(rhs.empty()==false) assign(rhs); else cstr=&saved;
|
||||||
|
break;
|
||||||
|
case CPPSTR:
|
||||||
|
this->str=rhs.str;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(idx!=string::npos && idx<rhs.length())
|
||||||
|
{
|
||||||
|
index=rhs.offset+idx;//Make index relative to beginning of original str
|
||||||
|
if(type==CPPSTR)
|
||||||
|
{
|
||||||
|
saved=(*str)[index];
|
||||||
|
(*str)[index]='\0';
|
||||||
|
} else {
|
||||||
|
saved=cstr[index];
|
||||||
|
cstr[index]='\0';
|
||||||
|
}
|
||||||
|
} else index=rhs.index;
|
||||||
|
offset=min(rhs.offset+off,index); //Works for CCSTR as offset is always zero
|
||||||
|
}
|
||||||
|
|
||||||
|
StringPart::StringPart(const StringPart& rhs)
|
||||||
|
: cstr(&saved), index(0), offset(0), saved('\0'), owner(false),
|
||||||
|
type(CSTR)
|
||||||
|
{
|
||||||
|
if(rhs.empty()==false) assign(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringPart& StringPart::operator= (const StringPart& rhs)
|
||||||
|
{
|
||||||
|
if(this==&rhs) return *this; //Self assignment
|
||||||
|
//Don't forget that we may own a pointer to someone else's string,
|
||||||
|
//so always clear() to detach from a string on assignment!
|
||||||
|
clear();
|
||||||
|
if(rhs.empty()==false) assign(rhs);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StringPart::startsWith(const StringPart& rhs) const
|
||||||
|
{
|
||||||
|
if(this->length()<rhs.length()) return false;
|
||||||
|
return memcmp(this->c_str(),rhs.c_str(),rhs.length())==0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t StringPart::findLastOf(char c) const
|
||||||
|
{
|
||||||
|
const char *begin=c_str();
|
||||||
|
//Not strrchr() to take advantage of knowing the string length
|
||||||
|
void *index=memrchr(begin,c,length());
|
||||||
|
if(index==0) return std::string::npos;
|
||||||
|
return reinterpret_cast<char*>(index)-begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *StringPart::c_str() const
|
||||||
|
{
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case CSTR: return cstr+offset;
|
||||||
|
case CCSTR: return ccstr; //Offset always 0
|
||||||
|
default: return str->c_str()+offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char StringPart::operator[] (size_t index) const
|
||||||
|
{
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case CSTR: return cstr[offset+index];
|
||||||
|
case CCSTR: return ccstr[index]; //Offset always 0
|
||||||
|
default: return (*str)[offset+index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StringPart::clear()
|
||||||
|
{
|
||||||
|
if(type==CSTR)
|
||||||
|
{
|
||||||
|
cstr[index]=saved;//Worst case we'll overwrite terminating \0 with an \0
|
||||||
|
if(owner) delete[] cstr;
|
||||||
|
} else if(type==CPPSTR) {
|
||||||
|
if(index!=str->length()) (*str)[index]=saved;
|
||||||
|
if(owner) delete str;
|
||||||
|
} //For CCSTR there's nothing to do
|
||||||
|
cstr=&saved; //Reusing saved as an empty string
|
||||||
|
saved='\0';
|
||||||
|
index=offset=0;
|
||||||
|
owner=false;
|
||||||
|
type=CSTR;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StringPart::assign(const StringPart& rhs)
|
||||||
|
{
|
||||||
|
cstr=new char[rhs.length()+1];
|
||||||
|
strcpy(cstr,rhs.c_str());
|
||||||
|
index=rhs.length();
|
||||||
|
offset=0;
|
||||||
|
owner=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,216 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef STRINGPART_H
|
||||||
|
#define STRINGPART_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* This class is used to take a substring of a string containing a file path
|
||||||
|
* without creating a copy, and therefore requiring additional memory
|
||||||
|
* allocation.
|
||||||
|
*
|
||||||
|
* When parsing a path like "/home/test/directory/file" it is often necessary
|
||||||
|
* to create substrings at the path component boundaries, such as "/home/test".
|
||||||
|
* In this case, it is possible to temporarily make a substring by replacing a
|
||||||
|
* '/' with a '\0'. The std::string will not 'forget' its orginal size, and
|
||||||
|
* when the '\0' will be converted back to a '/', the string will look identical
|
||||||
|
* to the previous one.
|
||||||
|
*
|
||||||
|
* This is an optimization made for filesystem mountpoint lookups, and is not
|
||||||
|
* believed to be useful outside of that purpose. Given that in Miosix, the
|
||||||
|
* mountpoints are stored in a map, this class supports operator< to correctly
|
||||||
|
* lookup mountpoints in the map.
|
||||||
|
*/
|
||||||
|
class StringPart
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Default constructor
|
||||||
|
*/
|
||||||
|
StringPart() : cstr(&saved), index(0), offset(0), saved('\0'),
|
||||||
|
owner(false), type(CSTR)
|
||||||
|
{
|
||||||
|
//We need an empty C string, that is, a pointer to a char that is \0,
|
||||||
|
//so we make cstr point to saved, and set saved to \0.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor from C++ string
|
||||||
|
* \param str original string. A pointer to the string is taken, the string
|
||||||
|
* is NOT copied. Therefore, the caller is responsible to guarantee the
|
||||||
|
* string won't be deallocated while this class is alive. Note that the
|
||||||
|
* original string is modified by inserting a '\0' at the index position,
|
||||||
|
* if index is given. The string will be restored to the exact original
|
||||||
|
* content only when this class is destroyed.
|
||||||
|
* \param idx if this parameter is given, this class becomes an in-place
|
||||||
|
* substring of the original string. Otherwise, this class will store the
|
||||||
|
* entire string passed. In this case, the original string will not be
|
||||||
|
* modified.
|
||||||
|
* \param off if this parameter is given, the first off characters of the
|
||||||
|
* string are skipped. Note that idx calculations take place <b>before</b>
|
||||||
|
* offset computation, so idx is relative to the original string.
|
||||||
|
*/
|
||||||
|
explicit StringPart(std::string& str, size_t idx=std::string::npos,
|
||||||
|
size_t off=0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor from C string
|
||||||
|
* \param s original string. A pointer to the string is taken, the string
|
||||||
|
* is NOT copied. Therefore, the caller is responsible to guarantee the
|
||||||
|
* string won't be deallocated while this class is alive. Note that the
|
||||||
|
* original string is modified by inserting a '\0' at the index position,
|
||||||
|
* if index is given. The string will be restored to the exact original
|
||||||
|
* content only when this class is destroyed.
|
||||||
|
* \param idx if this parameter is given, this class becomes an in-place
|
||||||
|
* substring of the original string. Otherwise, this class will store the
|
||||||
|
* entire string passed. In this case, the original string will not be
|
||||||
|
* modified.
|
||||||
|
* \param off if this parameter is given, the first off characters of the
|
||||||
|
* string are skipped. Note that idx calculations take place <b>before</b>
|
||||||
|
* offset computation, so idx is relative to the original string.
|
||||||
|
*/
|
||||||
|
explicit StringPart(char *s, size_t idx=std::string::npos, size_t off=0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor from const C string
|
||||||
|
* \param s original string. A pointer to the string is taken, the string
|
||||||
|
* is NOT copied. Therefore, the caller is responsible to guarantee the
|
||||||
|
* string won't be deallocated while this class is alive. Note that this
|
||||||
|
* constructor misses the idx and off parameters as it's not possible to
|
||||||
|
* make an in-place substring of a string if it's const.
|
||||||
|
*/
|
||||||
|
explicit StringPart(const char *s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Substring constructor. Given a StringPart, produce another StringPart
|
||||||
|
* holding a substring of the original StringPart. Unlike the normal copy
|
||||||
|
* constructor that does deep copy, this one does shallow copy, and
|
||||||
|
* therefore the newly created object will share the same string pointer
|
||||||
|
* as rhs. Useful for making substrings of a substring without memory
|
||||||
|
* allocation.
|
||||||
|
* \param rhs a StringPart
|
||||||
|
*/
|
||||||
|
StringPart(StringPart& rhs, size_t idx, size_t off=0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy constructor. Note that deep copying is used, so that the newly
|
||||||
|
* created StringPart is a self-contained string. It has been done like
|
||||||
|
* that to be able to store the paths of mounted filesystems in an std::map
|
||||||
|
* \param rhs a StringPart
|
||||||
|
*/
|
||||||
|
StringPart(const StringPart& rhs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Operator = Note that deep copying is used, so that the assigned
|
||||||
|
* StringPart becomes a self-contained string. It has been done like
|
||||||
|
* that to be able to store the paths of mounted filesystems in an std::map
|
||||||
|
* \param rhs a StringPart
|
||||||
|
*/
|
||||||
|
StringPart& operator= (const StringPart& rhs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare two StringParts for inequality
|
||||||
|
* \param rhs second StringPart to compare to
|
||||||
|
* \return true if *this < rhs
|
||||||
|
*/
|
||||||
|
bool operator<(const StringPart& rhs) const
|
||||||
|
{
|
||||||
|
return strcmp(this->c_str(),rhs.c_str())<0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \param rhs a StringPart
|
||||||
|
* \return true if this starts with rhs
|
||||||
|
*/
|
||||||
|
bool startsWith(const StringPart& rhs) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \param c char to find in the string, starting from the end
|
||||||
|
* \return the index of the last occurrence of c, or string::npos
|
||||||
|
*/
|
||||||
|
size_t findLastOf(char c) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return the StringPart length, which is the same as strlen(this->c_str())
|
||||||
|
*/
|
||||||
|
size_t length() const { return index-offset; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return the StringPart as a C string
|
||||||
|
*/
|
||||||
|
const char *c_str() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \param index index into the string
|
||||||
|
* \return the equivalent of this->c_str()[index]
|
||||||
|
*/
|
||||||
|
char operator[] (size_t index) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return true if the string is empty
|
||||||
|
*/
|
||||||
|
bool empty() const { return length()==0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make this an empty string
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor
|
||||||
|
*/
|
||||||
|
~StringPart() { clear(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* To implement copy constructor and operator=. *this must be empty.
|
||||||
|
* \param rhs other StringPart that is assigned to *this.
|
||||||
|
*/
|
||||||
|
void assign(const StringPart& rhs);
|
||||||
|
|
||||||
|
union {
|
||||||
|
std::string *str; ///< Pointer to underlying C++ string
|
||||||
|
char *cstr; ///< Pointer to underlying C string
|
||||||
|
const char *ccstr; ///< Pointer to underlying const C string
|
||||||
|
};
|
||||||
|
size_t index; ///< Index into the character substituted by '\0'
|
||||||
|
size_t offset; ///< Offset to skip the first part of a string
|
||||||
|
char saved; ///< Char that was replaced by '\0'
|
||||||
|
bool owner; ///< True if this class owns str
|
||||||
|
char type; ///< either CPPSTR or CSTR. Using char to reduce size
|
||||||
|
enum { CPPSTR, CSTR, CCSTR }; ///< Possible values fot type
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //STRINGPART_H
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2010 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef ARCH_REGISTERS_H
|
||||||
|
#define ARCH_REGISTERS_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Interfaces
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file arch_registers.h
|
||||||
|
* This file should contain the list of hardware registers of the selected
|
||||||
|
* architecture, to allow application to directly access the hardware.
|
||||||
|
*
|
||||||
|
* The list of these registers is usually provided by the chip vendor in the
|
||||||
|
* form of one or more header files.
|
||||||
|
*
|
||||||
|
* To include these registers in a portable way, here we only include
|
||||||
|
* arch_registers_impl.h, which will be an header file in
|
||||||
|
* arch/arch name/board name/interfaces_impl
|
||||||
|
*
|
||||||
|
* The usual implementation of arch_registers_impl.h is simply to include
|
||||||
|
* the header files provided by the chip vendor.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "boot/arch_registers_impl.h"
|
||||||
|
|
||||||
|
#endif //ARCH_REGISTERS_H
|
||||||
|
|
@ -0,0 +1,169 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2013, 2014, 2015 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef ATOMIC_OPS_H
|
||||||
|
#define ATOMIC_OPS_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Interfaces
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file atomic_ops.h
|
||||||
|
* This file contains various atomic operations useful for implementing
|
||||||
|
* lock-free algorithms.
|
||||||
|
*
|
||||||
|
* For architectures without hardware support for these operations, they are
|
||||||
|
* emulated by disabling interrupts. Note that these functions should be safe
|
||||||
|
* to be called also with interrupts disabled, so implementations that disable
|
||||||
|
* interrupts should be careful not to accidentally re-enable them if these
|
||||||
|
* functions are called with interupts disabled.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a value in one memory location, and atomically read back the
|
||||||
|
* previously stored value. Performs atomically the following operation:
|
||||||
|
*
|
||||||
|
* \code
|
||||||
|
* inline int atomicSwap(volatile int *p, int v)
|
||||||
|
* {
|
||||||
|
* int result=*p;
|
||||||
|
* *p=v;
|
||||||
|
* return result;
|
||||||
|
* }
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* \param p pointer to memory location where the atomic swap will take place
|
||||||
|
* \param v new value to be stored in *p
|
||||||
|
* \return the previous value of *p
|
||||||
|
*/
|
||||||
|
inline int atomicSwap(volatile int *p, int v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomically read the content of a memory location, add a number to the loaded
|
||||||
|
* value, and store the result. Performs atomically the following operation:
|
||||||
|
*
|
||||||
|
* \code
|
||||||
|
* inline void atomicAdd(volatile int *p, int incr)
|
||||||
|
* {
|
||||||
|
* *p+=incr;
|
||||||
|
* }
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* \param p pointer to memory location where the atomic add will take place
|
||||||
|
* \param incr value to be added to *p
|
||||||
|
*/
|
||||||
|
inline void atomicAdd(volatile int *p, int incr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomically read the content of a memory location, add a number to the loaded
|
||||||
|
* value, store the result and return the previous value stored.
|
||||||
|
* Performs atomically the following operation:
|
||||||
|
*
|
||||||
|
* \code
|
||||||
|
* inline int atomicAddExchange(volatile int *p, int incr)
|
||||||
|
* {
|
||||||
|
* int result=*p;
|
||||||
|
* *p+=incr;
|
||||||
|
* return result;
|
||||||
|
* }
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* \param pointer to memory location where the atomic add will take place
|
||||||
|
* \param incr value to be added to *p
|
||||||
|
* \return the previous value of *p
|
||||||
|
*/
|
||||||
|
inline int atomicAddExchange(volatile int *p, int incr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomically read the value of a memory location, and store a new value in it
|
||||||
|
* if it matches a given value. Also, return the previously stored value.
|
||||||
|
* Performs atomically the following operation:
|
||||||
|
*
|
||||||
|
* \code
|
||||||
|
* inline int atomicCompareAndSwap(volatile int *p, int prev, int next)
|
||||||
|
* {
|
||||||
|
* int result=*p;
|
||||||
|
* if(*p==prev) *p=next;
|
||||||
|
* return result;
|
||||||
|
* }
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* \param p pointer to the memory location to compare and swap
|
||||||
|
* \param prev value to be compared against the content of *p
|
||||||
|
* \param next value to be stored in *p if *p==prev
|
||||||
|
* \return the value actually read from *p
|
||||||
|
*/
|
||||||
|
inline int atomicCompareAndSwap(volatile int *p, int prev, int next);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of atomicFetchAndIncrement, as described in
|
||||||
|
* http://www.drdobbs.com/atomic-reference-counting-pointers/184401888
|
||||||
|
* Atomically read a pointer stored in one memory loaction, and add
|
||||||
|
* a constant to a memory loaction placed at a given offset from the
|
||||||
|
* pointer. Performs atomically the following operation:
|
||||||
|
*
|
||||||
|
* \code
|
||||||
|
* void *atomicFetchAndIncrement(void * const volatile *p, int offset, int incr)
|
||||||
|
* {
|
||||||
|
* int *result=*p;
|
||||||
|
* if(result==0) return 0;
|
||||||
|
* *(result+offset)+=incr;
|
||||||
|
* return result;
|
||||||
|
* }
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* \param p pointer to a const volatile pointer to object. While p is not
|
||||||
|
* subject to thread contention, *p is.
|
||||||
|
* \param offset the memory location to increment is **p+offset*sizeof(int)
|
||||||
|
* \param incr value to be added to **p+offset*sizeof(int)
|
||||||
|
* \return *p
|
||||||
|
*/
|
||||||
|
inline void *atomicFetchAndIncrement(void * const volatile * p, int offset,
|
||||||
|
int incr);
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef _ARCH_ARM7_LPC2000
|
||||||
|
#include "core/atomic_ops_impl_arm7.h"
|
||||||
|
#elif defined(_ARCH_CORTEXM3) || defined(_ARCH_CORTEXM4) \
|
||||||
|
|| defined(_ARCH_CORTEXM7)
|
||||||
|
#include "core/atomic_ops_impl_cortexMx.h"
|
||||||
|
#elif defined(_ARCH_CORTEXM0)
|
||||||
|
#include "core/atomic_ops_impl_cortexM0.h"
|
||||||
|
#else
|
||||||
|
#error "No atomic ops for this architecture"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //ATOMIC_OPS_H
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2010 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef BSP_H
|
||||||
|
#define BSP_H
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Interfaces
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file bsp.h
|
||||||
|
* This file contains architecture specific board support package.
|
||||||
|
* It must at least provide these four functions:
|
||||||
|
*
|
||||||
|
* IRQbspInit(), to initialize the board to a known state early in the boot
|
||||||
|
* process (before the kernel is started, and when interrupts are disabled)
|
||||||
|
*
|
||||||
|
* bspInit2(), to perform the remaining part of the initialization, once the
|
||||||
|
* kernel is started
|
||||||
|
*
|
||||||
|
* shutdown(), for system shutdown. This function is called in case main()
|
||||||
|
* returns, and is available to be called by user code.
|
||||||
|
*
|
||||||
|
* reboot(), a function that can be called to reboot the system under normal
|
||||||
|
* (non error) conditions. It should sync and unmount the filesystem, and
|
||||||
|
* perform a reboot. This function is available for user code.
|
||||||
|
*
|
||||||
|
* Other than this, the board support package might contain other functions,
|
||||||
|
* classes, macros etc. to support peripherals and or board hardware.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Initializes the I/O pins, and put system peripherals to a known state.<br>
|
||||||
|
* Must be called before starting kernel. Interrupts must be disabled.<br>
|
||||||
|
* This function is used by the kernel and should not be called by user code.
|
||||||
|
*/
|
||||||
|
void IRQbspInit();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Performs the part of initialization that must be done after the kernel is
|
||||||
|
* started.<br>
|
||||||
|
* This function is used by the kernel and should not be called by user code.
|
||||||
|
*/
|
||||||
|
void bspInit2();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function disables filesystem (if enabled), serial port (if enabled) and
|
||||||
|
* shuts down the system, usually by putting the procesor in a deep sleep
|
||||||
|
* state.<br>
|
||||||
|
* The action to start a new boot is system-specific, can be for example a
|
||||||
|
* reset, powercycle or a special GPIO configured to wakeup the processor from
|
||||||
|
* deep sleep.<br>
|
||||||
|
* This function does not return.<br>
|
||||||
|
* WARNING: close all files before using this function, since it unmounts the
|
||||||
|
* filesystem.<br>
|
||||||
|
*/
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The difference between this function and miosix_private::IRQsystemReboot()
|
||||||
|
* is that this function disables filesystem (if enabled), serial port
|
||||||
|
* (if enabled) while miosix_private::system_reboot() does not do all these
|
||||||
|
* things. miosix_private::IRQsystemReboot() is designed to reboot the system
|
||||||
|
* when an unrecoverable error occurs, and is used primarily in kernel code,
|
||||||
|
* reboot() is designed to reboot the system in normal conditions.<br>
|
||||||
|
* This function does not return.<br>
|
||||||
|
* WARNING: close all files before using this function, since it unmounts the
|
||||||
|
* filesystem.
|
||||||
|
*/
|
||||||
|
void reboot();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //BSP_H
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2010 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef DELAYS_H
|
||||||
|
#define DELAYS_H
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Interfaces
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file delays.h
|
||||||
|
* This file contains two functions, delayMs() and delayUs() which implement
|
||||||
|
* busy wait delays.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delay function. Accuracy depends on the underlying implementation which is
|
||||||
|
* architecture specific.<br>
|
||||||
|
* Delay time can be inaccurate if interrupts are enabled or the kernel is
|
||||||
|
* running due to time spent in interrupts and due to preemption.<br>
|
||||||
|
* It is implemented using busy wait, so can be safely used even when the
|
||||||
|
* kernel is paused or interrupts are disabled.<br>
|
||||||
|
* If the kernel is running it is *highly* recomended to use Thread::sleep since
|
||||||
|
* it gives CPU time to other threads and/or it puts the CPU in low power mode.
|
||||||
|
* \param mseconds milliseconds to wait
|
||||||
|
*/
|
||||||
|
void delayMs(unsigned int mseconds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delay function. Accuracy depends on the underlying implementation which is
|
||||||
|
* architecture specific.<br>
|
||||||
|
* Delay time can be inaccurate if interrupts are enabled or the kernel is
|
||||||
|
* running due to time spent in interrupts and due to preemption.<br>
|
||||||
|
* It is implemented using busy wait, so can be safely used even when the
|
||||||
|
* kernel is paused or interrupts are disabled.<br>
|
||||||
|
* \param useconds microseconds to wait. Only values between 1 and 1000 are
|
||||||
|
* allowed. For greater delays use Thread::sleep() or delayMs().
|
||||||
|
*/
|
||||||
|
void delayUs(unsigned int useconds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //DELAYS_H
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2011 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifdef _ARCH_ARM7_LPC2000
|
||||||
|
#include "core/endianness_impl_arm7.h"
|
||||||
|
#elif defined(_ARCH_CORTEXM0) || defined(_ARCH_CORTEXM3) \
|
||||||
|
|| defined(_ARCH_CORTEXM4) || defined(_ARCH_CORTEXM7)
|
||||||
|
#include "core/endianness_impl_cortexMx.h"
|
||||||
|
#else
|
||||||
|
#error "No endianness code for this architecture"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ENDIANNESS_H
|
||||||
|
#define ENDIANNESS_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Interfaces
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file endianness.h
|
||||||
|
* This file contains optimized functions to convert data from the system's
|
||||||
|
* endianness to little or big endian, as well as to perform byte swapping.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Implementation of these functions is in endianness_impl.h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define __MIOSIX_INLINE inline
|
||||||
|
#else //__cplusplus
|
||||||
|
#define __MIOSIX_INLINE static inline
|
||||||
|
#endif //__cplusplus
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn inline unsigned short swapBytes16(unsigned short x)
|
||||||
|
* \param x an short int
|
||||||
|
* \return the same short with its bytes swapped
|
||||||
|
*/
|
||||||
|
__MIOSIX_INLINE unsigned short swapBytes16(unsigned short x);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn inline unsigned int swapBytes32(unsigned int x)
|
||||||
|
* \param x an int
|
||||||
|
* \return the same int with its bytes swapped
|
||||||
|
*/
|
||||||
|
__MIOSIX_INLINE unsigned int swapBytes32(unsigned int x);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn inline unsigned long long swapBytes64(unsigned long long x)
|
||||||
|
* \param x a long long
|
||||||
|
* \return the same long long with its bytes swapped
|
||||||
|
*/
|
||||||
|
__MIOSIX_INLINE unsigned long long swapBytes64(unsigned long long x);
|
||||||
|
|
||||||
|
#undef __MIOSIX_INLINE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \def toLittleEndian16(x)
|
||||||
|
* Convert a (signed or unsigned) short int from the system representation
|
||||||
|
* to little endian
|
||||||
|
* \param x value to convert
|
||||||
|
* \return value converted to little endian
|
||||||
|
*
|
||||||
|
* \def toLittleEndian32(x)
|
||||||
|
* Convert a (signed or unsigned) int from the system representation
|
||||||
|
* to little endian
|
||||||
|
* \param x value to convert
|
||||||
|
* \return value converted to little endian
|
||||||
|
*
|
||||||
|
* \def toLittleEndian64(x)
|
||||||
|
* Convert a (signed or unsigned) long long from the system representation
|
||||||
|
* to little endian
|
||||||
|
* \param x value to convert
|
||||||
|
* \return value converted to little endian
|
||||||
|
*
|
||||||
|
* \def toBigEndian16(x)
|
||||||
|
* Convert a (signed or unsigned) short int from the system representation
|
||||||
|
* to big endian
|
||||||
|
* \param x value to convert
|
||||||
|
* \return value converted to big endian
|
||||||
|
*
|
||||||
|
* \def toBigEndian32(x)
|
||||||
|
* Convert a (signed or unsigned) int from the system representation
|
||||||
|
* to big endian
|
||||||
|
* \param x value to convert
|
||||||
|
* \return value converted to big endian
|
||||||
|
*
|
||||||
|
* \def toBigEndian64(x)
|
||||||
|
* Convert a (signed or unsigned) long long from the system representation
|
||||||
|
* to big endian
|
||||||
|
* \param x value to convert
|
||||||
|
* \return value converted to big endian
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef MIOSIX_LITTLE_ENDIAN
|
||||||
|
#define toLittleEndian16(x) (x)
|
||||||
|
#define toLittleEndian32(x) (x)
|
||||||
|
#define toLittleEndian64(x) (x)
|
||||||
|
#define fromLittleEndian16(x) (x)
|
||||||
|
#define fromLittleEndian32(x) (x)
|
||||||
|
#define fromLittleEndian64(x) (x)
|
||||||
|
#define toBigEndian16(x) swapBytes16(x)
|
||||||
|
#define toBigEndian32(x) swapBytes32(x)
|
||||||
|
#define toBigEndian64(x) swapBytes64(x)
|
||||||
|
#define fromBigEndian16(x) swapBytes16(x)
|
||||||
|
#define fromBigEndian32(x) swapBytes32(x)
|
||||||
|
#define fromBigEndian64(x) swapBytes64(x)
|
||||||
|
#elif defined(MIOSIX_BIG_ENDIAN)
|
||||||
|
#define toLittleEndian16(x) swapBytes16(x)
|
||||||
|
#define toLittleEndian32(x) swapBytes32(x)
|
||||||
|
#define toLittleEndian64(x) swapBytes64(x)
|
||||||
|
#define fromLittleEndian16(x) swapBytes16(x)
|
||||||
|
#define fromLittleEndian32(x) swapBytes32(x)
|
||||||
|
#define fromLittleEndian64(x) swapBytes64(x)
|
||||||
|
#define toBigEndian16(x) (x)
|
||||||
|
#define toBigEndian32(x) (x)
|
||||||
|
#define toBigEndian64(x) (x)
|
||||||
|
#define fromBigEndian16(x) (x)
|
||||||
|
#define fromBigEndian32(x) (x)
|
||||||
|
#define fromBigEndian64(x) (x)
|
||||||
|
#else
|
||||||
|
#error "endianness_impl.h does not define endianness"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif //ENDIANNESS_H
|
||||||
|
|
@ -0,0 +1,144 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2010 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef GPIO_H
|
||||||
|
#define GPIO_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Interfaces
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file gpio.h
|
||||||
|
* The interface to gpios provided by Miosix is in the form of templates,
|
||||||
|
* therefore this file can only include gpio_impl.h with the architecture
|
||||||
|
* dependand code.
|
||||||
|
*
|
||||||
|
* The interface should be as follows:
|
||||||
|
* First a class Mode containing an enum Mode_ needs to be defined. Its minimum
|
||||||
|
* implementation is this:
|
||||||
|
* \code
|
||||||
|
* class Mode
|
||||||
|
* {
|
||||||
|
* public:
|
||||||
|
* enum Mode_
|
||||||
|
* {
|
||||||
|
* INPUT,
|
||||||
|
* OUTPUT
|
||||||
|
* };
|
||||||
|
* private:
|
||||||
|
* Mode(); //Just a wrapper class, disallow creating instances
|
||||||
|
* };
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* This class should define the possible configurations of a gpio pin.
|
||||||
|
* The minimum required is INPUT and OUTPUT, but this can be extended to other
|
||||||
|
* options to reflect the hardware capabilities of gpios. For example, if
|
||||||
|
* gpios can be set as input with pull up, it is possible to add INPUT_PULL_UP
|
||||||
|
* to the enum.
|
||||||
|
*
|
||||||
|
* Then a template Gpio class should be provided, with at least the following
|
||||||
|
* member functions:
|
||||||
|
* \code
|
||||||
|
* template<unsigned int P, unsigned char N>
|
||||||
|
* class Gpio
|
||||||
|
* {
|
||||||
|
* public:
|
||||||
|
* static void mode(Mode::Mode_ m);
|
||||||
|
* static void high();
|
||||||
|
* static void low();
|
||||||
|
* static int value();
|
||||||
|
* unsigned int getPort() const;
|
||||||
|
* unsigned char getNumber() const;
|
||||||
|
* private:
|
||||||
|
* Gpio();//Only static member functions, disallow creating instances
|
||||||
|
* };
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* mode() should take a Mode::Mode_ enum and set the mode of the gpio
|
||||||
|
* (input, output or other architecture specific)
|
||||||
|
*
|
||||||
|
* high() should set a gpio configured as output to high logic level
|
||||||
|
*
|
||||||
|
* low() should set a gpio configured as output to low logic level
|
||||||
|
*
|
||||||
|
* value() should return either 1 or 0 to refect the state of a gpio configured
|
||||||
|
* as input
|
||||||
|
*
|
||||||
|
* getPort() should return the gpio port
|
||||||
|
*
|
||||||
|
* getNumber() should return the gpio pin number
|
||||||
|
*
|
||||||
|
* Lastly, a number of constants must be provided to be passed as first template
|
||||||
|
* parameter of the Gpio class, usually identifying the gpio port, while the
|
||||||
|
* second template parameter should be used to specify a gpio pin within a port.
|
||||||
|
*
|
||||||
|
* The intended use is this:
|
||||||
|
* considering an architecture with two ports, PORTA and PORTB each with 8 pins.
|
||||||
|
* The header gpio_impl.h should provide two constants, for example named
|
||||||
|
* GPIOA_BASE and GPIOB_BASE.
|
||||||
|
*
|
||||||
|
* The user can declare the hardware mapping between gpios and what is connected
|
||||||
|
* to them, usually in an header file. If for example PORTA.0 is connected to
|
||||||
|
* a button while PORTB.4 to a led, the header file might contain:
|
||||||
|
*
|
||||||
|
* \code
|
||||||
|
* typedef Gpio<GPIOA_BASE,0> button;
|
||||||
|
* typedef Gpio<GPIOB_BASE,4> led;
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* This allows the rest of the code to be written in terms of leds and buttons,
|
||||||
|
* without a reference to which pin they are connected to, something that might
|
||||||
|
* change.
|
||||||
|
*
|
||||||
|
* A simple code using these gpios could be:
|
||||||
|
* \code
|
||||||
|
* void blinkUntilButtonPressed()
|
||||||
|
* {
|
||||||
|
* led::mode(Mode::OUTPUT);
|
||||||
|
* button::mode(Mode::INPUT);
|
||||||
|
* for(;;)
|
||||||
|
* {
|
||||||
|
* if(button::value()==1) break;
|
||||||
|
* led::high();
|
||||||
|
* Thread::sleep(250);
|
||||||
|
* led::low();
|
||||||
|
* Thread::sleep(250);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "interfaces-impl/gpio_impl.h"
|
||||||
|
|
||||||
|
#endif //GPIO_H
|
||||||
|
|
@ -0,0 +1,195 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2010, 2011, 2012 by Terraneo Federico *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef PORTABILITY_H
|
||||||
|
#define PORTABILITY_H
|
||||||
|
|
||||||
|
//For SCHED_TYPE_* config options
|
||||||
|
#include "config/miosix_settings.h"
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Interfaces
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file portability.h
|
||||||
|
* This file is the interface from the Miosix kernel to the hardware.
|
||||||
|
* It ccontains what is required to perform a context switch, disable
|
||||||
|
* interrupts, set up the stack frame and registers of a newly created thread,
|
||||||
|
* and contains iterrupt handlers for preemption and yield.
|
||||||
|
*
|
||||||
|
* Since some of the functions in this file must be inline for speed reasons,
|
||||||
|
* and context switch code must be macros, at the end of this file the file
|
||||||
|
* portability_impl.h is included.
|
||||||
|
* This file should contain the implementation of those inline functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \namespace miosix_pivate
|
||||||
|
* contains architecture-specific functions. These functions are separated from
|
||||||
|
* the functions in kernel.h because:<br>
|
||||||
|
* - to port the kernel to another processor you only need to rewrite these
|
||||||
|
* functions.
|
||||||
|
* - these functions are only useful for writing hardare drivers, most user code
|
||||||
|
* does not need them.
|
||||||
|
*/
|
||||||
|
namespace miosix_private {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup Interfaces
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Used after an unrecoverable error condition to restart the system, even from
|
||||||
|
* within an interrupt routine.
|
||||||
|
*/
|
||||||
|
void IRQsystemReboot();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Cause a context switch.
|
||||||
|
* It is used by the kernel, and should not be used by end users.
|
||||||
|
*/
|
||||||
|
inline void doYield();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Initializes a ctxsave array when a thread is created.
|
||||||
|
* It is used by the kernel, and should not be used by end users.
|
||||||
|
* \param ctxsave a pointer to a field ctxsave inside a Thread class that need
|
||||||
|
* to be filled
|
||||||
|
* \param pc starting program counter of newly created thread, used to
|
||||||
|
* initialize ctxsave
|
||||||
|
* \param sp starting stack pointer of newly created thread, used to initialize
|
||||||
|
* ctxsave
|
||||||
|
* \param argv starting data passed to newly created thread, used to initialize
|
||||||
|
* ctxsave
|
||||||
|
*/
|
||||||
|
void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp,
|
||||||
|
void *argv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Used before every context switch to check if the stack of the thread has
|
||||||
|
* overflowed must be called before IRQfindNextThread().
|
||||||
|
*/
|
||||||
|
void IRQstackOverflowCheck();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Called by miosix::start_kernel to handle the architecture-specific part of
|
||||||
|
* initialization. It is used by the kernel, and should not be used by end users
|
||||||
|
*/
|
||||||
|
void IRQportableStartKernel();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* This function disables interrupts.
|
||||||
|
* This is used by the kernel to implement disableInterrupts() and
|
||||||
|
* enableInterrupts(). You should never need to call these functions directly.
|
||||||
|
*/
|
||||||
|
inline void doDisableInterrupts();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* This function enables interrupts.
|
||||||
|
* This is used by the kernel to implement disableInterrupts() and
|
||||||
|
* enableInterrupts(). You should never need to call these functions directly.
|
||||||
|
*/
|
||||||
|
inline void doEnableInterrupts();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* This is used by the kernel to implement areInterruptsEnabled()
|
||||||
|
* You should never need to call this function directly.
|
||||||
|
* \return true if interrupts are enabled
|
||||||
|
*/
|
||||||
|
inline bool checkAreInterruptsEnabled();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* used by the idle thread to put cpu in low power mode
|
||||||
|
*/
|
||||||
|
void sleepCpu();
|
||||||
|
|
||||||
|
#ifdef SCHED_TYPE_CONTROL_BASED
|
||||||
|
/**
|
||||||
|
* Allow access to a second timer to allow variable burst preemption together
|
||||||
|
* with fixed tick timekeeping.
|
||||||
|
*/
|
||||||
|
class AuxiliaryTimer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Initializes the auxiliary timer.
|
||||||
|
*/
|
||||||
|
static void IRQinit();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Used to implement new control-based scheduler. Used to get the error
|
||||||
|
* between desired burst time and real burst time.
|
||||||
|
* It is responsibility of who implements this function to ensure that
|
||||||
|
* the returned value has the three most significant bits set to zero
|
||||||
|
* (that is, is a positive number between 0 and 0x1fffffff). This is
|
||||||
|
* necessary to avoid oerflow in the scheduler implementation.
|
||||||
|
* \return time since last time set_timer_value() was called.
|
||||||
|
*/
|
||||||
|
static int IRQgetValue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* Reset timer counter to zero and set next preempt after x timer counts.
|
||||||
|
* \param x time before next preempt will occur. Must be >0
|
||||||
|
*/
|
||||||
|
static void IRQsetValue(int x);
|
||||||
|
|
||||||
|
private:
|
||||||
|
//Unwanted functions
|
||||||
|
AuxiliaryTimer();
|
||||||
|
AuxiliaryTimer& operator= (AuxiliaryTimer& );
|
||||||
|
};
|
||||||
|
#endif //SCHED_TYPE_CONTROL_BASED
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} //namespace miosix_private
|
||||||
|
|
||||||
|
// This contains the macros and the implementation of inline functions
|
||||||
|
#include "portability_impl.h"
|
||||||
|
|
||||||
|
#endif //PORTABILITY_H
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2016 by Lorenzo Pinosa *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "IRQDisplayPrint.h"
|
||||||
|
#include "string"
|
||||||
|
#include "display.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace mxgui;
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
IRQDisplayPrint::IRQDisplayPrint()
|
||||||
|
: Device(TTY), right_margin(5), bottom_margin(5),
|
||||||
|
carriage_return_enabled(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
IRQDisplayPrint::~IRQDisplayPrint() {}
|
||||||
|
|
||||||
|
void IRQDisplayPrint::IRQwrite(const char * to_print)
|
||||||
|
{
|
||||||
|
string str = to_print;
|
||||||
|
input_queue.put(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t IRQDisplayPrint::writeBlock(const void * buffer, size_t size, off_t where)
|
||||||
|
{
|
||||||
|
string str = reinterpret_cast<const char*>(buffer);
|
||||||
|
str = str.substr(where, size);
|
||||||
|
input_queue.put(str);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRQDisplayPrint::printIRQ()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
string tmp;
|
||||||
|
input_queue.get(tmp);
|
||||||
|
if (tmp.size() == 0)
|
||||||
|
continue;
|
||||||
|
if (carriage_return_fix(tmp))
|
||||||
|
continue;
|
||||||
|
process_string(tmp);
|
||||||
|
check_array_overflow();
|
||||||
|
internal_print();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//returns true if the current string has to be discarded
|
||||||
|
bool IRQDisplayPrint::carriage_return_fix(string str)
|
||||||
|
{
|
||||||
|
if (!carriage_return_enabled && str.compare("\r\n") == 0)
|
||||||
|
{
|
||||||
|
carriage_return_enabled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (str.compare("\r\n") == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
carriage_return_enabled = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRQDisplayPrint::check_array_overflow()
|
||||||
|
{
|
||||||
|
int font_height, display_h;
|
||||||
|
{
|
||||||
|
DrawingContext dc(Display::instance());
|
||||||
|
font_height = dc.getFont().getHeight();
|
||||||
|
display_h = dc.getHeight();
|
||||||
|
}
|
||||||
|
while (font_height * print_lines.size() >= display_h - bottom_margin)
|
||||||
|
{
|
||||||
|
print_lines.erase(print_lines.begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRQDisplayPrint::process_string(string str)
|
||||||
|
{
|
||||||
|
int display_w;
|
||||||
|
auto_ptr<Font> font;
|
||||||
|
{
|
||||||
|
DrawingContext dc(Display::instance());
|
||||||
|
display_w = dc.getWidth();
|
||||||
|
font = auto_ptr<Font>(new Font(dc.getFont()));
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<string> lines;
|
||||||
|
while (str.length() > 0)
|
||||||
|
{
|
||||||
|
int lenght = font->calculateLength(str.c_str());
|
||||||
|
if (lenght < display_w)
|
||||||
|
{
|
||||||
|
lines.push_back(str);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//else
|
||||||
|
int len = 0;
|
||||||
|
string str_part;
|
||||||
|
for (int i = 1; len < display_w - right_margin; i++)
|
||||||
|
{
|
||||||
|
str_part = str.substr(0, i);
|
||||||
|
len = font->calculateLength(str_part.c_str());
|
||||||
|
}
|
||||||
|
lines.push_back(str_part);
|
||||||
|
str = str.substr(str_part.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (vector<string>::iterator it = lines.begin(); it != lines.end(); ++it)
|
||||||
|
{
|
||||||
|
print_lines.push_back(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRQDisplayPrint::internal_print()
|
||||||
|
{
|
||||||
|
DrawingContext dc(Display::instance());
|
||||||
|
//dc.clear(0xFFF);
|
||||||
|
int cur_y = 0;
|
||||||
|
|
||||||
|
for (vector<string>::iterator it = print_lines.begin(); it != print_lines.end(); ++it)
|
||||||
|
{
|
||||||
|
string str = *it;
|
||||||
|
dc.write(Point(0, cur_y), str.c_str());
|
||||||
|
Point up_left(dc.getFont().calculateLength(str.c_str()), cur_y);
|
||||||
|
Point down_right(dc.getWidth() -1, cur_y + dc.getFont().getHeight());
|
||||||
|
dc.clear(up_left, down_right, 0x0);
|
||||||
|
cur_y += dc.getFont().getHeight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2016 by Lorenzo Pinosa *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* As a special exception, if other files instantiate templates or use *
|
||||||
|
* macros or inline functions from this file, or you compile this file *
|
||||||
|
* and link it with other works to produce a work based on this file, *
|
||||||
|
* this file does not by itself cause the resulting work to be covered *
|
||||||
|
* by the GNU General Public License. However the source code for this *
|
||||||
|
* file must still be made available in accordance with the GNU General *
|
||||||
|
* Public License. This exception does not invalidate any other reasons *
|
||||||
|
* why a work based on this file might be covered by the GNU General *
|
||||||
|
* Public License. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "../filesystem/devfs/devfs.h"
|
||||||
|
#include "queue.h"
|
||||||
|
#include <vector>
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
class IRQDisplayPrint : public Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IRQDisplayPrint();
|
||||||
|
~IRQDisplayPrint();
|
||||||
|
|
||||||
|
void IRQwrite(const char *str);
|
||||||
|
ssize_t writeBlock(const void *buffer, size_t size, off_t where);
|
||||||
|
|
||||||
|
void printIRQ();
|
||||||
|
private:
|
||||||
|
Queue<string, 20> input_queue;
|
||||||
|
vector<string> print_lines;
|
||||||
|
|
||||||
|
int right_margin;
|
||||||
|
int bottom_margin;
|
||||||
|
bool carriage_return_enabled;
|
||||||
|
|
||||||
|
bool carriage_return_fix(string str);
|
||||||
|
void process_string(string str);
|
||||||
|
void check_array_overflow();
|
||||||
|
void internal_print();
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
|
||||||
|
#include "SystemMap.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#ifdef WITH_PROCESSES
|
||||||
|
|
||||||
|
namespace miosix {
|
||||||
|
|
||||||
|
SystemMap& SystemMap::instance()
|
||||||
|
{
|
||||||
|
static SystemMap singleton;
|
||||||
|
return singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemMap::addElfProgram(const char* name, const unsigned int *elf, unsigned int size)
|
||||||
|
{
|
||||||
|
string sName(name);
|
||||||
|
if(mPrograms.find(sName) == mPrograms.end())
|
||||||
|
mPrograms.insert(make_pair(sName, make_pair(elf, size)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemMap::removeElfProgram(const char* name)
|
||||||
|
{
|
||||||
|
string sName(name);
|
||||||
|
ProgramsMap::iterator it = mPrograms.find(sName);
|
||||||
|
|
||||||
|
if(it != mPrograms.end())
|
||||||
|
mPrograms.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
pair<const unsigned int*, unsigned int> SystemMap::getElfProgram(const char* name) const
|
||||||
|
{
|
||||||
|
ProgramsMap::const_iterator it = mPrograms.find(string(name));
|
||||||
|
|
||||||
|
if(it == mPrograms.end())
|
||||||
|
return make_pair<const unsigned int*, unsigned int>(0, 0);
|
||||||
|
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int SystemMap::getElfCount() const
|
||||||
|
{
|
||||||
|
return mPrograms.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace miosix
|
||||||
|
|
||||||
|
#endif //WITH_PROCESSES
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue