270 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			270 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/*
 | 
						|
 * 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;
 | 
						|
}
 |