1
1
Форкнуть 0

Драйвер devnp-e100.so для ЗОСРВ Нейтрино редакции 2020

Этот коммит содержится в:
Коммит b31d5858eb
54 изменённых файлов: 5696 добавлений и 0 удалений

4
Makefile Обычный файл
Просмотреть файл

@ -0,0 +1,4 @@
LIST=ALL
EARLY_DIRS=lib
include recurse.mk

472
README.md Обычный файл
Просмотреть файл

@ -0,0 +1,472 @@
## Общая структура сетевого стека
```
┌─────────────────────────────┐
│ │
│ Сетевой контроллер │
│ │
└──────────────▴──────────────┘
┌──────────────┴──────────────┐
│ │
│ Сетевой драйвер (devnp-*) │
│ │
└──────────────▴──────────────┘
*
┌──────────────┴──────────────┐ ┌───────────────────────────┐
│ │ │ │
│ Сетевой менеджер (io-pkt-*) ◂─── * ───┤ Клиентское приложение │
│ │ │ │
└─────────────────────────────┘ ▲ └───────────────────────────┘
Интерфейс libsocket ───────┘
```
## Дерево исходных кодов
```
|- hardware/devnp/
| |- e100/ - Исходный код драйвера Fast Ethernet контроллеров Intel 8255x
| |- sample/ - Исходный код примера сетевого драйвера (поясняет изложенное в данном readme)
| `- Makefile - Правила сборки дерева исходников
|
|- netdrivers.mk - Параметры сборки драйверов
`- Makefile - Правила сборки дерева исходников
```
## Сборка драйвера
- Установить и настроить [комплект разработчика](https://help.kpda.ru/help/topic/ru.kpda.doc.dev_tools_ru/html/devkit/devkit.html) для [ЗОСРВ "Нейтрино" редакции 2020](https://help.kpda.ru/help/index.jsp).
- Выполнить команду:
```
make
```
## Запуск драйвера
Общая схема запуска драйвера:
```
io-pkt-* -d e100 [опция[,опция ...]] ...
```
или
```
io-pkt-*
...
mount -T io-pkt -o [опция[,опция ...]] devnp-e100.so
```
## Разработка сетевого драйвера
Сетевой драйвер выступает прослойкой между оборудованием и сетевым стеком. Он включает низкоуровневые обращения к конкретному оборудованию и высокоуровневые интерфейсы взаимодействия с _io-pkt_. Первая часть уникальна для каждого конкретного устройства и в данном материале не рассматривается.
При изучении основ разработки сетевых драйверов следует ориентироваться на драйвер [sample](hardware/devnp/sample), поскольку он свободен от аппаратно-зависимого кода и может расмативаться в качестве каркаса (шаблона) при разработке нового драйвера.
Далее будут рассматриваться следующие особенности драйверов:
- Инициализация
- Обработка прерываний и получение пакетов
- Отправка пакетов
- Периодические таймеры
- Состояние линка
- Контроль и управление
- Завершение драйвера
- Краткосрочные ожидания в драйвере
- Многопоточность
- Функция отсоединения
**Инициализация**
Код инициализации является самой сложной частью драйвера. Он разбит на две части: однократную и периодическую, причем, последняя требует более тщательной отладки.
Инициализация начинается с регистрации точки входа:
```
struct nw_dll_syms sam_syms[] = {
{ "iopkt_drvr_entry", &IOPKT_DRVR_ENTRY_SYM( sam ) },
{ NULL, NULL }
};
```
Это определение требует от сетевого стека в момент инициализации вызвать для данного драйвера функцию *sam_entry()*. Задача этой фунции в конечном счете вызвать *dev_attach()* для каждого экземпляра поддерживаемого оборудования, обнаруженного драйвером. Очевидно, экземпляров может быть один или несколько.
Прототип функции *dev_attach()*:
```
int dev_attach( char *drvr,
char *options,
struct cfattach *ca,
void *cfat_arg,
int *single,
struct device **devp,
int (*print)( void *, const char * ) );
```
Ее аргументы:
- **drvr**. Строка, используемая в качестве префикса имени интерфейса. В нашем примере оно указано как "sam", и по умолчанию создается интерфейс "sam0".
- **options**. Строка параметров, переданая драйверу. Она анализируется *dev_attach()* в поисках параметров *name*, *lan* и *unit*, которые переопределяют имя интерфейса по умолчанию. Параметры *lan* и *unit* идентичны по смыслу - они переопределяют число, добавляемое к имени интерфейса. Опция *name* переопределяет аргумент *drvr*.
- **ca**. Указатель на структуру *cfattach*, которая определяет размер структуры устройства, а также detach/attach-функции драйвера. Для инициализации экземпляра этой структуры используется макрос *CFATTACH_DECL()*.
- **cfat_arg**. Аргумент, который передается attach-функции драйвера в качестве третьего аргумента.
- **single**. Если параметр *lan* или *unit* находится в строке параметров, то целое число, на которое указывает *single*, устанавливается равным 1.
- **devp**. Указатель наструктуру `struct device`:
- Если не равно `NULL`, параметр определяет родительское устройство. При завершении драйвера проверяется, что удаляемое устройство не является родителем других устройств. Большинство драйверов устанавливают *devp* в `NULL`.
- Функция *dev_attach()* через *devp* преедает указатель на структуру, созданную функцией для нового устройства. Этот указатель также передается в качестве второго аргумента attach-функции.
- **print**. `NULL` или указатель на функцию отладки. *dev_attach()* будет вызывать ее следующим способом:
```
if ( print != NULL )
(*print)( cfat_arg, NULL );
```
Если возникает ошибка, *dev_attach()* возвращает значение *errno*. В противном случае вызывается attach-функция драйвера, которая должна вернуть `EOK` в случае успеха. При этом *dev_attach()* возвращает значение, переданное attach-функцией.
Функция *dev_attach()* через препроцессор с помощью параметра *&sam_ca* получает указатель на:
```
CFATTACH_DECL( sam, sizeof( struct sam_dev ), NULL, sam_attach, sam_detach, NULL );
```
В конечном счете для каждого инициализируемого экземпляра оборудования **ровно один раз** быдет вызвана функция *sam_attach()*. В общем случае, она решает две задачи: выделение ресурсов, требуемых драйверу и оборудованию, а также регистрация оборудования в стеке.
Функция *sam_attach()* подключается к стеку двумя способами:
- Настройка callout-фукнций в структуре *ifp*. Например, когда стек хочет отправить пакет в сеть, вызывается указатель на callout-фукнцию *ifp->if_start()*. Обратите внимание, что он не имеет никакого отношения к инициализации. В шаблоне драйвера в функции *sam_attach()* указатель *ifp->if_start* устанавливается на адрес функции *sam_start()*.
- Настройка обработки прерываний. Вызвав функцию *interrupt_entry_init()*, стеку передается в качестве параметра указатель на структуру *sc_inter*. Эта структура должна быть размещена в пределах уникального для экземпляра оборудования дескриптора устройства, обслуживаемого драйвером. Структура *sc_inter* включает указатели на функции *sam_process_interrupt()* и *sam_enable_interrupt()*, а также указатель на уникальный дескриптор устройства (*sam*, от названия драйвера - sample).
Обратите внимание, что в драйвере нигде не вызывается *pthread_create()*. Это является важной особенностью сетевого стека. Все потоки в нем порождает лишь сам стек. Драйверный код также должен работать под управлением стека, отсюда явный запрет на создание собственных потоков.
На данном этапе **однократная инициализация** драйвера считается завершенной. При этом, сетевое оборудование еще не является проинициализированным и не готово к приему-передаче пакетов. Для это требуется хотя бы однократный вызов кода, отвечающего за **периодическую инициализацию**. Он вызывается при запуске утилиты *ifconfig*. Также он может быть однократно вызван самим драйвером из функции *sam_attach()* в заключительной ее части. Пример:
```
ifconfig sam0 10.42.107.238
```
В этот момент стек вызовет callout-функцию *ifp->if_init()* (которая указывает на функцию *sam_init()*) для включения аппаратного обеспечения.
Имейте в виду, callout-функция *ifp->if_init()* будет вызываться стеком регулярно при практически каждом вызове утилиты *ifconfig*. Например, при вызове:
```
ifconfig sam0 mtu 8100
```
Таким образом, данная функция должна обеспечить завершение инициализации оборудования. Можно увидеть, что было бы ошибкой устанавливать MTU в функции *sam_attach()*. Функция периодической инициализации *sam_init()* должна постоянно проверять текущую конфигурацию оборудования и корректировать ее в соответствии с требованиями пользователя. При этом было бы ошибкой отключать аппаратуру и инициализировать ее заново, так как даже небольшое изменение конфигурации будет прерывать любые текущие потоки трафика.
Резюмируя сказанное: функция *sam_attach()* вызывается однократно для выделения ресурсов и подключения к сетевому стеку, а *sam_init()* вызывается многократно для настройки и включения оборудования.
Если разрабатывается драйвер для сетевого контроллера на шине PCI, драйверу придется озаботиться вопросом идентификации оборудования посредством VID:DID идентификаторов. В рассматриваемом примере драйвера такого кода по понятным причинам нет. Аналогичные задачи должны быть решены и при обращении к шине USB.
**Обработка прерываний и получение пакетов**
В шаблоне есть две функции *sam_isr()*. Одна использует для работы с прерываниями функцию *InterruptMask()*, а вторая маскирует их через регистры. Вторая может быть немного быстрее. В любом случае *sam_isr()* должна замаскировать прерывание и поставить в очередь сетевого стека фактический обработчик прерывания, вызвав *interrupt_queue()*.
После завершения ISR возвращаемое *interrupt_queue()* значение пробуждает сетевой стек и вызывает callout-функцию драйвера *sam_process_interrupt()*, определенную через указатель *sam->sc_inter.func()*. Функция *sam_process_interrupt()* и является фактическим обработчиком прерывания, который должен обслуживать запросы оборудования: выполнять работу с регистрами, обрабатывать ошибки и т.д. Она также может обслуживать TX-логику оборудования (обычно это не рекомендуется из-за негативного влияния на производительность).
При этом, ISR должен обслуживать RX-логику оборудования. Любой заполненный контроллером входящий пакет должен удаляться из оборудования, а новые пустые пакеты должны возвращаться в оборудование. Заполненные полученные пакеты передаются в стек с помощью callout-функции *ifp->if_input()*.
Возвращаемое ISR значение 0 означает, что функция завершилась не окончив всю работу. Это позволит другим сетевым интерфейсам выполнять обработку своих ISR, помещая *sam_process_interrupt()* в конец очереди выполнения. Возвращение 1 сигнализирует о том, что драйвер завершил обработку прерываний. В этом случае стеком будет вызвана функция *sam_enable_interrupt()*, которая должна размаскировать (включить) прерывания и обратить действия, выполненные *sam_isr()*.
**Отправка пакетов**
Когда сетевой стек хочет передать пакет, он вызывает callout-функцию *ifp->if_start()* драйвера, которая была задана в *dev_attach()* и соответствует *sam_start()*.
В первую очередь стоит убедиться, что имеются аппаратные ресурсы (дескрипторы, буферы и т.д.), доступные для передачи пакетов. Если ресурсы отсутствуют, драйвер должен вернуться из функции *ifp->if_start()*, установив статус `IFF_OACTIVE`:
```
ifp->if_flags_tx |= IFF_OACTIVE;
```
Также необходимо освободить мьютекс передачи, о котором будет сказано ниже.
Если данный флаг установлен стек не будет пытаться вызывать функцию *ifp->if_start()* при добавлении пакета в выходную очередь интерфейса. На этом этапе драйвер определить момент, когда ресурсов станет достаточно (с помощью периодического опроса, или в момент бработки TX-прерывания). После этого драйвер должен захватить мьютекс передачи и снова вызвать функцию начала передачи для отправки данных в очередь вывода.
Большинство драйверов, не возвращают управление из *ifp->if_start()*, пока не закончатся поступающие от стека пакеты или не закончатся аппаратные ресурсы.
Есть несколько удобных макросов, которые можно использовать при этом:
- Макрос *IFQ_POLL()* позволяет определить имеются ли у стека другие доступные для отправки пакеты. Если их нет, обработку исходящих пакетов можно завершить.
- Макрос *IFQ_DEQUEUE()* извлекает из очереди стека первый пакет, готовый к отправке. Некоторые драйверы не используют первый макрос, ожидая, что стек повторно вызовет *ifp->if_start()* при наличии других готовых исходящих пакетов. При извлечении пакета из очереди, его обязательно передать в сеть.
Прежде чем вернуться из этой callout-функции, следует освободить мьютекс передачи:
```
NW_SIGUNLOCK_P( &ifp->if_snd_ex, iopkt_selfp, wtp );
```
Обратите внимание, что в рассматриваемом драйвере функция *ifp->if_start()* вызывает *m_free( m )* для освобождения переданного пакета. Это осуществляется для избежания утечки памяти. Но если сетевая карта функционирует на основе дескрипторов, выполнять это в общем случае не требуется, так как дескрипторы обычно циклически переиспользуются.
Если сетевая карта требует копирования передаваемого пакета в буфер, вызвать *m_free( m )* скорее всего придется. Она сообщит стеку, что буфер доступен для повторного использования и в него можно производить запись новых данных.
Для сетевых карт, ориентированных на дескрипторы, передаваемый пакет не копируется: аппаратное обеспечение выполняет DMA-операцию и вы буфер пакета будет освобожден только после того, как операция завершится. Это позвлит избежать перезаписи данных пакета до его передачи в сеть. В исходном коде таких драйверов можно обнаружить функцию "harvest" или "reap", которая будет проверять переданные дескрипторы и попутно освобождать их буферы.
Это требует, чтобы где-то хранился указатель на переданный пакет (*mbuf*). Часто аппаратное обеспечение имеет несколько свободных байтов в дескрипторе для этой цели. В противном случае следует создать и обслуживать соответствующий массив *mbuf*, который будет индексироваться при освобождении дескрипторов.
Обычно пакеты поступают от стека в виде нескольких буферов. Для TCP-пакетов их количество равно 3, где первый из которых содержит заголовки, второй содержит остатки предыдущего mbuf, а третий содержит начало следующего mbuf. Плохо фрагментированные пакеты могут потребовать копирования в новый непрерывный буфер в зависимости от возможностей оборудования и степени фрагментации буфера. Очевидно, что это негативно влияет на производительность и следует всячески избегать таких ситуаций.
**Периодические таймеры**
Сетевым драйверам часто требуются периодические таймеры для выполнения вспомогательных функций. Например, для обслуживания канала и освобождения TX-дескрипторов. При этом, они не должны создавать собственный поток или асинхронные таймеры средствами ОС. Корректный способ установки периодического таймера в callout-функции *ifp->if_init()* следующий:
```
callout_msec( &dev->mii_callout, 2 * 1000, dev_monitor, dev );
```
Это приведет к вызову функцию *dev_monitor()* потоком сетевого стека по истечении двух секунд. Если требуется периодический таймер, при завершении *dev_monitor()* должна перезапустить таймер. Таким образом, данный таймер не является периодическим. Вероятно, потребуется добавить переменную *run_timer* и очистить ее при остановке таймера, а также вызвать *callout_stop()* и вызывать *callout_msec()* только в конце функции *dev_monitor()*, если эта переменная не установлена. Это позволит исключить состояния гонки, когда *dev_monitor()* не завершилась, когда другой поток выполняет *callout_stop()*, а затем по завершении *dev_monitor()* снова вызывается функция callout_msec(), перезапуская таймер, который предполагается остановить.
Создавать таймеры следует только один раз вызовом *callout_init()*:
```
callout_init( &dev->mii_callout );
```
*callout_msec()* может быть вызван несколько раз. Этот вызов запускает остановленный таймер или сбрасывает работающий. Вызов *callout_stop()* для остановленного таймера не имеет негативных последствий, но вызов *callout_init()* более одного раза приводит к неустранимой ошибке. *callout_init()* обычно используется в callout-функции *ifp->if_attach()*, которая вызывается единожды для каждого устройства. *callout_msec()* используется в *ifp->if_init()*, а также в самом callback-вызове. Поскольку он сбрасывает работающий таймер и запускает остановленный, дальнейшая блокировка не требуется. callout обычно останавливается вызовом *callout_stop()* из функции *ifp->if_stop()*.
Если TX-код вызывается для освобождения дескрипторов, следует заблокировать мьютекс передачи с помощью макроса *NW_SIGLOCK()*. Это позволит избежать повреждения данных и регистров.
**Состояние линка**
Пользователи должны быть уведомлены об изменениях состояния линка. Это осуществляется следующим образом:
```
if_link_state_change( ifp, LINK_STATE_UP );
if_link_state_change( ifp, LINK_STATE_DOWN );
```
**Контроль и управление**
Управление драйвером осуществляется с помощью callout-функции *ifp->if_ioctl()*, соответствующей *sam_ioctl()*.
Данная функция может быть пустой или довольно сложной, в зависимости от перечня поддерживаемых функций. Для совместимости с утилитой *nicinfo* (например, для успешного выполнения *nicinfo sam0*) следует добавить поддержку команд `SIOCGDRVCOM` `DRVCOM_CONFIG`/`DRVCOM_STATS`. Для поддержки аппаратного рассчета контрольных сумм следует поддержать команду `SIOCSIFCAP`.
Для отображения скорость канала (режима работы линка) и дуплекс с помощью:
```
ifconfig -v
```
следует поддержать команды `SIOCGIFMEDIA` и `SIOCSIFMEDIA`. Они также могут использоваться для установки данных параметров. Драйверы, поддерживающие настройку параметров среды передачи, имеют в своем составе файл с именем *bsd_media.c*. Для вывода перечня поддерживаемых режимов используется вызов:
```
ifconfig -m
```
Кроме того, *ioctl()* позволяет многоадресную передачу. В *sam.c* имеется пример работы с такими адресами (см. использование макросов *ETHER_FIRST_MULTI()* и *ETHER_NEXT_MULTI()*).
**Завершение драйвера**
Возможны следующие сценарии завершения драйвера:
- **Команда *ifconfig sam0 down*, приводящая к вызову callout-функции *sam_stop()***. Сценарий должен остановить все операции приема и передачи, а также очистить все используемые буферы (чтобы данные в них не могли появиться при следующем включении интерфейса). Обратите внимание, что кроме буферов и Tx/Rx передач, остальные структуры драйвера и аппаратные ресурсы освобождать не следует. Следующее обращение к драйверу, скорее всего, будет вызвано командой *ifconfig up*, что приведет к исполнению *sam_init()* и повторной инициализации оборудования.
- **Команда *ifconfig sam0 destroy*, приводящая к вызову callout-функции *sam_detach()***. Сценарий должен сбросить оборудование и освободить все ресурсы. Ожидается, что драйвер в скором времени будет выгружен из сетевого стека, но сам стек продолжит работать. Типичный тест корректности обработки данного сценария включает: в цикле осуществлять монтирование драйвера, вызов *ifconfig* с указанием адреса, выполнить несколько операций передачи трафика и выполнение *ifconfig ... destroy*. Корректно функционирующий драйвер не должен приводить к падению стека, утечкам памяти и невозможности передачи трафика.
- **Завершение сетевого стека или аварийное завершение с вызовом callout-функции *sam_shutdown()***. Сценарий должен сбросить оборудование, чтобы остановить любые операции (включая DMA). При этом следует избегать любого освобождения драйверных ресурсов, поскольку это может привести к рекурсивному вызову callout-а (маскируя первоначальную причину в дампе аварийного завершения процесса).
Функция *sam_detach()* для драйвера определяется уже известным образом:
```
CFATTACH_DECL( sam, sizeof( struct sam_dev ), NULL, sam_attach, sam_detach, NULL );
```
А вот аварийный callout-вызов *sam_shutdown()* определяется чуть сложнее:
```
sam->sc_sdhook = shutdownhook_establish( sam_shutdown, sam );
```
Важно не забыть установить этот хук в callout-функции *sam_attach()*, а также удалить его в *sam_detach()* с помощью:
```
shutdownhook_disestablish( sam->sc_sdhook );
```
**Краткосрочные ожидания в драйвере**
При обслуживании оборудования драйверу может регулярно требоваться выполнить непольшое ожидание. Как было сказано ранее, в сетевом драйвере все функции вызываются из специализированных потоков сетевого стека и они не подлежат управлению со стороны драйвера. При обслуживании драйвером нескольких интерфейсов это может стать причиной проблем, когда ожидание на одном интерфейсе влияет на потоки данных на другом.
Сетевой стек использует асинхронные псевдо-потоки, чтобы избежать излишних блокировок. Единственный сценарий, в котором задержка невозможна — это периодические таймеры (рассмотрены ранее). Единственным способом задержки в этих условиях является установка нового таймера. При запуске сетевого стека этот механизм еще не запущен и допустимо использовать стандартный механизм задержки.
Пример задержки на 0.5 секунд:
```
if ( !ISSTART && ISSTACK )
{
/*
* Called from an io-pkt thread and not at startup so can't use normal delay,
* work out what type of delay to use.
*/
if ( curproc == stk_ctl.proc0 )
{
/*
* Called from a callout, can only do another callout. If ltsleep is tried
* it returns success without actually sleeping.
*/
callout_msec( &dev->delay_callout, 500, next_part, dev );
return;
}
/* Normal io-pkt thread case. Use ltsleep to avoid blocking other interfaces */
timo = hz / 2;
ltsleep( &wait, 0, "delay", timo, NULL );
} else {
/*
* Either io-pkt is starting up or called from a different
* thread so will not block other interfaces. Just use delay.
*/
delay(500);
}
```
**Многопоточность**
Драйвер не должен создавать собственные потоки и должен работать под правлением потоков сетевого стека. Однако, бывают ситуации, когда драйверу действительно требуется отдельный поток (например, для обслуживания взаимодействия по USB или SDIO). Создавать стандартные потоки с помощью pthread_create() не рекомендуется, поскольку они не будут связаны с обработкой `mbufs`. В случае **острой необходимости** потоки обработки `mbuf` должны быть созданы с помощью *nw_pthread_create()* и ни в коем случае через *pthread_create()*:
```
nw_pthread_create( &tid, NULL, thread_fn, dev, 0, thread_init_fn, dev );
```
При этом, потоковая функция должна установить отдельное имя потоку, чтобы отличить его от стандартных потоков стека. Кроме того, следует также установить callout-обработчик *wtp->quiesce_callout()*. Допустимо выполнение и других инициализаций:
```
static int thread_init_fn( void *arg )
{
struct nw_work_thread *wtp;
dev_handle_t *dev = (dev_handle_t *)arg;
pthread_setname_np( 0, "My driver thread" );
wtp = WTP;
wtp->quiesce_callout = thread_quiesce;
wtp->quiesce_arg = dev;
return (EOK);
}
```
Имеет смысл назначать имя потока исходя из принадлежности конкретному драйверу и контуру обработки. Например:
```
# pidin -p io-pkt-v4 thread
pid name thread name STATE Blocked
4100 sbin/io-pkt-v4 io-pkt main SIGWAITINFO
4100 sbin/io-pkt-v4 io-pkt#0x00 RECEIVE 1
4100 sbin/io-pkt-v4 asixx Rx RECEIVE 22
```
В примере есть следующие потоки:
- **io-pkt main**. Используется для обработчика сигналов и обработки блокирующих запросов.
- **io-pkt#0x00**. Поток для выполнения основной работы сетевого стека. Другие нумерованные потоки создаются стеком для работы на отдельных процессорных ядрах и при дополнительных вызовах *interrupt_entry_init()*.
- **asixx Rx**. Драйверный поток, ассоциированный с *devnp-asixx.so* и его Rx-контуром. Его задачей является обработка специальных пакетов с малой задержкой, когда стек занят обслуживанием других запросов. Отсутствие у потока имени приведет к тому, что он автоматически получит нумерование и не будет отличим от потоков второго типа.
Сценарии вызова callout-обработчика *wtp->quiesce_callout()*:
- Стеку необходимо изменить некоторые структуры (например, при других вызовах *nw_pthread_create()*), что требует, чтобы все остальные потоки были заблокированы, пока обновление не завершится.
- При терминировании потоков, например, во время завершения сетевого стека.
Параметр *die* используется для определения одного из этих сценариев. Обратите внимание, что сама callout-функция вызывается из потока стека и должна уведомить драйверный поток о вызове *quiesce_block()* через глобальные переменные или сообщение-импульс. Синтетический пример, использующий глобальные переменные:
```
static int quiescing = 0;
static int quiesce_die = 0;
static void thread_quiesce( void *arg, int die )
{
dev_handle_t *dev = (dev_handle_t *)arg;
quiescing = 1;
quiesce_die = die;
}
static void *thread_fn( void *arg )
{
while ( 1 )
{
if ( quiescing )
{
if ( quiesce_die )
{
/* Thread will terminate on calling quiesce_block(), clean up here if required. */
}
quiesce_block( quiesce_die );
quiescing = 0;
}
/* Do normal thread work */
}
}
```
Если вызывается detach-функция драйвера, функция *quiesce_all()* вызывается стеком. Это может вызвать проблемы в других драйверах, если detach-функция выполняется длительное времени (например, много вызовов *nic_delay()*). В этом случае драйвер должен самостоятельно приостанавливаться (вызывать quiesce-функции), чтобы свести к минимуму влияние, которое он может оказать на другие сетевые драйверы. Если драйвер будет это выполнять, необходимо установить соответствующий флаг в attach-функции:
```
sam->dev.dv_flags |= DVF_QUIESCESELF;
```
Затем он может вызывать функции приостановки в detach-функции:
```
/* self quiesce */
quiesce_all();
ether_ifdetach( ifp );
if_detach( ifp );
unquiesce_all();
```
**Функция отсоединения (detach-функции)**
Одной из обязанностей detach-функции драйвера является определение необходимости отмонтирования драйвера. Она вызывается для каждого устройства, обслуживаемого драйвером. Таким образом, отмонтирование должно происходить при обработке последнего из устройств. Драйвер сам определяет способ отслеживания доступных устройств.
Если драйвер определяет, что отмонтирование преждевременно, следует выполнить следующее:
```
sam->dev.dv_dll_hdl = NULL;
```
В этой callout-функции часто необходимо использовать *nic_delay()* или другой вызов, который может передать контекст стека другому потоку. Контекст стека не должен быть передан после того, как драйвер внутренне пометил устройство как удаленное (уменьшен счетчик наличия устройств или устройство удалено из списка устройств). Если контекст стека передается detach-функции другого устройства, драйвер может быть выгружен, пока первое устройство еще завершает отсоединение. Это приведет к сбою. Аналогичная проблема может возникнуть, если attach-функция драйвера передаст контекст стека перед маркировкой устройства в качестве доступного.

3
hardware/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1,3 @@
LIST=HARDWARE
EARLY_DIRS=devn
include recurse.mk

2
hardware/devnp/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1,2 @@
LIST=DEVNP
include recurse.mk

2
hardware/devnp/e100/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1,2 @@
LIST=CPU
include recurse.mk

2
hardware/devnp/e100/arm/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1,2 @@
LIST=VARIANT
include recurse.mk

1
hardware/devnp/e100/arm/dll.le.v7/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1 @@
include ../../common.mk

1
hardware/devnp/e100/arm/dll.le/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1 @@
include ../../common.mk

245
hardware/devnp/e100/bsd_media.c Обычный файл
Просмотреть файл

@ -0,0 +1,245 @@
/*
* (c) 2016-2017, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*****************************************************************************
* *
* Driver for the Intel 8255x 10/100 Mbps Ethernet Controller Family *
* Network controller bsd media routines *
* *
*****************************************************************************/
#include "e100.h"
//
// this is a callback, made by the bsd media code. We passed
// a pointer to this function during the ifmedia_init() call
// in bsd_mii_initmedia()
//
void bsd_mii_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
{
e100_dev_t *e100 = ifp->if_softc;
e100->bsd_mii.mii_media_active = IFM_ETHER;
e100->bsd_mii.mii_media_status = IFM_AVALID;
if (e100->force_advertise != -1) { // link is forced
if (e100->cfg.flags & NIC_FLAG_LINK_DOWN) {
e100->bsd_mii.mii_media_active |= IFM_NONE;
e100->bsd_mii.mii_media_status = 0;
} else { // link is up
e100->bsd_mii.mii_media_status |= IFM_ACTIVE;
switch(e100->cfg.media_rate) {
case 0:
e100->bsd_mii.mii_media_active |= IFM_NONE;
break;
case 1000*10:
e100->bsd_mii.mii_media_active |= IFM_10_T;
break;
case 1000*100:
e100->bsd_mii.mii_media_active |= IFM_100_TX;
break;
#if 0
case 1000*1000:
e100->bsd_mii.mii_media_active |= IFM_1000_T;
break;
#endif
default: // this shouldnt really happen, but ...
e100->bsd_mii.mii_media_active |= IFM_NONE;
break;
}
if (e100->cfg.duplex) {
e100->bsd_mii.mii_media_active |= IFM_FDX;
}
}
} else if (!(e100->cfg.flags & NIC_FLAG_LINK_DOWN)) { // link is auto-detect and up
e100->bsd_mii.mii_media_status |= IFM_ACTIVE;
switch(e100->cfg.media_rate) {
case 1000*10:
e100->bsd_mii.mii_media_active |= IFM_10_T;
break;
case 1000*100:
e100->bsd_mii.mii_media_active |= IFM_100_TX;
break;
#if 0
case 1000*1000:
e100->bsd_mii.mii_media_active |= IFM_1000_T;
break;
#endif
default: // this shouldnt really happen, but ...
e100->bsd_mii.mii_media_active |= IFM_NONE;
break;
}
if (e100->cfg.duplex) {
e100->bsd_mii.mii_media_active |= IFM_FDX;
}
// could move this to mii.c so there was no lag
ifmedia_set(&e100->bsd_mii.mii_media, IFM_ETHER|IFM_AUTO);
} else { // link is auto-detect and down
e100->bsd_mii.mii_media_active |= IFM_NONE;
e100->bsd_mii.mii_media_status = 0;
// could move this to mii.c so there was no lag
ifmedia_set(&e100->bsd_mii.mii_media, IFM_ETHER|IFM_NONE);
}
// stuff parameter values with hoked-up bsd values
ifmr->ifm_status = e100->bsd_mii.mii_media_status;
ifmr->ifm_active = e100->bsd_mii.mii_media_active;
}
//
// this is a callback, made by the bsd media code. We passed
// a pointer to this function during the ifmedia_init() call
// in bsd_mii_initmedia(). This function is called when
// someone makes an ioctl into us, we call into the generic
// ifmedia source, and it make this callback to actually
// force the speed and duplex, just as if the user had
// set the cmd line options
//
int bsd_mii_mediachange(struct ifnet *ifp)
{
e100_dev_t *e100 = ifp->if_softc;
int old_media_rate = e100->cfg.media_rate;
int old_duplex = e100->cfg.duplex;
struct ifmedia *ifm = &e100->bsd_mii.mii_media;
int user_duplex = ifm->ifm_media & IFM_FDX ? 1 : 0;
int user_media = ifm->ifm_media & IFM_TMASK;
if (!(ifp->if_flags & IFF_UP)) {
if (e100->cfg.verbose)
slogf(_SLOGC_NETWORK, _SLOG_WARNING, "%s(): isn't up, ioctl ignored", __devname__);
return 0;
}
if (!(ifm->ifm_media & IFM_ETHER)) {
if (e100->cfg.verbose)
slogf(_SLOGC_NETWORK, _SLOG_WARNING, "%s(): interface - bad media: 0x%X",
__devname__, ifm->ifm_media);
return 0; // should never happen
}
switch (user_media) {
case IFM_AUTO: // auto-select media
e100->force_advertise = -1;
e100->cfg.media_rate = -1;
e100->cfg.duplex = -1;
ifmedia_set(&e100->bsd_mii.mii_media, IFM_ETHER|IFM_AUTO);
break;
case IFM_NONE: // disable media
//
// forcing the link with a speed of zero means to disable the link
//
e100->force_advertise = 0;
e100->cfg.media_rate = 0;
e100->cfg.duplex = 0;
ifmedia_set(&e100->bsd_mii.mii_media, IFM_ETHER|IFM_NONE);
break;
case IFM_10_T: // force 10baseT
e100->force_advertise = user_duplex ? MDI_10bTFD : MDI_10bT;
e100->cfg.media_rate = 10 * 1000;
e100->cfg.duplex = user_duplex;
ifmedia_set(&e100->bsd_mii.mii_media,
user_duplex ? IFM_ETHER|IFM_10_T|IFM_FDX : IFM_ETHER|IFM_10_T);
break;
case IFM_100_TX: // force 100baseTX
e100->force_advertise = user_duplex ? MDI_100bTFD : MDI_100bT;
e100->cfg.media_rate = 100 * 1000;
e100->cfg.duplex = user_duplex;
ifmedia_set(&e100->bsd_mii.mii_media,
user_duplex ? IFM_ETHER|IFM_100_TX|IFM_FDX : IFM_ETHER|IFM_100_TX);
break;
#if 0
case IFM_1000_T: // force 1000baseT
//
// N.B. I have not had good luck, trying to get gige to work half
// duplex. Even with different gige switches, I can only force full duplex
//
e100->force_advertise = user_duplex ? MDI_1000bTFD : MDI_1000bT;
e100->cfg.media_rate = 1000 * 1000;
e100->cfg.duplex = user_duplex;
ifmedia_set(&e100->bsd_mii.mii_media,
user_duplex ? IFM_ETHER|IFM_1000_T|IFM_FDX : IFM_ETHER|IFM_1000_T);
break;
#endif
default: // should never happen
if (e100->cfg.verbose)
slogf(_SLOGC_NETWORK, _SLOG_WARNING, "%s(): - unknown media: 0x%X", __devname__, user_media);
return 0;
break;
}
// does the user want something different than it already is?
if ((e100->cfg.media_rate != old_media_rate) ||
(e100->cfg.duplex != old_duplex) ||
(e100->cfg.flags & NIC_FLAG_LINK_DOWN) ) {
// re-initialize hardware with new parameters
ifp->if_init(ifp);
}
return 0;
}
void bsd_mii_initmedia(e100_dev_t *e100)
{
e100->bsd_mii.mii_ifp = &e100->ecom.ec_if;
ifmedia_init(&e100->bsd_mii.mii_media, IFM_IMASK, bsd_mii_mediachange,
bsd_mii_mediastatus);
// we do NOT call mii_attach() - we do our own link management
//
// must create these entries to make ifconfig media work
// see net/if_media.h for defines
//
// ifconfig fxp0 media none (x22)
ifmedia_add(&e100->bsd_mii.mii_media, IFM_ETHER|IFM_NONE, 0, NULL);
// ifconfig fxp0 media auto (x20)
ifmedia_add(&e100->bsd_mii.mii_media, IFM_ETHER|IFM_AUTO, 0, NULL);
// ifconfig fxp0 media 10baseT (x23 - half duplex)
ifmedia_add(&e100->bsd_mii.mii_media, IFM_ETHER|IFM_10_T, 0, NULL);
// ifconfig fxp0 media 10baseT-FDX (x100023)
ifmedia_add(&e100->bsd_mii.mii_media, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);
// ifconfig fxp0 media 100baseTX (x26 - half duplex)
ifmedia_add(&e100->bsd_mii.mii_media, IFM_ETHER|IFM_100_TX, 0, NULL);
// ifconfig fxp0 media 100baseTX-FDX (x100026 - full duplex)
ifmedia_add(&e100->bsd_mii.mii_media, IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL);
#if 0
// ifconfig fxp0 media 1000baseT (x30 - half duplex)
ifmedia_add(&e100->bsd_mii.mii_media, IFM_ETHER|IFM_1000_T, 0, NULL);
// ifconfig fxp0 media 1000baseT mediaopt fdx (x100030 - full duplex)
ifmedia_add(&e100->bsd_mii.mii_media, IFM_ETHER|IFM_1000_T|IFM_FDX, 0, NULL);
#endif
// add more entries to support flow control via ifconfig media
// link is initially down
ifmedia_set(&e100->bsd_mii.mii_media, IFM_ETHER|IFM_NONE);
}

39
hardware/devnp/e100/common.mk Обычный файл
Просмотреть файл

@ -0,0 +1,39 @@
#
# (c) 2017-2019, SWD Embedded Systems Limited, http://www.kpda.ru
#
ifndef QCONFIG
QCONFIG=qconfig.mk
endif
include $(QCONFIG)
ISKERNEL := 1
include ../../../../../netdrivers.mk
LIBS = drvrS cacheS
LIBS += $(foreach libpath,$(LIBVPATH), $(if $(wildcard $(libpath)/libnetdrvrS.a), netdrvrS) )
#CCFLAGS + -Wfatal-errors
CCFLAGS += -Werror
# CCFLAGS += -DE100_DEBUG_LEVEL=1
CCFLAGS += -DRFD_ALIGNMENT_FUDGE=4
FW_BLOB_SRCS = fw_blob.s
FW_BLOB_OBJS = fw_blob.o
EXTRA_OBJS += $(FW_BLOB_OBJS)
EXTRA_CLEAN += $(FW_BLOB_SRCS) firmwares.h
NAME = devnp-$(PROJECT)
USEFILE=$(PROJECT_ROOT)/$(NAME).use
define PINFO
PINFO DESCRIPTION=Driver for the Intel 8255x 10/100 Mbps Ethernet Controller Family
endef
include $(MKFILES_ROOT)/qtargets.mk
$(PROJECT_ROOT)/e100_fw.c: $(FW_BLOB_SRCS)
$(FW_BLOB_SRCS): $(PROJECT_ROOT)/firmware/mkblob.sh
$(PROJECT_ROOT)/firmware/mkblob.sh $@ firmwares.h $(wildcard $(PROJECT_ROOT)/firmware/*.bin)

79
hardware/devnp/e100/devctl.c Обычный файл
Просмотреть файл

@ -0,0 +1,79 @@
/*
* (c) 2016-2017, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*****************************************************************************
* *
* Driver for the Intel 8255x 10/100 Mbps Ethernet Controller Family *
* Network controller DEVCTL routines *
* *
*****************************************************************************/
#include "e100.h"
int e100_ioctl(struct ifnet * ifp, unsigned long cmd, caddr_t data)
{
int error = 0;
e100_dev_t *e100 = ifp->if_softc;
struct drvcom_config *dcfgp;
struct drvcom_stats *dstp;
struct ifdrv_com *ifdc;
switch (cmd) {
case SIOCGDRVCOM:
ifdc = (struct ifdrv_com *)data;
switch (ifdc->ifdc_cmd) {
case DRVCOM_CONFIG:
dcfgp = (struct drvcom_config *)ifdc;
if (ifdc->ifdc_len != sizeof(nic_config_t)) {
error = EINVAL;
break;
}
memcpy(&dcfgp->dcom_config, &e100->cfg, sizeof(e100->cfg));
break;
case DRVCOM_STATS:
dstp = (struct drvcom_stats *)ifdc;
if (ifdc->ifdc_len != sizeof(nic_stats_t)) {
error = EINVAL;
break;
}
memcpy(&dstp->dcom_stats, &e100->stats, sizeof(e100->stats));
if (e100->cfg.verbose > 1) {
e100_hw_dump_registers(e100, 0);
}
break;
default:
error = ENOTTY;
}
break;
case SIOCSIFMEDIA:
case SIOCGIFMEDIA: {
struct ifreq *ifr = (struct ifreq *)data;
error = ifmedia_ioctl(ifp, ifr, &e100->bsd_mii.mii_media, cmd);
break;
}
default:
error = ether_ioctl(ifp, cmd, data);
if (error == ENETRESET) {
/*
* Multicast list has changed; set the
* hardware filter accordingly.
*/
if (ifp->if_init != NULL)
ifp->if_init(ifp);
error = 0;
}
break;
}
return error;
}

32
hardware/devnp/e100/devnp-e100.use Обычный файл
Просмотреть файл

@ -0,0 +1,32 @@
%C Driver for the Intel 8255x 10/100 Mbps Ethernet Controller Family
Syntax:
io-pkt-v4 -d e100 [option[,option ...]] ... &
Options (to override autodetected defaults):
duplex=0|1 Half (0) or full (1) duplex mode. Default autodetect.
did=0xXXXX PCI device ID.
irq=num IRQ of the interface. Default autodetect.
kermask=0|1 (1) Use kernel interrupt masking methodology
(0) manually mask the nic in the interrupt handler
mac=XXXXXXXXXXXX Interface address of the controller. Default eeprom.
use_io Use IO mapped registers (default: memory mapped)
pci=0xXXXX PCI index of the controller.
phy=num Address of connected PHY device. Default autodetect.
receive=X Set the number of rx descriptors
(min 16, default 256, max 2048).
speed=10|100 Media data rate in Megabits/Second. Default autodetect.
transmit=X Set the number of tx descriptors
(min 64, default 1024, max 2048).
verbose=N Set verbosity level. (default 0)
vid=0xXXXX PCI vendor ID. Default 0x8086.
typed_mem=tmem Use typed memory area
Note: if the duplex is specified via the "duplex" option, you should
also specify the speed using the "speed" option.
Examples:
# Start v4 TCP/IP io-pkt using the e100 driver:
io-pkt-v4 -d e100
ifconfig fxp0 10.184

695
hardware/devnp/e100/e100.h Обычный файл
Просмотреть файл

@ -0,0 +1,695 @@
/*
* (c) 2017-2019, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*****************************************************************************
* *
* Driver for the Intel 8255x 10/100 Mbps Ethernet Controller Family *
* Network controller main header *
* *
*****************************************************************************/
#ifndef _E100_H
#define _E100_H
#include <io-pkt/iopkt_driver.h>
#include <stdio.h>
#include <errno.h>
#include <atomic.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/siginfo.h>
#include <sys/syspage.h>
#include <sys/neutrino.h>
#include <sys/mbuf.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/if_ether.h>
#include <net/if_media.h>
#include <sys/io-pkt.h>
#include <sys/cache.h>
#include <sys/callout.h>
#include <hw/inout.h>
#if _KPDA_VERSION > 20180500
#include <netdrvr/mdi.h>
#include <netdrvr/eth.h>
#include <netdrvr/nicsupport.h>
#else
#include <drvr/mdi.h>
#include <drvr/eth.h>
#include <drvr/nicsupport.h>
#endif
#include <hw/nicinfo.h>
#include <hw/pci.h>
#include <nw_pci.h>
#include <hw/pci_devices.h>
#include <sys/device.h>
#include <quiesce.h>
#include <siglock.h>
#include <dev/mii/miivar.h>
#include <sys/slog.h>
#include <sys/slogcodes.h>
#include "bpfilter.h"
#if NBPFILTER > 0
#include <net/bpf.h>
#include <net/bpfdesc.h>
#endif
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <net/if_vlanvar.h>
#include <net/ifdrvcom.h>
#include <sys/sockio.h>
#include <sys/malloc.h>
#include <device_qnx.h>
#if E100_DEBUG_LEVEL > 0
# define E100_DEBUG(x) if ( e100->cfg.verbose >= E100_DEBUG_LEVEL ) { x; }
# if E100_DEBUG_LEVEL > 1
# define E100_DEBUG2(x) x
# else
# define E100_DEBUG2(x)
# endif
# define RXID(__rx) (((void *)(__rx) - (void *)e100->rxs) / sizeof(struct rx))
#else
# define E100_DEBUG(x)
# define E100_DEBUG2(x)
#endif
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef u16 __le16;
typedef u32 __le32;
typedef u32 dma_addr_t;
#ifndef false
typedef int bool;
#define true (1==1)
#define false (1==0)
#endif
#define FULL 1
#define LITE 0
#define udelay(__usec) nanospin_ns((__usec)*1000);
#define msleep(__msec) delay((__msec))
#define msleep_interruptible msleep
#define cpu_to_le16(__val) ENDIAN_LE16((u16)(__val))
#define cpu_to_le32(__val) ENDIAN_LE32((u32)(__val))
#define le16_to_cpu cpu_to_le16
#define le32_to_cpu cpu_to_le32
#ifdef __X86__
#define iowrite8(__val, __port) ( \
!e100->use_io ? (*(volatile uint8_t *)(__port) = (__val)) : \
out8((uintptr_t)(__port), (__val)) \
)
#define iowrite16(__val, __port) ( \
!e100->use_io ? (*(volatile uint16_t *)(__port) = cpu_to_le16(__val)): \
out16((uintptr_t)(__port), cpu_to_le16(__val)) \
)
#define iowrite32(__val, __port) ( \
!e100->use_io ? (*(volatile uint32_t *)(__port) = cpu_to_le32(__val)): \
out32((uintptr_t)(__port), cpu_to_le32(__val)) \
)
#define ioread8(__port) ( \
!e100->use_io ? *(volatile uint8_t *)(__port) : \
in8((uintptr_t)(__port)) \
)
#define ioread16(__port) ( \
!e100->use_io ? le16_to_cpu(*(volatile uint16_t *)(__port)) : \
le16_to_cpu(in16((uintptr_t)(__port))) \
)
#define ioread32(__port) ( \
!e100->use_io ? le32_to_cpu(*(volatile uint32_t *)(__port)) : \
le32_to_cpu(in32((uintptr_t)(__port))) \
)
#else
#define iowrite8(__val, __port) (out8((uintptr_t)(__port), (__val)))
#define iowrite16(__val, __port) (out16((uintptr_t)(__port), cpu_to_le16(__val)))
#define iowrite32(__val, __port) (out32((uintptr_t)(__port), cpu_to_le32(__val)))
#define ioread8(__port) (in8((uintptr_t)(__port)))
#define ioread16(__port) (le16_to_cpu(in16((uintptr_t)(__port))))
#define ioread32(__port) (le32_to_cpu(in32((uintptr_t)(__port))))
#endif
#define dma_wmb() __cpu_membarrier()
#define dma_rmb() __cpu_membarrier()
#define FROM_POINTER_TO_INT(p, type) ( (type)(((uintptr_t)(p)) & ((1ULL << (sizeof(type) * 8)) - 1)) )
struct pci_device_id {
u32 vendor, device; /* Vendor and device ID or PCI_ANY_ID*/
u32 driver_data; /* Data private to the driver */
};
struct firmware_list {
uint32_t start;
// uint32_t end;
size_t size;
char *name;
};
struct firmware {
size_t size;
const u8 *data;
};
enum mac {
mac_82557_D100_A = 0,
mac_82557_D100_B = 1,
mac_82557_D100_C = 2,
mac_82558_D101_A4 = 4,
mac_82558_D101_B0 = 5,
mac_82559_D101M = 8,
mac_82559_D101S = 9,
mac_82550_D102 = 12,
mac_82550_D102_C = 13,
mac_82551_E = 14,
mac_82551_F = 15,
mac_82551_10 = 16,
mac_unknown = 0xFF,
};
enum phy {
phy_100a = 0x000003E0,
phy_100c = 0x035002A8,
phy_82555_tx = 0x015002A8,
phy_nsc_tx = 0x5C002000,
phy_82562_et = 0x033002A8,
phy_82562_em = 0x032002A8,
phy_82562_ek = 0x031002A8,
phy_82562_eh = 0x017002A8,
phy_82552_v = 0xd061004d,
phy_unknown = 0xFFFFFFFF,
};
/* CSR (Control/Status Registers) */
struct csr {
struct {
u8 status;
u8 stat_ack;
u8 cmd_lo;
u8 cmd_hi;
u32 gen_ptr;
} scb;
u32 port;
u16 flash_ctrl;
u8 eeprom_ctrl_lo;
u8 eeprom_ctrl_hi;
u32 mdi_ctrl;
u32 rx_dma_count;
};
enum scb_status {
rus_no_res = 0x08,
rus_ready = 0x10,
rus_mask = 0x3C,
};
enum ru_state {
RU_SUSPENDED = 0,
RU_RUNNING = 1,
RU_UNINITIALIZED = -1,
};
enum scb_stat_ack {
stat_ack_not_ours = 0x00,
stat_ack_sw_gen = 0x04,
stat_ack_mdi_done = 0x08,
stat_ack_rnr = 0x10,
stat_ack_cu_idle = 0x20,
stat_ack_frame_rx = 0x40,
stat_ack_cu_cmd_done = 0x80,
stat_ack_not_present = 0xFF,
stat_ack_rx = (stat_ack_sw_gen | stat_ack_rnr | stat_ack_frame_rx),
stat_ack_tx = (stat_ack_cu_idle | stat_ack_cu_cmd_done),
};
enum scb_cmd_hi {
irq_mask_none = 0x00,
irq_mask_all = 0x01,
irq_sw_gen = 0x02,
};
enum scb_cmd_lo {
cuc_nop = 0x00,
ruc_start = 0x01,
ruc_resume = 0x02,
ruc_abort = 0x04,
ruc_load_base = 0x06,
cuc_start = 0x10,
cuc_resume = 0x20,
cuc_dump_addr = 0x40,
cuc_dump_stats = 0x50,
cuc_load_base = 0x60,
cuc_dump_reset = 0x70,
};
enum cuc_dump {
cuc_dump_complete = 0x0000A005,
cuc_dump_reset_complete = 0x0000A007,
};
enum port {
software_reset = 0x0000,
selftest = 0x0001,
selective_reset = 0x0002,
};
enum eeprom_ctrl_lo {
eesk = 0x01,
eecs = 0x02,
eedi = 0x04,
eedo = 0x08,
};
enum mdi_ctrl {
mdi_write = 0x04000000,
mdi_read = 0x08000000,
mdi_ready = 0x10000000,
};
enum eeprom_op {
op_write = 0x05,
op_read = 0x06,
op_ewds = 0x10,
op_ewen = 0x13,
};
enum eeprom_offsets {
eeprom_cnfg_mdix = 0x03,
eeprom_map_cntr = 0x05,
eeprom_phy_iface = 0x06,
eeprom_id = 0x0A,
eeprom_config_asf = 0x0D,
eeprom_smbus_addr = 0x90,
};
enum eeprom_cnfg_mdix {
eeprom_mdix_enabled = 0x0080,
};
enum eeprom_phy_iface {
NoSuchPhy = 0,
I82553AB,
I82553C,
I82503,
DP83840,
S80C240,
S80C24,
II82555,
DP83840A = 10,
};
enum eeprom_id {
eeprom_id_wol = 0x0020,
};
enum eeprom_config_asf {
eeprom_asf = 0x8000,
eeprom_gcl = 0x4000,
};
enum cb_status {
cb_complete = 0x8000,
cb_ok = 0x2000,
};
/**
* cb_command - Command Block flags
* @cb_tx_nc: 0: controller does CRC (normal), 1: CRC from skb memory
*/
enum cb_command {
cb_nop = 0x0000,
cb_iaaddr = 0x0001,
cb_config = 0x0002,
cb_multi = 0x0003,
cb_tx = 0x0004,
cb_ucode = 0x0005,
cb_dump = 0x0006,
cb_tx_sf = 0x0008,
cb_tx_nc = 0x0010,
cb_cid = 0x1f00,
cb_i = 0x2000,
cb_s = 0x4000,
cb_el = 0x8000,
};
struct rfd {
__le16 status;
__le16 command;
__le32 link;
__le32 rbd;
__le16 actual_size;
__le16 size;
};
struct rx {
struct rx *next, *prev;
struct mbuf *m;
dma_addr_t dma_addr;
};
enum destroy_variants {
DESTROY_ALL = -1,
ATTACH_DONE,
PCI_ENABLE_DONE,
EVCNT_DONE,
CACHE_INIT_DONE,
MMAP_CSR_DONE,
PCI_MASTER_DONE,
MMAP_E100_DONE,
ALLOC_CBS_DONE,
ALLOC_RXS_DONE,
INTER_ENTRY_DONE,
IF_SETUP_DONE
};
#if defined(__BIGENDIAN__)
#define X(a,b) b,a
#else
#define X(a,b) a,b
#endif
struct config {
/*0*/ u8 X(byte_count:6, pad0:2);
/*1*/ u8 X(X(rx_fifo_limit:4, tx_fifo_limit:3), pad1:1);
/*2*/ u8 adaptive_ifs;
/*3*/ u8 X(X(X(X(mwi_enable:1, type_enable:1), read_align_enable:1),
term_write_cache_line:1), pad3:4);
/*4*/ u8 X(rx_dma_max_count:7, pad4:1);
/*5*/ u8 X(tx_dma_max_count:7, dma_max_count_enable:1);
/*6*/ u8 X(X(X(X(X(X(X(late_scb_update:1, direct_rx_dma:1),
tno_intr:1), cna_intr:1), standard_tcb:1), standard_stat_counter:1),
rx_save_overruns : 1), rx_save_bad_frames : 1);
/*7*/ u8 X(X(X(X(X(rx_discard_short_frames:1, tx_underrun_retry:2),
pad7:2), rx_extended_rfd:1), tx_two_frames_in_fifo:1),
tx_dynamic_tbd:1);
/*8*/ u8 X(X(mii_mode:1, pad8:6), csma_disabled:1);
/*9*/ u8 X(X(X(X(X(rx_tcpudp_checksum:1, pad9:3), vlan_arp_tco:1),
link_status_wake:1), arp_wake:1), mcmatch_wake:1);
/*10*/ u8 X(X(X(pad10:3, no_source_addr_insertion:1), preamble_length:2),
loopback:2);
/*11*/ u8 X(linear_priority:3, pad11:5);
/*12*/ u8 X(X(linear_priority_mode:1, pad12:3), ifs:4);
/*13*/ u8 ip_addr_lo;
/*14*/ u8 ip_addr_hi;
/*15*/ u8 X(X(X(X(X(X(X(promiscuous_mode:1, broadcast_disabled:1),
wait_after_win:1), pad15_1:1), ignore_ul_bit:1), crc_16_bit:1),
pad15_2:1), crs_or_cdt:1);
/*16*/ u8 fc_delay_lo;
/*17*/ u8 fc_delay_hi;
/*18*/ u8 X(X(X(X(X(rx_stripping:1, tx_padding:1), rx_crc_transfer:1),
rx_long_ok:1), fc_priority_threshold:3), pad18:1);
/*19*/ u8 X(X(X(X(X(X(X(addr_wake:1, magic_packet_disable:1),
fc_disable:1), fc_restop:1), fc_restart:1), fc_reject:1),
full_duplex_force:1), full_duplex_pin:1);
/*20*/ u8 X(X(X(pad20_1:5, fc_priority_location:1), multi_ia:1), pad20_2:1);
/*21*/ u8 X(X(pad21_1:3, multicast_all:1), pad21_2:4);
/*22*/ u8 X(X(rx_d102_mode:1, rx_vlan_drop:1), pad22:6);
u8 pad_d102[9];
};
#define E100_MAX_MULTICAST_ADDRS 64
struct multi {
__le16 count;
u8 addr[E100_MAX_MULTICAST_ADDRS * ETH_MAC_LEN + 2/*pad*/];
};
/* Important: keep total struct u32-aligned */
#define UCODE_SIZE 134
#define E100_TX_FRAGMENTS 8
// #define E100_TX_FRAGMENTS 1
struct cb {
__le16 status;
__le16 command;
__le32 link;
union {
u8 iaaddr[ETH_MAC_LEN];
__le32 ucode[UCODE_SIZE];
struct config config;
struct multi multi;
struct {
u32 tbd_array;
u16 tcb_byte_count;
u8 threshold;
u8 tbd_count;
struct {
__le32 buf_addr;
__le16 size;
u16 eol;
} tbd[E100_TX_FRAGMENTS];
} tcb;
__le32 dump_buffer_addr;
} u;
struct cb *next, *prev;
dma_addr_t dma_addr;
void *data;
};
enum loopback {
lb_none = 0, lb_mac = 1, lb_phy = 3,
};
struct stats {
__le32 tx_good_frames, tx_max_collisions, tx_late_collisions,
tx_underruns, tx_lost_crs, tx_deferred, tx_single_collisions,
tx_multiple_collisions, tx_total_collisions;
__le32 rx_good_frames, rx_crc_errors, rx_alignment_errors,
rx_resource_errors, rx_overrun_errors, rx_cdt_errors,
rx_short_frame_errors;
__le32 fc_xmt_pause, fc_rcv_pause, fc_rcv_unsupported;
__le16 xmt_tco_frames, rcv_tco_frames;
__le32 complete;
};
struct mem {
struct {
u32 signature;
u32 result;
} selftest;
struct stats stats;
u8 dump_buf[596];
};
struct param_range {
u32 min;
u32 max;
u32 count;
u32 reap;
};
struct params {
struct param_range rfds;
struct param_range cbs;
};
#define ____cacheline_aligned __attribute__((__aligned__(NET_CACHELINE_SIZE)))
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
typedef struct _nic_nto_ext {
struct ethercom ecom; /* common device */
struct callout hk_callout; /* house keeping */
struct callout mii_callout; /* link up/down */
nic_config_t cfg;
nic_stats_t stats;
struct _iopkt_self *iopkt;
struct cache_ctrl cachectl;
void *pci_dev_hdl;
struct pci_dev_info pci_info;
unsigned short pci_command;
unsigned short pci_status;
u8 stat_ack;
int prot_flags;
int map_flags;
int dying;
volatile unsigned stopping;
const struct sigevent * (*isrp)(void *, int); ____cacheline_aligned
u16 (*mdio_ctrl)(struct _nic_nto_ext *e100, u32 addr, u32 dir, u32 reg, u16 data);
void *sd_hook;
int kermask;
int use_io;
int eeprom_bad_csum_allow;
int force_rx_bug;
int extra_rnr;
#define E100_DEFAULT_HK_TIMEOUT 5
unsigned hk_timeout;
__le16 tx_command;
struct rx *rxs ____cacheline_aligned;
struct rx *rx_to_use;
struct rx *rx_to_clean;
struct rfd blank_rfd;
enum ru_state ru_running;
// intrspin_t cb_lock ____cacheline_aligned;
// intrspin_t cmd_lock;
struct csr *csr ____cacheline_aligned;
enum scb_cmd_lo cuc_cmd;
unsigned int cbs_avail;
struct cb *cbs;
struct cb *cb_to_use;
struct cb *cb_to_send;
struct cb *cb_to_clean;
enum {
ich = (1 << 0),
promiscuous = (1 << 1),
multicast_all = (1 << 2),
wol_magic = (1 << 3),
ich_10h_workaround = (1 << 4),
} flags ____cacheline_aligned;
enum mac mac;
enum phy phy;
struct params params;
int num_receive;
int num_transmit;
enum loopback loopback;
struct mem *mem;
dma_addr_t dma_addr;
dma_addr_t cbs_dma_addr;
volatile unsigned irq_sw_gen;
/* TX descriptor and buffer tracking */
struct evcnt ev_txdrop ____cacheline_aligned;
int start_running; // tx in progress
mdi_t *mdi;
int pkts_received; // optimization to not probe phy
int force_advertise;
int tx_reap;
int bmstr; /* Cpu to Pci physical memory translation */
/* RX descriptor and buffer tracking */
struct _iopkt_inter inter ____cacheline_aligned;
int iid ____cacheline_aligned;
struct mii_data bsd_mii; // for media devctls
int tmem_fd;
char* tmem; // typed memory area
u8 adaptive_ifs;
u8 tx_threshold;
u32 tx_frames;
u32 tx_collisions;
u32 tx_fc_pause;
u32 tx_tco_frames;
u32 rx_fc_pause;
u32 rx_fc_unsupported;
u32 rx_tco_frames;
u32 rx_over_length_errors;
u16 eeprom_wc;
__le16 eeprom[256];
struct firmware *fw;
} e100_dev_t;
struct e100_dev {
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
#define __devname__ (e100 && e100->ecom.ec_if.if_xname[0] ? e100->ecom.ec_if.if_xname : "fxp*")
struct device sc_dev; /* common device */
e100_dev_t *sc_e100;
char filler[sizeof(e100_dev_t) + NET_CACHELINE_SIZE];
};
// main
int e100_init(struct ifnet *ifp);
void e100_stop(struct ifnet *ifp, int disable);
// mii
int e100_phy_supports_mii(e100_dev_t *e100);
int e100_findphy(e100_dev_t *e100);
int nic_force_advertise(e100_dev_t *e100);
u16 mdio_ctrl_hw(e100_dev_t *e100, u32 addr, u32 dir, u32 reg, u16 data);
void e100_mii_callout(void *arg);
// bsd_media
void bsd_mii_initmedia(e100_dev_t *e100);
// devctl
int e100_ioctl(struct ifnet * ifp, unsigned long cmd, caddr_t data);
// fw
int e100_load_ucode_wait(e100_dev_t *e100);
// hw
void e100_write_flush(e100_dev_t *e100);
int e100_eeprom_load(e100_dev_t *e100);
int e100_alloc_cbs(e100_dev_t *e100);
void e100_clean_cbs(e100_dev_t *e100, int full_clean);
void e100_enable_irq(e100_dev_t *e100);
void e100_disable_irq(e100_dev_t *e100);
int e100_hw_init(e100_dev_t *e100);
void e100_set_multicast_list(e100_dev_t *e100);
void e100_hw_dump_registers(e100_dev_t *e100, int desc);
int e100_exec_cb(e100_dev_t *e100, void *data, int (*cb_prepare)(e100_dev_t *, struct cb *));
int e100_configure(e100_dev_t *e100, struct cb *cb);
int e100_exec_cmd(e100_dev_t *e100, u8 cmd, dma_addr_t dma_addr);
void e100_hw_reset(e100_dev_t *e100);
// misc
int netif_running(e100_dev_t *e100);
int early_transmit(e100_dev_t *e100, struct nw_work_thread *wtp);
void setup_mmap_flags(e100_dev_t *e100);
int is_broadcast_mac(uint8_t *mac_addr);
int valid_mac_addr(char *mac_addr);
void setup_tmem(e100_dev_t *e100);
void e100_get_defaults(e100_dev_t *e100);
int pci_set_master(e100_dev_t *e100);
int pci_get_config(e100_dev_t *e100);
int pci_enable_device(e100_dev_t *e100);
void pci_disable_device(e100_dev_t *e100);
void fill_random_mac(char *mac_addr);
int e100_set_ringparam(e100_dev_t *e100, int num_receive, int num_transmit, int tx_reap);
void e100_adjust_adaptive_ifs(e100_dev_t *e100, int speed, int duplex);
void e100_update_stats(e100_dev_t *e100);
// interrupt
const struct sigevent *e100_isr_kermask(void *arg, int iid);
const struct sigevent *e100_isr(void *arg, int iid);
int e100_enable_interrupt_kermask(void *arg);
int e100_enable_interrupt(void *arg);
int e100_process_interrupt(void *arg, struct nw_work_thread *wtp);
// transmit
void e100_start(struct ifnet *ifp);
int e100_tx_clean(e100_dev_t *e100);
// receive
#ifndef RFD_ALIGNMENT_FUDGE
#define RFD_ALIGNMENT_FUDGE 0
#endif
#define E100_NAPI_WEIGHT 16
#define MAX_ETHER_LEN (ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN)
#define RFD_BUF_LEN (sizeof(struct rfd) + MAX_ETHER_LEN)
#define RFD_FROM_M(m) (struct rfd *)(mtod(m, struct rfd *) - 1)
int e100_rx_alloc_list(e100_dev_t *e100);
void e100_rx_clean_list(e100_dev_t *e100, int full_clean);
int e100_rx_init_list(e100_dev_t *e100, struct nw_work_thread *wtp);
void e100_start_receiver(e100_dev_t *e100, struct rx *rx);
void e100_rx_clean(e100_dev_t *e100, struct nw_work_thread *wtp, unsigned int *work_done, unsigned int work_to_do);
#endif

