Пытаюсь реализовать полность асинхронную обработку I2C без выделения памяти. Нюанс этой шины в том, что чтобы что-то прочитать, нужно сперва записать адрес регистра, из которого нужно выполнить чтение. И с точки зрения машины состояний, эти два шага — независимые. Если бы сиутация была как с UART, то достаточно было бы завести кольцевой буфер и все запросы на чтение и запись сваливать туда. А тут требуется разбивать запросы логически на блоки, иначе как понять, что после записи очередной порции данных, теперь нужно считывать?
Я подумал, что задачу можно решить следующим образом. Пусть инициаторы запросов сами формируют структуру сообщения, а драйвер I2C будет лишь связывать структуры в список и исполнять машину состояний для очередного сообщения. После очередного достижения терминального состония, текущее сообщение удаляется из списка (для связного списка это просто переприсваивание указателей), и в обработку берётся следующее, если оно есть.
Например, структура сообщения может иметь вид:
struct msg {
int type; // 0 - READ, 1 - WRITE
int size;
char *data;
msg *prev;
msg *next;
void (*callback)(); // вызывается драйвером из прерывания, когда данные прочитаны/записаны
};
Тогда инициатор запроса будет заполнят нужные поля сам и отправлять сообщение драйверу. Драйвер добавляет сообщение в начало списка, а обрабатывает список с конца (или наоборот, это неважно). Весь код является последовательным, и только прерывания добавляют асинхронности, поэтому каждому пользователю шины достаточно хранить одну структуру сообщения в статической памяти. Таким образом достигается и потокобезопасность, и отсутствие необходимости в динамической мапяти.
Как идея — имеет право на жизнь?