浅析firmware完整生存和使用流程

2822阅读 0评论2012-02-01 土豆和地瓜
分类:

浅析firmware完整生存和使用流程

1. http://blog.chinaunix.net/u1/38994/showart_1288259.html

request_firmware
=>_request_firmware
=>fw_setup_device
=>fw_register_device
=>
static int fw_register_device(struct device **dev_p, const char *fw_name,
             struct device *device)
{
    ...
    fw_priv->attr_data = firmware_attr_data_tmpl;//sysfs中本firmware传输,使用的bin类型文件定义.
    ...
    f_dev->uevent_suppress = 1;//该设备在device_register中,过滤掉uevent事件,不发布到netlink上[luther.gliethttp]
    retval = device_register(f_dev);
    ...
}
static struct bin_attribute firmware_attr_data_tmpl = {
    .attr = {.name = "data", .mode = 0644},
    .size = 0,//现在文件大小,因为还没有读入任何数据,所以这里大小为0
    .read = firmware_data_read,//调用的读方法
    .write = firmware_data_write,//调用的写方法
};
int device_register(struct device *dev)
{
    device_initialize(dev);
    return device_add(dev);
}
void device_initialize(struct device *dev)
{
    dev->kobj.kset = devices_kset;//归属devices_kset来管理
    kobject_init(&dev->kobj, &device_ktype);
    klist_init(&dev->klist_children, klist_children_get,
         klist_children_put);
    INIT_LIST_HEAD(&dev->dma_pools);
    INIT_LIST_HEAD(&dev->node);
    init_MUTEX(&dev->sem);
    spin_lock_init(&dev->devres_lock);
    INIT_LIST_HEAD(&dev->devres_head);
    device_init_wakeup(dev, 0);
    set_dev_node(dev, -1);
}
int device_add(struct device *dev)//向/sys文件系统注册生成dev相关的目录和文件,然后uevent到用户空间[luther.gliethttp]
{
    struct device *parent = NULL;
    struct class_interface *class_intf;
    int error;

    dev = get_device(dev);
    if (!dev || !strlen(dev->bus_id)) {
        error = -EINVAL;
        goto Done;
    }

    pr_debug("device: '%s': %s\n", dev->bus_id, __FUNCTION__);

    parent = get_device(dev->parent);
    setup_parent(dev, parent);//执行之后
dev->kobj.parent将等于/class/firmware

    /* first, register with generic layer. */
    error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id);//创建/class/firmware/mmc1:0001:1/目录
    if (error)
        goto Error;

    /* notify platform of device entry */
    if (platform_notify)
        platform_notify(dev);

    /* notify clients of device entry (new way) */
    if (dev->bus)
        blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                     BUS_NOTIFY_ADD_DEVICE, dev);

    error = device_create_file(dev, &uevent_attr);
    if (error)
        goto attrError;

    if (MAJOR(dev->devt)) {
        error = device_create_file(dev, &devt_attr);
        if (error)
            goto ueventattrError;
    }

    error = device_add_class_symlinks(dev);
    if (error)
        goto SymlinkError;
    error = device_add_attrs(dev);
    if (error)
        goto AttrsError;
    error = dpm_sysfs_add(dev);
    if (error)
        goto PMError;
    device_pm_add(dev);
    error = bus_add_device(dev);
    if (error)
        goto BusError;
    kobject_uevent(&dev->kobj, KOBJ_ADD);//向用户空间发送uevent事件,如果kset和class
    bus_attach_device(dev);
    if (parent)
        klist_add_tail(&dev->knode_parent, &parent->klist_children);

    if (dev->class) {
        down(&dev->class->sem);
        /* tie the class to the device */
        list_add_tail(&dev->node, &dev->class->devices);

        /* notify any interfaces that the device is here */
        list_for_each_entry(class_intf, &dev->class->interfaces, node)
            if (class_intf->add_dev)
                class_intf->add_dev(dev, class_intf);
        up(&dev->class->sem);
    }
 Done:
    put_device(dev);
    return error;
 BusError:
    device_pm_remove(dev);
 PMError:
    if (dev->bus)
        blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                     BUS_NOTIFY_DEL_DEVICE, dev);
    device_remove_attrs(dev);
 AttrsError:
    device_remove_class_symlinks(dev);
 SymlinkError:
    if (MAJOR(dev->devt))
        device_remove_file(dev, &devt_attr);
 ueventattrError:
    device_remove_file(dev, &uevent_attr);
 attrError:
    kobject_uevent(&dev->kobj, KOBJ_REMOVE);
    kobject_del(&dev->kobj);
 Error:
    cleanup_device_parent(dev);
    if (parent)
        put_device(parent);
    goto Done;
}