227
hardware/devnp/e100/e100_fw.c Обычный файл
Просмотреть файл

@ -0,0 +1,227 @@
/*
* (c) 2017-2019, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*****************************************************************************
* *
* Driver for the Intel 8255x 10/100 Mbps Ethernet Controller Family *
* Network controller firmware routines *
* *
*****************************************************************************/
#include "e100.h"
#include "firmwares.h"
static bool fw_get_builtin_firmware(struct firmware *fw, const char *name)
{
int i;
struct firmware_list *fw_list;
extern struct firmware_list firmwares[];
for ( i = 0; i < sizeof(firmwares) / sizeof(firmwares[0]); i++ ) {
fw_list = &firmwares[i];
if (strcmp(name, fw_list->name) == 0) {
fw->data = (uint8_t *)fw_list->start;
fw->size = fw_list->size;
return true;
}
}
return false;
}
static void release_firmware(struct firmware **firmware_p)
{
if ( firmware_p != NULL && *firmware_p != NULL ) {
free(*firmware_p, M_TEMP);
*firmware_p = NULL;
}
}
static int request_firmware(struct firmware **firmware_p, const char *name)
{
struct firmware *firmware;
init_firmwares();
firmware = malloc(sizeof(*firmware), M_TEMP, M_NOWAIT);
if ( firmware == NULL ) {
return ENOMEM;
}
memset(firmware, 0, sizeof(*firmware));
if ( fw_get_builtin_firmware(firmware, name) ) {
*firmware_p = firmware;
return 0;
}
return ENOENT;
}
#define BUNDLESMALL 1
#define BUNDLEMAX (u16)6
#define INTDELAY (u16)1536 /* 0x600 */
/* Initialize firmware */
static const int e100_request_firmware(e100_dev_t *e100)
{
const char *fw_name;
struct firmware *fw = e100->fw;
u8 timer, bundle, min_size;
int err = 0;
bool required = false;
/* do not load u-code for ICH devices */
if (e100->flags & ich)
goto done;
/* Search for ucode match against h/w revision
*
* Based on comments in the source code for the FreeBSD fxp
* driver, the FIRMWARE_D102E ucode includes both CPUSaver and
*
* "fixes for bugs in the B-step hardware (specifically, bugs
* with Inline Receive)."
*
* So we must fail if it cannot be loaded.
*
* The other microcode files are only required for the optional
* CPUSaver feature. Nice to have, but no reason to fail.
*/
E100_DEBUG(log(LOG_ERR, "%s: E100 MAC %d", __devname__, e100->mac));
if (e100->mac == mac_82559_D101M) {
fw_name = FIRMWARE_D101M;
} else if (e100->mac == mac_82559_D101S) {
fw_name = FIRMWARE_D101S;
} else if (e100->mac == mac_82551_F || e100->mac == mac_82551_10) {
fw_name = FIRMWARE_D102E;
required = true;
} else { /* No ucode on other devices */
goto done;
}
/* If the firmware has not previously been loaded, request a pointer
* to it. If it was previously loaded, we are reinitializing the
* adapter, possibly in a resume from hibernate, in which case
* request_firmware() cannot be used.
*/
if (!fw)
err = request_firmware(&fw, fw_name);
if (err) {
if (required) {
log(LOG_ERR, "%s: Failed to load firmware \"%s\": %d", __devname__,
fw_name, err);
goto error_done;
} else {
log(LOG_WARNING, "%s: CPUSaver disabled. Needs \"%s\": %d", __devname__,
fw_name, err);
goto done;
}
}
/* Firmware should be precisely UCODE_SIZE (words) plus three bytes
indicating the offsets for BUNDLESMALL, BUNDLEMAX, INTDELAY */
if (fw->size != (((UCODE_SIZE * 4 + 3) + 4U) & ~3U)) {
#ifdef __64__
log(LOG_ERR, "%s: Firmware \"%s\" has wrong size %lu", __devname__,
#else
log(LOG_ERR, "%s: Firmware \"%s\" has wrong size %u", __devname__,
#endif
fw_name, fw->size);
release_firmware(&fw);
errno = EINVAL;
goto error_done;
}
/* Read timer, bundle and min_size from end of firmware blob */
timer = fw->data[UCODE_SIZE * 4];
bundle = fw->data[UCODE_SIZE * 4 + 1];
min_size = fw->data[UCODE_SIZE * 4 + 2];
if (timer >= UCODE_SIZE || bundle >= UCODE_SIZE ||
min_size >= UCODE_SIZE) {
log(LOG_ERR, "%s: \"%s\" has bogus offset values (0x%x,0x%x,0x%x)", __devname__,
fw_name, timer, bundle, min_size);
release_firmware(&fw);
errno = EINVAL;
goto error_done;
}
/* OK, firmware is validated and ready to use. Save a pointer
* to it in the e100 */
e100->fw = fw;
done:
return 0;
error_done:
return -1;
}
static int e100_setup_ucode(e100_dev_t *e100, struct cb *cb)
{
const struct firmware *fw = (struct firmware *)cb->data;
u8 timer, bundle, min_size;
/* It's not a real data; we just abused the fact that e100_exec_cb
will pass it through to here... */
cb->data = NULL;
/* firmware is stored as little endian already */
memcpy(cb->u.ucode, fw->data, UCODE_SIZE * 4);
/* Read timer, bundle and min_size from end of firmware blob */
timer = fw->data[UCODE_SIZE * 4];
bundle = fw->data[UCODE_SIZE * 4 + 1];
min_size = fw->data[UCODE_SIZE * 4 + 2];
/* Insert user-tunable settings in cb->u.ucode */
cb->u.ucode[timer] &= cpu_to_le32(0xFFFF0000);
cb->u.ucode[timer] |= cpu_to_le32(INTDELAY);
cb->u.ucode[bundle] &= cpu_to_le32(0xFFFF0000);
cb->u.ucode[bundle] |= cpu_to_le32(BUNDLEMAX);
cb->u.ucode[min_size] &= cpu_to_le32(0xFFFF0000);
cb->u.ucode[min_size] |= cpu_to_le32((BUNDLESMALL) ? 0xFFFF : 0xFF80);
cb->command = cpu_to_le16(cb_ucode | cb_el);
return 0;
}
int e100_load_ucode_wait(e100_dev_t *e100)
{
int err = 0, counter = 50;
struct cb *cb = e100->cb_to_clean;
/* If it's NULL, then no ucode is required */
if ( (err = e100_request_firmware(e100)) != 0 || e100->fw == NULL ) {
if ( err )
log(LOG_ERR, "%s: Request firmware is failed %d", __devname__, err);
return err;
}
if ((err = e100_exec_cb(e100, (void *)e100->fw, e100_setup_ucode)))
log(LOG_ERR, "%s: ucode cmd failed with error %d", __devname__, err);
/* must restart cuc */
e100->cuc_cmd = cuc_start;
/* wait for completion */
e100_write_flush(e100);
udelay(10);
/* wait for possibly (ouch) 500ms */
while (!(cb->status & cpu_to_le16(cb_complete))) {
msleep(10);
if (!--counter) break;
}
/* ack any interrupts, something could have been set */
iowrite8(~0, &e100->csr->scb.stat_ack);
/* if the command failed, or is not OK, notify and return */
if (!counter || !(cb->status & cpu_to_le16(cb_ok))) {
log(LOG_ERR, "%s: ucode load failed", __devname__);
err = -EPERM;
}
return err;
}

616
hardware/devnp/e100/e100_hw.c Обычный файл
Просмотреть файл

@ -0,0 +1,616 @@
/*
* (c) 2017-2019, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*****************************************************************************
* *
* Driver for the Intel 8255x 10/100 Mbps Ethernet Controller Family *
* Network controller hw routines *
* *
*****************************************************************************/
#include "e100.h"
void e100_write_flush(e100_dev_t *e100)
{
/* Flush previous PCI writes through intermediate bridges
* by doing a benign read */
(void)ioread8(&e100->csr->scb.status);
}
void e100_enable_irq(e100_dev_t *e100)
{
unsigned irq_sw_gen_queue;
// InterruptLock(&e100->cmd_lock);
if ( e100->stopping ) {
e100_disable_irq(e100);
return;
}
irq_sw_gen_queue = atomic_clr_value(&e100->irq_sw_gen, 0xFFFFFFFF);
iowrite8(irq_sw_gen_queue | irq_mask_none, &e100->csr->scb.cmd_hi);
e100_write_flush(e100);
// InterruptUnlock(&e100->cmd_lock);
}
void e100_disable_irq(e100_dev_t *e100)
{
// InterruptLock(&e100->cmd_lock);
iowrite8(irq_mask_all, &e100->csr->scb.cmd_hi);
e100_write_flush(e100);
// InterruptUnlock(&e100->cmd_lock);
}
void e100_hw_reset(e100_dev_t *e100)
{
E100_DEBUG(
pci_get_config(e100);
)
/* Put CU and RU into idle with a selective reset to get
* device off of PCI bus */
iowrite32(selective_reset, &e100->csr->port);
e100_write_flush(e100); udelay(20);
// /* Now fully reset device */
iowrite32(software_reset, &e100->csr->port);
e100_write_flush(e100); udelay(20);
/* Mask off our interrupt line - it's unmasked after reset */
e100_disable_irq(e100);
E100_DEBUG(
pci_get_config(e100);
)
}
int e100_self_test(e100_dev_t *e100)
{
u32 dma_addr = e100->dma_addr + offsetof(struct mem, selftest);
/* Passing the self-test is a pretty good indication
* that the device can DMA to/from host memory */
e100->mem->selftest.signature = 0;
e100->mem->selftest.result = 0xFFFFFFFF;
iowrite32(selftest | dma_addr, &e100->csr->port);
e100_write_flush(e100);
/* Wait 10 msec for self-test to complete */
msleep(10);
/* Interrupts are enabled after self-test */
e100_disable_irq(e100);
/* Check results of self-test */
if (e100->mem->selftest.result != 0) {
log(LOG_ERR, "%s: Self-test failed: result=0x%08X", __devname__,
e100->mem->selftest.result);
return ETIMEDOUT;
}
if (e100->mem->selftest.signature == 0) {
log(LOG_ERR, "%s: Self-test failed: timed out", __devname__);
return ETIMEDOUT;
}
return 0;
}
#define E100_WAIT_SCB_TIMEOUT 20000 /* we might have to wait 100ms!!! */
#define E100_WAIT_SCB_FAST 20 /* delay like the old code */
int e100_exec_cmd(e100_dev_t *e100, u8 cmd, dma_addr_t dma_addr)
{
unsigned int i;
int err = 0;
// InterruptLock(&e100->cmd_lock);
/* Previous command is accepted when SCB clears */
for (i = 0; i < E100_WAIT_SCB_TIMEOUT; i++) {
if (likely(!ioread8(&e100->csr->scb.cmd_lo)))
break;
if (unlikely(i > E100_WAIT_SCB_FAST))
udelay(5);
}
if (unlikely(i == E100_WAIT_SCB_TIMEOUT)) {
err = -EAGAIN;
goto err_unlock;
}
if (unlikely(cmd != cuc_resume && cmd != ruc_resume && cmd != ruc_abort)) {
if (dma_addr) {
// FIXME it's right?
dma_addr += e100->bmstr;
}
iowrite32(dma_addr, &e100->csr->scb.gen_ptr);
}
iowrite8(cmd, &e100->csr->scb.cmd_lo);
err_unlock:
// InterruptUnlock(&e100->cmd_lock);
return err;
}
int e100_exec_cb(e100_dev_t *e100, void *data,
int (*cb_prepare)(e100_dev_t *, struct cb *))
{
// struct ifnet *ifp = &e100->ecom.ec_if;
struct cb *cb;
int err;
// InterruptLock(&e100->cb_lock);
if (unlikely(!e100->cbs_avail)) {
err = ENOMEM;
goto err_unlock;
}
cb = e100->cb_to_use;
e100->cb_to_use = cb->next;
e100->cbs_avail--;
cb->data = data;
memset(&cb->u, 0, sizeof(cb->u));
err = cb_prepare(e100, cb);
E100_DEBUG(
log(LOG_DEBUG, "%s: %s() cbs_avail %d command 0x%04x err %d", __devname__, __func__, e100->cbs_avail, cb->command, err);
)
if (err)
goto err_unlock;
if (unlikely(!e100->cbs_avail))
err = ENOSPC;
/* Order is important otherwise we'll be in a race with h/w:
* set S-bit in current first, then clear S-bit in previous. */
cb->command |= cpu_to_le16(cb_s);
dma_wmb();
cb->prev->command &= cpu_to_le16(~cb_s);
while (e100->cb_to_send != e100->cb_to_use) {
if (unlikely(e100_exec_cmd(e100, e100->cuc_cmd,
e100->cb_to_send->dma_addr))) {
/* Ok, here's where things get sticky. It's
* possible that we can't schedule the command
* because the controller is too busy, so
* let's just queue the command and try again
* when another command is scheduled. */
E100_DEBUG(
log(LOG_DEBUG, "%s: %s() EXEC CMD BUSY", __devname__, __func__);
)
break;
} else {
e100->cuc_cmd = cuc_resume;
e100->cb_to_send = e100->cb_to_send->next;
}
}
err_unlock:
// InterruptUnlock(&e100->cb_lock);
return err;
}
int e100_configure(e100_dev_t *e100, struct cb *cb)
{
struct config *config = &cb->u.config;
// E100_DEBUG (
u8 *c = (u8 *)config;
// )
cb->command = cpu_to_le16(cb_config);
memset(config, 0, sizeof(struct config));
config->byte_count = 0x16; /* bytes in this struct */
config->rx_fifo_limit = 0x8; /* bytes in FIFO before DMA */
config->direct_rx_dma = 0x1; /* reserved */
config->standard_tcb = 0x1; /* 1=standard, 0=extended */
config->standard_stat_counter = 0x1; /* 1=standard, 0=extended */
config->rx_discard_short_frames = 0x1; /* 1=discard, 0=pass */
config->tx_underrun_retry = 0x3; /* # of underrun retries */
if (e100_phy_supports_mii(e100))
config->mii_mode = 1; /* 1=MII mode, 0=i82503 mode */
config->pad10 = 0x6;
config->no_source_addr_insertion = 0x1; /* 1=no, 0=yes */
config->preamble_length = 0x2; /* 0=1, 1=3, 2=7, 3=15 bytes */
config->ifs = 0x6; /* x16 = inter frame spacing */
config->ip_addr_hi = 0xF2; /* ARP IP filter - not used */
config->pad15_1 = 0x1;
config->pad15_2 = 0x1;
config->crs_or_cdt = 0x0; /* 0=CRS only, 1=CRS or CDT */
config->fc_delay_hi = 0x40; /* time delay for fc frame */
config->tx_padding = 0x1; /* 1=pad short frames */
config->fc_priority_threshold = 0x7; /* 7=priority fc disabled */
config->pad18 = 0x1;
config->full_duplex_pin = 0x1; /* 1=examine FDX# pin */
config->pad20_1 = 0x1F;
config->fc_priority_location = 0x1; /* 1=byte#31, 0=byte#19 */
config->pad21_1 = 0x5;
config->adaptive_ifs = e100->adaptive_ifs;
config->loopback = e100->loopback;
if (e100->force_advertise && e100->cfg.duplex > 0)
config->full_duplex_force = 0x1; /* 1=force, 0=auto */
if (e100->flags & promiscuous || e100->loopback) {
config->rx_save_bad_frames = 0x1; /* 1=save, 0=discard */
config->rx_discard_short_frames = 0x0; /* 1=discard, 0=save */
config->promiscuous_mode = 0x1; /* 1=on, 0=off */
}
if (e100->flags & multicast_all)
config->multicast_all = 0x1; /* 1=accept, 0=no */
/* disable WoL when up */
if (netif_running(e100) || !(e100->flags & wol_magic))
config->magic_packet_disable = 0x1; /* 1=off, 0=on */
if (e100->mac >= mac_82558_D101_A4) {
config->fc_disable = 0x1; /* 1=Tx fc off, 0=Tx fc on */
if ( (e100->pci_command & PCI_COMMAND_INVALIDATE_ENABLE) )
config->mwi_enable = 0x1; /* 1=enable, 0=disable */
config->rx_long_ok = 0x1; /* 1=VLANs ok, 0=standard */
if (e100->mac >= mac_82559_D101M) {
config->tno_intr = 0x1; /* TCO stats enable */
/* Enable TCO in extended config */
#if 1
// FIXME
if (e100->mac >= mac_82551_10) {
config->byte_count = 0x20; /* extended bytes */
config->rx_d102_mode = 0x1; /* GMRC for TCO */
}
#endif
} else {
config->standard_stat_counter = 0x0;
}
}
// E100_DEBUG (
if ( e100->cfg.verbose > 1 ) {
log(LOG_DEBUG, "%s: config [00-07]=%08xh", __devname__, *(u32 *)c); c += 8;
log(LOG_DEBUG, "%s: config [08-15]=%08xh", __devname__, *(u32 *)c); c += 8;
log(LOG_DEBUG, "%s: config [16-23]=%08xh", __devname__, *(u32 *)c);
}
// )
return 0;
}
int e100_setup_iaaddr(e100_dev_t *e100, struct cb *cb)
{
cb->command = cpu_to_le16(cb_iaaddr);
memcpy(cb->u.iaaddr, e100->cfg.current_address, ETH_MAC_LEN);
return 0;
}
static int e100_multi(e100_dev_t *e100, struct cb *cb)
{
struct ethercom *ec;
struct ether_multi *enm;
struct ether_multistep step;
int mcentries = 0;
ec = &(e100->ecom);
cb->command = cpu_to_le16(cb_multi);
ETHER_FIRST_MULTI (step, ec, enm);
while (enm != NULL) {
if ((mcentries > E100_MAX_MULTICAST_ADDRS) || (memcmp (enm->enm_addrlo, enm->enm_addrhi,
ETHER_ADDR_LEN)) != 0) {
ec->ec_if.if_flags |= IFF_ALLMULTI;
break;
}
if ( e100->cfg.verbose > 1 ) {
log(LOG_DEBUG,"%s: enm %p %02X:%02X:%02X mcentries %d", __devname__, enm,
enm->enm_addrlo[0], enm->enm_addrlo[1], enm->enm_addrlo[2], mcentries);
}
memcpy(&cb->u.multi.addr[mcentries * ETH_MAC_LEN], &enm->enm_addrlo, ETH_MAC_LEN);
mcentries++;
ETHER_NEXT_MULTI (step, enm);
}
cb->u.multi.count = cpu_to_le16(min(mcentries, E100_MAX_MULTICAST_ADDRS) * ETH_MAC_LEN);
return 0;
}
static int e100_mc_count(e100_dev_t *e100)
{
struct ethercom *ec;
struct ether_multi *enm;
struct ether_multistep step;
int mcentries = 0;
ec = &e100->ecom;
ETHER_FIRST_MULTI (step, ec, enm);
while (enm != NULL) {
if ((mcentries > E100_MAX_MULTICAST_ADDRS) || (memcmp (enm->enm_addrlo, enm->enm_addrhi,
ETHER_ADDR_LEN)) != 0) {
ec->ec_if.if_flags |= IFF_ALLMULTI;
mcentries = E100_MAX_MULTICAST_ADDRS + 1;
return mcentries;
}
if ( e100->cfg.verbose > 1 ) {
log(LOG_DEBUG,"%s: enm %p %02X:%02X:%02X mcentries %d", __devname__, enm,
enm->enm_addrlo[0], enm->enm_addrlo[1], enm->enm_addrlo[2], mcentries);
}
mcentries++;
ETHER_NEXT_MULTI (step, enm);
}
return mcentries;
}
void e100_set_multicast_list(e100_dev_t *e100)
{
struct ifnet *ifp;
int mc_count = 0;
ifp = &e100->ecom.ec_if;
if ( (e100->cfg.flags & NIC_FLAG_MULTICAST) ) {
ifp->if_flags &= ~IFF_ALLMULTI;
if (ifp->if_flags & IFF_PROMISC) {
e100->cfg.flags |= NIC_FLAG_PROMISCUOUS;
e100->flags |= promiscuous;
} else {
e100->cfg.flags &= ~NIC_FLAG_PROMISCUOUS;
e100->flags &= ~promiscuous;
}
if ( (mc_count = e100_mc_count(e100)) > E100_MAX_MULTICAST_ADDRS)
e100->flags |= multicast_all;
else
e100->flags &= ~multicast_all;
} else {
e100->flags &= ~multicast_all;
}
e100_exec_cb(e100, NULL, e100_configure);
if ( (e100->cfg.flags & NIC_FLAG_MULTICAST) ) {
e100_exec_cb(e100, NULL, e100_multi);
}
}
int e100_hw_init(e100_dev_t *e100)
{
int err = 0;
e100_hw_reset(e100);
E100_DEBUG(log(LOG_DEBUG, "%s: e100_hw_init", __devname__));
if ((err = e100_self_test(e100))) {
log(LOG_DEBUG, "%s: Self test is failed: %d", __devname__, err);
return err;
}
if ((err = e100_findphy(e100))) {
log(LOG_ERR, "%s: Find PHY is failed: %d", __devname__, err);
return err;
}
if ((err = e100_exec_cmd(e100, cuc_load_base, 0))) {
log(LOG_ERR, "%s: Execute of 'cuc load base' is failed: %d", __devname__, err);
return err;
}
if ((err = e100_exec_cmd(e100, ruc_load_base, 0))) {
log(LOG_ERR, "%s: Execute of 'ruc load base' is failed: %d", __devname__, err);
return err;
}
if ((err = e100_load_ucode_wait(e100))) {
log(LOG_ERR, "%s: Load ucode is failed: %d", __devname__, err);
return err;
}
if ((err = e100_exec_cb(e100, NULL, e100_configure))) {
log(LOG_ERR, "%s: Execute of 'configure' is failed: %d", __devname__, err);
return err;
}
if ((err = e100_exec_cb(e100, NULL, e100_setup_iaaddr))) {
log(LOG_ERR, "%s: Execute of 'setup iaddr' is failed: %d", __devname__, err);
return err;
}
if ((err = e100_exec_cmd(e100, cuc_dump_addr,
e100->dma_addr + offsetof(struct mem, stats)))) {
log(LOG_ERR, "%s: Execute of 'setup cuc dump addr' is failed: %d", __devname__, err);
return err;
}
if ((err = e100_exec_cmd(e100, cuc_dump_reset, 0))) {
log(LOG_ERR, "%s: Execute of 'setup cuc dump reset' is failed: %d", __devname__, err);
return err;
}
e100_disable_irq(e100);
return 0;
}
#if 0
static void e100_eeprom_write(e100_dev_t *e100, u16 addr_len, u16 addr, __le16 data)
{
u32 cmd_addr_data[3];
u8 ctrl;
int i, j;
/* Three cmds: write/erase enable, write data, write/erase disable */
cmd_addr_data[0] = op_ewen << (addr_len - 2);
cmd_addr_data[1] = (((op_write << addr_len) | addr) << 16) |
le16_to_cpu(data);
cmd_addr_data[2] = op_ewds << (addr_len - 2);
/* Bit-bang cmds to write word to eeprom */
for (j = 0; j < 3; j++) {
/* Chip select */
iowrite8(eecs | eesk, &e100->csr->eeprom_ctrl_lo);
e100_write_flush(e100); udelay(4);
for (i = 31; i >= 0; i--) {
ctrl = (cmd_addr_data[j] & (1 << i)) ?
eecs | eedi : eecs;
iowrite8(ctrl, &e100->csr->eeprom_ctrl_lo);
e100_write_flush(e100); udelay(4);
iowrite8(ctrl | eesk, &e100->csr->eeprom_ctrl_lo);
e100_write_flush(e100); udelay(4);
}
/* Wait 10 msec for cmd to complete */
msleep(10);
/* Chip deselect */
iowrite8(0, &e100->csr->eeprom_ctrl_lo);
e100_write_flush(e100); udelay(4);
}
};
/* Save (portion of) driver EEPROM cache to device and update checksum */
static int e100_eeprom_save(e100_dev_t *e100, u16 start, u16 count)
{
u16 addr, addr_len = 8, checksum = 0;
/* Try reading with an 8-bit addr len to discover actual addr len */
e100_eeprom_read(e100, &addr_len, 0);
e100->eeprom_wc = 1 << addr_len;
if (start + count >= e100->eeprom_wc)
return -EINVAL;
for (addr = start; addr < start + count; addr++)
e100_eeprom_write(e100, addr_len, addr, e100->eeprom[addr]);
/* The checksum, stored in the last word, is calculated such that
* the sum of words should be 0xBABA */
for (addr = 0; addr < e100->eeprom_wc - 1; addr++)
checksum += le16_to_cpu(e100->eeprom[addr]);
e100->eeprom[e100->eeprom_wc - 1] = cpu_to_le16(0xBABA - checksum);
e100_eeprom_write(e100, addr_len, e100->eeprom_wc - 1,
e100->eeprom[e100->eeprom_wc - 1]);
return 0;
}
#endif
/* General technique stolen from the eepro100 driver - very clever */
static __le16 e100_eeprom_read(e100_dev_t *e100, u16 *addr_len, u16 addr)
{
u32 cmd_addr_data;
u16 data = 0;
u8 ctrl;
int i;
cmd_addr_data = ((op_read << *addr_len) | addr) << 16;
/* Chip select */
iowrite8(eecs | eesk, &e100->csr->eeprom_ctrl_lo);
e100_write_flush(e100); udelay(4);
/* Bit-bang to read word from eeprom */
for (i = 31; i >= 0; i--) {
ctrl = (cmd_addr_data & (1 << i)) ? eecs | eedi : eecs;
iowrite8(ctrl, &e100->csr->eeprom_ctrl_lo);
e100_write_flush(e100); udelay(4);
iowrite8(ctrl | eesk, &e100->csr->eeprom_ctrl_lo);
e100_write_flush(e100); udelay(4);
/* Eeprom drives a dummy zero to EEDO after receiving
* complete address. Use this to adjust addr_len. */
ctrl = ioread8(&e100->csr->eeprom_ctrl_lo);
if (!(ctrl & eedo) && i > 16) {
*addr_len -= (i - 16);
i = 17;
}
data = (data << 1) | (ctrl & eedo ? 1 : 0);
}
/* Chip deselect */
iowrite8(0, &e100->csr->eeprom_ctrl_lo);
e100_write_flush(e100); udelay(4);
return cpu_to_le16(data);
};
/* Load entire EEPROM image into driver cache and validate checksum */
int e100_eeprom_load(e100_dev_t *e100)
{
u16 addr, addr_len = 8, checksum = 0;
/* Try reading with an 8-bit addr len to discover actual addr len */
e100_eeprom_read(e100, &addr_len, 0);
e100->eeprom_wc = 1 << addr_len;
for (addr = 0; addr < e100->eeprom_wc; addr++) {
e100->eeprom[addr] = e100_eeprom_read(e100, &addr_len, addr);
if (addr < e100->eeprom_wc - 1) {
checksum += le16_to_cpu(e100->eeprom[addr]);
E100_DEBUG(
log(LOG_ERR, "%s: checksum += eeprom[%d] %04X = %04X", __devname__, addr, e100->eeprom[addr], checksum);
)
}
}
E100_DEBUG(
log(LOG_ERR, "%s: eeprom[%d] %04X", __devname__, e100->eeprom_wc - 1, e100->eeprom[e100->eeprom_wc - 1]);
)
/* The checksum, stored in the last word, is calculated such that
* the sum of words should be 0xBABA */
if (cpu_to_le16(0xBABA - checksum) != e100->eeprom[e100->eeprom_wc - 1]) {
log(LOG_ERR, "%s: EEPROM corrupted (%d)", __devname__, addr_len);
if (!e100->eeprom_bad_csum_allow)
return EAGAIN;
}
return 0;
}
int e100_alloc_cbs(e100_dev_t *e100)
{
struct cb *cb;
unsigned int i, count = e100->params.cbs.count;
e100->cuc_cmd = cuc_start;
e100->cb_to_use = e100->cb_to_send = e100->cb_to_clean = NULL;
e100->cbs_avail = 0;
e100->cbs = (struct cb *)mmap(0, count * sizeof(struct cb), e100->prot_flags, e100->map_flags, e100->tmem_fd, 0);
if (e100->cbs == (struct cb *)MAP_FAILED) {
return errno;
}
e100->cbs_dma_addr = (dma_addr_t)drvr_mphys(e100->cbs);
memset(e100->cbs, 0, count * sizeof(struct cb));
for (cb = e100->cbs, i = 0; i < count; cb++, i++) {
cb->next = (i + 1 < count) ? cb + 1 : e100->cbs;
cb->prev = (i == 0) ? e100->cbs + count - 1 : cb - 1;
cb->dma_addr = e100->cbs_dma_addr + i * sizeof(struct cb);
cb->link = cpu_to_le32(e100->cbs_dma_addr +
((i+1) % count) * sizeof(struct cb) + e100->bmstr);
}
e100->cb_to_use = e100->cb_to_send = e100->cb_to_clean = e100->cbs;
e100->cbs_avail = count;
E100_DEBUG(log(LOG_DEBUG, "%s: ALLOC CBS %d", __devname__, count));
return 0;
}
void e100_clean_cbs(e100_dev_t *e100, int full_clean)
{
if (e100->cbs) {
while ( e100_tx_clean(e100) )
;
if ( full_clean ) {
munmap(e100->cbs, e100->params.cbs.count * sizeof(struct cb));
e100->cbs = NULL;
e100->cbs_avail = 0;
}
}
e100->cuc_cmd = cuc_start;
e100->cb_to_use = e100->cb_to_send = e100->cb_to_clean = e100->cbs;
E100_DEBUG(log(LOG_DEBUG, "%s: Clean CBS %d -> %d", __devname__, e100->params.cbs.count, e100->cbs_avail));
}
void e100_hw_dump_registers(e100_dev_t *e100, int desc)
{
log(LOG_ERR, "%s (%s) Logical Net %d, Reg : 0x%08x",
e100->cfg.device_description, __devname__, e100->cfg.lan, FROM_POINTER_TO_INT(e100->csr, uint32_t));
if ( e100->mdi != NULL )
MDI_DumpRegisters( e100->mdi, e100->cfg.phy_addr );
}

148
hardware/devnp/e100/e100_interrupt.c Обычный файл
Просмотреть файл

@ -0,0 +1,148 @@
/*
* (c) 2017-2018, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*****************************************************************************
* *
* Driver for the Intel 8255x 10/100 Mbps Ethernet Controller Family *
* Network controller interrupt routines *
* *
*****************************************************************************/
#include "e100.h"
const struct sigevent * e100_isr_kermask(void *arg, int iid)
{
e100_dev_t *e100 = arg;
struct _iopkt_inter *ient;
u8 stat_ack = ioread8(&e100->csr->scb.stat_ack);
ient = &e100->inter;
if (ient->on_list == 0 &&
(stat_ack == stat_ack_not_ours || /* Not our interrupt */
stat_ack == stat_ack_not_present /* Hardware is ejected */)) {
/* IRQ not caused by this card. */
ient->spurious++;
return NULL;
}
/*
* Close window where this is referenced in e100_process_interrupt().
* We may get an interrupt, return a sigevent and have another
* thread start processing on SMP before the InterruptAttach()
* has returned.
*/
/* Ack interrupt(s) */
iowrite8(stat_ack, &e100->csr->scb.stat_ack);
/* We hit Receive No Resource (RNR); restart RU after cleaning */
if (stat_ack & stat_ack_rnr)
e100->ru_running = RU_SUSPENDED;
InterruptMask(e100->cfg.irq[0], iid);
e100->iid = iid;
return interrupt_queue(e100->iopkt, ient);
}
const struct sigevent *e100_isr(void *arg, int iid)
{
e100_dev_t *e100 = arg;
struct _iopkt_inter *ient;
u8 stat_ack = ioread8(&e100->csr->scb.stat_ack);
ient = &e100->inter;
if (ient->on_list == 0 &&
(stat_ack == stat_ack_not_ours || /* Not our interrupt */
stat_ack == stat_ack_not_present /* Hardware is ejected */)) {
/* IRQ not caused by this card. */
ient->spurious++;
return NULL;
}
/*
* Close window where this is referenced in e100_enable_interrupt().
* We may get an interrupt, return a sigevent and have another
* thread start processing on SMP before the InterruptAttach()
* has returned.
*/
/* Ack interrupt(s) */
iowrite8(stat_ack, &e100->csr->scb.stat_ack);
e100->stat_ack = stat_ack;
/* We hit Receive No Resource (RNR); restart RU after cleaning */
if (stat_ack & stat_ack_rnr)
e100->ru_running = RU_SUSPENDED;
e100_disable_irq(e100);
e100->iid = iid;
#ifdef VARIANT_norx
// TODO norx variant
while ( (stat_ack) ) {
e100->pkts_received = 1; // set activity flag for phy probe
e100_rx_clean(e100, &work_done, E100_NAPI_WEIGHT);
stat_ack = ioread8(&e100->csr->scb.stat_ack);
/* Ack interrupt(s) */
iowrite8(stat_ack, &e100->csr->scb.stat_ack);
}
e100_enable_irq(e100);
return NULL;
#endif
return interrupt_queue(e100->iopkt, ient);
}
int e100_enable_interrupt_kermask(void *arg)
{
e100_dev_t *e100 = (e100_dev_t *)arg;
InterruptUnmask(e100->cfg.irq[0], e100->iid);
return 1;
}
int e100_enable_interrupt(void *arg)
{
e100_dev_t *e100 = (e100_dev_t *)arg;
E100_DEBUG(log(LOG_DEBUG, "%s: %s() start", __devname__, __func__));
e100_enable_irq(e100);
E100_DEBUG(log(LOG_DEBUG, "%s: %s() done", __devname__, __func__));
return 1;
}
int e100_process_interrupt(void *arg, struct nw_work_thread *wtp)
{
uint32_t work_done;
e100_dev_t *e100 = (e100_dev_t *)arg;
struct ifnet *ifp;
ifp = &e100->ecom.ec_if;
if ( e100->stopping ) {
return 1;
}
do {
work_done = 0;
e100->pkts_received = 1;
E100_DEBUG(log(LOG_DEBUG, "%s: %s() stack_ack %02X status %02X", __devname__, __func__, e100->stat_ack, ioread8(&e100->csr->scb.status)));
e100_rx_clean(e100, wtp, &work_done, E100_NAPI_WEIGHT);
e100->pkts_received = 0;
// if the transmit side is quiet, process txd descriptors now
if (!(ifp->if_flags_tx & IFF_OACTIVE)) {
NW_SIGLOCK_P(&ifp->if_snd_ex, e100->iopkt, wtp);
e100_tx_clean(e100);
NW_SIGUNLOCK_P(&ifp->if_snd_ex, e100->iopkt, wtp);
}
if ( work_done >= E100_NAPI_WEIGHT && e100->cfg.verbose > 1 ) {
log(LOG_DEBUG, "%s: receive budget has been exceeded %d/%d", __devname__, work_done, E100_NAPI_WEIGHT);
}
} while ( work_done >= E100_NAPI_WEIGHT );
E100_DEBUG(log(LOG_DEBUG, "%s: %s() done", __devname__, __func__));
return (work_done < E100_NAPI_WEIGHT ? 1 : 0);
}

300
hardware/devnp/e100/e100_misc.c Обычный файл
Просмотреть файл

@ -0,0 +1,300 @@
/*
* (c) 2017-2019, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*****************************************************************************
* *
* Driver for the Intel 8255x 10/100 Mbps Ethernet Controller Family *
* Network controller misc routines *
* *
*****************************************************************************/
#include "e100.h"
int netif_running(e100_dev_t *e100)
{
struct ifnet *ifp;
ifp = &e100->ecom.ec_if;
return (ifp->if_flags & IFF_RUNNING);
}
int early_transmit(e100_dev_t *e100, struct nw_work_thread *wtp)
{
struct ifnet *ifp = &e100->ecom.ec_if;
if ( (ifp->if_flags_tx & IFF_RUNNING) == 0 ||
(e100->cfg.flags & NIC_FLAG_LINK_DOWN) ) {
return 2;
}
if ( e100->start_running ) {
return 1;
}
return 0;
}
// Get system memory cache attrs
static int mem_smart_cache(void)
{
#if 1
struct cacheattr_entry *cache, *base;
int i;
base = SYSPAGE_ENTRY(cacheattr);
for (i = SYSPAGE_ENTRY(cpuinfo)->data_cache; i != CACHE_LIST_END; i = cache->next) {
cache = &base[i];
if (!(cache->flags & CACHE_FLAG_SNOOPED))
return 0;
}
return 1;
#else
return 0;
#endif
}
void setup_mmap_flags(e100_dev_t *e100)
{
/* Prepare mmap flags */
if (e100->tmem_fd != NOFD)
e100->map_flags = MAP_SHARED;
else
e100->map_flags = MAP_PRIVATE | MAP_PHYS | MAP_ANON;
if (mem_smart_cache())
e100->prot_flags = PROT_READ | PROT_WRITE;
else
e100->prot_flags = PROT_READ | PROT_WRITE | PROT_NOCACHE;
E100_DEBUG(
log(LOG_DEBUG, "%s: %s() PROT_NOCACHE %d", __devname__, __func__, e100->prot_flags & PROT_NOCACHE);
)
}
void setup_tmem(e100_dev_t *e100)
{
if ( e100->tmem == NULL ) {
goto notmem;
}
if ((e100->tmem_fd = posix_typed_mem_open(e100->tmem, O_RDWR,
POSIX_TYPED_MEM_ALLOCATE_CONTIG)) == -1) {
log(LOG_ERR, "%s: unable to open typed memory %s, will not use typed memory",
__devname__, e100->tmem);
goto notmem;
}
return;
notmem:
e100->tmem_fd = NOFD;
}
int pci_get_config(e100_dev_t *e100)
{
if ( pci_read_config(e100->pci_dev_hdl, offsetof (struct _pci_config_regs, Command), 1, 2, &e100->pci_command) == -1 ) {
return ENODEV;
}
if ( pci_read_config(e100->pci_dev_hdl, offsetof (struct _pci_config_regs, Status), 1, 2, &e100->pci_status) == -1 ) {
return ENODEV;
}
E100_DEBUG(
log(LOG_INFO, "%s: Command %04X Status %04X", __devname__, e100->pci_command, e100->pci_status);
)
return 0;
}
int pci_set_master(e100_dev_t *e100)
{
if ( e100->pci_dev_hdl == NULL ) {
return ENOMEM;
}
pci_attach_device(e100->pci_dev_hdl,
PCI_MASTER_ENABLE, e100->cfg.device_index, &e100->pci_info);
return pci_get_config(e100);
}
int pci_enable_device(e100_dev_t *e100)
{
e100->pci_dev_hdl = pci_attach_device(NULL, PCI_INIT_ALL | PCI_MASTER_ENABLE, e100->cfg.device_index, &e100->pci_info);
e100->cfg.device_revision = e100->pci_info.Revision;
if (e100->pci_dev_hdl == NULL) {
return errno;
}
return pci_get_config(e100);
}
void pci_disable_device(e100_dev_t *e100)
{
int ret;
ret = pci_detach_device(e100->pci_dev_hdl);
E100_DEBUG(log(LOG_ERR, "%s: PCI disable device %d", __devname__, ret));
(void)ret;
}
void fill_random_mac(char *mac_addr)
{
int i;
uint64_t clc = ClockCycles();
uint32_t rnd = ((clc >> 32) & 0xFFFFFF) ^ (clc & 0xFFFFFF);
for ( i = 0; i < 6; i++ ) {
rnd = (0xFE0303D9 * rnd + 0x2F77DF2F);
mac_addr[i] = rnd >> 24;
}
mac_addr[0] &= ~1;
mac_addr[0] |= 2;
}
int is_broadcast_mac(uint8_t *mac_addr)
{
return !memcmp(mac_addr, "\xFF\xFF\xFF\xFF\xFF\xFF", ETH_MAC_LEN);
}
int valid_mac_addr(char *mac_addr)
{
return (mac_addr[0] & 1 || !memcmp(mac_addr, "\0\0\0\0\0\0", ETH_MAC_LEN)) ? 0 : 1;
}
int e100_set_ringparam(e100_dev_t *e100, int num_receive, int num_transmit, int tx_reap)
{
struct ifnet *ifp;
struct param_range *rfds = &e100->params.rfds;
struct param_range *cbs = &e100->params.cbs;
int e100_running = netif_running(e100);
ifp = &e100->ecom.ec_if;
if (e100_running)
e100_stop(ifp, 0);
if ( num_receive >= 0 ) {
rfds->count = max(num_receive, rfds->min);
rfds->count = min(rfds->count, rfds->max);
}
if ( num_transmit >= 0 ) {
cbs->count = max(num_transmit, cbs->min);
cbs->count = min(cbs->count, cbs->max);
}
if ( tx_reap >= 0 ) {
cbs->reap = max(tx_reap, cbs->min);
cbs->reap = min(cbs->reap, cbs->max);
}
if ( e100->cfg.verbose && (num_receive >= 0 || num_transmit >= 0 || tx_reap >= 0) )
log(LOG_INFO, "%s: Ring Param settings: rx: %d, tx %d, reap %d", __devname__, rfds->count, cbs->count, cbs->reap);
if (e100_running)
e100_init(ifp);
return 0;
}
void e100_get_defaults(e100_dev_t *e100)
{
struct param_range rfds = { .min = 16, .max = 2048, .count = 256, .reap = 0 };
struct param_range cbs = { .min = 64, .max = 2048, .count = 1024, .reap = 64 };
/* MAC type is encoded as rev ID; exception: ICH is treated as 82559 */
e100->mac = (e100->flags & ich) ? mac_82559_D101M : e100->pci_info.Revision;
if (e100->mac == mac_unknown)
e100->mac = mac_82557_D100_A;
e100->params.rfds = rfds;
e100->params.cbs = cbs;
e100_set_ringparam(e100, rfds.count, cbs.count, cbs.reap);
/* Quadwords to DMA into FIFO before starting frame transmit */
e100->tx_threshold = 0xE0;
/* no interrupt for every tx completion, delay = 256us if not 557 */
e100->tx_command = cpu_to_le16(cb_tx | cb_tx_sf |
((e100->mac >= mac_82558_D101_A4) ? cb_cid : cb_i));
/* Template for a freshly allocated RFD */
e100->blank_rfd.command = 0;
e100->blank_rfd.rbd = cpu_to_le32(0xFFFFFFFF);
e100->blank_rfd.size = cpu_to_le16(MAX_ETHER_LEN);
if ( e100->num_receive >=0 || e100->num_transmit >= 0 || e100->tx_reap ) {
e100_set_ringparam(e100, e100->num_receive, e100->num_transmit, e100->tx_reap);
}
}
void e100_adjust_adaptive_ifs(e100_dev_t *e100, int speed, int duplex)
{
/* Adjust inter-frame-spacing (IFS) between two transmits if
* we're getting collisions on a half-duplex connection. */
if (duplex == 0) {
u32 prev = e100->adaptive_ifs;
u32 min_frames = (speed == 100) ? 1000 : 100;
if ((e100->tx_frames / 32 < e100->tx_collisions) &&
(e100->tx_frames > min_frames)) {
if (e100->adaptive_ifs < 60)
e100->adaptive_ifs += 5;
} else if (e100->tx_frames < min_frames) {
if (e100->adaptive_ifs >= 5)
e100->adaptive_ifs -= 5;
}
if (e100->adaptive_ifs != prev)
e100_exec_cb(e100, NULL, e100_configure);
}
if (e100->flags & ich && speed == 10 && duplex == 0)
/* Need SW workaround for ICH[x] 10Mbps/half duplex Tx hang. */
e100->flags |= ich_10h_workaround;
else
e100->flags &= ~ich_10h_workaround;
}
void e100_update_stats(e100_dev_t *e100)
{
nic_stats_t *stats = &e100->stats;
nic_ethernet_stats_t *estats = &stats->un.estats;
struct stats *s = &e100->mem->stats;
__le32 *complete = (e100->mac < mac_82558_D101_A4) ? &s->fc_xmt_pause :
(e100->mac < mac_82559_D101M) ? (__le32 *)&s->xmt_tco_frames :
&s->complete;
/* Device's stats reporting may take several microseconds to
* complete, so we're always waiting for results of the
* previous command. */
if (*complete == cpu_to_le32(cuc_dump_reset_complete)) {
*complete = 0;
e100->tx_frames = le32_to_cpu(s->tx_good_frames);
e100->tx_collisions = le32_to_cpu(s->tx_total_collisions);
estats->xcoll_aborted += le32_to_cpu(s->tx_max_collisions);
estats->late_collisions += le32_to_cpu(s->tx_late_collisions);
estats->tx_deferred += le32_to_cpu(s->tx_deferred);
estats->single_collisions += le32_to_cpu(s->tx_single_collisions);
estats->multi_collisions += le32_to_cpu(s->tx_multiple_collisions);
estats->no_carrier += le32_to_cpu(s->tx_lost_crs);
estats->internal_tx_errors += le32_to_cpu(s->tx_underruns);
estats->total_collision_frames += e100->tx_collisions;
estats->short_packets += le32_to_cpu(s->rx_short_frame_errors);
estats->oversized_packets = e100->rx_over_length_errors;
estats->fcs_errors += le32_to_cpu(s->rx_crc_errors);
estats->align_errors += le32_to_cpu(s->rx_alignment_errors);
estats->internal_rx_errors += le32_to_cpu(s->rx_overrun_errors);
estats->internal_rx_errors += le32_to_cpu(s->rx_resource_errors);
estats->internal_rx_errors += le32_to_cpu(s->rx_cdt_errors);
if (e100->mac >= mac_82558_D101_A4) {
e100->tx_fc_pause += le32_to_cpu(s->fc_xmt_pause);
e100->rx_fc_pause += le32_to_cpu(s->fc_rcv_pause);
e100->rx_fc_unsupported +=
le32_to_cpu(s->fc_rcv_unsupported);
if (e100->mac >= mac_82559_D101M) {
e100->tx_tco_frames +=
le16_to_cpu(s->xmt_tco_frames);
e100->rx_tco_frames +=
le16_to_cpu(s->rcv_tco_frames);
}
}
}
if (e100_exec_cmd(e100, cuc_dump_reset, 0))
log(LOG_ERR, "%s: exec cuc_dump_reset failed", __devname__);
}

901
hardware/devnp/e100/e100_nto.c Обычный файл
Просмотреть файл

@ -0,0 +1,901 @@
/*
* (c) 2017-2019, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*****************************************************************************
* *
* Driver for the Intel 8255x 10/100 Mbps Ethernet Controller Family *
* Network controller main routines *
* *
*****************************************************************************/
#include "e100.h"
static int e100_attach(struct device *parent, struct device *self, void *aux);
static int e100_detach(struct device *dev, int flags);
static void e100_shutdown(void *arg);
static void e100_destroy(e100_dev_t *e100, enum destroy_variants dstage);
static int e100_alloc(e100_dev_t *e100);
static void e100_free(e100_dev_t *e100);
static int e100_inter_entry_setup(e100_dev_t *e100);
static void ifp_setup(e100_dev_t *e100, const char *ifname);
static int e100_parse_options(e100_dev_t *e100, const char *optstring, unsigned instance, nic_config_t *cfg);
static int e100_entry(void *dll_hdl, struct _iopkt_self *iopkt, char *options);
static void e100_hk_callout(void *arg);
int e100_init(struct ifnet *ifp);
void e100_stop(struct ifnet *ifp, int disable);
struct _iopkt_drvr_entry IOPKT_DRVR_ENTRY_SYM(e100) = IOPKT_DRVR_ENTRY_SYM_INIT(e100_entry);
CFATTACH_DECL(e100,
sizeof(struct e100_dev),
NULL,
e100_attach,
e100_detach,
NULL);
struct e100_attach_args {
void *dll_hdl;
char *options;
unsigned instance;
unsigned vendor_id;
unsigned device_id;
unsigned bus;
unsigned devfn;
int device_idx;
unsigned driver_data;
};
#define INTEL_8255X_ETHERNET_DEVICE(device_id, ich) {\
PCI_VENDOR_ID_INTEL, device_id, ich }
static struct pci_device_id known_device_ids[] = {
INTEL_8255X_ETHERNET_DEVICE(0x1029, 0),
INTEL_8255X_ETHERNET_DEVICE(0x1030, 0),
INTEL_8255X_ETHERNET_DEVICE(0x1031, 3),
INTEL_8255X_ETHERNET_DEVICE(0x1032, 3),
INTEL_8255X_ETHERNET_DEVICE(0x1033, 3),
INTEL_8255X_ETHERNET_DEVICE(0x1034, 3),
INTEL_8255X_ETHERNET_DEVICE(0x1035, 3),
INTEL_8255X_ETHERNET_DEVICE(0x1036, 3),
INTEL_8255X_ETHERNET_DEVICE(0x1037, 3),
INTEL_8255X_ETHERNET_DEVICE(0x1038, 3),
INTEL_8255X_ETHERNET_DEVICE(0x1039, 4),
INTEL_8255X_ETHERNET_DEVICE(0x103A, 4),
INTEL_8255X_ETHERNET_DEVICE(0x103B, 4),
INTEL_8255X_ETHERNET_DEVICE(0x103C, 4),
INTEL_8255X_ETHERNET_DEVICE(0x103D, 4),
INTEL_8255X_ETHERNET_DEVICE(0x103E, 4),
INTEL_8255X_ETHERNET_DEVICE(0x1050, 5),
INTEL_8255X_ETHERNET_DEVICE(0x1051, 5),
INTEL_8255X_ETHERNET_DEVICE(0x1052, 5),
INTEL_8255X_ETHERNET_DEVICE(0x1053, 5),
INTEL_8255X_ETHERNET_DEVICE(0x1054, 5),
INTEL_8255X_ETHERNET_DEVICE(0x1055, 5),
INTEL_8255X_ETHERNET_DEVICE(0x1056, 5),
INTEL_8255X_ETHERNET_DEVICE(0x1057, 5),
INTEL_8255X_ETHERNET_DEVICE(0x1059, 0),
INTEL_8255X_ETHERNET_DEVICE(0x1064, 6),
INTEL_8255X_ETHERNET_DEVICE(0x1065, 6),
INTEL_8255X_ETHERNET_DEVICE(0x1066, 6),
INTEL_8255X_ETHERNET_DEVICE(0x1067, 6),
INTEL_8255X_ETHERNET_DEVICE(0x1068, 6),
INTEL_8255X_ETHERNET_DEVICE(0x1069, 6),
INTEL_8255X_ETHERNET_DEVICE(0x106A, 6),
INTEL_8255X_ETHERNET_DEVICE(0x106B, 6),
INTEL_8255X_ETHERNET_DEVICE(0x1091, 7),
INTEL_8255X_ETHERNET_DEVICE(0x1092, 7),
INTEL_8255X_ETHERNET_DEVICE(0x1093, 7),
INTEL_8255X_ETHERNET_DEVICE(0x1094, 7),
INTEL_8255X_ETHERNET_DEVICE(0x1095, 7),
INTEL_8255X_ETHERNET_DEVICE(0x10fe, 7),
INTEL_8255X_ETHERNET_DEVICE(0x1209, 0),
INTEL_8255X_ETHERNET_DEVICE(0x1229, 0),
INTEL_8255X_ETHERNET_DEVICE(0x2449, 2),
INTEL_8255X_ETHERNET_DEVICE(0x2459, 2),
INTEL_8255X_ETHERNET_DEVICE(0x245D, 2),
INTEL_8255X_ETHERNET_DEVICE(0x27DC, 7),
{ 0, }
};
static int e100_entry(void *dll_hdl, struct _iopkt_self *iopkt, char *options)
{
nic_config_t cfg;
int devid, idx;
unsigned bus, dev_func;
int rc;
int instance, status, single;
struct e100_attach_args e100_args;
struct device *dev;
single = 0;
memset(&cfg, 0, sizeof(cfg));
if (options != NULL) {
cfg.vendor_id = cfg.device_id = cfg.device_index = 0xffffffff;
if ((rc = e100_parse_options(NULL, options, -1, &cfg)) != 0)
return rc;
if (cfg.device_id != 0xffffffff) {
if (cfg.vendor_id == 0xffffffff) {
cfg.vendor_id = PCI_VENDOR_ID_INTEL;
}
known_device_ids[0].device = cfg.device_id;
known_device_ids[0].vendor = cfg.vendor_id;
known_device_ids[1].vendor = known_device_ids[1].device = 0;
}
} else {
cfg.device_index = 0xffffffff;
}
if (nw_pci_hdl == -1) {
log(LOG_ERR, "%s: Could not attach to PCI server", "fxp*");
return EAGAIN;
}
rc = ENODEV;
instance = 0;
for (devid = 0; known_device_ids[devid].vendor != 0; devid++) {
idx = ((cfg.device_index == 0xffffffff) ? 0 : cfg.device_index);
while (1) {
E100_DEBUG2(
log(LOG_DEBUG, "fxp%d: PCI find device %04X:%04X.%d", instance, known_device_ids[devid].vendor, known_device_ids[devid].device, idx)
);
if ((status = pci_find_device(known_device_ids[devid].device,
known_device_ids[devid].vendor, idx, &bus, &dev_func)) !=
PCI_SUCCESS)
break;
e100_args.dll_hdl = dll_hdl;
e100_args.instance = instance;
e100_args.options = options;
e100_args.vendor_id = known_device_ids[devid].vendor;
e100_args.device_id = known_device_ids[devid].device;
e100_args.bus = bus;
e100_args.devfn = dev_func;
e100_args.device_idx = idx;
e100_args.driver_data = known_device_ids[devid].driver_data;
dev = NULL;
if ((rc = dev_attach("fxp", options, &e100_ca,
&e100_args, &single, &dev, NULL)) != EOK) {
goto done;
}
instance++;
if (cfg.device_index != 0xffffffff || single != 0)
goto done;
idx++;
}
}
done:
if (instance)
return EOK;
return rc;
}
enum e100_opts_enum {
E100_RECEIVE,
E100_TRANSMIT,
E100_TX_REAP,
E100_USE_IO,
E100_KERMASK,
E100_EEPROM_BAD_CSUM_ALLOW,
E100_FORCE_RX_BUG,
E100_EXTRA_RNR,
E100_HK_TIMEOUT,
TYPED_MEM
};
static char *e100_opts[] = {
"receive",
"transmit",
"tx_reap",
"use_io",
"kermask",
"eeprom_bad_csum_allow",
"force_rx_bug",
"extra_rnr",
"hk_timeout",
"typed_mem",
NULL
};
static int e100_parse_options(e100_dev_t *e100, const char *optstring, unsigned instance, nic_config_t *cfg)
{
char *value;
int opt;
char *options, *freeptr;
char *c;
int rc = 0;
if ( e100 != NULL ) {
e100->num_transmit = -1;
e100->num_receive = -1;
e100->tx_reap = -1;
e100->hk_timeout = E100_DEFAULT_HK_TIMEOUT;
#ifdef __E2K__
e100->tmem = "/memory/below4G/ram";
#endif
}
if (optstring == NULL)
return 0;
/* getsubopt() is destructive */
options = malloc(strlen(optstring) + 1, M_TEMP, M_NOWAIT);
if (options == NULL)
return ENOMEM;
strcpy(options, optstring);
freeptr = options;
while (options && *options != '\0') {
c = options;
if ((opt = getsubopt(&options, e100_opts, &value)) == -1) {
if (nic_parse_options(cfg, value) == EOK)
continue;
goto error;
}
if (e100 == NULL)
continue;
switch (opt) {
case E100_RECEIVE:
e100->num_receive = strtoul(value, 0, 0);
continue;
case E100_TRANSMIT:
e100->num_transmit = strtoul(value, 0, 0);
continue;
case E100_TX_REAP:
e100->tx_reap = strtoul(value, 0, 0);
continue;
case E100_USE_IO:
e100->use_io = 1;
continue;
case E100_KERMASK:
if (value != NULL) {
e100->kermask = strtol(value, 0, 0);
if (e100->kermask != 0)
e100->kermask = 1;
}
continue;
case E100_EEPROM_BAD_CSUM_ALLOW:
if (value != NULL) {
e100->eeprom_bad_csum_allow = strtoul(value, 0, 0);
}
continue;
case E100_FORCE_RX_BUG:
e100->force_rx_bug = 1;
continue;
case E100_EXTRA_RNR:
e100->extra_rnr = 1;
continue;
case E100_HK_TIMEOUT:
if (value != NULL) {
e100->hk_timeout = strtoul(value, 0, 0);
}
continue;
case TYPED_MEM:
if (value != NULL)
e100->tmem = value;
continue;
}
error:
log(LOG_WARNING, "fxp%d: unknown option %s", instance, c);
rc = EINVAL;
}
free(freeptr, M_TEMP);
return rc;
}
static int e100_inter_entry_setup(e100_dev_t *e100)
{
int rc = 0;
if ((rc = interrupt_entry_init(&e100->inter, 0, NULL, e100->cfg.priority)) != EOK) {
return rc;
}
e100->inter.func = e100_process_interrupt;
if (e100->kermask == 0) {
e100->inter.enable = e100_enable_interrupt;
e100->isrp = e100_isr;
}
else {
e100->inter.enable = e100_enable_interrupt_kermask;
e100->isrp = e100_isr_kermask;
}
e100->inter.arg = e100;
return rc;
}
static void ifp_setup(e100_dev_t *e100, const char *ifname)
{
struct ifnet *ifp;
ifp = &e100->ecom.ec_if;
ifp->if_softc = e100;
strcpy(ifp->if_xname, ifname);
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
if ( (e100->cfg.flags & NIC_FLAG_MULTICAST) ) {
ifp->if_flags |= IFF_MULTICAST;
}
ifp->if_ioctl = e100_ioctl;
ifp->if_start = e100_start;
ifp->if_init = e100_init;
ifp->if_stop = e100_stop;
IFQ_SET_READY(&ifp->if_snd);
strcpy((char *)e100->cfg.device_description, "Intel 8255x 10/100 Mbps");
if_attach(ifp);
ether_ifattach(ifp, e100->cfg.current_address);
}
static int e100_alloc(e100_dev_t *e100)
{
e100->mem = (struct mem *)mmap(0, sizeof(struct mem), e100->prot_flags, e100->map_flags, e100->tmem_fd, 0);
if (e100->mem == (struct mem *)MAP_FAILED) {
return errno;
}
memset(e100->mem, 0, sizeof(struct mem));
e100->dma_addr = (dma_addr_t)drvr_mphys(e100->mem);
return 0;
}
static void e100_free(e100_dev_t *e100)
{
if (e100->mem) {
munmap(e100->mem, sizeof(struct mem));
e100->mem = NULL;
}
}
static int e100_attach(struct device *parent, struct device *self, void *aux)
{
struct e100_dev *idev;
e100_dev_t *e100;
nic_config_t *cfg;
void *head;
struct pci_dev_info *pci_info;
int rc;
struct e100_attach_args *e100_args;
idev = (struct e100_dev *) self;
head = idev->filler;
e100 = NET_CACHELINE_ALIGN (head);
idev->sc_e100 = e100;
e100_args = aux;
strcpy(e100->ecom.ec_if.if_xname, self->dv_xname);
self->dv_dll_hdl = e100_args->dll_hdl;
E100_DEBUG2(log(LOG_DEBUG, "%s: attach start", __devname__));
e100->iopkt = iopkt_selfp;
e100->iid = -1;
cfg = &e100->cfg;
/* set some defaults for the command line options */
cfg->flags = NIC_FLAG_MULTICAST;
cfg->device_index = e100_args->device_idx;
pci_info = &e100->pci_info;
memset ((char *)pci_info, 0, sizeof (struct pci_dev_info));
pci_info->VendorId = cfg->vendor_id = e100_args->vendor_id;
pci_info->DeviceId = cfg->device_id = e100_args->device_id;
cfg->media_rate = cfg->duplex = -1;
cfg->priority = IRUPT_PRIO_DEFAULT;
cfg->iftype = IFT_ETHER;
cfg->lan = -1;
strcpy((char *)cfg->uptype, "en");
e100->mdio_ctrl = mdio_ctrl_hw;
// #ifdef __X86__
// e100->use_io = 1;
// #else
e100->use_io = 0;
// #endif
if ((rc = e100_parse_options(e100, e100_args->options, e100_args->instance, cfg)) != 0) {
log(LOG_ERR, "%s: error parsing options: %d", __devname__, rc);
e100_destroy(e100, ATTACH_DONE);
return rc;
}
E100_DEBUG(log(LOG_DEBUG, "%s: CBS {%d %d %d}", __devname__, e100->params.cbs.min, e100->params.cbs.max, e100->params.cbs.count));
E100_DEBUG(log(LOG_DEBUG, "%s: RFDS {%d %d %d}", __devname__, e100->params.rfds.min, e100->params.rfds.max, e100->params.rfds.count));
cfg->lan = idev->sc_dev.dv_unit;
if (e100->cfg.mtu == 0 || e100->cfg.mtu > ETH_MAX_PKT_LEN)
e100->cfg.mtu = ETH_MAX_PKT_LEN;
if (e100->cfg.mru == 0 || e100->cfg.mru > ETH_MAX_PKT_LEN)
e100->cfg.mru = ETH_MAX_PKT_LEN;
e100->cfg.revision = NIC_CONFIG_REVISION;
if ( (rc = nic_force_advertise(e100)) != 0 ) {
e100_destroy(e100, ATTACH_DONE);
return rc;
}
if (nw_pci_hdl == -1) {
log(LOG_ERR, "%s: unable to locate pci server", __devname__);
e100_destroy(e100, ATTACH_DONE);
return EAGAIN;
}
setup_tmem(e100);
setup_mmap_flags(e100);
if ( (rc = pci_enable_device(e100)) ) {
log(LOG_ERR, "%s: pci_enable_device: %d", __devname__, rc);
e100_destroy(e100, ATTACH_DONE);
return rc;
}
if (cfg->num_irqs == 0) {
cfg->irq[0] = pci_info->Irq;
cfg->num_irqs = 1;
}
if (cfg->num_io_windows == 0) {
cfg->io_window_base[0] = PCI_IO_ADDR(pci_info->CpuBaseAddress[1]);
cfg->io_window_size[0] = pci_info->BaseAddressSize[1];
cfg->num_io_windows = 1;
}
if (cfg->num_mem_windows == 0) {
cfg->mem_window_base[0] = PCI_MEM_ADDR(pci_info->CpuBaseAddress[0]);
cfg->mem_window_size[0] = pci_info->BaseAddressSize[0];
cfg->num_mem_windows = 1;
}
if (pci_info->PciRom)
cfg->rom_base = PCI_ROM_ADDR(pci_info->CpuRom);
e100->bmstr = pci_info->CpuBmstrTranslation;
evcnt_attach_dynamic(&e100->ev_txdrop, EVCNT_TYPE_MISC, NULL, self->dv_xname, "txdrop");
e100->cachectl.fd = NOFD;
if (cache_init(0, &e100->cachectl, NULL) == -1) {
rc = errno;
log(LOG_ERR, "%s: cache_init: %d", __devname__, rc);
e100_destroy(e100, EVCNT_DONE);
return rc;
}
if (!e100->use_io) {
e100->csr = (struct csr *)mmap_device_memory(NULL,
/*sizeof(struct csr)*/ e100->cfg.mem_window_size[0],
e100->prot_flags | PROT_NOCACHE, 0,
e100->cfg.mem_window_base[0]);
if (e100->csr == (struct csr *)MAP_FAILED) {
rc = errno;
log(LOG_ERR,
"%s: Unable to mmap_device_memory: %d.", __devname__, rc);
e100_destroy(e100, CACHE_INIT_DONE);
return rc;
}
} else {
e100->csr = (struct csr *)mmap_device_io(sizeof(struct csr),
e100->cfg.io_window_base[0]);
if (e100->csr == (struct csr *)MAP_DEVICE_FAILED) {
rc = errno;
log(LOG_ERR,
"%s: Unable to mmap_device_io: %d.", __devname__, rc);
e100_destroy(e100, CACHE_INIT_DONE);
return rc;
}
}
if (e100_args->driver_data)
e100->flags |= ich;
else
e100->flags &= ~ich;
/* Ethernet stats we are interested in */
e100->stats.un.estats.valid_stats =
NIC_ETHER_STAT_XCOLL_ABORTED |
NIC_ETHER_STAT_SINGLE_COLLISIONS |
NIC_ETHER_STAT_MULTI_COLLISIONS |
NIC_ETHER_STAT_LATE_COLLISIONS |
NIC_ETHER_STAT_NO_CARRIER |
NIC_ETHER_STAT_TX_DEFERRED |
NIC_ETHER_STAT_INTERNAL_TX_ERRORS |
NIC_ETHER_STAT_TOTAL_COLLISION_FRAMES |
NIC_ETHER_STAT_SHORT_PACKETS |
NIC_ETHER_STAT_OVERSIZED_PACKETS |
NIC_ETHER_STAT_FCS_ERRORS |
NIC_ETHER_STAT_ALIGN_ERRORS |
NIC_ETHER_STAT_INTERNAL_RX_ERRORS;
/* Generic networking stats we are interested in */
e100->stats.valid_stats =
NIC_STAT_TX_FAILED_ALLOCS | NIC_STAT_RX_FAILED_ALLOCS |
NIC_STAT_TXED_BROADCAST |
NIC_STAT_RXED_BROADCAST;
if ( (cfg->flags & NIC_FLAG_MULTICAST) ) {
e100->stats.valid_stats |= NIC_STAT_TXED_MULTICAST | NIC_STAT_RXED_MULTICAST;
}
// memset(&e100->cmd_lock, 0, sizeof(e100->cmd_lock));
// memset(&e100->cb_lock, 0, sizeof(e100->cb_lock));
atomic_clr(&e100->irq_sw_gen, 0xFFFFFFFF);
atomic_clr(&e100->stopping, 0xFFFFFFFF);
/* Reset the device before pci_set_master in case device is in some
* funky state and has an interrupt pending - hint: we don't have the
* interrupt handler registered yet. */
e100_hw_reset(e100);
// if ( (rc = pci_set_master(e100)) ) {
// log(LOG_ERR, "%s: pci_set_master: %d", __devname__, rc);
// e100_destroy(e100, MMAP_CSR_DONE);
// return rc;
// }
if ( (rc = e100_eeprom_load(e100)) ) {
e100_destroy(e100, PCI_MASTER_DONE);
return rc;
}
e100_get_defaults(e100);
/* D100 MAC doesn't allow rx of vlan packets with normal MTU */
if (e100->mac >= mac_82558_D101_A4)
e100->ecom.ec_capabilities |= ETHERCAP_VLAN_MTU;
if ((rc = e100_alloc(e100))) {
log(LOG_ERR, "%s: Cannot alloc driver memory, aborting", __devname__);
e100_destroy(e100, PCI_MASTER_DONE);
return rc;
}
if ( (rc = e100_findphy(e100)) ) {
e100_destroy(e100, MMAP_E100_DONE);
return rc;
}
/* Copy MAC address from EEPROM */
memcpy(e100->cfg.permanent_address, e100->eeprom, ETH_MAC_LEN);
E100_DEBUG(log(LOG_DEBUG, "%s: eeprom MAC:%02X:%02X:%02X:%02X:%02X:%02X", __devname__,
e100->cfg.permanent_address[0], e100->cfg.permanent_address[1],
e100->cfg.permanent_address[2], e100->cfg.permanent_address[3],
e100->cfg.permanent_address[4], e100->cfg.permanent_address[5]));
if ( !valid_mac_addr((char *)e100->cfg.permanent_address) ) {
if (!e100->eeprom_bad_csum_allow) {
log(LOG_ERR, "%s: Invalid MAC address from EEPROM, aborting", __devname__);
e100_destroy(e100, MMAP_E100_DONE);
} else {
log(LOG_WARNING, "%s: Invalid MAC address from EEPROM, using random.", __devname__);
fill_random_mac((char *)e100->cfg.permanent_address);
}
}
/* Override with MAC address from syspage, if there is one */
nic_get_syspage_mac((char *)e100->cfg.permanent_address);
/* check for command line override */
if ( !valid_mac_addr((char *)e100->cfg.current_address) )
memcpy(e100->cfg.current_address, e100->cfg.permanent_address, ETH_MAC_LEN);
// FIXME needed there or in _init()?
if ( (rc = e100_alloc_cbs(e100)) ) {
log(LOG_ERR,
"%s: Unable to alloc cbs: %d.", __devname__, rc);
e100_destroy(e100, MMAP_E100_DONE);
return rc;
}
if ( (rc = e100_rx_alloc_list(e100)) ) {
log(LOG_ERR,
"%s: Unable to alloc rxs: %d.", __devname__, rc);
e100_destroy(e100, ALLOC_CBS_DONE);
return rc;
}
if ((rc = e100_inter_entry_setup(e100)) != 0) {
e100_destroy(e100, ALLOC_RXS_DONE);
return rc;
}
// hook up so media devctls work
bsd_mii_initmedia(e100);
ifp_setup(e100, self->dv_xname);
if ((e100->sd_hook = shutdownhook_establish(e100_shutdown,
e100)) == NULL) {
e100_destroy(e100, IF_SETUP_DONE);
return ENOMEM;
}
if ( e100->cfg.verbose ) {
log(LOG_INFO, "%s:", __devname__);
nic_dump_config(&e100->cfg);
}
return 0;
}
static void e100_shutdown(void *arg)
{
/* All of io-pkt is going away. Just quiet hardware. */
e100_destroy((e100_dev_t *)arg, DESTROY_ALL);
}
static int e100_detach(struct device *dev, int flags)
{
struct e100_dev *kdev;
e100_dev_t *e100;
kdev = (struct e100_dev *)dev;
e100 = kdev->sc_e100;
e100_destroy(e100, DESTROY_ALL);
return 0;
}
static pthread_mutex_t e100_destroy_mutex = PTHREAD_MUTEX_INITIALIZER;
static void e100_destroy(e100_dev_t *e100, enum destroy_variants dstage)
{
struct ifnet *ifp;
ifp = &e100->ecom.ec_if;
E100_DEBUG(log(LOG_DEBUG, "%s: destroy with stage %d", __devname__, (int)dstage));
pthread_mutex_lock(&e100_destroy_mutex);
if (e100->dying) {
goto destroy_done;
}
switch (dstage) {
case DESTROY_ALL:
/* Don't init() while we're dying. */
e100->dying = 1;
e100_stop(ifp, 1);
case IF_SETUP_DONE:
ether_ifdetach(ifp);
if_detach(ifp);
case INTER_ENTRY_DONE:
if (ISSTACK) {
if ( e100->inter.func ) {
interrupt_entry_remove(&e100->inter, NULL); /* Must be 'the stack' to call this */
}
} else {
log(LOG_ERR, "%s: stop: unexpected context", __devname__);
}
case ALLOC_RXS_DONE:
e100_rx_clean_list(e100, 1);
case ALLOC_CBS_DONE:
e100_clean_cbs(e100, 1);
case MMAP_E100_DONE:
e100_free(e100);
case PCI_MASTER_DONE:
// pci_disable_device(e100);
case MMAP_CSR_DONE:
if (e100->use_io) {
munmap_device_io((uintptr_t)e100->csr, sizeof(struct csr));
} else {
munmap_device_memory((void *)e100->csr, sizeof(struct csr));
}
case CACHE_INIT_DONE:
cache_fini(&e100->cachectl);
case EVCNT_DONE:
evcnt_detach (&e100->ev_txdrop);
case PCI_ENABLE_DONE:
pci_disable_device(e100);
case ATTACH_DONE:
break;
}
if (e100->sd_hook != NULL)
shutdownhook_disestablish (e100->sd_hook);
destroy_done:
pthread_mutex_unlock(&e100_destroy_mutex);
}
static void e100_hk_callout(void *arg)
{
e100_dev_t *e100 = arg;
struct ifnet *ifp = &e100->ecom.ec_if;
struct nw_work_thread *wtp = WTP;
/* Software generated interrupt to recover from (rare) Rx
* allocation failure.
* Unfortunately have to use a spinlock to not re-enable interrupts
* accidentally, due to hardware that shares a register between the
* interrupt mask bit and the SW Interrupt generation bit */
// InterruptLock(&e100->cmd_lock);
atomic_set(&e100->irq_sw_gen, irq_sw_gen);
// iowrite8(ioread8(&e100->csr->scb.cmd_hi) | irq_sw_gen, &e100->csr->scb.cmd_hi);
// e100_write_flush(e100);
// InterruptUnlock(&e100->cmd_lock);
e100_update_stats(e100);
NW_SIGLOCK_P(&ifp->if_snd_ex, iopkt_selfp, wtp);
if (e100->mac <= mac_82557_D100_C || e100->force_rx_bug)
/* Issue a multicast command to workaround a 557 lock up */
e100_set_multicast_list(e100);
e100_tx_clean(e100);
E100_DEBUG(
log(LOG_DEBUG, "%s: %s() stat_ack %02X status %02X mask %02X", __devname__, __func__, ioread8(&e100->csr->scb.stat_ack), ioread8(&e100->csr->scb.status), ioread8(&e100->csr->scb.cmd_hi));
)
NW_SIGUNLOCK_P(&ifp->if_snd_ex, iopkt_selfp, wtp);
callout_msec(&e100->hk_callout, e100->hk_timeout * 1000, e100_hk_callout, e100);
}
int e100_init(struct ifnet *ifp)
{
int ret;
e100_dev_t *e100;
struct _iopkt_self *iopkt;
struct nw_work_thread *wtp;
/*
* - enable hardware.
* - look at ifp->if_capenable_[rx/tx]
* - enable promiscuous / multicast filter.
* - attach to interrupt.
*/
e100 = (e100_dev_t *)ifp->if_softc;
if (e100->dying == 1)
return 0;
E100_DEBUG(log(LOG_DEBUG, "%s: %s() start", __devname__, __func__));
iopkt = e100->iopkt;
wtp = WTP;
e100_stop(ifp, 0);
NW_SIGLOCK_P(&ifp->if_snd_ex, iopkt, wtp);
ifp->if_flags_tx |= IFF_OACTIVE;
E100_DEBUG(log(LOG_DEBUG, "%s: %s() e100->cbs %p", __devname__, __func__, e100->cbs));
if ((ret = e100_rx_init_list(e100, wtp))) {
log(LOG_ERR, "%s: Receive list init is failed: %d", __devname__, ret);
goto do_err;
}
E100_DEBUG(log(LOG_DEBUG, "%s: %s() e100->cbs0 %p", __devname__, __func__, e100->cbs));
if ((ret = e100_hw_init(e100))) {
log(LOG_ERR, "%s: Hardware init is failed: %d", __devname__, ret);
goto err_rx_clean_list;
}
E100_DEBUG(log(LOG_DEBUG, "%s: %s() e100->cbs1 %p", __devname__, __func__, e100->cbs));
e100_set_multicast_list(e100);
E100_DEBUG(log(LOG_DEBUG, "%s: %s() e100->cbs2 %p", __devname__, __func__, e100->cbs));
e100_start_receiver(e100, NULL);
E100_DEBUG(log(LOG_DEBUG, "%s: %s() e100->cbs3 %p", __devname__, __func__, e100->cbs));
callout_msec (&e100->mii_callout, 3 * 1000, e100_mii_callout, e100);
E100_DEBUG(log(LOG_DEBUG, "%s: %s() e100->cbs4 %p", __devname__, __func__, e100->cbs));
// start housekeeping callout - give it 10 seconds
callout_msec(&e100->hk_callout, e100->hk_timeout * 1000, e100_hk_callout, e100);
E100_DEBUG(log(LOG_DEBUG, "%s: %s() e100->cbs5 %p", __devname__, __func__, e100->cbs));
if (e100->iid == -1) {
if ((ret = InterruptAttach_r(e100->cfg.irq[0], e100->isrp,
e100, sizeof(*e100), _NTO_INTR_FLAGS_TRK_MSK)) < 0) {
ret = -ret;
log(LOG_ERR, "%s: Interrupt attach for %X failed", __devname__, e100->cfg.irq[0]);
goto do_err;
}
e100->iid = ret;
}
atomic_clr(&e100->stopping, 0xFFFFFFFF);
E100_DEBUG(log(LOG_DEBUG, "%s: %s() e100->cbs6 %p", __devname__, __func__, e100->cbs));
e100_enable_irq(e100);
E100_DEBUG(log(LOG_DEBUG, "%s: %s() e100->cbs7 %p e100 %p", __devname__, __func__, e100->cbs, e100));
// get mtu from stack, mostly for nicinfo
e100->cfg.mtu = ifp->if_mtu;
e100->cfg.mru = ifp->if_mtu;
ifp->if_flags_tx |= IFF_RUNNING;
ifp->if_flags_tx &= ~IFF_OACTIVE;
NW_SIGUNLOCK_P(&ifp->if_snd_ex, iopkt, wtp);
ifp->if_flags |= IFF_RUNNING;
return EOK;
err_rx_clean_list:
e100_rx_clean_list(e100, 0);
do_err:
ifp->if_flags_tx &= ~IFF_OACTIVE;
NW_SIGUNLOCK_P(&ifp->if_snd_ex, iopkt, wtp);
log(LOG_ERR, "%s: not running.", __devname__);
return ret;
}
void e100_stop(struct ifnet *ifp, int disable)
{
e100_dev_t *e100 = ifp->if_softc;
struct _iopkt_self *iopkt = e100->iopkt;
struct nw_work_thread *wtp = WTP;
int i;
/*
* - Cancel any pending io
* - Clear any interrupt source registers
* - Clear any interrupt pending registers
* - Release any queued transmit buffers.
*/
atomic_set(&e100->stopping, 0x1);
i = 0;
e100_disable_irq(e100);
E100_DEBUG(log(LOG_DEBUG, "%s: %s() start", __devname__, __func__));
E100_DEBUG(log(LOG_DEBUG, "%s: %s() e100->cbs %p", __devname__, __func__, e100->cbs));
/* Shut off the housekeeping & mii callout */
callout_stop(&e100->hk_callout);
callout_stop(&e100->mii_callout);
if (e100->mdi) {
MDI_DeRegister((mdi_t **)&e100->mdi);
e100->mdi = NULL;
}
e100_hw_reset(e100);
E100_DEBUG(log(LOG_DEBUG, "%s: %s() e100->cbs2 %p", __devname__, __func__, e100->cbs));
/* Lock out the transmit side */
NW_SIGLOCK_P(&ifp->if_snd_ex, iopkt, wtp);
for (i = 0; i < 10; i++) {
if ((ifp->if_flags_tx & IFF_OACTIVE) == 0)
break;
NW_SIGUNLOCK_P(&ifp->if_snd_ex, iopkt, wtp);
delay(50);
NW_SIGLOCK_P(&ifp->if_snd_ex, iopkt, wtp);
}
if (i < 10) {
ifp->if_flags_tx &= ~IFF_RUNNING;
NW_SIGUNLOCK_P(&ifp->if_snd_ex, iopkt, wtp);
}
else {
/* Heavy load or bad luck. Try the big gun. */
quiesce_all();
ifp->if_flags_tx &= ~IFF_RUNNING;
unquiesce_all();
}
e100_clean_cbs(e100, 0);
if (disable) {
if (e100->iid != -1) {
InterruptDetach(e100->iid);
e100->iid = -1;
}
e100_rx_clean_list(e100, 0);
}
E100_DEBUG(log(LOG_DEBUG, "%s: %s() e100->cbs3 %p", __devname__, __func__, e100->cbs));
E100_DEBUG(log(LOG_DEBUG, "%s: %s() e100->cbs4 %p", __devname__, __func__, e100->cbs));
/* Mark the interface as down */
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
}

