device_create
主要应用场景是在/dev下创建设备节点;主要是dev_t设备号,引起了device_add的设备节点创建操作
struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...)
{va_list vargs;struct device *dev;va_start(vargs, fmt);dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);va_end(vargs);return dev;
}struct device *device_create_vargs(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt,va_list args)
{return device_create_groups_vargs(class, parent, devt, drvdata, NULL,fmt, args);
}static struct device *
device_create_groups_vargs(struct class *class, struct device *parent,dev_t devt, void *drvdata,const struct attribute_group **groups,const char *fmt, va_list args)
{struct device *dev = NULL;int retval = -ENODEV;if (class == NULL || IS_ERR(class))goto error;dev = kzalloc(sizeof(*dev), GFP_KERNEL);if (!dev) {retval = -ENOMEM;goto error;}device_initialize(dev);dev->devt = devt;dev->class = class;dev->parent = parent;dev->groups = groups;dev->release = device_create_release;dev_set_drvdata(dev, drvdata);retval = kobject_set_name_vargs(&dev->kobj, fmt, args);if (retval)goto error;retval = device_add(dev);if (retval)goto error;return dev;error:put_device(dev);return ERR_PTR(retval);
}
实例
动态申请设备号;申请cdev并赋值ops;添加cdev;创建class;创建设备节点
static int __init led_init(void)
{int ret;dev_t devno;struct cdev *cdev;struct dev *dev;/* 注册设备号 */ret = alloc_chrdev_region(&devno, 0, 1, "led");if (ret < 0) return ret;/* 分配、初始化、注册cdev*/cdev = cdev_alloc();if (IS_ERR(cdev)) {ret = PTR_ERR(cdev);goto out_unregister_devno;}cdev_init(&cdev, &led_fops);cdev.owner = THIS_MODULE;ret = cdev_add(&cdev, devno, 1); if (ret) goto out_free_cdev;/* 创建设备类 */led_class = class_create(THIS_MODULE, "led_class");if (IS_ERR(led_class)) {ret = PTR_ERR(led_class);goto out_unregister_cdev;} /* 创建设备节点 */dev = device_create(led_class, NULL, devno, NULL, "led");if (IS_ERR(dev)) {ret = PTR_ERR(dev);goto out_del_class;}return 0;
}
device_register
一般用于匹配设备驱动的注册行为,比如platform_device,i2c_client的创建和注册等;所以就只是device_initialize后直接device_add,来添加并注册设备到系统中
int device_register(struct device *dev)
{device_initialize(dev);return device_add(dev);
}
实例1: platform_device_register
int platform_device_register(struct platform_device *pdev)
{device_initialize(&pdev->dev);arch_setup_pdev_archdata(pdev);return platform_device_add(pdev);
}
实例2:i2c_new_device
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{struct i2c_client *client;int status;client = kzalloc(sizeof *client, GFP_KERNEL);if (!client)return NULL;client->adapter = adap;client->dev.platform_data = info->platform_data;if (info->archdata)client->dev.archdata = *info->archdata;client->flags = info->flags;client->addr = info->addr;client->irq = info->irq;if (!client->irq)client->irq = i2c_dev_irq_from_resources(info->resources,info->num_resources);strlcpy(client->name, info->type, sizeof(client->name));status = i2c_check_addr_validity(client->addr, client->flags);if (status) {dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);goto out_err_silent;}/* Check for address business */status = i2c_check_addr_busy(adap, i2c_encode_flags_to_addr(client));if (status)goto out_err;client->dev.parent = &client->adapter->dev;client->dev.bus = &i2c_bus_type;client->dev.type = &i2c_client_type;client->dev.of_node = info->of_node;client->dev.fwnode = info->fwnode;i2c_dev_set_name(adap, client);if (info->properties) {status = device_add_properties(&client->dev, info->properties);if (status) {dev_err(&adap->dev,"Failed to add properties to client %s: %d\n",client->name, status);goto out_err;}}status = device_register(&client->dev);if (status)goto out_free_props;dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",client->name, dev_name(&client->dev));return client;out_free_props:if (info->properties)device_remove_properties(&client->dev);
out_err:dev_err(&adap->dev,"Failed to register i2c client %s at 0x%02x (%d)\n",client->name, client->addr, status);
out_err_silent:kfree(client);return NULL;
}
device_add
device_add一般也是跟device_initialize一起配合使用的;只是跟device_register的区别在于这样的灵活性高:
1. 根据不同设备驱动模块的不同特性,针对device的parent,class,name等信息初始化2. 在device_initialize和device_add之间,也可以加入不同模块的的不同初始化操作
以下代码和注释是device_add的详细分析
int device_add(struct device *dev) dev = get_device(dev);//增加该设备的引用计数 if (!dev->p) { error = device_private_init(dev);//初始化设备的私有成员p if (error) goto done; } if (dev->init_name) { dev_set_name(dev, "%s", dev->init_name);//初始化设备内部的kobject的名字 dev->init_name = NULL; } if (!dev_name(dev) && dev->bus && dev->bus->dev_name) dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);//使用bus以及设备id来初始化设备内部kobject名字 if (!dev_name(dev)) {//获得设备的名字 error = -EINVAL; goto name_error; } parent = get_device(dev->parent);增加设备父设备并增加父设备引用计数 kobj = get_device_parent(dev, parent); if (kobj) dev->kobj.parent = kobj;//在kobject层实现设备父子关系 if (parent) set_dev_node(dev, dev_to_node(parent)); error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);//将设备加入到kobject模型中,创建sys相关目录 /* notify platform of device entry */ if (platform_notify) platform_notify(dev); error = device_create_file(dev, &uevent_attr);//创建sys目录下设备的uevent属性文件,通过它可以查看设备的uevent事件 if (MAJOR(dev->devt)) { error = device_create_file(dev, &devt_attr);//创建sys目录下设备的设备号属性,即major和minor error = device_create_sys_dev_entry(dev); devtmpfs_create_node(dev); //在/dev下创建设备节点文件} error = device_add_class_symlinks(dev); error = device_add_attrs(dev);//创建sys目录下设备其他属性文件 error = bus_add_device(dev);//将设备加入到管理它的bus总线的设备连表上 error = dpm_sysfs_add(dev);//电源管理相关 device_pm_add(dev); /* Notify clients of device addition. This call must come * after dpm_sysfs_add() and before kobject_uevent(). */ if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_ADD_DEVICE, dev);//通知注册监听该总线的设备,有新设备加入 kobject_uevent(&dev->kobj, KOBJ_ADD);//产生一个内核uevent事件,该事件可以被内核以及应用层捕获,属于linux设备模型中热插拔机制 bus_probe_device(dev);//------------开始寻找设备所对应的驱动------------ if (parent) klist_add_tail(&dev->p->knode_parent, &parent->p->klist_children);
//建立设备与总线间的父子关系 if (dev->class) {//如果设备的属于某个设备类,比如Mass storage,HID等等 mutex_lock(&dev->class->p->mutex); /* tie the class to the device */ klist_add_tail(&dev->knode_class,&dev->class->p->klist_devices);//将设备挂接在其设备类上面 /* notify any interfaces that the device is here */ list_for_each_entry(class_intf,&dev->class->p->interfaces, node) if (class_intf->add_dev) class_intf->add_dev(dev, class_intf);//通知有新设备加入 mutex_unlock(&dev->class->p->mutex);
实例1
设备树节点生成platform_device的时候
在之间加初始化操作init和add之间加bus和platform_data信息
static struct platform_device *of_platform_device_create_pdata(struct device_node *np,const char *bus_id,void *platform_data,struct device *parent)
{struct platform_device *dev;if (!of_device_is_available(np) ||of_node_test_and_set_flag(np, OF_POPULATED))return NULL;dev = of_device_alloc(np, bus_id, parent);if (!dev)goto err_clear_flag;dev->dev.bus = &platform_bus_type;dev->dev.platform_data = platform_data;of_msi_configure(&dev->dev, dev->dev.of_node);if (of_device_add(dev) != 0) {platform_device_put(dev);goto err_clear_flag;}return dev;err_clear_flag:of_node_clear_flag(np, OF_POPULATED);return NULL;
}
实例2
先mmc_alloc_host申请host结构,并device_initialize初始化host结构的device成员;后面的mmc_add_host调用device_add,将device添加到系统
static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
{struct mmc_omap_slot *slot = NULL;struct mmc_host *mmc;int r;mmc = mmc_alloc_host(sizeof(struct mmc_omap_slot), host->dev);if (mmc == NULL)return -ENOMEM;slot = mmc_priv(mmc);slot->host = host;slot->mmc = mmc;slot->id = id;slot->power_mode = MMC_POWER_UNDEFINED;slot->pdata = &host->pdata->slots[id];host->slots[id] = slot;mmc->caps = 0;if (host->pdata->slots[id].wires >= 4)mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_ERASE;mmc->ops = &mmc_omap_ops;mmc->f_min = 400000;if (mmc_omap2())mmc->f_max = 48000000;elsemmc->f_max = 24000000;if (host->pdata->max_freq)mmc->f_max = min(host->pdata->max_freq, mmc->f_max);mmc->ocr_avail = slot->pdata->ocr_mask;/* Use scatterlist DMA to reduce per-transfer costs.* NOTE max_seg_size assumption that small blocks aren't* normally used (except e.g. for reading SD registers).*/mmc->max_segs = 32;mmc->max_blk_size = 2048; /* BLEN is 11 bits (+1) */mmc->max_blk_count = 2048; /* NBLK is 11 bits (+1) */mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;mmc->max_seg_size = mmc->max_req_size;if (slot->pdata->get_cover_state != NULL) {setup_timer(&slot->cover_timer, mmc_omap_cover_timer,(unsigned long)slot);tasklet_init(&slot->cover_tasklet, mmc_omap_cover_handler,(unsigned long)slot);}r = mmc_add_host(mmc);if (r < 0)goto err_remove_host;if (slot->pdata->name != NULL) {r = device_create_file(&mmc->class_dev,&dev_attr_slot_name);if (r < 0)goto err_remove_host;}if (slot->pdata->get_cover_state != NULL) {r = device_create_file(&mmc->class_dev,&dev_attr_cover_switch);if (r < 0)goto err_remove_slot_name;tasklet_schedule(&slot->cover_tasklet);}return 0;err_remove_slot_name:if (slot->pdata->name != NULL)device_remove_file(&mmc->class_dev, &dev_attr_slot_name);
err_remove_host:mmc_remove_host(mmc);mmc_free_host(mmc);return r;
}
mmc_alloc_host会在device_initialize后先一些初始化(device_enable_async_suspend);以及判断操作来决定是否device_add
struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{int err;struct mmc_host *host;host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);if (!host)return NULL;/* scanning will be enabled when we're ready */host->rescan_disable = 1;err = ida_simple_get(&mmc_host_ida, 0, 0, GFP_KERNEL);if (err < 0) {kfree(host);return NULL;}host->index = err;dev_set_name(&host->class_dev, "mmc%d", host->index);host->parent = dev;host->class_dev.parent = dev;host->class_dev.class = &mmc_host_class;device_initialize(&host->class_dev);device_enable_async_suspend(&host->class_dev);if (mmc_gpio_alloc(host)) {put_device(&host->class_dev);ida_simple_remove(&mmc_host_ida, host->index);kfree(host);return NULL;}spin_lock_init(&host->lock);init_waitqueue_head(&host->wq);INIT_DELAYED_WORK(&host->detect, mmc_rescan);INIT_DELAYED_WORK(&host->sdio_irq_work, sdio_irq_work);setup_timer(&host->retune_timer, mmc_retune_timer, (unsigned long)host);/** By default, hosts do not support SGIO or large requests.* They have to set these according to their abilities.*/host->max_segs = 1;host->max_seg_size = PAGE_SIZE;host->max_req_size = PAGE_SIZE;host->max_blk_size = 512;host->max_blk_count = PAGE_SIZE / 512;return host;
}
mmc_add_host调用device_add将mmc主机设备添加到系统
int mmc_add_host(struct mmc_host *host)
{int err;WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&!host->ops->enable_sdio_irq);err = device_add(&host->class_dev);if (err)return err;led_trigger_register_simple(dev_name(&host->class_dev), &host->led);#ifdef CONFIG_DEBUG_FSmmc_add_host_debugfs(host);
#endifmmc_start_host(host);mmc_register_pm_notifier(host);return 0;
}