Commit fd169139 authored by Guilherme Sanches's avatar Guilherme Sanches

Compiling version of the CANBUS driver. The previously introduced files:

canmux.c/h, grcan.c/h, satcan.c/h, were deleted since this were drivers
for boards other than the N2X.
parent 98233dbc
/*
* CAN_MUX driver
*
* --------------------------------------------------------------------------
* -- This file is a part of GAISLER RESEARCH source code.
* -- Copyright (C) 2008, Gaisler Research AB - all rights reserved.
* --
* -- ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN
* -- ACCORDANCE WITH THE GAISLER LICENSE AGREEMENT AND MUST BE APPROVED
* -- IN ADVANCE IN WRITING.
* --
* -- BY DEFAULT, DISTRIBUTION OR DISCLOSURE IS NOT PERMITTED.
* --------------------------------------------------------------------------
*
*/
#include <rtems/libio.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <bsp.h>
#include <rtems/bspIo.h> /* printk */
#include <canmux.h>
#include <ambapp.h>
#ifndef GAISLER_CANMUX
#define GAISLER_CANMUX 0x081
#endif
#if !defined(CANMUX_DEVNAME)
#undef CANMUX_DEVNAME
#define CANMUX_DEVNAME "/dev/canmux"
#endif
/* Enable debug output? */
/* #define DEBUG */
#ifdef DEBUG
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif
#define BUSA_SELECT (1 << 0)
#define BUSB_SELECT (1 << 1)
struct canmux_priv {
volatile unsigned int *muxreg;
rtems_id devsem;
int open;
};
static struct canmux_priv *priv;
static rtems_device_driver canmux_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg);
static rtems_device_driver canmux_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg);
static rtems_device_driver canmux_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg);
static rtems_device_driver canmux_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg);
static rtems_device_driver canmux_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg);
static rtems_device_driver canmux_initialize(rtems_device_major_number major, rtems_device_minor_number unused, void *arg);
static rtems_device_driver canmux_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
{
rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t*)arg;
DBG("CAN_MUX: IOCTL %d\n\r", ioarg->command);
ioarg->ioctl_return = 0;
switch(ioarg->command) {
case CANMUX_IOC_BUSA_SATCAN: *priv->muxreg &= ~BUSA_SELECT; break;
case CANMUX_IOC_BUSA_OCCAN1: *priv->muxreg |= BUSA_SELECT; break;
case CANMUX_IOC_BUSB_SATCAN: *priv->muxreg &= ~BUSB_SELECT; break;
case CANMUX_IOC_BUSB_OCCAN2: *priv->muxreg |= BUSB_SELECT; break;
default: return RTEMS_NOT_DEFINED;
}
return RTEMS_SUCCESSFUL;
}
static rtems_device_driver canmux_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
{
rtems_libio_rw_args_t *rw_args=(rtems_libio_rw_args_t*)arg;
rw_args->bytes_moved = 0;
return RTEMS_SUCCESSFUL;
}
static rtems_device_driver canmux_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
{
rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t*)arg;
rw_args->bytes_moved = 0;
return RTEMS_SUCCESSFUL;
}
static rtems_device_driver canmux_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
{
DBG("CAN_MUX: Closing %d\n\r",minor);
priv->open = 0;
return RTEMS_SUCCESSFUL;
}
static rtems_device_driver canmux_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
{
DBG("CAN_MUX: Opening %d\n\r",minor);
rtems_semaphore_obtain(priv->devsem,RTEMS_WAIT, RTEMS_NO_TIMEOUT);
if (priv->open) {
rtems_semaphore_release(priv->devsem);
return RTEMS_RESOURCE_IN_USE; /* EBUSY */
}
priv->open = 1;
rtems_semaphore_release(priv->devsem);
DBG("CAN_MUX: Opening %d success\n\r",minor);
return RTEMS_SUCCESSFUL;
}
static rtems_device_driver canmux_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
{
struct ambapp_apb_info d;
char fs_name[20];
rtems_status_code status;
DBG("CAN_MUX: Initialize..\n\r");
strcpy(fs_name, CANMUX_DEVNAME);
/* Find core and initialize register pointer */
if (!ambapp_find_apbslv(&ambapp_plb, VENDOR_GAISLER, GAISLER_CANMUX, &d)) {
printk("CAN_MUX: Failed to find CAN_MUX core\n\r");
return -1;
}
status = rtems_io_register_name(fs_name, major, minor);
if (RTEMS_SUCCESSFUL != status)
rtems_fatal_error_occurred(status);
/* Create private structure */
if ((priv = malloc(sizeof(struct canmux_priv))) == NULL) {
printk("CAN_MUX driver could not allocate memory for priv structure\n\r");
return -1;
}
priv->muxreg = (unsigned int*)d.start;
status = rtems_semaphore_create(
rtems_build_name('M', 'd', 'v', '0'),
1,
RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \
RTEMS_NO_PRIORITY_CEILING,
0,
&priv->devsem);
if (status != RTEMS_SUCCESSFUL) {
printk("CAN_MUX: Failed to create dev semaphore (%d)\n\r", status);
free(priv);
return RTEMS_UNSATISFIED;
}
priv->open = 0;
return RTEMS_SUCCESSFUL;
}
#define CANMUX_DRIVER_TABLE_ENTRY { canmux_initialize, canmux_open, canmux_close, canmux_read, canmux_write, canmux_ioctl }
static rtems_driver_address_table canmux_driver = CANMUX_DRIVER_TABLE_ENTRY;
int canmux_register(void)
{
rtems_status_code r;
rtems_device_major_number m;
DBG("CAN_MUX: canmux_register called\n\r");
if ((r = rtems_io_register_driver(0, &canmux_driver, &m)) == RTEMS_SUCCESSFUL) {
DBG("CAN_MUX driver successfully registered, major: %d\n\r", m);
} else {
switch(r) {
case RTEMS_TOO_MANY:
printk("CAN_MUX rtems_io_register_driver failed: RTEMS_TOO_MANY\n\r"); break;
case RTEMS_INVALID_NUMBER:
printk("CAN_MUX rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n\r"); break;
case RTEMS_RESOURCE_IN_USE:
printk("CAN_MUX rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n\r"); break;
default:
printk("CAN_MUX rtems_io_register_driver failed\n\r");
}
return 1;
}
return 0;
}
This diff is collapsed.
/*
* Header file for RTEMS CAN_MUX driver
*
* COPYRIGHT (c) 2008.
* Cobham Gaisler AB.
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#ifndef __CANMUX_H__
#define __CANMUX_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Driver interface */
int canmux_register(void);
/* ioctl calls */
#define CANMUX_IOC_BUSA_SATCAN 1
#define CANMUX_IOC_BUSA_OCCAN1 2
#define CANMUX_IOC_BUSB_SATCAN 3
#define CANMUX_IOC_BUSB_OCCAN2 4
#ifdef __cplusplus
}
#endif
#endif /* __CANMUX_H__ */
/**
* @file
* @ingroup sparc_bsp
* @defgroup can GRCAN
* @ingroup can
* @brief Macros used for grcan controller
*/
/*
* COPYRIGHT (c) 2007.
* Cobham Gaisler AB.
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#ifndef __GRCAN_H__
#define __GRCAN_H__
#ifdef __cplusplus
extern "C" {
#endif
struct grcan_regs {
volatile unsigned int conf; /* 0x00 */
volatile unsigned int stat; /* 0x04 */
volatile unsigned int ctrl; /* 0x08 */
volatile unsigned int dummy0[3]; /* 0x0C-0x014 */
volatile unsigned int smask; /* 0x18 */
volatile unsigned int scode; /* 0x1C */
volatile unsigned int dummy1[56]; /* 0x20-0xFC */
volatile unsigned int pimsr; /* 0x100 */
volatile unsigned int pimr; /* 0x104 */
volatile unsigned int pisr; /* 0x108 */
volatile unsigned int pir; /* 0x10C */
volatile unsigned int imr; /* 0x110 */
volatile unsigned int picr; /* 0x114 */
volatile unsigned int dummy2[58]; /* 0x118-0x1FC */
volatile unsigned int tx0ctrl; /* 0x200 */
volatile unsigned int tx0addr; /* 0x204 */
volatile unsigned int tx0size; /* 0x208 */
volatile unsigned int tx0wr; /* 0x20C */
volatile unsigned int tx0rd; /* 0x210 */
volatile unsigned int tx0irq; /* 0x214 */
volatile unsigned int dummy3[58]; /* 0x218-0x2FC */
volatile unsigned int rx0ctrl; /* 0x300 */
volatile unsigned int rx0addr; /* 0x304 */
volatile unsigned int rx0size; /* 0x308 */
volatile unsigned int rx0wr; /* 0x30C */
volatile unsigned int rx0rd; /* 0x310 */
volatile unsigned int rx0irq; /* 0x314 */
volatile unsigned int rx0mask; /* 0x318 */
volatile unsigned int rx0code; /* 0x31C */
};
struct grcan_stats {
unsigned int passive_cnt;
unsigned int overrun_cnt;
unsigned int rxsync_cnt;
unsigned int txsync_cnt;
unsigned int txloss_cnt;
unsigned int ahberr_cnt;
unsigned int ints;
};
struct grcan_timing {
unsigned char scaler;
unsigned char ps1;
unsigned char ps2;
unsigned int rsj;
unsigned char bpr;
};
struct grcan_selection {
int selection;
int enable0;
int enable1;
};
struct grcan_filter {
unsigned long long mask;
unsigned long long code;
};
/* CAN MESSAGE */
typedef struct {
char extended; /* 1= Extended Frame (29-bit id), 0= STD Frame (11-bit id) */
char rtr; /* RTR - Remote Transmission Request */
char unused; /* unused */
unsigned char len;
unsigned char data[8];
unsigned int id;
} CANMsg;
#define GRCAN_CFG_ABORT 0x00000001
#define GRCAN_CFG_ENABLE0 0x00000002
#define GRCAN_CFG_ENABLE1 0x00000004
#define GRCAN_CFG_SELECTION 0x00000008
#define GRCAN_CFG_SILENT 0x00000010
#define GRCAN_CFG_BPR 0x00000300
#define GRCAN_CFG_RSJ 0x00007000
#define GRCAN_CFG_PS1 0x00f00000
#define GRCAN_CFG_PS2 0x000f0000
#define GRCAN_CFG_SCALER 0xff000000
#define GRCAN_CFG_BPR_BIT 8
#define GRCAN_CFG_RSJ_BIT 12
#define GRCAN_CFG_PS1_BIT 20
#define GRCAN_CFG_PS2_BIT 16
#define GRCAN_CFG_SCALER_BIT 24
#define GRCAN_CTRL_RESET 0x2
#define GRCAN_CTRL_ENABLE 0x1
#define GRCAN_TXCTRL_ENABLE 1
#define GRCAN_TXCTRL_ONGOING 1
#define GRCAN_RXCTRL_ENABLE 1
#define GRCAN_RXCTRL_ONGOING 1
/* Relative offset of IRQ sources to AMBA Plug&Play */
#define GRCAN_IRQ_IRQ 0
#define GRCAN_IRQ_TXSYNC 1
#define GRCAN_IRQ_RXSYNC 2
#define GRCAN_ERR_IRQ 0x1
#define GRCAN_OFF_IRQ 0x2
#define GRCAN_OR_IRQ 0x4
#define GRCAN_RXAHBERR_IRQ 0x8
#define GRCAN_TXAHBERR_IRQ 0x10
#define GRCAN_RXIRQ_IRQ 0x20
#define GRCAN_TXIRQ_IRQ 0x40
#define GRCAN_RXFULL_IRQ 0x80
#define GRCAN_TXEMPTY_IRQ 0x100
#define GRCAN_RX_IRQ 0x200
#define GRCAN_TX_IRQ 0x400
#define GRCAN_RXSYNC_IRQ 0x800
#define GRCAN_TXSYNC_IRQ 0x1000
#define GRCAN_RXERR_IRQ 0x2000
#define GRCAN_TXERR_IRQ 0x4000
#define GRCAN_RXMISS_IRQ 0x8000
#define GRCAN_TXLOSS_IRQ 0x10000
#define GRCAN_STAT_PASS 0x1
#define GRCAN_STAT_OFF 0x2
#define GRCAN_STAT_OR 0x4
#define GRCAN_STAT_AHBERR 0x8
#define GRCAN_STAT_ACTIVE 0x10
#define GRCAN_STAT_RXERRCNT 0xff00
#define GRCAN_STAT_TXERRCNT 0xff0000
/* IOCTL Commands controlling operational
* mode
*/
#define GRCAN_IOC_START 1 /* Bring the link up after open or bus-off */
#define GRCAN_IOC_STOP 2 /* stop to change baud rate/config or closing down */
#define GRCAN_IOC_ISSTARTED 3 /* return RTEMS_SUCCESSFUL when started, othervise EBUSY */
#define GRCAN_IOC_FLUSH 4 /* Waits until all TX messages has been sent */
/* IOCTL Commands that require connection
* to be stopped
*/
#define GRCAN_IOC_SET_SILENT 16 /* enable silent mode read only state */
#define GRCAN_IOC_SET_ABORT 17 /* enable/disable stopping link on AHB Error */
#define GRCAN_IOC_SET_SELECTION 18 /* Set Enable0,Enable1,Selection */
#define GRCAN_IOC_SET_SPEED 19 /* Set baudrate by using driver's baud rate timing calculation routines */
#define GRCAN_IOC_SET_BTRS 20 /* Set baudrate by specifying the timing registers manually */
/* IOCTL Commands can be called whenever */
#define GRCAN_IOC_SET_RXBLOCK 32 /* Enable/disable Blocking on reception (until at least one message has been received) */
#define GRCAN_IOC_SET_TXBLOCK 33 /* Enable/disable Blocking on transmission (until at least one message has been transmitted) */
#define GRCAN_IOC_SET_TXCOMPLETE 34 /* Enable/disable Blocking until all requested messages has been sent */
#define GRCAN_IOC_SET_RXCOMPLETE 35 /* Enable/disable Blocking until all requested has been received */
#define GRCAN_IOC_GET_STATS 36 /* Get Statistics */
#define GRCAN_IOC_CLR_STATS 37 /* Clear Statistics */
#define GRCAN_IOC_SET_AFILTER 38 /* Set Acceptance filters, provide pointer to "struct grcan_filter" or NULL to disable filtering (let all messages pass) */
#define GRCAN_IOC_SET_SFILTER 40 /* Set Sync Messages RX/TX filters, NULL disables the IRQ completely */
#define GRCAN_IOC_GET_STATUS 41 /* Get status register of GRCAN core */
void grcan_register_drv(void);
#ifdef __cplusplus
}
#endif
#endif
......@@ -147,7 +147,7 @@ struct occan_afilter {
#define OCCAN_BLK_MODE_RX 0x1
#define OCCAN_BLK_MODE_TX 0x2
int occan_register(struct ambapp_bus *bus);
int occan_register(amba_confarea_type *bus);
#define OCCAN_SPEED_500K 500000
......@@ -158,8 +158,38 @@ int occan_register(struct ambapp_bus *bus);
#define OCCAN_SPEED_25K 25000
#define OCCAN_SPEED_10K 10000
/**
* @brief Paramameter block for read/write.
*
* It must include 'offset' instead of using iop's offset since we can have
* multiple outstanding i/o's on a device.
*/
typedef struct {
char *buffer;
uint32_t count;
uint32_t flags;
uint32_t bytes_moved;
} rtems_libio_rw_args_t;
/**
* @brief Parameter block for open/close.
*/
typedef struct {
uint32_t flags;
uint32_t mode;
} rtems_libio_open_close_args_t;
/**
* @brief Parameter block for ioctl.
*/
typedef struct {
uint32_t command;
void *buffer;
uint32_t ioctl_return;
} rtems_libio_ioctl_args_t;
#ifdef __cplusplus
}
#endif
#endif
#endif //__OCCAN_H__
/*
* Header file for RTEMS SATCAN FPGA driver
*
* COPYRIGHT (c) 2009.
* Cobham Gaisler AB.
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#ifndef __SATCAN_H__
#define __SATCAN_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Config structure passed to SatCAN_init(..) */
typedef struct {
/* Configuration */
int nodeno;
int dps;
/* Callback functions */
void (*ahb_irq_callback)(void);
void (*pps_irq_callback)(void);
void (*m5_irq_callback)(void);
void (*m4_irq_callback)(void);
void (*m3_irq_callback)(void);
void (*m2_irq_callback)(void);
void (*m1_irq_callback)(void);
void (*sync_irq_callback)(void);
void (*can_irq_callback)(unsigned int fifo);
} satcan_config;
#define SATCAN_HEADER_SIZE 4
#define SATCAN_HEADER_NMM_POS 3
#define SATCAN_PAYLOAD_SIZE 8
/* SatCAN message */
typedef struct {
unsigned char header[SATCAN_HEADER_SIZE]; /* Header of SatCAN message */
unsigned char payload[SATCAN_PAYLOAD_SIZE]; /* Payload of SatCAN message */
} satcan_msg;
/* SatCAN modify register structure */
typedef struct {
unsigned int reg;
unsigned int val;
} satcan_regmod;
/* Driver interface */
int satcan_register(satcan_config *conf);
/* SatCAN interrupt IDs */
#define SATCAN_IRQ_NONACT_TO_ACT 0
#define SATCAN_IRQ_ACTIVE_TO_NONACT 1
#define SATCAN_IRQ_STR1_TO_DPS 2
#define SATCAN_IRQ_DPS_TO_STR1 3
#define SATCAN_IRQ_STR2_TO_DPS 4
#define SATCAN_IRQ_DPS_TO_STR2 5
#define SATCAN_IRQ_STR3_TO_DPS 6
#define SATCAN_IRQ_DPS_TO_STR3 7
#define SATCAN_IRQ_PLD1_TO_DPS 8
#define SATCAN_IRQ_DPS_TO_PLD1 9
#define SATCAN_IRQ_PLD2_TO_DPS 10
#define SATCAN_IRQ_DPS_TO_PLD2 11
#define SATCAN_IRQ_SYNC 16
#define SATCAN_IRQ_TIME_MARKER1 17
#define SATCAN_IRQ_TIME_MARKER2 18
#define SATCAN_IRQ_TIME_MARKER3 19
#define SATCAN_IRQ_TIME_MARKER4 20
#define SATCAN_IRQ_TIME_MARKER5 21
#define SATCAN_IRQ_EOD1 22
#define SATCAN_IRQ_EOD2 23
#define SATCAN_IRQ_TOD 24
#define SATCAN_IRQ_CRITICAL 25
/* IOC */
#define SATCAN_IOC_DMA_2K 1 /* Use DMA area for 2K messages */
#define SATCAN_IOC_DMA_8K 2 /* Use DMA area for 8K messages */
#define SATCAN_IOC_GET_REG 3 /* Provides direct read access to all core registers */
#define SATCAN_IOC_SET_REG 4 /* Provides direct write access to all core registers */
#define SATCAN_IOC_OR_REG 5 /* Provides direct read access to all core registers */
#define SATCAN_IOC_AND_REG 6 /* Provides direct write access to all core registers */
#define SATCAN_IOC_EN_TX1_DIS_TX2 7 /* Enable DMA TX channel 1, Disable DMA TX channel 2 */
#define SATCAN_IOC_EN_TX2_DIS_TX1 8 /* Enable DMA TX channel 2, Disable DMA TX channel 1 */
#define SATCAN_IOC_GET_DMA_MODE 9 /* Returns the current DMA mode */
#define SATCAN_IOC_SET_DMA_MODE 10 /* Sets the DMA mode */
#define SATCAN_IOC_ACTIVATE_DMA 11 /* Directly activate DMA channel */
#define SATCAN_IOC_DEACTIVATE_DMA 12 /* Directly deactivate DMA channel */
#define SATCAN_IOC_DMA_STATUS 13 /* Returns status of directly activated DMA */
#define SATCAN_IOC_GET_DOFFSET 14 /* Get TX DMA offset */
#define SATCAN_IOC_SET_DOFFSET 15 /* Set TX DMA offset */
#define SATCAN_IOC_GET_TIMEOUT 16 /* Set TX DMA timeout */
#define SATCAN_IOC_SET_TIMEOUT 17 /* Get TX DMA timeout */
/* Values used to select core register with IOC_SET_REG/IOC_GET_REG */
#define SATCAN_SWRES 0 /* Software reset */
#define SATCAN_INT_EN 1 /* Interrupt enable */
#define SATCAN_FIFO 3 /* FIFO read */
#define SATCAN_FIFO_RES 4 /* FIFO reset */
#define SATCAN_TSTAMP 5 /* Current time stamp */
#define SATCAN_CMD0 6 /* Command register 0 */
#define SATCAN_CMD1 7 /* Command register 1 */
#define SATCAN_START_CTC 8 /* Start cycle time counter */
#define SATCAN_RAM_BASE 9 /* RAM offset address */
#define SATCAN_STOP_CTC 10 /* Stop cycle time counter / DPS active status */
#define SATCAN_DPS_ACT 10 /* Stop cycle time counter / DPS active status */
#define SATCAN_PLL_RST 11 /* DPLL reset */
#define SATCAN_PLL_CMD 12 /* DPLL command */
#define SATCAN_PLL_STAT 13 /* DPLL status */
#define SATCAN_PLL_OFF 14 /* DPLL offset */
#define SATCAN_DMA 15 /* DMA channel enable */
#define SATCAN_DMA_TX_1_CUR 16 /* DMA channel 1 TX current address */
#define SATCAN_DMA_TX_1_END 17 /* DMA channel 1 TX end address */
#define SATCAN_DMA_TX_2_CUR 18 /* DMA channel 2 TX current address */
#define SATCAN_DMA_TX_2_END 19 /* DMA channel 2 TX end address */
#define SATCAN_RX 20 /* CAN RX enable / Filter start ID */
#define SATCAN_FILTER_START 20 /* CAN RX enable / Filter start ID */
#define SATCAN_FILTER_SETUP 21 /* Filter setup / Filter stop ID */
#define SATCAN_FILTER_STOP 21 /* Filter setup / Filter stop ID */
#define SATCAN_WCTRL 32 /* Wrapper status/control register */
#define SATCAN_WIPEND 33 /* Wrapper interrupt pending register */
#define SATCAN_WIMASK 34 /* Wrapper interrupt mask register */
#define SATCAN_WAHBADDR 35 /* Wrapper AHB address register */
/* Values used to communicate DMA mode */
#define SATCAN_DMA_MODE_USER 0
#define SATCAN_DMA_MODE_SYSTEM 1
/* Values used to directly activate DMA channel */
#define SATCAN_DMA_ENABLE_TX1 1
#define SATCAN_DMA_ENABLE_TX2 2
#ifdef __cplusplus
}
#endif
#endif /* __SATCAN_H__ */
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment