Linux设备模型firmware(固件)

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

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

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


固件函数接口

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

<span class="kw4">struct</span> firmware <span class="br0">{</span>
	size_t size<span class="sy0">;</span>
	<span class="kw4">const</span> u8 <span class="sy0">*</span>data<span class="sy0">;</span>
<span class="br0">}</span><span class="sy0">;</span>


固件函数接口原型说明

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

  • 函数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的参数。该函数的原型列出如下:

<span class="kw4">int</span>  request_firmware_nowait<span class="br0">(</span>
	<span class="kw4">struct</span> module <span class="sy0">*</span>module<span class="sy0">,</span> <span class="kw4">int</span> uevent<span class="sy0">,</span>
	<span class="kw4">const</span> <span class="kw4">char</span> <span class="sy0">*</span>name<span class="sy0">,</span> <span class="kw4">struct</span> device <span class="sy0">*</span>device<span class="sy0">,</span> <span class="kw4">void</span> <span class="sy0">*</span>context<span class="sy0">,</span>
	<span class="kw4">void</span> <span class="br0">(</span><span class="sy0">*</span>cont<span class="br0">)</span><span class="br0">(</span><span class="kw4">const</span> <span class="kw4">struct</span> firmware <span class="sy0">*</span>fw<span class="sy0">,</span> <span class="kw4">void</span> <span class="sy0">*</span>context<span class="br0">)</span><span class="br0">)</span>


固件接口函数的使用方法

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

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


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

<span class="co2">#变量$DEVPATH(固件设备的路径)和$FIRMWARE(固件映像名)应已在环境变量中提供</span>
 
HOTPLUG_FW_DIR<span class="sy0">=/</span>usr<span class="sy0">/</span>lib<span class="sy0">/</span>hotplug<span class="sy0">/</span>firmware<span class="sy0">/</span>    <span class="co2">#固件映像文件所在目录</span>
 
echo <span class="nu0">1</span> <span class="sy0">></span> <span class="sy0">/</span>sys<span class="sy0">/</span>$DEVPATH<span class="sy0">/</span>loading
cat $HOTPLUG_FW_DIR<span class="sy0">/</span>$FIRMWARE <span class="sy0">></span> <span class="sy0">/</span>sysfs<span class="sy0">/</span>$DEVPATH<span class="sy0">/</span>data
echo <span class="nu0">0</span> <span class="sy0">></span> <span class="sy0">/</span>sys<span class="sy0">/</span>$DEVPATH<span class="sy0">/</span>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规则文件,运行规则所定义的动作,与固件相关的规则列出如下:

<span class="sy0">^-^</span>$ <span class="sy0">/</span>etc<span class="sy0">/</span>udev<span class="sy0">/</span>rules.<span class="me1">d</span><span class="sy0">/</span><span class="nu0">50</span><span class="sy0">-</span>udev<span class="sy0">-</span><span class="kw1">default</span>.<span class="me1">rules</span>
……
<span class="co2"># firmware class requests</span>
SUBSYSTEM<span class="sy0">==</span><span class="st0">"firmware"</span><span class="sy0">,</span> ACTION<span class="sy0">==</span><span class="st0">"add"</span><span class="sy0">,</span> RUN<span class="sy0">+=</span><span class="st0">"firmware.sh"</span>
……

从上述规则可以看出,固件添加事件将引起运行脚本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中):

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

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

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

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

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

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值