Start of topic | Skip to actions
Linux Unified Device Model: использование и расширение
Встраивание базовых структур, "наследование" в C-стилеБазовые структуры (см выше) довольно компактны и содержат в себе только самое необходимое для регистрации в связных списках и регистрации в sysfs. А как быть, если то, что мы описываем, требует большего количества данных? И, самое главное, как использовать callback'и, на которых построено взаимодействие устройств и драйверов в UDM, если они на вход принимают только указатели на базовые структуры? Для этого в UDM предусмотрен механизм "наследования". Этот механизм основан на использовании макроса container_of, определенного в linux/kernel.h:
#define container_of(ptr, type, member) \
({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) ); \
})
Рассмотрим использование container_of на примере. Для этого определим структуру,
встраивающую в себя структуру device.
my_device.h:
#include <linux/device.h>
struct my_device {
struct device dev;
struct my_device_data data;
};
#define to_my_device(x) container_of((x), struct my_device, dev)
my_device.c:
#include "my_device.h"
struct my_device my_dev;
struct device* dev = &my_dev.dev;
int test_function( void )
{
struct my_device* ptr = to_my_device(dev);
return (ptr == &my_dev) ? 0 : -EFAULT;
}
Функция test_function вернет 0 (нормальное завершение).
Из примера видно, что макрос container_of позволяет, имея указатель на
встроенную структуру (в примере struct device), получить указатель на структуру,
расширяющую встроенную (в примере struct my_device).
Использование макроса container_of позволяет встраивать базовые структуры UDM в свои структуры,
и при этом использовать те же типы для callback'ов, поскольку в callback'и передаются указатели
на базовые структуры и ничто не мешает преобразовать их в указатели на производные структуры.
Продолжим пример, чтобы проиллюстрировать использование макроса container_of в callback'ах.
Для этого определим переменную типа struct device_driver и callback my_device_probe.
my_device.c:
int my_device_init(struct my_device* my_dev)
{
// ...
}
int my_device_probe(struct device* dev)
{
struct my_device* my_dev = to_my_device(dev);
// Здесь программист должен быть очень внимателен.
// Является ли структура (*dev) встроенной в my_device?
return my_device_init( my_dev );
}
struct device_driver my_drv = {
.name = "my driver",
.probe = my_device_probe
};
Итак, мы познакомились с принципом наследования в UDM, который позволяет
неограниченно расширять существующие типы данных в соответствии с требованиями конкретной задачи.
Посмотрим, как этот принцип используется в ядре linux 2.6.15 на примере расширения platform.
platform: расширение UDM.UDM - гибкая система взаимодействия устройств и драйверов. Она хорошо подходит для поддержки hotplug-устройств (устройств "горячего" включения). Однако, есть целый класс устройств, которые присутствуют в системе на протяжении всего цикла ее жизни. Этими устройствами являются:
struct platform_device {
const char * name;
u32 id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
};
Видно, что platform_driver просто переопределяет callback'и так, чтобы им передавались указатели не на struct device,
а на struct platform_device.
А вот platform_device имеет несколько дополнительных членов:
extern struct platform_device *platform_device_register_simple(char *, unsigned int, struct resource *, unsigned int); extern struct platform_device *platform_device_alloc(const char *name, unsigned int id); extern int platform_device_add_resources(struct platform_device *pdev, struct resource *res, unsigned int num); extern int platform_device_add_data(struct platform_device *pdev, void *data, size_t size); extern int platform_device_add(struct platform_device *pdev); extern void platform_device_put(struct platform_device *pdev);Рассмотрим подробнее регистрацию устройства:
ЗаключениеUDM представляет собой масштабируемую систему взаимодействия устройств и драйверов. platform является хорошей иллюстрацией решения, основанного на идеологии и архитектуре UDM. Какие плюсы имеет использование platform_device & platform_driver:
Литература, ссылки
| |