Fazemos uma placa de rede USB externa ENC28J60

O ENC28J60 é um controlador Ethernet simples que pode atuar como uma placa de rede externa para computadores de placa única com GPIO (há até um driver pronto para o raspberry) e outros arduins. GPIOs não são exibidos no meu laptop, vamos tentar consertar essa falha e aparafusar o ENC28J60 usando STM32F103 e um cabo USB.





Vamos ver como você pode fazer isso.





Nós precisamos:





  • ENC28J60





  • Placa de depuração com STM32 com suporte a dispositivo USB (por exemplo, este):





  • 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 /).





Você não precisa se preocupar com a framboesa, mas conecte diretamente o cabo de rede do roteador ao ENC28J60 (você precisa desligar o wi-fi em seu computador e definir o endereço tap0 correto). Mas testar é mais fácil com o framboesa, você pode ver tudo o que acontece no tcpdump.





Por que tudo isso

Usar tal pacote na vida provavelmente não é muito conveniente (especialmente se houver adaptadores ethernet usb baratos à venda), mas foi muito interessante fazê-lo. Obrigado pela atenção. Link de código (projeto em Atollic TrueStudio).








All Articles