int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
    return kobject_uevent_env(kobj, action, NULL);//发布uevent事件
}
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
         char *envp_ext[])
{
    struct kobj_uevent_env *env;
    const char *action_string = kobject_actions[action];
    const char *devpath = NULL;
    const char *subsystem;
    struct kobject *top_kobj;
    struct kset *kset;
    struct kset_uevent_ops *uevent_ops;
    u64 seq;
    int i = 0;
    int retval = 0;

    pr_debug("kobject: '%s' (%p): %s\n",
         kobject_name(kobj), kobj, __FUNCTION__);

    /* search the kset we belong to */
    top_kobj = kobj;
    while (!top_kobj->kset && top_kobj->parent)
        top_kobj = top_kobj->parent;

    if (!top_kobj->kset) {
        pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
             "without kset!\n", kobject_name(kobj), kobj,
             __FUNCTION__);
        return -EINVAL;
    }
//对于device_register(),
//kset = devices_kset;
//uevent_ops = device_uevent_ops;[luther.gliethttp]
    kset = top_kobj->kset;
    uevent_ops = kset->uevent_ops;

    /* skip the event, if the filter returns zero. */
    if (uevent_ops && uevent_ops->filter)
        if (!uevent_ops->filter(kset, kobj)) {
//该uevent是否被过滤了,
//对于device_register(),如果dev->uevent_suppress = 1;
//那么表示用户希望过滤掉该uevent,所以在这里直接返回即可.
//对于上面request_firmware中fw_register_device的
//retval = device_register(f_dev);在执行之前,调用了f_dev->uevent_suppress = 1;
//就表示在这里将直接返回,它不希望产生uevent事件到用户空间,它会自己选择时机
//调用kobject_uevent()来让uevent事件发送给用户空间[luther.gliethttp].
            pr_debug("kobject: '%s' (%p): %s: filter function "
                 "caused the event to drop!\n",
                 kobject_name(kobj), kobj, __FUNCTION__);
            return 0;
        }

    /* originating subsystem */
    if (uevent_ops && uevent_ops->name)
        subsystem = uevent_ops->name(kset, kobj);
    else
        subsystem = kobject_name(&kset->kobj);
    if (!subsystem) {
        pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
             "event to drop!\n", kobject_name(kobj), kobj,
             __FUNCTION__);
        return 0;
    }

    /* environment buffer */
    env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
    if (!env)
        return -ENOMEM;

    /* complete object path */
    devpath = kobject_get_path(kobj, GFP_KERNEL);
    if (!devpath) {
        retval = -ENOENT;
        goto exit;
    }

    /* default keys */
    retval = add_uevent_var(env, "ACTION=%s", action_string);
    if (retval)
        goto exit;
    retval = add_uevent_var(env, "DEVPATH=%s", devpath);
    if (retval)
        goto exit;
    retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
    if (retval)
        goto exit;

    /* keys passed in from the caller */
    if (envp_ext) {
        for (i = 0; envp_ext[i]; i++) {
            retval = add_uevent_var(env, envp_ext[i]);
            if (retval)
                goto exit;
        }
    }

    /* let the kset specific function add its stuff */
    if (uevent_ops && uevent_ops->uevent) {
//对于device_register()来说,就是对于f_dev这个kobj来说,
//就是调用dev_uevent添加major和minor等操作[luther.gliethttp]
        retval = uevent_ops->uevent(kset, kobj, env);
        if (retval) {
            pr_debug("kobject: '%s' (%p): %s: uevent() returned "
                 "%d\n", kobject_name(kobj), kobj,
                 __FUNCTION__, retval);
            goto exit;
        }
    }
   ...
}
int __init devices_init(void)
{
    devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
//在sysfs文件系统的根目录下建立deviecs这个kset可视文件,比如/sys/devices
//该kset的uevent处理函数为device_uevent_ops
    if (!devices_kset)
        return -ENOMEM;
    return 0;
}
static struct kset_uevent_ops device_uevent_ops = {
    .filter =    dev_uevent_filter,
    .name =        dev_uevent_name,
    .uevent =    dev_uevent,
};
static int dev_uevent_filter(struct kset *kset, struct kobject *kobj)
{
    struct kobj_type *ktype = get_ktype(kobj);

    if (ktype == &device_ktype) {
//为默认的device_ktype管理
//在device_register=>device_initialize=>kobject_init(&dev->kobj, &device_ktype);
        struct device *dev = to_dev(kobj);
        if (dev->uevent_suppress)//调用device_register()函数的驱动不希望dev的uevent发布到用户空间
            return 0;
        if (dev->bus)
            return 1;
        if (dev->class)
            return 1;
    }
    return 0;
}
static int dev_uevent(struct kset *kset, struct kobject *kobj,
         struct kobj_uevent_env *env)
{
    struct device *dev = to_dev(kobj);
    int retval = 0;

