为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

davinci之路-CMEM模块分析

2013-07-17 10页 pdf 240KB 36阅读

用户头像

is_123442

暂无简介

举报
davinci之路-CMEM模块分析 DAVINCI之路—CMEM模块分析 --- liangxing031@163.com cmem用于管理一个或者多个连续的物理块内存并提供地址转换(虚拟地址转换到物理 地址或物理地址转换到虚拟地址)功能,物理连续地址内存用于主处理器与 DSP(或者协 处理器、DMA)的数据 BUF共享。 在系统启动的时候,av_capture_load.sh运行加载 cmemk.ko驱动初始化,注册字符设备 CMEM,并传递命令参数到内核,格式如下: ...
davinci之路-CMEM模块分析
DAVINCI之路—CMEM模块分析 --- liangxing031@163.com cmem用于管理一个或者多个连续的物理块内存并提供地址转换(虚拟地址转换到物理 地址或物理地址转换到虚拟地址)功能,物理连续地址内存用于主处理器与 DSP(或者协 处理器、DMA)的数据 BUF共享。 在系统启动的时候,av_capture_load.sh运行加载 cmemk.ko驱动初始化,注册字符设备 CMEM,并传递命令参数到内核,格式如下: Insmod cmemk.ko phys_start=0x83C00000,phys_end=0x88000000 pools=50x512,2x4096,2x8192,2x16384,1x32768,1x51200,1x102400,1x4096000 phys_start、phys_end代 CMEM的开始和结束物理地址,16进制表示。 pools代表分配的内存池,指定了大小和数量,如 50x512,表示分配 50个 512大小的空间, 十进制表示。 上面 SH文件中的命令行创建了 8个 pools,分别是:50 buffers of size 512,2 buffers of size 4096,2 buffers of size 8192,2 buffers of size 16384,1 buffers of size 32768,1 buffers of size 51200,1 buffers of size 102400,1 buffers of size 4096000。实际分配是以页为最小单位,大 小依赖于平台页的大小(PAGE_SIZE,一般为 4K),如 50x512,实际分配的是 50x4096。 因此,实际分配的 Pools空间大于等于命令行请求的大小。CMEM剩余空间用于 Heap的分 配。 Davinci系统内存可以分成两大类: LINUX系统管理——kmalloc获取内核空间内存,malloc获取用户空间内存。 CMEM模块管理——CMEM_alloc获得cmem内存,CMEM_free()释放cmem内存 图一 通过修改 U-BOOT 的参数 bootargs 配置 linux 系统内存大小,剩下的内存通过加载 CMEM,用于内核模块 CMEM使用。 CMEM内存可以分成4种类型: Video/Audio codec usage memory Encode data circular buffer Cache pool buffer Input/output buffer CMEM模块软件结构包括两部分:驱动和封装的中间库。中间库是调用驱动的接口(open、 release、mmap、ioctl)实现,用于向应用层提供统一 API接口函数。 提供的 API接口如下: /* Initialize the CMEM module. Must be called before other API calls. */ int CMEM_init (void) /*Finalize the CMEM module.*/ CMEM_exit (void) /*Find the pool that best fits a given buffer size and has a buffer available*/ int CMEM_getPool (size_t size) /* Allocate memory from a specified pool*/ void * CMEM_allocPool (int poolid, CMEM_AllocParams *params) /*Allocate memory of a specified size.*/ void * CMEM_alloc (size_t size, CMEM_AllocParams *params) /*Free a buffer previously allocated with CMEM_alloc()/CMEM_allocPool().*/ int CMEM_free (void *ptr, CMEM_AllocParams *params) /*Register shared usage of an already-allocated buffer.*/ void * CMEM_registerAlloc (unsigned long physp) /*Unregister use of a buffer previously registered with CMEM_registerAlloc()*/ int CMEM_unregister (void *ptr, CMEM_AllocParams *params) /*Get the physical address of a contiguous buffer*/ unsigned long CMEM_getPhys (void *ptr) 以 H264 编码算法为例,算法内存分配主要用到以下 5 个 API 接口函数:CMEM_init,CMEM_exit , CMEM_alloc,CMEM_free,CMEM_getPhys。 1、CMEM_init()源代码: int CMEM_init(void) { int flags; unsigned int version; /*调用了 CMEM驱动接口 open,open中的具体实现将在 CMEM驱动中说明*/ cmem_fd = open("/dev/cmem", O_RDWR); if (cmem_fd == -1) { __E("init: Failed to open /dev/cmem: '%s'\n", strerror(errno)); return -1; } ref_count++; /*版本检查*/ version = CMEM_getVersion(); if ((version & 0xffff0000) != (CMEM_VERSION & 0xffff0000)) { __E("init: major version mismatch between interface and driver.\n"); __E(" needs driver version %#x, got %#x\n", CMEM_VERSION, version); CMEM_exit(); return -1; } else if ((version & 0x0000ffff) < (CMEM_VERSION & 0x0000ffff)) { __E("init: minor version mismatch between interface and driver.\n"); __E(" needs driver minor version %#x or greater.\n"" got minor version %#x (full version %#x)\n", CMEM_VERSION & 0x0000ffff, version & 0x0000ffff, version); CMEM_exit(); return -1; } flags = fcntl(cmem_fd, F_GETFD); if (flags != -1) { fcntl(cmem_fd, F_SETFD, flags | FD_CLOEXEC); } else { __E("init: fcntl(F_GETFD) failed: '%s'\n", strerror(errno)); } return 0; } 2、CMEM_alloc源代码: void *CMEM_alloc(size_t size, CMEM_AllocParams *params) { return alloc(0, size, params); } static void *alloc(int blockid, size_t size, CMEM_AllocParams *params) { if (params == NULL) { params = &CMEM_DEFAULTPARAMS;//如果未指定 params,使用默认的 params。 } if (!validate_init()) { return NULL; } if (params->type == CMEM_POOL) { return getAndAllocFromPool(blockid, size, params);//分配 Pool空间 } else { return allocFromHeap(blockid, size, params);//分配 Heap空间 } } 2.1、 getAndAllocFromPool()源代码: static void *getAndAllocFromPool(int blockid, size_t size, CMEM_AllocParams *params) { int poolid; /*brief Find the pool in memory block blockid that best fits a given buffer size and has a buffer available*/. poolid = CMEM_getPool2(blockid, size); if (poolid == -1) return NULL; /*根据 poolid,获得 pool空间*/ return allocFromPool(blockid, poolid, params); } 2.2 、allocFromHeap()源代码: static void *allocFromHeap(int blockid, size_t size, CMEM_AllocParams *params) { void *userp; union CMEM_AllocUnion allocDesc; unsigned long physp; unsigned int cmd; int rv; cmd = CMEM_IOCALLOCHEAP | params->flags; allocDesc.alloc_heap_inparams.size = size; allocDesc.alloc_heap_inparams.align = params->alignment == 0 ? 1 : params->alignment; allocDesc.alloc_heap_inparams.blockid = blockid; rv = ioctl(cmem_fd, cmd, &allocDesc); if (rv != 0) { __E("allocHeap: ioctl %s failed: %d\n", cmd == CMEM_IOCALLOCHEAP ? "CMEM_IOCALLOCHEAP" : "CMEM_IOCALLOCHEAPCACHED",rv); return NULL; } physp = allocDesc.physp; __D("allocHeap: allocated phys buffer %#lx\n", physp); /* Map the physical address to user space */ userp = mmap(0, // Preferred start address size, // Length to be mapped PROT_WRITE | PROT_READ, // Read and write access MAP_SHARED, // Shared memory cmem_fd, // File descriptor physp); // The byte offset from fd if (userp == MAP_FAILED) { __E("allocHeap: Failed to mmap buffer at physical address %#lx\n",physp); __E(" Freeing phys buffer %#lx\n", physp); ioctl(cmem_fd, CMEM_IOCFREEHEAPPHYS, &physp); return NULL; } __D("allocHeap: mmap succeeded, returning virt buffer %p\n", userp); return userp; } 上面的库 API都是基于驱动 CMEM提供的接口(open、release、mmap、ioctl)实现,以下分析 CMEM 驱动的各个接口的具体实现。 在 SH文件 av_capture_load.sh运行加载 cmemk.ko时,驱动中初始化函数 cmem_init()执行。 主要完全字符设备 CMEM的注册,根据 cmemk.ko传递的参数完成 POOLS、Heap的初始化。 字符设备提供的文件操作结构描述定义如下: static struct file_operations cmem_fxns = { ioctl: ioctl, mmap: mmap, open: open, release: release }; 1、open接口 static int open(struct inode *inode, struct file *filp) { atomic_inc(&reference_count); /*原子变量的初始化*/ return 0; } 2、mmap接口 static int mmap(struct file *filp, struct vm_area_struct *vma) { unsigned long physp; struct pool_buffer *entry; physp = vma->vm_pgoff << PAGE_SHIFT; if (down_interruptible(&cmem_mutex)) { return -ERESTARTSYS; } /* looks for an allocated pool buffer with phyisical addr physp*/ entry = find_busy_entry(physp, NULL, NULL, NULL); up(&cmem_mutex); if (entry != NULL) { if (entry->flags & CMEM_CACHED) { __D("mmap: calling set_cached(%p) ...\n", vma); return set_cached(vma); } else { __D("mmap: calling set_noncached(%p) ...\n", vma); return set_noncached(vma); } } else { __E("mmap: can't find allocated buffer with physp %lx\n", physp); return -EINVAL; } } 3、ioctl接口 Ioctl支持的命令有 CMEM_IOCALLOC、CMEM_IOCALLOCHEAP、 CMEM_IOCFREE、CMEM_IOCGETPHYS、CMEM_IOCGETSIZE、CMEM_IOCGETPOOL CMEM_IOCCACHE、CMEM_IOCGETVERSION、CMEM_IOCGETBLOCK、 CMEM_IOCREGUSER 以 DM365 的 H264 编码为例,主要用到的 IOCTL 命令有:CMEM_IOCALLOCHEAP, CMEM_IOCFREE、CMEM_IOCGETPHYS、CMEM_IOCCACHE、CMEM_IOCGETVERSION 下面的代码也仅以使用的 IOCTL做为简单说明。 static int ioctl(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long args) { unsigned int __user *argp = (unsigned int __user *) args; struct list_head *freelistp = NULL; struct list_head *busylistp = NULL; struct list_head *registeredlistp; struct list_head *e = NULL; struct list_head *u; struct list_head *unext; struct pool_buffer *entry; struct registered_user *user; unsigned long physp; unsigned long virtp, virtp_end; size_t reqsize, size, align; int delta = MAXTYPE(int); int pool = -1,I,bi,id; struct block_struct block; union CMEM_AllocUnion allocDesc; switch (cmd & CMEM_IOCCMDMASK) { case CMEM_IOCALLOCHEAP: //Heap的分配 printk("CMEM_IOCALLOCHEAP\n"); if (copy_from_user(&allocDesc, argp, sizeof(allocDesc))) { return -EFAULT; } size = allocDesc.alloc_heap_inparams.size; align = allocDesc.alloc_heap_inparams.align; bi = allocDesc.alloc_heap_inparams.blockid; __D("ALLOCHEAP%s ioctl received on heap pool for block %d\n",cmd & CMEM_CACHED ? "CACHED" : "", bi); if (bi >= NBLOCKS) { __E("ioctl: invalid block id %d, must be < %d\n",bi, NBLOCKS); return -EINVAL; } if (heap_pool[bi] == -1) { __E("ioctl: no heap available in block %d\n", bi); return -EINVAL; } if (down_interruptible(&cmem_mutex)) { return -ERESTARTSYS; } virtp = (unsigned long)HeapMem_alloc(bi, size, align); if (virtp == (unsigned long)NULL) { __E("ioctl: failed to allocate heap buffer of size %#x\n",size); up(&cmem_mutex); return -ENOMEM; } entry = kmalloc(sizeof(struct pool_buffer), GFP_KERNEL); if (!entry) { __E("ioctl: failed to kmalloc pool_buffer struct for heap"); HeapMem_free(bi, (void *)virtp, size); up(&cmem_mutex); return -ENOMEM; } physp = get_phys(virtp); entry->id = heap_pool[bi]; entry->physp = physp; entry->kvirtp = virtp; entry->size = size; entry->flags = cmd & ~CMEM_IOCCMDMASK; INIT_LIST_HEAD(&entry->users); busylistp = &p_objs[bi][heap_pool[bi]].busylist; list_add_tail(&entry->element, busylistp); user = kmalloc(sizeof(struct registered_user), GFP_KERNEL); user->filp = filp; list_add(&user->element, &entry->users); up(&cmem_mutex); if (put_user(physp, argp)) { return -EFAULT; } __D("ALLOCHEAP%s: allocated %#x size buffer at %#lx (phys address)\n", cmd & CMEM_CACHED ? "CACHED" : "", entry->size, entry->physp); break; /*argp contains a pointer to an alloc descriptor coming in, and the * physical address and size of the allocated buffer when returning.*/ case CMEM_IOCALLOC: printk("CMEM_IOCALLOC\n"); break; /*argp contains either the user virtual address or the physical * address of the buffer to free coming in, and contains the pool * where it was freed from and the size of the block on return.*/ case CMEM_IOCFREE: printk("CMEM_IOCFREE\n"); break; /* argp contains the user virtual address of the buffer to translate * coming in, and the translated physical address on return.*/ case CMEM_IOCGETPHYS : printk("GETPHYS ioctl received.\n"); if (get_user(virtp, argp)) { return -EFAULT; } physp = get_phys(virtp); if (physp == ~(0L)) { __E("GETPHYS: Failed to convert virtual %#lx to physical.\n",virtp); return -EFAULT; } if (put_user(physp, argp)) { return -EFAULT; } __D("GETPHYS: returning %#lx\n", physp); break; /* argp contains the pool to query for size coming in, and the size * of the pool on return.*/ case CMEM_IOCGETSIZE: printk("GETSIZE ioctl received\n"); break; /* argp contains the requested pool buffers size coming in, and the * pool id (index) on return.*/ case CMEM_IOCGETPOOL://根据请求的 pool buf大小,返回 pool id printk("GETPOOL ioctl received.\n"); break; case CMEM_IOCCACHE: printk("CACHE%s%s ioctl received.\n", cmd & CMEM_WB ? "WB" : "", cmd & CMEM_INV ? "INV" : ""); if (copy_from_user(&block, argp, sizeof(block))) { return -EFAULT; } virtp = block.addr; virtp_end = virtp + block.size; switch (cmd) { case CMEM_IOCCACHEWB: #ifdef USE_CACHE_VOID_ARG dmac_clean_range((void *)virtp, (void *)virtp_end); #else dmac_clean_range(virtp, virtp_end); #endif printk("CACHEWB: cleaned user virtual %#lx->%#lx\n",virtp, virtp_end); break; case CMEM_IOCCACHEINV: #ifdef USE_CACHE_VOID_ARG dmac_inv_range((void *)virtp, (void *)virtp_end); #else dmac_inv_range(virtp, virtp_end); #endif printk("CACHEINV: invalidated user virtual %#lx->%#lx\n",virtp, virtp_end); break; case CMEM_IOCCACHEWBINV: #ifdef USE_CACHE_VOID_ARG dmac_flush_range((void *)virtp, (void *)virtp_end); #else dmac_flush_range(virtp, virtp_end); #endif printk("CACHEWBINV: flushed user virtual %#lx->%#lx\n",virtp, virtp_end); break; } break; case CMEM_IOCGETVERSION: //获得 CMEM的版本号 printk("GETVERSION ioctl received, returning %#x.\n", version); if (put_user(version, argp)) { return -EFAULT; } break; case CMEM_IOCGETBLOCK: printk("GETBLOCK ioctl received.\n"); break; case CMEM_IOCREGUSER: printk("REGUSER ioctl received.\n"); break; default: printk("Unknown ioctl received.\n"); return -EINVAL; } return 0; } 由于篇幅有限,有些细节没有详细说明,如果有朋友有任何疑问,欢迎给我邮件 (liangxing031@163.com),大家一起探讨,共同进步!
/
本文档为【davinci之路-CMEM模块分析】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索