//------------------------------------------------------------------------------------------------------------
//
// ODROID-C2 GPIO Status Check Application.
//
// Compile : gcc -o <create excute file name> <source file name>
// Run : sudo ./<created excute file name>
//
//------------------------------------------------------------------------------------------------------------
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <ctype.h>
#include <poll.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
//------------------------------------------------------------------------------
//
// Global handle Define
//
//------------------------------------------------------------------------------
// MMAP Address for GPIO
// GPIO Banks Control Register Address 0xC8834400
#define GPIO_REG_MAP 0xC8834000
// GPIO AO Control Register Address 0xC8100000
#define GPIO_AO_REG_MAP 0xC8100000
#define BLOCK_SIZE (4*1024)
static volatile unsigned int *gpio;
static volatile unsigned int *gpio_ao;
static volatile unsigned int *ao_in;
//------------------------------------------------------------------------------
// refer kernel/include/dt-bindings/gpio/gxbb.h
#define GPIOAO_OFFSET 122
#define GPIOAO_PIN_START (GPIOAO_OFFSET + 0)
#define GPIOAO_PIN_END (GPIOAO_OFFSET + 13)
#define GPIOAO_FSEL_REG_OFFSET 0x09
#define GPIOAO_OUTP_REG_OFFSET 0x09
#define GPIOAO_INP_REG_OFFSET 0x0A
#define GPIOAO_PUPD_REG_OFFSET 0x0B
#define GPIOAO_PUEN_REG_OFFSET 0x0B
//------------------------------------------------------------------------------
#define GPIO_BANKS_OFFSET 136
#define GPIODV_PIN_START (GPIO_BANKS_OFFSET + 45)
#define GPIODV_PIN_END (GPIO_BANKS_OFFSET + 74)
#define GPIODV_FSEL_REG_OFFSET 0x10C
#define GPIODV_OUTP_REG_OFFSET 0x10D
#define GPIODV_INP_REG_OFFSET 0x10E
#define GPIODV_PUPD_REG_OFFSET 0x13A
#define GPIODV_PUEN_REG_OFFSET 0x148
//------------------------------------------------------------------------------
#define GPIOY_PIN_START (GPIO_BANKS_OFFSET + 75)
#define GPIOY_PIN_END (GPIO_BANKS_OFFSET + 91)
#define GPIOY_FSEL_REG_OFFSET 0x10F
#define GPIOY_OUTP_REG_OFFSET 0x110
#define GPIOY_INP_REG_OFFSET 0x111
#define GPIOY_PUPD_REG_OFFSET 0x13D
#define GPIOY_PUEN_REG_OFFSET 0x149
//------------------------------------------------------------------------------
#define GPIOX_PIN_START (GPIO_BANKS_OFFSET + 92)
#define GPIOX_PIN_END (GPIO_BANKS_OFFSET + 114)
#define GPIOX_FSEL_REG_OFFSET 0x118
#define GPIOX_OUTP_REG_OFFSET 0x119
#define GPIOX_INP_REG_OFFSET 0x11A
#define GPIOX_PUPD_REG_OFFSET 0x13E
#define GPIOX_PUEN_REG_OFFSET 0x14C
//------------------------------------------------------------------------------
// GPIO Banks Pin Mux Control Register Address 0xC88344B0 ~ 0xC88344D4 (REG0 ~ REG9)
#define MUX_REG0 0x12C
#define MUX_REG1 0x12D
#define MUX_REG2 0x12E
#define MUX_REG3 0x12F
#define MUX_REG4 0x130
#define MUX_REG5 0x131
#define MUX_REG6 0x132
#define MUX_REG7 0x133
#define MUX_REG8 0x134
#define MUX_REG9 0x135
// GPIO AO Pin Mux Control Register Address 0xC8100014/0xC8100018 (AO_REG, A0_REG2)
#define AO_MUX_REG1 0x005
#define AO_MUX_REG2 0x006
//------------------------------------------------------------------------------
#define BIT(x) (1 << x)
//------------------------------------------------------------------------------
const char MODE_STR[9][5] = {
" - ",
"ALT1",
"ALT2",
"ALT3",
"ALT4",
"ALT5",
"ALT6",
"IN",
"OUT",
};
//------------------------------------------------------------------------------
const char PUPD_STR[3][5] = {
" - ",
"P/U",
"P/D",
};
//------------------------------------------------------------------------------
struct header_info {
int gpio;
char name[10];
};
//------------------------------------------------------------------------------
const struct header_info header_J2[40] = {
{ -1, "3.3V" }, { -1, "5.0V" },
{ 205, "I2CA_SDA" }, { -1, "5.0V" },
{ 206, "I2CA_SCL" }, { -1, "GND" },
{ 249, "GPIOX.21" }, { 240, "GPIOX.12" },
{ -1, "GND" }, { 241, "GPIOX.13" },
{ 247, "GPIOX.19" }, { 238, "GPIOX.10" },
{ 239, "GPIOX.11" }, { -1, "GND" },
{ 237, "GPIOX.9" }, { 236, "GPIOX.8" },
{ -1, "3.3V" }, { 233, "GPIOX.5" },
{ 235, "GPIOX.7" }, { -1, "GND" },
{ 232, "GPIOX.4" }, { 231, "GPIOX.3" },
{ 230, "GPIOX.2" }, { 229, "GPIOX.1" },
{ -1, "GND" }, { 225, "GPIOY.14" },
{ 207, "I2CB_SDA" }, { 208, "I2CB_SCL" },
{ 228, "GPIOX.0" }, { -1, "GND" },
{ 219, "GPIOY.8" }, { 224, "GPIOY.13" },
{ 234, "GPIOX.6" }, { -1, "GND" },
{ 214, "GPIOY.3" }, { 218, "GPIOY.7" },
{ -1, "ADC.AIN0" }, { -1, "1.8V REF" },
{ -1, "GND" }, { -1, "ADC.AIN1" },
};
//------------------------------------------------------------------------------
const struct header_info header_J7[7] = {
{ -1, "GND" },
{ 128, "GPIOAO.6" },
{ -1, "5.0V" },
{ 130, "GPIOAO.8" },
{ 132, "GPIOAO.10" },
{ 131, "GPIOAO.9" },
{ 133, "GPIOAO.11" },
};
//------------------------------------------------------------------------------------------------------------
int get_mode_gpiox (int pin)
{
switch(pin) {
case 0:
if (*(gpio + MUX_REG8) & BIT(5)) return 1;
break;
case 1:
if (*(gpio + MUX_REG8) & BIT(4)) return 1;
break;
case 2:
if (*(gpio + MUX_REG8) & BIT(3)) return 1;
break;
case 3:
if (*(gpio + MUX_REG8) & BIT(2)) return 1;
break;
case 4:
if (*(gpio + MUX_REG8) & BIT(1)) return 1;
break;
case 5:
if (*(gpio + MUX_REG8) & BIT(0)) return 1;
break;
case 6:
if (*(gpio + MUX_REG3) & BIT(9)) return 4;
if (*(gpio + MUX_REG3) & BIT(17)) return 5;
break;
case 7:
if (*(gpio + MUX_REG8) & BIT(11)) return 1;
if (*(gpio + MUX_REG3) & BIT(8)) return 4;
if (*(gpio + MUX_REG3) & BIT(18)) return 5;
break;
case 8:
if (*(gpio + MUX_REG4) & BIT(7)) return 1;
if (*(gpio + MUX_REG3) & BIT(30)) return 3;
if (*(gpio + MUX_REG3) & BIT(10)) return 4;
break;
case 9:
if (*(gpio + MUX_REG4) & BIT(6)) return 1;
if (*(gpio + MUX_REG3) & BIT(29)) return 3;
if (*(gpio + MUX_REG3) & BIT(7)) return 4;
break;
case 10:
if (*(gpio + MUX_REG3) & BIT(28)) return 3;
break;
case 11:
if (*(gpio + MUX_REG3) & BIT(27)) return 3;
break;
case 12:
if (*(gpio + MUX_REG4) & BIT(13)) return 2;
if (*(gpio + MUX_REG4) & BIT(17)) return 3;
break;
case 13:
if (*(gpio + MUX_REG4) & BIT(12)) return 2;
if (*(gpio + MUX_REG4) & BIT(16)) return 3;
break;
case 14:
if (*(gpio + MUX_REG4) & BIT(11)) return 2;
if (*(gpio + MUX_REG4) & BIT(15)) return 3;
break;
case 15:
if (*(gpio + MUX_REG4) & BIT(10)) return 2;
if (*(gpio + MUX_REG4) & BIT(14)) return 3;
break;
case 19:
if (*(gpio + MUX_REG2) & BIT(22)) return 2;
if (*(gpio + MUX_REG2) & BIT(30)) return 5;
break;
case 20: case 21:
break;
default :
return 0;
}
return *(gpio + GPIOX_FSEL_REG_OFFSET) & BIT(pin) ? 7 : 8;
}
//------------------------------------------------------------------------------------------------------------
int get_mode_gpioy (int pin)
{
switch(pin) {
case 0:
if (*(gpio + MUX_REG2) & BIT(19)) return 1;
if (*(gpio + MUX_REG3) & BIT(2)) return 2;
if (*(gpio + MUX_REG1) & BIT(0)) return 5;
break;
case 1:
if (*(gpio + MUX_REG2) & BIT(18)) return 1;
if (*(gpio + MUX_REG3) & BIT(1)) return 2;
if (*(gpio + MUX_REG1) & BIT(1)) return 5;
break;
case 2:
if (*(gpio + MUX_REG2) & BIT(17)) return 1;
if (*(gpio + MUX_REG3) & BIT(0)) return 2;
break;
case 3:
if (*(gpio + MUX_REG2) & BIT(16)) return 1;
if (*(gpio + MUX_REG3) & BIT(4)) return 2;
if (*(gpio + MUX_REG1) & BIT(2)) return 5;
break;
case 4:
if (*(gpio + MUX_REG2) & BIT(16)) return 1;
if (*(gpio + MUX_REG3) & BIT(5)) return 2;
if (*(gpio + MUX_REG1) & BIT(12)) return 4;
break;
case 5:
if (*(gpio + MUX_REG2) & BIT(16)) return 1;
if (*(gpio + MUX_REG3) & BIT(5)) return 2;
if (*(gpio + MUX_REG1) & BIT(13)) return 4;
break;
case 6:
if (*(gpio + MUX_REG2) & BIT(16)) return 1;
if (*(gpio + MUX_REG3) & BIT(5)) return 2;
if (*(gpio + MUX_REG1) & BIT(3)) return 5;
break;
case 7:
if (*(gpio + MUX_REG2) & BIT(16)) return 1;
if (*(gpio + MUX_REG3) & BIT(5)) return 2;
if (*(gpio + MUX_REG1) & BIT(4)) return 5;
break;
case 8:
if (*(gpio + MUX_REG2) & BIT(16)) return 1;
if (*(gpio + MUX_REG3) & BIT(5)) return 2;
if (*(gpio + MUX_REG1) & BIT(5)) return 5;
break;
case 9:
if (*(gpio + MUX_REG2) & BIT(16)) return 1;
if (*(gpio + MUX_REG3) & BIT(5)) return 2;
if (*(gpio + MUX_REG1) & BIT(6)) return 5;
break;
case 10:
if (*(gpio + MUX_REG2) & BIT(16)) return 1;
if (*(gpio + MUX_REG3) & BIT(5)) return 2;
if (*(gpio + MUX_REG1) & BIT(7)) return 5;
break;
case 11:
if (*(gpio + MUX_REG3) & BIT(3)) return 2;
if (*(gpio + MUX_REG1) & BIT(19)) return 3;
if (*(gpio + MUX_REG1) & BIT(8)) return 5;
break;
case 12:
if (*(gpio + MUX_REG1) & BIT(18)) return 3;
if (*(gpio + MUX_REG1) & BIT(9)) return 5;
break;
case 13:
if (*(gpio + MUX_REG1) & BIT(17)) return 3;
if (*(gpio + MUX_REG1) & BIT(10)) return 5;
break;
case 14:
if (*(gpio + MUX_REG1) & BIT(16)) return 3;
if (*(gpio + MUX_REG1) & BIT(11)) return 5;
break;
case 15:
if (*(gpio + MUX_REG2) & BIT(20)) return 1;
if (*(gpio + MUX_REG1) & BIT(20)) return 4;
if (*(gpio + MUX_REG1) & BIT(22)) return 5;
break;
case 16:
if (*(gpio + MUX_REG2) & BIT(21)) return 1;
if (*(gpio + MUX_REG1) & BIT(21)) return 4;
break;
default :
return 0;
}
return *(gpio + GPIOY_FSEL_REG_OFFSET) & BIT(pin) ? 7 : 8;
}
//------------------------------------------------------------------------------------------------------------
int get_mode_gpiodv (int pin)
{
switch(pin) {
case 24:
if (*(gpio + MUX_REG0) & BIT(7)) return 1;
if (*(gpio + MUX_REG0) & BIT(12)) return 2;
if (*(gpio + MUX_REG5) & BIT(12)) return 3;
if (*(gpio + MUX_REG2) & BIT(29)) return 5;
if (*(gpio + MUX_REG7) & BIT(26)) return 6;
break;
case 25:
if (*(gpio + MUX_REG0) & BIT(6)) return 1;
if (*(gpio + MUX_REG0) & BIT(11)) return 2;
if (*(gpio + MUX_REG5) & BIT(11)) return 3;
if (*(gpio + MUX_REG2) & BIT(28)) return 5;
if (*(gpio + MUX_REG7) & BIT(27)) return 6;
break;
case 26:
if (*(gpio + MUX_REG0) & BIT(10)) return 2;
if (*(gpio + MUX_REG5) & BIT(10)) return 3;
if (*(gpio + MUX_REG2) & BIT(27)) return 5;
if (*(gpio + MUX_REG7) & BIT(24)) return 6;
break;
case 27:
if (*(gpio + MUX_REG0) & BIT(9)) return 2;
if (*(gpio + MUX_REG5) & BIT(9)) return 3;
if (*(gpio + MUX_REG5) & BIT(8)) return 4;
if (*(gpio + MUX_REG2) & BIT(26)) return 5;
if (*(gpio + MUX_REG7) & BIT(25)) return 6;
break;
case 28:
if (*(gpio + MUX_REG3) & BIT(20)) return 5;
if (*(gpio + MUX_REG7) & BIT(22)) return 6;
break;
case 29:
if (*(gpio + MUX_REG3) & BIT(22)) return 4;
if (*(gpio + MUX_REG3) & BIT(21)) return 5;
if (*(gpio + MUX_REG7) & BIT(23)) return 6;
break;
default :
return 0;
}
return *(gpio + GPIODV_FSEL_REG_OFFSET) & BIT(pin) ? 7 : 8;
}
//------------------------------------------------------------------------------------------------------------
int get_mode_gpioao (int pin)
{
switch(pin) {
case 0:
if (*(gpio_ao + AO_MUX_REG1) & BIT(12)) return 1;
if (*(gpio_ao + AO_MUX_REG1) & BIT(26)) return 2;
break;
case 1:
if (*(gpio_ao + AO_MUX_REG1) & BIT(11)) return 1;
if (*(gpio_ao + AO_MUX_REG1) & BIT(25)) return 2;
break;
case 2:
if (*(gpio_ao + AO_MUX_REG1) & BIT(10)) return 1;
if (*(gpio_ao + AO_MUX_REG1) & BIT(8)) return 2;
break;
case 3:
if (*(gpio_ao + AO_MUX_REG1) & BIT(9)) return 1;
if (*(gpio_ao + AO_MUX_REG1) & BIT(7)) return 2;
if (*(gpio_ao + AO_MUX_REG1) & BIT(22)) return 3;
break;
case 4:
if (*(gpio_ao + AO_MUX_REG1) & BIT(24)) return 2;
if (*(gpio_ao + AO_MUX_REG1) & BIT(6)) return 3;
if (*(gpio_ao + AO_MUX_REG1) & BIT(2)) return 4;
break;
case 5:
if (*(gpio_ao + AO_MUX_REG1) & BIT(25)) return 2;
if (*(gpio_ao + AO_MUX_REG1) & BIT(5)) return 3;
if (*(gpio_ao + AO_MUX_REG1) & BIT(1)) return 4;
break;
case 6:
if (*(gpio_ao + AO_MUX_REG1) & BIT(18)) return 3;
if (*(gpio_ao + AO_MUX_REG1) & BIT(16)) return 4;
return 2;
case 7:
if (*(gpio_ao + AO_MUX_REG1) & BIT(0)) return 1;
if (*(gpio_ao + AO_MUX_REG1) & BIT(21)) return 2;
break;
case 8:
if (*(gpio_ao + AO_MUX_REG1) & BIT(30)) return 4;
return 3;
case 9:
if (*(gpio_ao + AO_MUX_REG1) & BIT(29)) return 4;
return 2;
case 10:
if (*(gpio_ao + AO_MUX_REG1) & BIT(28)) return 4;
return 2;
case 11:
if (*(gpio_ao + AO_MUX_REG1) & BIT(27)) return 4;
return 3;
case 12:
if (*(gpio_ao + AO_MUX_REG1) & BIT(15)) return 1;
if (*(gpio_ao + AO_MUX_REG1) & BIT(14)) return 2;
if (*(gpio_ao + AO_MUX_REG1) & BIT(17)) return 3;
if (*(gpio_ao + AO_MUX_REG2) & BIT(0)) return 4;
break;
case 13:
if (*(gpio_ao + AO_MUX_REG1) & BIT(31)) return 1;
if (*(gpio_ao + AO_MUX_REG1) & BIT(4)) return 2;
if (*(gpio_ao + AO_MUX_REG1) & BIT(3)) return 3;
if (*(gpio_ao + AO_MUX_REG2) & BIT(1)) return 4;
break;
default :
return 0;
}
return *(gpio_ao + GPIOAO_FSEL_REG_OFFSET) & BIT(pin) ? 7 : 8;
}
//------------------------------------------------------------------------------------------------------------
int get_mode (int pin)
{
switch (pin) {
case GPIOX_PIN_START...GPIOX_PIN_END:
return get_mode_gpiox(pin - GPIOX_PIN_START);
case GPIOY_PIN_START...GPIOY_PIN_END:
return get_mode_gpioy(pin - GPIOY_PIN_START);
case GPIODV_PIN_START...GPIODV_PIN_END:
return get_mode_gpiodv(pin - GPIODV_PIN_START);
case GPIOAO_PIN_START...GPIOAO_PIN_END:
return get_mode_gpioao(pin - GPIOAO_PIN_START);
default :
break;
}
return 0;
}
//------------------------------------------------------------------------------------------------------------
int get_pupd (int pin)
{
switch (pin) {
case GPIOX_PIN_START...GPIOX_PIN_END:
if (*(gpio + GPIOX_PUEN_REG_OFFSET) & BIT(pin - GPIOX_PIN_START)) {
return *(gpio + GPIOX_PUPD_REG_OFFSET) & BIT(pin - GPIOX_PIN_START) ?
1 : 2;
}
break;
case GPIOY_PIN_START...GPIOY_PIN_END:
if (*(gpio + GPIOY_PUEN_REG_OFFSET) & BIT(pin - GPIOY_PIN_START)) {
return *(gpio + GPIOY_PUPD_REG_OFFSET) & BIT(pin - GPIOY_PIN_START) ?
1 : 2;
}
break;
case GPIODV_PIN_START...GPIODV_PIN_END:
if (*(gpio + GPIODV_PUEN_REG_OFFSET) & BIT(pin - GPIODV_PIN_START)) {
return *(gpio + GPIODV_PUPD_REG_OFFSET) & BIT(pin - GPIODV_PIN_START) ?
1 : 2;
}
break;
case GPIOAO_PIN_START...GPIOAO_PIN_END:
if (*(gpio_ao + GPIOAO_PUEN_REG_OFFSET) & BIT(pin - GPIOAO_PIN_START)) {
return *(gpio_ao + GPIOAO_PUPD_REG_OFFSET) & BIT(pin - GPIOAO_PIN_START + 16) ?
1 : 2;
}
break;
default :
break;
}
return 0; // PU/PD disable
}
//------------------------------------------------------------------------------------------------------------
int get_status (int pin)
{
switch (pin) {
case GPIOX_PIN_START...GPIOX_PIN_END:
return *(gpio + GPIOX_INP_REG_OFFSET) & BIT(pin - GPIOX_PIN_START) ?
1 : 0;
case GPIOY_PIN_START...GPIOY_PIN_END:
return *(gpio + GPIOY_INP_REG_OFFSET) & BIT(pin - GPIOY_PIN_START) ?
1 : 0;
case GPIODV_PIN_START...GPIODV_PIN_END:
return *(gpio + GPIODV_INP_REG_OFFSET) & BIT(pin - GPIODV_PIN_START) ?
1 : 0;
case GPIOAO_PIN_START...GPIOAO_PIN_END:
return *(gpio_ao + GPIOAO_INP_REG_OFFSET) & BIT(pin - GPIOAO_PIN_START) ?
1 : 0;
default :
break;
}
return 0;
}
//------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------
//
// system init
//
//------------------------------------------------------------------------------------------------------------
int system_init(void)
{
int fd;
if ((fd = open ("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC) ) < 0) {
fprintf(stderr, "/dev/mem open error!\n"); fflush(stdout);
return -1;
}
gpio = (unsigned int *)mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_REG_MAP);
gpio_ao = (unsigned int *)mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_AO_REG_MAP);
if((unsigned int)gpio == -1 || (unsigned int)gpio_ao == -1) {
fprintf(stderr, "mmap error!\n"); fflush(stdout);
return -1;
}
return 0;
}
//------------------------------------------------------------------------------------------------------------
//
// Start Program
//
//------------------------------------------------------------------------------------------------------------
int main (int argc, char *argv[])
{
int i;
if (system_init() < 0)
{
fprintf (stderr, "%s: System Init failed\n", __func__); fflush(stdout);
return -1;
}
printf("+------+----------+------+---+-------+--- J2 ---+-------+---+------+----------+------+\n");
printf("| GPIO | Name | Mode | V | PU/PD | Physical | PU/PD | V | Mode | Name | GPIO |\n");
printf("+------+----------+------+---+-------+----++----+-------+---+------+----------+------+\n");
for (i = 0; i < 40; i += 2) {
if (header_J2[i].gpio != -1) {
printf("| %3d | %8s | %4s | %d | %4s | %2d |",
header_J2[i].gpio,
header_J2[i].name,
MODE_STR[get_mode(header_J2[i].gpio)],
get_status(header_J2[i].gpio),
PUPD_STR[get_pupd(header_J2[i].gpio)],
i + 1);
}
else {
printf("| - | %8s | - | - | - | %2d |",
header_J2[i].name,
i + 1);
}
if (header_J2[i+1].gpio != -1) {
printf("| %2d | %4s | %d | %4s | %8s | %3d |\n",
i + 2,
PUPD_STR[get_pupd(header_J2[i+1].gpio)],
get_status(header_J2[i+1].gpio),
MODE_STR[get_mode(header_J2[i+1].gpio)],
header_J2[i+1].name,
header_J2[i+1].gpio);
}
else {
printf("| %2d | - | - | - | %8s | - |\n",
i + 2,
header_J2[i+1].name);
}
}
printf("+------+----------+------+---+-------+----++----+-------+---+------+----------+------+\n");
printf("+------+-----------+------+---+-------+--- J7 ---+\n");
printf("| GPIO | Name | Mode | V | PU/PD | Physical |\n");
printf("+------+-----------+------+---+-------+----------+\n");
for(i = 0; i < 7; i ++) {
if (header_J7[i].gpio != -1) {
printf("| %3d | %9s | %4s | %d | %4s | %2d |\n",
header_J7[i].gpio,
header_J7[i].name,
MODE_STR[get_mode(header_J7[i].gpio)],
get_status(header_J7[i].gpio),
PUPD_STR[get_pupd(header_J7[i].gpio)],
i + 1);
}
else {
printf("| - | %9s | - | - | - | %2d |\n",
header_J7[i].name,
i + 1);
}
}
printf("+------+-----------+------+---+-------+----------+\n");
fflush(stdout);
return 0 ;
}
//------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------