5b90ae9c72
SAMv7 encoder driver with GPIO pins was written without IOCTL support. While this driver does not require any lower level IOCTLs for its functionality the upper layer fails on assertion when unknown command can not be send to lower layer. This commit adds IOCTL handler that always returns -ENOTTY as no commands are supported. Signed-off-by: Michal Lenc <michallenc@seznam.cz>
387 lines
11 KiB
C
387 lines
11 KiB
C
/****************************************************************************
|
|
* boards/arm/samv7/common/src/sam_gpio_enc.c
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <errno.h>
|
|
#include <debug.h>
|
|
#include <stdio.h>
|
|
|
|
#include <nuttx/board.h>
|
|
#include <nuttx/sensors/qencoder.h>
|
|
#include <arch/board/board.h>
|
|
#include <nuttx/irq.h>
|
|
|
|
#include "chip.h"
|
|
#include "arm_internal.h"
|
|
|
|
#include "board_gpio_enc.h"
|
|
|
|
#if defined(CONFIG_SENSORS_QENCODER) && defined(CONFIG_SAMV7_GPIO_ENC)
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
struct sam_qeconfig_s
|
|
{
|
|
gpio_pinset_t enca; /* ENC_A pin */
|
|
gpio_pinset_t encb; /* ENC_B pin */
|
|
int enca_irq; /* ENC_A irq */
|
|
int encb_irq; /* ENC_B irq */
|
|
uint32_t position; /* Current position */
|
|
uint32_t position_base; /* Base position */
|
|
uint32_t error; /* Error count */
|
|
};
|
|
|
|
struct sam_gpio_enc_lowerhalf_s
|
|
{
|
|
/* The first field of this state structure must be a pointer to the lower-
|
|
* half callback structure:
|
|
*/
|
|
|
|
const struct qe_ops_s *ops; /* Lower half callback structure */
|
|
struct sam_qeconfig_s *config; /* static configuration */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static int board_gpio_enc_irqx(gpio_pinset_t pinset, int irq,
|
|
xcpt_t irqhandler, void *arg);
|
|
static int sam_gpio_enc_interrupt(int irq, void *context, void *arg);
|
|
static int sam_gpio_enc_position(struct qe_lowerhalf_s *lower, int32_t *pos);
|
|
static int sam_gpio_enc_setup(struct qe_lowerhalf_s *lower);
|
|
static int sam_gpio_enc_shutdown(struct qe_lowerhalf_s *lower);
|
|
static int sam_gpio_enc_reset(struct qe_lowerhalf_s *lower);
|
|
static int sam_gpio_enc_ioctl(struct qe_lowerhalf_s *lower, int cmd,
|
|
unsigned long arg);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const struct qe_ops_s g_qecallbacks =
|
|
{
|
|
.setup = sam_gpio_enc_setup,
|
|
.shutdown = sam_gpio_enc_shutdown,
|
|
.position = sam_gpio_enc_position,
|
|
.setposmax = NULL,
|
|
.reset = sam_gpio_enc_reset,
|
|
.ioctl = sam_gpio_enc_ioctl,
|
|
};
|
|
|
|
static struct sam_qeconfig_s sam_gpio_enc_config =
|
|
{
|
|
.position = 0,
|
|
.position_base = 0,
|
|
.error = 0,
|
|
};
|
|
|
|
static struct sam_gpio_enc_lowerhalf_s sam_gpio_enc_priv =
|
|
{
|
|
.ops = &g_qecallbacks,
|
|
.config = &sam_gpio_enc_config,
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: board_gpio_enc_irqx
|
|
*
|
|
* Description:
|
|
* This function implements the core of the board_button_irq() logic.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int board_gpio_enc_irqx(gpio_pinset_t pinset, int irq,
|
|
xcpt_t irqhandler, void *arg)
|
|
{
|
|
irqstate_t flags;
|
|
|
|
/* Disable interrupts until we are done. This guarantees that the
|
|
* following operations are atomic.
|
|
*/
|
|
|
|
flags = enter_critical_section();
|
|
|
|
/* Are we attaching or detaching? */
|
|
|
|
if (irqhandler != NULL)
|
|
{
|
|
/* Configure the interrupt */
|
|
|
|
sam_gpioirq(pinset);
|
|
irq_attach(irq, irqhandler, arg);
|
|
sam_gpioirqenable(irq);
|
|
}
|
|
else
|
|
{
|
|
/* Detach and disable the interrupt */
|
|
|
|
irq_detach(irq);
|
|
sam_gpioirqdisable(irq);
|
|
}
|
|
|
|
leave_critical_section(flags);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: sam_gpio_enc_interrupt
|
|
*
|
|
* Description:
|
|
* This function implements the core of the board_button_irq() logic.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int sam_gpio_enc_interrupt(int irq, void *context, void *arg)
|
|
{
|
|
struct sam_gpio_enc_lowerhalf_s *dev =
|
|
(struct sam_gpio_enc_lowerhalf_s *)arg;
|
|
struct sam_qeconfig_s *priv = (struct sam_qeconfig_s *)dev->config;
|
|
|
|
unsigned int state_a;
|
|
unsigned int state_b;
|
|
int32_t incr;
|
|
uint32_t new;
|
|
uint32_t incr_mask;
|
|
|
|
/* Read the status of encoder pins */
|
|
|
|
state_a = sam_gpioread(priv->enca);
|
|
state_b = sam_gpioread(priv->encb);
|
|
|
|
new = (state_b << 1 | (state_a ^ state_b));
|
|
incr = ((new - priv->position + 1) & 3) - 1;
|
|
incr_mask = (int32_t)(1 - incr) >> 31;
|
|
|
|
/* Increment position */
|
|
|
|
priv->position += incr & ~incr_mask;
|
|
|
|
/* Count error */
|
|
|
|
priv->error -= incr_mask;
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: sam_gpio_enc_position
|
|
*
|
|
* Description:
|
|
* Return the current position measurement.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int sam_gpio_enc_position(struct qe_lowerhalf_s *lower, int32_t *pos)
|
|
{
|
|
struct sam_gpio_enc_lowerhalf_s *priv =
|
|
(struct sam_gpio_enc_lowerhalf_s *)lower;
|
|
struct sam_qeconfig_s *config = priv->config;
|
|
|
|
*pos = config->position - config->position_base;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: sam_gpio_enc_setup
|
|
*
|
|
* Description:
|
|
* This method is called when the driver is opened. The lower half driver
|
|
* should configure and initialize the device so that it is ready for use.
|
|
* The initial position value should be zero.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int sam_gpio_enc_setup(struct qe_lowerhalf_s *lower)
|
|
{
|
|
struct sam_gpio_enc_lowerhalf_s *priv =
|
|
(struct sam_gpio_enc_lowerhalf_s *)lower;
|
|
struct sam_qeconfig_s *config = priv->config;
|
|
int ret;
|
|
unsigned int state_a;
|
|
unsigned int state_b;
|
|
uint32_t new;
|
|
|
|
/* Configure GPIOs */
|
|
|
|
sam_configgpio(config->encb);
|
|
sam_configgpio(config->enca);
|
|
|
|
/* Reset the encoder position */
|
|
|
|
state_a = sam_gpioread(config->enca);
|
|
state_b = sam_gpioread(config->encb);
|
|
new = (state_b << 1 | (state_a ^ state_b));
|
|
config->position_base = config->position = new;
|
|
|
|
/* Setup interrups for ENC_A and ENC_B pins. */
|
|
|
|
ret = board_gpio_enc_irqx(config->enca, config->enca_irq,
|
|
sam_gpio_enc_interrupt, priv);
|
|
if (ret != OK)
|
|
{
|
|
snerr("ERROR: board_gpio_enc_irqx for ENC_A failed %d\n", ret);
|
|
return -ERROR;
|
|
}
|
|
|
|
ret = board_gpio_enc_irqx(config->encb, config->encb_irq,
|
|
sam_gpio_enc_interrupt, priv);
|
|
if (ret != OK)
|
|
{
|
|
snerr("ERROR: board_gpio_enc_irqx for ENC_B failed %d\n", ret);
|
|
return -ERROR;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: sam_gpio_enc_shutdown
|
|
*
|
|
* Description:
|
|
* This method is called when the driver is closed. The lower half driver
|
|
* should stop data collection, free any resources, disable timer hardware,
|
|
* and put the system into the lowest possible power usage state
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int sam_gpio_enc_shutdown(struct qe_lowerhalf_s *lower)
|
|
{
|
|
struct sam_gpio_enc_lowerhalf_s *priv =
|
|
(struct sam_gpio_enc_lowerhalf_s *)lower;
|
|
struct sam_qeconfig_s *config = priv->config;
|
|
int ret;
|
|
|
|
/* Disable GPIO interrupts. */
|
|
|
|
ret = board_gpio_enc_irqx(config->enca, config->enca_irq,
|
|
NULL, priv);
|
|
if (ret != OK)
|
|
{
|
|
snerr("ERROR: board_gpio_enc_irqx disable for ENC_A failed %d\n",
|
|
ret);
|
|
return -ERROR;
|
|
}
|
|
|
|
ret = board_gpio_enc_irqx(config->encb, config->encb_irq,
|
|
NULL, priv);
|
|
if (ret != OK)
|
|
{
|
|
snerr("ERROR: board_gpio_enc_irqx disable for ENC_B failed %d\n",
|
|
ret);
|
|
return -ERROR;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: sam_gpio_enc_reset
|
|
*
|
|
* Description:
|
|
* Reset the position measurement to base position.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int sam_gpio_enc_reset(struct qe_lowerhalf_s *lower)
|
|
{
|
|
struct sam_gpio_enc_lowerhalf_s *priv =
|
|
(struct sam_gpio_enc_lowerhalf_s *)lower;
|
|
struct sam_qeconfig_s *config =
|
|
(struct sam_qeconfig_s *)priv->config;
|
|
|
|
config->position = config->position_base;
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: sam_gpio_enc_ioctl
|
|
*
|
|
* Description:
|
|
* This method is called when IOCTL command can not be handled by upper
|
|
* half of the driver.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int sam_gpio_enc_ioctl(struct qe_lowerhalf_s *lower, int cmd,
|
|
unsigned long arg)
|
|
{
|
|
/* No commands supported. */
|
|
|
|
return -ENOTTY;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: sam_gpio_enc_init
|
|
*
|
|
* Description:
|
|
* Initialize and register the ENC driver.
|
|
*
|
|
* Input Parameters:
|
|
* enca_cfg - ENC_A pin
|
|
* encb_cfg - ENC_B pin
|
|
* enca_irq - ENC_A interrupt
|
|
* encb_irq - ENC_B interrupt
|
|
*
|
|
****************************************************************************/
|
|
|
|
int sam_gpio_enc_init(gpio_pinset_t enca_cfg, gpio_pinset_t encb_cfg,
|
|
int enca_irq, int encb_irq)
|
|
{
|
|
int ret;
|
|
|
|
struct sam_gpio_enc_lowerhalf_s *dev =
|
|
(struct sam_gpio_enc_lowerhalf_s *)&sam_gpio_enc_priv;
|
|
struct sam_qeconfig_s *priv = (struct sam_qeconfig_s *)dev->config;
|
|
|
|
/* Register the device as "dev/gpio_enc". */
|
|
|
|
ret = qe_register("/dev/gpio_enc", (struct qe_lowerhalf_s *)dev);
|
|
if (ret < 0)
|
|
{
|
|
snerr("ERROR: qe_register failed: %d\n", ret);
|
|
return -ERROR;
|
|
}
|
|
|
|
priv->enca = enca_cfg;
|
|
priv->encb = encb_cfg;
|
|
priv->enca_irq = enca_irq;
|
|
priv->encb_irq = encb_irq;
|
|
|
|
return OK;
|
|
}
|
|
|
|
#endif /* CONFIG_SENSORS_QENCODER && CONFIG_SAMV7_GPIO_ENC */
|