前言
驅動開發中通常爲設備定義一個設備相關的設備結構體,其包含該設備的cdev 、私有數據、信號量、irq等這些信息。
驅動開發中通常將文件的私有數據private_data指向設備結構體,在read()、write()、ioctl()等函數通過 private_data 訪問數據 設備結構體。
container_of() 是一個比較常用的宏,其作用爲通過結構體成員的指針找到對應結構體的指針。
通過分析 akm8975.c 這個文件進行分析
分析
自定義的結構體 akm8975_data ,包含了 i2c_client 、work_struct等信息。
struct akm8975_data {
struct i2c_client *this_client;
struct akm8975_platform_data *pdata;
struct input_dev *input_dev;
struct work_struct work;
struct mutex flags_lock;
#ifdef CONFIG_HAS_EARLYSUSPEND
struct early_suspend early_suspend;
#endif
定義一個全局的變量 akmd_data
/*
* Because misc devices can not carry a pointer from driver register to
* open, we keep this global. This limits the driver to a single instance.
*/
struct akm8975_data *akmd_data;
自定義的設備結構體的初始化:
int akm8975_probe(struct i2c_client *client,
const struct i2c_device_id *devid){
...
struct akm8975_data *akm; //申請一個局部的結構體指針
akm = kzalloc(sizeof(struct akm8975_data), GFP_KERNEL); //初始這個指針
akmd_data = akm; //複製給全局的變量
}
將自定義的設備結構體賦值給 file的private_data
static int akm_aot_open(struct inode *inode, struct file *file){
...
file->private_data = akmd_data;
}
通過file取得private_data
tatic int akm_aot_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
void __user *argp = (void __user *) arg;
short flag;
struct akm8975_data *akm = file->private_data;
自定義的設備結構體,通常有以下操作
- 定義一個全局的變量
- 在probe 或者入口函數中初始化 全局變量
- 在 file_operation open 中,將全局變量賦值給file的private_data
在read() 、wriete、ioctl()的操作中,通過file取中private_data 進行操作
在定義的設備結構體中 一項 工作隊列。 這個工作隊列 是如何和 container_of聯繫起來的呢? 先看下代碼中的相關內容。
初始化工作隊列
int akm8975_probe(struct i2c_client *client,
const struct i2c_device_id *devid){
...
INIT_WORK(&akm->work, akm_work_func);
}
工隊隊列
static void akm_work_func(struct work_struct *work)
{
struct akm8975_data *akm =
container_of(work, struct akm8975_data, work); //利用container_of,獲得work指針對於的設備結構體指針 akm
FUNCDBG("called");
//通過獲得的akm指針,從結構體中,取出成員來執行相應的操作。
enable_irq(akm->this_client->irq);
}
調度 工作隊列
static irqreturn_t akm8975_interrupt(int irq, void *dev_id)
{
struct akm8975_data *akm = dev_id;
FUNCDBG("called");
disable_irq_nosync(akm->this_client->irq);
schedule_work(&akm->work);
return IRQ_HANDLED;
}
通過定義一個設備結構體,可以將設備相關的操作、信息封裝在一起,後邊可以使用 container_of 得到這個結構體。將數據傳遞給private_data 更加規範和調理。
container_of
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
offsetof 通過將 0 地址,強制轉爲爲TYPE類型的指針,然後取它的成員 NUMBER , 在轉爲size_t 類型。即得到 NUMBER 在這個TYPE的位置。
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
#endif
參考文獻
磁傳感器AKM8975驅動和中間層
offsetof與container_of宏總結
GCC typeof在kernel中的使用——C語言的“編譯時多態”
akm8975.c