Correzione di alcuni errori nello stack TCP-IP per micro NXP LPC1768

Inquadriamo l’argomento: con il mio amico Gianni sto integrando lo stack Open-Source TCP-IP chiamato uIP scritto da Adam Dunkels qualche annetto fa per i microcontrollori di fascia bassa, del quale è stato fatto il porting per i micro CortexM3 di NXP, che uso abitualmente nei miei progetti. Ovviamente si tratta di un argomento estremamente specifico in una applicazione altrettanto specifica.

Nel porting per LPCXpresso (o CodeRed), probabilmente fatto alla veloce, abbiamo trovato degli errori, alcuni dei quali sono veramente bestiali, che hanno fatto penare non poco.

Il primo, per il quale ringrazio Gianni per la segnalazione e la correzione, riguarda l’allocazione dei buffer relativi alla ethernet stessa.

In pratica i buffer di interscambio con il PHY e l’applicazione, vengono allocati (vedere file EthDev_LPC17xx.h, riga Circa 60) in una posizione fissa.

/* EMAC variables located in AHB SRAM bank 1*/
#define AHB_SRAM_BANK1_BASE  0x2007c000UL
#define RX_DESC_BASE        (AHB_SRAM_BANK1_BASE         )
#define RX_STAT_BASE        (RX_DESC_BASE + NUM_RX_FRAG*(2*4))     /* 2 * uint32_t, see RX_DESC_TypeDef */
#define TX_DESC_BASE        (RX_STAT_BASE + NUM_RX_FRAG*(2*4))     /* 2 * uint32_t, see RX_STAT_TypeDef */
#define TX_STAT_BASE        (TX_DESC_BASE + NUM_TX_FRAG*(2*4))     /* 2 * uint32_t, see TX_DESC_TypeDef */
#define ETH_BUF_BASE        (TX_STAT_BASE + NUM_TX_FRAG*(1*4))     /* 1 * uint32_t, see TX_STAT_TypeDef */

/* RX and TX descriptor and status definitions. */
#define RX_DESC_PACKET(i)   (*(unsigned int *)(RX_DESC_BASE   + 8*i))
#define RX_DESC_CTRL(i)     (*(unsigned int *)(RX_DESC_BASE+4 + 8*i))
#define RX_STAT_INFO(i)     (*(unsigned int *)(RX_STAT_BASE   + 8*i))
#define RX_STAT_HASHCRC(i)  (*(unsigned int *)(RX_STAT_BASE+4 + 8*i))
#define TX_DESC_PACKET(i)   (*(unsigned int *)(TX_DESC_BASE   + 8*i))
#define TX_DESC_CTRL(i)     (*(unsigned int *)(TX_DESC_BASE+4 + 8*i))
#define TX_STAT_INFO(i)     (*(unsigned int *)(TX_STAT_BASE   + 4*i))
#define ETH_BUF(i)          ( ETH_BUF_BASE + ETH_FRAG_SIZE*i )

Il problema nasce ovviamente quando, con le normali e canoniche tecniche vi vanno ad allocare delle variabili nello stesso banco di memoria. Il linker alloca le variabili come deve ma non è istruito per conoscere le variabili di cui sopra, per cui la stessa locazione di memoria viene usata contemporaneamente per due scopi diversi, con ovvi risultati distruttivi.

Il problema può essere brillantemente risolto allocando i buffer un po’ meno alla cavolo, istruendo il linker in maniera opportuna, ad esempio così:

#include <cr_section_macros.h>

__BSS(RAM2)    RX_DESC_TypeDef  RxDesc[NUM_RX_FRAG];
__BSS(RAM2)    RX_STAT_TypeDef  RxStat[NUM_RX_FRAG];
__BSS(RAM2)    TX_DESC_TypeDef  TxDesc[NUM_TX_FRAG];
__BSS(RAM2)    TX_STAT_TypeDef  TxStat[NUM_TX_FRAG];
__BSS(RAM2)    unsigned char     EthBuffers[ETH_NUM_BUFFERS*ETH_FRAG_SIZE];

#define ETH_BUF_BASE            ((unsigned int)(&EthBuffers[0]))
#define RX_DESC_BASE            ((unsigned int)(&RxDesc[0]))
#define RX_STAT_BASE            ((unsigned int)(&RxStat[0]))
#define TX_DESC_BASE            ((unsigned int)(&TxDesc[0]))
#define TX_STAT_BASE            ((unsigned int)(&TxStat[0]))

#define ETH_BUF(i)            (ETH_BUF_BASE + ETH_FRAG_SIZE*i)
#define TX_STAT_INFO(i)       (TxStat[i].Info)
#define TX_DESC_CTRL(i)       (TxDesc[i].Ctrl)
#define TX_DESC_PACKET(i)     (TxDesc[i].Packet)
#define RX_STAT_HASHCRC(i)    (RxStat[i].HashCRC)
#define RX_STAT_INFO(i)       (RxStat[i].Info)
#define RX_DESC_CTRL(i)       (RxDesc[i].Ctrl)
#define RX_DESC_PACKET(i)     (RxDesc[i].Packet)

Il secondo problema è un po’ più concettuale. Quello che mi aspetto da uno stack TCPIP è di essere il più modulare e scalare possibile.

Nel caso del porting uIP che ho io è possibile gestire più connessioni “in listening”, ovvero più server su diverse porte, ma bisogna modificare il codice di un paio di funzioni ad ogni uso.

Non solo, bisogna gestire il numero di porta in due punti diversi del programma, ovvero alla creazione del server e nel dispatch dei vari pacchetti.

La cosa è, come si può ben intuire, ben lontana dall’essere fruibile rapidamente, e l'uso del numero di porta nella gestione del dispatch può provocare dei problemi tanto più se, come sicuramente farei, lo cambio in uno solo dei due punti (magari nel solo dispatch e non all’apertura).

Quello che ho fatto è stato effettuare qualche modifica nel file uip.c.

Sotto la dichiarazione del vettore uip_listenports, ho aggiunto un semplice vettore di puntatori a funzioni void così:

void (* uip_callbacks[UIP_LISTENPORTS])(void);

quindi ho cambiato la definizione della funzione di creazione del listener per la gestione del server in questo modo:

void uip_listen(u16_t port, void * uip_listen_handler)
{
  for(c = 0; c < UIP_LISTENPORTS; ++c) {
    if(uip_listenports[c] == 0) {
      uip_listenports[c] = port;
      uip_callbacks[c] = uip_listen_handler;
      return;
    }
  }
}

ed ho modificato la gestione delle callback chiamando una funzione scritta nel file uip.c che a questo punto può essere fissa e non modificata al bisogno, quindi una perfetta funzione di libreria.

void dbaddendum_uip_appcall(void)
{
    u8_t cnt;
    for (cnt=0;cnt < UIP_LISTENPORTS; cnt++)
    {
        if (uip_listenports[cnt] > 0)
        {
            if (uip_listenports[cnt] == uip_conn->lport)
            {
                uip_callbacks[cnt]();
                return;
            }
        }
    }
    #if UIP_ACTIVE_OPEN
    webclient_appcall();
    #endif
}

Adesso sto lavorando sulla parte di webclient per risolvere un paio di problemi similari.

Dopo di che provvederò all’inserimento in libreria della gestione del protocollo FTP server.

Alla prossima

posted @ giovedì 11 ottobre 2012 13:09

Print