469 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			469 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2006-2023, RT-Thread Development Team
 | 
						|
 *
 | 
						|
 * SPDX-License-Identifier: Apache-2.0
 | 
						|
 *
 | 
						|
 * Change Logs:
 | 
						|
 * Date           Author       Notes
 | 
						|
 * 2021-11-11     GuEe-GUI     the first version
 | 
						|
 * 2023-10-12     fangjianzhou support SDL2
 | 
						|
 */
 | 
						|
 | 
						|
#include <rtthread.h>
 | 
						|
#include <cpuport.h>
 | 
						|
 | 
						|
#include <virtio.h>
 | 
						|
 | 
						|
rt_inline void _virtio_dev_check(struct virtio_device *dev)
 | 
						|
{
 | 
						|
    RT_ASSERT(dev != RT_NULL);
 | 
						|
    RT_ASSERT(dev->mmio_config != RT_NULL);
 | 
						|
}
 | 
						|
 | 
						|
void virtio_reset_device(struct virtio_device *dev)
 | 
						|
{
 | 
						|
    _virtio_dev_check(dev);
 | 
						|
 | 
						|
    dev->mmio_config->status = 0;
 | 
						|
}
 | 
						|
 | 
						|
void virtio_status_acknowledge_driver(struct virtio_device *dev)
 | 
						|
{
 | 
						|
    _virtio_dev_check(dev);
 | 
						|
 | 
						|
    dev->mmio_config->status |= VIRTIO_STATUS_ACKNOWLEDGE | VIRTIO_STATUS_DRIVER;
 | 
						|
}
 | 
						|
 | 
						|
void virtio_status_driver_ok(struct virtio_device *dev)
 | 
						|
{
 | 
						|
    _virtio_dev_check(dev);
 | 
						|
 | 
						|
    dev->mmio_config->status |= VIRTIO_STATUS_FEATURES_OK | VIRTIO_STATUS_DRIVER_OK;
 | 
						|
}
 | 
						|
 | 
						|
