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

原文地址: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 ]   .
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值