2
hardware/devnp/e100/e2k/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1,2 @@
LIST=VARIANT
include recurse.mk

1
hardware/devnp/e100/e2k/dll.le/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1 @@
include ../../common.mk

Двоичные данные
hardware/devnp/e100/firmware/d101m_ucode.bin Обычный файл

Двоичный файл не отображается.

Двоичные данные
hardware/devnp/e100/firmware/d101s_ucode.bin Обычный файл

Двоичный файл не отображается.

Двоичные данные
hardware/devnp/e100/firmware/d102e_ucode.bin Обычный файл

Двоичный файл не отображается.

62
hardware/devnp/e100/firmware/mkblob.sh Исполняемый файл
Просмотреть файл

@ -0,0 +1,62 @@
#!/usr/bin/env ksh
target="$1"
shift
header="$1"
shift
firmwares="$@"
if [ -z "$firmwares" -o -z "$target" -o -z "$header" ]; then
echo "Nothing to do"
exit 0
fi
nblob=0
for blob in $firmwares; do
((label=nblob+1))
bname="$(basename $blob .bin)"
firmwares_start[$nblob]=_${bname}_start
firmwares_size[$nblob]=_${bname}_size
cat >>"$target"<<EOF
.global ${firmwares_start[$nblob]}
.global ${firmwares_size[$nblob]}
.section .rodata
${firmwares_start[$nblob]}:
.incbin "${blob}"
.align 2
${label}:
${firmwares_size[$nblob]}:
.int ${label}b - ${firmwares_start[$nblob]}
.int 0
EOF
BNAME="$(echo "${bname}" | awk -F_ '{ print toupper($1) }')"
cat >>"$header"<<EOF
#define FIRMWARE_${BNAME} "${bname}"
extern const uint32_t ${firmwares_start[$nblob]};
extern const size_t ${firmwares_size[$nblob]};
EOF
firmwares_name[$nblob]=FIRMWARE_${BNAME}
((nblob++))
done
cat >>"$header"<<EOF
#define FIRMWARE_CNT ${nblob}
struct firmware_list firmwares[FIRMWARE_CNT];
static void init_firmwares(void)
{
EOF
n=0
while [ $n -lt $nblob ]; do
cat >>"$header"<<EOF
firmwares[$n].start = FROM_POINTER_TO_INT(&${firmwares_start[$n]}, uint32_t);
firmwares[$n].size = ${firmwares_size[$n]};
firmwares[$n].name = ${firmwares_name[$n]};
EOF
((n++))
done
cat >>"$header"<<EOF
}
EOF

