转自:http://blog.csdn.net/zhenwenxian/article/details/4677604

浅析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 intflush_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;
}

浅析firmware完整生存和使用流程 【转】的更多相关文章

  1. 完整的SOPC开发流程体验

    课程目标:学习并掌握完整的SOPC开发流程. 开发环境:Quartus15.1 学习内容:1.使用QSYS工具建立能够运行流水灯项目的NIOS II处理器系统 2.在quartus ii中添加NIOS ...

  2. (转载)Kaggle_Titanic生存预测 -- 详细流程吐血梳理

    Kaggle_Titanic生存预测 -- 详细流程吐血梳理 https://blog.csdn.net/Koala_Tree/article/details/78725881 Kaggle中Tita ...

  3. Vue Router 路由守卫:完整的导航解析流程

    完整的导航解析流程 1 导航被触发. 2 在失活的组件里调用离开守卫. 3 调用全局的 beforeEach 守卫. 4 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+). ...

  4. 本页面用来演示如何通过JS SDK,创建完整的QQ登录流程,并调用openapi接口

    QQ登录将用户信息存储在cookie中,命名为__qc__k ,请不要占用 __qc__k : 1) :: 在页面顶部引入JS SDK库: 将“js?”后面的appid参数(示例代码中的:100229 ...

  5. 完整的验证码识别流程基于svm(若是想提升,可优化)

    字符型图片验证码识别完整过程及Python实现 首先很感觉这篇文章的作者,将这篇文章写的这么好.我呢,也是拿来学习,觉得太好,所以忍不住就进行了转载. 因为我个人现在手上也有个验证码识别的项目,只是难 ...

  6. Android 4.4 Kitkat Phone工作流程浅析(七)__来电(MT)响铃流程

    本文来自http://blog.csdn.net/yihongyuelan 转载请务必注明出处 本文代码以MTK平台Android 4.4为分析对象,与Google原生AOSP有些许差异,请读者知悉. ...

  7. 详细讲解:零知识证明 之 ZCash 完整的匿名交易流程

    作者:林冠宏 / 指尖下的幽灵 博客:http://www.cnblogs.com/linguanh/ 掘金:https://juejin.im/user/587f0dfe128fe100570ce2 ...

  8. 一个完整的产品设计流程——家庭安全管家

    不管是产品设计,还是前后端开发,始终都应该做出来才能够有很好的提高锻炼.书看得再多,如果不配合实际练习始终得不到实质性的进展. 接下来的案例是和几位学弟学妹一起做的,契机是参加一个用户体验设计比赛,从 ...

  9. 以太网驱动的流程浅析(二)-Ifconfig的详细代码流程【原创】

    以太网驱动流程浅析(二)-ifconfig的详细代码流程 Author:张昺华 Email:920052390@qq.com Time:2019年3月23日星期六 此文也在我的个人公众号以及<L ...

随机推荐

  1. 1055. The World's Richest (25)

    Forbes magazine publishes every year its list of billionaires based on the annual ranking of the wor ...

  2. childNodes 和children

    childNodes 兼容性不是很好,一般用children 元素.childNodes : 只读 属性 子节点列表集合标准下:包含了空白换行和元素类型的节点,也会包含非法嵌套的子节点非标准下:只包含 ...

  3. 演示save point及自治事务的用处

    1.确认数据库版本 2 举一个例子,说明save point的用处,给出SQL演示. 2.1环境准备 save point的作用是通过在事务中间设置检查点,可以更加精细的控制事务,防止一部分操作错误而 ...

  4. Sql Server按树形结构排序查询表记录

    http://blog.csdn.net/dxnn520/article/details/8089149 ----------------------------------------------- ...

  5. sql语句的各种模糊查询

    一般模糊语句如下: SELECT 字段 FROM 表 WHERE 某字段 Like 条件 其中关于条件,SQL提供了四种匹配模式: 1.%:表示任意0个或多个字符.可匹配任意类型和长度的字符,有些情况 ...

  6. 4s使用iOS 8的一些真實感受

    iPhone永遠離不開史上手機的爭論!你是否也在用呢? 今年iPhone 6/6Plus的發佈和上市可以說是振奮人心,大螢幕的升級.圓潤的外觀改變.全新的iOS 8系統,都是極具吸引力的.作為一名互聯 ...

  7. Wcf 双工通信的应用

    概述 双工(Duplex)模式的消息交换方式体现在消息交换过程中,参与的双方均可以向对方发送消息.基于双工MEP消息交换可以看成是多个基本模式下(比如请求-回复模式和单项模式)消息交换的组合.双工ME ...

  8. Otsu algorithm

    一.介绍 OTSU算法也称最大类间差法,有时也称之为大津算法,被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响,因此在数字图像处理上得到了广泛的应用.它是按图像的灰度特性,将 ...

  9. Centos7 安装 nodejs

    https://nodejs.org/en/download/package-manager/ 设置node v4的源 curl --silent --location https://rpm.nod ...

  10. Go-Agent原理分析及FQ介绍

    作为一个程序员,相信大家是极度依赖google/stackoverflow/github的,可是国内有强大的GFW存在,以至于编程少了很多乐趣. 最近闹GFW狂潮,很多Chrome插件被封,连Shad ...