浅析frmware的加载和init通过netlink处理uevent事件的一般流程

来源:互联网 发布:淘宝咋注册 编辑:程序博客网 时间:2024/06/05 20:11

原文地址:http://www.4ucode.com/Study/Topic/1388056

浅析frmware的加载和init通过netlink处理uevent事件的一般流程


当总线检测代id相macth的设备或者驱动时调用, wlan_probe
= > wlan_probe
= > wlan_add_card
= > sbi_register_dev
= > priv- > hotplug_device = & func- > dev; 这样priv- > hotplug_device就指向了/ sys/ bus/ sdio/ devices下的设备节点描述结构体
接下来下载wlan的firmware固件驱动, 
= > wlan_init_fw
= > request_firmware( & priv- > firmware, fw_name, priv- > hotplug_device) ; 给priv- > hotplug_device设备申请名字为fw_name的firmware
  数据, 让后将结果放到& priv- > firmware中, 
  struct firmware { 
    size_t size; 
    u8 * data; 
  } ; 
  可以看到, 如果应用层的程序成功load了firmware固件文件, 那么firmware. data将指向固件数据, firmware. size为固件大小. 
module_param( fw_name, charp, 0) ; 
MODULE_PARM_DESC( fw_name, "Firmware name" ) ; 这里可以看到fw_name是作为参数可以自由指定的, 如果没有指定, 那么将使用如下默认名:
# define DEFAULT_FW_NAME "mrvl/sd8688.bin" 
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 
request_firmware
= > _request_firmware
= > fw_setup_device
= > fw_register_device//添加临时download firmware的dev设备节点,注册该inode的class为 
= > fw_priv- > attr_data = firmware_attr_data_tmpl; //文件属性操作函数集 
= > strlcpy( fw_priv- > fw_id, fw_name, FIRMWARE_NAME_MAX) ; //拷贝参数就是我们的fw_name即默认的"mrvl/sd8688.bin" 
= > sysfs_create_bin_file( & f_dev- > kobj, & fw_priv- > attr_data) ; //创建DEVPATH/data文件,操作该文件的方法为firmware_attr_data_tmpl 
//这样init程序就可以打开这个DEVPATH/data文件,然后向这个DEVPATH/data文件写入firmware固件bin内容,然后kernel的driver就可以通过 
//firmware->data和firmware->size来读取有uevent处理程序init加载进来的firmware数据了[luther.gliethttp] 
= > f_dev- > class = & firmware_class; 
= > if ( uevent) kobject_uevent( & f_dev- > kobj, KOBJ_ADD) ; 发送uevent消息. 
= > wait_for_completion( & fw_priv- > completion) ; 等待完成


static struct bin_attribute firmware_attr_data_tmpl = { 
    . attr = { . name = "data" , . mode = 0644} , 
    . size = 0, 
    . read = firmware_data_read, 
    . write = firmware_data_write, 
} ; 
= > firmware_data_write
= > fw_realloc_buffer//类似realloc实现,释放原有的,申请新加的 
= > fw_priv- > fw- > data = new_data; 


= > kobject_uevent
= > kobject_uevent_env
/*
devices_init
=>devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
static struct kset_uevent_ops device_uevent_ops = {
    .filter =    dev_uevent_filter,
    .name =        dev_uevent_name,
    .uevent =    dev_uevent,
};
=>dev_uevent
=>dev->class->dev_uevent

void device_initialize(struct device *dev)
{
    dev->kobj.kset = devices_kset;
    ...
}
*/
 
= > uevent_ops = kset- > uevent_ops; //这里uevent_ops就指向device_uevent_ops了 
= > uevent_ops- > uevent( kset, kobj, env) ; 建立环境变量
= > device_uevent_ops
= > dev_uevent
= > dev- > class - > dev_uevent就是firmware_class的dev_uevent, 即:firmware_uevent
= > firmware_uevent
= > 
/*
static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env)
{
    struct firmware_priv *fw_priv = dev_get_drvdata(dev);

    if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->fw_id))即默认的"mrvl/sd8688.bin"
        return -ENOMEM;
    if (add_uevent_var(env, "TIMEOUT=%i", loading_timeout))
        return -ENOMEM;

    return 0;
}
*/
 