497
hardware/devnp/e100/mii.c Обычный файл
Просмотреть файл

@ -0,0 +1,497 @@
/*
* (c) 2016-2019, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*****************************************************************************
* *
* Driver for the Intel 8255x 10/100 Mbps Ethernet Controller Family *
* Network controller MII PHY routines *
* *
*****************************************************************************/
#include "e100.h"
void e100_mdi_callback (void *hdl, uint8_t phy_id, uint8_t link_state)
{
e100_dev_t *e100 = (e100_dev_t *)hdl;
int i, mode;
char *s;
struct ifnet *ifp = &e100->ecom.ec_if;
switch (link_state) {
case MDI_LINK_UP:
if ((i = MDI_GetActiveMedia(e100->mdi, e100->cfg.phy_addr, &mode)) != MDI_LINK_UP) {
if (e100->cfg.verbose > 8)
slogf(_SLOGC_NETWORK, _SLOG_INFO, "%s: callback GetActiveMedia returned %x", __devname__, i);
mode = 0;
}
switch (mode) {
case MDI_10bTFD:
s = "10 BaseT Full Duplex";
e100->cfg.duplex = 1;
e100->cfg.media_rate = 10000L;
break;
case MDI_10bT:
s = "10 BaseT Half Duplex";
e100->cfg.duplex = 0;
e100->cfg.media_rate = 10000L;
break;
case MDI_100bTFD:
s = "100 BaseT Full Duplex";
e100->cfg.duplex = 1;
e100->cfg.media_rate = 100000L;
break;
case MDI_100bT:
s = "100 BaseT Half Duplex";
e100->cfg.duplex = 0;
e100->cfg.media_rate = 100000L;
break;
case MDI_100bT4:
s = "100 BaseT4";
e100->cfg.duplex = 0;
e100->cfg.media_rate = 100000L;
break;
case MDI_1000bT:
s = "1000 BaseT Half Duplex";
e100->cfg.duplex = 0;
e100->cfg.media_rate = 1000000L;
break;
case MDI_1000bTFD:
s = "1000 BaseT Full Duplex";
e100->cfg.duplex = 1;
e100->cfg.media_rate = 1000000L;
break;
default:
s = "Unknown";
e100->cfg.duplex = 0;
e100->cfg.media_rate = 0L;
break;
}
if (e100->cfg.verbose) {
log(LOG_INFO, "%s: Link up (%s)", __devname__, s);
}
e100->cfg.flags &= ~NIC_FLAG_LINK_DOWN;
if_link_state_change(ifp, LINK_STATE_UP);
if (mode)
e100_adjust_adaptive_ifs(e100, e100->cfg.media_rate / 1000, e100->cfg.duplex);
break;
case MDI_LINK_DOWN:
e100->cfg.media_rate = e100->cfg.duplex = -1;
// MDI_ResetPhy (rtl->mdi, rtl->cfg.phy_addr, WaitBusy);
// MDI_SyncPhy (rtl->mdi, rtl->cfg.phy_addr);
MDI_AutoNegotiate (e100->mdi, e100->cfg.phy_addr, NoWait);
e100->cfg.flags |= NIC_FLAG_LINK_DOWN;
if (e100->cfg.verbose) {
slogf(_SLOGC_NETWORK, _SLOG_INFO, "%s: Link down %d", __devname__, e100->cfg.lan);
}
if_link_state_change(ifp, LINK_STATE_DOWN);
break;
default:
if (e100->cfg.verbose) {
log(LOG_INFO, "%s: Unknown link state %hhu", __devname__, link_state);
}
break;
}
}
void e100_mii_callout(void *arg)
{
e100_dev_t *e100 = arg;
if ((e100->cfg.flags & NIC_FLAG_LINK_DOWN) ||
(e100->cfg.media_rate <= 0) ||
!e100->pkts_received) {
if (e100->cfg.verbose > 12)
log(LOG_DEBUG, "%s: calling MDI_MonitorPhy()", __devname__);
MDI_MonitorPhy (e100->mdi);
}
e100->pkts_received = 0;
callout_msec(&e100->mii_callout, 3 * 1000, e100_mii_callout, e100);
}
uint16_t mii_read(void *hdl, uint8_t phy_id, uint8_t reg_num)
{
e100_dev_t *e100 = (e100_dev_t *)hdl;
return e100->mdio_ctrl(e100, phy_id, mdi_read, reg_num, 0);
}
void mii_write(void *hdl, uint8_t phy_id, uint8_t reg_num, uint16_t val)
{
e100_dev_t *e100 = (e100_dev_t *)hdl;
e100->mdio_ctrl(e100, phy_id, mdi_write, reg_num, val);
}
/* the standard mdio_ctrl() function for usual MII-compliant hardware */
u16 mdio_ctrl_hw(e100_dev_t *e100, u32 addr, u32 dir, u32 reg, u16 data)
{
u32 data_out = 0;
unsigned int i;
for (i = 100; i; --i) {
if (ioread32(&e100->csr->mdi_ctrl) & mdi_ready)
break;
udelay(20);
}
if (unlikely(!i)) {
log(LOG_DEBUG, "%s: e100.mdio_ctrl won't go Ready", __devname__);
return 0; /* No way to indicate timeout error */
}
iowrite32((reg << 16) | (addr << 21) | dir | data, &e100->csr->mdi_ctrl);
for (i = 0; i < 100; i++) {
udelay(20);
if ((data_out = ioread32(&e100->csr->mdi_ctrl)) & mdi_ready)
break;
}
E100_DEBUG2(
log(LOG_DEBUG, "%s: %s:addr=%d, reg=%d, data_in=0x%04X, data_out=0x%04X", __devname__,
dir == mdi_read ? "READ" : "WRITE",
addr, reg, data, data_out));
E100_DEBUG2(log(LOG_DEBUG, "%s: mdi_ctrl %08X BIT29 : %d", __devname__, ioread32(&e100->csr->mdi_ctrl), (ioread32(&e100->csr->mdi_ctrl) & (1<<29)));)
return (u16)data_out;
}
/* slightly tweaked mdio_ctrl() function for phy_82552_v specifics */
static u16 mdio_ctrl_phy_82552_v(e100_dev_t *e100,
u32 addr,
u32 dir,
u32 reg,
u16 data)
{
if ((reg == MDI_BMCR) && (dir == mdi_write)) {
if (data & (BMCR_RESTART_AN | BMCR_AN_ENABLE)) {
u16 advert = mii_read(e100, e100->cfg.phy_addr,
MDI_ANAR);
/*
* Workaround Si issue where sometimes the part will not
* autoneg to 100Mbps even when advertised.
*/
if (advert & ANAR_100bTFD)
data |= BMCR_SPEED_100 | BMCR_FULL_DUPLEX;
else if (advert & ANAR_100bT)
data |= BMCR_SPEED_100;
}
}
return mdio_ctrl_hw(e100, addr, dir, reg, data);
}
/* Fully software-emulated mdio_ctrl() function for cards without
* MII-compliant PHYs.
* For now, this is mainly geared towards 80c24 support; in case of further
* requirements for other types (i82503, ...?) either extend this mechanism
* or split it, whichever is cleaner.
*/
static u16 mdio_ctrl_phy_mii_emulated(e100_dev_t *e100,
u32 addr,
u32 dir,
u32 reg,
u16 data)
{
/* might need to allocate a e100_priv'ed register array eventually
* to be able to record state changes, but for now
* some fully hardcoded register handling ought to be ok I guess. */
if (dir == mdi_read) {
switch (reg) {
case MDI_BMCR:
/* Auto-negotiation, right? */
return BMCR_AN_ENABLE |
BMCR_FULL_DUPLEX;
case MDI_BMSR:
return BMSR_LINK_STATUS /* for mii_link_ok() */ |
BMSR_AN_ABILITY |
BMSR_10FD;
case MDI_ANAR:
/* 80c24 is a "combo card" PHY, right? */
return ANAR_10bT |
ANAR_10bTFD;
default:
log(LOG_DEBUG, "%s: %s:addr=%d, reg=%d, data=0x%04X: unimplemented emulation!", __devname__,
dir == mdi_read ? "READ" : "WRITE",
addr, reg, data);
return 0xFFFF;
}
} else {
switch (reg) {
default:
log(LOG_DEBUG, "%s: %s:addr=%d, reg=%d, data=0x%04X: unimplemented emulation!", __devname__,
dir == mdi_read ? "READ" : "WRITE",
addr, reg, data);
return 0xFFFF;
}
}
}
int e100_phy_supports_mii(e100_dev_t *e100)
{
/* for now, just check it by comparing whether we
are using MII software emulation.
*/
return (e100->mdio_ctrl != mdio_ctrl_phy_mii_emulated);
}
int e100_phy_check_without_mii(e100_dev_t *e100)
{
u8 phy_type;
int without_mii;
phy_type = (e100->eeprom[eeprom_phy_iface] >> 8) & 0x0f;
switch (phy_type) {
case NoSuchPhy: /* Non-MII PHY; UNTESTED! */
case I82503: /* Non-MII PHY; UNTESTED! */
case S80C24: /* Non-MII PHY; tested and working */
/* paragraph from the FreeBSD driver, "FXP_PHY_80C24":
* The Seeq 80c24 AutoDUPLEX(tm) Ethernet Interface Adapter
* doesn't have a programming interface of any sort. The
* media is sensed automatically based on how the link partner
* is configured. This is, in essence, manual configuration.
*/
log(LOG_DEBUG, "%s: found MII-less i82503 or 80c24 or other PHY", __devname__);
e100->mdio_ctrl = mdio_ctrl_phy_mii_emulated;
e100->cfg.phy_addr = 0; /* is this ok for an MII-less PHY? */
/* these might be needed for certain MII-less cards...
* e100->flags |= ich;
* e100->flags |= ich_10h_workaround; */
without_mii = 1;
break;
default:
without_mii = 0;
break;
}
return without_mii;
}
#define MII_RESV1 0x17 /* Reserved... */
#define MII_TPISTATUS 0x1b /* TPI status for 10mbps */
#define MII_NCONFIG 0x1c /* Network interface config */
#define NCONFIG_AUTO_SWITCH 0x0080
#define MII_NSC_CONG MII_RESV1
#define NSC_CONG_ENABLE 0x0100
#define NSC_CONG_TXREADY 0x0400
#define ADVERTISE_FC_SUPPORTED 0x0400
int e100_findphy (e100_dev_t *e100)
{
int an_capable,status;
int addr;
uint16_t reg;
uint16_t bmcr, stat, cong, id_lo, id_hi;
e100->cfg.phy_addr = -1;
if (e100->mdi) {
MDI_DeRegister ((mdi_t **)&e100->mdi);
}
status = MDI_Register_Extended (e100, mii_write, mii_read,
e100_mdi_callback, (mdi_t **)&e100->mdi, NULL, 0, 0);
if (status != MDI_SUCCESS) {
log(LOG_ERR, "%s: Cannot register the mii routines", __devname__);
e100->mdi = NULL;
return -1;
}
callout_init(&e100->mii_callout);
for (e100->cfg.phy_addr = 0;
e100->cfg.phy_addr < 32; e100->cfg.phy_addr++) {
#if 0
if (MDI_FindPhy(e100->mdi, e100->cfg.phy_addr) == MDI_SUCCESS)
break;
#else
bmcr = mii_read(e100, e100->cfg.phy_addr, MDI_BMCR);
stat = mii_read(e100, e100->cfg.phy_addr, MDI_BMSR);
stat = mii_read(e100, e100->cfg.phy_addr, MDI_BMSR);
if (!((bmcr == 0xFFFF) || ((stat == 0) && (bmcr == 0))))
break;
#endif
}
addr = e100->cfg.phy_addr;
///////////
if (addr == 32) {
/* uhoh, no PHY detected: check whether we seem to be some
* weird, rare variant which is *known* to not have any MII.
* But do this AFTER MII checking only, since this does
* lookup of EEPROM values which may easily be unreliable. */
if (e100_phy_check_without_mii(e100))
return 0; /* simply return and hope for the best */
else {
/* for unknown cases log a fatal error */
log(LOG_ERR, "%s: Cannot find an active PHY", __devname__);
return -1;
}
} else if (e100->cfg.verbose) {
log(LOG_INFO, "%s: MII transceiver found at address %d.", __devname__, e100->cfg.phy_addr);
}
if (MDI_InitPhy(e100->mdi, e100->cfg.phy_addr) != MDI_SUCCESS) {
log(LOG_ERR, "%s: Cannot init the PHY status", __devname__);
return -1;
}
/* Get phy ID */
id_lo = mii_read(e100, e100->cfg.phy_addr, MDI_PHYID_1);
id_hi = mii_read(e100, e100->cfg.phy_addr, MDI_PHYID_2);
e100->phy = (u32)id_hi << 16 | (u32)id_lo;
if (e100->cfg.verbose) {
log(LOG_INFO, "%s: phy ID = 0x%08X", __devname__, e100->phy);
}
/* Select the phy and isolate the rest */
for (addr = 0; addr < 32; addr++) {
if (addr != e100->cfg.phy_addr) {
mii_write(e100, addr, MDI_BMCR, BMCR_ISOLATE);
} else if (e100->phy != phy_82552_v) {
bmcr = mii_read(e100, addr, MDI_BMCR);
mii_write(e100, addr, MDI_BMCR,
bmcr & ~BMCR_ISOLATE);
}
}
/*
* Workaround for 82552:
* Clear the ISOLATE bit on selected phy_id last (mirrored on all
* other phy_id's) using bmcr value from addr discovery loop above.
*/
if (e100->phy == phy_82552_v)
mii_write(e100, e100->cfg.phy_addr, MDI_BMCR,
bmcr & ~BMCR_ISOLATE);
/* Handle National tx phys */
#define NCS_PHY_MODEL_MASK 0xFFF0FFFF
if ((e100->phy & NCS_PHY_MODEL_MASK) == phy_nsc_tx) {
/* Disable congestion control */
cong = mii_read(e100, e100->cfg.phy_addr, MII_NSC_CONG);
cong |= NSC_CONG_TXREADY;
cong &= ~NSC_CONG_ENABLE;
mii_write(e100, e100->cfg.phy_addr, MII_NSC_CONG, cong);
}
if (e100->phy == phy_82552_v) {
u16 advert = mii_read(e100, e100->cfg.phy_addr, MDI_ANAR);
/* assign special tweaked mdio_ctrl() function */
e100->mdio_ctrl = mdio_ctrl_phy_82552_v;
/* Workaround Si not advertising flow-control during autoneg */
advert |= ANAR_FLOW_CONTROL | ANAR_FLOW_ASYMMETRIC;
mii_write(e100, e100->cfg.phy_addr, MDI_ANAR, advert);
/* Reset for the above changes to take effect */
bmcr = mii_read(e100, e100->cfg.phy_addr, MDI_BMCR);
bmcr |= BMCR_RESET;
mii_write(e100, e100->cfg.phy_addr, MDI_BMCR, bmcr);
} else if ((e100->mac >= mac_82550_D102) || ((e100->flags & ich) &&
(mii_read(e100, e100->cfg.phy_addr, MII_TPISTATUS) & 0x8000) &&
(e100->eeprom[eeprom_cnfg_mdix] & eeprom_mdix_enabled))) {
/* enable/disable MDI/MDI-X auto-switching. */
mii_write(e100, e100->cfg.phy_addr, MII_NCONFIG,
e100->force_advertise ? 0 : NCONFIG_AUTO_SWITCH);
}
//////////////////////////////////////////
e100->cfg.connector = NIC_CONNECTOR_MII;
an_capable = mii_read(e100, e100->cfg.phy_addr, MDI_BMSR) & 8;
//
// if the user has specified the speed or duplex
// or if the phy cannot auto-negotiate ...
//
if (e100->force_advertise != -1 || !an_capable) {
reg = mii_read(e100, e100->cfg.phy_addr, MDI_BMCR);
reg &= ~(BMCR_RESTART_AN|BMCR_SPEED_100|BMCR_FULL_DUPLEX);
if (an_capable && e100->force_advertise != 0) {
/*
* If we force the speed, but the link partner
* is autonegotiating, there is a greater chance
* that everything will work if we advertise with
* the speed that we are forcing to.
*/
MDI_SetAdvert(e100->mdi,
e100->cfg.phy_addr, e100->force_advertise);
reg |= BMCR_RESTART_AN | BMCR_AN_ENABLE;
if (e100->cfg.verbose)
log(LOG_INFO, "%s: "
"restricted autonegotiate (%dMbps only)", __devname__,
e100->cfg.media_rate/1000);
} else {
reg &= ~BMCR_AN_ENABLE;
if (e100->cfg.verbose)
log(LOG_INFO, "%s: forcing the link", __devname__);
}
if (e100->cfg.duplex > 0)
reg |= BMCR_FULL_DUPLEX;
if (e100->cfg.media_rate == 100*1000)
reg |= BMCR_SPEED_100;
mii_write(e100, e100->cfg.phy_addr, MDI_BMCR, reg);
if (reg & BMCR_AN_ENABLE)
MDI_EnableMonitor(e100->mdi, 1);
} else { // normal auto-negotiation mode
e100->cfg.flags |= NIC_FLAG_LINK_DOWN;
MDI_AutoNegotiate(e100->mdi, e100->cfg.phy_addr, NoWait);
status = MDI_EnableMonitor(e100->mdi, 1);
if (status != MDI_SUCCESS)
log(LOG_ERR,
"%s: MDI_EnableMonitor returned %x", __devname__, status);
}
return (0);
}
int nic_force_advertise(e100_dev_t *e100)
{
nic_config_t *cfg;
cfg = &e100->cfg;
e100->force_advertise = -1;
// did user specify either of speed or duplex on the cmd line?
if ((cfg->media_rate != -1) || (cfg->duplex != -1)) {
if (cfg->media_rate == -1) {
log(LOG_ERR, "%s(): must also specify speed when duplex is specified", __FUNCTION__);
return EINVAL;
}
if (cfg->duplex == -1) {
log(LOG_ERR, "%s(): must also specify duplex when speed is specified", __FUNCTION__);
return EINVAL;
}
// we get here, we know both media_rate and duplex are set
switch(cfg->media_rate) {
case 0:
e100->force_advertise = 0; // disable link
break;
case 10*1000:
e100->force_advertise = cfg->duplex ? MDI_10bTFD : MDI_10bT;
break;
case 100*1000:
e100->force_advertise = cfg->duplex ? MDI_100bTFD : MDI_100bT;
break;
default:
log(LOG_ERR, "%s(): invalid speed: %d", __FUNCTION__, cfg->media_rate/1000);
return EINVAL;
}
}
return 0;
}