    /* add the major/minor if present */
    if (MAJOR(dev->devt)) {//填充major和minor设备号,以便接收uevent事件的init进程,能够mknod来创建相应的节点文件在/dev目录下[luther.gliethttp].
        add_uevent_var(env, "MAJOR=%u", MAJOR(dev->devt));
        add_uevent_var(env, "MINOR=%u", MINOR(dev->devt));
    }

    if (dev->type && dev->type->name)
        add_uevent_var(env, "DEVTYPE=%s", dev->type->name);

    if (dev->driver)
        add_uevent_var(env, "DRIVER=%s", dev->driver->name);

#ifdef CONFIG_SYSFS_DEPRECATED
    if (dev->class) {
        struct device *parent = dev->parent;

        /* find first bus device in parent chain */
        while (parent && !parent->bus)
            parent = parent->parent;
        if (parent && parent->bus) {
            const char *path;

            path = kobject_get_path(&parent->kobj, GFP_KERNEL);
            if (path) {
                add_uevent_var(env, "PHYSDEVPATH=%s", path);
                kfree(path);
            }

            add_uevent_var(env, "PHYSDEVBUS=%s", parent->bus->name);

            if (parent->driver)
                add_uevent_var(env, "PHYSDEVDRIVER=%s",
                     parent->driver->name);
        }
    } else if (dev->bus) {
        add_uevent_var(env, "PHYSDEVBUS=%s", dev->bus->name);

        if (dev->driver)
            add_uevent_var(env, "PHYSDEVDRIVER=%s",
                 dev->driver->name);
    }
#endif

    /* have the bus specific function add its stuff */
    if (dev->bus && dev->bus->uevent) {
        retval = dev->bus->uevent(dev, env);
        if (retval)
            pr_debug("device: '%s': %s: bus uevent() returned %d\n",
                 dev->bus_id, __FUNCTION__, retval);
    }

    /* have the class specific function add its stuff */
    if (dev->class && dev->class->dev_uevent) {
        retval = dev->class->dev_uevent(dev, env);
        if (retval)
            pr_debug("device: '%s': %s: class uevent() "
                 "returned %d\n", dev->bus_id,
                 __FUNCTION__, retval);
    }

    /* have the device type specific fuction add its stuff */
    if (dev->type && dev->type->uevent) {
        retval = dev->type->uevent(dev, env);
        if (retval)
            pr_debug("device: '%s': %s: dev_type uevent() "
                 "returned %d\n", dev->bus_id,
                 __FUNCTION__, retval);
    }

    return retval;
}

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;

    /* Need to pin this module until class device is destroyed */
    __module_get(THIS_MODULE);

    fw_priv = dev_get_drvdata(f_dev);

    fw_priv->fw = fw;

    retval = sysfs_create_bin_file(&f_dev->kobj, &fw_priv->attr_data);
//在sysfs中创建bin类型文件,即:firmware_attr_data_tmpl
//sysfs_create_bin_file
//直接向sysfs的'内存磁盘'创建'磁盘文件'-firmware_attr_data_tmpl
    if (retval) {
        printk(KERN_ERR "%s: sysfs_create_bin_file failed\n",
         __FUNCTION__);
        goto error_unreg;
    }
//device_create_file=>sysfs_create_file
//直接向sysfs的'内存磁盘'创建'磁盘文件'-dev_attr_loading
    retval = device_create_file(f_dev, &dev_attr_loading);//firmware处理状态提示文件
    if (retval) {
        printk(KERN_ERR "%s: device_create_file failed\n",
         __FUNCTION__);
        goto error_unreg;
    }

    if (uevent)
//如果希望该request_firmware发送uevent到用户空间,那么f_dev->uevent_suppress清0[luther.gliethttp]
        f_dev->uevent_suppress = 0;
    *dev_p = f_dev;
    goto out;

error_unreg:
    device_unregister(f_dev);