= > if ( uevent_sock) { 那么通过netlink将该uevent事件广播出去, 
= > netlink_broadcast( uevent_sock, skb, 0, 1, GFP_KERNEL) ; 
= > 在include/ linux/ autoconf. h中
# define CONFIG_UEVENT_HELPER_PATH "/sbin/hotplug" 
= > 但是我们的root文件系统/ sbin/ 下没有hotplug这个文件, 只有一个adbd程序, 所以这样看来就不能运行了, 所以这里应该将char uevent_helper[UEVENT_HELPER_PATH_LEN] = 清0才对, 让if( uevent_helper[ 0] ) 失败, 进而不继续执行和hotplug的相关操作, 但是现在autoconf. h中
include / linux/ autoconf. h
# define CONFIG_NET 1
# define CONFIG_HOTPLUG 1 
仍然被设置成1, 不知道为什么kernel team还要这样设置[ luther. gliethttp] . 
. 
所以我们的系统使用的是netlink接收事件广播, 我的ubuntu8. 04的/ sbin下也没有找到hotplug这个文件, 可能正如大家所说的, hotplug的诸多
缺陷导致它已经淡出了linux世界, 而天生丽质的netlink已经在linx世界中全面开花[ luther. gliethttp] . 

然后init进程开始处理这个firmware请求, 
init
= > main
= > handle_device_fd调用uevent的NETLINK_KOBJECT_UEVENT的socket处理函数
= > parse_event
= > handle_firmware_event
= > pid = fork( ) ; 子进程执行process_firmware_event
= > process_firmware_event
# define SYSFS_PREFIX "/sys" 
= > asprintf( & root, SYSFS_PREFIX"%s/" , uevent- > path) ; 
//这里的uevent->path是parse_event函数解析时对应的"DEVPATH="节内容,也就是dev设备路径
= > asprintf( & loading, "%sloading" , root) ; //在该路径下创建loading文件
= > asprintf( & data, "%sdata" , root) ; //该路径下的data文件
= > loading_fd = open ( loading, O_WRONLY) ; //创建该loading文件,然后向其中写入"1"表示开始加载,加载成功写入"0",失败写入"-1".
= > data_fd = open ( data, O_WRONLY
# define FIRMWARE_DIR "/system/lib/firmware" 原来路径是/ etc/ firmware, 我的mrvl/ sd8688. bin也放在那里, 
//但是虽然ramdisk虽然经过压缩,可是存储ramdisk.img的总大小才512k,所以不能将有可能不断扩大大小的firmware放到那里,
//于是最近将init进程搜索路径改为"/system/lib/firmware".
= > asprintf( & file , FIRMWARE_DIR"/%s" , uevent- > firmware) ; 
= > fw_fd = open ( file , O_RDONLY) ; //打开通过uevent传递过来的firmware文件,然后拷贝过去
= > load_firmware( fw_fd, loading_fd, data_fd) ) 这样加载
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 
# define module_param_named( name, value, type, perm)              \
    param_check_# # type( name, & ( value) ) ;                  \
    module_param_call( name, param_set_# # type, param_get_# # type, & value, perm) ; \
    __MODULE_PARM_TYPE( name, # type) 

# define module_param( name, type, perm)                 \
    module_param_named( name, name, type, perm) 
//对应param_check_##type检测参数类型函数如下,可以检测如下参数类型[luther.gliehttp]
param_check_bool param_check_int param_check_short param_check_ushort
param_check_byte param_check_invbool param_check_uint param_check_proto_abbrev
param_check_charp param_check_long param_check_ulong param_check_scroll
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 
比如usb通用驱动的使用, 这样所有usb设备都可以通过serial方式进行访问, 因为不是该usb设备专有的usb驱动, 所以速度可能慢一些[ luther. gliethttp] . 
sudo insmod / lib/ modules/ 2. 6. 22- 14- generic/ kernel/ drivers/ usb/ serial/ usbserial. ko vendor= 0x8086 product= 0xd001
在drivers/ usb/ serial/ generic. c驱动中, 
module_param( vendor, ushort, 0) ; 
MODULE_PARM_DESC( vendor, "User specified USB idVendor" ) ; 

module_param( product, ushort, 0) ; 
MODULE_PARM_DESC( product, "User specified USB idProduct" ) ; 

所以通过module_param可以方便的给ko驱动传递参数, 很方便的咚咚, kernel那群人真能整[ luther. gliethttp] .
原创粉丝点击