[Настройка Linux, Сетевые технологии, Программирование микроконтроллеров] Делаем из ENC28J60 внешнюю USB сетевую карту
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
ENC28J60 - простой Ethernet контроллер, который может выступать в роли внешней сетевой карты у одноплатных компьютеров с GPIO (для raspberry есть даже готовый драйвер) и прочих ардуин. У моего лэптопа GPIO не выведены, попробуем исправить этот недостаток и прикрутить к нему ENC28J60 посредством STM32F103 и шнурка USB.Давайте посмотрим, как это можно сделать. Нам понадобится:
- ENC28J60
- Отладочная плата с STM32 с поддержкой USB device (например, вот такая):
- Компьютер с Linux (я использую Ubuntu 16)
- Второй компьютер с Ethernet для тестирования соединения (у меня raspberry pi), подключенный по wi-fi (и в одной локальной сети с первым)
Настройку STM32F103 описывать не буду, мануалов в сети хватает. Как и программирование самого ENC28J60 (в своем примере я использовал вот этот код c минимальными модификациями). Подключение ENC28J60 к STM32F103 по SPI1 описано там же.СоединениеКомпьютер (usb) -> stm32(SPI) -> ENC28J60(Ethernet кабель) -> raspberry
Как все работаетОбойдемся без написания драйверов ядра, будем работать в user space. На компьютере создадим виртуальный сетевой tap интерфейс (второго уровня, с заголовками Ethernet фреймов, есть еще tun интерфейсы, которые работают только с ip пакетами), к которому подсоединим нашу программу (назовем ее tap_handler.c). Чтобы фрейм попал в сетевой стек Linuxа, tap_handler'у достаточно записать его в tap интерфейс. Ну и наоборот, пакеты, адресованные tap интерфейсу, будут читаться tap_handler'ом, который может с ними что-то дальше сделать. В итоге, tap_handler бегает в бесконечном цикле и ждет появления данных либо со стороны tap интерфейса, либо со стороны /dev/ttyACM0 (это представление нашего USB девайса в Linuxе). В первом случае полученные данные пишем в /dev/ttyACM0, во втором в tap интерфейс.Более подробно про работу с виртуальными интерфейсами написано здесь (для демонстрации работы vpn). Отсюда же я взял код, отвечающий за работу с виртуальными интерфейсами.На STM32 при помощи CubeMX подключаем библиотеки для работы с USB CDC (virtual com port). После подключения SMT32 к компу Linux создаст файл /dev/ttyACM0 (ну или другой номер). Из этого файла можно прочитать данные, которые нам отправил STM32, а если записать туда данные, их сможет прочитать STM32.Прошивка STM32 работает аналогично. В бесконечном цикле читаем данные с компа (функция CDC_Receive_FS в файле usbd_cdc_if.c) и записываем их в ENC28J60, который уже передает их дальше в сеть, а также считываем фреймы из ENC28J60 и направляем их в комп функцией CDC_Transmit_FS.Насколько я понимаю, CDC должен передавать данные без ошибок. Однако я столкнулся с тем, что ошибки все же есть. Более того, первый пакет обычно всегда дублируется (причину я не нашел, дубляж виден в том числе в wireshark при прослушивании шины usb). Гуглеж показал, что у кого-то это происходило из-за использования неподабающего генератора частоты на STM32, что вряд ли является причиной проблемы в моем случае, т.к. я просто использовал внешний кварц. Поэтому и пришлось городить огород с метками.Работа со стороны компаСетевой интерфейс создаем командой:
sudo openvpn --mktun --dev tap0
Присваиваем ip адрес:
sudo ifconfig tap0 10.0.0.1/24 up
tap_handler.сДля правильной работы с /dev/ttyACM0 нужно передавать данные в raw виде и не давать драйверу терминала вносить в них изменения (например, считать нетекстовые данные управляющими символами и пр.). Настройка терминала:
char cdc_name[20]="/dev/ttyACM0";
int tty_fd = open(cdc_name, O_RDWR | O_NOCTTY);
struct termios portSettings;
tcgetattr(tty_fd, &portSettings);
cfmakeraw(&portSettings);
tcsetattr(tty_fd, TCSANOW, &portSettings);
tcflush(tty_fd, TCOFLUSH);
Подключаем tap_handler к tap0:
/* в dev передаем имя уже созданного интерфейса tap0*/
int tun_alloc(char *dev, int flags) {
struct ifreq ifr;
int fd, err;
char *clonedev = "/dev/net/tun";
/* отталкиваемся от устройства /dev/net/tun */
if( (fd = open(clonedev , O_RDWR)) < 0 ) {
perror("Opening /dev/net/tun");
return fd;
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = flags;
/* используем уже созданный tap0 */
if (*dev) {
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
}
/* подсоединяемся к интерфейсу */
if( (err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0 ) {
perror("ioctl(TUNSETIFF)");
close(fd);
return err;
}
strcpy(dev, ifr.ifr_name);
return fd;
}
Вызываем эту функцию в main.c следующим образом:
strcpy(tun_name, "tap0");
int tap_fd = tun_alloc(tun_name, IFF_TAP | IFF_NO_PI);
Флаг IFF_TAP указывает на тип интерфейса (tap). Флаг IFF_NO_PI нужен, чтобы ядро не добавляло префиксные байты перед началом пакета.Проверяем наличие данных в tap0 и в /dev/ttyACM0. Пока данных нет, tap_handler находится в блокирующем select:
while(1) {
int ret;
fd_set rd_set;
FD_ZERO(&rd_set);
/* tap_fd - tap inteface descriptor */
FD_SET(tap_fd, &rd_set);
/* tty_fd - /dev/ttyACM0 descriptor */
FD_SET(tty_fd, &rd_set);
ret = select(maxfd + 1, &rd_set, NULL, NULL, NULL);
После получения данных проверяем источник. При получении фрейма от tap0 tap_handler формирует пакет для STM32 (структура пакета: пакет начинается с метки - определенных 4 байт, чтобы идентифицировать начало фрейма, следующие 2 байта - длина фрейма, затем уже сам фрейм) и записывает его в /dev/ttyACM0. Затем небольшая задержка, чтобы данные прошли успешно:
if(FD_ISSET(tap_fd, &rd_set)) {
uint16_t nread = cread(tap_fd, buffer, BUFSIZE);
uint8_t buf[6];
*(uint32_t *)buf = PACKET_START_SIGN;
*(uint16_t *)(buf + 4) = nread;
cwrite(tty_fd,(char *)buf,6);
cwrite(tty_fd, buffer, nread);
delay_micro(delay_m);
}
Если есть данные в /dev/ttyACM0, убеждаемся что они начинаются с правильной метки (те же 4 байта), затем считываем длину фрейма, и потом сам фрейм. Полученный фрейм записываем в tap интерфейс:
if(FD_ISSET(tty_fd, &rd_set)) {
uint32_t sign;
/* считываем метку */
int nread = read_n(tty_fd, (char *)&sign, sizeof(sign));
/* дескриптор закрыт, выходим из программы */
if(nread == 0) {
break;
}
/* если не совпадает, пытаемся найти подпись в следующих 4 байтах */
if(sign != PACKET_START_SIGN){
continue;
}
/* читаем длину фрейма */
nread = read_n(tty_fd, (char *)&plength, 2);
if(nread == 0) {
break;
}
if (nread != 2){
continue;
}
/* здесь обрабатываем ситуацию, когда после запуска программы первый пакет дублируется */
if(flag){
flag = 0;
nread = cread(tty_fd, buffer, sizeof(buffer));
if(nread != 6){
continue;
}
}
/* слишком большая длина пакета, заканчиваем программу */
if(plength > BUFSIZE){
break;
}
/* читаем фрейм (plength байт) и пишем его в tap interface*/
nread = read_n(tty_fd, buffer, plength);
if (nread != 0){
cwrite(tap_fd, buffer, nread);
delay_micro(delay_m);
}
}
Со стороны STM32Кроме USB CDC в CubeMX подключим также HAL драйверы для работы с SPI1 и светодиодом.Прием данных выполняется в callback'е CDC_Receive_FS (файл usbd_cdc_if.c), запуск которого инициируется прерываниями в библиотеке USB. В этом месте нельзя ставить долгоиграющие операции, поэтому пришедшие данные копируем в кольцевой буфер, из которого забираем их в основном цикле и отправляем наружу через ENC28J60. При риске переполнения буфера данные отбрасываются:
/* USB_POINTERS_ARRAY_SIZE - размер array_pos */
/* MAX_FRAMELEN - максимальная длина фрейма */
/* USB_BUFSIZE - размер кольцевого буфера */
extern uint8_t usb_buf[]; /* кольцевой буфер для полученных от компа данных */
extern uint32_t pos_int; /* индекс для размещения следующего пакета в кольцевом буфере */
extern uint32_t array_pos[]; /* кольцевой массив из индексов, которые указывают на полученные пакеты в кольцевом буфере */
extern uint32_t p_a; /* индекс следующей записи в array_pos для CDC_Receive_FS*/
extern uint32_t pl_a;/* индекс следующей записи в array_pos у основного потока */
/* USB_POINTERS_ARRAY_SIZE - размер array_pos */
/* MAX_FRAMELEN - максимальная длина фрейма */
/* USB_BUFSIZE - размер кольцевого буфера */
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
int8_t memok = 1;
/* отбрасываем пришедшие данные, если не хватает места в кольцевом буфере */
if( pl_a !=0 && p_a !=0){
int32_t mem_lag = array_pos[(p_a - 1) % USB_POINTERS_ARRAY_SIZE] - array_pos[(pl_a - 1) % USB_POINTERS_ARRAY_SIZE];
if(mem_lag > USB_BUFSIZE - MAX_FRAMELEN)
memok = 0;
}
/* Копируем поступившие данные в кольцевой буфер,
обновляем массив индексов пришедших пакетов (array_pos) */
if(*Len < USB_BUFSIZE && *Len != 0 && memok){
uint16_t offset = pos_int % USB_BUFSIZE;
uint16_t new_pos = offset + *Len;
uint8_t split = 0;
if (new_pos > USB_BUFSIZE){
split = 1;
}
if(split){
int len1 = USB_BUFSIZE - offset;
int len2 = *Len - len1;
memcpy(usb_buf + offset, Buf, len1);
memcpy(usb_buf, Buf + len1, len2);
}
else
memcpy(usb_buf + offset, Buf, *Len);
pos_int += *Len;
array_pos[p_a % USB_POINTERS_ARRAY_SIZE] = pos_int;
p_a++;
}
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
return (USBD_OK);
}
В основном потоке (main.c) смотрим на наличие данных в буфере и отправляем их через ENC28J60:
if(pl_a < p_a){
uint32_t prev = 0;
if(pl_a > 0)
prev = array_pos[(pl_a - 1) % USB_POINTERS_ARRAY_SIZE];
/* размер пакета (кусочек фрейма), принятого в CDC_Receive_FS */
int32_t n = array_pos[pl_a % USB_POINTERS_ARRAY_SIZE] - prev;//usb frame size
/* указатель на пакет в буфере */
uint8_t *from = usb_buf + prev % USB_BUFSIZE;
/* признак корректности пакета */
uint8_t right_n = 1;
if (n < 0 || n > MAX_FRAMELEN){
right_n = 0;
}
/* проверка на новый фрейм. В пакете должно быть минимум 6 байтов (подпись 4 байта и 2 байта длина) */
if((packet_len == 0) && packet_start && (n > 5) && right_n){
/* спец. функция для чтения из кольцевого буфера */
uint32_t sign = read32(from,usb_buf);
/* получаем указатель на данные через 4 байта */
uint8_t *next = next_usb_ptr(from,usb_buf,4);
/* читаем размер фрейма */
packet_size = read16(next,usb_buf);// 2 bytes after sign is packet length
/* отбрасываем неправильный пакет */
if (packet_size > MAX_FRAMELEN || sign != PACKET_START_SIGN){
packet_size = 0;
}
else{
/* копируем принятый пакет данных в буфер фрейма */
next = next_usb_ptr(from,usb_buf,6);
copy_buf(packet_buf, next, usb_buf, n - 6);
packet_len = n - 6;
packet_next_ptr = packet_buf + packet_len;
packet_start = 0;
}
}
/* обрабатываем последующие пакеты в фрейме */
else if(packet_len < packet_size && right_n){
/* копируем принятый пакет данных в буфер фрейма */
copy_buf(packet_next_ptr, from, usb_buf, n);
packet_len += n;
packet_next_ptr = packet_buf + packet_len;
}
/* отбрасываем ошибочный фрейм */
else if (packet_len > packet_size){
packet_len = 0;
packet_start = 1;
}
/* отправляем фрейм через enc28j60 */
if(packet_len == packet_size && packet_size > 0){
enc28j60_packetSend(packet_buf, packet_size);
packet_len = 0;
packet_start = 1;
}
pl_a++;
}
а также проверяем наличие данных в ENC28J60 и при наличии отправляем их в USB и мигаем светодиодом:
len = enc28j60_packetReceive(net_buf,sizeof(net_buf));
if (len > 0) {
*((uint16_t*)(sign_buf + 4)) = len;
while(CDC_Transmit_FS(sign_buf, sizeof(sign_buf)) == USBD_BUSY_CDC_TRANSMIT);
while(CDC_Transmit_FS(net_buf, len) == USBD_BUSY_CDC_TRANSMIT);
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
}
В CDC_Transmit_FS я немного изменил код, чтобы функцию можно было поместить в while. В этом месте CDC_Transmit_FS возвращаем новый статус USBD_BUSY_CDC_TRANSMIT вместо просто USBD_BUSY. Без изменений узнать приняты ли данные к отправке мне не удалось:
if (hcdc->TxState != 0){
return USBD_BUSY_CDC_TRANSMIT;
}
В функции инициализации ENC28J60 enc28j60_ini нужно разрешить принимать и передавать любые фреймы, не только адресованные ему (promiscuous mode):
enc28j60_writeRegByte(ERXFCON,0);
Настройка и проверкаRaspberryПоднимаем eth0 и устанавливаем ip адрес. Запускаем ping
sudo ifconfig eth0 up 10.0.0.2/24
ping 10.0.0.2
В другом окне можно и tcpdump:
sudo tcpdump -i eth0
КомпПодсоединяем прошитый STM32 к компу, соединяем ENC28J60 сетевым кабелем с raspberry. На STM32 начинает мигать светодиод, показывая приход arp / icmp пакетов (от ping). Убеждаемся, что появился /dev/ttyACM0:
ls /dev/ttyACM*
Компилируем и запускаем tap_handler:
gcc tap_handler.c -o tap_handler
./tap_handler
tap_handler запускает всю цепочку - подхватывает приходящие пакеты от raspberry, адресует их tap0, получает от него фрейм в ответ, шлет его STM32, когда фрейм доходит до raspberry мы видим, что пинги начинают проходить.Делаем интернет на компеЕсли пинги проходят проверяем работу нашей сетевой карты.КомпЯ подсоединился к raspberry по ssh через wi-fi и управляю им с компа, соединение нам терять не стоит, поэтому просто меняем default gateway. Браузер перестает работать, но соединение с raspberry не теряется:
sudo route del default gateway 192.168.1.1
sudo route add default gateway 10.0.0.2
Может потребоваться корректировка DNS сервера (в /etc/resolv.conf нужно прописать в качестве nameserver, например, 8.8.8.8).RaspberryРазрешаем переход пакетов из eth0 в wlan0 и включаем NAT:
echo 1 | sudo tee -a /proc/sys/net/ipv4/ip_forward
sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
Проверяем комп. Интернет должен появиться, правда небыстрый (у меня 0.5 Мбит/с).Можно не заморачиваться с raspberry, а напрямую подсоединить сетевой провод от рутера в ENC28J60 (нужно отключить wi-fi на компе и задать правильный адрес tap0). Но тестировать проще с raspberry, в tcpdump видно все что происходит.Зачем все этоИспользовать такую связку в жизни наверно не очень удобно (особенно при наличии недорогих usb ethernet адаптеров в продаже), но сделать ее было очень интересно. Спасибо за внимание. Ссылка на код (проект в Atollic TrueStudio).
===========
Источник:
habr.com
===========
Похожие новости:
- [C++, Разработка робототехники, Программирование микроконтроллеров, DIY или Сделай сам] ESP32 в окружении VSCode
- [Настройка Linux, Разработка под Linux] Многозадачность в shell скриптах
- [Настройка Linux, Системное администрирование] Файловый сервер на Samba, видимый отовсюду
- [Сетевые технологии, Сетевое оборудование, Транспорт] В НАСА разрабатывают аналог кабеля Ethernet для сверхзвуковых самолетов
- [Сетевые технологии, Беспроводные технологии, Разработка систем связи, Научно-популярное, Космонавтика] Всё о проекте «Спутниковый интернет Starlink». Часть 20. Внутреннее устройство терминала SL
- [Сетевые технологии] Как находить проблемы с интернетом и кто виноват ч.1
- [Промышленное программирование, Разработка робототехники, Программирование микроконтроллеров, Разработка под Arduino, Производство и разработка электроники] Портирование ModBus Slave RTU/ASCII на IAR AVR v3
- [Анализ и проектирование систем, Программирование микроконтроллеров, Управление проектами, Производство и разработка электроники, Транспорт] Опыт разработки системы управления для железнодорожной техники на отечественных микроконтроллерах
- [Сетевые технологии, IT-стандарты, Стандарты связи, Разработка для интернета вещей] Эффективное применение DLMS/COSEM в больших системах с ограниченными ресурсами (перевод)
- [Программирование микроконтроллеров] Полноценный трехпортовый USB-Serial адаптер на STM32 Blue Pill (STM32F103C8T6)
Теги для поиска: #_nastrojka_linux (Настройка Linux), #_setevye_tehnologii (Сетевые технологии), #_programmirovanie_mikrokontrollerov (Программирование микроконтроллеров), #_stm32, #_tap, #_usb, #_nastrojka_linux (
Настройка Linux
), #_setevye_tehnologii (
Сетевые технологии
), #_programmirovanie_mikrokontrollerov (
Программирование микроконтроллеров
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 07:24
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
ENC28J60 - простой Ethernet контроллер, который может выступать в роли внешней сетевой карты у одноплатных компьютеров с GPIO (для raspberry есть даже готовый драйвер) и прочих ардуин. У моего лэптопа GPIO не выведены, попробуем исправить этот недостаток и прикрутить к нему ENC28J60 посредством STM32F103 и шнурка USB.Давайте посмотрим, как это можно сделать. Нам понадобится:
Как все работаетОбойдемся без написания драйверов ядра, будем работать в user space. На компьютере создадим виртуальный сетевой tap интерфейс (второго уровня, с заголовками Ethernet фреймов, есть еще tun интерфейсы, которые работают только с ip пакетами), к которому подсоединим нашу программу (назовем ее tap_handler.c). Чтобы фрейм попал в сетевой стек Linuxа, tap_handler'у достаточно записать его в tap интерфейс. Ну и наоборот, пакеты, адресованные tap интерфейсу, будут читаться tap_handler'ом, который может с ними что-то дальше сделать. В итоге, tap_handler бегает в бесконечном цикле и ждет появления данных либо со стороны tap интерфейса, либо со стороны /dev/ttyACM0 (это представление нашего USB девайса в Linuxе). В первом случае полученные данные пишем в /dev/ttyACM0, во втором в tap интерфейс.Более подробно про работу с виртуальными интерфейсами написано здесь (для демонстрации работы vpn). Отсюда же я взял код, отвечающий за работу с виртуальными интерфейсами.На STM32 при помощи CubeMX подключаем библиотеки для работы с USB CDC (virtual com port). После подключения SMT32 к компу Linux создаст файл /dev/ttyACM0 (ну или другой номер). Из этого файла можно прочитать данные, которые нам отправил STM32, а если записать туда данные, их сможет прочитать STM32.Прошивка STM32 работает аналогично. В бесконечном цикле читаем данные с компа (функция CDC_Receive_FS в файле usbd_cdc_if.c) и записываем их в ENC28J60, который уже передает их дальше в сеть, а также считываем фреймы из ENC28J60 и направляем их в комп функцией CDC_Transmit_FS.Насколько я понимаю, CDC должен передавать данные без ошибок. Однако я столкнулся с тем, что ошибки все же есть. Более того, первый пакет обычно всегда дублируется (причину я не нашел, дубляж виден в том числе в wireshark при прослушивании шины usb). Гуглеж показал, что у кого-то это происходило из-за использования неподабающего генератора частоты на STM32, что вряд ли является причиной проблемы в моем случае, т.к. я просто использовал внешний кварц. Поэтому и пришлось городить огород с метками.Работа со стороны компаСетевой интерфейс создаем командой: sudo openvpn --mktun --dev tap0
sudo ifconfig tap0 10.0.0.1/24 up
char cdc_name[20]="/dev/ttyACM0";
int tty_fd = open(cdc_name, O_RDWR | O_NOCTTY); struct termios portSettings; tcgetattr(tty_fd, &portSettings); cfmakeraw(&portSettings); tcsetattr(tty_fd, TCSANOW, &portSettings); tcflush(tty_fd, TCOFLUSH); /* в dev передаем имя уже созданного интерфейса tap0*/
int tun_alloc(char *dev, int flags) { struct ifreq ifr; int fd, err; char *clonedev = "/dev/net/tun"; /* отталкиваемся от устройства /dev/net/tun */ if( (fd = open(clonedev , O_RDWR)) < 0 ) { perror("Opening /dev/net/tun"); return fd; } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = flags; /* используем уже созданный tap0 */ if (*dev) { strncpy(ifr.ifr_name, dev, IFNAMSIZ); } /* подсоединяемся к интерфейсу */ if( (err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0 ) { perror("ioctl(TUNSETIFF)"); close(fd); return err; } strcpy(dev, ifr.ifr_name); return fd; } strcpy(tun_name, "tap0");
int tap_fd = tun_alloc(tun_name, IFF_TAP | IFF_NO_PI); while(1) {
int ret; fd_set rd_set; FD_ZERO(&rd_set); /* tap_fd - tap inteface descriptor */ FD_SET(tap_fd, &rd_set); /* tty_fd - /dev/ttyACM0 descriptor */ FD_SET(tty_fd, &rd_set); ret = select(maxfd + 1, &rd_set, NULL, NULL, NULL); if(FD_ISSET(tap_fd, &rd_set)) {
uint16_t nread = cread(tap_fd, buffer, BUFSIZE); uint8_t buf[6]; *(uint32_t *)buf = PACKET_START_SIGN; *(uint16_t *)(buf + 4) = nread; cwrite(tty_fd,(char *)buf,6); cwrite(tty_fd, buffer, nread); delay_micro(delay_m); } if(FD_ISSET(tty_fd, &rd_set)) {
uint32_t sign; /* считываем метку */ int nread = read_n(tty_fd, (char *)&sign, sizeof(sign)); /* дескриптор закрыт, выходим из программы */ if(nread == 0) { break; } /* если не совпадает, пытаемся найти подпись в следующих 4 байтах */ if(sign != PACKET_START_SIGN){ continue; } /* читаем длину фрейма */ nread = read_n(tty_fd, (char *)&plength, 2); if(nread == 0) { break; } if (nread != 2){ continue; } /* здесь обрабатываем ситуацию, когда после запуска программы первый пакет дублируется */ if(flag){ flag = 0; nread = cread(tty_fd, buffer, sizeof(buffer)); if(nread != 6){ continue; } } /* слишком большая длина пакета, заканчиваем программу */ if(plength > BUFSIZE){ break; } /* читаем фрейм (plength байт) и пишем его в tap interface*/ nread = read_n(tty_fd, buffer, plength); if (nread != 0){ cwrite(tap_fd, buffer, nread); delay_micro(delay_m); } } /* USB_POINTERS_ARRAY_SIZE - размер array_pos */
/* MAX_FRAMELEN - максимальная длина фрейма */ /* USB_BUFSIZE - размер кольцевого буфера */ extern uint8_t usb_buf[]; /* кольцевой буфер для полученных от компа данных */ extern uint32_t pos_int; /* индекс для размещения следующего пакета в кольцевом буфере */ extern uint32_t array_pos[]; /* кольцевой массив из индексов, которые указывают на полученные пакеты в кольцевом буфере */ extern uint32_t p_a; /* индекс следующей записи в array_pos для CDC_Receive_FS*/ extern uint32_t pl_a;/* индекс следующей записи в array_pos у основного потока */ /* USB_POINTERS_ARRAY_SIZE - размер array_pos */ /* MAX_FRAMELEN - максимальная длина фрейма */ /* USB_BUFSIZE - размер кольцевого буфера */ static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len) { int8_t memok = 1; /* отбрасываем пришедшие данные, если не хватает места в кольцевом буфере */ if( pl_a !=0 && p_a !=0){ int32_t mem_lag = array_pos[(p_a - 1) % USB_POINTERS_ARRAY_SIZE] - array_pos[(pl_a - 1) % USB_POINTERS_ARRAY_SIZE]; if(mem_lag > USB_BUFSIZE - MAX_FRAMELEN) memok = 0; } /* Копируем поступившие данные в кольцевой буфер, обновляем массив индексов пришедших пакетов (array_pos) */ if(*Len < USB_BUFSIZE && *Len != 0 && memok){ uint16_t offset = pos_int % USB_BUFSIZE; uint16_t new_pos = offset + *Len; uint8_t split = 0; if (new_pos > USB_BUFSIZE){ split = 1; } if(split){ int len1 = USB_BUFSIZE - offset; int len2 = *Len - len1; memcpy(usb_buf + offset, Buf, len1); memcpy(usb_buf, Buf + len1, len2); } else memcpy(usb_buf + offset, Buf, *Len); pos_int += *Len; array_pos[p_a % USB_POINTERS_ARRAY_SIZE] = pos_int; p_a++; } USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]); USBD_CDC_ReceivePacket(&hUsbDeviceFS); return (USBD_OK); } if(pl_a < p_a){
uint32_t prev = 0; if(pl_a > 0) prev = array_pos[(pl_a - 1) % USB_POINTERS_ARRAY_SIZE]; /* размер пакета (кусочек фрейма), принятого в CDC_Receive_FS */ int32_t n = array_pos[pl_a % USB_POINTERS_ARRAY_SIZE] - prev;//usb frame size /* указатель на пакет в буфере */ uint8_t *from = usb_buf + prev % USB_BUFSIZE; /* признак корректности пакета */ uint8_t right_n = 1; if (n < 0 || n > MAX_FRAMELEN){ right_n = 0; } /* проверка на новый фрейм. В пакете должно быть минимум 6 байтов (подпись 4 байта и 2 байта длина) */ if((packet_len == 0) && packet_start && (n > 5) && right_n){ /* спец. функция для чтения из кольцевого буфера */ uint32_t sign = read32(from,usb_buf); /* получаем указатель на данные через 4 байта */ uint8_t *next = next_usb_ptr(from,usb_buf,4); /* читаем размер фрейма */ packet_size = read16(next,usb_buf);// 2 bytes after sign is packet length /* отбрасываем неправильный пакет */ if (packet_size > MAX_FRAMELEN || sign != PACKET_START_SIGN){ packet_size = 0; } else{ /* копируем принятый пакет данных в буфер фрейма */ next = next_usb_ptr(from,usb_buf,6); copy_buf(packet_buf, next, usb_buf, n - 6); packet_len = n - 6; packet_next_ptr = packet_buf + packet_len; packet_start = 0; } } /* обрабатываем последующие пакеты в фрейме */ else if(packet_len < packet_size && right_n){ /* копируем принятый пакет данных в буфер фрейма */ copy_buf(packet_next_ptr, from, usb_buf, n); packet_len += n; packet_next_ptr = packet_buf + packet_len; } /* отбрасываем ошибочный фрейм */ else if (packet_len > packet_size){ packet_len = 0; packet_start = 1; } /* отправляем фрейм через enc28j60 */ if(packet_len == packet_size && packet_size > 0){ enc28j60_packetSend(packet_buf, packet_size); packet_len = 0; packet_start = 1; } pl_a++; } len = enc28j60_packetReceive(net_buf,sizeof(net_buf));
if (len > 0) { *((uint16_t*)(sign_buf + 4)) = len; while(CDC_Transmit_FS(sign_buf, sizeof(sign_buf)) == USBD_BUSY_CDC_TRANSMIT); while(CDC_Transmit_FS(net_buf, len) == USBD_BUSY_CDC_TRANSMIT); HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); } if (hcdc->TxState != 0){
return USBD_BUSY_CDC_TRANSMIT; } enc28j60_writeRegByte(ERXFCON,0);
sudo ifconfig eth0 up 10.0.0.2/24
ping 10.0.0.2 sudo tcpdump -i eth0
ls /dev/ttyACM*
gcc tap_handler.c -o tap_handler
./tap_handler sudo route del default gateway 192.168.1.1
sudo route add default gateway 10.0.0.2 echo 1 | sudo tee -a /proc/sys/net/ipv4/ip_forward
sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE =========== Источник: habr.com =========== Похожие новости:
Настройка Linux ), #_setevye_tehnologii ( Сетевые технологии ), #_programmirovanie_mikrokontrollerov ( Программирование микроконтроллеров ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 07:24
Часовой пояс: UTC + 5