270 lines
6.1 KiB
C
270 lines
6.1 KiB
C
/*
|
|
* COPYRIGHT (C) 2011-2021, Real-Thread Information Technology Ltd
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2013-11-04 Grissiom add comment
|
|
*/
|
|
|
|
#include <rthw.h>
|
|
#include <rtthread.h>
|
|
#include <rtdevice.h>
|
|
|
|
#include "vbus.h"
|
|
|
|
static void _rx_indicate(void *ctx)
|
|
{
|
|
rt_device_t dev = ctx;
|
|
|
|
if (dev->rx_indicate)
|
|
dev->rx_indicate(dev, 0);
|
|
}
|
|
|
|
static void _tx_complete(void *ctx)
|
|
{
|
|
rt_device_t dev = ctx;
|
|
|
|
if (dev->tx_complete)
|
|
dev->tx_complete(dev, 0);
|
|
}
|
|
|
|
static rt_err_t _open(rt_device_t dev, rt_uint16_t oflag)
|
|
{
|
|
int chnr;
|
|
struct rt_vbus_dev *vdev = dev->user_data;
|
|
|
|
if (vdev->chnr)
|
|
return RT_EOK;
|
|
|
|
/* FIXME: request the same name for twice will crash */
|
|
chnr = rt_vbus_request_chn(&vdev->req, RT_WAITING_FOREVER);
|
|
if (chnr < 0)
|
|
return chnr;
|
|
|
|
vdev->chnr = chnr;
|
|
rt_vbus_register_listener(chnr, RT_VBUS_EVENT_ID_RX, _rx_indicate, dev);
|
|
rt_vbus_register_listener(chnr, RT_VBUS_EVENT_ID_TX, _tx_complete, dev);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t _close(rt_device_t dev)
|
|
{
|
|
struct rt_vbus_dev *vdev = dev->user_data;
|
|
|
|
RT_ASSERT(vdev->chnr != 0);
|
|
|
|
rt_vbus_close_chn(vdev->chnr);
|
|
vdev->chnr = 0;
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_ssize_t _read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
|
|
{
|
|
rt_size_t outsz = 0;
|
|
struct rt_vbus_dev *vdev = dev->user_data;
|
|
|
|
RT_ASSERT(vdev->chnr != 0);
|
|
|
|
if (vdev->act == RT_NULL)
|
|
{
|
|
vdev->act = rt_vbus_data_pop(vdev->chnr);
|
|
vdev->pos = 0;
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
rt_err_t err;
|
|
|
|
while (vdev->act)
|
|
{
|
|
rt_size_t cpysz;
|
|
|
|
if (size - outsz > vdev->act->size - vdev->pos)
|
|
cpysz = vdev->act->size - vdev->pos;
|
|
else
|
|
cpysz = size - outsz;
|
|
|
|
rt_memcpy((char*)buffer + outsz, ((char*)(vdev->act+1)) + vdev->pos, cpysz);
|
|
vdev->pos += cpysz;
|
|
|
|
outsz += cpysz;
|
|
if (outsz == size)
|
|
{
|
|
return outsz;
|
|
}
|
|
else if (outsz > size)
|
|
RT_ASSERT(0);
|
|
|
|
/* free old and get new */
|
|
rt_free(vdev->act);
|
|
vdev->act = rt_vbus_data_pop(vdev->chnr);
|
|
vdev->pos = 0;
|
|
}
|
|
|
|
/* TODO: We don't want to touch the rx_indicate here. But this lead to
|
|
* some duplication. Maybe we should find a better way to handle this.
|
|
*/
|
|
if (rt_interrupt_get_nest() == 0)
|
|
{
|
|
err = rt_vbus_listen_on(vdev->chnr, RT_WAITING_FOREVER);
|
|
}
|
|
else
|
|
{
|
|
err = rt_vbus_listen_on(vdev->chnr, 0);
|
|
}
|
|
if (err != RT_EOK)
|
|
{
|
|
rt_set_errno(err);
|
|
return outsz;
|
|
}
|
|
vdev->act = rt_vbus_data_pop(vdev->chnr);
|
|
vdev->pos = 0;
|
|
}
|
|
}
|
|
|
|
static rt_ssize_t _write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
|
|
{
|
|
rt_err_t err;
|
|
struct rt_vbus_dev *vdev = dev->user_data;
|
|
|
|
RT_ASSERT(vdev->chnr != 0);
|
|
|
|
if (rt_interrupt_get_nest() == 0)
|
|
{
|
|
/* Thread context. */
|
|
err = rt_vbus_post(vdev->chnr, vdev->req.prio,
|
|
buffer, size, RT_WAITING_FOREVER);
|
|
}
|
|
else
|
|
{
|
|
/* Interrupt context. */
|
|
err = rt_vbus_post(vdev->chnr, vdev->req.prio,
|
|
buffer, size, 0);
|
|
}
|
|
|
|
if (err)
|
|
{
|
|
rt_set_errno(err);
|
|
return 0;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
rt_err_t _control(rt_device_t dev, int cmd, void *args)
|
|
{
|
|
RT_ASSERT(dev);
|
|
|
|
switch (cmd) {
|
|
case VBUS_IOC_LISCFG: {
|
|
struct rt_vbus_dev *vdev = dev->user_data;
|
|
struct rt_vbus_dev_liscfg *liscfg = args;
|
|
|
|
RT_ASSERT(vdev->chnr != 0);
|
|
if (!liscfg)
|
|
return -RT_ERROR;
|
|
|
|
rt_vbus_register_listener(vdev->chnr, liscfg->event,
|
|
liscfg->listener, liscfg->ctx);
|
|
return RT_EOK;
|
|
}
|
|
break;
|
|
#ifdef RT_VBUS_USING_FLOW_CONTROL
|
|
case VBUS_IOCRECV_WM: {
|
|
struct rt_vbus_dev *vdev = dev->user_data;
|
|
struct rt_vbus_wm_cfg *cfg;
|
|
|
|
RT_ASSERT(vdev->chnr != 0);
|
|
|
|
if (!args)
|
|
return -RT_ERROR;
|
|
|
|
cfg = (struct rt_vbus_wm_cfg*)args;
|
|
if (cfg->low > cfg->high)
|
|
return -RT_ERROR;
|
|
|
|
rt_vbus_set_recv_wm(vdev->chnr, cfg->low, cfg->high);
|
|
return RT_EOK;
|
|
}
|
|
break;
|
|
case VBUS_IOCPOST_WM: {
|
|
struct rt_vbus_dev *vdev = dev->user_data;
|
|
struct rt_vbus_wm_cfg *cfg;
|
|
|
|
RT_ASSERT(vdev->chnr != 0);
|
|
|
|
if (!args)
|
|
return -RT_ERROR;
|
|
|
|
cfg = (struct rt_vbus_wm_cfg*)args;
|
|
if (cfg->low > cfg->high)
|
|
return -RT_ERROR;
|
|
|
|
rt_vbus_set_post_wm(vdev->chnr, cfg->low, cfg->high);
|
|
return RT_EOK;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
};
|
|
|
|
return -RT_ENOSYS;
|
|
}
|
|
|
|
rt_uint8_t rt_vbus_get_chnnr(rt_device_t dev)
|
|
{
|
|
struct rt_vbus_dev *vdev;
|
|
|
|
RT_ASSERT(dev);
|
|
|
|
vdev = dev->user_data;
|
|
|
|
return vdev->chnr;
|
|
}
|
|
|
|
void rt_vbus_chnx_register_disconn(rt_device_t dev,
|
|
rt_vbus_event_listener indi,
|
|
void *ctx)
|
|
{
|
|
if (dev && dev->user_data)
|
|
{
|
|
struct rt_vbus_dev *vdev = dev->user_data;
|
|
RT_ASSERT(vdev->chnr != 0);
|
|
|
|
rt_vbus_register_listener(vdev->chnr, RT_VBUS_EVENT_ID_DISCONN,
|
|
indi, ctx);
|
|
}
|
|
}
|
|
|
|
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
|
|
|
|
extern struct rt_vbus_dev rt_vbus_chn_devx[];
|
|
static struct rt_device _devx[32];
|
|
|
|
rt_err_t rt_vbus_chnx_init(void)
|
|
{
|
|
int i;
|
|
struct rt_vbus_dev *p;
|
|
|
|
for (i = 0, p = rt_vbus_chn_devx;
|
|
i < ARRAY_SIZE(_devx) && p->req.name;
|
|
i++, p++)
|
|
{
|
|
_devx[i].type = RT_Device_Class_Char;
|
|
_devx[i].open = _open;
|
|
_devx[i].close = _close;
|
|
_devx[i].read = _read;
|
|
_devx[i].write = _write;
|
|
_devx[i].control = _control;
|
|
_devx[i].user_data = p;
|
|
rt_device_register(&_devx[i], p->req.name, RT_DEVICE_FLAG_RDWR);
|
|
}
|
|
|
|
return RT_EOK;
|
|
}
|