vs2017做网站,建什么样的网站好,中国联通网站备案系统,公司怎么注册营业执照作为一个驱动作者, 你可能发现你面对一个设备必须在它能支持工作前下载固件到它里面. 硬件市场的许多地方的竞争是如此得强烈, 以至于甚至一点用作设备控制固件的 EEPROM 的成本制造商都不愿意花费. 因此固件发布在随硬件一起的一张 CD 上, 并且操作系统负责传送固件到设备自身… 作为一个驱动作者, 你可能发现你面对一个设备必须在它能支持工作前下载固件到它里面. 硬件市场的许多地方的竞争是如此得强烈, 以至于甚至一点用作设备控制固件的 EEPROM 的成本制造商都不愿意花费. 因此固件发布在随硬件一起的一张 CD 上, 并且操作系统负责传送固件到设备自身. 硬件越来越复杂硬件的许多功能使用了程序实现与直接硬件实现相比固件拥有处理复杂事物的灵活性和便于升级、维护等优点。固件firmware就是这样的一段在设备硬件自身中执行的程序通过固件标准驱动程序才能实现特定机器的操作如光驱、刻录机等都有内部的固件。 固件一般存放在设备上的flash存储器中但出于成本和灵活性考虑许多设备都将固件的映像(image)以文件的形式存放在硬盘中设备驱动程序初始化时再装载到设备内部的存储器中。这样方便了固件的升级并省略了设备的flash存储器。 一、驱动和固件的区别 从计算机领域来说驱动和固件从来没有过明确的定义就好像今天我们说内存大部分人用来表示SDRAM但也有人把Android里的“固化的Flash/Storage称为“内存”你不能说这样说就错了因为这确实是一种“内部存储”。 但在Linux Kernel中Driver和Firmware是有明确含义的 1、驱动 Driver是控制被操作系统管理的外部设备Device的代码段。很多时候Driver会被实现为LKM但这不是必要条件。driver通过driver_register()注册到总线(bus_type上代表系统具备了驱动某种设备device的能力。当某个device被注册到同样的总线的时候通常是总线枚举的时候发现了这个设备总线驱动会对driver和device会通过一定的策略进行binding即进行匹配如果Binding成功总线驱动会调用driver的probe()函数把设备的信息例如端口中断号等传递给驱动驱动就可以对真实的物理部件进行初始化并把对该设备的控制接口注册到Linux的其他子系统上例如字符设备v4l2子系统等。这样操作系统的其他部分就可以通过这些通用的接口来访问设备了。 2、固件 Firmware是表示运行在非“控制处理器”指不直接运行操作系统的处理器例如外设中的处理器或者被用于bare metal的主处理器的其中一些核)中的程序。这些程序很多时候使用和操作系统所运行的处理器完全不同的指令集。这些程序以二进制形式存在于Linux内核的源代码树中生成目标系统的时候通常拷贝在/lib/firmware目录下。当driver对device进行初始化的时候通过request_firmware()等接口在一个用户态helper程序的帮助下可以把指定的firmware加载到内存中由驱动传输到指定的设备上。 所以总的来说其实driver和firmware没有什么直接的关系但firmware通常由驱动去加载。我们讨论的那个OS一般不需要理解firmware是什么只是把它当做数据。firmware是什么只有使用这些数据的那个设备才知道。好比你用一个电话电话中有一个软件这个软件你完全不关心如何工作的你换这个软件的时候就可以叫这个软件是“固件”但如果你用了一个智能手机你要细细关系什么是上面的应用程序Android平台插件之类的细节内容你可能就不叫这个东西叫“固件”了。 如何解决固件问题呢你可能想解决固件问题使用这样的一个声明: static char my_firmware[] { 0x34, 0x78, 0xa4, ... }; 但是, 这个方法几乎肯定是一个错误. 将固件编码到一个驱动扩大了驱动的代码, 使固件升级困难, 并且非常可能产生许可问题. 供应商不可能已经发布固件映象在 GPL 之下, 因此和 GPL-许可的代码混合常常是一个错误. 为此, 包含内嵌固件的驱动不可能被接受到主流内核或者被 Linux 发布者包含. 二、内核固件接口 正确的方法是当你需要它时从用户空间获取它. 但是, 请抵制试图从内核空间直接打开包含固件的文件的诱惑; 那是一个易出错的操作, 并且它安放了策略(以一个文件名的形式)到内核. 相反, 正确的方法时使用固件接口, 它就是为此而创建的: [cpp] view plaincopy #include linux/firmware.h int request_firmware(const struct firmware **fw, char *name, struct device *device); 函数request_firmware向用户空间请求提供一个名为name固件映像文件并等待完成。参数device为固件装载的设备。文件内容存入request_firmware 返回如果固件请求成功返回0。该函数从用户空间得到的数据未做任何检查用户在编写驱动程序时应对固件映像做数据安全检查检查方向由设备固件提供商确定通常有检查标识符、校验和等方法。 调用 request_firmware 要求用户空间定位并提供一个固件映象给内核; 我们一会儿看它如何工作的细节. name 应当标识需要的固件; 正常的用法是供应者提供的固件文件名. 某些象 my_firmware.bin 的名子是典型的. 如果固件被成功加载, 返回值是 0(负责常用的错误码被返回), 并且 fw 参数指向一个这些结构: [cpp] view plaincopy struct firmware { size_t size; u8 *data; }; 那个结构包含实际的固件, 它现在可被下载到设备中. 小心这个固件是来自用户空间的未被检查的数据; 你应当在发送它到硬件之前运用任何并且所有的你能够想到的检查来说服你自己它是正确的固件映象. 设备固件常常包含标识串, 校验和, 等等; 在信任数据前全部检查它们. 在你已经发送固件到设备前, 你应当释放 in-kernel 结构, 使用: [cpp] view plaincopy void release_firmware(struct firmware *fw); 因为 request_firmware 请求用户空间来帮忙, 它保证在返回前睡眠. 如果你的驱动当它必须请求固件时不在睡眠的位置, 异步的替代方法可能要使用: [cpp] view plaincopy int request_firmware_nowait(struct module *module, char *name, struct device *device, void *context, void (*cont)(const struct firmware *fw, void *context)); 这里额外的参数是 moudle( 它将一直是 THIS_MODULE), context (一个固件子系统不使用的私有数据指针), 和 cont. 如果都进行顺利, request_firmware_nowait 开始固件加载过程并且返回 0. 在将来某个时间, cont 将用加载的结果被调用. 如果由于某些原因固件加载失败, fw 是 NULL. 三、固件如何工作 固件子系统使用 sysfs 和热插拔机制. 当调用 request_firmware, 一个新目录在 /sys/class/firmware 下使用你的驱动的名子被创建. 那个目录包含 3 个属性: loading 这个属性应当被加载固件的用户空间进程设置为 1. 当加载进程完成, 它应当设为 0. 写一个值 -1 到 loading 会中止固件加载进程. data data 是一个二进制的接收固件数据自身的属性. 在设置 loading 后, 用户空间进程应当写固件到这个属性. device 这个属性是一个符号连接到 /sys/devices 下面的被关联入口项. 一旦创建了 sysfs 入口项, 内核为你的设备产生一个热插拔事件. 传递给热插拔处理者的环境包括一个变量 FIRMWARE, 它被设置为提供给 request_firmware 的名子. 这个处理者应当定位固件文件, 并且拷贝它到内核使用提供的属性. 如果这个文件无法找到, 处理者应当设置 loading 属性为 -1. 如果一个固件请求在 10 秒内没有被服务, 内核就放弃并返回一个失败状态给驱动. 超时周期可通过 sysfs 属性 /sys/class/firmware/timeout 属性改变. 使用 request_firmware 接口允许你随你的驱动发布设备固件. 当正确地集成到热插拔机制, 固件加载子系统允许设备简化工作在盒子之外 显然这是处理问题的最好方法. 但是, 请允许我们提出多一条警告: 设备固件没有制造商的许可不应当发布. 许多制造商会同意在合理的条款下许可它们的固件, 如果客气地请求; 一些其他的可能不何在. 无论如何, 在没有许可时拷贝和发布它们的固件是对版权法的破坏并且招致麻烦. 四、固件接口函数的使用方法 当驱动程序需要使用固件驱动时在驱动程序的初始化化过程中需要加下如下的代码 [cpp] view plaincopy if(request_firmware(fw_entry, $FIRMWARE, device) 0) /*从用户空间请求映像数据*/ /*将固件映像拷贝到硬件的存储器拷贝函数由用户编写*/ copy_fw_to_device(fw_entry-data, fw_entry-size); release(fw_entry); 用户还需要在用户空间提供脚本通过文件系统sysfs中的文件data将固件映像文件读入到内核的缓冲区中。脚本样例列出如下 [cpp] view plaincopy #变量$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请求从用户空间拷贝固件映像文件到内核缓冲区。该函数的工作流程列出如下 a -- 在文件系统sysfs中创建文件/sys/class/firmware/xxx/loading和dataxxx表示固件的名字给文件loading和data附加读写函数设置文件属性文件loading表示开/关固件映像文件装载功能文件data的写操作将映像文件的数据写入内核缓冲区读操作从内核缓冲区读取数据。 b -- 将添加固件的uevent事件即add通过内核对象模型发送到用户空间。 c -- 用户空间管理uevent事件的后台进程udevd接收到事件后查找udev规则文件运行规则所定义的动作与固件相关的规则列出如下 [cpp] view plaincopy $ /etc/udev/rules.d/50-udev-default.rules …… # firmware class requests SUBSYSTEMfirmware, ACTIONadd, RUNfirmware.sh …… 从上述规则可以看出固件添加事件将引起运行脚本firmware.sh。 d -- 脚本firmware.sh打开装载功能同命令cat 映像文件 /sys/class/firmware/xxx/data将映像文件数据写入到内核的缓冲区。 e -- 映像数据拷贝完成后函数request_firmware从文件系统/sysfs注销固件设备对应的目录xxx。如果请求成功函数返回0。 f -- 用户就将内核缓冲区的固件映像数据拷贝到固件的内存中。然后调用函数release_firmware(fw_entry)释放给固件映像分配的缓冲区。 函数request_firmware列出如下在drivers/base/firmware_class.c中 [cpp] view plaincopy 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中创建固件设备的目录和文件其列出如下 [cpp] view plaincopy 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中创建固件设备对应的设备类存放固件驱动程序私有数据。其列出如下 [cpp] view plaincopy 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; …… //省略了出错保护 } [cpp] view plaincopy /*文件属性结构实例设置文件系统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, /*释放设备的函数*/ };