out:
    return retval;
}

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;
    int retval;

    if (!firmware_p)
        return -EINVAL;

    *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
    if (!firmware) {
        printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n",
         __FUNCTION__);
        retval = -ENOMEM;
        goto out;
    }

    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);
        }
//因为上面device_register时,dev->uevent_suppress = 1;
//所以device_register将uevent过滤掉了,没有将uevent发送到用户空间,
//后来dev->uevent_suppress = 0;所以所以经过上面乱七八糟的设置之后,现在它认为可以安全
//向用户空间发送uevent了,即:它现在希望通过uevent告知等待该类型netlink的init进程可以安全执行uevent事件对应的动作了,于是现在这里再次调用kobject_uevent将uevent事件发送给用户空间[luther.gliethttp]
        kobject_uevent(&f_dev->kobj, 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);//因为已经完成了导入使命,所以这个提供给用户空间传递数据进入kernel的入口可以删除掉了,这里调用
device_unregister(f_dev);将创建的所有相关目录和文件从sysfs这个'内存物理磁盘'系统中删除掉!
    goto out;

error_kfree_fw:
    kfree(firmware);
    *firmware_p = NULL;
out:
    return retval;
}
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 DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store);

static ssize_t firmware_loading_store(struct device *dev,
                      struct device_attribute *attr,
                      const char *buf, size_t count)
{
    struct firmware_priv *fw_priv = dev_get_drvdata(dev);
    int loading = simple_strtol(buf, NULL, 10);

    switch (loading) {
    case 1://开始下载firmware,
        mutex_lock(&fw_lock);
        if (!fw_priv->fw) {
            mutex_unlock(&fw_lock);
            break;
        }
        vfree(fw_priv->fw->data);
        fw_priv->fw->data = NULL;
        fw_priv->fw->size = 0;
        fw_priv->alloc_size = 0;
        set_bit(FW_STATUS_LOADING, &fw_priv->status);
        mutex_unlock(&fw_lock);
        break;
    case 0://成功完成下载
        if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
            complete(&fw_priv->completion);//唤醒等待着的kernel
            clear_bit(FW_STATUS_LOADING, &fw_priv->status);
            break;
        }
        /* fallthrough */
    default:
        printk(KERN_ERR "%s: unexpected value (%d)\n", __FUNCTION__,
               loading);
        /* fallthrough */
    case -1://init进程写入-1,表示错误,超时时也会调用下面这个函数
        fw_load_abort(fw_priv);
        break;
    }

    return count;
}

==========================================================
让我们看看用户空间的open,write怎么和sysfs文件系统中的'物理文件'对应起来的[luther.gliethttp]
使用sysfs_lookup来向这个内存式的'物理文件系统'查找是否在'物理磁道'上存在dentry对应的文件,
//当lib库中的open系统调用sys_open执行之后,
//sys_open会现查找dentry是否在kernel的内存中存在,如果不存在,那么将
//real_lookup=>truct dentry * dentry = d_alloc(parent, name);
//result = dir->i_op->lookup(dir, dentry, nd);
//对于sysfs文件系统就是sysfs_lookup了.
static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
                struct nameidata *nd)
{
    struct dentry *ret = NULL;
    struct sysfs_dirent *parent_sd = dentry->d_parent->d_fsdata;
    struct sysfs_dirent *sd;
    struct inode *inode;

    mutex_lock(&sysfs_mutex);

    sd = sysfs_find_dirent(parent_sd, dentry->d_name.name);//sysfs的'内存磁盘'中查找,是否已经有了name对应的'内存物理文件',

    /* no such entry */
    if (!sd) {
        ret = ERR_PTR(-ENOENT);
        goto out_unlock;
    }

    /* attach dentry and inode */
    inode = sysfs_get_inode(sd);//该文件确实已经在sysfs的'内存磁盘'被创建了,所以这里引用它,同时如果inode没有在kernel内存创建那么创建inode,同时根据sd的mode,来初始化对应的inode操作方法集[luther.gliethttp].
    if (!inode) {
        ret = ERR_PTR(-ENOMEM);
        goto out_unlock;
    }

    /* instantiate and hash dentry */
    dentry->d_op = &sysfs_dentry_ops;
    dentry->d_fsdata = sysfs_get(sd);//将sd放入dentry的d_fsdata,以供open,read,write时使用.
    d_instantiate(dentry, inode);
    d_rehash(dentry);

 out_unlock:
    mutex_unlock(&sysfs_mutex);
    return ret;
}
struct inode * sysfs_get_inode(struct sysfs_dirent *sd)
{
    struct inode *inode;

