pigpio/pigpio.c

5189 lines
118 KiB
C

/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
*/
/* pigpio version 9 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <ctype.h>
#include <syslog.h>
#include <poll.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <limits.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include "pigpio.h"
#include "command.h"
/* --------------------------------------------------------------- */
/*
0 GPFSEL0 GPIO Function Select 0
1 GPFSEL1 GPIO Function Select 1
2 GPFSEL2 GPIO Function Select 2
3 GPFSEL3 GPIO Function Select 3
4 GPFSEL4 GPIO Function Select 4
5 GPFSEL5 GPIO Function Select 5
6 - Reserved
7 GPSET0 GPIO Pin Output Set 0
8 GPSET1 GPIO Pin Output Set 1
9 - Reserved
10 GPCLR0 GPIO Pin Output Clear 0
11 GPCLR1 GPIO Pin Output Clear 1
12 - Reserved
13 GPLEV0 GPIO Pin Level 0
14 GPLEV1 GPIO Pin Level 1
15 - Reserved
16 GPEDS0 GPIO Pin Event Detect Status 0
17 GPEDS1 GPIO Pin Event Detect Status 1
18 - Reserved
19 GPREN0 GPIO Pin Rising Edge Detect Enable 0
20 GPREN1 GPIO Pin Rising Edge Detect Enable 1
21 - Reserved
22 GPFEN0 GPIO Pin Falling Edge Detect Enable 0
23 GPFEN1 GPIO Pin Falling Edge Detect Enable 1
24 - Reserved
25 GPHEN0 GPIO Pin High Detect Enable 0
26 GPHEN1 GPIO Pin High Detect Enable 1
27 - Reserved
28 GPLEN0 GPIO Pin Low Detect Enable 0
29 GPLEN1 GPIO Pin Low Detect Enable 1
30 - Reserved
31 GPAREN0 GPIO Pin Async. Rising Edge Detect 0
32 GPAREN1 GPIO Pin Async. Rising Edge Detect 1
33 - Reserved
34 GPAFEN0 GPIO Pin Async. Falling Edge Detect 0
35 GPAFEN1 GPIO Pin Async. Falling Edge Detect 1
36 - Reserved
37 GPPUD GPIO Pin Pull-up/down Enable
38 GPPUDCLK0 GPIO Pin Pull-up/down Enable Clock 0
39 GPPUDCLK1 GPIO Pin Pull-up/down Enable Clock 1
40 - Reserved
41 - Test
*/
/*
0 CS DMA Channel 0 Control and Status
1 CPI_ONBLK_AD DMA Channel 0 Control Block Address
2 TI DMA Channel 0 CB Word 0 (Transfer Information)
3 SOURCE_AD DMA Channel 0 CB Word 1 (Source Address)
4 DEST_AD DMA Channel 0 CB Word 2 (Destination Address)
5 TXFR_LEN DMA Channel 0 CB Word 3 (Transfer Length)
6 STRIDE DMA Channel 0 CB Word 4 (2D Stride)
7 NEXTCPI_ONBK DMA Channel 0 CB Word 5 (Next CB Address)
8 DEBUG DMA Channel 0 Debug
*/
/*
DEBUG register bits
bit 2 READ_ERROR
Slave Read Response Error RW 0x0
Set if the read operation returned an error value on
the read response bus. It can be cleared by writing
a 1.
bit 1 FIFO_ERROR
Fifo Error RW 0x0
Set if the optional read Fifo records an error
condition. It can be cleared by writing a 1.
bit 0 READ_LAST_NOT_SET_ERROR
Read Last Not Set Error RW 0x0
If the AXI read last signal was not set when
expected, then this error bit will be set. It can be
cleared by writing a 1.
*/
/*
0 CTL PWM Control
1 STA PWM Status
2 DMAC PWM DMA Configuration
4 RNG1 PWM Channel 1 Range
5 DAT1 PWM Channel 1 Data
6 FIF1 PWM FIFO Input
8 RNG2 PWM Channel 2 Range
9 DAT2 PWM Channel 2 Data
*/
/*
0 PCM_CS PCM Control and Status
1 PCM_FIFO PCM FIFO Data
2 PCM_MODE PCM Mode
3 PCM_RXC PCM Receive Configuration
4 PCM_TXC PCM Transmit Configuration
5 PCM_DREQ PCM DMA Request Level
6 PCM_INTEN PCM Interrupt Enables
7 PCM_INTSTC PCM Interrupt Status & Clear
8 PCM_GRAY PCM Gray Mode Control
*/
/*
0 CS System Timer Control/Status
1 CLO System Timer Counter Lower 32 bits
2 CHI System Timer Counter Higher 32 bits
3 C0 System Timer Compare 0
4 C1 System Timer Compare 1
5 C2 System Timer Compare 2
6 C3 System Timer Compare 3
*/
/* --------------------------------------------------------------- */
#define THOUSAND 1000
#define MILLION 1000000
#define BILLION 1000000000
#define BANK (gpio>>5)
#define BIT (1<<(gpio&0x1F))
#define CHECK_INITED \
do \
{ \
if (!libInitialised) \
{ \
fprintf(stderr, \
"%s %s: pigpio uninitialised, call gpioInitialise()\n",\
myTimeStamp(), __FUNCTION__); \
return PI_NOT_INITIALISED; \
} \
} \
while (0)
#define CHECK_NOT_INITED \
do \
{ \
if (libInitialised) \
{ \
fprintf(stderr, \
"%s %s: pigpio initialised, call gpioTerminate()\n", \
myTimeStamp(), __FUNCTION__); \
return PI_INITIALISED; \
} \
} \
while (0)
#define DBG(level, format, arg...) \
do \
{ \
if (gpioCfg.dbgLevel >= level) \
fprintf(stderr, "%s %s: " format "\n" , \
myTimeStamp(), __FUNCTION__ , ## arg); \
} \
while (0)
#define SOFT_ERROR(x, format, arg...) \
do \
{ \
fprintf(stderr, "%s %s: " format "\n", \
myTimeStamp(), __FUNCTION__ , ## arg); \
return x; \
} \
while (0)
#define PERM_ERROR(format, arg...) \
do \
{ \
fprintf(stderr, "%s " format "\n", myTimeStamp(), ## arg); \
} \
while (0)
#define TIMER_ADD(a, b, result) \
do \
{ \
(result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
(result)->tv_nsec = (a)->tv_nsec + (b)->tv_nsec; \
if ((result)->tv_nsec >= BILLION) \
{ \
++(result)->tv_sec; \
(result)->tv_nsec -= BILLION; \
} \
} \
while (0)
#define TIMER_SUB(a, b, result) \
do \
{ \
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
(result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec; \
if ((result)->tv_nsec < 0) \
{ \
--(result)->tv_sec; \
(result)->tv_nsec += BILLION; \
} \
} \
while (0)
#define DMA_BUS_ADR 0x40000000
#define CLK_BASE 0x20101000
#define DMA_BASE 0x20007000
#define DMA15_BASE 0x20E05000
#define GPIO_BASE 0x20200000
#define PCM_BASE 0x20203000
#define PWM_BASE 0x2020C000
#define SPI0_BASE 0x20204000
#define SYST_BASE 0x20003000
#define UART0_BASE 0x20201000
#define UART1_BASE 0x20215000
#define DMA_LEN 0x1000 /* allow access to all channels */
#define CLK_LEN 0xA8
#define GPIO_LEN 0xB4
#define SYST_LEN 0x1C
#define PCM_LEN 0x24
#define PWM_LEN 0x28
#define DMA_ENABLE (0xFF0/4)
#define GPFSEL0 0
#define GPSET0 7
#define GPSET1 8
#define GPCLR0 10
#define GPCLR1 11
#define GPLEV0 13
#define GPLEV1 14
#define GPEDS0 16
#define GPEDS1 17
#define GPREN0 19
#define GPREN1 20
#define GPFEN0 22
#define GPFEN1 23
#define GPHEN0 25
#define GPHEN1 26
#define GPLEN0 28
#define GPLEN1 29
#define GPAREN0 31
#define GPAREN1 32
#define GPAFEN0 34
#define GPAFEN1 35
#define GPPUD 37
#define GPPUDCLK0 38
#define GPPUDCLK1 39
#define DMA_CS 0
#define DMA_CONBLK_AD 1
#define DMA_DEBUG 8
/* DMA CS Control and Status bits */
#define DMA_CHANNEL_RESET (1<<31)
#define DMA_WAIT_ON_WRITES (1<<28)
#define DMA_PANIC_PRIORITY(x) ((x)<<20)
#define DMA_PRIORITY(x) ((x)<<16)
#define DMA_INTERRUPT_STATUS (1<< 2)
#define DMA_END_FLAG (1<< 1)
#define DMA_ACTIVATE (1<< 0)
/* DMA control block "info" field bits */
#define DMA_NO_WIDE_BURSTS (1<<26)
#define DMA_PERIPHERAL_MAPPING(x) ((x)<<16)
#define DMA_BURST_LENGTH(x) ((x)<<12)
#define DMA_SRC_IGNORE (1<<11)
#define DMA_SRC_DREQ (1<<10)
#define DMA_SRC_INC (1<< 8)
#define DMA_DEST_IGNORE (1<< 7)
#define DMA_DEST_DREQ (1<< 6)
#define DMA_DEST_INC (1<< 4)
#define DMA_WAIT_RESP (1<< 3)
#define DMA_DEBUG_READ_ERR (1<<2)
#define DMA_DEBUG_FIFO_ERR (1<<1)
#define DMA_DEBUG_RD_LST_NOT_SET_ERR (1<<0)
#define PWM_CTL 0
#define PWM_STA 1
#define PWM_DMAC 2
#define PWM_RNG1 4
#define PWM_DAT1 5
#define PWM_FIFO 6
#define PWM_RNG2 8
#define PWM_DAT2 9
#define PWM_CTL_CLRF1 (1<<6)
#define PWM_CTL_USEF1 (1<<5)
#define PWM_CTL_MODE1 (1<<1)
#define PWM_CTL_PWEN1 (1<<0)
#define PWM_DMAC_ENAB (1 <<31)
#define PWM_DMAC_PANIC(x) ((x)<< 8)
#define PWM_DMAC_DREQ(x) (x)
#define PCM_CS 0
#define PCM_FIFO 1
#define PCM_MODE 2
#define PCM_RXC 3
#define PCM_TXC 4
#define PCM_DREQ 5
#define PCM_INTEN 6
#define PCM_INTSTC 7
#define PCM_GRAY 8
#define PCM_CS_STBY (1 <<25)
#define PCM_CS_SYNC (1 <<24)
#define PCM_CS_RXSEX (1 <<23)
#define PCM_CS_RXERR (1 <<16)
#define PCM_CS_TXERR (1 <<15)
#define PCM_CS_DMAEN (1 <<9)
#define PCM_CS_RXTHR(x) ((x)<<7)
#define PCM_CS_TXTHR(x) ((x)<<5)
#define PCM_CS_RXCLR (1 <<4)
#define PCM_CS_TXCLR (1 <<3)
#define PCM_CS_TXON (1 <<2)
#define PCM_CS_RXON (1 <<1)
#define PCM_CS_EN (1 <<0)
#define PCM_MODE_CLK_DIS (1 <<28)
#define PCM_MODE_PDMN (1 <<27)
#define PCM_MODE_PDME (1 <<26)
#define PCM_MODE_FRXP (1 <<25)
#define PCM_MODE_FTXP (1 <<24)
#define PCM_MODE_CLKM (1 <<23)
#define PCM_MODE_CLKI (1 <<22)
#define PCM_MODE_FSM (1 <<21)
#define PCM_MODE_FSI (1 <<20)
#define PCM_MODE_FLEN(x) ((x)<<10)
#define PCM_MODE_FSLEN(x) ((x)<< 0)
#define PCM_RXC_CH1WEX (1 <<31)
#define PCM_RXC_CH1EN (1 <<30)
#define PCM_RXC_CH1POS(x) ((x)<<20)
#define PCM_RXC_CH1WID(x) ((x)<<16)
#define PCM_RXC_CH2WEX (1 <<15)
#define PCM_RXC_CH2EN (1 <<14)
#define PCM_RXC_CH2POS(x) ((x)<< 4)
#define PCM_RXC_CH2WID(x) ((x)<< 0)
#define PCM_TXC_CH1WEX (1 <<31)
#define PCM_TXC_CH1EN (1 <<30)
#define PCM_TXC_CH1POS(x) ((x)<<20)
#define PCM_TXC_CH1WID(x) ((x)<<16)
#define PCM_TXC_CH2WEX (1 <<15)
#define PCM_TXC_CH2EN (1 <<14)
#define PCM_TXC_CH2POS(x) ((x)<< 4)
#define PCM_TXC_CH2WID(x) ((x)<< 0)
#define PCM_DREQ_TX_PANIC(x) ((x)<<24)
#define PCM_DREQ_RX_PANIC(x) ((x)<<16)
#define PCM_DREQ_TX_REQ_L(x) ((x)<< 8)
#define PCM_DREQ_RX_REQ_L(x) ((x)<< 0)
#define PCM_INTEN_RXERR (1<<3)
#define PCM_INTEN_TXERR (1<<2)
#define PCM_INTEN_RXR (1<<1)
#define PCM_INTEN_TXW (1<<0)
#define PCM_INTSTC_RXERR (1<<3)
#define PCM_INTSTC_TXERR (1<<2)
#define PCM_INTSTC_RXR (1<<1)
#define PCM_INTSTC_TXW (1<<0)
#define PCM_GRAY_FLUSH (1<<2)
#define PCM_GRAY_CLR (1<<1)
#define PCM_GRAY_EN (1<<0)
#define CLK_PASSWD (0x5A<<24)
#define CLK_CTL_MASH(x)((x)<<9)
#define CLK_CTL_BUSY (1 <<7)
#define CLK_CTL_KILL (1 <<5)
#define CLK_CTL_ENAB (1 <<4)
#define CLK_CTL_SRC(x) ((x)<<0)
#define CLK_CTL_SRC_OSC 1 /* 19.2 MHz */
#define CLK_CTL_SRC_PLLD 6 /* 500.0 MHz */
#define CLK_DIV_DIVI(x) ((x)<<12)
#define CLK_DIV_DIVF(x) ((x)<< 0)
#define CLK_PCMCTL 38
#define CLK_PCMDIV 39
#define CLK_PWMCTL 40
#define CLK_PWMDIV 41
#define SYST_CS 0
#define SYST_CLO 1
#define SYST_CHI 2
/* --------------------------------------------------------------- */
#define NORMAL_DMA (DMA_NO_WIDE_BURSTS | DMA_WAIT_RESP)
#define TIMED_DMA(x) (DMA_DEST_DREQ | DMA_PERIPHERAL_MAPPING(x))
#define DBG_MIN_LEVEL 0
#define DBG_STARTUP 1
#define DBG_DMACBS 2
#define DBG_USER 3
#define DBG_INTERNAL 4
#define DBG_SLOW_TICK 5
#define DBG_FAST_TICK 6
#define DBG_MAX_LEVEL 6
#define GPIO_UNDEFINED 0
#define GPIO_INPUT 1
#define GPIO_OUTPUT 2
#define GPIO_PWM 3
#define GPIO_SERVO 4
#define GPIO_ALTERNATE 5
#define STACK_SIZE (256*1024)
#define PAGE_SIZE 4096
#define PWM_FREQS 18
#define CYCLES_PER_BLOCK 80
#define PULSE_PER_CYCLE 25
#define PAGES_PER_BLOCK 53
#define CBS_PER_IPAGE 117
#define LVS_PER_IPAGE 38
#define OFF_PER_IPAGE 38
#define TCK_PER_IPAGE 2
#define ON_PER_IPAGE 2
#define CBS_PER_OPAGE 118
#define ONOFF_PER_OPAGE 79
#define CBS_PER_CYCLE ((PULSE_PER_CYCLE*3)+2)
#define NUM_CBS (CBS_PER_CYCLE * bufferCycles)
#define SUPERCYCLE 800
#define SUPERLEVEL 20000
#define BLOCK_SIZE (PAGES_PER_BLOCK*PAGE_SIZE)
#define DMA_PAGES (PAGES_PER_BLOCK * bufferBlocks)
#define TICKSLOTS 50
#define PI_NOTIFY_SLOTS 32
#define PI_NOTIFY_CLOSED 0
#define PI_NOTIFY_CLOSING 1
#define PI_NOTIFY_OPENED 2
#define PI_NOTIFY_RUNNING 3
#define PI_NOTIFY_PAUSED 4
#define PI_WFRX_NONE 0
#define PI_WFRX_SERIAL 1
#define PI_WF_MICROS 2
#define DATUMS 2000
#define DEFAULT_PWM_IDX 5
#define MAX_EMITS (PIPE_BUF / sizeof(gpioReport_t))
/* --------------------------------------------------------------- */
typedef void (*callbk_t) ();
typedef struct { /* linux/arch/arm/mach-bcm2708/include/mach/dma.h */
unsigned long info;
unsigned long src;
unsigned long dst;
unsigned long length;
unsigned long stride;
unsigned long next;
unsigned long pad[2];
} dmaCbs_t;
typedef struct
{
dmaCbs_t cb [128];
} dmaPage_t;
typedef struct
{
dmaCbs_t cb [CBS_PER_IPAGE];
uint32_t level [LVS_PER_IPAGE];
uint32_t gpioOff [OFF_PER_IPAGE];
uint32_t tick [TCK_PER_IPAGE];
uint32_t gpioOn [ON_PER_IPAGE];
uint32_t periphData;
uint32_t pad[7];
} dmaIPage_t;
typedef struct
{
dmaCbs_t cb [CBS_PER_OPAGE];
uint32_t gpioOnOff [ONOFF_PER_OPAGE];
uint32_t periphData;
} dmaOPage_t;
typedef struct
{
uint8_t is;
uint8_t pad;
uint16_t width;
uint16_t range; /* duty cycles specified by 0 .. range */
uint16_t freqIdx;
} gpioInfo_t;
typedef struct
{
callbk_t func;
unsigned ex;
void * userdata;
int timeout;
uint32_t tick;
} gpioAlert_t;
typedef struct
{
callbk_t func;
unsigned ex;
void * userdata;
} gpioSignal_t;
typedef struct
{
callbk_t func;
unsigned ex;
void * userdata;
uint32_t bits;
} gpioGetSamples_t;
typedef struct
{
callbk_t func;
unsigned ex;
void * userdata;
unsigned id;
unsigned running;
unsigned millis;
struct timespec nextTick;
pthread_t pthId;
} gpioTimer_t;
typedef struct
{
uint16_t valid;
uint16_t bits;
uint16_t divi;
uint16_t divf;
uint16_t mash;
uint16_t servoIdx;
uint16_t pwmIdx;
} clkCfg_t;
typedef struct
{
uint16_t seqno;
uint16_t state;
uint32_t bits;
int fd;
int pipe;
} gpioNotify_t;
typedef struct
{
uint32_t startTick;
uint32_t alertTicks;
uint32_t diffTick[TICKSLOTS];
uint32_t cbTicks;
uint32_t cbCalls;
uint32_t maxEmit;
uint32_t emitFrags;
uint32_t maxSamples;
uint32_t numSamples;
} gpioStats_t;
typedef struct
{
unsigned bufferMilliseconds;
unsigned clockMicros;
unsigned clockPeriph;
unsigned clockSource;
unsigned DMAprimaryChannel;
unsigned DMAsecondaryChannel;
unsigned socketPort;
unsigned ifFlags;
int dbgLevel;
unsigned showStats;
} gpioCfg_t;
typedef struct
{
uint32_t micros;
uint32_t highMicros;
uint32_t maxMicros;
uint32_t pulses;
uint32_t highPulses;
uint32_t maxPulses;
uint32_t cbs;
uint32_t highCbs;
uint32_t maxCbs;
} wfStats_t;
typedef struct
{
gpioRx_t * rxp;
uint32_t baud;
uint32_t fullBit;
uint32_t halfBit;
uint32_t startBitTick;
uint32_t nextBitDiff;
int bit;
int byte;
int level;
int mode;
} wfRx_t;
/* --------------------------------------------------------------- */
/* initialise once then preserve */
static volatile gpioCfg_t gpioCfg =
{
PI_DEFAULT_BUFFER_MILLIS,
PI_DEFAULT_CLK_MICROS,
PI_DEFAULT_CLK_PERIPHERAL,
PI_DEFAULT_CLK_SOURCE,
PI_DEFAULT_DMA_PRIMARY_CHANNEL,
PI_DEFAULT_DMA_SECONDARY_CHANNEL,
PI_DEFAULT_SOCKET_PORT,
PI_DEFAULT_IF_FLAGS,
0,
0,
};
static volatile gpioStats_t gpioStats;
static int gpioMaskSet = 0;
/* initialise every gpioInitialise */
static struct timespec libStarted;
/* initialse if not libInitialised */
static uint64_t gpioMask;
static gpioPulse_t wf[3][PI_WAVE_MAX_PULSES];
static int wfc[3]={0, 0, 0};
static int wfcur=0;
static wfStats_t wfStats=
{
0, 0, -1,
0, 0, PI_WAVE_MAX_PULSES,
0, 0, (PAGES_PER_BLOCK * CBS_PER_OPAGE)
};
static wfRx_t wfRx[PI_MAX_USER_GPIO+1];
static volatile uint32_t alertBits = 0;
static volatile uint32_t monitorBits = 0;
static volatile uint32_t notifyBits = 0;
static volatile int DMAstarted = 0;
static int libInitialised = 0;
static int pthAlertRunning = 0;
static int pthFifoRunning = 0;
static int pthSocketRunning = 0;
static gpioAlert_t gpioAlert [PI_MAX_USER_GPIO+1];
static gpioGetSamples_t gpioGetSamples;
static gpioInfo_t gpioInfo [PI_MAX_USER_GPIO+1];
static gpioNotify_t gpioNotify [PI_NOTIFY_SLOTS];
static gpioSignal_t gpioSignal [PI_MAX_SIGNUM+1];
static gpioTimer_t gpioTimer [PI_MAX_TIMER+1];
static int pwmFreq[PWM_FREQS];
/* no initialisation required */
static unsigned bufferBlocks; /* number of blocks in buffer */
static unsigned bufferCycles; /* number of cycles */
static pthread_attr_t pthAttr;
static pthread_t pthAlert;
static pthread_t pthFifo;
static pthread_t pthSocket;
static gpioSample_t gpioSample[DATUMS];
static gpioReport_t gpioReport[DATUMS];
/* resources which must be released on gpioTerminate */
static FILE * inpFifo = NULL;
static FILE * outFifo = NULL;
static int fdLock = -1;
static int fdMem = -1;
static int fdSock = -1;
static dmaPage_t * * dmaBloc = MAP_FAILED;
static dmaPage_t * * dmaVirt = MAP_FAILED;
static dmaPage_t * * dmaPhys = MAP_FAILED;
static dmaIPage_t * * dmaIVirt = MAP_FAILED;
static dmaIPage_t * * dmaIPhys = MAP_FAILED;
static dmaOPage_t * * dmaOVirt = MAP_FAILED;
static dmaOPage_t * * dmaOPhys = MAP_FAILED;
static volatile uint32_t * clkReg = MAP_FAILED;
static volatile uint32_t * dmaReg = MAP_FAILED;
static volatile uint32_t * gpioReg = MAP_FAILED;
static volatile uint32_t * pcmReg = MAP_FAILED;
static volatile uint32_t * pwmReg = MAP_FAILED;
static volatile uint32_t * systReg = MAP_FAILED;
static volatile uint32_t * dmaIn = MAP_FAILED;
static volatile uint32_t * dmaOut = MAP_FAILED;
/* constant data */
static const clkCfg_t clkCfg[]=
{
/* valid bits divi divf mash servo pwm */
{ 0, 0, 0, 0, 0, 0, 0}, /* 0 */
{ 1, 9, 2, 546, 1, 17, DEFAULT_PWM_IDX}, /* 1 */
{ 1, 19, 2, 86, 1, 16, DEFAULT_PWM_IDX}, /* 2 */
{ 0, 19, 3, 129, 1, 0, 0}, /* 3 */
{ 1, 11, 6, 4021, 1, 15, DEFAULT_PWM_IDX}, /* 4 */
{ 1, 8, 12, 0, 0, 14, DEFAULT_PWM_IDX}, /* 5 */
{ 0, 23, 5, 35, 1, 0, 0}, /* 6 */
{ 0, 27, 4004, 0, 1, 0, 0}, /* 7 */
{ 1, 51, 3, 48, 1, 13, DEFAULT_PWM_IDX}, /* 8 */
{ 0, 43, 4, 76, 1, 0, 0}, /* 9 */
{ 1, 8, 24, 0, 0, 12, DEFAULT_PWM_IDX}, /* 10 */
};
static const uint16_t pwmCycles[PWM_FREQS]=
{ 1, 2, 4, 5, 8, 10, 16, 20, 25,
32, 40, 50, 80, 100, 160, 200, 400, 800};
static const uint16_t pwmRealRange[PWM_FREQS]=
{ 25, 50, 100, 125, 200, 250, 400, 500, 625,
800, 1000, 1250, 2000, 2500, 4000, 5000, 10000, 20000};
/* ======================================================================= */
static void intNotifyBits(void);
static int gpioNotifyOpenInBand(int fd);
static void myGpioSleep(int seconds, int micros)
{
struct timespec ts, rem;
ts.tv_sec = seconds;
ts.tv_nsec = micros * 1000;
while (clock_nanosleep(CLOCK_REALTIME, 0, &ts, &rem))
{
/* copy remaining time to ts */
ts.tv_sec = rem.tv_sec;
ts.tv_nsec = rem.tv_nsec;
}
}
static uint32_t myGpioDelay(uint32_t micros)
{
uint32_t start;
start = systReg[SYST_CLO];
if (micros < 100) while ((systReg[SYST_CLO] - start) <= micros) ;
else myGpioSleep(micros/MILLION, micros%MILLION);
return (systReg[SYST_CLO] - start);
}
static char * myTimeStamp()
{
static struct timeval last;
static char buf[32];
struct timeval now;
struct tm tmp;
gettimeofday(&now, NULL);
if (now.tv_sec != last.tv_sec)
{
localtime_r(&now.tv_sec, &tmp);
strftime(buf, sizeof(buf), "%F %T", &tmp);
last.tv_sec = now.tv_sec;
}
return buf;
}
/* ----------------------------------------------------------------------- */
static void myCreatePipe(char * name, int perm)
{
unlink(name);
mkfifo(name, perm);
if (chmod(name, perm) < 0)
{
DBG(DBG_MIN_LEVEL, "Can't set permissions (%d) for %s, %m", perm, name);
return;
}
}
/* ----------------------------------------------------------------------- */
static void myOffPageSlot(int pos, int * page, int * slot)
{
*page = pos/OFF_PER_IPAGE;
*slot = pos%OFF_PER_IPAGE;
}
/* ----------------------------------------------------------------------- */
static void myLvsPageSlot(int pos, int * page, int * slot)
{
*page = pos/LVS_PER_IPAGE;
*slot = pos%LVS_PER_IPAGE;
}
/* ----------------------------------------------------------------------- */
static void myTckPageSlot(int pos, int * page, int * slot)
{
*page = pos/TCK_PER_IPAGE;
*slot = pos%TCK_PER_IPAGE;
}
/* ----------------------------------------------------------------------- */
static uint32_t myGetLevel(int pos)
{
uint32_t level;
int page, slot;
myLvsPageSlot(pos, &page, &slot);
level = dmaIVirt[page]->level[slot];
return level;
}
/* ----------------------------------------------------------------------- */
static uint32_t myGetTick(int pos)
{
uint32_t tick;
int page, slot;
myTckPageSlot(pos, &page, &slot);
tick = dmaIVirt[page]->tick[slot];
return tick;
}
/* ----------------------------------------------------------------------- */
static void myDoCommand(cmdCmd_t * cmd)
{
int p1, p2, res;
uint32_t mask;
p1 = cmd->p1;
p2 = cmd->p2;
res = 0;
switch (cmd->cmd)
{
case PI_CMD_MODES:
if (gpioMask & (uint64_t)(1<<p1)) res = gpioSetMode(p1, p2);
else
{
PERM_ERROR("gpioSetMode: gpio %d, no permission to update", p1);
res = PI_NOT_PERMITTED;
}
break;
case PI_CMD_MODEG:
res = gpioGetMode(p1);
break;
case PI_CMD_PUD:
if (gpioMask & (uint64_t)(1<<p1)) res = gpioSetPullUpDown(p1, p2);
else
{
PERM_ERROR("gpioSetPullUpDown: gpio %d, no permission to update", p1);
res = PI_NOT_PERMITTED;
}
break;
case PI_CMD_READ:
res = gpioRead(p1);
break;
case PI_CMD_WRITE:
if (gpioMask & (uint64_t)(1<<p1)) res = gpioWrite(p1, p2);
else
{
PERM_ERROR("gpioWrite: gpio %d, no permission to update", p1);
res = PI_NOT_PERMITTED;
}
break;
case PI_CMD_PWM:
if (gpioMask & (uint64_t)(1<<p1)) res = gpioPWM(p1, p2);
else
{
PERM_ERROR("gpioPWM: gpio %d, no permission to update", p1);
res = PI_NOT_PERMITTED;
}
break;
case PI_CMD_PRS:
if (gpioMask & (uint64_t)(1<<p1)) res = gpioSetPWMrange(p1, p2);
else
{
PERM_ERROR("gpioSetPWMrange: gpio %d, no permission to update", p1);
res = PI_NOT_PERMITTED;
}
break;
case PI_CMD_PFS:
if (gpioMask & (uint64_t)(1<<p1)) res = gpioSetPWMfrequency(p1, p2);
else
{
PERM_ERROR("gpioSetPWMfrequency: gpio %d, no permission to update", p1);
res = PI_NOT_PERMITTED;
}
break;
case PI_CMD_SERVO:
if (gpioMask & (uint64_t)(1<<p1)) res = gpioServo(p1, p2);
else
{
PERM_ERROR("gpioServo: gpio %d, no permission to update", p1);
res = PI_NOT_PERMITTED;
}
break;
case PI_CMD_WDOG:
res = gpioSetWatchdog(p1, p2);
break;
case PI_CMD_BR1:
res = gpioRead_Bits_0_31();
break;
case PI_CMD_BR2:
res = gpioRead_Bits_32_53();
break;
case PI_CMD_BC1:
mask = gpioMask;
res = gpioWrite_Bits_0_31_Clear(p1&mask);
if ((mask | p1) != mask)
{
PERM_ERROR("gpioWrite_Bits_0_31_Clear: bad levels %08X (permissions %08X)",
p1, mask);
res = PI_SOME_PERMITTED;
}
break;
case PI_CMD_BC2:
mask = gpioMask>>32;
res = gpioWrite_Bits_32_53_Clear(p1&mask);
if ((mask | p1) != mask)
{
PERM_ERROR("gpioWrite_Bits_32_53_Clear: bad levels %08X (permissions %08X)",
p1, mask);
res = PI_SOME_PERMITTED;
}
break;
case PI_CMD_BS1:
mask = gpioMask;
res = gpioWrite_Bits_0_31_Set(p1&mask);
if ((mask | p1) != mask)
{
PERM_ERROR("gpioWrite_Bits_0_31_Set: bad levels %08X (permissions %08X)",
p1, mask);
res = PI_SOME_PERMITTED;
}
break;
case PI_CMD_BS2:
mask = gpioMask>>32;
res = gpioWrite_Bits_32_53_Set(p1&mask);
if ((mask | p1) != mask)
{
PERM_ERROR("gpioWrite_Bits_32_53_Set: bad levels %08X (permissions %08X)",
p1, mask);
res = PI_SOME_PERMITTED;
}
break;
case PI_CMD_TICK:
res = gpioTick();
break;
case PI_CMD_HWVER:
res = gpioHardwareRevision();
break;
case PI_CMD_PRG:
res = gpioGetPWMrange(p1);
break;
case PI_CMD_PFG:
res = gpioGetPWMfrequency(p1);
break;
case PI_CMD_PRRG:
res = gpioGetPWMrealRange(p1);
break;
case PI_CMD_NO:
res = gpioNotifyOpen();
break;
case PI_CMD_NB:
res = gpioNotifyBegin(p1, p2);
break;
case PI_CMD_NP:
res = gpioNotifyPause(p1);
break;
case PI_CMD_NC:
res = gpioNotifyClose(p1);
break;
case PI_CMD_HELP:
break;
}
cmd->res = res;
}
/* ----------------------------------------------------------------------- */
static void mySetGpioOff(unsigned gpio, int pos)
{
int page, slot;
myOffPageSlot(pos, &page, &slot);
dmaIVirt[page]->gpioOff[slot] |= (1<<gpio);
}
/* ----------------------------------------------------------------------- */
static void myClearGpioOff(unsigned gpio, int pos)
{
int page, slot;
myOffPageSlot(pos, &page, &slot);
dmaIVirt[page]->gpioOff[slot] &= ~(1<<gpio);
}
/* ----------------------------------------------------------------------- */
static void mySetGpioOn(unsigned gpio, int pos)
{
int page, slot;
page = pos/ON_PER_IPAGE;
slot = pos%ON_PER_IPAGE;
dmaIVirt[page]->gpioOn[slot] |= (1<<gpio);
}
/* ----------------------------------------------------------------------- */
static void myClearGpioOn(unsigned gpio, int pos)
{
int page, slot;
page = pos/ON_PER_IPAGE;
slot = pos%ON_PER_IPAGE;
dmaIVirt[page]->gpioOn[slot] &= ~(1<<gpio);
}
/* ----------------------------------------------------------------------- */
static void myGpioSetPwm(unsigned gpio, int oldVal, int newVal)
{
int switchGpioOff;
int newOff, oldOff, realRange, cycles, i;
DBG(DBG_INTERNAL,
"myGpioSetPwm %d from %d to %d", gpio, oldVal, newVal);
switchGpioOff = 0;
realRange = pwmRealRange[gpioInfo[gpio].freqIdx];
cycles = pwmCycles [gpioInfo[gpio].freqIdx];
newOff = (newVal * realRange)/gpioInfo[gpio].range;
oldOff = (oldVal * realRange)/gpioInfo[gpio].range;
if (newOff != oldOff)
{
if (newOff && oldOff) /* PWM CHANGE */
{
for (i=0; i<SUPERLEVEL; i+=realRange)
mySetGpioOff(gpio, i+newOff);
for (i=0; i<SUPERLEVEL; i+=realRange)
myClearGpioOff(gpio, i+oldOff);
}
else if (newOff) /* PWM START */
{
for (i=0; i<SUPERLEVEL; i+=realRange)
mySetGpioOff(gpio, i+newOff);
/* schedule new gpio on */
for (i=0; i<SUPERCYCLE; i+=cycles) mySetGpioOn(gpio, i);
}
else /* PWM STOP */
{
/* deschedule gpio on */
for (i=0; i<SUPERCYCLE; i+=cycles)
myClearGpioOn(gpio, i);
for (i=0; i<SUPERLEVEL; i+=realRange)
myClearGpioOff(gpio, i+oldOff);
switchGpioOff = 1;
}
if (switchGpioOff)
{
*(gpioReg + GPCLR0) = (1<<gpio);
*(gpioReg + GPCLR0) = (1<<gpio);
}
}
}
/* ----------------------------------------------------------------------- */
static void myGpioSetServo(unsigned gpio, int oldVal, int newVal)
{
int switchGpioOff;
int newOff, oldOff, realRange, cycles, i;
DBG(DBG_INTERNAL,
"myGpioSetServo %d from %d to %d", gpio, oldVal, newVal);
switchGpioOff = 0;
realRange = pwmRealRange[gpioInfo[gpio].freqIdx];
cycles = pwmCycles [gpioInfo[gpio].freqIdx];
newOff = (newVal * realRange)/20000;
oldOff = (oldVal * realRange)/20000;
if (newOff != oldOff)
{
if (newOff && oldOff) /* SERVO CHANGE */
{
for (i=0; i<SUPERLEVEL; i+=realRange)
mySetGpioOff(gpio, i+newOff);
for (i=0; i<SUPERLEVEL; i+=realRange)
myClearGpioOff(gpio, i+oldOff);
}
else if (newOff) /* SERVO START */
{
for (i=0; i<SUPERLEVEL; i+=realRange)
mySetGpioOff(gpio, i+newOff);
/* schedule new gpio on */
for (i=0; i<SUPERCYCLE; i+=cycles)
mySetGpioOn(gpio, i);
}
else /* SERVO STOP */
{
/* deschedule gpio on */
for (i=0; i<SUPERCYCLE; i+=cycles)
myClearGpioOn(gpio, i);
for (i=0; i<SUPERLEVEL; i+=realRange)
myClearGpioOff(gpio, i+oldOff);
switchGpioOff = 1;
}
if (switchGpioOff)
{
*(gpioReg + GPCLR0) = (1<<gpio);
*(gpioReg + GPCLR0) = (1<<gpio);
}
}
}
/* ======================================================================= */
static dmaCbs_t * waveCbVOadr(int pos)
{
int page, slot;
page = pos/CBS_PER_OPAGE;
slot = pos%CBS_PER_OPAGE;
return &dmaOVirt[page]->cb[slot];
}
/* ----------------------------------------------------------------------- */
static uint32_t waveCbPOadr(int pos)
{
int page, slot;
page = pos/CBS_PER_OPAGE;
slot = pos%CBS_PER_OPAGE;
return (uint32_t) &dmaOPhys[page]->cb[slot];
}
/* ----------------------------------------------------------------------- */
static uint32_t waveOnOffPOadr(int pos)
{
int page, slot;
page = pos/ONOFF_PER_OPAGE;
slot = pos%ONOFF_PER_OPAGE;
return (uint32_t) &dmaOPhys[page]->gpioOnOff[slot];
}
/* ----------------------------------------------------------------------- */
static void waveCbOPrint(int pos)
{
dmaCbs_t * p;
p = waveCbVOadr(pos);
fprintf(stderr, "i=%lx s=%lx d=%lx len=%lx s=%lx nxt=%lx\n",
p->info, p->src, p->dst, p->length, p->stride, p->next);
}
/* ----------------------------------------------------------------------- */
void waveBitDelay(unsigned baud, unsigned * bitDelay)
{
unsigned fullBit, halfBit, s, e, d, m, i, err;
fullBit = 100000000 / baud;
halfBit = 50000000 / baud;
d = (fullBit/200)*200;
s = 0;
e = d;
bitDelay[0] = d/100;
err = d / 3;
for (i=0; i<8; i++)
{
s = e;
m = halfBit + (i+1)*fullBit;
e = s + d;
if ((e-m) < err) e+=200;
bitDelay[i+1] = (e-s)/100;
}
s = e;
e = ((1000000000 / baud)+100)/200*200;
bitDelay[9] = (e-s)/100;
}
/* ----------------------------------------------------------------------- */
void gpioWaveDump(void)
{
int i;
unsigned numPulses, t;
gpioPulse_t * pulses;
numPulses = wfc[wfcur];
pulses = wf [wfcur];
t = 0;
for (i=0; i<numPulses; i++)
{
printf("%10u %08X %08X %10u\n",
t, pulses[i].gpioOn, pulses[i].gpioOff, pulses[i].usDelay);
t += pulses[i].usDelay;
}
}
/* ----------------------------------------------------------------------- */
static int wave2Cbs(unsigned mode)
{
int cb=0, onoff=0;
dmaCbs_t * p=NULL;
unsigned i, half, repeatCb;
unsigned numPulses;
gpioPulse_t * pulses;
numPulses = wfc[wfcur];
pulses = wf [wfcur];
half = PI_WF_MICROS/2;
/* add delay cb at start of DMA */
p = waveCbVOadr(cb++);
/* use the secondary clock */
if (gpioCfg.clockPeriph != PI_CLOCK_PCM)
{
p->info = NORMAL_DMA |
DMA_DEST_DREQ |
DMA_PERIPHERAL_MAPPING(2);
p->dst = ((PCM_BASE + PCM_FIFO*4) & 0x00ffffff) | 0x7e000000;
}
else
{
p->info = NORMAL_DMA |
DMA_DEST_DREQ |
DMA_PERIPHERAL_MAPPING(5);
p->dst = ((PWM_BASE + PWM_FIFO*4) & 0x00ffffff) | 0x7e000000;
}
p->src = (uint32_t) (&dmaOPhys[0]->periphData) | DMA_BUS_ADR;
p->length = 4 * 50 / PI_WF_MICROS; /* 50 micros delay */
p->next = waveCbPOadr(cb) | DMA_BUS_ADR;
repeatCb = cb;
for (i=0; i<numPulses; i++)
{
if (pulses[i].gpioOn)
{
dmaOVirt[onoff/ONOFF_PER_OPAGE]->gpioOnOff[onoff%ONOFF_PER_OPAGE] =
pulses[i].gpioOn;
p = waveCbVOadr(cb++);
p->info = NORMAL_DMA;
p->src = waveOnOffPOadr(onoff++) | DMA_BUS_ADR;
p->dst = ((GPIO_BASE + (GPSET0*4)) & 0x00ffffff) | 0x7e000000;
p->length = 4;
p->next = waveCbPOadr(cb) | DMA_BUS_ADR;
}
if (pulses[i].gpioOff)
{
dmaOVirt[onoff/ONOFF_PER_OPAGE]->gpioOnOff[onoff%ONOFF_PER_OPAGE] =
pulses[i].gpioOff;
p = waveCbVOadr(cb++);
p->info = NORMAL_DMA;
p->src = waveOnOffPOadr(onoff++) | DMA_BUS_ADR;
p->dst = ((GPIO_BASE + (GPCLR0*4)) & 0x00ffffff) | 0x7e000000;
p->length = 4;
p->next = waveCbPOadr(cb) | DMA_BUS_ADR;
}
if (pulses[i].usDelay)
{
p = waveCbVOadr(cb++);
/* use the secondary clock */
if (gpioCfg.clockPeriph != PI_CLOCK_PCM)
{
p->info = NORMAL_DMA |
DMA_DEST_DREQ |
DMA_PERIPHERAL_MAPPING(2);
p->dst = ((PCM_BASE + PCM_FIFO*4) & 0x00ffffff) | 0x7e000000;
}
else
{
p->info = NORMAL_DMA |
DMA_DEST_DREQ |
DMA_PERIPHERAL_MAPPING(5);
p->dst = ((PWM_BASE + PWM_FIFO*4) & 0x00ffffff) | 0x7e000000;
}
p->src = (uint32_t) (&dmaOPhys[0]->periphData) | DMA_BUS_ADR;
p->length = 4 * ((pulses[i].usDelay+half)/PI_WF_MICROS);
p->next = waveCbPOadr(cb) | DMA_BUS_ADR;
}
}
if (p != NULL)
{
if (mode == PI_WAVE_MODE_ONE_SHOT)
p->next = 0;
else p->next = waveCbPOadr(repeatCb) | DMA_BUS_ADR;
}
return cb;
}
/* ----------------------------------------------------------------------- */
static void waveRxSerial(wfRx_t * s, int level, uint32_t tick)
{
int diffTicks;
if (s->bit >= 0)
{
diffTicks = tick - s->startBitTick;
if (level != PI_TIMEOUT) s->level = level;
while ((s->bit < 9) && (diffTicks > s->nextBitDiff))
{
if (s->bit)
{
if (!(s->level)) s->byte |= (1<<(s->bit-1));
}
else s->byte = 0;
++(s->bit);
s->nextBitDiff += s->fullBit;
}
if (s->bit == 9)
{
s->rxp->buf[s->rxp->writePos] = s->byte;
if (++s->rxp->writePos >= s->rxp->bufSize) s->rxp->writePos = 0;
if (level == 0) /* true transition high->low, not a timeout */
{
s->bit = 0;
s->startBitTick = tick;
s->nextBitDiff = s->halfBit;
}
else s->bit = -1;
}
}
else
{
/* start bit if high->low */
if (level == 0)
{
s->level = 0;
s->bit = 0;
s->startBitTick = tick;
s->nextBitDiff = s->halfBit;
}
}
}
/* ----------------------------------------------------------------------- */
static void waveRxBit(int gpio, int level, uint32_t tick)
{
switch (wfRx[gpio].mode)
{
case PI_WFRX_NONE:
break;
case PI_WFRX_SERIAL:
waveRxSerial(&wfRx[gpio], level, tick);
}
}
/* ----------------------------------------------------------------------- */
static int waveMerge(unsigned numIn1, gpioPulse_t * in1)
{
unsigned inPos1=0, inPos2=0, outPos=0;
unsigned cbs=0;
unsigned numIn2, numOut;
uint32_t tNow, tNext1, tNext2, tDelay;
gpioPulse_t * in2, * out;
numIn2 = wfc[wfcur];
in2 = wf[wfcur];
numOut = PI_WAVE_MAX_PULSES;
out = wf[1-wfcur];
tNow = 0;
if (!numIn1) tNext1 = -1; else tNext1 = 0;
if (!numIn2) tNext2 = -1; else tNext2 = 0;
while (((inPos1<numIn1) || (inPos2<numIn2)) && (outPos<numOut))
{
if (tNext1 < tNext2)
{
/* pulse 1 due */
if (tNow < tNext1)
{
/* extend previous delay */
out[outPos-1].usDelay += (tNext1 - tNow);
tNow = tNext1;
}
out[outPos].gpioOn = in1[inPos1].gpioOn;
out[outPos].gpioOff = in1[inPos1].gpioOff;
tNext1 = tNow + in1[inPos1].usDelay; ++inPos1;
}
else if (tNext2 < tNext1)
{
/* pulse 2 due */
if (tNow < tNext2)
{
/* extend previous delay */
out[outPos-1].usDelay += (tNext2 - tNow);
tNow = tNext2;
}
out[outPos].gpioOn = in2[inPos2].gpioOn;
out[outPos].gpioOff = in2[inPos2].gpioOff;
tNext2 = tNow + in2[inPos2].usDelay; ++inPos2;
}
else
{
/* pulse 1 and 2 both due */
if (tNow < tNext1)
{
/* extend previous delay */
out[outPos-1].usDelay += (tNext1 - tNow);
tNow = tNext1;
}
out[outPos].gpioOn = in1[inPos1].gpioOn | in2[inPos2].gpioOn;
out[outPos].gpioOff = in1[inPos1].gpioOff | in2[inPos2].gpioOff;
tNext1 = tNow + in1[inPos1].usDelay; ++inPos1;
tNext2 = tNow + in2[inPos2].usDelay; ++inPos2;
}
if (tNext1 <= tNext2) { tDelay = tNext1 - tNow; tNow = tNext1; }
else { tDelay = tNext2 - tNow; tNow = tNext2; }
out[outPos].usDelay = tDelay;
cbs++; /* one cb for delay */
if (out[outPos].gpioOn) cbs++; /* one cb if gpio on */
if (out[outPos].gpioOff) cbs++; /* one cb if gpio off */
outPos++;
if (inPos1 >= numIn1) tNext1 = -1;
if (inPos2 >= numIn2) tNext2 = -1;
}
if (outPos < numOut)
{
wfStats.micros = tNow;
if (tNow > wfStats.highMicros) wfStats.highMicros = tNow;
wfStats.pulses = outPos;
if (outPos > wfStats.highPulses) wfStats.highPulses = outPos;
wfStats.cbs = cbs;
if (cbs > wfStats.highCbs) wfStats.highCbs = cbs;
wfc[1-wfcur] = outPos;
wfcur = 1 - wfcur;
return outPos;
}
else return PI_TOO_MANY_PULSES;
}
/* ======================================================================= */
static dmaCbs_t * dmaCB2adr(int pos)
{
int page, slot;
page = pos/CBS_PER_IPAGE;
slot = pos%CBS_PER_IPAGE;
return &dmaIVirt[page]->cb[slot];
}
/* ----------------------------------------------------------------------- */
static void dmaCbPrint(int pos)
{
dmaCbs_t * p;
p = dmaCB2adr(pos);
fprintf(stderr, "i=%lx s=%lx d=%lx len=%lx s=%lx nxt=%lx\n",
p->info, p->src, p->dst, p->length, p->stride, p->next);
}
/* ----------------------------------------------------------------------- */
static unsigned dmaCurrentCb(void)
{
unsigned cb;
static unsigned lastPage=0;
unsigned page;
uint32_t cbAddr;
uint32_t startTick, endTick;
startTick = systReg[SYST_CLO];
cbAddr = dmaIn[DMA_CONBLK_AD];
page = lastPage;
/* which page are we dma'ing? */
while (1)
{
cb = (cbAddr - ((int)dmaIPhys[page] | DMA_BUS_ADR)) / 32;
if (cb < CBS_PER_IPAGE)
{
endTick = systReg[SYST_CLO];
if (endTick != startTick)
gpioStats.cbTicks += (endTick - startTick);
else gpioStats.cbTicks ++;
gpioStats.cbCalls++;
lastPage = page;
return (page*CBS_PER_IPAGE) + cb;
}
if (page++ >= DMA_PAGES) page=0;
if (page == lastPage) break;
}
return 0;
}
/* ----------------------------------------------------------------------- */
static unsigned dmaCurrentSlot(unsigned pos)
{
unsigned cycle=0, slot=0, tmp;
cycle = (pos/CBS_PER_CYCLE);
tmp = (pos%CBS_PER_CYCLE);
if (tmp > 2) slot = ((tmp-2)/3);
return (cycle*PULSE_PER_CYCLE)+slot;
}
/* ----------------------------------------------------------------------- */
static uint32_t dmaPwmDataAdr(int pos)
{
return (uint32_t) &dmaIPhys[pos]->periphData;
}
static uint32_t dmaGpioOnAdr(int pos)
{
int page, slot;
page = pos/ON_PER_IPAGE;
slot = pos%ON_PER_IPAGE;
return (uint32_t) &dmaIPhys[page]->gpioOn[slot];
}
/* ----------------------------------------------------------------------- */
static uint32_t dmaGpioOffAdr(int pos)
{
int page, slot;
myOffPageSlot(pos, &page, &slot);
return (uint32_t) &dmaIPhys[page]->gpioOff[slot];
}
/* ----------------------------------------------------------------------- */
static uint32_t dmaTickAdr(int pos)
{
int page, slot;
myTckPageSlot(pos, &page, &slot);
return (uint32_t) &dmaIPhys[page]->tick[slot];
}
/* ----------------------------------------------------------------------- */
static uint32_t dmaReadLevelsAdr(int pos)
{
int page, slot;
myLvsPageSlot(pos, &page, &slot);
return (uint32_t) &dmaIPhys[page]->level[slot];
}
/* ----------------------------------------------------------------------- */
static uint32_t dmaCbAdr(int pos)
{
int page, slot;
page = (pos/CBS_PER_IPAGE);
slot = (pos%CBS_PER_IPAGE);
return (uint32_t) &dmaIPhys[page]->cb[slot];
}
/* ----------------------------------------------------------------------- */
static void dmaGpioOnCb(int b, int pos)
{
dmaCbs_t * p;
p = dmaCB2adr(b);
p->info = NORMAL_DMA;
p->src = dmaGpioOnAdr(pos) | DMA_BUS_ADR;
p->dst = ((GPIO_BASE + (GPSET0*4)) & 0x00ffffff) | 0x7e000000;
p->length = 4;
p->next = dmaCbAdr(b+1) | DMA_BUS_ADR;
}
/* ----------------------------------------------------------------------- */
static void dmaTickCb(int b, int pos)
{
dmaCbs_t * p;
p = dmaCB2adr(b);
p->info = NORMAL_DMA;
p->src = ((SYST_BASE + (SYST_CLO*4)) & 0x00ffffff) | 0x7e000000;
p->dst = dmaTickAdr(pos) | DMA_BUS_ADR;
p->length = 4;
p->next = dmaCbAdr(b+1) | DMA_BUS_ADR;
}
/* ----------------------------------------------------------------------- */
static void dmaGpioOffCb(int b, int pos)
{
dmaCbs_t * p;
p = dmaCB2adr(b);
p->info = NORMAL_DMA;
p->src = dmaGpioOffAdr(pos) | DMA_BUS_ADR;
p->dst = ((GPIO_BASE + (GPCLR0*4)) & 0x00ffffff) | 0x7e000000;
p->length = 4;
p->next = dmaCbAdr(b+1) | DMA_BUS_ADR;
}
/* ----------------------------------------------------------------------- */
static void dmaReadLevelsCb(int b, int pos)
{
dmaCbs_t * p;
p = dmaCB2adr(b);
p->info = NORMAL_DMA;
p->src = ((GPIO_BASE + (GPLEV0*4)) & 0x00ffffff) | 0x7e000000;
p->dst = dmaReadLevelsAdr(pos) | DMA_BUS_ADR;
p->length = 4;
p->next = dmaCbAdr(b+1) | DMA_BUS_ADR;
}
/* ----------------------------------------------------------------------- */
static void dmaDelayCb(int b)
{
dmaCbs_t * p;
p = dmaCB2adr(b);
if (gpioCfg.clockPeriph == PI_CLOCK_PCM)
{
p->info = NORMAL_DMA | TIMED_DMA(2);
p->dst = ((PCM_BASE + PCM_FIFO*4) & 0x00ffffff) | 0x7e000000;
}
else
{
p->info = NORMAL_DMA | TIMED_DMA(5);
p->dst = ((PWM_BASE + PWM_FIFO*4) & 0x00ffffff) | 0x7e000000;
}
p->src = dmaPwmDataAdr(b%DMA_PAGES) | DMA_BUS_ADR;
p->length = 4;
p->next = dmaCbAdr(b+1) | DMA_BUS_ADR;
}
/* ----------------------------------------------------------------------- */
static void dmaInitCbs(void)
{
int b, pulse, level, cycle;
dmaCbs_t * p;
/* set up the DMA control blocks */
DBG(DBG_STARTUP, "");
b = -1;
level = 0;
for (cycle=0; cycle<bufferCycles; cycle++)
{
b++; dmaGpioOnCb(b, cycle%SUPERCYCLE); /* gpio on slot */
b++; dmaTickCb(b, cycle); /* tick slot */
for (pulse=0; pulse<PULSE_PER_CYCLE; pulse++)
{
b++; dmaReadLevelsCb(b, level); /* read levels slot */
b++; dmaDelayCb(b); /* delay slot */
b++; dmaGpioOffCb(b, (level%SUPERLEVEL)+1); /* gpio off slot */
++level;
}
}
/* point last cb back to first for continuous loop */
p = dmaCB2adr(b);
p->next = dmaCbAdr(0) | DMA_BUS_ADR;
DBG(DBG_STARTUP, "DMA page type size = %d", sizeof(dmaIPage_t));
DBG(DBG_STARTUP, "%d control blocks (exp=%d)", b+1, NUM_CBS);
}
/* ======================================================================= */
static void sigHandler(int signum)
{
if ((signum >= PI_MIN_SIGNUM) && (signum <= PI_MAX_SIGNUM))
{
if (gpioSignal[signum].func)
{
if (gpioSignal[signum].ex)
{
(gpioSignal[signum].func)(signum, gpioSignal[signum].userdata);
}
else
{
(gpioSignal[signum].func)(signum);
}
}
else
{
if (signum == SIGUSR1)
{
if (gpioCfg.dbgLevel > DBG_MIN_LEVEL)
{
--gpioCfg.dbgLevel;
}
else gpioCfg.dbgLevel = DBG_MIN_LEVEL;
DBG(DBG_USER, "Debug level %d\n", gpioCfg.dbgLevel);
}
else if (signum == SIGUSR2)
{
if (gpioCfg.dbgLevel < DBG_MAX_LEVEL)
{
++gpioCfg.dbgLevel;
}
else gpioCfg.dbgLevel = DBG_MAX_LEVEL;
DBG(DBG_USER, "Debug level %d\n", gpioCfg.dbgLevel);
}
else if (signum == SIGPIPE)
{
DBG(DBG_USER, "SIGPIPE received");
}
else
{
/* exit */
DBG(DBG_MIN_LEVEL, "Unhandled signal %d, terminating\n", signum);
exit(-1);
}
}
}
else
{
/* exit */
DBG(DBG_MIN_LEVEL, "Unhandled signal %d, terminating\n", signum);
exit(-1);
}
}
/* ----------------------------------------------------------------------- */
static void sigSetHandler(void)
{
int i;
struct sigaction new;
for (i=PI_MIN_SIGNUM; i<=PI_MAX_SIGNUM; i++)
{
memset(&new, 0, sizeof(new));
new.sa_handler = sigHandler;
sigaction(i, &new, NULL);
}
}
/* ======================================================================= */
static void * pthAlertThread(void *x)
{
struct timespec req, rem;
uint32_t oldLevel, newLevel, level, reportedLevel;
uint32_t oldSlot, newSlot;
uint32_t tick, expected;
int32_t diff;
int cycle, pulse;
int emit, seqno, emitted;
uint32_t changes, bits, changedBits, timeoutBits;
int numSamples, d;
int b, n, v;
int err;
char fifo[32];
req.tv_sec = 0;
/* don't start until DMA started */
while (!DMAstarted) myGpioDelay(1000);
myGpioDelay(20000); /* let DMA run for a while */
reportedLevel = gpioReg[GPLEV0];
tick = systReg[SYST_CLO];
gpioStats.startTick = tick;
oldSlot = dmaCurrentSlot(dmaCurrentCb());
cycle = (oldSlot/PULSE_PER_CYCLE);
pulse = (oldSlot%PULSE_PER_CYCLE);
while (1)
{
gpioStats.alertTicks++;
req.tv_nsec = 850000;
while (nanosleep(&req, &rem))
{
req.tv_sec = rem.tv_sec;
req.tv_nsec = rem.tv_nsec;
}
newSlot = dmaCurrentSlot(dmaCurrentCb());
numSamples = 0;
changedBits = 0;
oldLevel = reportedLevel & monitorBits;
while ((oldSlot != newSlot) && (numSamples < DATUMS))
{
level = myGetLevel(oldSlot++);
newLevel = (level & monitorBits);
if (newLevel != oldLevel)
{
gpioSample[numSamples].tick = tick;
gpioSample[numSamples].level = level;
changedBits |= (newLevel ^ oldLevel);
oldLevel = newLevel;
numSamples++;
}
tick += gpioCfg.clockMicros;
if (++pulse >= PULSE_PER_CYCLE)
{
pulse = 0;
if (++cycle >= bufferCycles)
{
cycle = 0;
oldSlot = 0;
}
expected = tick;
tick = myGetTick(cycle);
diff = tick - expected;
diff += (TICKSLOTS/2);
if (diff < 0) gpioStats.diffTick[0]++;
else if (diff >= TICKSLOTS)
gpioStats.diffTick[TICKSLOTS-1]++;
else gpioStats.diffTick[diff]++;
}
}
/* should gpioGetSamples be called */
if (changedBits)
{
if (gpioGetSamples.func)
{
if (gpioGetSamples.ex)
{
(gpioGetSamples.func)
(gpioSample, numSamples, gpioGetSamples.userdata);
}
else
{
(gpioGetSamples.func)
(gpioSample, numSamples);
}
}
}
/* reset timeouts for any changed bits */
if (changedBits)
{
for (b=0; b<=PI_MAX_USER_GPIO; b++)
{
if (changedBits & (1<<b)) gpioAlert[b].tick = tick;
}
}
/* call alert callbacks for each bit transition */
if (changedBits & alertBits)
{
oldLevel = reportedLevel & alertBits;
for (d=0; d<numSamples; d++)
{
newLevel = gpioSample[d].level & alertBits;
if (newLevel != oldLevel)
{
changes = newLevel ^ oldLevel;
for (b=0; b<=PI_MAX_USER_GPIO; b++)
{
if (changes & (1<<b))
{
if (newLevel & (1<<b)) v = 1; else v = 0;
if (gpioAlert[b].func)
{
if (gpioAlert[b].ex)
{
(gpioAlert[b].func)
(b, v, gpioSample[d].tick,
gpioAlert[b].userdata);
}
else
{
(gpioAlert[b].func)
(b, v, gpioSample[d].tick);
}
}
}
}
oldLevel = newLevel;
}
}
}
/* check for timeout watchdogs */
timeoutBits = 0;
for (b=0; b<=PI_MAX_USER_GPIO; b++)
{
if (gpioAlert[b].timeout)
{
diff = tick - gpioAlert[b].tick;
if (diff > (gpioAlert[b].timeout*1000))
{
timeoutBits |= (1<<b);
gpioAlert[b].tick += (gpioAlert[b].timeout*1000);
if (gpioAlert[b].func)
{
if (gpioAlert[b].ex)
{
(gpioAlert[b].func)(b, 2, tick, gpioAlert[b].userdata);
}
else
{
(gpioAlert[b].func)(b, 2, tick);
}
}
}
}
}
for (n=0; n<PI_NOTIFY_SLOTS; n++)
{
if (gpioNotify[n].state == PI_NOTIFY_CLOSING)
{
if (gpioNotify[n].pipe)
{
close(gpioNotify[n].fd);
sprintf(fifo, "/dev/pigpio%d", n);
unlink(fifo);
}
gpioNotify[n].state = PI_NOTIFY_CLOSED;
}
else if (gpioNotify[n].state == PI_NOTIFY_RUNNING)
{
bits = gpioNotify[n].bits;
emit = 0;
seqno = gpioNotify[n].seqno;
/* check to see if any bits have changed for this
notification.
bits is the set of notification bits
changedBits is the set of changed bits
*/
if (changedBits & bits)
{
oldLevel = reportedLevel & bits;
for (d=0; d<numSamples; d++)
{
newLevel = gpioSample[d].level & bits;
if (newLevel != oldLevel)
{
gpioReport[emit].seqno = seqno;
gpioReport[emit].flags = 0;
gpioReport[emit].tick = gpioSample[d].tick;
gpioReport[emit].level = gpioSample[d].level;
oldLevel = newLevel;
emit++;
seqno++;
}
}
}
/* check to see if any watchdogs are due for this
notification.
bits is the set of notification bits
timeoutBits is the set of timed out bits
*/
if (timeoutBits & bits)
{
/* at least one watchdog has fired for this
notification.
*/
for (b=0; b<=PI_MAX_USER_GPIO; b++)
{
if (timeoutBits & bits & (1<<b))
{
if (numSamples)
newLevel = gpioSample[numSamples-1].level;
else
newLevel = reportedLevel;
gpioReport[emit].seqno = seqno;
gpioReport[emit].flags = PI_NTFY_FLAGS_WDOG |
PI_NTFY_FLAGS_BIT(b);
gpioReport[emit].tick = tick;
gpioReport[emit].level = newLevel;
emit++;
seqno++;
}
}
}
if (emit)
{
if (emit > gpioStats.maxEmit) gpioStats.maxEmit = emit;
emitted = 0;
while (emit > 0)
{
if (emit > MAX_EMITS)
{
gpioStats.emitFrags++;
err = write(gpioNotify[n].fd,
gpioReport+emitted,
MAX_EMITS*sizeof(gpioReport_t));
if (err != (MAX_EMITS*sizeof(gpioReport_t)))
{
DBG(0, "fd=%d err=%d errno=%d",
gpioNotify[n].fd, err, errno);
if (err < 0) DBG(0, "%s", strerror(errno));
if ((err != EAGAIN) && (err != EWOULDBLOCK))
{
/* serious error, no point continuing */
gpioNotify[n].bits = 0;
gpioNotify[n].state = PI_NOTIFY_CLOSING;
intNotifyBits();
break;
}
}
emitted += MAX_EMITS;
emit -= MAX_EMITS;
}
else
{
err = write(gpioNotify[n].fd,
gpioReport+emitted,
emit*sizeof(gpioReport_t));
if (err != (emit*sizeof(gpioReport_t)))
{
DBG(0, "fd=%d err=%d errno=%d",
gpioNotify[n].fd, err, errno);
if (err < 0) DBG(0, "%s", strerror(errno));
if ((err != EAGAIN) && (err != EWOULDBLOCK))
{
/* serious error, no point continuing */
gpioNotify[n].bits = 0;
gpioNotify[n].state = PI_NOTIFY_CLOSING;
intNotifyBits();
break;
}
}
emitted += emit;
emit = 0;
}
}
gpioNotify[n].seqno = seqno;
}
}
}
/* once all outputs have been emitted set reported level */
if (numSamples) reportedLevel = gpioSample[numSamples-1].level;
if (numSamples > gpioStats.maxSamples)
gpioStats.maxSamples = numSamples;
gpioStats.numSamples += numSamples;
}
return 0;
}
/* ----------------------------------------------------------------------- */
static void * pthTimerTick(void *x)
{
gpioTimer_t * tp;
struct timespec req, rem, period;
char buf[256];
tp = x;
clock_gettime(CLOCK_REALTIME, &tp->nextTick);
while (1)
{
clock_gettime(CLOCK_REALTIME, &rem);
period.tv_sec = tp->millis / THOUSAND;
period.tv_nsec = (tp->millis % THOUSAND) * THOUSAND * THOUSAND;
do
{
TIMER_ADD(&tp->nextTick, &period, &tp->nextTick);
TIMER_SUB(&tp->nextTick, &rem, &req);
}
while (req.tv_sec < 0);
while (nanosleep(&req, &rem))
{
req.tv_sec = rem.tv_sec;
req.tv_nsec = rem.tv_nsec;
}
if (gpioCfg.dbgLevel >= DBG_SLOW_TICK)
{
if ((tp->millis > 50) || (gpioCfg.dbgLevel >= DBG_FAST_TICK))
{
sprintf(buf, "pigpio: TIMER=%d @ %u %u\n",
tp->id,
(unsigned)tp->nextTick.tv_sec,
(unsigned)tp->nextTick.tv_nsec);
fprintf(stderr, buf);
}
}
if (tp->ex) (tp->func)(tp->userdata);
else (tp->func)();
}
return 0;
}
/* ----------------------------------------------------------------------- */
static void * pthFifoThread(void *x)
{
char inBuf[128];
int idx, flags;
cmdCmd_t cmd;
myCreatePipe(PI_INPFIFO, 0662);
if ((inpFifo = fopen(PI_INPFIFO, "r+")) == NULL)
SOFT_ERROR((void*)PI_INIT_FAILED, "fopen %s failed(%m)", PI_INPFIFO);
myCreatePipe(PI_OUTFIFO, 0664);
if ((outFifo = fopen(PI_OUTFIFO, "w+")) == NULL)
SOFT_ERROR((void*)PI_INIT_FAILED, "fopen %s failed (%m)", PI_OUTFIFO);
/* set outFifo non-blocking */
flags = fcntl(fileno(outFifo), F_GETFL, 0);
fcntl(fileno(outFifo), F_SETFL, flags | O_NONBLOCK);
while (1)
{
if (fgets(inBuf, sizeof(inBuf), inpFifo) == NULL)
SOFT_ERROR((void*)PI_INIT_FAILED, "fifo fgets failed (%m)");
if ((idx=cmdParse(inBuf, &cmd)) >= 0)
{
myDoCommand(&cmd);
switch (cmdInfo[idx].rv)
{
case 0:
fprintf(outFifo, "%d\n", cmd.res);
break;
case 1:
fprintf(outFifo, "%d\n", cmd.res);
break;
case 2:
fprintf(outFifo, "%d\n", cmd.res);
break;
case 3:
fprintf(outFifo, "%08X\n", cmd.res);
break;
case 4:
fprintf(outFifo, "%u\n", cmd.res);
break;
case 5:
fprintf(outFifo, cmdUsage);
break;
}
}
else fprintf(outFifo, "%d\n", PI_BAD_FIFO_COMMAND);
fflush(outFifo);
}
return 0;
}
/* ----------------------------------------------------------------------- */
static void * pthSocketThreadHandler(void *fdC)
{
int sock = *(int*)fdC;
cmdCmd_t cmd;
free(fdC);
while(1)
{
if (recv(sock, &cmd, sizeof(cmdCmd_t), 0) == sizeof(cmdCmd_t))
{
if (cmd.cmd != PI_CMD_NOIB)
{
myDoCommand(&cmd);
}
else
{
cmd.res = gpioNotifyOpenInBand(sock);
}
write(sock, &cmd, sizeof(cmdCmd_t));
}
else break;
}
close(sock);
return 0;
}
/* ----------------------------------------------------------------------- */
static void * pthSocketThread(void *x)
{
int fdC, c, *sock;
struct sockaddr_in server, client;
pthread_attr_t attr;
char * portStr;
unsigned port;
if (pthread_attr_init(&attr))
SOFT_ERROR((void*)PI_INIT_FAILED,
"pthread_attr_init failed (%m)");
if (pthread_attr_setstacksize(&attr, STACK_SIZE))
SOFT_ERROR((void*)PI_INIT_FAILED,
"pthread_attr_setstacksize failed (%m)");
if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED))
SOFT_ERROR((void*)PI_INIT_FAILED,
"pthread_attr_setdetachstate failed (%m)");
fdSock = socket(AF_INET , SOCK_STREAM , 0);
if (fdSock == -1)
SOFT_ERROR((void*)PI_INIT_FAILED, "socket failed (%m)");
portStr = getenv(PI_ENVPORT);
if (portStr) port = atoi(portStr); else port = gpioCfg.socketPort;
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(port);
if (bind(fdSock,(struct sockaddr *)&server , sizeof(server)) < 0)
SOFT_ERROR((void*)PI_INIT_FAILED, "bind failed (%m)");
listen(fdSock, 100);
c = sizeof(struct sockaddr_in);
while ((fdC =
accept(fdSock, (struct sockaddr *)&client, (socklen_t*)&c)))
{
pthread_t thr;
sock = malloc(sizeof(int));
*sock = fdC;
if (pthread_create
(&thr, &attr, pthSocketThreadHandler, (void*) sock) < 0)
SOFT_ERROR((void*)PI_INIT_FAILED,
"socket pthread_create failed (%m)");
}
if (fdC < 0)
SOFT_ERROR((void*)PI_INIT_FAILED, "accept failed (%m)");
return 0;
}
/* ======================================================================= */
static int initGrabLockFile(void)
{
int fd;
int lockResult;
char pidStr[20];
/* try to grab the lock file */
fd = open(PI_LOCKFILE, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0644);
if (fd != -1)
{
lockResult = flock(fd, LOCK_EX|LOCK_NB);
if(lockResult == 0)
{
sprintf(pidStr, "%d\n", (int)getpid());
write(fd, pidStr, strlen(pidStr));
}
else
{
close(fd);
return -1;
}
}
return fd;
}
/* ----------------------------------------------------------------------- */
static int initZaps
(
int pmapFd,
dmaPage_t *dmaV1,
dmaPage_t *dmaV2[],
dmaPage_t *dmaP[],
int pages)
{
int n;
long index;
off_t offset;
ssize_t t;
int status;
uint32_t pageAdr2;
unsigned long long pa;
DBG(DBG_STARTUP, "");
status = 0;
pageAdr2 = (uint32_t) dmaV2[0];
index = ((uint32_t)dmaV1 / PAGE_SIZE) * 8;
offset = lseek(pmapFd, index, SEEK_SET);
if (offset != index)
SOFT_ERROR(PI_INIT_FAILED, "lseek pagemap failed (%m)");
for (n=0; n<pages; n++)
{
t = read(pmapFd, &pa, sizeof(pa));
if (t != sizeof(pa))
SOFT_ERROR(PI_INIT_FAILED, "read pagemap failed (%m)");
DBG(DBG_STARTUP, "pf%d=%016llX", n, pa);
dmaP[n] = (dmaPage_t *) (uint32_t) (PAGE_SIZE * (pa & 0xFFFFFFFF));
dmaV2[n] = mmap
(
(void *)pageAdr2,
PAGE_SIZE,
PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_FIXED|MAP_LOCKED|MAP_NORESERVE,
fdMem,
(uint32_t)dmaP[n] | 0x40000000
);
pageAdr2 += PAGE_SIZE;
if (dmaP[n] == 0) status = 1;
}
return status;
}
/* ----------------------------------------------------------------------- */
static uint32_t * initMapMem(int fd, uint32_t addr, uint32_t len)
{
return (uint32_t *) mmap(0, len,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_SHARED|MAP_LOCKED,
fd, addr);
}
/* ----------------------------------------------------------------------- */
static int initCheckPermitted(void)
{
DBG(DBG_STARTUP, "");
if ((fdMem = open("/dev/mem", O_RDWR | O_SYNC) ) < 0)
{
fprintf(stderr,
"\n" \
"+---------------------------------------------------------+\n" \
"|Sorry, you don't have permission to run this program. |\n" \
"|Try running as root, e.g. precede the command with sudo. |\n" \
"+---------------------------------------------------------+\n\n");
exit(-1);
}
return 0;
}
/* ----------------------------------------------------------------------- */
static int initPeripherals(void)
{
uint32_t dmaBase;
DBG(DBG_STARTUP, "");
gpioReg = initMapMem(fdMem, GPIO_BASE, GPIO_LEN);
if (gpioReg == MAP_FAILED)
SOFT_ERROR(PI_INIT_FAILED, "mmap gpio failed (%m)");
/* dma channels 0-14 share one page, 15 has another */
if (gpioCfg.DMAprimaryChannel < 15)
{
dmaBase = DMA_BASE;
}
else dmaBase = DMA15_BASE;
dmaReg = initMapMem(fdMem, dmaBase, DMA_LEN);
if (dmaReg == MAP_FAILED)
SOFT_ERROR(PI_INIT_FAILED, "mmap dma failed (%m)");
if (gpioCfg.DMAprimaryChannel < 15)
{
dmaIn = dmaReg + (gpioCfg.DMAprimaryChannel * 0x40);
dmaOut = dmaReg + (gpioCfg.DMAsecondaryChannel * 0x40);
}
DBG(DBG_STARTUP, "DMA #%d @ %08X @ %08X",
gpioCfg.DMAprimaryChannel, dmaBase, (uint32_t)dmaIn);
DBG(DBG_STARTUP, "debug reg is %08X", dmaIn[DMA_DEBUG]);
clkReg = initMapMem(fdMem, CLK_BASE, CLK_LEN);
if (clkReg == MAP_FAILED)
SOFT_ERROR(PI_INIT_FAILED, "mmap clk failed (%m)");
systReg = initMapMem(fdMem, SYST_BASE, SYST_LEN);
if (systReg == MAP_FAILED)
SOFT_ERROR(PI_INIT_FAILED, "mmap syst failed (%m)");
pwmReg = initMapMem(fdMem, PWM_BASE, PWM_LEN);
if (pwmReg == MAP_FAILED)
SOFT_ERROR(PI_INIT_FAILED, "mmap pwm failed (%m)");
pcmReg = initMapMem(fdMem, PCM_BASE, PCM_LEN);
if (pcmReg == MAP_FAILED)
SOFT_ERROR(PI_INIT_FAILED, "mmap pcm failed (%m)");
return 0;
}
/* ----------------------------------------------------------------------- */
static int initDMAblock(int pagemapFd, int block)
{
int trys, ok;
unsigned pageNum;
DBG(DBG_STARTUP, "");
dmaBloc[block] = mmap(
0, (PAGES_PER_BLOCK*PAGE_SIZE),
PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_ANONYMOUS|MAP_NORESERVE|MAP_LOCKED,
-1, 0);
if (dmaBloc[block] == MAP_FAILED)
SOFT_ERROR(PI_INIT_FAILED, "mmap dma block %d failed (%m)", block);
/* force allocation of physical memory */
memset((void *)dmaBloc[block], 0, (PAGES_PER_BLOCK*PAGE_SIZE));
pageNum = block * PAGES_PER_BLOCK;
dmaVirt[pageNum] = mmap(
0, (PAGES_PER_BLOCK*PAGE_SIZE),
PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_ANONYMOUS|MAP_NORESERVE|MAP_LOCKED,
-1, 0);
if (dmaVirt[pageNum] == MAP_FAILED)
SOFT_ERROR(PI_INIT_FAILED, "mmap dma block %d failed (%m)", block);
munmap(dmaVirt[pageNum], PAGES_PER_BLOCK*PAGE_SIZE);
trys = 0;
ok = 0;
while ((trys < 10) && !ok)
{
if (initZaps(pagemapFd,
dmaBloc[block],
&dmaVirt[pageNum],
&dmaPhys[pageNum],
PAGES_PER_BLOCK) == 0) ok = 1;
else myGpioDelay(50000);
++trys;
}
if (!ok) SOFT_ERROR(PI_INIT_FAILED, "initZaps failed");
return 0;
}
/* ----------------------------------------------------------------------- */
static int initDMAcbs(void)
{
int pid;
char str[64];
int pagemapFd;
int i, servoCycles, superCycles;
DBG(DBG_STARTUP, "");
/* Calculate the number of blocks needed for buffers. The number
of blocks must be a multiple of the 20ms servo cycle.
*/
servoCycles = gpioCfg.bufferMilliseconds / 20;
if (gpioCfg.bufferMilliseconds % 20) servoCycles++;
bufferCycles = (SUPERCYCLE * servoCycles) / gpioCfg.clockMicros;
superCycles = bufferCycles / SUPERCYCLE;
if (bufferCycles % SUPERCYCLE) superCycles++;
bufferCycles = SUPERCYCLE * superCycles;
bufferBlocks = bufferCycles / CYCLES_PER_BLOCK;
DBG(DBG_STARTUP, "bmillis=%d mics=%d bblk=%d bcyc=%d",
gpioCfg.bufferMilliseconds, gpioCfg.clockMicros,
bufferBlocks, bufferCycles);
/* allocate memory for pointers to virtual and physical pages */
dmaBloc = mmap(
0, (bufferBlocks+PI_WAVE_BLOCKS)*sizeof(dmaPage_t *),
PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_LOCKED,
-1, 0);
if (dmaBloc == MAP_FAILED)
SOFT_ERROR(PI_INIT_FAILED, "mmap dma virtual failed (%m)");
dmaVirt = mmap(
0, PAGES_PER_BLOCK*(bufferBlocks+PI_WAVE_BLOCKS)*sizeof(dmaPage_t *),
PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_LOCKED,
-1, 0);
if (dmaVirt == MAP_FAILED)
SOFT_ERROR(PI_INIT_FAILED, "mmap dma virtual failed (%m)");
dmaPhys = mmap(
0, PAGES_PER_BLOCK*(bufferBlocks+PI_WAVE_BLOCKS)*sizeof(dmaPage_t *),
PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_LOCKED,
-1, 0);
if (dmaPhys == MAP_FAILED)
SOFT_ERROR(PI_INIT_FAILED, "mmap dma physical failed (%m)");
dmaIPhys = (dmaIPage_t **) dmaPhys;
dmaIVirt = (dmaIPage_t **) dmaVirt;
dmaOPhys = (dmaOPage_t **)(dmaPhys + (PAGES_PER_BLOCK*bufferBlocks));
dmaOVirt = (dmaOPage_t **)(dmaVirt + (PAGES_PER_BLOCK*bufferBlocks));
pid = getpid();
sprintf(str, "/proc/%d/pagemap", pid);
pagemapFd = open(str, O_RDONLY);
if (pagemapFd < 0)
SOFT_ERROR(PI_INIT_FAILED, "open pagemap failed(%m)");
for (i=0; i<(bufferBlocks+PI_WAVE_BLOCKS); i++) initDMAblock(pagemapFd, i);
close(pagemapFd);
DBG(DBG_STARTUP, "dmaBloc=%08X dmaIn=%08X",
(uint32_t)dmaBloc, (uint32_t)dmaIn);
DBG(DBG_STARTUP, "gpioReg=%08X pwmReg=%08X pcmReg=%08X clkReg=%08X",
(uint32_t)gpioReg, (uint32_t)pwmReg,
(uint32_t)pcmReg, (uint32_t)clkReg);
for (i=0; i<DMA_PAGES; i++)
DBG(DBG_STARTUP, "dmaIPhys[%d]=%08X", i, (uint32_t)dmaIPhys[i]);
dmaInitCbs();
if (gpioCfg.dbgLevel >= DBG_DMACBS)
{
fprintf(stderr, "*** INPUT DMA CONTROL BLOCKS ***\n");
for (i=0; i<NUM_CBS; i++) dmaCbPrint(i);
}
return 0;
}
/* ----------------------------------------------------------------------- */
static void initPWM(unsigned bits)
{
DBG(DBG_STARTUP, "");
/* reset PWM */
pwmReg[PWM_CTL] = 0;
myGpioDelay(10);
pwmReg[PWM_STA] = -1;
myGpioDelay(10);
/* set number of bits to transmit */
pwmReg[PWM_RNG1] = bits;
myGpioDelay(10);
dmaIVirt[0]->periphData = 1;
/* enable PWM DMA, raise panic and dreq thresholds to 15 */
pwmReg[PWM_DMAC] = PWM_DMAC_ENAB |
PWM_DMAC_PANIC(15) |
PWM_DMAC_DREQ(15);
myGpioDelay(10);
/* clear PWM fifo */
pwmReg[PWM_CTL] = PWM_CTL_CLRF1;
myGpioDelay(10);
/* enable PWM channel 1 and use fifo */
pwmReg[PWM_CTL] = PWM_CTL_USEF1 | PWM_CTL_MODE1 | PWM_CTL_PWEN1;
}
/* ----------------------------------------------------------------------- */
static void initPCM(unsigned bits)
{
DBG(DBG_STARTUP, "");
/* disable PCM so we can modify the regs */
pcmReg[PCM_CS] = 0;
myGpioDelay(1000);
pcmReg[PCM_FIFO] = 0;
pcmReg[PCM_MODE] = 0;
pcmReg[PCM_RXC] = 0;
pcmReg[PCM_TXC] = 0;
pcmReg[PCM_DREQ] = 0;
pcmReg[PCM_INTEN] = 0;
pcmReg[PCM_INTSTC] = 0;
pcmReg[PCM_GRAY] = 0;
myGpioDelay(1000);
pcmReg[PCM_MODE] = PCM_MODE_FLEN(bits-1); /* # bits in frame */
/* enable channel 1 with # bits width */
pcmReg[PCM_TXC] = PCM_TXC_CH1EN | PCM_TXC_CH1WID(bits-8);
pcmReg[PCM_CS] |= PCM_CS_STBY; /* clear standby */
myGpioDelay(1000);
pcmReg[PCM_CS] |= PCM_CS_TXCLR; /* clear TX FIFO */
pcmReg[PCM_CS] |= PCM_CS_DMAEN; /* enable DREQ */
pcmReg[PCM_DREQ] = PCM_DREQ_TX_PANIC(16) | PCM_DREQ_TX_REQ_L(30);
pcmReg[PCM_INTSTC] = 0b1111; /* clear status bits */
/* enable PCM */
pcmReg[PCM_CS] |= PCM_CS_EN ;
/* enable tx */
pcmReg[PCM_CS] |= PCM_CS_TXON;
dmaIVirt[0]->periphData = 0x0F;
}
/* ----------------------------------------------------------------------- */
static void initClock(int mainClock)
{
unsigned clkCtl, clkDiv, clkSrc, clkDivI, clkDivF, clkMash, clkBits;
char * per, * src;
unsigned micros;
DBG(DBG_STARTUP, "");
if (mainClock) micros = gpioCfg.clockMicros;
else micros = PI_WF_MICROS;
if ((gpioCfg.clockPeriph == PI_CLOCK_PCM) && mainClock)
{
clkCtl = CLK_PCMCTL;
clkDiv = CLK_PCMDIV;
per = "PCM";
}
else
{
clkCtl = CLK_PWMCTL;
clkDiv = CLK_PWMDIV;
per = "PWM";
}
if (gpioCfg.clockSource == PI_CLOCK_PLLD)
{
clkSrc = CLK_CTL_SRC_PLLD;
clkDivI = 50 * micros;
clkDivF = 0;
clkMash = 0;
clkBits = 10;
src = "PLLD";
}
else
{
clkSrc = CLK_CTL_SRC_OSC;
clkDivI = clkCfg[micros].divi;
clkDivF = clkCfg[micros].divf;
clkMash = clkCfg[micros].mash;
clkBits = clkCfg[micros].bits;
src = "OSC";
}
DBG(DBG_STARTUP, "%s %s divi=%d divf=%d mash=%d bits=%d",
per, src, clkDivI, clkDivF, clkMash, clkBits);
clkReg[clkCtl] = CLK_PASSWD | CLK_CTL_KILL;
myGpioDelay(10);
clkReg[clkDiv] =
(CLK_PASSWD | CLK_DIV_DIVI(clkDivI) | CLK_DIV_DIVF(clkDivF));
myGpioDelay(10);
clkReg[clkCtl] =
(CLK_PASSWD | CLK_CTL_MASH(clkMash) | CLK_CTL_SRC(clkSrc));
myGpioDelay(10);
clkReg[clkCtl] |= (CLK_PASSWD | CLK_CTL_ENAB);
myGpioDelay(10);
if ((gpioCfg.clockPeriph == PI_CLOCK_PCM) && mainClock)
initPCM(clkBits);
else initPWM(clkBits);
myGpioDelay(2000);
}
/* ----------------------------------------------------------------------- */
static void initDMAgo(volatile uint32_t * dmaAddr, uint32_t cbAddr)
{
DBG(DBG_STARTUP, "");
dmaAddr[DMA_CS] = DMA_CHANNEL_RESET;
dmaAddr[DMA_CS] = DMA_INTERRUPT_STATUS | DMA_END_FLAG;
dmaAddr[DMA_CONBLK_AD] = cbAddr | DMA_BUS_ADR;
/* clear READ/FIFO/READ_LAST_NOT_SET error bits */
dmaAddr[DMA_DEBUG] = DMA_DEBUG_READ_ERR |
DMA_DEBUG_FIFO_ERR |
DMA_DEBUG_RD_LST_NOT_SET_ERR;
dmaAddr[DMA_CS] = DMA_WAIT_ON_WRITES |
DMA_PANIC_PRIORITY(8) |
DMA_PRIORITY(8) |
DMA_ACTIVATE;
DMAstarted = 1;
}
/* ----------------------------------------------------------------------- */
static void initClearGlobals(void)
{
int i;
DBG(DBG_STARTUP, "");
alertBits = 0;
monitorBits = 0;
notifyBits = 0;
libInitialised = 0;
DMAstarted = 0;
pthAlertRunning = 0;
pthFifoRunning = 0;
pthSocketRunning = 0;
wfc[0] = 0;
wfc[1] = 0;
wfc[2] = 0;
wfcur=0;
wfStats.micros = 0;
wfStats.highMicros = 0;
wfStats.maxMicros = -1;
wfStats.pulses = 0;
wfStats.highPulses = 0;
wfStats.maxPulses = PI_WAVE_MAX_PULSES;
wfStats.cbs = 0;
wfStats.highCbs = 0;
wfStats.maxCbs = (PAGES_PER_BLOCK * CBS_PER_OPAGE);
gpioGetSamples.func = NULL;
gpioGetSamples.ex = 0;
gpioGetSamples.userdata = NULL;
gpioGetSamples.bits = 0;
for (i=0; i<=PI_MAX_USER_GPIO; i++)
{
wfRx[i].mode = PI_WFRX_NONE;
gpioAlert[i].func = NULL;
gpioInfo [i].is = GPIO_UNDEFINED;
gpioInfo [i].width = 0;
gpioInfo [i].range = PI_DEFAULT_DUTYCYCLE_RANGE;
gpioInfo [i].freqIdx = DEFAULT_PWM_IDX;
}
for (i=0; i<PI_NOTIFY_SLOTS; i++)
{
gpioNotify[i].seqno = 0;
gpioNotify[i].state = PI_NOTIFY_CLOSED;
}
for (i=0; i<=PI_MAX_SIGNUM; i++)
{
gpioSignal[i].func = NULL;
gpioSignal[i].ex = 0;
gpioSignal[i].userdata = NULL;
}
for (i=0; i<=PI_MAX_TIMER; i++)
{
gpioTimer[i].running = 0;
gpioTimer[i].func = NULL;
}
/* calculate the usable PWM frequencies */
for (i=0; i<PWM_FREQS; i++)
{
pwmFreq[i]=
(1000000.0/
((float)PULSE_PER_CYCLE*gpioCfg.clockMicros*pwmCycles[i]))+0.5;
DBG(DBG_STARTUP, "f%d is %d", i, pwmFreq[i]);
}
inpFifo = NULL;
outFifo = NULL;
fdLock = -1;
fdMem = -1;
fdSock = -1;
dmaBloc = MAP_FAILED;
dmaVirt = MAP_FAILED;
dmaPhys = MAP_FAILED;
clkReg = MAP_FAILED;
dmaReg = MAP_FAILED;
gpioReg = MAP_FAILED;
pcmReg = MAP_FAILED;
pwmReg = MAP_FAILED;
systReg = MAP_FAILED;
}
/* ----------------------------------------------------------------------- */
static void initReleaseResources(void)
{
int i;
DBG(DBG_STARTUP, "");
/* shut down running threads */
for (i=0; i<=PI_MAX_TIMER; i++)
{
if (gpioTimer[i].running)
{
/* destroy thread */
pthread_cancel(gpioTimer[i].pthId);
pthread_join(gpioTimer[i].pthId, NULL);
gpioTimer[i].running = 0;
}
}
if (pthAlertRunning)
{
pthread_cancel(pthAlert);
pthread_join(pthAlert, NULL);
pthAlertRunning = 0;
}
if (pthFifoRunning)
{
pthread_cancel(pthFifo);
pthread_join(pthFifo, NULL);
pthFifoRunning = 0;
}
if (pthSocketRunning)
{
pthread_cancel(pthSocket);
pthread_join(pthSocket, NULL);
pthSocketRunning = 0;
}
/* release mmap'd memory */
if (clkReg != MAP_FAILED) munmap((void *)clkReg, CLK_LEN);
if (dmaReg != MAP_FAILED) munmap((void *)dmaReg, DMA_LEN);
if (gpioReg != MAP_FAILED) munmap((void *)gpioReg, GPIO_LEN);
if (pcmReg != MAP_FAILED) munmap((void *)pcmReg, PCM_LEN);
if (pwmReg != MAP_FAILED) munmap((void *)pwmReg, PWM_LEN);
if (systReg != MAP_FAILED) munmap((void *)systReg, SYST_LEN);
clkReg = MAP_FAILED;
dmaReg = MAP_FAILED;
gpioReg = MAP_FAILED;
pcmReg = MAP_FAILED;
pwmReg = MAP_FAILED;
systReg = MAP_FAILED;
if (dmaVirt != MAP_FAILED)
{
for (i=0; i<PAGES_PER_BLOCK*(bufferBlocks+PI_WAVE_BLOCKS); i++)
{
munmap(dmaVirt[i], PAGE_SIZE);
}
munmap(dmaVirt, PAGES_PER_BLOCK*(bufferBlocks+PI_WAVE_BLOCKS)*sizeof(dmaPage_t *));
}
dmaVirt = MAP_FAILED;
if (dmaPhys != MAP_FAILED)
{
for (i=0; i<PAGES_PER_BLOCK*(bufferBlocks+PI_WAVE_BLOCKS); i++)
{
munmap(dmaPhys[i], PAGE_SIZE);
}
munmap(dmaPhys, PAGES_PER_BLOCK*(bufferBlocks+PI_WAVE_BLOCKS)*sizeof(dmaPage_t *));
}
dmaPhys = MAP_FAILED;
if (dmaBloc != MAP_FAILED)
{
for (i=0; i<(bufferBlocks+PI_WAVE_BLOCKS); i++)
{
munmap(dmaBloc[i], PAGES_PER_BLOCK*PAGE_SIZE);
}
munmap(dmaBloc, (bufferBlocks+PI_WAVE_BLOCKS)*sizeof(dmaPage_t *));
}
dmaBloc = MAP_FAILED;
if (inpFifo != NULL)
{
fclose(inpFifo);
unlink(PI_INPFIFO);
inpFifo = NULL;
}
if (outFifo != NULL)
{
fclose(outFifo);
unlink(PI_OUTFIFO);
outFifo = NULL;
}
if (fdMem != -1)
{
close(fdMem);
fdMem = -1;
}
if (fdLock != -1)
{
close(fdLock);
unlink(PI_LOCKFILE);
fdLock = -1;
}
if (fdSock != -1)
{
close(fdSock);
fdSock = -1;
}
}
/* ======================================================================= */
int gpioInitialise(void)
{
int i;
clock_gettime(CLOCK_REALTIME, &libStarted);
DBG(DBG_STARTUP, "");
if (libInitialised) return PIGPIO_VERSION;
initClearGlobals();
if (initCheckPermitted() < 0) return PI_INIT_FAILED;
fdLock = initGrabLockFile();
if (fdLock < 0)
SOFT_ERROR(PI_INIT_FAILED, "Can't lock %s", PI_LOCKFILE);
if (!gpioMaskSet)
{
i = gpioHardwareRevision();
if (i == 0) gpioMask = PI_DEFAULT_UPDATE_MASK_R0;
else if (i < 4) gpioMask = PI_DEFAULT_UPDATE_MASK_R1;
else gpioMask = PI_DEFAULT_UPDATE_MASK_R2;
gpioMaskSet = 1;
}
sigSetHandler();
if (initPeripherals() < 0) return PI_INIT_FAILED;
if (initDMAcbs() < 0) return PI_INIT_FAILED;
/* done with /dev/mem */
if (fdMem != -1)
{
close(fdMem);
fdMem = -1;
}
initClock(1); /* initialise main clock */
libInitialised = 1;
atexit(gpioTerminate);
if (pthread_attr_init(&pthAttr))
SOFT_ERROR(PI_INIT_FAILED, "pthread_attr_init failed (%m)");
if (pthread_attr_setstacksize(&pthAttr, STACK_SIZE))
SOFT_ERROR(PI_INIT_FAILED, "pthread_attr_setstacksize failed (%m)");
if (pthread_create(&pthAlert, &pthAttr, pthAlertThread, &i))
SOFT_ERROR(PI_INIT_FAILED, "pthread_create alert failed (%m)");
pthAlertRunning = 1;
if (!(gpioCfg.ifFlags & PI_DISABLE_FIFO_IF))
{
if (pthread_create(&pthFifo, &pthAttr, pthFifoThread, &i))
SOFT_ERROR(PI_INIT_FAILED, "pthread_create fifo failed (%m)");
pthFifoRunning = 1;
}
if (!(gpioCfg.ifFlags & PI_DISABLE_SOCK_IF))
{
if (pthread_create(&pthSocket, &pthAttr, pthSocketThread, &i))
SOFT_ERROR(PI_INIT_FAILED, "pthread_create socket failed (%m)");
pthSocketRunning = 1;
}
initDMAgo((uint32_t *)dmaIn, (uint32_t)dmaIPhys[0]);
return PIGPIO_VERSION;
}
/* ----------------------------------------------------------------------- */
void gpioTerminate(void)
{
int i;
DBG(DBG_USER, "");
gpioMaskSet = 0;
if (libInitialised)
{
/* reset DMA */
dmaIn[DMA_CS] = DMA_CHANNEL_RESET;
dmaOut[DMA_CS] = DMA_CHANNEL_RESET;
/* reset PWM */
pwmReg[PWM_CTL] = 0;
libInitialised = 0;
DMAstarted = 0;
if (gpioCfg.showStats)
{
fprintf(stderr, "micros=%d\n", gpioCfg.clockMicros);
fprintf(stderr, "samples %u maxSamples %u maxEmit %u emitFrags %u\n",
gpioStats.numSamples, gpioStats.maxSamples,
gpioStats.maxEmit, gpioStats.emitFrags);
fprintf(stderr, "cb time %d, calls %u alert ticks %u\n",
gpioStats.cbTicks, gpioStats.cbCalls, gpioStats.alertTicks);
for (i=0; i< TICKSLOTS; i++)
fprintf(stderr, "%9u ", gpioStats.diffTick[i]);
fprintf(stderr, "\n");
}
}
initReleaseResources();
fflush(NULL);
}
/* ----------------------------------------------------------------------- */
int gpioSetMode(unsigned gpio, unsigned mode)
{
int reg, shift;
DBG(DBG_USER, "gpio=%d mode=%d", gpio, mode);
CHECK_INITED;
if (gpio > PI_MAX_GPIO)
SOFT_ERROR(PI_BAD_GPIO, "bad gpio (%d)", gpio);
if (mode > PI_ALT3)
SOFT_ERROR(PI_BAD_MODE, "gpio %d, bad mode (%d)", gpio, mode);
reg = gpio/10;
shift = (gpio%10) * 3;
if (gpio <= PI_MAX_USER_GPIO)
{
if (mode != PI_OUTPUT)
{
switch (gpioInfo[gpio].is)
{
case GPIO_SERVO:
/* switch servo off */
myGpioSetServo(gpio,
gpioInfo[gpio].width/gpioCfg.clockMicros, 0);
break;
case GPIO_PWM:
/* switch pwm off */
myGpioSetPwm(gpio, gpioInfo[gpio].width, 0);
break;
}
gpioInfo[gpio].is = GPIO_UNDEFINED;
}
}
gpioReg[reg] = (gpioReg[reg] & ~(7<<shift)) | (mode<<shift);
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioGetMode(unsigned gpio)
{
int reg, shift;
DBG(DBG_USER, "gpio=%d", gpio);
CHECK_INITED;
if (gpio > PI_MAX_GPIO)
SOFT_ERROR(PI_BAD_GPIO, "bad gpio (%d)", gpio);
reg = gpio/10;
shift = (gpio%10) * 3;
return (*(gpioReg + reg) >> shift) & 7;
}
/* ----------------------------------------------------------------------- */
int gpioSetPullUpDown(unsigned gpio, unsigned pud)
{
DBG(DBG_USER, "gpio=%d pud=%d", gpio, pud);
CHECK_INITED;
if (gpio > PI_MAX_GPIO)
SOFT_ERROR(PI_BAD_GPIO, "bad gpio (%d)", gpio);
if (pud > PI_PUD_UP)
SOFT_ERROR(PI_BAD_PUD, "gpio %d, bad pud (%d)", gpio, pud);
*(gpioReg + GPPUD) = pud;
myGpioDelay(20);
*(gpioReg + GPPUDCLK0 + BANK) = BIT;
myGpioDelay(20);
*(gpioReg + GPPUD) = 0;
*(gpioReg + GPPUDCLK0 + BANK) = 0;
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioRead(unsigned gpio)
{
DBG(DBG_USER, "gpio=%d", gpio);
CHECK_INITED;
if (gpio > PI_MAX_GPIO)
SOFT_ERROR(PI_BAD_GPIO, "bad gpio (%d)", gpio);
if ((*(gpioReg + GPLEV0 + BANK) & BIT) != 0) return PI_ON;
else return PI_OFF;
}
/* ----------------------------------------------------------------------- */
int gpioWrite(unsigned gpio, unsigned level)
{
DBG(DBG_USER, "gpio=%d level=%d", gpio, level);
CHECK_INITED;
if (gpio > PI_MAX_GPIO)
SOFT_ERROR(PI_BAD_GPIO, "bad gpio (%d)", gpio);
if (level > PI_ON)
SOFT_ERROR(PI_BAD_LEVEL, "gpio %d, bad level (%d)", gpio, level);
if (gpio <= PI_MAX_USER_GPIO)
{
if (gpioInfo[gpio].is != GPIO_OUTPUT)
{
if (gpioInfo[gpio].is == GPIO_UNDEFINED)
{
gpioSetMode(gpio, PI_OUTPUT);
}
else if (gpioInfo[gpio].is == GPIO_PWM)
{
/* switch pwm off */
myGpioSetPwm(gpio, gpioInfo[gpio].width, 0);
}
else if (gpioInfo[gpio].is == GPIO_SERVO)
{
/* switch servo off */
myGpioSetServo(
gpio, gpioInfo[gpio].width/gpioCfg.clockMicros, 0);
}
gpioInfo[gpio].is=GPIO_OUTPUT;
gpioInfo[gpio].width=0;
}
}
if (level == PI_OFF) *(gpioReg + GPCLR0 + BANK) = BIT;
else *(gpioReg + GPSET0 + BANK) = BIT;
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioPWM(unsigned gpio, unsigned val)
{
DBG(DBG_USER, "gpio=%d dutycycle=%d", gpio, val);
CHECK_INITED;
if (gpio > PI_MAX_USER_GPIO)
SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
if (val > gpioInfo[gpio].range)
SOFT_ERROR(PI_BAD_DUTYCYCLE, "gpio %d, bad dutycycle (%d)", gpio, val);
if (gpioInfo[gpio].is != GPIO_PWM)
{
if (gpioInfo[gpio].is == GPIO_UNDEFINED)
{
gpioSetMode(gpio, PI_OUTPUT);
}
else if (gpioInfo[gpio].is == GPIO_SERVO)
{
/* switch servo off */
myGpioSetServo(gpio, gpioInfo[gpio].width, 0);
gpioInfo[gpio].width = 0;
gpioInfo[gpio].freqIdx = DEFAULT_PWM_IDX; /* default frequency */
}
gpioInfo[gpio].is = GPIO_PWM;
}
myGpioSetPwm(gpio, gpioInfo[gpio].width, val);
gpioInfo[gpio].width=val;
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioSetPWMrange(unsigned gpio, unsigned range)
{
int oldWidth, newWidth;
DBG(DBG_USER, "gpio=%d range=%d", gpio, range);
CHECK_INITED;
if (gpio > PI_MAX_USER_GPIO)
SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
if ((range < PI_MIN_DUTYCYCLE_RANGE) || (range > PI_MAX_DUTYCYCLE_RANGE))
SOFT_ERROR(PI_BAD_DUTYRANGE, "gpio %d, bad range (%d)", gpio, range);
oldWidth = gpioInfo[gpio].width;
if (oldWidth)
{
if (gpioInfo[gpio].is == GPIO_PWM)
{
newWidth = (range * oldWidth) / gpioInfo[gpio].range;
myGpioSetPwm(gpio, oldWidth, 0);
gpioInfo[gpio].range = range;
gpioInfo[gpio].width = newWidth;
myGpioSetPwm(gpio, 0, newWidth);
}
}
gpioInfo[gpio].range = range;
/* return the actual range for the current gpio frequency */
return pwmRealRange[gpioInfo[gpio].freqIdx];
}
/* ----------------------------------------------------------------------- */
int gpioGetPWMrange(unsigned gpio)
{
DBG(DBG_USER, "gpio=%d", gpio);
CHECK_INITED;
if (gpio > PI_MAX_USER_GPIO)
SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
return (gpioInfo[gpio].range);
}
/* ----------------------------------------------------------------------- */
int gpioGetPWMrealRange(unsigned gpio)
{
DBG(DBG_USER, "gpio=%d", gpio);
CHECK_INITED;
if (gpio > PI_MAX_USER_GPIO)
SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
return pwmRealRange[gpioInfo[gpio].freqIdx];
}
/* ----------------------------------------------------------------------- */
int gpioSetPWMfrequency(unsigned gpio, unsigned frequency)
{
int i, width;
unsigned diff, best, idx;
DBG(DBG_USER, "gpio=%d frequency=%d", gpio, frequency);
CHECK_INITED;
if (gpio > PI_MAX_USER_GPIO)
SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
if (frequency > pwmFreq[0]) idx = 0;
else if (frequency < pwmFreq[PWM_FREQS-1]) idx = PWM_FREQS-1;
else
{
best = 100000; /* impossibly high frequency difference */
idx = 0;
for (i=0; i<PWM_FREQS; i++)
{
if (frequency > pwmFreq[i]) diff = frequency - pwmFreq[i];
else diff = pwmFreq[i] - frequency;
if (diff < best)
{
best = diff;
idx = i;
}
}
}
width = gpioInfo[gpio].width;
if (width)
{
if (gpioInfo[gpio].is == GPIO_PWM)
{
myGpioSetPwm(gpio, width, 0);
gpioInfo[gpio].freqIdx = idx;
myGpioSetPwm(gpio, 0, width);
}
}
gpioInfo[gpio].freqIdx = idx;
return pwmFreq[idx];
}
/* ----------------------------------------------------------------------- */
int gpioGetPWMfrequency(unsigned gpio)
{
DBG(DBG_USER, "gpio=%d", gpio);
CHECK_INITED;
if (gpio > PI_MAX_USER_GPIO)
SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
return (pwmFreq[gpioInfo[gpio].freqIdx]);
}
/* ----------------------------------------------------------------------- */
int gpioServo(unsigned gpio, unsigned val)
{
DBG(DBG_USER, "gpio=%d pulsewidth=%d", gpio, val);
CHECK_INITED;
if (gpio > PI_MAX_USER_GPIO)
SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
if ((val!=PI_SERVO_OFF) && (val<PI_MIN_SERVO_PULSEWIDTH))
SOFT_ERROR(PI_BAD_PULSEWIDTH,
"gpio %d, bad pulsewidth (%d)", gpio, val);
if (val>PI_MAX_SERVO_PULSEWIDTH)
SOFT_ERROR(PI_BAD_PULSEWIDTH,
"gpio %d, bad pulsewidth (%d)", gpio, val);
if (gpioInfo[gpio].is != GPIO_SERVO)
{
if (gpioInfo[gpio].is == GPIO_UNDEFINED)
{
gpioSetMode(gpio, PI_OUTPUT);
}
else if (gpioInfo[gpio].is == GPIO_PWM)
{
/* switch pwm off */
myGpioSetPwm(gpio, gpioInfo[gpio].width, 0);
gpioInfo[gpio].width=0;
}
gpioInfo[gpio].is = GPIO_SERVO;
gpioInfo[gpio].freqIdx = clkCfg[gpioCfg.clockMicros].servoIdx;
}
myGpioSetServo(gpio, gpioInfo[gpio].width, val);
gpioInfo[gpio].width=val;
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioWaveGetMicros(void)
{
DBG(DBG_USER, "");
CHECK_INITED;
return wfStats.micros;
}
/* ----------------------------------------------------------------------- */
int gpioWaveGetHighMicros(void)
{
DBG(DBG_USER, "");
CHECK_INITED;
return wfStats.highMicros;
}
/* ----------------------------------------------------------------------- */
int gpioWaveGetMaxMicros(void)
{
DBG(DBG_USER, "");
CHECK_INITED;
return wfStats.maxMicros;
}
/* ----------------------------------------------------------------------- */
int gpioWaveGetPulses(void)
{
DBG(DBG_USER, "");
CHECK_INITED;
return wfStats.pulses;
}
/* ----------------------------------------------------------------------- */
int gpioWaveGetHighPulses(void)
{
DBG(DBG_USER, "");
CHECK_INITED;
return wfStats.highPulses;
}
/* ----------------------------------------------------------------------- */
int gpioWaveGetMaxPulses(void)
{
DBG(DBG_USER, "");
CHECK_INITED;
return wfStats.maxPulses;
}
/* ----------------------------------------------------------------------- */
int gpioWaveGetCbs(void)
{
DBG(DBG_USER, "");
CHECK_INITED;
return wfStats.cbs;
}
/* ----------------------------------------------------------------------- */
int gpioWaveGetHighCbs(void)
{
DBG(DBG_USER, "");
CHECK_INITED;
return wfStats.highCbs;
}
/* ----------------------------------------------------------------------- */
int gpioWaveGetMaxCbs(void)
{
DBG(DBG_USER, "");
CHECK_INITED;
return wfStats.maxCbs;
}
/* ----------------------------------------------------------------------- */
int gpioWaveClear(void)
{
DBG(DBG_USER, "");
CHECK_INITED;
wfc[0] = 0;
wfc[1] = 0;
wfc[2] = 0;
wfcur = 0;
wfStats.micros = 0;
wfStats.pulses = 0;
wfStats.cbs = 0;
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioWaveAddGeneric(unsigned numPulses, gpioPulse_t * pulses)
{
DBG(DBG_USER, "numPulses=%u pulses=%08X", numPulses, (uint32_t)pulses);
CHECK_INITED;
if (numPulses > PI_WAVE_MAX_PULSES)
SOFT_ERROR(PI_TOO_MANY_PULSES, "bad number of pulses (%d)", numPulses);
return waveMerge(numPulses, pulses);
}
/* ----------------------------------------------------------------------- */
int gpioWaveAddSerial(unsigned gpio,
unsigned baud,
unsigned numChar,
char * str)
{
int i, b, p, lev, c, v;
unsigned bitDelay[10];
DBG(DBG_USER, "gpio=%d baud=%d numChar=%d str*=%08X",
gpio, baud, numChar, (uint32_t)str);
CHECK_INITED;
if (gpio > PI_MAX_USER_GPIO)
SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
if ((baud < PI_WAVE_MIN_BAUD) || (baud > PI_WAVE_MAX_BAUD))
SOFT_ERROR(PI_BAD_WAVE_BAUD,
"gpio %d, bad baud rate (%d)", gpio, baud);
if (numChar > PI_WAVE_MAX_CHARS)
SOFT_ERROR(PI_TOO_MANY_CHARS, "too many chars (%d)", numChar);
if (!numChar) return 0;
waveBitDelay(baud, bitDelay);
p = 0;
wf[2][p].gpioOn = (1<<gpio);
wf[2][p].gpioOff = 0;
wf[2][p].usDelay = bitDelay[0];
for (i=0; i<numChar; i++)
{
p++;
/* start bit */
wf[2][p].gpioOn = 0;
wf[2][p].gpioOff = (1<<gpio);
wf[2][p].usDelay = bitDelay[0];
lev = 0;
c = str[i];
for (b=0; b<8; b++)
{
if (c & (1<<b)) v=1; else v=0;
if (v == lev) wf[2][p].usDelay += bitDelay[b+1];
else
{
p++;
lev = v;
if (lev)
{
wf[2][p].gpioOn = (1<<gpio);
wf[2][p].gpioOff = 0;
}
else
{
wf[2][p].gpioOn = 0;
wf[2][p].gpioOff = (1<<gpio);
}
wf[2][p].usDelay = bitDelay[b+1];
}
}
/* stop bit */
if (lev) wf[2][p].usDelay += bitDelay[9];
else
{
p++;
wf[2][p].gpioOn = (1<<gpio);
wf[2][p].gpioOff = 0;
wf[2][p].usDelay = bitDelay[9];
}
}
p++;
wf[2][p].gpioOn = (1<<gpio);
wf[2][p].gpioOff = 0;
wf[2][p].usDelay = bitDelay[0];
return waveMerge(p, wf[2]);
}
/*-------------------------------------------------------------------------*/
int gpioWaveSerialReadStart(unsigned gpio,
unsigned baud,
gpioRx_t *rxp)
{
int bitTime, timeoutMs;
DBG(DBG_USER, "gpio=%d baud=%d rxp*=%08X", gpio, baud, (uint32_t)rxp);
CHECK_INITED;
if (gpio > PI_MAX_USER_GPIO)
SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
if ((baud < PI_WAVE_MIN_BAUD) || (baud > PI_WAVE_MAX_BAUD))
SOFT_ERROR(PI_BAD_WAVE_BAUD,
"gpio %d, bad baud rate (%d)", gpio, baud);
if (rxp == NULL)
SOFT_ERROR(PI_BAD_SERIAL_STRUC, "Null structure pointer");
if (rxp->buf == NULL)
SOFT_ERROR(PI_BAD_SERIAL_BUF, "Null buffer pointer");
bitTime = MILLION / baud;
timeoutMs = ((12 * bitTime)+1000)/1000;
wfRx[gpio].mode = PI_WFRX_SERIAL;
wfRx[gpio].baud = baud;
wfRx[gpio].rxp = rxp;
wfRx[gpio].baud = baud;
wfRx[gpio].fullBit = bitTime;
wfRx[gpio].halfBit = bitTime/2;
wfRx[gpio].rxp->readPos = 0;
wfRx[gpio].rxp->writePos = 0;
wfRx[gpio].bit = -1;
gpioSetWatchdog(gpio, timeoutMs); /* get a nudge if no change */
gpioSetAlertFunc(gpio, waveRxBit);
return 0;
}
/*-------------------------------------------------------------------------*/
int gpioWaveSerialReadStop(unsigned gpio)
{
DBG(DBG_USER, "gpio=%d", gpio);
CHECK_INITED;
if (gpio > PI_MAX_USER_GPIO)
SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
switch(wfRx[gpio].mode)
{
case PI_WFRX_NONE:
SOFT_ERROR(PI_NOT_SERIAL_GPIO, "no serial read on gpio (%d)", gpio);
break;
case PI_WFRX_SERIAL:
gpioSetWatchdog(gpio, 0); /* switch off timeouts */
gpioSetAlertFunc(gpio, NULL); /* cancel alert */
wfRx[gpio].mode = PI_WFRX_NONE;
break;
}
return 0;
}
/*-------------------------------------------------------------------------*/
int gpioWaveTxBusy(void)
{
DBG(DBG_USER, "");
CHECK_INITED;
if (dmaOut[DMA_CONBLK_AD])
return 1;
else
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioWaveTxStart(unsigned mode)
{
static int secondaryClockInited = 0;
int cb, i;
DBG(DBG_USER, "mode=%d", mode);
CHECK_INITED;
if (mode > PI_WAVE_MODE_REPEAT)
SOFT_ERROR(PI_BAD_WAVE_MODE, "bad wave mode (%d)", mode);
if (wfc[wfcur] == 0) return 0;
if (!secondaryClockInited)
{
initClock(0); /* initialise secondary clock */
secondaryClockInited = 1;
}
cb = wave2Cbs(mode);
if (gpioCfg.dbgLevel >= DBG_SLOW_TICK)
{
fprintf(stderr, "*** OUTPUT DMA CONTROL BLOCKS ***\n");
for (i=0; i<cb; i++) waveCbOPrint(i);
}
initDMAgo((uint32_t *)dmaOut, (uint32_t)dmaOPhys[0]);
return cb;
}
/* ----------------------------------------------------------------------- */
int gpioWaveTxStop(void)
{
DBG(DBG_USER, "");
CHECK_INITED;
dmaOut[DMA_CS] = DMA_CHANNEL_RESET;
dmaOut[DMA_CONBLK_AD] = 0;
return 0;
}
/* ----------------------------------------------------------------------- */
static int intGpioSetAlertFunc(
unsigned gpio,
void * f,
int user,
void * userdata)
{
DBG(DBG_INTERNAL, "gpio=%d function=%08X, user=%d, userdata=%08X",
gpio, (uint32_t)f, user, (uint32_t)userdata);
gpioAlert[gpio].ex = user;
gpioAlert[gpio].userdata = userdata;
gpioAlert[gpio].func = f;
if (f)
{
alertBits |= BIT;
}
else
{
alertBits &= ~BIT;
}
monitorBits = alertBits | notifyBits | gpioGetSamples.bits;
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioSetAlertFunc(unsigned gpio, gpioAlertFunc_t f)
{
DBG(DBG_USER, "gpio=%d function=%08X", gpio, (uint32_t)f);
CHECK_INITED;
if (gpio > PI_MAX_USER_GPIO)
SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
intGpioSetAlertFunc(gpio, f, 0, NULL);
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioSetAlertFuncEx(unsigned gpio, gpioAlertFuncEx_t f, void * userdata)
{
DBG(DBG_USER, "gpio=%d function=%08X userdata=%08X",
gpio, (uint32_t)f, (uint32_t)userdata);
CHECK_INITED;
if (gpio > PI_MAX_USER_GPIO)
SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
intGpioSetAlertFunc(gpio, f, 1, userdata);
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioNotifyOpen(void)
{
int i, slot, fd;
char name[32];
DBG(DBG_USER, "");
CHECK_INITED;
slot = -1;
for (i=0; i<PI_NOTIFY_SLOTS; i++)
{
if (gpioNotify[i].state == PI_NOTIFY_CLOSED)
{
slot = i;
break;
}
}
if (slot < 0)
SOFT_ERROR(PI_NO_HANDLE, "no handle");
sprintf(name, "/dev/pigpio%d", slot);
myCreatePipe(name, 0664);
fd = open(name, O_RDWR|O_NONBLOCK);
if (fd < 0)
SOFT_ERROR(PI_BAD_PATHNAME, "open %s failed (%m)", name);
gpioNotify[slot].state = PI_NOTIFY_OPENED;
gpioNotify[slot].seqno = 0;
gpioNotify[slot].bits = 0;
gpioNotify[slot].fd = fd;
gpioNotify[slot].pipe = 1;
return slot;
}
/* ----------------------------------------------------------------------- */
static int gpioNotifyOpenInBand(int fd)
{
int i, slot;
DBG(DBG_USER, "");
CHECK_INITED;
slot = -1;
for (i=0; i<PI_NOTIFY_SLOTS; i++)
{
if (gpioNotify[i].state == PI_NOTIFY_CLOSED)
{
slot = i;
break;
}
}
if (slot < 0) SOFT_ERROR(PI_NO_HANDLE, "no handle");
gpioNotify[slot].state = PI_NOTIFY_OPENED;
gpioNotify[slot].seqno = 0;
gpioNotify[slot].bits = 0;
gpioNotify[slot].fd = fd;
gpioNotify[slot].pipe = 0;
return slot;
}
/* ----------------------------------------------------------------------- */
static void intNotifyBits(void)
{
int i;
uint32_t bits;
bits = 0;
for (i=0; i<PI_NOTIFY_SLOTS; i++)
{
if (gpioNotify[i].state == PI_NOTIFY_RUNNING)
{
bits |= gpioNotify[i].bits;
}
}
notifyBits = bits;
monitorBits = alertBits | notifyBits | gpioGetSamples.bits;
}
/* ----------------------------------------------------------------------- */
int gpioNotifyBegin(unsigned handle, uint32_t bits)
{
DBG(DBG_USER, "handle=%d bits=%08X", handle, bits);
CHECK_INITED;
if (handle > PI_NOTIFY_SLOTS)
SOFT_ERROR(PI_BAD_HANDLE, "bad handle (%d)", handle);
if (gpioNotify[handle].state <= PI_NOTIFY_CLOSING)
SOFT_ERROR(PI_BAD_HANDLE, "bad handle (%d)", handle);
gpioNotify[handle].bits = bits;
gpioNotify[handle].state = PI_NOTIFY_RUNNING;
intNotifyBits();
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioNotifyPause (unsigned handle)
{
DBG(DBG_USER, "handle=%d", handle);
CHECK_INITED;
if (handle > PI_NOTIFY_SLOTS)
SOFT_ERROR(PI_BAD_HANDLE, "bad handle (%d)", handle);
if (gpioNotify[handle].state <= PI_NOTIFY_CLOSING)
SOFT_ERROR(PI_BAD_HANDLE, "bad handle (%d)", handle);
gpioNotify[handle].bits = 0;
gpioNotify[handle].state = PI_NOTIFY_PAUSED;
intNotifyBits();
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioNotifyClose(unsigned handle)
{
DBG(DBG_USER, "handle=%d", handle);
CHECK_INITED;
if (handle > PI_NOTIFY_SLOTS)
SOFT_ERROR(PI_BAD_HANDLE, "bad handle (%d)", handle);
if (gpioNotify[handle].state <= PI_NOTIFY_CLOSING)
SOFT_ERROR(PI_BAD_HANDLE, "bad handle (%d)", handle);
gpioNotify[handle].bits = 0;
gpioNotify[handle].state = PI_NOTIFY_CLOSING;
intNotifyBits();
/* actual close done in alert thread */
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioSetWatchdog(unsigned gpio, unsigned timeout)
{
DBG(DBG_USER, "gpio=%d timeout=%d", gpio, timeout);
CHECK_INITED;
if (gpio > PI_MAX_USER_GPIO)
SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
if (timeout > PI_MAX_WDOG_TIMEOUT)
SOFT_ERROR(PI_BAD_WDOG_TIMEOUT,
"gpio %d, bad timeout (%d)", gpio, timeout);
gpioAlert[gpio].timeout = timeout;
gpioAlert[gpio].tick = systReg[SYST_CLO];
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioSetGetSamplesFunc(gpioGetSamplesFunc_t f, uint32_t bits)
{
DBG(DBG_USER, "function=%08X bits=%08X", (uint32_t)f, bits);
CHECK_INITED;
gpioGetSamples.ex = 0;
gpioGetSamples.userdata = NULL;
gpioGetSamples.func = f;
if (f) gpioGetSamples.bits = bits;
else gpioGetSamples.bits = 0;
monitorBits = alertBits | notifyBits | gpioGetSamples.bits;
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioSetGetSamplesFuncEx(gpioGetSamplesFuncEx_t f,
uint32_t bits,
void * userdata)
{
DBG(DBG_USER, "function=%08X bits=%08X", (uint32_t)f, bits);
CHECK_INITED;
gpioGetSamples.ex = 1;
gpioGetSamples.userdata = userdata;
gpioGetSamples.func = f;
if (f) gpioGetSamples.bits = bits;
else gpioGetSamples.bits = 0;
monitorBits = alertBits | notifyBits | gpioGetSamples.bits;
return 0;
}
/* ----------------------------------------------------------------------- */
static int intGpioSetTimerFunc(unsigned id,
unsigned ms,
void * f,
int user,
void * userdata)
{
DBG(DBG_INTERNAL, "id=%d ms=%d function=%08X user=%d userdata=%08X",
id, ms, (uint32_t)f, user, (uint32_t)userdata);
gpioTimer[id].id = id;
if (f)
{
gpioTimer[id].func = f;
gpioTimer[id].ex = user;
gpioTimer[id].userdata = userdata;
gpioTimer[id].millis = ms;
if (!gpioTimer[id].running)
{
if (pthread_create(
&gpioTimer[id].pthId, &pthAttr, pthTimerTick, &gpioTimer[id]))
SOFT_ERROR(PI_TIMER_FAILED,
"timer %d, create failed (%m)", id);
gpioTimer[id].running = 1;
}
}
else
{
if (gpioTimer[id].running)
{
/* destroy thread */
if (pthread_cancel(gpioTimer[id].pthId))
SOFT_ERROR(PI_TIMER_FAILED, "timer %d, cancel failed (%m)", id);
if (pthread_join(gpioTimer[id].pthId, NULL))
SOFT_ERROR(PI_TIMER_FAILED, "timer %d, join failed (%m)", id);
gpioTimer[id].running = 0;
gpioTimer[id].func = f;
}
}
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioSetTimerFunc(unsigned id, unsigned ms, gpioTimerFunc_t f)
{
DBG(DBG_USER, "id=%d ms=%d function=%08X", id, ms, (uint32_t)f);
CHECK_INITED;
if (id > PI_MAX_TIMER)
SOFT_ERROR(PI_BAD_TIMER, "bad timer id (%d)", id);
if ((ms < PI_MIN_MS) || (ms > PI_MAX_MS))
SOFT_ERROR(PI_BAD_MS, "timer %d, bad ms (%d)", id, ms);
intGpioSetTimerFunc(id, ms, f, 0, NULL);
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioSetTimerFuncEx(unsigned id, unsigned ms, gpioTimerFuncEx_t f,
void * userdata)
{
DBG(DBG_USER, "id=%d ms=%d function=%08X, userdata=%08X",
id, ms, (uint32_t)f, (uint32_t)userdata);
CHECK_INITED;
if (id > PI_MAX_TIMER)
SOFT_ERROR(PI_BAD_TIMER, "bad timer id (%d)", id);
if ((ms < PI_MIN_MS) || (ms > PI_MAX_MS))
SOFT_ERROR(PI_BAD_MS, "timer %d, bad ms (%d)", id, ms);
intGpioSetTimerFunc(id, ms, f, 1, userdata);
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioSetSignalFunc(unsigned signum, gpioSignalFunc_t f)
{
DBG(DBG_USER, "signum=%d function=%08X", signum, (uint32_t)f);
CHECK_INITED;
if (signum > PI_MAX_SIGNUM)
SOFT_ERROR(PI_BAD_SIGNUM, "bad signum (%d)", signum);
gpioSignal[signum].ex = 0;
gpioSignal[signum].userdata = NULL;
gpioSignal[signum].func = f;
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioSetSignalFuncEx(unsigned signum, gpioSignalFuncEx_t f,
void * userdata)
{
DBG(DBG_USER, "signum=%d function=%08X userdata=%08X",
signum, (uint32_t)f, (uint32_t)userdata);
CHECK_INITED;
if (signum > PI_MAX_SIGNUM)
SOFT_ERROR(PI_BAD_SIGNUM, "bad signum (%d)", signum);
gpioSignal[signum].ex = 1;
gpioSignal[signum].userdata = userdata;
gpioSignal[signum].func = f;
return 0;
}
/* ----------------------------------------------------------------------- */
uint32_t gpioRead_Bits_0_31(void)
{
DBG(DBG_USER, "");
CHECK_INITED;
return (*(gpioReg + GPLEV0));
}
/* ----------------------------------------------------------------------- */
uint32_t gpioRead_Bits_32_53(void)
{
DBG(DBG_USER, "");
CHECK_INITED;
return (*(gpioReg + GPLEV1));
}
/* ----------------------------------------------------------------------- */
int gpioWrite_Bits_0_31_Clear(uint32_t levels)
{
DBG(DBG_USER, "levels=%08X", levels);
CHECK_INITED;
*(gpioReg + GPCLR0) = levels;
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioWrite_Bits_32_53_Clear(uint32_t levels)
{
DBG(DBG_USER, "levels=%08X", levels);
CHECK_INITED;
*(gpioReg + GPCLR1) = levels;
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioWrite_Bits_0_31_Set(uint32_t levels)
{
DBG(DBG_USER, "levels=%08X", levels);
CHECK_INITED;
*(gpioReg + GPSET0) = levels;
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioWrite_Bits_32_53_Set(uint32_t levels)
{
DBG(DBG_USER, "levels=%08X", levels);
CHECK_INITED;
*(gpioReg + GPSET1) = levels;
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioTime(unsigned timetype, int * seconds, int * micros)
{
struct timespec ts;
DBG(DBG_USER, "timetype=%d &seconds=%08X &micros=%08X",
timetype, (uint32_t)seconds, (uint32_t)micros);
CHECK_INITED;
if (timetype > PI_TIME_ABSOLUTE)
SOFT_ERROR(PI_BAD_TIMETYPE, "bad timetype (%d)", timetype);
if (timetype == PI_TIME_ABSOLUTE)
{
clock_gettime(CLOCK_REALTIME, &ts);
*seconds = ts.tv_sec;
*micros = ts.tv_nsec/1000;
}
else
{
clock_gettime(CLOCK_REALTIME, &ts);
TIMER_SUB(&ts, &libStarted, &ts);
*seconds = ts.tv_sec;
*micros = ts.tv_nsec/1000;
}
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioSleep(unsigned timetype, int seconds, int micros)
{
struct timespec ts, rem;
DBG(DBG_USER, "timetype=%d seconds=%d micros=%d",
timetype, seconds, micros);
CHECK_INITED;
if (timetype > PI_TIME_ABSOLUTE)
SOFT_ERROR(PI_BAD_TIMETYPE, "bad timetype (%d)", timetype);
if (seconds < 0)
SOFT_ERROR(PI_BAD_SECONDS, "bad seconds (%d)", seconds);
if ((micros < 0) || (micros > 999999))
SOFT_ERROR(PI_BAD_MICROS, "bad micros (%d)", micros);
ts.tv_sec = seconds;
ts.tv_nsec = micros * 1000;
if (timetype == PI_TIME_ABSOLUTE)
{
while (clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, &rem));
}
else
{
while (clock_nanosleep(CLOCK_REALTIME, 0, &ts, &rem))
{
/* copy remaining time to ts */
ts.tv_sec = rem.tv_sec;
ts.tv_nsec = rem.tv_nsec;
}
}
return 0;
}
/* ----------------------------------------------------------------------- */
uint32_t gpioDelay(uint32_t micros)
{
uint32_t start;
DBG(DBG_USER, "microseconds=%u", micros);
CHECK_INITED;
start = systReg[SYST_CLO];
if (micros < 100) while ((systReg[SYST_CLO] - start) <= micros) ;
else gpioSleep(PI_TIME_RELATIVE, (micros/MILLION), (micros%MILLION));
return (systReg[SYST_CLO] - start);
}
/* ----------------------------------------------------------------------- */
uint32_t gpioTick(void)
{
CHECK_INITED;
return systReg[SYST_CLO];
}
/* ----------------------------------------------------------------------- */
unsigned gpioHardwareRevision(void)
{
static unsigned rev = 0;
FILE * filp;
char buf[512];
char term;
DBG(DBG_USER, "");
if (rev) return rev;
filp = fopen ("/proc/cpuinfo", "r");
if (filp != NULL)
{
while (fgets(buf, sizeof(buf), filp) != NULL)
{
if (!strncasecmp("revision\t", buf, 9))
{
if (sscanf(buf+strlen(buf)-5, "%x%c", &rev, &term) == 2)
{
if (term == '\n') break;
rev = 0;
}
}
}
fclose(filp);
}
return rev;
}
/* ----------------------------------------------------------------------- */
int gpioCfgBufferSize(unsigned millis)
{
DBG(DBG_USER, "millis=%d", millis);
CHECK_NOT_INITED;
if ((millis < PI_BUF_MILLIS_MIN) || (millis > PI_BUF_MILLIS_MAX))
SOFT_ERROR(PI_BAD_BUF_MILLIS, "bad millis (%d)", millis);
gpioCfg.bufferMilliseconds = millis;
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioCfgClock(unsigned micros, unsigned peripheral, unsigned source)
{
DBG(DBG_USER, "micros=%d peripheral=%d source=%d",
micros, peripheral, source);
CHECK_NOT_INITED;
if ((micros < 1) || (micros > 10))
SOFT_ERROR(PI_BAD_CLK_MICROS, "bad micros (%d)", micros);
if (!clkCfg[micros].valid)
SOFT_ERROR(PI_BAD_CLK_MICROS, "bad micros (%d)", micros);
if (peripheral > PI_CLOCK_PCM)
SOFT_ERROR(PI_BAD_CLK_PERIPH, "bad peripheral (%d)", peripheral);
if (source > PI_CLOCK_PLLD)
SOFT_ERROR(PI_BAD_CLK_SOURCE, "bad clock (%d)", source);
gpioCfg.clockMicros = micros;
gpioCfg.clockPeriph = peripheral;
gpioCfg.clockSource = source;
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioCfgDMAchannel(unsigned channel)
{
DBG(DBG_USER, "channel=%d", channel);
CHECK_NOT_INITED;
if ((channel < PI_MIN_DMA_CHANNEL) || (channel > PI_MAX_DMA_CHANNEL))
SOFT_ERROR(PI_BAD_CHANNEL, "bad channel (%d)", channel);
gpioCfg.DMAprimaryChannel = channel;
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioCfgDMAchannels(unsigned primaryChannel, unsigned secondaryChannel)
{
DBG(DBG_USER, "primary channel=%d, secondary channel=%d",
primaryChannel, secondaryChannel);
CHECK_NOT_INITED;
if (primaryChannel > PI_MAX_PRIMARY_CHANNEL)
SOFT_ERROR(PI_BAD_PRIM_CHANNEL, "bad primary channel (%d)",
primaryChannel);
if (secondaryChannel > PI_MAX_SECONDARY_CHANNEL)
SOFT_ERROR(PI_BAD_SECO_CHANNEL, "bad secondary channel (%d)",
secondaryChannel);
gpioCfg.DMAprimaryChannel = primaryChannel;
gpioCfg.DMAsecondaryChannel = secondaryChannel;
return 0;
}
/*-------------------------------------------------------------------------*/
int gpioCfgPermissions(uint64_t updateMask)
{
DBG(DBG_USER, "gpio update mask=%llX", updateMask);
CHECK_NOT_INITED;
gpioMask = updateMask;
gpioMaskSet = 1;
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioCfgInterfaces(unsigned ifFlags)
{
DBG(DBG_USER, "ifFlags=%X", ifFlags);
CHECK_NOT_INITED;
if (ifFlags > 3)
SOFT_ERROR(PI_BAD_IF_FLAGS, "bad ifFlags (%X)", ifFlags);
gpioCfg.ifFlags = ifFlags;
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioCfgSocketPort(unsigned port)
{
DBG(DBG_USER, "port=%d", port);
CHECK_NOT_INITED;
if ((port < PI_MIN_SOCKET_PORT) || (port > PI_MAX_SOCKET_PORT))
SOFT_ERROR(PI_BAD_SOCKET_PORT, "bad port (%d)", port);
gpioCfg.socketPort = port;
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioCfgInternals(unsigned what, int value)
{
int retVal = PI_BAD_CFG_INTERNAL;
DBG(DBG_USER, "what=%u, value=%d", what, value);
CHECK_NOT_INITED;
/*
133084774
207081315
293640712
394342930
472769257
430873902
635370313
684442696
786301093
816051706
858202631
997413601
*/
switch(what)
{
case 562484977:
gpioCfg.showStats = value;
DBG(DBG_MIN_LEVEL, "showStats is %u", value);
retVal = 0;
break;
case 984762879:
if (value < DBG_MIN_LEVEL) value = DBG_MIN_LEVEL;
if (value > DBG_MAX_LEVEL) value = DBG_MAX_LEVEL;
gpioCfg.dbgLevel = value;
DBG(DBG_MIN_LEVEL, "Debug level is %u", value);
retVal = 0;
break;
}
return retVal;
}