Chinaunix首页 | 论坛 | 博客
  • 博客访问: 766911
  • 博文数量: 370
  • 博客积分: 2334
  • 博客等级: 大尉
  • 技术积分: 3222
  • 用 户 组: 普通用户
  • 注册时间: 2012-05-06 16:56
文章分类

全部博文(370)

文章存档

2013年(2)

2012年(368)

分类: LINUX

2012-05-31 16:25:05

硬件越来越复杂,硬件的许多功能使用了程序实现,与直接硬件实现相比,固件拥有处理复杂事物的灵活性和便于升级、维护等优点。固件 (firmware)就是这样的一段在设备硬件自身中执行的程序,通过固件标准驱动程序才能实现特定机器的操作,如:光驱、刻录机等都有内部的固件。

固件一般存放在设备上的flash存储器中,但出于成本和灵活性考虑,许多设备都将固件的映像(image)以文件的形式存放在硬盘中,设备驱动程序初始化时再装载到设备内部的存储器中。这样,方便了固件的升级,并省略了设备的flash存储器。

本章分析了驱动程序加载固件映像文件的过程。

目录
[]
固件函数接口

Linux 内核对设备固件的装载和清除提供了支持接口,可将固件映像文件加载到设备指定的存储地址,固件映像文件的内容由设备自身来解析,Linux内核只将映像文 件当件未知的二进制文件。Linux内核用结构firmware描述固件映像文件的内容,该结构列出如下(在include/linux /firmware.h中):

struct firmware { size_t size; const u8 *data; };


固件函数接口原型说明

固件函数接口原型说明如下:

  • 函数request_firmware

函 数request_firmware向用户空间请求提供一个名为name固件映像文件并等待完成。参数device为固件装载的设备。文件内容存入 request_firmware 返回,如果固件请求成功,返回0。该函数从用户空间得到的数据未做任何检查,用户在编写驱动程序时,应对固件映像做数据安全检查,检查方向由设备固件提供 商确定,通常有检查标识符、校验和等方法。

int request_firmware(const struct firmware **firmware_p, const char *name, struct device *device)

  • 函数release_firmware

函数release_firmware在完成固件装载后,释放所申请的内存块fw。

void release_firmware(struct firmware *fw);

  • 函数request_firmware_nowait

函 数request_firmware_nowait是函数request_firmware的异步请求版本,用于不能睡眠的内核线程中调用。参数 module为请求固件的模块;参数uevent为非0时,表示发送uevent事件用于自动拷贝固件映像,否则,必须人工拷贝映像;参数name为固件 映像文件的名字;参数device为装载固件的设备;参数cont为固件请求完成时调用的函数;参数context为函数cont的参数。该函数的原型列 出如下:

int request_firmware_nowait( struct module *module, int uevent, const char *name, struct device *device, void *context, void (*cont)(const struct firmware *fw, void *context))


固件接口函数的使用方法

当驱动程序需要使用固件驱动时,在驱动程序的初始化化过程中需要加下如下的代码:

if(request_firmware(&fw_entry, $FIRMWARE, device) == 0) /*从用户空间请求映像数据*/ /*将固件映像拷贝到硬件的存储器,拷贝函数由用户编写*/ copy_fw_to_device(fw_entry->data, fw_entry->size); release(fw_entry);


用户还需要在用户空间提供脚本通过文件系统sysfs中的文件data将固件映像文件读入到内核的缓冲区中。脚本样例列出如下:

#变量$DEVPATH(固件设备的路径)和$FIRMWARE(固件映像名)应已在环境变量中提供   HOTPLUG_FW_DIR=/usr/lib/hotplug/firmware/ #固件映像文件所在目录   echo 1 > /sys/$DEVPATH/loading cat $HOTPLUG_FW_DIR/$FIRMWARE > /sysfs/$DEVPATH/data echo 0 > /sys/$DEVPATH/loading


固件请求函数request_firmware

函数request_firmware请求从用户空间拷贝固件映像文件到内核缓冲区。该函数的工作流程列出如下:

(1) 在文件系统sysfs中创建文件/sys/class/firmware/xxx/loading和data,"xxx"表示固件的名字,给文件 loading和data附加读写函数,设置文件属性,文件loading表示开/关固件映像文件装载功能;文件data的写操作将映像文件的数据写入内 核缓冲区,读操作从内核缓冲区读取数据。

(2)将添加固件的uevent事件(即"add")通过内核对象模型发送到用户空间。

(3)用户空间管理uevent事件的后台进程udevd接收到事件后,查找udev规则文件,运行规则所定义的动作,与固件相关的规则列出如下:

^-^$ /etc/udev/rules.d/50-udev-default.rules …… # firmware class requests SUBSYSTEM=="firmware", ACTION=="add", RUN+="firmware.sh" ……

从上述规则可以看出,固件添加事件将引起运行脚本firmware.sh。

(4)脚本firmware.sh打开"装载"功能,同命令"cat 映像文件 > /sys/class/firmware/xxx/data"将映像文件数据写入到内核的缓冲区。

(5)映像数据拷贝完成后,函数request_firmware从文件系统/sysfs注销固件设备对应的目录"xxx"。如果请求成功,函数返回0。

(6)用户就将内核缓冲区的固件映像数据拷贝到固件的内存中。然后,调用函数release_firmware(fw_entry)释放给固件映像分配的缓冲区。

函 数request_firmware的调用层次图如图3所示。它先设置uevent事件为1,然后调用设备驱动程序模型:函数 device_register在文件系统sysfs中创建目录"xxx",函数kobject_uevent发送事件,函数 device_unregister在装载完固件映像数据后清除目录"xxx"。