    inode = iget_locked(sysfs_sb, sd->s_ino);
    if (inode && (inode->i_state & I_NEW))
        sysfs_init_inode(sd, inode);//订制该inode为sysfs个性式的inode

    return inode;
}
 static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
{
    struct bin_attribute *bin_attr;

    inode->i_blocks = 0;
    inode->i_mapping->a_ops = &sysfs_aops;
    inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;
    inode->i_op = &sysfs_inode_operations;
    inode->i_ino = sd->s_ino;
    lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key);

    if (sd->s_iattr) {
        /* sysfs_dirent has non-default attributes
         * get them for the new inode from persistent copy
         * in sysfs_dirent
         */

        set_inode_attr(inode, sd->s_iattr);
    } else
        set_default_inode_attr(inode, sd->s_mode);


    /* initialize inode according to type */
    switch (sysfs_type(sd)) {
    case SYSFS_DIR:
        inode->i_op = &sysfs_dir_inode_operations;
        inode->i_fop = &sysfs_dir_operations;
        inode->i_nlink = sysfs_count_nlink(sd);
        break;
    case SYSFS_KOBJ_ATTR:
        inode->i_size = PAGE_SIZE;
        inode->i_fop = &sysfs_file_operations;
        break;
    case SYSFS_KOBJ_BIN_ATTR:
        bin_attr = sd->s_bin_attr.bin_attr;
        inode->i_size = bin_attr->size;
        inode->i_fop = &bin_fops;//这就是firmare操作文件函数集了.
        break;
    case SYSFS_KOBJ_LINK:
        inode->i_op = &sysfs_symlink_inode_operations;
        break;
    default:
        BUG();
    }

    unlock_new_inode(inode);
}
所以到这里我们就可以给出一个open调用图谱了:
open=>sys_open=>bin_fops.open
=>将执行bb = kzalloc(sizeof(*bb), GFP_KERNEL);等操作
write=>sys_write=>bin_fops.write=>flush_write
=>
static int
flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count)
{
    struct sysfs_dirent *attr_sd = dentry->d_fsdata;//还记得上面sysfs_lookup的
dentry->d_fsdata = sysfs_get(sd);吧
    struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr;
    struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
    int rc;

    /* need attr_sd for attr, its parent for kobj */
    if (!sysfs_get_active_two(attr_sd))
        return -ENODEV;

    rc = -EIO;
    if (attr->write)
        rc = attr->write(kobj, attr, buffer, offset, count);//调用firmware_attr_data_tmpl的firmware_data_write方法

    sysfs_put_active_two(attr_sd);

    return rc;
}
==========================================================
最后回到driver,看看如何使用request_firmware接口函数
struct firmware {
    size_t size;
    u8 *data;
};
1. request_firmware(&priv->firmware, fw_name, priv->hotplug_device);获得firmware数据
2. priv->firmware->data即为通过vmalloc申请到的物理内存空间首地址,priv->firmware->size为固件大小
3. 将data开头的size大小的数据下发到硬件cpu之后,vmalloc的data就可以释放掉了
4. release_firmware(priv->firmware);释放内存,不然就出现内存泄露了[luther.gliethttp].
==========================================================
另外一个就是firmware固件驱动存储位置,这是由接收处理uevent事件的用户态进程指定的,
我的是init进程来解析,
在用户空间的init进程里
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))这样加载


static int load_firmware(int fw_fd, int loading_fd, int data_fd)
{
    struct stat st;
    long len_to_copy;
    int ret = 0;

    if(fstat(fw_fd, &st) < 0)
        return -1;
    len_to_copy = st.st_size;
//开始传递firmware到kernel
    write(loading_fd, "1", 1);  /* start transfer */

    while (len_to_copy > 0) {
        char buf[PAGE_SIZE];
        ssize_t nr;

        nr = read(fw_fd, buf, sizeof(buf));
        if(!nr)
            break;
        if(nr < 0) {
            ret = -1;
            break;
        }

        len_to_copy -= nr;
        while (nr > 0) {
            ssize_t nw = 0;

            nw = write(data_fd, buf + nw, nr);
            if(nw <= 0) {
                ret = -1;
                goto out;
            }
            nr -= nw;
        }
    }

out:
    if(!ret)

//firmware成功传递到内核

        write(loading_fd, "0", 1);  /* successful end of transfer */
    else
        write(loading_fd, "-1", 2); /* abort transfer */

    return ret;
}

上一篇:网络设备的IP地址结构
下一篇:[原创]WPA_SUPPLICANT源码分析(1):EVENT LOOP的实现