2023-11-11 02:07:22 +08:00
|
|
|
/*
|
2024-01-02 00:02:48 +08:00
|
|
|
* @copyright (c) 2023-2024, MR Development Team
|
2023-11-11 02:07:22 +08:00
|
|
|
*
|
|
|
|
|
* @license SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*
|
|
|
|
|
* @date 2023-11-08 MacRsh First version
|
|
|
|
|
*/
|
|
|
|
|
|
2024-01-02 00:02:48 +08:00
|
|
|
#include "include/device/mr_pin.h"
|
2023-11-11 02:07:22 +08:00
|
|
|
|
2023-11-25 00:29:11 +08:00
|
|
|
#ifdef MR_USING_PIN
|
2023-11-11 02:07:22 +08:00
|
|
|
|
2023-12-30 03:28:33 +08:00
|
|
|
/**
|
|
|
|
|
* @brief Pin irq structure.
|
|
|
|
|
*/
|
2023-12-05 00:37:13 +08:00
|
|
|
struct pin_irq
|
|
|
|
|
{
|
2023-12-30 03:28:33 +08:00
|
|
|
int number; /**< Pin number */
|
|
|
|
|
int desc; /**< Device descriptor */
|
|
|
|
|
int (*call)(int desc, void *args); /**< Callback function */
|
|
|
|
|
struct mr_list list; /**< List */
|
2023-12-05 00:37:13 +08:00
|
|
|
};
|
|
|
|
|
|
2023-12-10 16:21:48 +08:00
|
|
|
static int pin_set_mode(struct mr_pin *pin, int number, int mode)
|
|
|
|
|
{
|
|
|
|
|
struct mr_pin_ops *ops = (struct mr_pin_ops *)pin->dev.drv->ops;
|
|
|
|
|
|
|
|
|
|
/* Check number is valid */
|
|
|
|
|
if (number < 0)
|
|
|
|
|
{
|
|
|
|
|
return MR_EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Configure pin mode */
|
|
|
|
|
int ret = ops->configure(pin, number, mode);
|
|
|
|
|
if (ret != MR_EOK)
|
|
|
|
|
{
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-27 23:47:57 +08:00
|
|
|
/* Disable interrupt */
|
|
|
|
|
mr_interrupt_disable();
|
|
|
|
|
|
2023-12-10 16:21:48 +08:00
|
|
|
/* If the irq exists, update it */
|
2023-12-27 23:47:57 +08:00
|
|
|
for (struct mr_list *list = pin->irq_list.next; list != &pin->irq_list; list = list->next)
|
2023-12-10 16:21:48 +08:00
|
|
|
{
|
2023-12-30 03:28:33 +08:00
|
|
|
struct pin_irq *irq = (struct pin_irq *)MR_CONTAINER_OF(list, struct pin_irq, list);
|
2023-12-10 16:21:48 +08:00
|
|
|
if (irq->number == number)
|
|
|
|
|
{
|
|
|
|
|
if (mode < MR_PIN_MODE_IRQ_RISING)
|
|
|
|
|
{
|
|
|
|
|
/* Remove irq */
|
|
|
|
|
mr_list_remove(list);
|
|
|
|
|
mr_free(irq);
|
|
|
|
|
} else
|
|
|
|
|
{
|
|
|
|
|
/* Update irq */
|
|
|
|
|
irq->desc = pin->dev.rd_call.desc;
|
|
|
|
|
irq->call = pin->dev.rd_call.call;
|
|
|
|
|
}
|
2023-12-27 23:47:57 +08:00
|
|
|
|
|
|
|
|
/* Enable interrupt */
|
|
|
|
|
mr_interrupt_enable();
|
2023-12-10 16:21:48 +08:00
|
|
|
return MR_EOK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If not exist, allocate new irq */
|
|
|
|
|
if (mode >= MR_PIN_MODE_IRQ_RISING)
|
|
|
|
|
{
|
2023-12-30 03:28:33 +08:00
|
|
|
/* Allocate irq */
|
2023-12-10 16:21:48 +08:00
|
|
|
struct pin_irq *irq = (struct pin_irq *)mr_malloc(sizeof(struct pin_irq));
|
2023-12-30 03:28:33 +08:00
|
|
|
if (irq == MR_NULL)
|
2023-12-10 16:21:48 +08:00
|
|
|
{
|
2023-12-30 03:28:33 +08:00
|
|
|
/* Enable interrupt */
|
|
|
|
|
mr_interrupt_enable();
|
|
|
|
|
return MR_ENOMEM;
|
2023-12-10 16:21:48 +08:00
|
|
|
}
|
2023-12-30 03:28:33 +08:00
|
|
|
irq->number = number;
|
|
|
|
|
irq->desc = pin->dev.rd_call.desc;
|
|
|
|
|
irq->call = pin->dev.rd_call.call;
|
|
|
|
|
mr_list_init(&irq->list);
|
|
|
|
|
mr_list_insert_before(&pin->irq_list, &irq->list);
|
|
|
|
|
|
|
|
|
|
/* Clear call */
|
|
|
|
|
pin->dev.rd_call.call = MR_NULL;
|
2023-12-10 16:21:48 +08:00
|
|
|
}
|
2023-12-27 23:47:57 +08:00
|
|
|
|
|
|
|
|
/* Enable interrupt */
|
|
|
|
|
mr_interrupt_enable();
|
2023-12-10 16:21:48 +08:00
|
|
|
return MR_EOK;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-25 23:39:33 +08:00
|
|
|
static ssize_t mr_pin_read(struct mr_dev *dev, int off, void *buf, size_t size, int async)
|
2023-11-11 02:07:22 +08:00
|
|
|
{
|
2023-11-25 00:29:11 +08:00
|
|
|
struct mr_pin *pin = (struct mr_pin *)dev;
|
|
|
|
|
struct mr_pin_ops *ops = (struct mr_pin_ops *)dev->drv->ops;
|
2023-11-11 02:07:22 +08:00
|
|
|
uint8_t *rd_buf = (uint8_t *)buf;
|
2023-12-31 16:32:01 +08:00
|
|
|
ssize_t rd_size;
|
2023-11-11 02:07:22 +08:00
|
|
|
|
2024-01-10 16:41:40 +08:00
|
|
|
#ifdef MR_USING_PIN_CHECK
|
2023-11-11 02:07:22 +08:00
|
|
|
/* Check offset is valid */
|
|
|
|
|
if (off < 0)
|
|
|
|
|
{
|
|
|
|
|
return MR_EINVAL;
|
|
|
|
|
}
|
2024-01-10 16:41:40 +08:00
|
|
|
#endif /* MR_USING_PIN_CHECK */
|
2023-11-11 02:07:22 +08:00
|
|
|
|
|
|
|
|
for (rd_size = 0; rd_size < size; rd_size += sizeof(*rd_buf))
|
|
|
|
|
{
|
2023-11-25 00:29:11 +08:00
|
|
|
*rd_buf = (uint8_t)ops->read(pin, off);
|
2023-11-11 02:07:22 +08:00
|
|
|
rd_buf++;
|
|
|
|
|
}
|
|
|
|
|
return rd_size;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-25 23:39:33 +08:00
|
|
|
static ssize_t mr_pin_write(struct mr_dev *dev, int off, const void *buf, size_t size, int async)
|
2023-11-11 02:07:22 +08:00
|
|
|
{
|
2023-11-25 00:29:11 +08:00
|
|
|
struct mr_pin *pin = (struct mr_pin *)dev;
|
|
|
|
|
struct mr_pin_ops *ops = (struct mr_pin_ops *)dev->drv->ops;
|
2023-11-11 02:07:22 +08:00
|
|
|
uint8_t *wr_buf = (uint8_t *)buf;
|
2023-12-31 16:32:01 +08:00
|
|
|
ssize_t wr_size;
|
2023-11-11 02:07:22 +08:00
|
|
|
|
2024-01-10 16:41:40 +08:00
|
|
|
#ifdef MR_USING_PIN_CHECK
|
2023-11-11 02:07:22 +08:00
|
|
|
/* Check offset is valid */
|
|
|
|
|
if (off < 0)
|
|
|
|
|
{
|
|
|
|
|
return MR_EINVAL;
|
|
|
|
|
}
|
2024-01-10 16:41:40 +08:00
|
|
|
#endif /* MR_USING_PIN_CHECK */
|
2023-11-11 02:07:22 +08:00
|
|
|
|
|
|
|
|
for (wr_size = 0; wr_size < size; wr_size += sizeof(*wr_buf))
|
|
|
|
|
{
|
2023-12-25 15:48:49 +08:00
|
|
|
ops->write(pin, off, *wr_buf);
|
2023-11-11 02:07:22 +08:00
|
|
|
wr_buf++;
|
|
|
|
|
}
|
|
|
|
|
return wr_size;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-25 00:29:11 +08:00
|
|
|
static int mr_pin_ioctl(struct mr_dev *dev, int off, int cmd, void *args)
|
2023-11-11 02:07:22 +08:00
|
|
|
{
|
2023-11-25 00:29:11 +08:00
|
|
|
struct mr_pin *pin = (struct mr_pin *)dev;
|
2023-11-11 02:07:22 +08:00
|
|
|
|
|
|
|
|
switch (cmd)
|
|
|
|
|
{
|
2023-12-20 16:04:33 +08:00
|
|
|
case MR_CTL_PIN_SET_MODE:
|
2023-12-10 16:21:48 +08:00
|
|
|
{
|
|
|
|
|
if (args != MR_NULL)
|
|
|
|
|
{
|
|
|
|
|
struct mr_pin_config config = *((struct mr_pin_config *)args);
|
|
|
|
|
|
|
|
|
|
return pin_set_mode(pin, off, config.mode);
|
|
|
|
|
}
|
|
|
|
|
return MR_EINVAL;
|
|
|
|
|
}
|
2023-11-11 02:07:22 +08:00
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
{
|
2023-11-15 17:19:24 +08:00
|
|
|
return MR_ENOTSUP;
|
2023-11-11 02:07:22 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-25 00:29:11 +08:00
|
|
|
static ssize_t mr_pin_isr(struct mr_dev *dev, int event, void *args)
|
2023-11-11 02:07:22 +08:00
|
|
|
{
|
2023-12-05 00:37:13 +08:00
|
|
|
struct mr_pin *pin = (struct mr_pin *)dev;
|
|
|
|
|
|
2023-11-11 02:07:22 +08:00
|
|
|
switch (event)
|
|
|
|
|
{
|
2023-12-11 18:01:06 +08:00
|
|
|
case MR_ISR_PIN_EXTI_INT:
|
2023-11-11 02:07:22 +08:00
|
|
|
{
|
2023-12-05 00:37:13 +08:00
|
|
|
ssize_t number = *(int *)args;
|
|
|
|
|
|
|
|
|
|
/* If the irq exists, call it */
|
2023-12-31 16:32:01 +08:00
|
|
|
for (struct mr_list *list = pin->irq_list.next; list != &pin->irq_list; list = list->next)
|
2023-12-05 00:37:13 +08:00
|
|
|
{
|
2023-12-30 03:28:33 +08:00
|
|
|
struct pin_irq *irq = (struct pin_irq *)MR_CONTAINER_OF(list, struct pin_irq, list);
|
2023-12-05 00:37:13 +08:00
|
|
|
if (irq->number == number)
|
|
|
|
|
{
|
|
|
|
|
irq->call(irq->desc, &number);
|
|
|
|
|
return MR_EEXIST;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-12-30 03:28:33 +08:00
|
|
|
return MR_ENOTFOUND;
|
2023-11-11 02:07:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
return MR_ENOTSUP;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2023-11-25 00:29:11 +08:00
|
|
|
* @brief This function registers a pin.
|
2023-11-11 02:07:22 +08:00
|
|
|
*
|
2023-11-25 00:29:11 +08:00
|
|
|
* @param pin The pin.
|
|
|
|
|
* @param name The name of the pin.
|
|
|
|
|
* @param drv The driver of the pin.
|
2023-11-11 02:07:22 +08:00
|
|
|
*
|
|
|
|
|
* @return MR_EOK on success, otherwise an error code.
|
|
|
|
|
*/
|
2023-11-25 00:29:11 +08:00
|
|
|
int mr_pin_register(struct mr_pin *pin, const char *name, struct mr_drv *drv)
|
2023-11-11 02:07:22 +08:00
|
|
|
{
|
|
|
|
|
static struct mr_dev_ops ops =
|
|
|
|
|
{
|
|
|
|
|
MR_NULL,
|
|
|
|
|
MR_NULL,
|
2023-11-25 00:29:11 +08:00
|
|
|
mr_pin_read,
|
|
|
|
|
mr_pin_write,
|
|
|
|
|
mr_pin_ioctl,
|
|
|
|
|
mr_pin_isr
|
2023-11-11 02:07:22 +08:00
|
|
|
};
|
|
|
|
|
|
2023-12-30 03:28:33 +08:00
|
|
|
MR_ASSERT(pin != MR_NULL);
|
|
|
|
|
MR_ASSERT(name != MR_NULL);
|
|
|
|
|
MR_ASSERT(drv != MR_NULL);
|
|
|
|
|
MR_ASSERT(drv->ops != MR_NULL);
|
2023-11-11 02:07:22 +08:00
|
|
|
|
2023-12-05 00:37:13 +08:00
|
|
|
/* Initialize the fields */
|
|
|
|
|
mr_list_init(&pin->irq_list);
|
|
|
|
|
|
2023-11-25 00:29:11 +08:00
|
|
|
/* Register the pin */
|
|
|
|
|
return mr_dev_register(&pin->dev, name, Mr_Dev_Type_Pin, MR_SFLAG_RDWR, &ops, drv);
|
2023-11-11 02:07:22 +08:00
|
|
|
}
|
|
|
|
|
|
2024-01-10 23:03:30 +08:00
|
|
|
#ifdef MR_USING_MSH
|
|
|
|
|
#include "include/components/mr_msh.h"
|
|
|
|
|
static int desc = -1;
|
|
|
|
|
static int msh_pin_mode(const char *arg)
|
|
|
|
|
{
|
|
|
|
|
/* Parse mode */
|
|
|
|
|
int mode;
|
|
|
|
|
if (strncmp(arg, "in", 2) == 0)
|
|
|
|
|
{
|
|
|
|
|
mode = MR_PIN_MODE_INPUT;
|
|
|
|
|
} else if (strncmp(arg, "out", 3) == 0)
|
|
|
|
|
{
|
|
|
|
|
mode = MR_PIN_MODE_OUTPUT;
|
|
|
|
|
} else
|
|
|
|
|
{
|
|
|
|
|
return MR_EINVAL;
|
|
|
|
|
}
|
|
|
|
|
/* Set mode */
|
|
|
|
|
int ret = mr_dev_ioctl(desc, MR_CTL_PIN_SET_MODE, &mode);
|
|
|
|
|
mr_msh_printf("%s\r\n", mr_strerror((ret < 0 ? ret : MR_EOK)));
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
static int msh_pin_write(const char *arg)
|
|
|
|
|
{
|
|
|
|
|
/* Parse level */
|
|
|
|
|
uint8_t level;
|
|
|
|
|
if ((strncmp(arg, "high", 4) == 0) || (strncmp(arg, "1", 1) == 0))
|
|
|
|
|
{
|
|
|
|
|
level = MR_PIN_HIGH_LEVEL;
|
|
|
|
|
} else if ((strncmp(arg, "low", 3) == 0) || (strncmp(arg, "0", 1) == 0))
|
|
|
|
|
{
|
|
|
|
|
level = MR_PIN_LOW_LEVEL;
|
|
|
|
|
} else
|
|
|
|
|
{
|
|
|
|
|
return MR_EINVAL;
|
|
|
|
|
}
|
|
|
|
|
/* Write level */
|
|
|
|
|
int ret = (int)mr_dev_write(desc, &level, sizeof(level));
|
|
|
|
|
mr_msh_printf("%s\r\n", mr_strerror((ret < 0 ? ret : MR_EOK)));
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
static int msh_pin_read(void)
|
|
|
|
|
{
|
|
|
|
|
/* Read level */
|
|
|
|
|
uint8_t level;
|
|
|
|
|
int ret = (int)mr_dev_read(desc, &level, sizeof(level));
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
|
|
|
|
mr_msh_printf("%s\r\n", mr_strerror(ret));
|
|
|
|
|
} else
|
|
|
|
|
{
|
|
|
|
|
mr_msh_printf("%s\r\n", (level ? "high" : "low"));
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
static int msh_pin_toggle(void)
|
|
|
|
|
{
|
|
|
|
|
/* Read level */
|
|
|
|
|
uint8_t level;
|
|
|
|
|
int ret = (int)mr_dev_read(desc, &level, sizeof(level));
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
|
|
|
|
mr_msh_printf("%s\r\n", mr_strerror(ret));
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
/* Toggle level */
|
|
|
|
|
level = !level;
|
|
|
|
|
ret = (int)mr_dev_write(desc, &level, sizeof(level));
|
|
|
|
|
mr_msh_printf("%s\r\n", mr_strerror((ret < 0 ? ret : MR_EOK)));
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
static void msh_pin_printf_usage(void)
|
|
|
|
|
{
|
|
|
|
|
mr_msh_printf("Usage: pin mode <number> <mode>\r\n");
|
|
|
|
|
mr_msh_printf(" pin write <number> <level>\r\n");
|
|
|
|
|
mr_msh_printf(" pin read <number>\r\n");
|
|
|
|
|
mr_msh_printf(" pin toggle <number>\r\n");
|
|
|
|
|
}
|
|
|
|
|
static int msh_pin(int argc, void *argv)
|
|
|
|
|
{
|
|
|
|
|
/* Open the pin */
|
|
|
|
|
if (desc < 0)
|
|
|
|
|
{
|
|
|
|
|
desc = mr_dev_open("pin", MR_OFLAG_RDWR);
|
|
|
|
|
if (desc < 0)
|
|
|
|
|
{
|
|
|
|
|
return desc;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check the arguments */
|
|
|
|
|
if (argc < 2)
|
|
|
|
|
{
|
|
|
|
|
msh_pin_printf_usage();
|
|
|
|
|
return MR_EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Parse number */
|
|
|
|
|
int number = -1;
|
|
|
|
|
sscanf(MR_MSH_GET_ARG(1), "%d", &number);
|
|
|
|
|
int ret = mr_dev_ioctl(desc, MR_CTL_PIN_SET_NUMBER, &number);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
|
|
|
|
mr_msh_printf("%s\r\n", mr_strerror(ret));
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
/* Parse command */
|
|
|
|
|
if (strncmp(MR_MSH_GET_ARG(0), "read", 4) == 0)
|
|
|
|
|
{
|
|
|
|
|
return msh_pin_read();
|
|
|
|
|
} else if (strncmp(MR_MSH_GET_ARG(0), "toggle", 6) == 0)
|
|
|
|
|
{
|
|
|
|
|
return msh_pin_toggle();
|
|
|
|
|
}
|
|
|
|
|
if (argc >= 3)
|
|
|
|
|
{
|
|
|
|
|
if (strncmp(MR_MSH_GET_ARG(0), "mode", 4) == 0)
|
|
|
|
|
{
|
|
|
|
|
return msh_pin_mode(MR_MSH_GET_ARG(2));
|
|
|
|
|
} else if (strncmp(MR_MSH_GET_ARG(0), "write", 5) == 0)
|
|
|
|
|
{
|
|
|
|
|
return msh_pin_write(MR_MSH_GET_ARG(2));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
msh_pin_printf_usage();
|
|
|
|
|
return MR_EINVAL;
|
|
|
|
|
}
|
|
|
|
|
MR_MSH_CMD_EXPORT(pin, msh_pin, "Pin control.");
|
|
|
|
|
#endif /* MR_USING_MSH */
|
|
|
|
|
|
2023-11-25 00:29:11 +08:00
|
|
|
#endif /* MR_USING_PIN */
|