2
hardware/devnp/e100/mips/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1,2 @@
LIST=VARIANT
include recurse.mk

1
hardware/devnp/e100/mips/dll.be/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1 @@
include ../../common.mk

Просмотреть файл

@ -0,0 +1 @@
include ../../common.mk

1
hardware/devnp/e100/mips/dll.le/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1 @@
include ../../common.mk

2
hardware/devnp/e100/ppc/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1,2 @@
LIST=VARIANT
include recurse.mk

Просмотреть файл

@ -0,0 +1 @@
include ../../common.mk

1
hardware/devnp/e100/ppc/dll.be/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1 @@
include ../../common.mk

377
hardware/devnp/e100/receive.c Обычный файл
Просмотреть файл

@ -0,0 +1,377 @@
/*
* (c) 2017-2019, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*****************************************************************************
* *
* Driver for the Intel 8255x 10/100 Mbps Ethernet Controller Family *
* Network controller receive routines *
* *
*****************************************************************************/
#include "e100.h"
void e100_rx_clean_list(e100_dev_t *e100, int full_clean)
{
struct rx *rx;
unsigned int i, count = e100->params.rfds.count;
e100->ru_running = RU_UNINITIALIZED;
if (e100->rxs) {
for (rx = e100->rxs, i = 0; i < count; rx++, i++) {
if (rx->m) {
m_freem(rx->m);
rx->m = NULL;
}
}
if ( full_clean ) {
free(e100->rxs, M_DEVBUF);
e100->rxs = NULL;
} else {
memset(e100->rxs, 0x00, sizeof(struct rx) * count);
}
}
e100->rx_to_use = e100->rx_to_clean = NULL;
}
int e100_rx_alloc_list(e100_dev_t *e100)
{
unsigned int count = e100->params.rfds.count;
e100->rxs = malloc(sizeof(struct rx) * count, M_DEVBUF, M_NOWAIT);
if (e100->rxs == NULL) {
return errno;
}
memset(e100->rxs, 0x00, sizeof(struct rx) * count);
return 0;
}
static int e100_rx_alloc_mbuf(e100_dev_t *e100, struct nw_work_thread *wtp, struct rx *rx)
{
nic_stats_t *gstats = &e100->stats;
if (!rx->m) {
struct mbuf *m;
dma_addr_t dma_addr;
m = m_getcl_wtp(M_DONTWAIT, MT_DATA, M_PKTHDR, wtp);
if (m == NULL) {
gstats->rx_failed_allocs++;
return ENOBUFS;
}
dma_addr = pool_phys(mtod(m, char*), m->m_ext.ext_page);
rx->dma_addr = FROM_POINTER_TO_INT(NET_CACHELINE_ALIGN(dma_addr + RFD_ALIGNMENT_FUDGE), dma_addr_t);
E100_DEBUG(
log(LOG_DEBUG, "%s: %s() [%d] dma_addr 0x%08X (0x%08X) P 0x%llX V %p V0 %p", __devname__, __func__, RXID(rx), dma_addr, rx->dma_addr, m->m_ext.ext_page->pg_phys, mtod(m, char*), m->m_ext.ext_page->pg_page);
)
CACHE_INVAL(&e100->cachectl, mtod(m, char*), dma_addr, m->m_ext.ext_size);
m->m_len = m->m_pkthdr.len = MCLBYTES;
m_adj(m, (uintptr_t)NET_CACHELINE_ALIGN(mtod(m,char *) + RFD_ALIGNMENT_FUDGE) - mtod(m,uintptr_t) + sizeof(struct rfd));
rx->m = m;
} else {
E100_DEBUG(
log(LOG_DEBUG, "%s: %s() [%d] dma_addr 0xXXXXXXXX (0x%08X) P 0x%llX V %p V0 %p", __devname__, __func__, RXID(rx), rx->dma_addr, rx->m->m_ext.ext_page->pg_phys, mtod(rx->m, char*), rx->m->m_ext.ext_page->pg_page);
)
}
memcpy(RFD_FROM_M(rx->m), &e100->blank_rfd, sizeof(struct rfd));
CACHE_FLUSH(&e100->cachectl, RFD_FROM_M(rx->m), rx->dma_addr, sizeof(struct rfd));
/* Link the RFD to end of RFA by linking previous RFD to
* this one. We are safe to touch the previous RFD because
* it is protected by the before last buffer's el bit being set */
if (rx->prev->m) {
struct rfd *prev_rfd = RFD_FROM_M(rx->prev->m);
prev_rfd->link = cpu_to_le32(rx->dma_addr + e100->bmstr);
CACHE_FLUSH(&e100->cachectl, prev_rfd, rx->prev->dma_addr, sizeof(struct rfd));
}
return 0;
}
int e100_rx_init_list(e100_dev_t *e100, struct nw_work_thread *wtp)
{
struct rx *rx;
unsigned int i, count = e100->params.rfds.count;
struct rfd *before_last;
e100->rx_to_use = e100->rx_to_clean = NULL;
e100->ru_running = RU_UNINITIALIZED;
for (rx = e100->rxs, i = 0; i < count; rx++, i++) {
rx->next = (i + 1 < count) ? rx + 1 : e100->rxs;
rx->prev = (i == 0) ? e100->rxs + count - 1 : rx - 1;
if (e100_rx_alloc_mbuf(e100, wtp, rx)) {
e100_rx_clean_list(e100, 0);
return ENOMEM;
}
}
/* Set the el-bit on the buffer that is before the last buffer.
* This lets us update the next pointer on the last buffer without
* worrying about hardware touching it.
* We set the size to 0 to prevent hardware from touching this buffer.
* When the hardware hits the before last buffer with el-bit and size
* of 0, it will RNR interrupt, the RU will go into the No Resources
* state. It will not complete nor write to this buffer. */
rx = e100->rxs->prev->prev;
before_last = RFD_FROM_M(rx->m);
before_last->command |= cpu_to_le16(cb_el);
before_last->size = 0;
CACHE_FLUSH(&e100->cachectl, before_last, rx->dma_addr, sizeof(struct rfd));
e100->rx_to_use = e100->rx_to_clean = e100->rxs;
e100->ru_running = RU_SUSPENDED;
return 0;
}
static int e100_rx_indicate(e100_dev_t *e100, struct rx *rx,
unsigned int *work_done, unsigned int work_to_do)
{
struct ifnet *ifp = &e100->ecom.ec_if;
struct mbuf *m = rx->m;
struct rfd *rfd = RFD_FROM_M(m);
nic_stats_t *gstats = &e100->stats;
u16 rfd_status, actual_size;
u16 fcs_pad = 0;
uint8_t *dptr;
if (unlikely(work_done && *work_done >= work_to_do))
return EAGAIN;
/* Need to sync before taking a peek at cb_complete bit */
CACHE_INVAL(&e100->cachectl, RFD_FROM_M(rx->m), rx->dma_addr, sizeof(struct rfd));
rfd_status = le16_to_cpu(rfd->status);
dma_rmb(); /* read size after status bit */
E100_DEBUG(
log(LOG_DEBUG, "%s: [%d] stat 0x%04X cmd 0x%04X size 0x%02u cur 0x%08X link 0x%08X state %d", __devname__, RXID(rx), rfd_status, le16_to_cpu(rfd->command), le16_to_cpu(rfd->actual_size), rx->dma_addr, le32_to_cpu(rfd->link), e100->ru_running);
dma_rmb(); /* read size after status bit */
)
/* If data isn't ready, nothing to indicate */
if (unlikely(!(rfd_status & cb_complete))) {
/* If the next buffer has the el bit, but we think the receiver
* is still running, check to see if it really stopped while
* we had interrupts off.
* This allows for a fast restart without re-enabling
* interrupts */
if ((le16_to_cpu(rfd->command) & cb_el) &&
(RU_RUNNING == e100->ru_running)) {
if (ioread8(&e100->csr->scb.status) & rus_no_res)
e100->ru_running = RU_SUSPENDED;
E100_DEBUG(
log(LOG_DEBUG, "%s: !cb_complete cb_el state %d", __devname__, e100->ru_running);
)
} else if ( e100->extra_rnr ) {
if (RU_RUNNING == e100->ru_running) {
if (ioread8(&e100->csr->scb.status) & rus_no_res) {
e100->ru_running = RU_SUSPENDED;
E100_DEBUG(
log(LOG_DEBUG, "%s: !cb_complete !cb_el no_res state %d", __devname__, e100->ru_running);
)
}
}
}
CACHE_INVAL(&e100->cachectl, RFD_FROM_M(rx->m), rx->dma_addr, sizeof(struct rfd));
return ENODATA;
}
/* Get actual data size */
// if (unlikely(dev->features & NETIF_F_RXFCS))
// fcs_pad = 4;
actual_size = le16_to_cpu(rfd->actual_size) & 0x3FFF;
if (unlikely(actual_size > RFD_BUF_LEN - sizeof(struct rfd)))
actual_size = RFD_BUF_LEN - sizeof(struct rfd);
/* If this buffer has the el bit, but we think the receiver
* is still running, check to see if it really stopped while
* we had interrupts off.
* This allows for a fast restart without re-enabling interrupts.
* This can happen when the RU sees the size change but also sees
* the el bit set. */
if ((le16_to_cpu(rfd->command) & cb_el) &&
(RU_RUNNING == e100->ru_running)) {
if (ioread8(&e100->csr->scb.status) & rus_no_res)
e100->ru_running = RU_SUSPENDED;
E100_DEBUG(
log(LOG_DEBUG, "%s: cb_complete cb_el state %d", __devname__, e100->ru_running);
)
}
if (unlikely(!(rfd_status & cb_ok))) {
/* Don't indicate if hardware indicates errors */
m_freem(m);
} else if (actual_size > MAX_ETHER_LEN + fcs_pad /*|| !actual_size *//*malformed packet*/) {
/* Don't indicate oversized frames */
e100->rx_over_length_errors++;
m_freem(m);
} else {
// process_mbuf:
/* Put the actual info */
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = actual_size;
m->m_len = m->m_pkthdr.len;
/* stats */
gstats->rxed_ok++;
gstats->octets_rxed_ok += actual_size - fcs_pad;
dptr = mtod(m, uint8_t *);
if (dptr[0] & 1) {
if (is_broadcast_mac(dptr))
gstats->rxed_broadcast++;
else
gstats->rxed_multicast++;
}
#if NBPFILTER > 0
/* Pass this up to any BPF listeners. */
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m);
#endif
/* Send it up */
ifp->if_ipackets++;
(*ifp->if_input)(ifp, m);
if (work_done)
(*work_done)++;
}
rx->m = NULL;
return 0;
}
void e100_rx_clean(e100_dev_t *e100, struct nw_work_thread *wtp, unsigned int *work_done, unsigned int work_to_do)
{
struct rx *rx;
int restart_required = 0, err = 0;
struct rx *old_before_last_rx, *new_before_last_rx;
struct rfd *old_before_last_rfd, *new_before_last_rfd;
/* Indicate newly arrived packets */
for (rx = e100->rx_to_clean; rx->m; rx = e100->rx_to_clean = rx->next) {
err = e100_rx_indicate(e100, rx, work_done, work_to_do);
/* Hit quota or no more to clean */
if (EAGAIN == err || ENODATA == err)
break;
}
E100_DEBUG2(
if ( err == ENODATA ) {
for (rx = e100->rx_to_clean; rx->m; rx = rx->next) {
struct rfd *rfd = RFD_FROM_M(rx->m);
u16 rfd_status;
u16 rfd_command;
/* Need to sync before taking a peek at cb_complete bit */
CACHE_INVAL(&e100->cachectl, RFD_FROM_M(rx->m), rx->dma_addr, sizeof(struct rfd));
rfd_status = le16_to_cpu(rfd->status);
rfd_command = le16_to_cpu(rfd->command);
if ( rfd_status || rfd_command ) {
log(LOG_DEBUG, "%s: DBG: [%d] stat 0x%04X cmd 0x%04X state %d", __devname__, RXID(rx), rfd_status, rfd_command, e100->ru_running);
}
if ( rfd_command ) {
break;
}
}
}
)
/* On EAGAIN, hit quota so have more work to do, restart once
* cleanup is complete.
* Else, are we already rnr? then pay attention!!! this ensures that
* the state machine progression never allows a start with a
* partially cleaned list, avoiding a race between hardware
* and rx_to_clean when in NAPI mode */
if (EAGAIN != err && RU_SUSPENDED == e100->ru_running)
restart_required = 1;
old_before_last_rx = e100->rx_to_use->prev->prev;
if (old_before_last_rx->m) {
old_before_last_rfd = RFD_FROM_M(old_before_last_rx->m);
} else {
old_before_last_rfd = NULL;
}
/* Alloc new mbufs to refill list */
for (rx = e100->rx_to_use; !rx->m; rx = e100->rx_to_use = rx->next) {
if (unlikely(e100_rx_alloc_mbuf(e100, wtp, rx)))
break; /* Better luck next time (see watchdog) */
}
new_before_last_rx = e100->rx_to_use->prev->prev;
if (new_before_last_rx != old_before_last_rx) {
/* Set the el-bit on the buffer that is before the last buffer.
* This lets us update the next pointer on the last buffer
* without worrying about hardware touching it.
* We set the size to 0 to prevent hardware from touching this
* buffer.
* When the hardware hits the before last buffer with el-bit
* and size of 0, it will RNR interrupt, the RUS will go into
* the No Resources state. It will not complete nor write to
* this buffer. */
new_before_last_rfd = RFD_FROM_M(new_before_last_rx->m);
E100_DEBUG(
log(LOG_DEBUG, "%s: DBG: before_last_rx %d (0x%08X) => %d (0x%08X)", __devname__,
RXID(old_before_last_rx), old_before_last_rx->dma_addr,
RXID(new_before_last_rx), new_before_last_rx->dma_addr
);
)
new_before_last_rfd->size = 0;
new_before_last_rfd->command |= cpu_to_le16(cb_el);
CACHE_FLUSH(&e100->cachectl, new_before_last_rfd, new_before_last_rx->dma_addr, sizeof(struct rfd));
if ( old_before_last_rfd != NULL ) {
/* Now that we have a new stopping point, we can clear the old
* stopping point. We must sync twice to get the proper
* ordering on the hardware side of things. */
old_before_last_rfd->command &= ~cpu_to_le16(cb_el);
CACHE_FLUSH(&e100->cachectl, old_before_last_rfd, old_before_last_rx->dma_addr, sizeof(struct rfd));
old_before_last_rfd->size = cpu_to_le16(MAX_ETHER_LEN);
CACHE_FLUSH(&e100->cachectl, old_before_last_rfd, old_before_last_rx->dma_addr, sizeof(struct rfd));
}
}
E100_DEBUG(
log(LOG_DEBUG, "%s: %s() restart %d", __devname__, __func__, restart_required);
)
if (restart_required) {
// ack the rnr?
iowrite8(stat_ack_rnr, &e100->csr->scb.stat_ack);
e100_start_receiver(e100, e100->rx_to_clean);
if (work_done)
(*work_done)++;
if ( e100->cfg.verbose > 1 ) {
log(LOG_DEBUG, "%s: receiver restart %d", __devname__, *work_done);
}
}
}
void e100_start_receiver(e100_dev_t *e100, struct rx *rx)
{
E100_DEBUG(
log(LOG_DEBUG, "%s: %s() start", __devname__, __func__);
)
if (!e100->rxs) return;
if (RU_SUSPENDED != e100->ru_running) return;
/* handle init time starts */
if (!rx) rx = e100->rxs;
/* (Re)start RU if suspended or idle and RFA is non-NULL */
if (rx->m) {
E100_DEBUG(
log(LOG_DEBUG, "%s: %s() start with dma_addr P 0x%08X", __devname__, __func__, rx->dma_addr);
while ( e100_exec_cmd(e100, ruc_abort, 0) ) {
log(LOG_DEBUG, "%s: %s() ruc_abort!", __devname__, __func__);
}
)
e100_exec_cmd(e100, ruc_start, rx->dma_addr);
e100->ru_running = RU_RUNNING;
}
E100_DEBUG(
log(LOG_DEBUG, "%s: %s() ru_running %d done", __devname__, __func__, e100->ru_running);
)
}

