269 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			269 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * COPYRIGHT (C) 2018, 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_size_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_size_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)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    struct rt_vbus_dev *vdev = dev->user_data;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    RT_ASSERT(vdev->chnr != 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (vdev)
							 | 
						||
| 
								 | 
							
								        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;
							 | 
						||
| 
								 | 
							
								}
							 |