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;
 | |
| }
 |