5.3.2 Архитектура программного обеспечения I2C |
Предыдущая Содержание Следующая |
Подсистема I2C появилась после большой реконструкции, с выходом ядра версии 2.6. В этом разделе мы обсудим архитектуру I2C в ядре версии 2.6. Хотя эта шина сама по себе очень проста, архитектура подсистемы I2C в Linux является довольно сложной и лучше всего её можно понять на примере. Предположим, что ваша плата использует I2C, как показано на Рисунке 5.8, который показывает на плате две шины I2C; каждая шина I2C находится под управлением адаптера шины I2C, подобного PCF8584, который действует как ведущее устройство I2C для данной шины и, кроме того, работает как интерфейс между шиной процессора и шиной I2C. Таким образом, процессор может обращаться к любому из устройств I2C на шинах I2C путём программирования этих адаптеров. К первой шине I2C подключены две микросхемы EEPROM, а к другой шине I2C подключена микросхема RTC.
Рисунок 5.8 Пример топологии шины I2C.
Подсистемой I2C в Linux определены следующие логические компоненты программного обеспечения:
▪Драйвер алгоритма I2C: каждый адаптер шины I2C имеет свой собственный способ взаимодействия с процессором и шиной I2C. В приведённом выше примере оба шинных адаптера используют стиль сопряжения микросхемы PCF, который определяет регистры, которые должны быть реализованы адаптером шины и реализацию алгоритмов для передачи и приёма данных. Драйвер алгоритма реализует основные процедуры обмена данными (передача и приём). Например, на Рисунке 5.8 показан только один драйвер алгоритма, драйвер алгоритма PCF8584. ▪Драйвер адаптера I2C: это можно рассматривать как уровень BSP для подсистемы I2C. Драйвер адаптера I2C и драйвер алгоритма вместе управляют шинами I2C в системе. В приведённом выше примере мы определяем два драйвера адаптера I2C для каждой из двух шин в системе. Оба эти драйвера адаптера связаны с драйвером алгоритма PCF8584. ▪Драйвер ведомого устройства I2C: драйвер ведомого устройства содержит процедуры для доступа к определённому виду ведомого устройства на шине I2C. В нашем примере мы предоставляем два драйвера: один для доступа к EEPROM на первой шине I2C, а другой для доступа к микросхеме RTC на второй шине I2C. ▪Драйвер клиента I2C: один драйвер клиента является представлением одной единицы оборудования, которое должно быть доступно через шину I2C. Драйверы ведомого устройства и клиента связаны друг с другом. В нашем примере мы должны определить три клиентских драйвера: два клиентских драйвера EEPROM и один клиентский драйвер RTC.
Почему подсистема разделена таким образом? Это делается для как можно большего повторного использования программного обеспечения и чтобы дать возможность переносить код. Это достигается за счёт сложности. Подсистема I2C находится в каталоге drivers/i2c дерева исходных текстов ядра. В этом каталоге подкаталог buses содержит различные драйверы адаптера шины, algos содержит различные драйверы микросхем алгоритма, а каталог chips содержит различные драйверы ведомых устройств и клиентов. Общая часть всей подсистемы I2C называется ядром I2C и реализована в файле drivers/ic2/i2c-core.c.
Драйвер алгоритма и адаптера шины
Для того, чтобы лучше понять, как писать эти драйверы, мы рассмотрим реализацию драйвера алгоритма для PCF8584 для Linux и шинный адаптер, который использует этот алгоритм. Прежде чем мы углубимся в исходный код, давайте сделаем очень общий обзор микросхемы интерфейса I2C PCF8584. PCF8584 представляет собой интерфейсное устройство между стандартными высокоскоростными параллельными шинами и шиной I2C. Микросхема осуществляет передачу данных между шиной I2C и широко распространённой параллельный шиной микроконтроллера, используя либо прерывание, либо опрос. PCF8584 определяет следующие регистры адаптера шины I2C.
▪S0: регистр буфера данных/сдвига, который выполняет параллельно-последовательное преобразование между процессором и шиной I2C. ▪S0': это внутренний регистр адрес и он заполняется во время инициализации. ▪S1: управляющий регистр и регистр состояния, используемый для доступа к шине и управления. ▪S2: регистр тактовой частоты. ▪S3: регистр вектора прерывания.
Спецификация PCF8584 содержит более подробную информацию о том, как программировать регистры для инициализации, передачи и приёма. Спецификацию можно загрузить с веб-сайта компании Philips Semiconductor. Каждый драйвер алгоритма связан со структурой данных i2c_algorithm, объявленной в файле include/linux/i2c.h. Эта структура данных имеет указатель на функцию master_xfer, который указывает на функцию, которая реализует фактический алгоритм передачи и приёма по I2C. Другими важными полями этой структуры являются:
▪name: название алгоритма. ▪id: каждый алгоритм определяется с помощью уникального номера. Различные типы алгоритмов определены в заголовочном файле include/linux/i2c-id.h. ▪algo_control: это указатель на функцию, подобную ioctl. ▪functionality: это указатель на функцию, которая возвращает функции, поддерживаемые адаптером, например, какие типы сообщений поддерживаются драйвером I2C.
static struct i2c_algorithm pcf_algo = {
Драйвер алгоритма сам по себе не имеет смысла, если он не связан с драйвером адаптера шины I2C. Драйвер алгоритма PCF обеспечивает для этой цели функцию привязки: i2c_pcf_add_bus(). Каждый драйвер адаптер связан со структурой данных i2c_adapter (объявленной в файле include/ linux/i2c.h), экземпляр которой создаётся драйвером адаптера. Драйвер адаптера вызывает функцию i2c_pcf_add_bus с указателем на структуру i2c_adapter. Важными полями структуры i2c_adapter, которые настраиваются драйвером адаптера, являются:
▪name: имя для адаптера. ▪class: указывает тип класса устройств I2C, который поддерживает этот драйвер. ▪algo: указатель на структуру данных i2c_algorithm. i2c_pcf_add_bus() устанавливает algo указывающим на pcf_algo. ▪algo_data: указатель на закрытую, зависимую от алгоритма структуру данных. Например, драйвер алгоритма PCF присваивает этому полю внутренний указатель на структуру данных i2c_algo_pcf_data. Эта структура данных содержит указатель на процедуры для доступа к различным регистрам адаптера. Таким образом, драйвер алгоритма отделён от деталей конструкции платы; драйвер адаптера экспортирует особенности конструкции платы используя эту структуру данных. Драйвер адаптера определяет различные процедуры, которые определены в структуре данных i2c_algo_pcf_data следующим образом:
static struct i2c_algo_pcf_data pcf_data = { .setpcf = pcf_setbyte, .getpcf = pcf_getbyte, .getown = pcf_getown, .getclock = pcf_getclock, .waitforpin = pcf_waitforpin, .udelay = 10, .mdelay = 10, .timeout = 100, };
Драйверу адаптера шины необходимо выполнить следующие действия, чтобы связать себя с драйвером алгоритма.
▪Определить структуру типа i2c_adapter следующим образом: ▪Определить функции инициализации, которые делают следующее:
I2C драйверы ведомых устройств и клиентов
Чтобы понять модель драйвера клиента I2C, предположим, что есть вымышленное устройство, подключенное по шине I2C, которое содержит один 32-х разрядный регистр. Функциональность драйвера заключается в предоставлении процедур для выполнения чтения и записи регистра. Мы также предполагаем наличие программного обеспечения драйвера алгоритма и драйвера адаптера. Это включает в себя создание драйвера ведомого устройства и клиента. Драйвер ведомого устройства использует структуру данных i2c_driver, объявленную в заголовочном файле include/linux/i2c.h. Важными полями этой структуры данных являются:
▪name: название клиента. ▪id: уникальный идентификатор этого устройства. Список всех идентификаторов может быть найден в файле include/linux/i2c-id.h. ▪flags: устанавливается в I2C_DF_NOTIFY, что разрешает уведомления при обнаружении устройств на шине, так что драйвер сможет обнаруживать новые устройства. ▪attach_adapter: указывает на функцию, которая определяет наличие устройств I2C на шине I2C. Если устройство найдено, то она вызывает функцию для создания нового экземпляра клиента и подключения клиента к ядру I2C. ▪detach_client: указывает на функцию, которая удаляет экземпляр клиента и уведомляет ядро I2C о его удалении. ▪command: это подобная ioctl команда, которая может быть использована для специальных функций в устройстве.
В нашем примере драйвера мы определяем структуру i2c_driver следующим образом:
static struct i2c_driver i2c_test_driver = { .owner = THIS_MODULE, .name = "TEST", .id = I2C_DRIVERID_TEST, .flags = I2C_DF_NOTIFY, .attach_adapter = i2c_test_scan_bus .detach_client = i2c_test_detach, .command = i2c_test_command };
Сначала рассмотрим функцию i2c_test_scan_bus, которая вызывается, когда добавляется новый адаптер или новое устройство. Аргументом этой функции является указатель на структуру i2c_adapter для шины, на которой обнаружено и добавлено ведомое устройство.
static int i2c_test_scan_bus(struct i2c_adapter *d) { return i2c_probe(d, &addr_data, i2c_test_attach); }
Функция i2c_probe предоставляется ядром I2C; эта функция использует информацию в структуре данных addr_data, чтобы вызвать функцию i2c_test_attach; последняя создаёт нового экземпляр клиента и регистрирует его в подсистеме. Структура addr_data объявлена в файле include/linux/i2c.h. Функция addr_data используется для выполнения следующего:
▪Принудительной регистрации устройства I2C по указанному адресу в качестве клиента без проверки ▪Игнорирования устройства I2C по указанному адресу ▪Зондирования устройства I2C по указанному адресу с помощью адаптера и обнаружения его присутствия ▪Функционирования в нормальном режиме, просто обращаясь к устройству I2C по указанному адресу и проверяя его присутствие
Функция i2c_test_attach создаёт структуру данных i2c_client для клиента и заполняет её. Структура данных i2c_client также объявлена в файле include/linux/i2c.h и её важными полями являются:
▪id: идентификация ▪addr: I2C адрес, по которому было обнаружено ведомое устройство ▪adapter: указатель на структуру i2c_adapter для шины, на которой был обнаружен клиент ▪driver: указатель на структуру i2c_driver
static int i2c_test_attach(struct i2c_adapter *adap, int addr, int type) { struct i2c_client *client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); client->id = TEST_CLIENT_ID: client->addr = addr; client->adapter = adapter; client->driver = &i2c_test_driver;
return(i2c_attach_client(client)); }
Наконец, командная функция, которая реализует функциональные возможности чтения и записи одного регистра микросхемы, выглядит в следующим образом:
static int i2c_test_command(struct i2c_client *client, unsigned int cmd, void *arg) { if(cmd == READ) return i2c_test_read(client, arg); else if(cmd == WRITE) return i2c_test_write(client, arg); return -EINVAL; }
static int i2c_test_read(struct i2c_client *client, void *arg) { i2c_master_recv(client,arg,4); }
static int i2c_test_write(struct i2c_client *client, void *arg) { i2c_master_send(client, arg ,4); }
Функции i2c_master_recv и i2c_master_send читают и записывают байты от данного клиента. Внутри они вызывают функцию драйвера master_xfer. Также доступна ещё одна функция, i2c_transfer; она посылает серию сообщений, которые могут быть смесью операций чтения и записи, приводя к комбинированным операциям.
|
Предыдущая Содержание Следующая |