376
hardware/devnp/e100/transmit.c Обычный файл
Просмотреть файл

@ -0,0 +1,376 @@
/*
* (c) 2017-2019, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*****************************************************************************
* *
* Driver for the Intel 8255x 10/100 Mbps Ethernet Controller Family *
* Network controller transmit routines *
* *
*****************************************************************************/
#include "e100.h"
//
// this function is called only if the packet is ridiculously
// fragmented, and will fit into one cluster
//
static struct mbuf *e100_defrag(struct mbuf *m)
{
struct mbuf *m2;
if (m->m_pkthdr.len > MCLBYTES) {
m_freem(m);
return NULL;
}
MGET(m2, M_DONTWAIT, MT_DATA);
if (m2 == NULL) {
m_freem(m);
return NULL;
}
M_COPY_PKTHDR(m2, m);
MCLGET(m2, M_DONTWAIT);
if ((m2->m_flags & M_EXT) == 0) {
m_freem(m);
m_freem(m2);
return NULL;
}
m_copydata(m, 0, m->m_pkthdr.len, mtod(m2, caddr_t));
m2->m_pkthdr.len = m2->m_len = m->m_pkthdr.len;
m_freem(m);
return m2;
}
static inline int mbuf_nfragments(struct mbuf *m0)
{
int num_frag;
struct mbuf *m = m0;
for ( num_frag = 0; m != NULL; num_frag++ ) {
m = m->m_next;
}
return num_frag;
}
static inline struct mbuf *ridiculously_fragmented_handling(e100_dev_t *e100, struct mbuf *m0)
{
struct mbuf *m;
int num_frag;
num_frag = mbuf_nfragments(m0);
if ( num_frag > E100_TX_FRAGMENTS ) {
if ( e100->cfg.verbose > 1 )
log(LOG_DEBUG, "%s: num_frag %d - ridiculously fragmented", __devname__, num_frag);
if ( (m = e100_defrag(m0)) == NULL) {
if (e100->cfg.verbose > 1)
log(LOG_ERR, "%s: e100_defrag() failed", __devname__);
goto done;
} else if ( e100->cfg.verbose > 1 ) {
log(LOG_ERR, "%s: warning: heavily fragmented normal packet transmitted: "
"defrag worked: size %d, orignal num_frag %d",
__devname__, m->m_pkthdr.len, num_frag);
}
m0 = m;
// must re-count mbuf fragments again
num_frag = mbuf_nfragments(m0);
if ( e100->cfg.verbose > 1 ) {
log(LOG_ERR, "%s: warning: heavily fragmented normal packet transmitted: "
"new num_frag %d",
__devname__, num_frag);
}
if ( num_frag > E100_TX_FRAGMENTS ) {
if ( e100->cfg.verbose > 1 ) {
log(LOG_ERR, "%s: dropped heavily fragmented huge packet: "
"size %d, num_frag %d, free tx descr %d",
__devname__, m0->m_pkthdr.len, num_frag, e100->cbs_avail);
}
m_freem(m0);
m0 = NULL;
goto done;
}
}
done:
return m0;
}
#if E100_DEBUG_LEVEL > 0
void dump_packet(e100_dev_t *e100, uint8_t *buf, size_t len, off64_t phys)
{
int i = 0;
size_t tail = len;
log(LOG_ERR, "%s: buf %p len %zu phys 0x%"PRIX64, __devname__, buf, len, phys);
for ( i = 0; i < len; ) {
if ( tail >= 8 ) {
log(LOG_ERR, "%s: %02X %02X %02X %02X %02X %02X %02X %02X", __devname__,
buf[i + 0],
buf[i + 1],
buf[i + 2],
buf[i + 3],
buf[i + 4],
buf[i + 5],
buf[i + 6],
buf[i + 7]
);
i += 8;
tail -= 8;
} else if ( tail >= 4 ) {
log(LOG_ERR, "%s: %02X %02X %02X %02X", __devname__,
buf[i + 0],
buf[i + 1],
buf[i + 2],
buf[i + 3]
);
i += 4;
tail -= 4;
} else if ( tail >= 2 ) {
log(LOG_ERR, "%s: %02X %02X", __devname__,
buf[i + 0],
buf[i + 1]
);
i += 2;
tail -= 2;
} else {
log(LOG_ERR, "%s: %02X", __devname__,
buf[i + 0]
);
i += 1;
tail -= 1;
}
}
}
#endif
int e100_tx_clean(e100_dev_t *e100)
{
struct cb *cb;
struct ifnet *ifp = &e100->ecom.ec_if;
struct mbuf *m;
nic_stats_t *gstats = &e100->stats;
int tx_cleaned = 0;
int reaped = 0;
uint8_t *dptr;
// InterruptLock(&e100->cb_lock);
/* Clean CBs marked complete */
E100_DEBUG(
log(LOG_DEBUG, "%s: %s() cbs_avail %d", __devname__, __func__, e100->cbs_avail);
// for (cb = e100->cb_to_clean;
// tx_cleaned < 16/*e100->params.cbs.count*/;
// cb = cb->next) {
// log(LOG_DEBUG, "%s: CB[%d]->status = 0x%04X cmd 0x%04X", __devname__,
// (int)(((void*)cb - (void*)e100->cbs)/sizeof(struct cb)),
// cb->status, cb->command);
// tx_cleaned++;
// }
// tx_cleaned = 0;
)
for (cb = e100->cb_to_clean;
e100->params.cbs.reap >= reaped && (cb->status & cpu_to_le16(cb_complete));
cb = e100->cb_to_clean = cb->next) {
dma_rmb(); /* read skb after status */
if ( e100->cfg.verbose > 3 ) {
log(LOG_DEBUG, "%s: cb[%d]->status = 0x%04X", __devname__,
(int)(((void*)cb - (void*)e100->cbs)/sizeof(struct cb)),
cb->status);
}
if (likely(cb->data != NULL)) {
m = (struct mbuf *)cb->data;
// stat begin
ifp->if_opackets++;
gstats->txed_ok++;
gstats->octets_txed_ok += m->m_pkthdr.len;
dptr = mtod(m, uint8_t *);
if (dptr[0] & 1) {
if (is_broadcast_mac(dptr))
gstats->txed_broadcast++;
else
gstats->txed_multicast++;
}
// stat end
m_freem(m);
cb->data = NULL;
tx_cleaned = 1;
}
cb->status = 0;
e100->cbs_avail++;
reaped++;
}
// InterruptUnlock(&e100->cb_lock);
return tx_cleaned;
}
E100_DEBUG2
(
static void log_dma_addr(e100_dev_t *e100, uint64_t paddr)
{
log(LOG_DEBUG, "%s: %s() 0x%llX", __devname__, __func__, paddr);
}
)
static int e100_xmit_prepare(e100_dev_t *e100, struct cb *cb)
{
int i;
dma_addr_t dma_addr;
struct mbuf *m0 = (struct mbuf *)cb->data;
struct mbuf *m;
cb->command = e100->tx_command;
cb->command &= ~cpu_to_le16(cb_tx_nc);
/* interrupt every 16 packets regardless of delay */
if ((e100->cbs_avail & ~15) == e100->cbs_avail)
cb->command |= cpu_to_le16(cb_i);
i = 0;
m = m0;
while ( m != NULL ) {
if ( !m->m_len ) {
goto next_chunk;
}
dma_addr = (dma_addr_t)mbuf_phys(m);
cb->u.tcb.tbd[i].buf_addr = cpu_to_le32(dma_addr + e100->bmstr);
cb->u.tcb.tbd[i].size = cpu_to_le16(m->m_len);
cb->u.tcb.tbd[i].eol = 0;
E100_DEBUG2(
log(LOG_DEBUG, "%s: %s() cb[%d]..tbd[%d].size %d(%d)", __devname__, __func__, (int)(((void*)cb - (void*)e100->cbs)/sizeof(struct cb)), i, cb->u.tcb.tbd[i].size, (int)((uintptr_t)&cb->u.tcb.tbd[i].size - (uintptr_t)cb));
log_dma_addr(e100, cb->dma_addr);
)
CACHE_FLUSH(&e100->cachectl, m->m_data, dma_addr, m->m_len);
i++;
next_chunk:
m = m->m_next;
}
cb->u.tcb.tbd_array = cb->dma_addr + offsetof(struct cb, u.tcb.tbd) + e100->bmstr;
cb->u.tcb.tcb_byte_count = 0;
cb->u.tcb.threshold = e100->tx_threshold;
cb->u.tcb.tbd_count = i;
E100_DEBUG(
log(LOG_DEBUG, "%s: %s() tbd_array = 0x%08X i=%d", __devname__, __func__, cb->u.tcb.tbd_array, i);
)
return 0;
}
void e100_start(struct ifnet *ifp)
{
e100_dev_t *e100;
struct mbuf *m0;
struct nw_work_thread *wtp;
int ret;
e100 = ifp->if_softc;
wtp = WTP;
if ( (ret = early_transmit(e100, wtp)) ) {
if ( ret > 1 ) {
IFQ_PURGE(&ifp->if_snd);
}
NW_SIGUNLOCK_P(&ifp->if_snd_ex, e100->iopkt, wtp);
if ( e100->cfg.verbose > 1 )
log(LOG_ERR, "%s: early transmit", __devname__);
return;
}
e100->start_running = 1;
E100_DEBUG(
log(LOG_ERR, "%s: start", __devname__);
)
if (unlikely(!e100->cbs_avail)) {
if ( e100->cfg.verbose > 8 )
log(LOG_ERR, "%s: CB cleanup", __devname__);
ifp->if_flags_tx |= IFF_OACTIVE;
goto done; // not enough tx descriptors, try later
}
ifp->if_flags_tx |= IFF_OACTIVE;
for (;;) {
IFQ_POLL(&ifp->if_snd, m0);
if (m0 == NULL) {
if ( e100->cfg.verbose > 12 )
log(LOG_ERR, "%s: IFQ_POLL 0", __devname__);
break;
}
if (unlikely(!e100->cbs_avail)) {
if ( e100->cfg.verbose > 1 )
log(LOG_ERR, "%s: No space for CB", __devname__);
ifp->if_flags_tx |= IFF_OACTIVE;
e100->stats.tx_failed_allocs++;
// e100->tx_queue++;
break;
}
/*
* Can look at m to see if you have the resources
* to transmit it.
*/
IFQ_DEQUEUE(&ifp->if_snd, m0);
#ifdef VARIANT_notx
{
m_freem(m0);
e100->stats.tx_failed_allocs++;
ifp->if_oerrors++;
goto done;
}
#endif
if ( (m0 = ridiculously_fragmented_handling(e100, m0)) == NULL ) {
e100->stats.tx_failed_allocs++;
ifp->if_oerrors++;
goto done;
}
if (e100->flags & ich_10h_workaround) {
/* SW workaround for ICH[x] 10Mbps/half duplex Tx hang.
Issue a NOP command followed by a 1us delay before
issuing the Tx command. */
if (e100_exec_cmd(e100, cuc_nop, 0))
log(LOG_ERR, "%s: exec cuc_nop failed", __devname__);
udelay(1);
}
ret = e100_exec_cb(e100, (void *)m0, e100_xmit_prepare);
switch (ret) {
case ENOSPC:
if ( e100->cfg.verbose > 1 )
log(LOG_DEBUG, "%s: No space for next CB", __devname__);
break;
case ENOMEM:
e100->stats.tx_failed_allocs++;
ifp->if_oerrors++;
if ( e100->cfg.verbose > 1 )
log(LOG_DEBUG, "%s: Out of Tx resources, returning mbuf", __devname__);
goto done;
}
#if NBPFILTER > 0
/* Pass the packet to any BPF listeners. */
if (ifp->if_bpf) {
bpf_mtap(ifp->if_bpf, m0);
}
#endif
} // for
done:
e100->start_running = 0;
ifp->if_flags_tx &= ~IFF_OACTIVE;
E100_DEBUG (
log(LOG_DEBUG, "%s: done", __devname__);
)
NW_SIGUNLOCK_P(&ifp->if_snd_ex, e100->iopkt, wtp);
}