图3 函数request_firmware的调用层次图

函数request_firmware列出如下(在drivers/base/firmware_class.c中):

int request_firmware(const struct firmware **firmware_p, const char *name, struct device *device) { int uevent = 1; return _request_firmware(firmware_p, name, device, uevent); }   static int _request_firmware(const struct firmware **firmware_p, const char *name, struct device *device, int uevent) { struct device *f_dev; struct firmware_priv *fw_priv; struct firmware *firmware; struct builtin_fw *builtin; int retval;   if (!firmware_p) return -EINVAL;   *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); …… //省略出错保护   /*如果固件映像在内部__start_builtin_fw指向的地址,拷贝数据到缓冲区*/ for (builtin = __start_builtin_fw; builtin != __end_builtin_fw; builtin++) { if (strcmp(name, builtin->name)) continue; dev_info(device, "firmware: using built-in firmware %s\n", name); /*打印信息*/ firmware->size = builtin->size; firmware->data = builtin->data; return 0; } ……//省略打印信息 /*在文件系统sysfs建立xxx目录及文件*/ retval = fw_setup_device(firmware, &f_dev, name, device, uevent); if (retval) goto error_kfree_fw;   fw_priv = dev_get_drvdata(f_dev);   if (uevent) { if (loading_timeout > 0) { /*加载定时器*/ fw_priv->timeout.expires = jiffies + loading_timeout * HZ; add_timer(&fw_priv->timeout); }   kobject_uevent(&f_dev->kobj, KOBJ_ADD); /*发送事件KOBJ_ADD*/ wait_for_completion(&fw_priv->completion); set_bit(FW_STATUS_DONE, &fw_priv->status); del_timer_sync(&fw_priv->timeout); } else wait_for_completion(&fw_priv->completion); /*等待完成固件映像数据的装载*/   mutex_lock(&fw_lock); /*如果装载出错,释放缓冲区*/ if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status)) { retval = -ENOENT; release_firmware(fw_priv->fw); *firmware_p = NULL; } fw_priv->fw = NULL; mutex_unlock(&fw_lock); device_unregister(f_dev); /*在文件系统sysfs注销xxx目录*/ goto out;   error_kfree_fw: kfree(firmware); *firmware_p = NULL; out: return retval; }

函数fw_setup_device在文件系统sysfs中创建固件设备的目录和文件,其列出如下:

static int fw_setup_device(struct firmware *fw, struct device **dev_p, const char *fw_name, struct device *device, int uevent) { struct device *f_dev; struct firmware_priv *fw_priv; int retval;   *dev_p = NULL; retval = fw_register_device(&f_dev, fw_name, device); if (retval) goto out;   …… fw_priv = dev_get_drvdata(f_dev); /*从设备结构中得到私有数据结构*/   fw_priv->fw = fw; retval = sysfs_create_bin_file(&f_dev->kobj, &fw_priv->attr_data); /*在sysfs中创建可执行文件*/ …… //省略出错保护   retval = device_create_file(f_dev, &dev_attr_loading); /*在sysfs中创建一般文件*/ …… //省略出错保护   if (uevent) f_dev->uevent_suppress = 0; *dev_p = f_dev; goto out;   error_unreg: device_unregister(f_dev); out: return retval; }

函数fw_register_device注册设备,在文件系统sysfs中创建固件设备对应的设备类,存放固件驱动程序私有数据。其列出如下:

static int fw_register_device(struct device **dev_p, const char *fw_name, struct device *device) { int retval; struct firmware_priv *fw_priv = kzalloc(sizeof(*fw_priv), GFP_KERNEL); struct device *f_dev = kzalloc(sizeof(*f_dev), GFP_KERNEL);   *dev_p = NULL;   …… //省略出错保护 init_completion(&fw_priv->completion); /*初始化completion机制的等待队列*/ fw_priv->attr_data = firmware_attr_data_tmpl; /*设置文件的属性结构*/ strlcpy(fw_priv->fw_id, fw_name, FIRMWARE_NAME_MAX);   fw_priv->timeout.function = firmware_class_timeout; /*超时装载退出函数*/ fw_priv->timeout.data = (u_long) fw_priv; init_timer(&fw_priv->timeout); /*初始化定时器*/   fw_setup_device_id(f_dev, device); /*拷贝device ->bus_id到f_dev中*/ f_dev->parent = device; f_dev->class = &firmware_class; /*设备类实例*/ dev_set_drvdata(f_dev, fw_priv); /*存放设备驱动的私有数据:f_dev ->driver_data = fw_priv*/ f_dev->uevent_suppress = 1; retval = device_register(f_dev); if (retval) { dev_err(device, "%s: device_register failed\n", __func__); goto error_kfree; } *dev_p = f_dev; return 0; …… //省略了出错保护 }   /*文件属性结构实例,设置文件系统sysfs中data文件的模式和读/写函数*/ static struct bin_attribute firmware_attr_data_tmpl = { .attr = {.name = "data", .mode = 0644}, .size = 0, .read = firmware_data_read, /*从内核缓冲区读出数据*/ .write = firmware_data_write, /*用于将固件映像文件的数据写入到内核缓冲区*/ };   /*设备类结构实例,含有发送uevent事件函数和释放设备的函数*/ static struct class firmware_class = { .name = "firmware", /*设备类的名字*/ .dev_uevent = firmware_uevent, /*设备发送uevent事件的函数*/ .dev_release = fw_dev_release, /*释放设备的函数*/ };
阅读(2571) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~