Используйте этот режим, если хотите использовать операционную систему. Рекомендуется использовать RTOS, которая корректно обрабатывает инверсию приоритета для использования LWIP_TCPIP_CORE_LOCKING.
Портирование: реализуйте все функции Porting [5].
Вы можете использовать Callback-style API вместе с tcpip_callback, и все вызовы Sequential-style API.
[Портирование: слой абстракции системы, интерфейс sys_arch для lwIP]
Слой эмуляции операционной системы предоставляет общий интерфейс между кодом lwIP и нижележащим ядром операционной системы. Основная идея портирования lwIP на новую архитектуру состоит в том, чтобы внести только небольшие изменения в заголовочные файлы и новую реализацию sys_arch. Также можно выполнить реализацию sys_arch, которая не опирается на какую-либо нижележащую операционную систему.
Модуль sys_arch для lwIP предоставляет семафоры, почтовые ящики (mailboxes) и мьютексы and mutexes to lwIP. Для полной функциональности lwIP поддержка нескольких потоков может быть реализована в sys_arch, однако для базового функционала lwIP. Планировщик на таймере реализован в lwIP, однако может быть реализован в портом sys_arch (LWIP_TIMERS_CUSTOM==1).
В дополнение к исходному коду, предоставляющему функционал sys_arch, слой эмуляции OS должен предоставлять несколько заголовочных файлов, определяющих макросы, используемые в lwip. Необходимые файлы и макросы перечислены ниже в описании sys_arch.
Семафоры могут быть либо счетными, либо двоичными, lwIP работает с ними обоими. Mailboxы-должны быть реализованы как очередь, которая позволяет поместить в неё несколько сообщений (реализация в качестве точки ожидания, когда одновременно может быть помещено только одно сообщение, может крайне негативно сказаться на производительности). Сообщение в mailbox это просто указатель, и ничего больше.
Семафоры представлены типом sys_sem_t, который определен через typedef в файле sys_arch.h. Mailboxe-ы эквивалентно представлены типом sys_mbox_t. Мьютексы представлены типом sys_mutex_t. Библиотека lwIP не накладывает никаких ограничений на то, как эти типы представлены внутри системы.
Начиная с lwIP 1.4.0 семафоры, мьютексы и майлбоксы прототипированы так, что позволяют использовать и указатели, и реальные структуры OS. Таким образом память, необходимая для этих типов, может быть выделена либо по месту (глобально или в стеке), либо из кучи (выделенной внутренне функциями *_new()).
В sys_arch должно быть реализовано следующее:
void sys_init(void)
Вызывается, чтобы инициализировать слой sys_arch.
err_t sys_sem_new(sys_sem_t *sem, u8_t count)
Создает новый семафор. Семафор выделяется в памяти, на которую указывает параметр sem (который может быть как указателем, так и реальной структурой OS). Аргумент count задает начальное состояние семафора (0 или 1). Если семафор был создан, должно быть возвращено значение ERR_OK. Возврат любого другого значения ошибки даст подсказку, что пошло не так, однако кроме assert не реализована реальная обработка ошибок.
void sys_sem_free(sys_sem_t *sem)
Удаляет семафор и освобождает память, выделенную для него.
void sys_sem_signal(sys_sem_t *sem)
Посылает сигнал семафору.
u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
Блокирует поток в ожидании сигнала на семафоре. Если аргумент timeout не нулевой, то поток должен заблокирован только на указананное время (в миллисекундах). Если аргумент timeout нулевой, то поток должен быть заблокирован, пока не появится сигнал на семафоре.
Если таймаут не нулевой, то возвращенное значение это количество миллисекунд, потраченное в ожидании сигнала на семафоре. Если семафор не сигнализировался в течение указанного времени, то будет возвращено значение SYS_ARCH_TIMEOUT. Если поток не ожидал на семафоре (например, на нем уже был сигнал), то функция может возвратить 0.
Обратите внимание, что lwIP реализует функцию с похожим именем sys_sem_wait(), которая использует функцию sys_arch_sem_wait().
int sys_sem_valid(sys_sem_t *sem)
Вернет 1, если семафор допустимый, иначе 0. При использовании указателей простейший способ проверить допустимость семафора - проверить его указатель, что он != NULL. Когда напрямую используются структуры OS, реализация проверки может быть более сложная. Это также может быть определение #define, и в этом случае функция не является прототипом.
void sys_sem_set_invalid(sys_sem_t *sem)
Делает семафор недопустимым, чтобы проверяющая его sys_sem_valid() функция возвращала 0.
ВНИМАНИЕ: это НЕ означает, что семафор должен быть удален (deallocated): перед вызовом этой функции всегда вызывается функция sys_sem_free()!
Это также может быть определение #define, и в этом случае функция не является прототипом.
void sys_mutex_new(sys_mutex_t *mutex)
Создает новый мьютекс. Он выделяется в памяти, на которую указывает параметр mutex (это может быть указатель или реальная структура OS). Если мьютекс был создан, должно быть возвращено значение ERR_OK. Возврат любого другого значения ошибки даст подсказку, что пошло не так, однако кроме assert не реализована реальная обработка ошибок.
void sys_mutex_free(sys_mutex_t *mutex)
Удаляет мьютекс и освобождает память, выделенную для него.
void sys_mutex_lock(sys_mutex_t *mutex)
Блокирует поток до тех пор, пока мьютекс не может быть захвачен.
void sys_mutex_unlock(sys_mutex_t *mutex)
Освобождает мьютекс, ранее заблокированный через sys_mutex_lock().
void sys_mutex_valid(sys_mutex_t *mutex)
Вернет 1, если мьютекс допустимый, иначе вернет 0. Когда используются указатели, простой способ такой проверки - проверить указатель на мьютекс, что он != NULL. Когда напрямую используются структуры OS, реализация проверки может быть более сложная. Это также может быть определение #define, и в этом случае функция не является прототипом.
void sys_mutex_set_invalid(sys_mutex_t *mutex)
Делает мьютекс недопустимым, чтобы проверяющая его функция sys_mutex_valid() возвращала 0.
ВНИМАНИЕ: это НЕ означает, что мьютекс должен быть удален (deallocated): перед вызовом этой функции всегда вызывается функция sys_mutex_free()!
Это также может быть определение #define, и в этом случае функция не является прототипом.
err_t sys_mbox_new(sys_mbox_t *mbox, int size)
Создает пустой mailbox для максимального количества size элементов. Элементы, сохраняемые в mailbox-ах, это указатели. Вы должны определить макрос _MBOX_SIZE в своем файле настроек lwipopts.h, или игнорировать этот параметр в своей реализации, и использовать размер по умолчанию.
Если mailbox был создан, то должно быть возвращено ERR_OK. Возврат любого другого значения ошибки даст подсказку, что пошло не так, однако кроме assert не реализована реальная обработка ошибок.
void sys_mbox_free(sys_mbox_t *mbox)
Удалит mailbox. Если при удалении mailbox в нем все еще есть сообщения, то это означает ошибку программирования в lwIP, о чем следует оповестить разработчика.
void sys_mbox_post(sys_mbox_t *mbox, void *msg)
Помещает сообщение msg в mailbox. Эта функция блокирует выполнение кода, пока msg не будет действительно помещено в ящик.
err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
Пробует поместить msg в mailbox. Вернет ERR_MEM, если ящик заполнен, иначе вернет ERR_OK, если сообщение msg было помещено в ящик.
u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
Блокирует поток, пока сообщение не поступит в mailbox, но не дольше, чем на время timeout в миллисекундах (подобно тому, как это сделано в функции sys_arch_sem_wait()). Если timeout указан 0, то поток должен заблокироваться на неопределенное время, пока не поступит сообщение. Аргумент msg это параметр результата, который устанавливается этой функцией (например действием *msg = ptr). Значение msg может быть NULL, чтобы показать, что сообщение должно быть отброшено.
Возвращаемые значения такие же, как и у функции sys_arch_sem_wait(): количество миллисекунд, потраченных на ожидание, или SYS_ARCH_TIMEOUT, если был таймаут.
Обратите внимание, что в lwIP реализована функция с похожим именем, sys_mbox_fetch().
u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
Эта функция подобна sys_arch_mbox_fetch, однако если сообщения нет в mailbox, она немедленно вернет код результата SYS_MBOX_EMPTY. В случае успеха будет возвращено значение 0.
Для эффективной реализации эта функция может быть реализована в sys_arch.h имитирующим функцию макросом вместо обычной функции. Например, наивной реализацией могла бы быть:
#define sys_arch_mbox_tryfetch(mbox,msg) \
sys_arch_mbox_fetch(mbox,msg,1)
... хотя такая реализация будет вводить нежелательные задержки.
int sys_mbox_valid(sys_mbox_t *mbox)
Вернет 1, если mailbox допустимый, иначе 0. При использовании указателей простой способ проверки - проверить указатель на ящик, что он != NULL. Когда напрямую используются структуры OS, реализация проверки может быть более сложная. Это так же может быть определение #define, и в этом случае функция не является прототипом.
void sys_mbox_set_invalid(sys_mbox_t *mbox)
Делает mailbox недопустимым, так что проверяющая его функция sys_mbox_valid() вернет 0.
ВНИМАНИЕ: это НЕ означает, что mailbox должен быть удален (deallocated): перед вызовом этой функции всегда вызывается функция sys_mutex_free()!
Это также может быть определение #define, и в этом случае функция не является прототипом.
Если потоки поддерживаются нижележащей операционной системой, и если такая функциональность нужна в lwIP, то также должна быть реализована функция:
sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio)
Запустит новый поток с именем name, с приоритетом prio, тело которого начнет выполняться в функции thread(). Аргумент arg будет передан как аргумент в функцию thread(). Размер стека, используемого этим потоком, задается в параметре stacksize. Функция вернет идентификатор нового потока. И идентификатор, и приоритет зависят от системы.
Когда lwIP используется больше чем в одном контексте (например из нескольких потоков, или из main-цикла и прерываний), то ДОЛЖНА быть разрешена защита SYS_LIGHTWEIGHT_PROT!
sys_prot_t sys_arch_protect(void)
Эта опциональная функция делает "быструю" защиту критического региона, и возвратить предыдущий уровень защиты. Эта функция должна вызываться только на очень коротких критических регионах. Встраиваемая система, которая поддерживает драйверы на основе ISR, может захотеть реализовать эту функцию для запрета прерываний. Системы, основанные на задачах, могут захотеть реализовать это на основе мьютекса или запрета переключения задач. Эта функция должна поддерживать рекурсивные вызовы из одной и той же задачи или прерывания. Другими словами, sys_arch_protect() могла быть безопасно вызвана, когда уже включена защита. Тогда возвращенное значение покажет, что защита уже активна.
sys_arch_protect() требуется только если Ваш порт поддерживает операционную систему.
void sys_arch_unprotect(sys_prot_t pval)
Эта опциональная функция делает "быстрое" снятие защиты с критического региона кода, которая была установлена предыдущим вызовом sys_arch_protect. Параметр pval указывает на защищаемый регион. Для дополнительной информации см. документацию на функцию sys_arch_protect(). Эта функция требуется только если Ваш порт требует поддержки операционной системы.
Для некоторых конфигураций также понадобится функция:
u32_t sys_now(void)
Эта опциональная функция вернет текущее время в миллисекундах (не беспокойтесь о переполнении этого значения, оно используется только для отслеживания разницы между абсолютными значениями времени). Если эта функция не реализована, то это значит, что Вы не сможете использовать некоторые модули (например метки TCP, внутренние таймауты для NO_SYS==1).
Замечание: будьте осторожны при использовании mem_malloc() в sys_arch. Когда malloc() ссылается на mem_malloc(), Вы можете столкнуться с проблемой циклического вызова функции. В модуле mem.c функция mem_init() пытается выделить семафор с использованием mem_malloc, что конечно же не может быть выполнено, когда sys_arch использует mem_malloc.
[Дополнительные файлы, которые нужны для слоя эмуляции "OS support"]
cc.h - архитектурное окружение, в некотором роде специфика компилятора, и в некотором роде специфика окружения (вероятно следует перенести код поддержки окружения в sys_arch.h.)
Определения типа, используемые lwIP:
u8_t, s8_t, u16_t, s16_t, u32_t, s32_t, mem_ptr_t
Подсказки компилятору для упаковки структур lwIP:
PACK_STRUCT_FIELD(x)
PACK_STRUCT_STRUCT
PACK_STRUCT_BEGIN
PACK_STRUCT_END
Вывод диагностики, специфичный для платформы:
LWIP_PLATFORM_DIAG(x) - не фатальная проблема, печать сообщения.
LWIP_PLATFORM_ASSERT(x) - фатально, вывод сообщения и остановка выполнения кода.
Определения портирования для форматтеров printf:
U16_F, S16_F, X16_F, U32_F, S32_F, X32_F, SZT_F
Облегченные механизмы синхронизации:
SYS_ARCH_DECL_PROTECT(x) - декларация переменной состояния защиты.
SYS_ARCH_PROTECT(x) - вход в режим защиты.
SYS_ARCH_UNPROTECT(x) - выход из режима защиты.
Если компилятор не предоставляет memset(), то этот файл должен включать определение для этого, или подключать файл, где есть соответствующее определение.
Этот файл должен либо подключать локальный для системы заголовок < errno.h>, где определены стандартные коды ошибок *nix, или должен использовать #define LWIP_PROVIDE_ERRNO, чтобы использовать коды #define в lwip/arch.h.
perf.h - измерение производительности, специфичное для архитектуры. Измерение может выполняться повсюду в lwip, эти макросы также могут быть определены как пустота.
PERF_START - начать измерение чего-либо.
PERF_STOP(x) - остановить измерение и записать результат.
sys_arch.h - заголовок, привязанный к sys_arch.c.
Следующие типы зависят от архитектуры:
sys_sem_t, sys_mbox_t, sys_thread_t,
и опционально:
sys_prot_t
Определения для установки переменных sys_mbox_t и sys_sem_t в NULL:
SYS_MBOX_NULL NULL
SYS_SEM_NULL NULL