轉自:http://blog.csdn.net/zhenwenxian/article/details/4677604html
淺析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這個'內存物理磁盤'系統中刪除掉!
node
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);吧
app
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)
socket
{
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 */
this
else
write(loading_fd, "-1", 2); /* abort transfer */
return ret;
}