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