/* * File : emac.c * This file is part of RT-Thread RTOS * COPYRIGHT (C) 2006-2014, RT-Thread Develop Team * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://openlab.rt-thread.com/license/LICENSE * * Change Logs: * Date Author Notes * 2014-08-29 aozima first implementation */ #include #include #include "lwipopts.h" #include "board.h" #include "app_phy.h" /* debug option */ #define ETH_DEBUG //#define ETH_RX_DUMP //#define ETH_TX_DUMP #ifdef ETH_DEBUG #define CME_ETH_PRINTF rt_kprintf #else #define CME_ETH_PRINTF(...) #endif #define MAX_ADDR_LEN 6 struct rt_cme_eth { /* inherit from ethernet device */ struct eth_device parent; /* interface address info. */ rt_uint8_t dev_addr[MAX_ADDR_LEN]; /* hw address */ uint32_t ETH_Speed; uint32_t ETH_Mode; struct rt_semaphore tx_buf_free; struct rt_mutex lock; }; static struct rt_cme_eth cme_eth_device; #if defined(ETH_RX_DUMP) || defined(ETH_TX_DUMP) static void packet_dump(const char * msg, const struct pbuf* p) { const struct pbuf* q; rt_uint32_t i,j; rt_uint8_t *ptr; rt_kprintf("%s %d byte\n", msg, p->tot_len); i=0; for(q=p; q != RT_NULL; q= q->next) { ptr = q->payload; for(j=0; jlen; j++) { if( (i%8) == 0 ) { rt_kprintf(" "); } if( (i%16) == 0 ) { rt_kprintf("\r\n"); } rt_kprintf("%02x ",*ptr); i++; ptr++; } } rt_kprintf("\n\n"); } #else #define packet_dump(...) #endif /* dump */ ///////////////////////////////////////////////////////////////// uint32_t rxTotalMemory = 0x2000; uint32_t rxDescNum = 3; uint32_t rxBufSize = 0x400; uint32_t rxBaseAddr = 0x2000C000;// C000-48K uint32_t txBaseAddr = 0x2000E000;// E000-56K uint32_t txTotalMemory = 0x2000; BOOL isRxNoBuf = FALSE; #define ETH_MAX_PACKET_SIZE 1520 /* ETH_HEADER + ETH_EXTRA + MAX_ETH_PAYLOAD + ETH_CRC */ #define ETH_RXBUFNB 4 #define ETH_TXBUFNB 2 struct eth_rx_buffer { ETH_RX_DESC desc; uint32_t buffer[ETH_MAX_PACKET_SIZE/4]; }; struct eth_tx_buffer { ETH_TX_DESC desc; uint32_t buffer[ETH_MAX_PACKET_SIZE/4]; }; static struct eth_rx_buffer rx_buffer[ETH_RXBUFNB]; static struct eth_tx_buffer tx_buffer[ETH_TXBUFNB]; static void RxDescChainInit(void) { uint32_t i; // initialize rx descriptor ETH_RX_DESC *desc = &rx_buffer[0].desc; for (i = 0; i < ETH_RXBUFNB; i++) { desc->RX_1.RX1_b.SIZE = ETH_MAX_PACKET_SIZE; desc->bufAddr = (uint32_t)rx_buffer[i].buffer; if((i+1) == ETH_RXBUFNB) desc->nextDescAddr = (uint32_t)&rx_buffer[0].desc; else desc->nextDescAddr = (uint32_t)&rx_buffer[i+1].desc; desc = (ETH_RX_DESC *)desc->nextDescAddr; } ETH_SetRxDescRing(&rx_buffer[0].desc); } static void TxDescChainInit(void) { uint32_t i; // initialize tx descriptor ETH_TX_DESC *desc = &tx_buffer[0].desc; for (i = 0; i < ETH_TXBUFNB; i++) { desc->TX_1.TX1_b.SIZE = ETH_MAX_PACKET_SIZE; desc->bufAddr = (uint32_t)tx_buffer[i].buffer; if((i+1) == ETH_TXBUFNB) desc->nextDescAddr = (uint32_t)&tx_buffer[0].desc; else desc->nextDescAddr = (uint32_t)&tx_buffer[i+1].desc; desc = (ETH_TX_DESC *)desc->nextDescAddr; } ETH_SetTxDescRing(&tx_buffer[0].desc); } ///////////////////////////////////////////////////////////////// /* initialize the interface */ static rt_err_t rt_cme_eth_init(rt_device_t dev) { struct rt_cme_eth * cme_eth = (struct rt_cme_eth *)dev; ETH_InitTypeDef init; ETH_FrameFilter flt; init.ETH_Speed = phy_GetSpeed(); init.ETH_Duplex = phy_GetDuplex(); init.ETH_LinkUp = phy_IsLink(); init.ETH_RxEn = TRUE; init.ETH_TxEn = TRUE; init.ETH_ChecksumOffload = FALSE; init.ETH_JumboFrame = FALSE; memcpy(init.ETH_MacAddr, cme_eth->dev_addr, sizeof(init.ETH_MacAddr)); // Disable broadcast; // TODO: why? memset(&flt, 0, sizeof(ETH_FrameFilter)); flt.ETH_BroadcastFilterEnable = FALSE; flt.ETH_OwnFilterEnable = FALSE; flt.ETH_SelfDrop = FALSE; flt.ETH_SourceFilterEnable = FALSE; flt.ETH_SourceDrop = FALSE; init.ETH_Filter = &flt; if (!phy_Init()) { rt_kprintf("phy_Init failed!\n"); while (1); } if (!ETH_Init(&init)) { rt_kprintf("ETH_Init failed!\n"); while (1); } RxDescChainInit(); TxDescChainInit(); ETH_ITConfig(ETH_INT_BUS_FATAL_ERROR, TRUE); ETH_ITConfig(ETH_INT_RX_COMPLETE_FRAME, TRUE); ETH_ITConfig(ETH_INT_RX_BUF_UNAVAI, TRUE); ETH_ITConfig(ETH_INT_RX_STOP, TRUE); ETH_StartRx(); ETH_ITConfig(ETH_INT_TX_COMPLETE_FRAME, TRUE); ETH_StartTx(); return RT_EOK; } static rt_err_t rt_cme_eth_open(rt_device_t dev, rt_uint16_t oflag) { return RT_EOK; } static rt_err_t rt_cme_eth_close(rt_device_t dev) { return RT_EOK; } static rt_size_t rt_cme_eth_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size) { rt_set_errno(-RT_ENOSYS); return 0; } static rt_size_t rt_cme_eth_write (rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size) { rt_set_errno(-RT_ENOSYS); return 0; } static rt_err_t rt_cme_eth_control(rt_device_t dev, int cmd, void *args) { switch(cmd) { case NIOCTL_GADDR: /* get mac address */ if(args) rt_memcpy(args, cme_eth_device.dev_addr, 6); else return -RT_ERROR; break; default : break; } return RT_EOK; } /* ethernet device interface */ /* transmit packet. */ rt_err_t rt_cme_eth_tx( rt_device_t dev, struct pbuf* p) { rt_err_t result = RT_EOK; ETH_TX_DESC *desc; struct rt_cme_eth * cme_eth = (struct rt_cme_eth *)dev; rt_mutex_take(&cme_eth->lock, RT_WAITING_FOREVER); #ifdef ETH_TX_DUMP packet_dump("TX dump", p); #endif /* ETH_TX_DUMP */ /* get free tx buffer */ { rt_err_t result; result = rt_sem_take(&cme_eth->tx_buf_free, RT_TICK_PER_SECOND/10); if (result != RT_EOK) { result = -RT_ERROR; goto _exit; } } desc = ETH_AcquireFreeTxDesc(); if(desc == RT_NULL) { CME_ETH_PRINTF("TxDesc not ready!\n"); RT_ASSERT(0); result = -RT_ERROR; goto _exit; } desc->TX_0.TX0_b.FS = TRUE; desc->TX_0.TX0_b.LS = TRUE; desc->TX_1.TX1_b.SIZE = p->tot_len; pbuf_copy_partial(p, ( void *)(desc->bufAddr), p->tot_len, 0); ETH_ReleaseTxDesc(desc); ETH_ResumeTx(); _exit: rt_mutex_release(&cme_eth->lock); return result; } /* reception packet. */ struct pbuf *rt_cme_eth_rx(rt_device_t dev) { struct pbuf* p = RT_NULL; ETH_RX_DESC *desc; uint32_t framelength; struct rt_cme_eth * cme_eth = (struct rt_cme_eth *)dev; rt_err_t result; result = rt_mutex_take(&cme_eth->lock, RT_WAITING_FOREVER); if (result == -RT_ETIMEOUT) { rt_kprintf("Take mutex time out.\n"); goto _exit; } else if (result == -RT_ERROR) { rt_kprintf("Take mutex error.\n"); goto _exit; } desc = ETH_AcquireFreeRxDesc(); if(desc == RT_NULL) { ETH_ITConfig(ETH_INT_RX_COMPLETE_FRAME, TRUE); ETH_ITConfig(ETH_INT_RX_BUF_UNAVAI, TRUE); ETH_ResumeRx(); goto _exit; } framelength = desc->RX_0.RX0_b.FL; /* allocate buffer */ p = pbuf_alloc(PBUF_LINK, framelength, PBUF_RAM); if (p != RT_NULL) { pbuf_take(p, (const void *)(desc->bufAddr), framelength); #ifdef ETH_RX_DUMP packet_dump("RX dump", p); #endif /* ETH_RX_DUMP */ } ETH_ReleaseRxDesc(desc); _exit: rt_mutex_release(&cme_eth->lock); return p; } static void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; /* Enable the USARTy Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = ETH_INT_IRQn; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = TRUE; NVIC_Init(&NVIC_InitStructure); } int cme_m7_eth_init(void) { // /* PHY RESET: PA4 */ // { // GPIO_ResetBits(GPIOA, GPIO_Pin_4); // rt_thread_delay(2); // GPIO_SetBits(GPIOA, GPIO_Pin_4); // rt_thread_delay(2); // } // GPIO_Configuration(); NVIC_Configuration(); // cme_eth_device.ETH_Speed = ETH_Speed_100M; // cme_eth_device.ETH_Mode = ETH_Mode_FullDuplex; /* OUI 00-80-E1 STMICROELECTRONICS. */ cme_eth_device.dev_addr[0] = 0x00; cme_eth_device.dev_addr[1] = 0x80; cme_eth_device.dev_addr[2] = 0xE1; /* generate MAC addr from 96bit unique ID (only for test). */ // cme_eth_device.dev_addr[3] = *(rt_uint8_t*)(0x1FFF7A10+4); // cme_eth_device.dev_addr[4] = *(rt_uint8_t*)(0x1FFF7A10+2); // cme_eth_device.dev_addr[5] = *(rt_uint8_t*)(0x1FFF7A10+0); cme_eth_device.dev_addr[3] = 12; cme_eth_device.dev_addr[4] = 34; cme_eth_device.dev_addr[5] = 56; cme_eth_device.parent.parent.init = rt_cme_eth_init; cme_eth_device.parent.parent.open = rt_cme_eth_open; cme_eth_device.parent.parent.close = rt_cme_eth_close; cme_eth_device.parent.parent.read = rt_cme_eth_read; cme_eth_device.parent.parent.write = rt_cme_eth_write; cme_eth_device.parent.parent.control = rt_cme_eth_control; cme_eth_device.parent.parent.user_data = RT_NULL; cme_eth_device.parent.eth_rx = rt_cme_eth_rx; cme_eth_device.parent.eth_tx = rt_cme_eth_tx; /* init EMAC lock */ rt_mutex_init(&cme_eth_device.lock, "emac0", RT_IPC_FLAG_PRIO); /* init tx buffer free semaphore */ rt_sem_init(&cme_eth_device.tx_buf_free, "tx_buf", ETH_TXBUFNB, RT_IPC_FLAG_FIFO); /* register eth device */ eth_device_init(&(cme_eth_device.parent), "e0"); return RT_EOK; } void ETH_IRQHandler(void) { /* enter interrupt */ rt_interrupt_enter(); if (ETH_GetITStatus(ETH_INT_TX_COMPLETE_FRAME)) { rt_sem_release(&cme_eth_device.tx_buf_free); ETH_ClearITPendingBit(ETH_INT_TX_COMPLETE_FRAME); } if (ETH_GetITStatus(ETH_INT_RX_STOP)) { CME_ETH_PRINTF("ETH_INT_RX_STOP\n"); ETH_ClearITPendingBit(ETH_INT_RX_STOP); } if ((ETH_GetITStatus(ETH_INT_RX_BUF_UNAVAI)) || (ETH_GetITStatus(ETH_INT_RX_COMPLETE_FRAME))) { /* a frame has been received */ eth_device_ready(&(cme_eth_device.parent)); ETH_ITConfig(ETH_INT_RX_COMPLETE_FRAME, FALSE); ETH_ITConfig(ETH_INT_RX_BUF_UNAVAI, FALSE); ETH_ClearITPendingBit(ETH_INT_RX_BUF_UNAVAI); ETH_ClearITPendingBit(ETH_INT_RX_COMPLETE_FRAME); } /* leave interrupt */ rt_interrupt_leave(); }