作者:咕唧咕唧liukun321
来自:http://blog.csdn.net/liukun321
三星系列处理器,从S3c6410开始SOC上就集成了一个MFC(Multi Format Codec)多格式媒体编解码器硬件模块。并且三星的linux BSP开源了对它的驱动。看过MFC驱动的朋友,肯定发现了,在驱动初始化过程中,内核需要加载一个Samsung MFC 固件。没有它MFC是用不了的。下面我们就看看,这个固件是怎么和内核联系起来的。从哪能获得固件。
先看MFC固件的加载,这个过程如下,以exynos4412 Linux 3.5 的BSP为例,在linux-3.5\drivers\media\video\s5p-mfc\s5p_mfc.c:static int s5p_mfc_open(struct file *file) { structs5p_mfc_dev *dev = video_drvdata(file); structs5p_mfc_ctx *ctx = NULL; structvb2_queue *q; unsignedlong flags; intret = 0; exynos_cpufreq_lock_freq(1,MAX_CPU_FREQ); #ifdef CONFIG_BUSFREQ_OPP dev_lock(dev->bus_dev,dev->device, BUSFREQ_400MHZ); #endif mfc_debug_enter(); dev->num_inst++; /* It is guarded by mfc_mutex in vfd */ /*Allocate memory for context */ ctx= kzalloc(sizeof *ctx, GFP_KERNEL); if(!ctx) { mfc_err("Notenough memory\n"); ret= -ENOMEM; gotoerr_alloc; } v4l2_fh_init(&ctx->fh,video_devdata(file)); file->private_data= &ctx->fh; v4l2_fh_add(&ctx->fh); ctx->dev= dev; INIT_LIST_HEAD(&ctx->src_queue); INIT_LIST_HEAD(&ctx->dst_queue); ctx->src_queue_cnt= 0; ctx->dst_queue_cnt= 0; /*Get context number */ ctx->num= 0; while(dev->ctx[ctx->num]) { ctx->num++; if(ctx->num >= MFC_NUM_CONTEXTS) { mfc_err("Toomany open contexts\n"); ret= -EBUSY; gotoerr_no_ctx; } } /*Mark context as idle */ spin_lock_irqsave(&dev->condlock,flags); clear_bit(ctx->num,&dev->ctx_work_bits); spin_unlock_irqrestore(&dev->condlock,flags); dev->ctx[ctx->num]= ctx; if(s5p_mfc_get_node_type(file) == MFCNODE_DECODER) { ctx->type= MFCINST_DECODER; ctx->c_ops= get_dec_codec_ops(); /*Setup ctrl handler */ ret= s5p_mfc_dec_ctrls_setup(ctx); if(ret) { mfc_err("Failedto setup mfc controls\n"); gotoerr_ctrls_setup; } }else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) { ctx->type= MFCINST_ENCODER; ctx->c_ops= get_enc_codec_ops(); /*only for encoder */ INIT_LIST_HEAD(&ctx->ref_queue); ctx->ref_queue_cnt= 0; /*Setup ctrl handler */ ret= s5p_mfc_enc_ctrls_setup(ctx); if(ret) { mfc_err("Failedto setup mfc controls\n"); gotoerr_ctrls_setup; } }else { ret= -ENOENT; gotoerr_bad_node; } ctx->fh.ctrl_handler= &ctx->ctrl_handler; ctx->inst_no= -1; /*Load firmware if this is the first instance */ if(dev->num_inst == 1) { dev->watchdog_timer.expires= jiffies + msecs_to_jiffies(MFC_WATCHDOG_INTERVAL); add_timer(&dev->watchdog_timer); ret= s5p_mfc_power_on(); if(ret < 0) { mfc_err("poweron failed\n"); gotoerr_pwr_enable; } s5p_mfc_clock_on(); ret = s5p_mfc_alloc_and_load_firmware(dev); if(ret) gotoerr_alloc_fw; /*Init the FW */ ret= s5p_mfc_init_hw(dev); if(ret) gotoerr_init_hw; s5p_mfc_clock_off(); } /*Init videobuf2 queue for CAPTURE */ q= &ctx->vq_dst; q->type= V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; q->drv_priv= &ctx->fh; if(s5p_mfc_get_node_type(file) == MFCNODE_DECODER) { q->io_modes= VB2_MMAP | VB2_DMABUF; q->ops= get_dec_queue_ops(); }else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) { q->io_modes= VB2_MMAP | VB2_USERPTR | VB2_DMABUF; q->ops= get_enc_queue_ops(); }else { ret= -ENOENT; gotoerr_queue_init; } q->mem_ops= (struct vb2_mem_ops *)&vb2_dma_contig_memops; ret= vb2_queue_init(q); if(ret) { mfc_err("Failedto initialize videobuf2 queue(capture)\n"); gotoerr_queue_init; } /*Init videobuf2 queue for OUTPUT */ q= &ctx->vq_src; q->type= V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; q->io_modes= VB2_MMAP; q->drv_priv= &ctx->fh; if(s5p_mfc_get_node_type(file) == MFCNODE_DECODER) { q->io_modes= VB2_MMAP | VB2_DMABUF; q->ops= get_dec_queue_ops(); }else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) { q->io_modes= VB2_MMAP | VB2_USERPTR | VB2_DMABUF; q->ops= get_enc_queue_ops(); }else { ret= -ENOENT; gotoerr_queue_init; } q->mem_ops= (struct vb2_mem_ops *)&vb2_dma_contig_memops; ret= vb2_queue_init(q); if(ret) { mfc_err("Failedto initialize videobuf2 queue(output)\n"); gotoerr_queue_init; } init_waitqueue_head(&ctx->queue); mfc_debug_leave(); returnret; /*Deinit when failure occured */ err_queue_init: err_init_hw: s5p_mfc_release_firmware(dev); err_alloc_fw: dev->ctx[ctx->num]= NULL; del_timer_sync(&dev->watchdog_timer); s5p_mfc_clock_off(); err_pwr_enable: if(dev->num_inst == 1) { if(s5p_mfc_power_off() < 0) mfc_err("poweroff failed\n"); s5p_mfc_release_firmware(dev); } err_ctrls_setup: s5p_mfc_dec_ctrls_delete(ctx); err_bad_node: err_no_ctx: v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); kfree(ctx); err_alloc: dev->num_inst--; mfc_debug_leave(); returnret; }
展开第83行函数ret = s5p_mfc_alloc_and_load_firmware(dev);:
int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) { struct firmware *fw_blob; size_t bank2_base_phys; void *b_base; int err; /* Firmare has to be present as a separate file or compiled * into kernel. */ mfc_debug_enter(); err = request_firmware((const struct firmware **)&fw_blob, "s5p-mfc.fw", dev->v4l2_dev.dev); if (err != 0) { mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n"); return -EINVAL; } dev->fw_size = ALIGN(fw_blob->size, FIRMWARE_ALIGN); if (s5p_mfc_bitproc_buf) { mfc_err("Attempting to allocate firmware when it seems that it is already loaded\n"); release_firmware(fw_blob); return -ENOMEM; } s5p_mfc_bitproc_buf = vb2_dma_contig_memops.alloc( dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], dev->fw_size); if (IS_ERR(s5p_mfc_bitproc_buf)) { s5p_mfc_bitproc_buf = NULL; mfc_err("Allocating bitprocessor buffer failed\n"); release_firmware(fw_blob); return -ENOMEM; }
只看关键部分,分析一下request_firmware((const struct firmware**)&fw_blob, "s5p-mfc.fw", dev->v4l2_dev.dev); 从函数名看,不难联想其功能:加载固件。 其实就是把固件中的数据搬到fw_blob 的一个成员指向的内存中,具体怎么搬,看后面分析。 说到这里,我们有必要再看一下struct firmware 这个结构的定义
struct firmware {
size_t size;
const u8*data;
struct page**pages;
};
该说说固件加载的过程了,request_firmware执行过程中,会为实例化的struct firmware结构分配内存,并且函数将在 /sys/class/firmware 下创建一个以设备名为目录名的新目录,其中包含3 个属性:
loading :这个属性应当被加载固件的用户空间进程设置为 1。当加载完毕, 它将被设为 0。被设为 -1 时,将中止固件加载。
data :一个用来接收固件数据的二进制属性。在设置 loading 为1后, 用户空间进程将固件写入这个属性。
device :一个链接到 /sys/devices 下相关入口项的符号链接。
一旦创建了 sysfs 入口项, 内核将为设备产生一个热插拔事件,并传递包括变量FIRMWARE的环境变量给处理热插拔的用户空间程序。FIRMWARE被设置为提供给 request_firmware 的固件文件名。
用户空间程序定位固件文件, 并将其拷贝到内核提供的二进制属性;若无法定位文件, 用户空间程序设置 loading 属性为 -1。
若固件请求在 10 秒内没有被服务, 内核就放弃并返回一个失败状态给驱动。超时周期可通过 sysfs 属性 /sys/class/firmware/timeout 属性改变。
说了这么多,其实还落了个重点。其实在执行request_firmware时还有这样一个过程:判断固件是否被编译进内核,如果在内核编译过程就加入了固件,上面说的那个过程就白扯了---用不到。看下面代码:
fw_priv =_request_firmware_prepare(firmware_p, name, device, true,false);
if (IS_ERR_OR_NULL(fw_priv))
returnPTR_RET(fw_priv);
这是request_firmware函数开头的一小段代码,_request_firmware_prepare 函数执行过程,判断固件若被编译入内核,则从内核全局的固件地址(指针)处往目标地址进行拷贝,也就是拷贝到structfirmware data成员指向的内存处(最终完成立了固件的加载),然后_request_firmware_prepare返回空指针,进而request_firmware函数也返回了。此过程代码实现如下:
static struct firmware_priv * _request_firmware_prepare(const struct firmware **firmware_p, const char *name, struct device *device, bool uevent, bool nowait) { struct firmware *firmware; struct firmware_priv *fw_priv; if (!firmware_p) return ERR_PTR(-EINVAL); *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); if (!firmware) { dev_err(device, "%s: kmalloc(struct firmware) failed\n", __func__); return ERR_PTR(-ENOMEM); } if (fw_get_builtin_firmware(firmware, name)) { dev_dbg(device, "firmware: using built-in firmware %s\n", name); return NULL; } 。。。。。 }
上面扯了一堆固件加载过程的东西,其实不止是MFC,几乎所有的固件加载都是遵循上面的过程。
再来点实用的东西。
我们该从哪里获得固件呢?
1、找三星技术支持。
2、找第三方开发板售后。
3、从网上Down。
前两条就当是废话,三星不会搭理我们这些小用户。开发板售后?他们只会说:自己找一下,光盘里呢!光盘里是有,可我想要高版本的哪去搞?
这个也不难,google一下流行的发行版的linux内核分支,固件嘛好在是应用于全平台的。立马就有了。
下面附上我拿到的固件:
Samsung MFC 固件下载共包含以下文件:
* MFCv5 firmware (s5p-mfc.fw) * MFCv6 firmware, version 1 (s5p-mfc-v6.fw) * MFCv6 firmware, version 2 (s5p-mfc-v6-v2.fw) * MFCv7 firmware (s5p-mfc-v7.fw) * MFCv8 firmware (s5p-mfc-v8.fw)
在我的内核包里,用的是s5p-mfc.fw,也就是MFCv5。
另外还找到了一些关于MFC固件版本与处理器对应关系的信息:
s5p-mfc-v7.fw: Used in exynos 5420
s5p-mfc-v8.fw: Used in exynos 5800
本文的内容就这么多了,下篇文章希望能够接上篇,分析Live555服务器流程。Live555用的时间短,文字码出来,总感觉质量不行。好事多磨吧。