在
ldd_bus.h 定义如下:
//LDD driver type
struct ldd_driver
{
char *version;
struct module *module;
struct device_driver driver;
struct driver_attribute version_attr;
};
//A device type for things "plugged" into the LDD bus.
struct ldd_device
{
char *name;
struct ldd_driver *driver;
struct device dev;
};
在
sculld.h 中定义如下:
struct sculld_dev
{
void **data;
struct sculld_dev *next; //next listitem
int vmas; //active mappings
int order; //the current allocation order
int qset; //the current array size
size_t size; //32-bit will suffice
struct semaphore sem; //Mutual exclusion
struct cdev cdev;
char devname[SCULLD_DEVNAME_SIZE];
struct ldd_device ldev;
};
struct device :
-
/**
-
* struct device - The basic device structure
-
* @parent: The device's "parent" device, the device to which it is attached.
-
* In most cases, a parent device is some sort of bus or host
-
* controller. If parent is NULL, the device, is a top-level device,
-
* which is not usually what you want.
-
* @p: Holds the private data of the driver core portions of the device.
-
* See the comment of the struct device_private for detail.
-
* @kobj: A top-level, abstract class from which other classes are derived.
-
* @init_name: Initial name of the device.
-
* @type: The type of device.
-
* This identifies the device type and carries type-specific
-
* information.
-
* @mutex: Mutex to synchronize calls to its driver.
-
* @bus: Type of bus device is on.
-
* @driver: Which driver has allocated this
-
* @platform_data: Platform data specific to the device.
-
* Example: For devices on custom boards, as typical of embedded
-
* and SOC based hardware, Linux often uses platform_data to point
-
* to board-specific structures describing devices and how they
-
* are wired. That can include what ports are available, chip
-
* variants, which GPIO pins act in what additional roles, and so
-
* on. This shrinks the "Board Support Packages" (BSPs) and
-
* minimizes board-specific #ifdefs in drivers.
-
* @power: For device power management.
-
* See Documentation/power/devices.txt for details.
-
* @pm_domain: Provide callbacks that are executed during system suspend,
-
* hibernation, system resume and during runtime PM transitions
-
* along with subsystem-level and driver-level callbacks.
-
* @numa_node: NUMA node this device is close to.
-
* @dma_mask: Dma mask (if dma'ble device).
-
* @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all
-
* hardware supports 64-bit addresses for consistent allocations
-
* such descriptors.
-
* @dma_parms: A low level driver may set these to teach IOMMU code about
-
* segment limitations.
-
* @dma_pools: Dma pools (if dma'ble device).
-
* @dma_mem: Internal for coherent mem override.
-
* @archdata: For arch-specific additions.
-
* @of_node: Associated device tree node.
-
* @devt: For creating the sysfs "dev".
-
* @devres_lock: Spinlock to protect the resource of the device.
-
* @devres_head: The resources list of the device.
-
* @knode_class: The node used to add the device to the class list.
-
* @class: The class of the device.
-
* @groups: Optional attribute groups.
-
* @release: Callback to free the device after all references have
-
* gone away. This should be set by the allocator of the
-
* device (i.e. the bus driver that discovered the device).
-
*
-
* At the lowest level, every device in a Linux system is represented by an
-
* instance of struct device. The device structure contains the information
-
* that the device model core needs to model the system. Most subsystems,
-
* however, track additional information about the devices they host. As a
-
* result, it is rare for devices to be represented by bare device structures;
-
* instead, that structure, like kobject structures, is usually embedded within
-
* a higher-level representation of the device.
-
*/
-
struct device {
-
struct device *parent;
-
-
struct device_private *p;
-
-
struct kobject kobj;
-
const char *init_name; /* initial name of the device */
-
const struct device_type *type;
-
-
struct mutex mutex; /* mutex to synchronize calls to
-
* its driver.
-
*/
-
-
struct bus_type *bus; /* type of bus device is on */
-
struct device_driver *driver; /* which driver has allocated this
-
device */
-
void *platform_data; /* Platform specific data, device
-
core doesn't touch it */
-
struct dev_pm_info power;
-
struct dev_pm_domain *pm_domain;
-
-
#ifdef CONFIG_NUMA
-
int numa_node; /* NUMA node this device is close to */
-
#endif
-
u64 *dma_mask; /* dma mask (if dma'able device) */
-
u64 coherent_dma_mask;/* Like dma_mask, but for
-
alloc_coherent mappings as
-
not all hardware supports
-
64 bit addresses for consistent
-
allocations such descriptors. */
-
-
struct device_dma_parameters *dma_parms;
-
-
struct list_head dma_pools; /* dma pools (if dma'ble) */
-
-
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
-
override */
-
/* arch specific additions */
-
struct dev_archdata archdata;
-
-
struct device_node *of_node; /* associated device tree node */
-
-
dev_t devt; /* dev_t, creates the sysfs "dev" */
-
-
spinlock_t devres_lock;
-
struct list_head devres_head;
-
-
struct klist_node knode_class;
-
struct class *class;
-
const struct attribute_group **groups; /* optional groups */
-
-
void (*release)(struct device *dev);
-
};
struct device_private :
-
/**
-
* struct device_private - structure to hold the private to the driver core portions of the device structure.
-
*
-
* @klist_children - klist containing all children of this device
-
* @knode_parent - node in sibling list
-
* @knode_driver - node in driver list
-
* @knode_bus - node in bus list
-
* @driver_data - private pointer for driver specific info. Will turn into a
-
* list soon.
-
* @device - pointer back to the struct class that this structure is
-
* associated with.
-
*
-
* Nothing outside of the driver core should ever touch these fields.
-
*/
-
struct device_private {
-
struct klist klist_children;
-
struct klist_node knode_parent;
-
struct klist_node knode_driver;
-
struct klist_node knode_bus;
-
void *driver_data;
-
struct device *device;
-
};
struct bus_type :
-
/**
-
* struct bus_type - The bus type of the device
-
*
-
* @name: The name of the bus.
-
* @bus_attrs: Default attributes of the bus.
-
* @dev_attrs: Default attributes of the devices on the bus.
-
* @drv_attrs: Default attributes of the device drivers on the bus.
-
* @match: Called, perhaps multiple times, whenever a new device or driver
-
* is added for this bus. It should return a nonzero value if the
-
* given device can be handled by the given driver.
-
* @uevent: Called when a device is added, removed, or a few other things
-
* that generate uevents to add the environment variables.
-
* @probe: Called when a new device or driver add to this bus, and callback
-
* the specific driver's probe to initial the matched device.
-
* @remove: Called when a device removed from this bus.
-
* @shutdown: Called at shut-down time to quiesce the device.
-
* @suspend: Called when a device on this bus wants to go to sleep mode.
-
* @resume: Called to bring a device on this bus out of sleep mode.
-
* @pm: Power management operations of this bus, callback the specific
-
* device driver's pm-ops.
-
* @iommu_ops: IOMMU specific operations for this bus, used to attach IOMMU
-
* driver implementations to a bus and allow the driver to do
-
* bus-specific setup
-
* @p: The private data of the driver core, only the driver core can
-
* touch this.
-
*
-
* A bus is a channel between the processor and one or more devices. For the
-
* purposes of the device model, all devices are connected via a bus, even if
-
* it is an internal, virtual, "platform" bus. Buses can plug into each other.
-
* A USB controller is usually a PCI device, for example. The device model
-
* represents the actual connections between buses and the devices they control.
-
* A bus is represented by the bus_type structure. It contains the name, the
-
* default attributes, the bus' methods, PM operations, and the driver core's
-
* private data.
-
*/
-
struct bus_type {
-
const char *name;
-
struct bus_attribute *bus_attrs;
-
struct device_attribute *dev_attrs;
-
struct driver_attribute *drv_attrs;
-
-
int (*match)(struct device *dev, struct device_driver *drv);
-
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
-
int (*probe)(struct device *dev);
-
int (*remove)(struct device *dev);
-
void (*shutdown)(struct device *dev);
-
-
int (*suspend)(struct device *dev, pm_message_t state);
-
int (*resume)(struct device *dev);
-
-
const struct dev_pm_ops *pm;
-
-
struct iommu_ops *iommu_ops;
-
-
struct subsys_private *p;
-
};
struct module :
-
struct module
-
{
-
enum module_state state;
-
-
/* Member of list of modules */
-
struct list_head list;
-
-
/* Unique handle for this module */
-
char name[MODULE_NAME_LEN];
-
-
/* Sysfs stuff. */
-
struct module_kobject mkobj;
-
struct module_attribute *modinfo_attrs;
-
const char *version;
-
const char *srcversion;
-
struct kobject *holders_dir;
-
-
/* Exported symbols */
-
const struct kernel_symbol *syms;
-
const unsigned long *crcs;
-
unsigned int num_syms;
-
-
/* Kernel parameters. */
-
struct kernel_param *kp;
-
unsigned int num_kp;
-
-
/* GPL-only exported symbols. */
-
unsigned int num_gpl_syms;
-
const struct kernel_symbol *gpl_syms;
-
const unsigned long *gpl_crcs;
-
-
#ifdef CONFIG_UNUSED_SYMBOLS
-
/* unused exported symbols. */
-
const struct kernel_symbol *unused_syms;
-
const unsigned long *unused_crcs;
-
unsigned int num_unused_syms;
-
-
/* GPL-only, unused exported symbols. */
-
unsigned int num_unused_gpl_syms;
-
const struct kernel_symbol *unused_gpl_syms;
-
const unsigned long *unused_gpl_crcs;
-
#endif
-
-
/* symbols that will be GPL-only in the near future. */
-
const struct kernel_symbol *gpl_future_syms;
-
const unsigned long *gpl_future_crcs;
-
unsigned int num_gpl_future_syms;
-
-
/* Exception table */
-
unsigned int num_exentries;
-
struct exception_table_entry *extable;
-
-
/* Startup function. */
-
int (*init)(void);
-
-
/* If this is non-NULL, vfree after init() returns */
-
void *module_init;
-
-
/* Here is the actual code + data, vfree'd on unload. */
-
void *module_core;
-
-
/* Here are the sizes of the init and core sections */
-
unsigned int init_size, core_size;
-
-
/* The size of the executable code in each section. */
-
unsigned int init_text_size, core_text_size;
-
-
/* Size of RO sections of the module (text+rodata) */
-
unsigned int init_ro_size, core_ro_size;
-
-
/* Arch-specific module values */
-
struct mod_arch_specific arch;
-
-
unsigned int taints; /* same bits as kernel:tainted */
-
-
#ifdef CONFIG_GENERIC_BUG
-
/* Support for BUG */
-
unsigned num_bugs;
-
struct list_head bug_list;
-
struct bug_entry *bug_table;
-
#endif
-
-
#ifdef CONFIG_KALLSYMS
-
/*
-
* We keep the symbol and string tables for kallsyms.
-
* The core_* fields below are temporary, loader-only (they
-
* could really be discarded after module init).
-
*/
-
Elf_Sym *symtab, *core_symtab;
-
unsigned int num_symtab, core_num_syms;
-
char *strtab, *core_strtab;
-
-
/* Section attributes */
-
struct module_sect_attrs *sect_attrs;
-
-
/* Notes attributes */
-
struct module_notes_attrs *notes_attrs;
-
#endif
-
-
/* The command line arguments (may be mangled). People like
-
keeping pointers to this stuff */
-
char *args;
-
-
#ifdef CONFIG_SMP
-
/* Per-cpu data. */
-
void __percpu *percpu;
-
unsigned int percpu_size;
-
#endif
-
-
#ifdef CONFIG_TRACEPOINTS
-
unsigned int num_tracepoints;
-
struct tracepoint * const *tracepoints_ptrs;
-
#endif
-
#ifdef HAVE_JUMP_LABEL
-
struct jump_entry *jump_entries;
-
unsigned int num_jump_entries;
-
#endif
-
#ifdef CONFIG_TRACING
-
unsigned int num_trace_bprintk_fmt;
-
const char **trace_bprintk_fmt_start;
-
#endif
-
#ifdef CONFIG_EVENT_TRACING
-
struct ftrace_event_call **trace_events;
-
unsigned int num_trace_events;
-
#endif
-
#ifdef CONFIG_FTRACE_MCOUNT_RECORD
-
unsigned int num_ftrace_callsites;
-
unsigned long *ftrace_callsites;
-
#endif
-
-
#ifdef CONFIG_MODULE_UNLOAD
-
/* What modules depend on me? */
-
struct list_head source_list;
-
/* What modules do I depend on? */
-
struct list_head target_list;
-
-
/* Who is waiting for us to be unloaded */
-
struct task_struct *waiter;
-
-
/* Destruction function. */
-
void (*exit)(void);
-
-
struct module_ref {
-
unsigned int incs;
-
unsigned int decs;
-
} __percpu *refptr;
-
#endif
-
-
#ifdef CONFIG_CONSTRUCTORS
-
/* Constructor functions. */
-
ctor_fn_t *ctors;
-
unsigned int num_ctors;
-
#endif
-
};
struct device_driver :
-
/**
-
* struct device_driver - The basic device driver structure
-
* @name: Name of the device driver.
-
* @bus: The bus which the device of this driver belongs to.
-
* @owner: The module owner.
-
* @mod_name: Used for built-in modules.
-
* @suppress_bind_attrs: Disables bind/unbind via sysfs.
-
* @of_match_table: The open firmware table.
-
* @probe: Called to query the existence of a specific device,
-
* whether this driver can work with it, and bind the driver
-
* to a specific device.
-
* @remove: Called when the device is removed from the system to
-
* unbind a device from this driver.
-
* @shutdown: Called at shut-down time to quiesce the device.
-
* @suspend: Called to put the device to sleep mode. Usually to a
-
* low power state.
-
* @resume: Called to bring a device from sleep mode.
-
* @groups: Default attributes that get created by the driver core
-
* automatically.
-
* @pm: Power management operations of the device which matched
-
* this driver.
-
* @p: Driver core's private data, no one other than the driver
-
* core can touch this.
-
*
-
* The device driver-model tracks all of the drivers known to the system.
-
* The main reason for this tracking is to enable the driver core to match
-
* up drivers with new devices. Once drivers are known objects within the
-
* system, however, a number of other things become possible. Device drivers
-
* can export information and configuration variables that are independent
-
* of any specific device.
-
*/
-
struct device_driver {
-
const char *name;
-
struct bus_type *bus;
-
-
struct module *owner;
-
const char *mod_name; /* used for built-in modules */
-
-
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
-
-
const struct of_device_id *of_match_table;
-
-
int (*probe) (struct device *dev);
-
int (*remove) (struct device *dev);
-
void (*shutdown) (struct device *dev);
-
int (*suspend) (struct device *dev, pm_message_t state);
-
int (*resume) (struct device *dev);
-
const struct attribute_group **groups;
-
-
const struct dev_pm_ops *pm;
-
-
struct driver_private *p;
-
};
struct attribute :
-
struct attribute {
-
const char *name;
-
mode_t mode;
-
#ifdef CONFIG_DEBUG_LOCK_ALLOC
-
struct lock_class_key *key;
-
struct lock_class_key skey;
-
#endif
-
};
struct driver_attribute :
-
struct driver_attribute {
-
struct attribute attr;
-
ssize_t (*show)(struct device_driver *driver, char *buf);
-
ssize_t (*store)(struct device_driver *driver, const char *buf,
-
size_t count);
-
};
在
ldd_bus.c 中
-
static BUS_ATTR(Version,S_IRUGO,show_bus_version,NULL);
会在
/sys/bus/ldd 目录中生成一个
Version 的目录
-
在将lddbus子系统装载到内核和从内核卸载的源码如下:
-
static int __init ldd_bus_init(void)
-
{
-
int ret;
-
ret = bus_register(&ldd_bus_type); /*注册总线,在调用这个函数之后ldd_bus_type 结构体将向内核注册,在/sys/bus中出现ldd文件夹,其中包含两个目录:devices 和 drivers */
-
if (ret)
-
return ret;
-
if (bus_create_file(&ldd_bus_type, &bus_attr_version)) /*添加总线属性,将在/sys/bus/ldd目录中出现version属性文件*/
-
printk(KERN_NOTICE "Unable to create version attribute ! \n");
-
ret = device_register(&ldd_bus);/*将总线作为设备注册。因为总线也可以是一个设备,比如在S3C2440中SPI总线控制器相对于ARMv7核心来说,其实就是一个外设。调用此函数后,就会在/sys/devices中出现ldd0目录*/
-
if (ret)
-
printk(KERN_NOTICE "Unable to register ldd0 ! \n");
-
-
printk(KERN_NOTICE "Mount lddbus ok !\nBus device is ldd0 !\nYou can see me in sys/module/ , sys/devices/ and sys/bus/ ! \n");
-
-
return ret;
-
}
#tree -AC /sys/module/lddbus/ /sys/devices/ldd0/ /sys/bus/ldd/
/sys/module/lddbus/
├── holders
├── initstate
├── refcnt
└── sections
├── __ksymtab
└── __ksymtab_strings
/sys/devices/ldd0/
├── power
│ └── wakeup
└── uevent
/sys/bus/ldd/
├── devices
├── drivers
├── drivers_autoprobe
├── drivers_probe
└── Version
问题1:驱动是如何加载到总线上的?
答:
1,实现一个 struct ldd_driver 变量:
-
static struct ldd_driver sculld_driver =
-
{
-
.version = "$reversion: 2.0 $",
-
.module = THIS_MODULE,
-
.driver =
-
{
-
.name = "sculld_driver",
-
},
-
};
2,把实现的这个变量添加到总线上:
-
/*
-
* Register with the driver core.
-
*/
-
register_ldd_driver(&sculld_driver);
3,在 register_ldd_driver
(&sculld_driver
); 实现如下:
a)定义驱动的总线类型:
-
driver->driver.bus = &ldd_bus_type;
-
//the bus type
-
struct bus_type ldd_bus_type =
-
{
-
.name = "ldd",
-
.match = ldd_match,
-
.uevent = ldd_hotplug,
-
};
b)注册驱动
-
ret = driver_register(&driver->driver);
-
/**
-
* driver_register - register driver with bus
-
* @drv: driver to register
-
*
-
* We pass off most of the work to the bus_add_driver() call,
-
* since most of the things we have to do deal with the bus
-
* structures.
-
*/
-
int driver_register(struct device_driver *drv)
-
{
-
int ret;
-
struct device_driver *other;
-
-
BUG_ON(!drv->bus->p);
-
-
//@1@
-
if ((drv->bus->probe && drv->probe) ||
-
(drv->bus->remove && drv->remove) ||
-
(drv->bus->shutdown && drv->shutdown))
-
printk(KERN_WARNING "Driver '%s' needs updating - please use "
-
"bus_type methods\n", drv->name);
-
-
//@2@
-
other = driver_find(drv->name, drv->bus);
-
if (other) {
-
put_driver(other);
-
printk(KERN_ERR "Error: Driver '%s' is already registered, "
-
"aborting...\n", drv->name);
-
return -EBUSY;
-
}
-
-
//@3@
-
ret = bus_add_driver(drv);
-
if (ret)
-
return ret;
-
-
-
ret = driver_add_groups(drv, drv->groups);
-
if (ret)
-
bus_remove_driver(drv);
-
return ret;
-
}
-
EXPORT_SYMBOL_GPL(driver_register);
@1@ :
函数首先是检查device_driver类型的drv对象和其bus域的回调函数是否已经被赋值,
如果为空,则打印警告。
@2@ : 检测驱动是否已注册
@3@ :驱动加载到总线上的核心操作
-
/**
-
* bus_add_driver - Add a driver to the bus.
-
* @drv: driver.
-
*/
-
int bus_add_driver(struct device_driver *drv)
-
{
-
struct bus_type *bus;
-
struct driver_private *priv;
-
int error = 0;
-
-
bus = bus_get(drv->bus);
-
if (!bus)
-
return -EINVAL;
-
-
pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
-
-
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-
if (!priv) {
-
error = -ENOMEM;
-
goto out_put_bus;
-
}
-
klist_init(&priv->klist_devices, NULL, NULL);
-
priv->driver = drv;
-
drv->p = priv;
-
priv->kobj.kset = bus->p->drivers_kset;
-
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
-
"%s", drv->name);
-
if (error)
-
goto out_unregister;
-
-
if (drv->bus->p->drivers_autoprobe) {
-
error = driver_attach(drv);
-
if (error)
-
goto out_unregister;
-
}
-
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
-
module_add_driver(drv->owner, drv);
-
-
error = driver_create_file(drv, &driver_attr_uevent);
-
if (error) {
-
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
-
__func__, drv->name);
-
}
-
error = driver_add_attrs(bus, drv);
-
if (error) {
-
/* How the hell do we get out of this pickle? Give up */
-
printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
-
__func__, drv->name);
-
}
-
-
if (!drv->suppress_bind_attrs) {
-
error = add_bind_files(drv);
-
if (error) {
-
/* Ditto */
-
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
-
__func__, drv->name);
-
}
-
}
-
-
kobject_uevent(&priv->kobj, KOBJ_ADD);
-
return 0;
-
-
out_unregister:
-
kobject_put(&priv->kobj);
-
kfree(drv->p);
-
drv->p = NULL;
-
out_put_bus:
-
bus_put(bus);
-
return error;
-
}
在bus_add_driver()函数中,首先是检查drv->bus,如果为空可立即返回EINVAL(无效的变量),注册失败,
可见bus域必须提前初始化好才行。接下来是对kobj域进行初始化,检查bus域等。
在 /sys/bus/ldd/drivers/ 目录中创建 sculld_driver 目录 ;
并在 /sys/bus/ldd/drivers/sculld_driver 创建属性文件 uevent,
最后调用add_bind_files()函数
-
/*
-
* Thanks to drivers making their tables __devinit, we can't allow manual
-
* bind and unbind from userspace unless CONFIG_HOTPLUG is enabled.
-
*/
-
static int __must_check add_bind_files(struct device_driver *drv)
-
{
-
int ret;
-
-
ret = driver_create_file(drv, &driver_attr_unbind);
-
if (ret == 0) {
-
ret = driver_create_file(drv, &driver_attr_bind);
-
if (ret)
-
driver_remove_file(drv, &driver_attr_unbind);
-
}
-
return ret;
-
}
在
/sys/bus/ldd/drivers/sculld_driver 创建属性文件 :bind 和 unbind
c)设置驱动的属性
-
driver->version_attr.attr.name = "verSion";
-
// driver->version_attr.attr.owner = driver->module;
-
driver->version_attr.attr.mode = S_IRUGO;
-
driver->version_attr.show = show_version;
-
-
return driver_create_file(&driver->driver,&driver->version_attr);
在
/sys/bus/ldd/drivers/sculld_driver 创建属性文件 :verSion
#tree -AC /sys/bus/ldd/
/sys/bus/ldd/
├── devices
├── drivers
│ ├── sculld_driver
│ │ └── sculld
│ │ ├── bind
│ │ ├── unbind
│ │ └── verSion
├── drivers_autoprobe
├── drivers_probe
└── Version
问题2:设备是如何加载到总线上的?
答:
在
sculld.c 中修改如下:
-
static ssize_t sculld_show_dev(struct device *ddev,struct device_attribute *attr,char *buf)
-
{
-
struct sculld_dev *dev = dev_get_drvdata(ddev);
-
return print_dev_t(buf,dev->cdev.dev);
-
// return 0;
-
}
-
-
static DEVICE_ATTR(dev,S_IRUGO,sculld_show_dev,NULL);
-
-
static void sculld_register_dev(struct sculld_dev *dev,int index)
-
{
-
sprintf(dev->devname,"sculld%d",index);
-
dev->ldev.name = dev->devname;
-
dev->ldev.driver= &sculld_driver;
-
dev_set_drvdata(&dev->ldev.dev,dev);
-
-
printk(KERN_NOTICE"\nsculld%i device\n",index);
-
register_ldd_device(&dev->ldev);
-
-
printk(KERN_NOTICE"\nsculld%i create\n",index);
-
device_create_file(&dev->ldev.dev,&dev_attr_dev);
-
}
问题3:驱动和设备是如何匹配上的?
答:
待续。。。
阅读(2318) | 评论(0) | 转发(2) |