/**
* struct bus_type - The bus type of the device
* * @name: The name of the bus.
* @dev_name: Used for subsystems to enumerate devices like ("foo%u", dev->id).
* @dev_root: Default device to use as the parent.
* @bus_groups: Default attributes of the bus.
* @dev_groups: Default attributes of the devices on the bus.
* @drv_groups: 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 positive value if the
* given device can be handled by the given driver and zero
* otherwise. It may also return error code if determining that
* the driver supports the device is not possible. In case of
* -EPROBE_DEFER it will queue the device for deferred probing.
* @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.
* * @online: Called to put the device back online (after offlining it).
* @offline: Called to put the device offline for hot-removal. May fail.
* * @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.
* @num_vf: Called to find out how many virtual functions a device on this
* bus supports.
* @dma_configure: Called to setup DMA configuration on a device on
* this bus.
* @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.
* @lock_key: Lock class key for use by the lock validator
* @need_parent_lock: When probing or removing a device on this bus, the
* device core should lock the device's parent.
* * 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;
const char *dev_name;
struct device *dev_root;
const struct attribute_group **bus_groups;
const struct attribute_group **dev_groups;
const struct attribute_group **drv_groups;
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 (*online)(struct device *dev);
int (*offline)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
int (*num_vf)(struct device *dev);
int (*dma_configure)(struct device *dev);
const struct dev_pm_ops *pm;
const struct iommu_ops *iommu_ops;
struct subsys_private *p;
struct lock_class_key lock_key;
bool need_parent_lock;
};
i2c_bus_type定义如下
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
};
EXPORT_SYMBOL_GPL(i2c_bus_type);
在这个i2c_bus_type总线上同样维护着两个链表:dev 链表和 drv 链表。
dev 链表上每一个节点描述的 I2C 外设的纯硬件信息对应的数据结构为 struct i2c_client,每当向 dev 链表添加一个 I2C 外设的硬件信息节点时,只需用此数据结构定义初始化一个对象即可,然后向 dev 链表添加,一旦添加完毕,内核会帮你遍历 drv 链表,从 drv 链表上取出每一个软件节点跟这个要注册的硬件节点进行匹配,内核通过调用总线提供的match 函数进行比较比较 i2c_client 的 name 和 i2c_driver 的 id_table 的 name, 如果匹配成功,硬件找到了对应的软件,内核会调用 i2c_driver 的 probe 函数,并且把匹配成功的硬件节点的首地址传递给 probe 函数。 最终完成硬件和软件的再次结合。
/**
- struct i2c_client - represent an I2C slave device
- @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;
- I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking
- @addr: Address used on the I2C bus connected to the parent adapter.
- @name: Indicates the type of the device, usually a chip name that's generic enough to hide second-sourcing and compatible revisions.
- @adapter: manages the bus segment hosting this I2C device
- @dev: Driver model device node for the slave.
- @irq: indicates the IRQ generated by this device (if any)
- @detected: member of an i2c_driver.clients list or i2c-core's
userspace_devices list
- @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter
- calls it to pass on slave events to the slave driver.
- An i2c_client identifies a single device (i.e. chip) connected to an i2c bus. The behaviour exposed to Linux is defined by the driver
- managing the device.
*/
struct i2c_client {
unsigned short flags; /* div., see below */
unsigned short addr; /* chip address - NOTE: 7bit */
/* addresses are stored in the */
/* _LOWER_ 7 bits */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct device dev; /* the device structure */
int init_irq; /* irq set at initialization */
int irq; /* irq issued by device */
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb; /* callback for slave mode */
#endif
};
功能:represent an I2C slave device,用于描述I2C外设的纯硬件信息
成员:addr: I2C 外设的设备地址,用于找外设,必须初始化!
name: 用于匹配,必须初始化!
dev: 重点关注其中的 void *platform_data 字段,此字段将来用于装载自定义的用于描述 I2C 外设的硬件信息。
irq:如果 I2C 外设和 CPU 之间有中断,此字段保存对应的中断号
切记: linux 内核对 i2c_client 的操作和 platform_device 所有区别,驱动开发者不用去自己定义初始化和注册一个struct i2c_client硬件节点对象,定义初始化和注册过程统一由内核来帮你完成
那么不用驱动开发者自己去定义初始化和注册一个struct i2c_client硬件节点,内核如何获取具体的iic硬件节点信息呢?答:使用i2c_board_info 这个结构体来给内核提供将来要初始化struct i2c_client硬件节点的具体信息
/**
- struct i2c_board_info - template for device creation
- @type: chip type, to initialize i2c_client.name
- @flags: to initialize i2c_client.flags
- @addr: stored in i2c_client.addr
- @dev_name: Overrides the default - dev_name if set
- @platform_data: stored in i2c_client.dev.platform_data
- @of_node: pointer to OpenFirmware device node
- @fwnode: device node supplied by the platform firmware
- @properties: additional device properties for the device
- @resources: resources associated with the device
- @num_resources: number of resources in the @resources array
- @irq: stored in i2c_client.irq
- - I2C doesn't actually support hardware probing, although controllers and
- devices may be able to use I2C_SMBUS_QUICK to tell whether or not there's
- a device at a given address. Drivers commonly need more information than
- that, such as chip type, configuration, associated IRQ, and so on.
- - i2c_board_info is used to build tables of information listing I2C devices
- that are present. This information is used to grow the driver model tree.
- For mainboards this is done statically using i2c_register_board_info();
- bus numbers identify adapters that aren't yet available. For add-on boards,
- i2c_new_device() does this dynamically with the adapter already known.
*/
struct i2c_board_info {
char type[I2C_NAME_SIZE];
unsigned short flags;
unsigned short addr;
const char *dev_name;
void *platform_data;
struct device_node *of_node;
struct fwnode_handle *fwnode;
const struct property_entry *properties;
const struct resource *resources;
unsigned int num_resources;
int irq;
};
该结构体功能:驱动开发者利用此数据结构将 I2C 外设的硬件信息告诉给 linux 内核,将来内核根据提供的 I2C外设的硬件信息帮你定义初始化和注册一个i2c_client硬件节点对象到 dev链表。
结构体成员:
/**
- struct i2c_driver - represent an I2C device driver
- @class: What kind of i2c device we instantiate (for detect)
- @probe: Callback for device binding - soon to be deprecated
- @probe_new: New callback for device binding
- @remove: Callback for device unbinding
- @shutdown: Callback for device shutdown
- @alert: Alert callback, for example for the SMBus alert protocol
- @command: Callback for bus-wide signaling (optional)
- @driver: Device driver model driver
- @id_table: List of I2C devices supported by this driver
- @detect: Callback for device detection
- @address_list: The I2C addresses to probe (for detect)
- @clients: List of detected clients we created (for i2c-core use only)
- @disable_i2c_core_irq_mapping: Tell the i2c-core to not do irq-mapping
- - The driver.owner field should be set to the module owner of this driver.
- The driver.name field should be set to the name of this driver.
- - For automatic device detection, both @detect and @address_list must
- be defined. @class should also be set, otherwise only devices forced
- with module parameters will be created. The detect function must
- fill at least the name field of the i2c_board_info structure it is
- handed upon successful detection, and possibly also the flags field.
- - If @detect is missing, the driver will still work fine for enumerated
- devices. Detected devices simply won't be supported. This is expected
- for the many I2C/SMBus devices which can't be detected reliably, and
- the ones which can always be enumerated in practice.
- - The i2c_client structure which is handed to the @detect callback is
- not a real i2c_client. It is initialized just enough so that you can
- call i2c_smbus_read_byte_data and friends on it. Don't do anything
- else with it. In particular, calling dev_dbg and friends on it is
- not allowed.
*/
struct i2c_driver {
unsigned int class;
/* Standard driver model interfaces */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
/* New driver model interface to aid the seamless removal of the
* current probe()'s, more commonly unused than used second parameter.
*/
int (*probe_new)(struct i2c_client *);
/* driver model interfaces that don't relate to enumeration */
void (*shutdown)(struct i2c_client *);
/* Alert callback, for example for the SMBus alert protocol.
* The format and meaning of the data value depends on the protocol.
* For the SMBus alert protocol, there is a single bit of data passed
* as the alert response's low bit ("event flag").
* For the SMBus Host Notify protocol, the data corresponds to the
* 16-bit payload data reported by the slave device acting as master.
*/
void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol,
unsigned int data);
/* a ioctl like command that can be used to perform specific functions
* with the device.
*/
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;
const struct i2c_device_id *id_table;
/* Device detection callback for automatic device creation */
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
bool disable_i2c_core_irq_mapping;
};
功能说明:represent an I2C device driver,描述 I2C 外设的软件信息
结构体成员:
probe:硬件节点和软件节点匹配成功,内核调用
remove:卸载软件节点,内核调用此函数 (这里的硬件节点无法卸载,写在一起了跟内核)
id_table:重点关注其中的 name 字段,此字段将来用于匹配
struct i2c_device_id {
char name[I2C_NAME_SIZE]; //用于匹配
unsigned long driver_data //用于给 probe 函数传递参数
}
配套函数:
向内核 drv 链表注册添加 I2C 外设软件节点对象
将来内核会帮你遍历,匹配,调用 probe 函数,传递参数
/* use a define to avoid include chaining to get THIS_MODULE */
#define i2c_add_driver(driver) \
i2c_register_driver(THIS_MODULE, driver)
i2c_register_driver()函数源码如下
/*
* An i2c_driver is used with one or more i2c_client (device) nodes to access
* i2c slave chips, on a bus instance associated with some i2c_adapter.
*/
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
int res;
/* Can't register until after driver model init */
if (WARN_ON(!is_registered))
return -EAGAIN;
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
INIT_LIST_HEAD(&driver->clients);
/* When registration returns, the driver core
* will have called probe() for all matching-but-unbound devices.
*/
res = driver_register(&driver->driver);
if (res)
return res;
pr_debug("driver [%s] registered\n", driver->driver.name);
/* Walk the adapters that are already present */
i2c_for_each_dev(driver, __process_new_driver);
return 0;
}
从内核 drv 链表删除软件节点对象
内核会帮你调用 remove 函数
/**
* i2c_del_driver - unregister I2C driver
* @driver: the driver being unregistered
* Context: can sleep
*/
void i2c_del_driver(struct i2c_driver *driver)
{
i2c_for_each_dev(driver, __process_removed_driver);
driver_unregister(&driver->driver);
pr_debug("driver [%s] unregistered\n", driver->driver.name);
}
#include
#include
#include
#include
#include
static struct i2c_device_id mma8653_id[] = {
{"mma8653", 0},
{ }
}; //"mma865"将来用于匹配
// probe函数实现
//client:指向匹配成功的硬件节点对象(由内核帮你创建)
//client->addr:获取设备地址
//client->irq:获取中断号
//client->dev.platform_data:获取自定义的硬件信息
static int mma8653_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
printk("%s\n", __func__);
return 0;
}
//remove函数实现
static int mma8653_remove(struct i2c_client *client)
{
printk("%s\n", __func__);
return 0;
}
//定义初始化MMA8653软件节点对象
static struct i2c_driver mma8653_drv = {
.driver = {
.name = "tarena" //不重要,匹配不是靠它完成 //不写会吐核 wkx
},
.id_table = mma8653_id, //匹配是靠其中的name字段
.probe = mma8653_probe,
.remove = mma8653_remove
};
//入口函数
static int mma8653_init(void)
{
//向内核drv链表添加软件节点对象,匹配成功,内核调用probe函数
i2c_add_driver(&mma8653_drv);
return 0;
}
//出口函数
static void mma8653_exit(void)
{
//从内核中删除软件节点对象,调用remove函数
i2c_del_driver(&mma8653_drv);
}
//各种修饰及其GPL规则
module_init(mma8653_init);
module_exit(mma8653_exit);
MODULE_LICENSE("GPL");