2
hardware/devnp/e100/x86/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1,2 @@
LIST=VARIANT
include recurse.mk

1
hardware/devnp/e100/x86/dll/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1 @@
include ../../common.mk

2
hardware/devnp/sample/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1,2 @@
LIST=CPU
include recurse.mk

8
hardware/devnp/sample/arm/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1,8 @@
LIST=VARIANT
ifndef QRECURSE
QRECURSE=recurse.mk
ifdef QCONFIG
QRDIR=$(dir $(QCONFIG))
endif
endif
include $(QRDIR)$(QRECURSE)

Просмотреть файл

@ -0,0 +1 @@
include ../../common.mk

1
hardware/devnp/sample/arm/dll.le/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1 @@
include ../../common.mk

22
hardware/devnp/sample/common.mk Обычный файл
Просмотреть файл

@ -0,0 +1,22 @@
#
# (c) 2014-2019, SWD Embedded Systems Limited, http://www.kpda.ru
#
ifndef QCONFIG
QCONFIG=qconfig.mk
endif
include $(QCONFIG)
ISKERNEL := 1
include ../../../../../netdrivers.mk
LIBS = drvrS cacheS
LIBS += $(foreach libpath,$(LIBVPATH), $(if $(wildcard $(libpath)/libnetdrvrS.a), netdrvrS) )
NAME = devnp-$(PROJECT)
define PINFO
PINFO DESCRIPTION=sample io-pkt ethernet driver.
endef
include $(MKFILES_ROOT)/qtargets.mk