void virtio_interrupt_ack(struct virtio_device *dev)
 | 
						|
{
 | 
						|
    rt_uint32_t status;
 | 
						|
 | 
						|
    _virtio_dev_check(dev);
 | 
						|
 | 
						|
    status = dev->mmio_config->interrupt_status;
 | 
						|
 | 
						|
    if (status != 0)
 | 
						|
    {
 | 
						|
        dev->mmio_config->interrupt_ack = status;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
rt_bool_t virtio_has_feature(struct virtio_device *dev, rt_uint32_t feature_bit)
 | 
						|
{
 | 
						|
    _virtio_dev_check(dev);
 | 
						|
 | 
						|
    return !!(dev->mmio_config->device_features & (1UL << feature_bit));
 | 
						|
}
 | 
						|
 | 
						|
rt_err_t virtio_queues_alloc(struct virtio_device *dev, rt_size_t queues_num)
 | 
						|
{
 | 
						|
    _virtio_dev_check(dev);
 | 
						|
 | 
						|
    dev->queues = rt_malloc(sizeof(struct virtq) * queues_num);
 | 
						|
 | 
						|
    if (dev->queues != RT_NULL)
 | 
						|
    {
 | 
						|
        dev->queues_num = queues_num;
 | 
						|
 | 
						|
        return RT_EOK;
 | 
						|
    }
 | 
						|
 | 
						|
    return -RT_ENOMEM;
 | 
						|
}
 | 
						|
 | 
						|
void virtio_queues_free(struct virtio_device *dev)
 | 
						|
{
 | 
						|
    if (dev->queues != RT_NULL)
 | 
						|
    {
 | 
						|
        dev->queues_num = 0;
 | 
						|
        rt_free(dev->queues);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
rt_err_t virtio_queue_init(struct virtio_device *dev, rt_uint32_t queue_index, rt_size_t ring_size)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
    void *pages;
 | 
						|
    rt_size_t pages_total_size;
 | 
						|
    struct virtq *queue;
 | 
						|
 | 
						|
    _virtio_dev_check(dev);
 | 
						|
 | 
						|
    RT_ASSERT(dev->mmio_config->queue_num_max > 0);
 | 
						|
    RT_ASSERT(dev->mmio_config->queue_num_max > queue_index);
 | 
						|
    /* ring_size is power of 2 */
 | 
						|
    RT_ASSERT(ring_size > 0);
 | 
						|
    RT_ASSERT(((ring_size - 1) & ring_size) == 0);
 | 
						|
 | 
						|
    queue = &dev->queues[queue_index];
 | 
						|
    pages_total_size = VIRTIO_PAGE_ALIGN(
 | 
						|
            VIRTQ_DESC_TOTAL_SIZE(ring_size) + VIRTQ_AVAIL_TOTAL_SIZE(ring_size)) + VIRTQ_USED_TOTAL_SIZE(ring_size);
 | 
						|
 | 
						|
    pages = rt_malloc_align(pages_total_size, VIRTIO_PAGE_SIZE);
 | 
						|
 | 
						|
    if (pages == RT_NULL)
 | 
						|
    {
 | 
						|
        return -RT_ENOMEM;
 | 
						|
    }
 | 
						|
 | 
						|
    queue->free = rt_malloc(sizeof(rt_bool_t) * ring_size);
 | 
						|
 | 
						|
    if (queue->free == RT_NULL)
 | 
						|
    {
 | 
						|
        rt_free_align(pages);
 | 
						|
        return -RT_ENOMEM;
 | 
						|
    }
 | 
						|
 | 
						|
    rt_memset(pages, 0, pages_total_size);
 | 
						|
 | 
						|
    dev->mmio_config->guest_page_size = VIRTIO_PAGE_SIZE;
 | 
						|
    dev->mmio_config->queue_sel = queue_index;
 | 
						|
    dev->mmio_config->queue_num = ring_size;
 | 
						|
    dev->mmio_config->queue_align = VIRTIO_PAGE_SIZE;
 | 
						|
    dev->mmio_config->queue_pfn = VIRTIO_VA2PA(pages) >> VIRTIO_PAGE_SHIFT;
 | 
						|
 | 
						|
    queue->num = ring_size;
 | 
						|
    queue->desc = (struct virtq_desc *)((rt_ubase_t)pages);
 | 
						|
    queue->avail = (struct virtq_avail *)(((rt_ubase_t)pages) + VIRTQ_DESC_TOTAL_SIZE(ring_size));
 | 
						|
    queue->used = (struct virtq_used *)VIRTIO_PAGE_ALIGN(
 | 
						|
            (rt_ubase_t)&queue->avail->ring[ring_size] + VIRTQ_AVAIL_RES_SIZE);
 | 
						|
 | 
						|
    queue->used_idx = 0;
 | 
						|
 | 
						|
    /* All descriptors start out unused */
 | 
						|
    for (i = 0; i < ring_size; ++i)
 | 
						|
    {
 | 
						|
        queue->free[i] = RT_TRUE;
 | 
						|
    }
 | 
						|
 | 
						|
    queue->free_count = ring_size;
 | 
						|
 | 
						|
    return RT_EOK;
 | 
						|
}
 | 
						|
 | 
						|
void virtio_queue_destroy(struct virtio_device *dev, rt_uint32_t queue_index)
 | 
						|
{
 | 
						|
    struct virtq *queue;
 | 
						|
 | 
						|
    _virtio_dev_check(dev);
 | 
						|
 | 
						|
    RT_ASSERT(dev->mmio_config->queue_num_max > 0);
 | 
						|
    RT_ASSERT(dev->mmio_config->queue_num_max > queue_index);
 | 
						|
 | 
						|
    queue = &dev->queues[queue_index];
 | 
						|
 | 
						|
    RT_ASSERT(queue->num > 0);
 | 
						|
 | 
						|
    rt_free(queue->free);
 | 
						|
    rt_free_align((void *)queue->desc);
 | 
						|
 | 
						|
    dev->mmio_config->queue_sel = queue_index;
 | 
						|
    dev->mmio_config->queue_pfn = RT_NULL;
 | 
						|
 | 
						|
    queue->num = 0;
 | 
						|
    queue->desc = RT_NULL;
 | 
						|
    queue->avail = RT_NULL;
 | 
						|
    queue->used = RT_NULL;
 | 
						|
}
 | 
						|
 | 
						|
void virtio_queue_notify(struct virtio_device *dev, rt_uint32_t queue_index)
 | 
						|
{
 | 
						|
    _virtio_dev_check(dev);
 | 
						|
 | 
						|
    dev->mmio_config->queue_notify = queue_index;
 | 
						|
}
 | 
						|
 | 
						|
void virtio_submit_chain(struct virtio_device *dev, rt_uint32_t queue_index, rt_uint16_t desc_index)
 | 
						|
{
 | 
						|
    rt_size_t ring_size;
 | 
						|
    struct virtq *queue;
 | 
						|
 | 
						|
    _virtio_dev_check(dev);
 | 
						|
 | 
						|
    queue = &dev->queues[queue_index];
 | 
						|
    ring_size = queue->num;
 | 
						|
 | 
						|
    /* Tell the device the first index in our chain of descriptors */
 | 
						|
    queue->avail->ring[queue->avail->idx % ring_size] = desc_index;
 | 
						|
    rt_hw_dsb();
 | 
						|
 | 
						|
    /* Tell the device another avail ring entry is available */
 | 
						|
    queue->avail->idx++;
 | 
						|
    rt_hw_dsb();
 | 
						|
}
 | 
						|
 | 
						|
rt_uint16_t virtio_alloc_desc(struct virtio_device *dev, rt_uint32_t queue_index)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
    struct virtq *queue;
 | 
						|
 | 
						|
    _virtio_dev_check(dev);
 | 
						|
 | 
						|
    RT_ASSERT(queue_index < dev->queues_num);
 | 
						|
 | 
						|
    queue = &dev->queues[queue_index];
 | 
						|
 | 
						|
    if (queue->free_count > 0)
 | 
						|
    {
 | 
						|
        rt_size_t ring_size = queue->num;
 | 
						|
 | 
						|
        for (i = 0; i < ring_size; ++i)
 | 
						|
        {
 | 
						|
            if (queue->free[i])
 | 
						|
            {
 | 
						|
                queue->free[i] = RT_FALSE;
 | 
						|
                queue->free_count--;
 | 
						|
 | 
						|
                return (rt_uint16_t)i;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return VIRTQ_INVALID_DESC_ID;
 | 
						|
}
 | 
						|
 | 
						|
void virtio_free_desc(struct virtio_device *dev, rt_uint32_t queue_index, rt_uint16_t desc_index)
 | 
						|
{
 | 
						|
    struct virtq *queue;
 | 
						|
 | 
						|
    _virtio_dev_check(dev);
 | 
						|
 | 
						|
    queue = &dev->queues[queue_index];
 | 
						|
 | 
						|
    RT_ASSERT(queue_index < dev->queues_num);
 | 
						|
    RT_ASSERT(!queue->free[desc_index]);
 | 
						|
 | 
						|
    queue->desc[desc_index].addr = 0;
 | 
						|
    queue->desc[desc_index].len = 0;
 | 
						|
    queue->desc[desc_index].flags = 0;
 | 
						|
    queue->desc[desc_index].next = 0;
 | 
						|
 | 
						|
    queue->free[desc_index] = RT_TRUE;
 | 
						|
 | 
						|
    queue->free_count++;
 | 
						|
}
 | 
						|
 | 
						|
rt_err_t virtio_alloc_desc_chain(struct virtio_device *dev, rt_uint32_t queue_index, rt_size_t count,
 | 
						|
        rt_uint16_t *indexs)
 | 
						|
{
 | 
						|
    int i, j;
 | 
						|
 | 
						|
    _virtio_dev_check(dev);
 | 
						|
 | 
						|
    RT_ASSERT(indexs != RT_NULL);
 | 
						|
 | 
						|
    if (dev->queues[queue_index].free_count < count)
 | 
						|
    {
 | 
						|
        return -RT_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
    for (i = 0; i < count; ++i)
 | 
						|
    {
 | 
						|
        indexs[i] = virtio_alloc_desc(dev, queue_index);
 | 
						|
 | 
						|
        if (indexs[i] == VIRTQ_INVALID_DESC_ID)
 | 
						|
        {
 | 
						|
            for (j = 0; j < i; ++j)
 | 
						|
            {
 | 
						|
                virtio_free_desc(dev, queue_index, indexs[j]);
 | 
						|
            }
 | 
						|
 | 
						|
            return -RT_ERROR;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return RT_EOK;
 | 
						|
}
 | 
						|
 | 
						|
void virtio_free_desc_chain(struct virtio_device *dev, rt_uint32_t queue_index, rt_uint16_t desc_index)
 | 
						|
{
 | 
						|
    rt_uint16_t flags, next;
 | 
						|
    struct virtq_desc *desc;
 | 
						|
 | 
						|
    _virtio_dev_check(dev);
 | 
						|
 | 
						|
    desc = &dev->queues[queue_index].desc[0];
 | 
						|
 | 
						|
    for (;;)
 | 
						|
    {
 | 
						|
        flags = desc[desc_index].flags;
 | 
						|
        next = desc[desc_index].next;
 | 
						|
 | 
						|
        virtio_free_desc(dev, queue_index, desc_index);
 | 
						|
 | 
						|
        if (flags & VIRTQ_DESC_F_NEXT)
 | 
						|
        {
 | 
						|
            desc_index = next;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void virtio_fill_desc(struct virtio_device *dev, rt_uint32_t queue_index, rt_uint16_t desc_index,
 | 
						|
        rt_uint64_t addr, rt_uint32_t len, rt_uint16_t flags, rt_uint16_t next)
 | 
						|
{
 | 
						|
    struct virtq_desc *desc;
 | 
						|
 | 
						|
    _virtio_dev_check(dev);
 | 
						|
 | 
						|
    desc = &dev->queues[queue_index].desc[desc_index];
 | 
						|
 | 
						|
    desc->addr = addr;
 | 
						|
    desc->len = len;
 | 
						|
    desc->flags = flags;
 | 
						|
    desc->next = next;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef RT_USING_SMART
 | 
						|
#ifdef RT_USING_VIRTIO_GPU
 | 
						|
 | 
						|
#include <virtio_gpu.h>
 | 
						|
#include "drivers/lcd.h"
 | 
						|
#include <dfs_file.h>
 | 
						|
#include <lwp_user_mm.h>
 | 
						|
 | 
						|
static struct rt_device_graphic_info _graphic_info;
 | 
						|
static struct rt_device_rect_info    _rect_info;
 | 
						|
static struct rt_device              _fb        = {};
 | 
						|
static rt_device_t                   _gpu_dev   = RT_NULL;
 | 
						|
 | 
						|
static rt_err_t fb_open(rt_device_t dev, rt_uint16_t oflag)
 | 
						|
{
 | 
						|
    return RT_EOK;
 | 
						|
}
 | 
						|
 | 
						|
static rt_err_t fb_close(rt_device_t dev)
 | 
						|
{
 | 
						|
    return RT_EOK;
 | 
						|
}
 | 
						|
 | 
						|
static rt_err_t fb_control(rt_device_t dev, int cmd, void *args)
 | 
						|
{
 | 
						|
    switch(cmd)
 | 
						|
    {
 | 
						|
        case FBIOPAN_DISPLAY:
 | 
						|
        {
 | 
						|
            rt_hw_cpu_dcache_clean(_graphic_info.framebuffer, _graphic_info.smem_len);
 | 
						|
            rt_device_control(_gpu_dev, RTGRAPHIC_CTRL_RECT_UPDATE, &_rect_info);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        case FBIOGET_FSCREENINFO:
 | 
						|
        {
 | 
						|
            struct fb_fix_screeninfo *info = (struct fb_fix_screeninfo *)args;
 | 
						|
            strncpy(info->id, "lcd", sizeof(info->id));
 | 
						|
            info->smem_len = _graphic_info.smem_len;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        case FBIOGET_VSCREENINFO:
 | 
						|
        {
 | 
						|
            struct fb_var_screeninfo *info = (struct fb_var_screeninfo *)args;
 | 
						|
            info->bits_per_pixel           = _graphic_info.bits_per_pixel;
 | 
						|
            info->xres                     = _graphic_info.width;
 | 
						|
            info->yres                     = _graphic_info.height;
 | 
						|
            info->yres_virtual             = _graphic_info.height;
 | 
						|
            info->xres_virtual             = _graphic_info.width;
 | 
						|
            info->transp.offset            = 24;
 | 
						|
            info->transp.length            = 8;
 | 
						|
            info->red.offset               = 0;
 | 
						|
            info->red.length               = 8;
 | 
						|
            info->green.offset             = 8;
 | 
						|
            info->green.length             = 8;
 | 
						|
            info->blue.offset              = 16;
 | 
						|
            info->blue.length              = 8;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        case RT_FIOMMAP2:
 | 
						|
        {
 | 
						|
            struct dfs_mmap2_args *mmap2 = (struct dfs_mmap2_args *)args;
 | 
						|
 | 
						|
            if(mmap2)
 | 
						|
            {
 | 
						|
                mmap2->ret = lwp_map_user_phy(lwp_self(), RT_NULL, rt_kmem_v2p(_graphic_info.framebuffer), mmap2->length, 1);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                return -EIO;
 | 
						|
            }
 | 
						|
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        default:
 | 
						|
            break;
 | 
						|
    }
 | 
						|
 | 
						|
    return RT_EOK;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef RT_USING_DEVICE_OPS
 | 
						|
const static struct rt_device_ops fb_ops =
 | 
						|
{
 | 
						|
    RT_NULL,
 | 
						|
    fb_open,
 | 
						|
    fb_close,
 | 
						|
    RT_NULL,
 | 
						|
    RT_NULL,
 | 
						|
    fb_control
 | 
						|
};
 | 
						|
#endif
 | 
						|
 | 
						|
static int fb_init()
 | 
						|
{
 | 
						|
    _gpu_dev = rt_device_find("virtio-gpu0");
 | 
						|
 | 
						|
    if(_gpu_dev == RT_NULL)
 | 
						|
    {
 | 
						|
        return -RT_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
    if(_gpu_dev != RT_NULL && rt_device_open(_gpu_dev, 0) == RT_EOK)
 | 
						|
    {
 | 
						|
        rt_memset(&_graphic_info, 0, sizeof(_graphic_info));
 | 
						|
        rt_memset(&_rect_info, 0, sizeof(_rect_info));
 | 
						|
        rt_device_control(_gpu_dev, VIRTIO_DEVICE_CTRL_GPU_SET_PRIMARY, RT_NULL);
 | 
						|
        rt_device_control(_gpu_dev, VIRTIO_DEVICE_CTRL_GPU_CREATE_2D, (void *)RTGRAPHIC_PIXEL_FORMAT_RGB888);
 | 
						|
        rt_device_control(_gpu_dev, RTGRAPHIC_CTRL_GET_INFO, &_graphic_info);
 | 
						|
        _rect_info.x      = 0;
 | 
						|
        _rect_info.y      = 0;
 | 
						|
        _rect_info.width  = _graphic_info.width;
 | 
						|
        _rect_info.height = _graphic_info.height;
 | 
						|
        memset(_graphic_info.framebuffer, 0xff, _graphic_info.smem_len);
 | 
						|
        rt_device_control(_gpu_dev, RTGRAPHIC_CTRL_RECT_UPDATE, &_rect_info);
 | 
						|
    }
 | 
						|
 | 
						|
    if(rt_device_find("fb0") != RT_NULL)
 | 
						|
    {
 | 
						|
        rt_kprintf("a device named fb0 already exists\n");
 | 
						|
        return -RT_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
    _fb.type = RT_Device_Class_Miscellaneous;
 | 
						|
 | 
						|
#ifdef RT_USING_DEVICE_OPS
 | 
						|
    _fb.ops        = &fb_ops;
 | 
						|
#else
 | 
						|
    _fb.init       = RT_NULL;
 | 
						|
    _fb.open       = fb_open;
 | 
						|
    _fb.close      = fb_close;
 | 
						|
    _fb.read       = RT_NULL;
 | 
						|
    _fb.write      = RT_NULL;
 | 
						|
    _fb.control    = fb_control;
 | 
						|
    _fb.user_data  = RT_NULL;
 | 
						|
#endif
 | 
						|
 | 
						|
    rt_device_register(&_fb, "fb0", RT_DEVICE_FLAG_RDWR);
 | 
						|
    return RT_EOK;
 | 
						|
}
 | 
						|
INIT_COMPONENT_EXPORT(fb_init);
 | 
						|
#endif
 | 
						|
#endif
 |