8
hardware/devnp/sample/e2k/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1,8 @@
LIST=VARIANT
ifndef QRECURSE
QRECURSE=recurse.mk
ifdef QCONFIG
QRDIR=$(dir $(QCONFIG))
endif
endif
include $(QRDIR)$(QRECURSE)

1
hardware/devnp/sample/e2k/dll.le/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1 @@
include ../../common.mk

8
hardware/devnp/sample/mips/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1,8 @@
LIST=VARIANT
ifndef QRECURSE
QRECURSE=recurse.mk
ifdef QCONFIG
QRDIR=$(dir $(QCONFIG))
endif
endif
include $(QRDIR)$(QRECURSE)

1
hardware/devnp/sample/mips/dll.be/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1 @@
include ../../common.mk

Просмотреть файл

@ -0,0 +1 @@
include ../../common.mk

1
hardware/devnp/sample/mips/dll.le/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1 @@
include ../../common.mk

8
hardware/devnp/sample/ppc/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1,8 @@
LIST=VARIANT
ifndef QRECURSE
QRECURSE=recurse.mk
ifdef QCONFIG
QRDIR=$(dir $(QCONFIG))
endif
endif
include $(QRDIR)$(QRECURSE)

Просмотреть файл

@ -0,0 +1 @@
include ../../common.mk

1
hardware/devnp/sample/ppc/dll.be/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1 @@
include ../../common.mk

491
hardware/devnp/sample/sam.c Обычный файл
Просмотреть файл

@ -0,0 +1,491 @@
/*
* (c) 2014, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include <io-pkt/iopkt_driver.h>
#include <sys/io-pkt.h>
#include <sys/syspage.h>
#include <sys/device.h>
#include <device_qnx.h>
#include <net/if_ether.h>
#include <net/if_media.h>
#include <net/netbyte.h>
#include <net80211/ieee80211_var.h>
int sam_entry( void *dll_hdl, struct _iopkt_self *iopkt, char *options );
int sam_init( struct ifnet * );
void sam_stop( struct ifnet *, int );
void sam_start( struct ifnet * );
int sam_ioctl( struct ifnet *, unsigned long, caddr_t );
const struct sigevent * sam_isr( void *, int );
int sam_process_interrupt( void *, struct nw_work_thread * );
int sam_enable_interrupt( void * );
void sam_shutdown( void * );
struct _iopkt_drvr_entry IOPKT_DRVR_ENTRY_SYM( sam ) = IOPKT_DRVR_ENTRY_SYM_INIT( sam_entry );
#ifdef VARIANT_a
#include <nw_dl.h>
/* This is what gets specified in the stack's dl.c */
struct nw_dll_syms sam_syms[] = {
{ "iopkt_drvr_entry", &IOPKT_DRVR_ENTRY_SYM( sam ) },
{ NULL, NULL }
};
#endif
const uint8_t etherbroadcastaddr[ETHER_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
struct sam_dev {
struct device sc_dev; /* common device */
struct ethercom sc_ec; /* common ethernet */
struct ieee80211com sc_ic; /* common 80211 */
/* whatever else you need follows */
struct _iopkt_self *sc_iopkt;
int sc_iid;
int sc_irq;
int sc_intr_cnt;
int sc_intr_spurious;
struct _iopkt_inter sc_inter;
void *sc_sdhook;
};
int sam_attach( struct device *, struct device *, void * );
int sam_detach( struct device *, int );
CFATTACH_DECL( sam,
sizeof( struct sam_dev ),
NULL,
sam_attach,
sam_detach,
NULL );
/*
* Initial driver entry point.
*/
int sam_entry( void *dll_hdl, struct _iopkt_self *iopkt, char *options )
{
int instance,
single;
struct device *dev;
void *attach_args;
/* parse options */
/* do options imply single? */
single = 1;
/* initialize to whatever you want to pass to sam_attach() */
attach_args = NULL;
for ( instance = 0;; )
{
/* Apply detection criteria */
/* Found one */
dev = NULL; /* No Parent */
if ( dev_attach( "sam", options, &sam_ca, attach_args, &single, &dev, NULL ) != EOK )
break;
dev->dv_dll_hdl = dll_hdl;
instance++;
if ( /* done_detection || */ single )
break;
}
if ( instance > 0 )
return (EOK);
return (ENODEV);
}
int sam_attach( struct device *parent, struct device *self, void *aux )
{
int err;
struct sam_dev *sam;
struct ifnet *ifp;
uint8_t enaddr[ETHER_ADDR_LEN];
struct qtime_entry *qtp;
/* initialization and attach */
sam = (struct sam_dev *)self;
ifp = &sam->sc_ec.ec_if;
sam->sc_iopkt = iopkt_selfp;
qtp = SYSPAGE_ENTRY( qtime );
sam->sc_irq = qtp->intr;
if ( (err = interrupt_entry_init( &sam->sc_inter, 0, NULL, IRUPT_PRIO_DEFAULT )) != EOK )
return err;
sam->sc_inter.func = sam_process_interrupt;
sam->sc_inter.enable = sam_enable_interrupt;
sam->sc_inter.arg = sam;
sam->sc_iid = -1; /* not attached yet */
/* set capabilities */
#if 0
ifp->if_capabilities_rx = IFCAP_CSUM_IPv4 | IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4;
ifp->if_capabilities_tx = IFCAP_CSUM_IPv4 | IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4;
sam->sc_ec.ec_capabilities |= ETHERCAP_JUMBO_MTU;
#endif
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
/* Set callouts */
ifp->if_ioctl = sam_ioctl;
ifp->if_start = sam_start;
ifp->if_init = sam_init;
ifp->if_stop = sam_stop;
IFQ_SET_READY( &ifp->if_snd );
ifp->if_softc = sam;
/* More callouts for 80211... */
strcpy( ifp->if_xname, sam->sc_dev.dv_xname );
if_attach( ifp );
{
int i;
for ( i = 0; i < ETHER_ADDR_LEN; i++ )
enaddr[i] = i;
}
#if 1
/* Normal ethernet */
ether_ifattach( ifp, enaddr );
#else
/* 80211 */
memcpy( sam->sc_ic.ic_myaddr, enaddr, ETHER_ADDR_LEN );
ieee80211_ifattach( &sam->sc_ic );
#endif
sam->sc_sdhook = shutdownhook_establish( sam_shutdown, sam );
return (EOK);
}
int sam_init( struct ifnet *ifp )
{
int ret;
struct sam_dev *sam;
/*
* - enable hardware.
* - look at ifp->if_capenable_[rx/tx]
* - enable promiscuous / multicast filter.
* - attach to interrupt.
*/
sam_stop( ifp, 0 );
sam = ifp->if_softc;
if ( sam->sc_iid == -1 )
{
if ( (ret = InterruptAttach_r( sam->sc_irq, sam_isr, sam, sizeof( *sam ), _NTO_INTR_FLAGS_TRK_MSK )) < 0 )
return -ret;
sam->sc_iid = ret;
}
ifp->if_flags |= IFF_RUNNING;
return (EOK);
}
void sam_stop( struct ifnet *ifp, int disable )
{
struct sam_dev *sam;
/*
* - Cancel any pending io
* - Clear any interrupt source registers
* - Clear any interrupt pending registers
* - Release any queued transmit buffers.
*/
sam = ifp->if_softc;
if ( disable )
{
if ( sam->sc_iid != -1 )
{
InterruptDetach( sam->sc_iid );
sam->sc_iid = -1;
}
/* rxdrain */
}
ifp->if_flags &= ~IFF_RUNNING;
}
void sam_start( struct ifnet *ifp )
{
struct sam_dev *sam;
struct mbuf *m;
struct nw_work_thread *wtp;
sam = ifp->if_softc;
wtp = WTP;
for ( ;; )
{
IFQ_POLL( &ifp->if_snd, m );
if ( m == NULL )
break;
/*
* Can look at m to see if you have the resources
* to transmit it.
*/
IFQ_DEQUEUE( &ifp->if_snd, m );
/* You're now committed to transmitting it */
m_freem( m );
ifp->if_opackets++; // for ifconfig -v
// or if error: ifp->if_oerrors++;
}
NW_SIGUNLOCK_P( &ifp->if_snd_ex, iopkt_selfp, wtp );
}
int sam_ioctl( struct ifnet *ifp, unsigned long cmd, caddr_t data )
{
int error = 0;
switch ( cmd )
{
default:
error = ether_ioctl( ifp, cmd, data );
if ( error == ENETRESET )
{
/*
* Multicast list has changed; set the
* hardware filter accordingly.
*/
error = 0;
}
break;
}
return error;
}
int sam_detach( struct device *dev, int flags )
{
struct sam_dev *sam;
struct ifnet *ifp;
/*
* Clean up everything.
*
* The interface is going away but io-pkt is staying up.
*/
sam = (struct sam_dev *)dev;
ifp = &sam->sc_ec.ec_if;
sam_stop( ifp, 1 );
#if 1
ether_ifdetach( ifp );
#else
ieee80211_ifdetach( &sam->sc_ic );
#endif
if_detach( ifp );
shutdownhook_disestablish( sam->sc_sdhook );
return (EOK);
}
void sam_shutdown( void *arg )
{
struct sam_dev *sam;
/* All of io-pkt is going away. Just quiet hardware. */
sam = arg;
sam_stop( &sam->sc_ec.ec_if, 1 );
}
#ifndef HW_MASK
const struct sigevent * sam_isr( void *arg, int iid )
{
struct sam_dev *sam;
struct _iopkt_inter *ient;
sam = arg;
ient = &sam->sc_inter;
/*
* Close window where this is referenced in sam_enable_interrupt().
* We may get an interrupt, return a sigevent and have another
* thread start processing on SMP before the InterruptAttach()
* has returned.
*/
sam->sc_iid = iid;
InterruptMask( sam->sc_irq, iid );
return interrupt_queue( sam->sc_iopkt, ient );
}
#else
const struct sigevent * sam_isr( void *arg, int iid )
{
struct sam_dev *sam;
struct _iopkt_self *iopkt;
const struct sigevent *evp;
struct inter_thread *itp;
sam = arg;
iopkt = sam->sc_iopkt;
evp = NULL;
#ifdef READ_CAUSE_IN_ISR
/*
* Trade offs.
* - Doing this here means another register read across the bus.
* - If not sharing interrupts, this boils down to exactly the
* same amount of work but doing more of it in the isr.
* - If sharing interupts, can short circuit some work in the
* stack here.
* - Maybe trade off is to only do it if we're detecting
* spurious interrupts which should happen under heavy
* shared interrupt load?
*/
#ifdef READ_CAUSE_ONLY_ON_SPURIOUS
if ( ient->spurrious )
{
#endif
if ( ient->on_list == 0 && (sam->sc_intr_cause = i82544->reg[I82544_ICR]) == 0 )
return NULL; /* Not ours */
sam->sc_flag |= CAUSE_VALID;
#ifdef READ_CAUSE_ONLY_ON_SPURIOUS
}
#endif
#endif
/*
* We have to make sure the interrupt is masked regardless
* of our on_list status. This is because of a window where
* a shared (spurious) interrupt comes after on_list
* is knocked down but before the enable() callout is made.
* If enable() then happened to run after we masked, we
* could end up on the list without the interrupt masked
* which would cause the kernel more than a little grief
* if one of our real interrupts then came in.
*
* This window doesn't exist when using kermask since the
* interrupt isn't unmasked until all the enable()s run
* (mask count is tracked by kernel).
*/
/*
* If this was controling real hardware, mask of
* interrupts here. eg from i82544 driver:
*/
i82544->reg[I82544_IMC] = 0xffffffff;
return interrupt_queue( sam->sc_iopkt, ient );
}
#endif
int sam_process_interrupt( void *arg, struct nw_work_thread *wtp )
{
struct sam_dev *sam;
struct mbuf *m;
struct ifnet *ifp;
struct ether_header *eh;
sam = arg;
ifp = &sam->sc_ec.ec_if;
if ( (sam->sc_intr_cnt++ % 1000) == 0 )
{
/* Send a packet up */
m = m_getcl_wtp( M_DONTWAIT, MT_DATA, M_PKTHDR, wtp );
if ( !m )
{
ifp->if_ierrors++; // for ifconfig -v
return (1);
}
m->m_pkthdr.len = m->m_len = sizeof( *eh );
// ip_input() needs this
m->m_pkthdr.rcvif = ifp;
// dummy up a broadcasted IP packet for testing
eh = mtod( m, struct ether_header * );
eh->ether_type = ntohs( ETHERTYPE_IP );
memcpy( eh->ether_dhost, etherbroadcastaddr, ETHER_ADDR_LEN );
ifp->if_ipackets++; // for ifconfig -v
(*ifp->if_input)( ifp, m );
printf( "sam_process_interrupt %d\n", sam->sc_intr_cnt );
}
/*
* return of 1 means were done.
*
* If we notice we're taking a long time (eg. processed
* half our rx descriptors) we could early out with a
* return of 0 which lets other interrupts be processed
* without calling our interrupt_enable func. This
* func will be called again later.
*/
return (1);
}
#ifndef HW_MASK
int sam_enable_interrupt( void *arg )
{
struct sam_dev *sam;
sam = arg;
InterruptUnmask( sam->sc_irq, sam->sc_iid );
return (1);
}
#else
int sam_enable_interrupt( void *arg )
{
struct sam_dev *sam;
sam = arg;
/* eg from i82544 driver */
i82544->reg[I82544_IMS] = i82544->intrmask;
return (1);
}
#endif

8
hardware/devnp/sample/x86/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1,8 @@
LIST=VARIANT
ifndef QRECURSE
QRECURSE=recurse.mk
ifdef QCONFIG
QRDIR=$(dir $(QCONFIG))
endif
endif
include $(QRDIR)$(QRECURSE)

1
hardware/devnp/sample/x86/dll/Makefile Обычный файл
Просмотреть файл

@ -0,0 +1 @@
include ../../common.mk

36
netdrivers.mk Обычный файл
Просмотреть файл

@ -0,0 +1,36 @@
# We link the stack with -E so a lot of the undefined
# references get resolved from the stack itself. If
# you want them listed at link time, turn off
# --allow-shlib-undefined and replace with --warn-once
# if desired.
#LDFLAGS+=-Wl,--warn-once
LDFLAGS+=-Wl,--allow-shlib-undefined
NETDRVR_ROOT:=$(patsubst %/,%, $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
DEVNP_ROOT:=$(NETDRVR_ROOT)/hardware/devnp
BSDDRVR_ROOT:=$(DEVNP_ROOT)/bsd
LIBNBDRVR_ROOT:=$(NETDRVR_ROOT)/lib/libnbdrvr
HDR_PATH=$(INSTALL_ROOT_HDR)/io-pkt
PUBLIC_HDR_PATH=$(KPDA_TARGET)/usr/include/io-pkt
# Check for staging area first
EXTRA_INCVPATH+= $(HDR_PATH) $(HDR_PATH)/sys-nto
# Use headers installed in system if staging area not available
EXTRA_INCVPATH+= $(PRODUCT_ROOT) $(PUBLIC_HDR_PATH) $(PUBLIC_HDR_PATH)/sys-nto
ifneq ($(NEED_QNXH),)
CCFLAGS += -Wp,-include -Wp,$(if $(wildcard $(HDR_PATH)),$(HDR_PATH)/qnx.h,$(PUBLIC_HDR_PATH)/qnx.h)
endif
ifneq ($(NEED_LIBNBDRVR),)
EXTRA_INCVPATH+=$(LIBNBDRVR_ROOT)
endif
ifneq ($(ISKERNEL),)
CCFLAGS += -D_KERNEL
endif
CCFLAGS_e2k += -fkernel
CCFLAGS += $(CCFLAGS_$(CPU))
# gcc sometime after 2.95.3 added a builtin log()
CCFLAGS += -fno-builtin-log